diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/app.rs | 63 | ||||
| -rw-r--r-- | src/main.rs | 23 | ||||
| -rw-r--r-- | src/screens/available_packages.rs | 95 | ||||
| -rw-r--r-- | src/screens/dashboard.rs | 31 | ||||
| -rw-r--r-- | src/screens/installed_packages.rs | 116 | ||||
| -rw-r--r-- | src/screens/screen.rs | 11 | ||||
| -rw-r--r-- | src/signal.rs | 9 | ||||
| -rw-r--r-- | src/widgets/helpers.rs | 2 | ||||
| -rw-r--r-- | src/widgets/package_metadata.rs | 5 |
9 files changed, 200 insertions, 155 deletions
@@ -1,9 +1,7 @@ use ratatui::{ Frame, - crossterm::event::{self, Event, KeyCode}, layout::{Constraint, Layout}, style::Stylize, - text::Text, widgets::Block, }; @@ -14,7 +12,7 @@ use crate::{ available_packages::AvailablePackagesScreen, dashboard::DashboardScreen, installed_packages::InstalledPackagesScreen, screen::Screen, }, - signal::Signal, + signal::{Event, Signal}, theme::Theme, widgets::tabs::render_tab, }; @@ -26,9 +24,11 @@ pub enum ViewState { AvailablePackages = 2, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Default, Clone, Copy)] pub enum LoadingState { + #[default] Idle, + Loading, Complete, Error, @@ -77,55 +77,48 @@ impl App { match self.view { ViewState::InstalledPackages => { self.installed_package_screen - .draw(frame, rest, &self.portage, &self.loading_state); + .draw(frame, rest, &self.portage); } ViewState::Dashboard => { - self.dashboard_screen - .draw(frame, rest, &self.portage, &self.loading_state); + self.dashboard_screen.draw(frame, rest, &self.portage); } ViewState::AvailablePackages => { self.available_package_screen - .draw(frame, rest, &self.portage, &self.loading_state); + .draw(frame, rest, &self.portage); } } } - pub fn update(&mut self) -> color_eyre::Result<bool> { - if let Ok(Event::Key(key)) = event::read() { - let signal = match self.view { + pub fn update(&mut self, event: Event) -> color_eyre::Result<bool> { + let signal = match event { + Event::KeyEvent(_) => match self.view { crate::app::ViewState::InstalledPackages => { - self.installed_package_screen.update(key, &self.portage) + self.installed_package_screen.update(&event, &self.portage) } ViewState::AvailablePackages => { - self.available_package_screen.update(key, &self.portage) + self.available_package_screen.update(&event, &self.portage) } + ViewState::Dashboard => self.dashboard_screen.update(&event, &self.portage), + }, + Event::LoadStateUpdate(_) => { + self.installed_package_screen.update(&event, &self.portage); + self.available_package_screen.update(&event, &self.portage); + self.dashboard_screen.update(&event, &self.portage); + None + } + }; - // Fallback, primarly while implementing - _ => match key.code { - KeyCode::Char('r') => { - if !matches!(self.loading_state, LoadingState::Loading) { - self.start_available_packages_load(); - } - None - } - KeyCode::Char('q') => Some(Signal::Quit), - KeyCode::Tab => Some(Signal::CycleTab), - _ => None, - }, - }; - - if let Some(signal) = signal { - match signal { - Signal::Quit => return Ok(true), - Signal::CycleTab => self.cycle_current_tab(), - } + if let Some(signal) = signal { + match signal { + Signal::Quit => return Ok(true), + Signal::CycleTab => self.cycle_current_tab(), } } Ok(false) } - pub fn poll_available_packages(&mut self) { + pub fn poll_available_packages(&mut self) -> Option<Event> { if let Some(loader) = &self.available_loader { if let Some(msg) = loader.try_recv() { match msg { @@ -145,8 +138,12 @@ impl App { self.available_loader = None; } } + + return Some(Event::LoadStateUpdate(self.loading_state)); } } + + None } pub fn start_available_packages_load(&mut self) { diff --git a/src/main.rs b/src/main.rs index 3f48139..971a882 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,7 @@ use clap::command; use ratatui::{ Terminal, crossterm::{ - event::poll, + event::{self, Event, poll}, execute, terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode}, }, @@ -60,19 +60,28 @@ fn main() -> color_eyre::Result<()> { fn run(mut terminal: Terminal<CrosstermBackend<Stdout>>, app: &mut App) -> color_eyre::Result<()> { loop { - app.poll_available_packages(); - terminal.draw(|frame| app.draw(frame))?; + let mut events = vec![]; + + if let Some(event) = app.poll_available_packages() { + events.push(event); + } + if poll(Duration::from_millis(50))? { - let should_exit = app.update()?; + if let Ok(Event::Key(key)) = event::read() { + events.push(crate::signal::Event::KeyEvent(key)); + } + } + + for event in events { + // Process different event types in order + let should_exit = app.update(event)?; if should_exit { - break; + return Ok(()); } } } - - Ok(()) } fn init_tui() -> io::Result<ratatui::Terminal<CrosstermBackend<Stdout>>> { diff --git a/src/screens/available_packages.rs b/src/screens/available_packages.rs index 1f61982..46d9f39 100644 --- a/src/screens/available_packages.rs +++ b/src/screens/available_packages.rs @@ -13,7 +13,7 @@ use crate::{ app::LoadingState, gentoo::package::{Package, PackageKey, PackageVersion}, screens::screen::Screen, - signal::Signal, + signal::{Event, Signal}, theme::Theme, widgets::{package_metadata::render_package_metadata, use_flags::render_use_flags}, }; @@ -28,8 +28,12 @@ enum CurrentList { pub struct AvailablePackagesScreen { chosen_list: CurrentList, + pkg_list: Option<List<'static>>, pkg_list_state: ListState, + variant_list_state: ListState, + + loading_state: LoadingState, } fn version_to_variant(pkg_version: &PackageVersion) -> String { @@ -41,20 +45,12 @@ fn version_to_variant(pkg_version: &PackageVersion) -> String { impl AvailablePackagesScreen { // TODO: this is the same as InstalledPackagesScreen, replace this with a widget function - fn render_packages(&mut self, frame: &mut Frame, area: Rect, package_keys: Vec<String>) { + fn render_packages(&mut self, frame: &mut Frame, area: Rect) { // TODO: Put this into screen, instead of rebuilding the list every frame - let list = List::new(package_keys) - .style(Color::White) - .highlight_style(Theme::selected()) - .highlight_symbol("> ") - .block( - Block::default() - .borders(Borders::ALL) - .border_style(Theme::block()) - .title("Available packages"), - ); - frame.render_stateful_widget(list, area, &mut self.pkg_list_state); + if let Some(list) = &self.pkg_list { + frame.render_stateful_widget(list, area, &mut self.pkg_list_state); + } } fn render_package_variants( @@ -98,8 +94,10 @@ impl Default for AvailablePackagesScreen { Self { chosen_list: CurrentList::PackageList, + pkg_list: None, pkg_list_state: list_state, variant_list_state: variant_state, + loading_state: LoadingState::Idle, } } } @@ -110,9 +108,8 @@ impl Screen for AvailablePackagesScreen { frame: &mut ratatui::Frame, area: ratatui::prelude::Rect, repo: &crate::gentoo::Portage, - loading_state: &crate::app::LoadingState, ) { - if !matches!(loading_state, LoadingState::Complete) { + if !matches!(self.loading_state, LoadingState::Complete) { // TODO: Implement loading screen frame.render_widget(Text::styled("Not implemented yet :P", Theme::text()), area); return; @@ -133,11 +130,7 @@ impl Screen for AvailablePackagesScreen { None => None, }; - self.render_packages( - frame, - package_list, - packages.iter().map(|x| x.qualified_name()).collect(), - ); + self.render_packages(frame, package_list); if let Some(pkg) = pkg { self.render_package_variants(frame, package_variants, pkg.versions.iter().collect()); @@ -165,30 +158,58 @@ impl Screen for AvailablePackagesScreen { fn update( &mut self, - key: ratatui::crossterm::event::KeyEvent, - _repo: &crate::gentoo::Portage, + event: &Event, + repo: &crate::gentoo::Portage, ) -> Option<crate::signal::Signal> { - match key.code { - KeyCode::Char('q') => return Some(Signal::Quit), - KeyCode::Tab => return Some(Signal::CycleTab), + match event { + Event::KeyEvent(key) => match key.code { + KeyCode::Char('q') => return Some(Signal::Quit), + KeyCode::Tab => return Some(Signal::CycleTab), - // TODO: This should cycle, also space isnt a good key for this - KeyCode::Char(' ') => self.cycle_list(), + // TODO: This should cycle, also space isnt a good key for this + KeyCode::Char(' ') => self.cycle_list(), - _ => match self.chosen_list { - CurrentList::PackageList => match key.code { - KeyCode::Char('j') => self.pkg_list_state.select_next(), - KeyCode::Char('k') => self.pkg_list_state.select_previous(), + _ => match self.chosen_list { + CurrentList::PackageList => match key.code { + KeyCode::Char('j') => self.pkg_list_state.select_next(), + KeyCode::Char('k') => self.pkg_list_state.select_previous(), - _ => {} - }, - CurrentList::VariantList => match key.code { - KeyCode::Char('j') => self.variant_list_state.select_next(), - KeyCode::Char('k') => self.variant_list_state.select_previous(), + _ => {} + }, + CurrentList::VariantList => match key.code { + KeyCode::Char('j') => self.variant_list_state.select_next(), + KeyCode::Char('k') => self.variant_list_state.select_previous(), - _ => {} + _ => {} + }, }, }, + Event::LoadStateUpdate(loading_state) => match loading_state { + LoadingState::Complete => { + let packages: Vec<String> = repo + .available_packages() + .iter() + .map(|x| &x.atom) + .map(|x| x.qualified_name()) + .collect(); + + let list = List::new(packages) + .style(Color::White) + .highlight_style(Theme::selected()) + .highlight_symbol("> ") + .block( + Block::default() + .borders(Borders::ALL) + .border_style(Theme::block()) + .title("Available packages"), + ); + + self.pkg_list = Some(list); + self.loading_state = LoadingState::Complete + } + + loading_state => self.loading_state = *loading_state, + }, } None diff --git a/src/screens/dashboard.rs b/src/screens/dashboard.rs index 1fcb242..e000052 100644 --- a/src/screens/dashboard.rs +++ b/src/screens/dashboard.rs @@ -6,12 +6,18 @@ use ratatui::{ }; use crate::{ - app::LoadingState, gentoo::Portage, screens::screen::Screen, signal::Signal, theme::Theme, + app::LoadingState, + gentoo::Portage, + screens::screen::Screen, + signal::{Event, Signal}, + theme::Theme, widgets::helpers::human_size, }; #[derive(Debug, Default)] -pub struct DashboardScreen {} +pub struct DashboardScreen { + loading_state: LoadingState, +} fn create_stats<'a>(title: &'a str, value: &'a str) -> Paragraph<'a> { Paragraph::new(vec![ @@ -35,7 +41,6 @@ impl Screen for DashboardScreen { frame: &mut ratatui::Frame, area: ratatui::prelude::Rect, repo: &crate::gentoo::Portage, - loading_state: &LoadingState, ) { let logo = " ██████╗ ██╗ ██╗██╗███████╗██╗ ██╗ @@ -123,7 +128,7 @@ impl Screen for DashboardScreen { let keys_text = "(q) to quit | (tab) to cycle tabs"; - let loading_text = match loading_state { + let loading_text = match self.loading_state { LoadingState::Loading => "⟳ Loading available packages...", LoadingState::Error => "⚠ Failed to load available packages", LoadingState::Complete => "✓ Available packages loaded", @@ -137,7 +142,7 @@ impl Screen for DashboardScreen { Span::raw(" | "), Span::styled( loading_text, - match loading_state { + match self.loading_state { crate::app::LoadingState::Loading => Theme::muted(), crate::app::LoadingState::Error => Theme::error(), crate::app::LoadingState::Complete => Theme::success(), @@ -157,12 +162,18 @@ impl Screen for DashboardScreen { frame.render_widget(key_notes, key_hints); } - fn update(&mut self, key: ratatui::crossterm::event::KeyEvent, _: &Portage) -> Option<Signal> { - match key.code { - KeyCode::Char('q') => Some(Signal::Quit), - KeyCode::Tab => Some(Signal::CycleTab), + fn update(&mut self, event: &Event, _: &Portage) -> Option<Signal> { + match event { + Event::KeyEvent(key) => match key.code { + KeyCode::Char('q') => Some(Signal::Quit), + KeyCode::Tab => Some(Signal::CycleTab), - _ => None, + _ => None, + }, + Event::LoadStateUpdate(loading_state) => { + self.loading_state = *loading_state; + None + } } } } diff --git a/src/screens/installed_packages.rs b/src/screens/installed_packages.rs index 8f0cc5d..1b0061a 100644 --- a/src/screens/installed_packages.rs +++ b/src/screens/installed_packages.rs @@ -1,6 +1,6 @@ use ratatui::{ Frame, - crossterm::event::{KeyCode, KeyEvent, KeyModifiers}, + crossterm::event::{KeyCode, KeyModifiers}, layout::{Constraint, Layout, Rect}, style::{Color, Modifier, Style}, text::{Line, Span}, @@ -9,10 +9,9 @@ use ratatui::{ use ratatui_textarea::TextArea; use crate::{ - app::LoadingState, gentoo::{InstalledPackage, Portage, package::PackageKey}, screens::screen::Screen, - signal::Signal, + signal::{Event, Signal}, theme::Theme, widgets::{ helpers::search_popup_rect, package_metadata::render_package_metadata, @@ -131,13 +130,7 @@ impl Default for InstalledPackagesScreen { } impl Screen for InstalledPackagesScreen { - fn draw( - &mut self, - frame: &mut Frame, - area: Rect, - repo: &Portage, - _loading_state: &LoadingState, - ) { + fn draw(&mut self, frame: &mut Frame, area: Rect, repo: &Portage) { let constraints = [Constraint::Fill(1), Constraint::Length(3)]; let layout = Layout::vertical(constraints); @@ -243,66 +236,71 @@ impl Screen for InstalledPackagesScreen { } } - fn update(&mut self, key: KeyEvent, repo: &Portage) -> Option<Signal> { - if self.search_popup.visible { - let packages = match self.filter_state { - FilterState::Unfiltered => repo.installed_packages(), - FilterState::WorldSet => repo.world_packages(), - }; + fn update(&mut self, event: &Event, repo: &Portage) -> Option<Signal> { + match event { + Event::KeyEvent(key) => { + if self.search_popup.visible { + let packages = match self.filter_state { + FilterState::Unfiltered => repo.installed_packages(), + FilterState::WorldSet => repo.world_packages(), + }; - match key.code { - KeyCode::Esc => self.search_popup.toggle(), - KeyCode::Enter => { - let result = self.search_popup.search(packages); - self.search_popup.toggle(); + match key.code { + KeyCode::Esc => self.search_popup.toggle(), + KeyCode::Enter => { + let result = self.search_popup.search(packages); + self.search_popup.toggle(); - if let Some(index) = result { - self.list_state.select(Some(index)); - } - } - _ => { - self.search_popup.textarea.input_without_shortcuts(key); - let result = self.search_popup.search(packages); + if let Some(index) = result { + self.list_state.select(Some(index)); + } + } + _ => { + self.search_popup.textarea.input_without_shortcuts(*key); + let result = self.search_popup.search(packages); - if let Some(index) = result { - self.list_state.select(Some(index)); - } - } - } - } else { - match key.modifiers { - KeyModifiers::CONTROL => match key.code { - KeyCode::Char('d') => self - .list_state - .select(Some(self.list_state.selected().unwrap_or(0) + 30)), - KeyCode::Char('u') => { - if let Some(selected) = self.list_state.selected() { - if selected < 30 { - self.list_state.select(Some(0)); - } else { - self.list_state.select(Some(selected - 30)); + if let Some(index) = result { + self.list_state.select(Some(index)); } } } + } else { + match key.modifiers { + KeyModifiers::CONTROL => match key.code { + KeyCode::Char('d') => self + .list_state + .select(Some(self.list_state.selected().unwrap_or(0) + 30)), + KeyCode::Char('u') => { + if let Some(selected) = self.list_state.selected() { + if selected < 30 { + self.list_state.select(Some(0)); + } else { + self.list_state.select(Some(selected - 30)); + } + } + } - _ => {} - }, + _ => {} + }, - _ => match key.code { - KeyCode::Char('q') => return Some(Signal::Quit), - KeyCode::Tab => return Some(Signal::CycleTab), - KeyCode::Char('j') => self.list_state.select_next(), - KeyCode::Char('k') => self.list_state.select_previous(), - KeyCode::Char('/') => self.search_popup.toggle(), + _ => match key.code { + KeyCode::Char('q') => return Some(Signal::Quit), + KeyCode::Tab => return Some(Signal::CycleTab), + KeyCode::Char('j') => self.list_state.select_next(), + KeyCode::Char('k') => self.list_state.select_previous(), + KeyCode::Char('/') => self.search_popup.toggle(), - // TODO: Reimplement searchable "spaces" - // KeyCode::Char('n') => todo!("Next search"), - // KeyCode::Char('N') => todo!("prev search"), - KeyCode::Char('f') => self.cycle_filter(), + // TODO: Reimplement searchable "spaces" + // KeyCode::Char('n') => todo!("Next search"), + // KeyCode::Char('N') => todo!("prev search"), + KeyCode::Char('f') => self.cycle_filter(), - _ => {} - }, + _ => {} + }, + } + } } + Event::LoadStateUpdate(_) => {} } None diff --git a/src/screens/screen.rs b/src/screens/screen.rs index 8e7449c..e1bb732 100644 --- a/src/screens/screen.rs +++ b/src/screens/screen.rs @@ -1,8 +1,11 @@ -use ratatui::{Frame, crossterm::event::KeyEvent, layout::Rect}; +use ratatui::{Frame, layout::Rect}; -use crate::{app::LoadingState, gentoo::Portage, signal::Signal}; +use crate::{ + gentoo::Portage, + signal::{Event, Signal}, +}; pub(crate) trait Screen { - fn draw(&mut self, frame: &mut Frame, area: Rect, repo: &Portage, loading_state: &LoadingState); - fn update(&mut self, key: KeyEvent, repo: &Portage) -> Option<Signal>; + fn draw(&mut self, frame: &mut Frame, area: Rect, repo: &Portage); + fn update(&mut self, event: &Event, repo: &Portage) -> Option<Signal>; } diff --git a/src/signal.rs b/src/signal.rs index 1210e17..e1353c9 100644 --- a/src/signal.rs +++ b/src/signal.rs @@ -1,4 +1,13 @@ +use ratatui::crossterm::event::KeyEvent; + +use crate::app::LoadingState; + pub enum Signal { Quit, CycleTab, } + +pub enum Event { + KeyEvent(KeyEvent), + LoadStateUpdate(LoadingState), +} diff --git a/src/widgets/helpers.rs b/src/widgets/helpers.rs index 1e76b5f..8b01b65 100644 --- a/src/widgets/helpers.rs +++ b/src/widgets/helpers.rs @@ -4,7 +4,7 @@ use ratatui::{ text::{Line, Span}, }; -use crate::gentoo::{InstalledPackage, package::Metadata}; +use crate::gentoo::package::Metadata; pub fn search_popup_rect(percent_x: u16, r: Rect) -> Rect { // Cut the given rectangle into three vertical pieces diff --git a/src/widgets/package_metadata.rs b/src/widgets/package_metadata.rs index d6c82cd..35fc1c4 100644 --- a/src/widgets/package_metadata.rs +++ b/src/widgets/package_metadata.rs @@ -7,10 +7,7 @@ use ratatui::{ }; use crate::{ - gentoo::{ - InstalledPackage, - package::{Metadata, PackageKey}, - }, + gentoo::package::Metadata, theme::Theme, widgets::helpers::{homepage_lines, human_size}, }; |
