From eaa1b915da8480bf3a86960c94f3667f386aa0f4 Mon Sep 17 00:00:00 2001 From: pablu Date: Wed, 3 Jun 2026 14:10:49 +0200 Subject: wip: browser screen implementation --- src/screens/available_packages.rs | 196 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 src/screens/available_packages.rs (limited to 'src/screens/available_packages.rs') diff --git a/src/screens/available_packages.rs b/src/screens/available_packages.rs new file mode 100644 index 0000000..1f61982 --- /dev/null +++ b/src/screens/available_packages.rs @@ -0,0 +1,196 @@ +use std::collections::HashSet; + +use ratatui::{ + Frame, + crossterm::event::KeyCode, + layout::{Constraint, Layout, Rect}, + style::Color, + text::Text, + widgets::{Block, Borders, List, ListState}, +}; + +use crate::{ + app::LoadingState, + gentoo::package::{Package, PackageKey, PackageVersion}, + screens::screen::Screen, + signal::Signal, + theme::Theme, + widgets::{package_metadata::render_package_metadata, use_flags::render_use_flags}, +}; + +#[derive(Debug)] +enum CurrentList { + PackageList, + VariantList, +} + +#[derive(Debug)] +pub struct AvailablePackagesScreen { + chosen_list: CurrentList, + + pkg_list_state: ListState, + variant_list_state: ListState, +} + +fn version_to_variant(pkg_version: &PackageVersion) -> String { + format!( + "{}::{}", + pkg_version.version, pkg_version.metadata.repository + ) +} + +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) { + // 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); + } + + fn render_package_variants( + &mut self, + frame: &mut Frame, + area: Rect, + variants: Vec<&PackageVersion>, + ) { + // TODO: Put this into screen, instead of rebuilding the list every frame + let list = List::new(variants.iter().map(|v| version_to_variant(v))) + .style(Color::White) + .highlight_style(Theme::selected()) + .highlight_symbol("> ") + .block( + Block::default() + .borders(Borders::ALL) + .border_style(Theme::block()) + .title("Variants"), + ); + + frame.render_stateful_widget(list, area, &mut self.variant_list_state); + } + + fn cycle_list(&mut self) { + self.variant_list_state.select(Some(0)); + + self.chosen_list = match self.chosen_list { + CurrentList::PackageList => CurrentList::VariantList, + CurrentList::VariantList => CurrentList::PackageList, + } + } +} + +impl Default for AvailablePackagesScreen { + fn default() -> Self { + let mut list_state = ListState::default(); + list_state.select(Some(0)); + + let mut variant_state = ListState::default(); + variant_state.select(Some(0)); + + Self { + chosen_list: CurrentList::PackageList, + pkg_list_state: list_state, + variant_list_state: variant_state, + } + } +} + +impl Screen for AvailablePackagesScreen { + fn draw( + &mut self, + frame: &mut ratatui::Frame, + area: ratatui::prelude::Rect, + repo: &crate::gentoo::Portage, + loading_state: &crate::app::LoadingState, + ) { + if !matches!(loading_state, LoadingState::Complete) { + // TODO: Implement loading screen + frame.render_widget(Text::styled("Not implemented yet :P", Theme::text()), area); + return; + } + + let constraints = [Constraint::Percentage(50), Constraint::Percentage(50)]; + let [left, right] = area.layout(&Layout::horizontal(constraints)); + + let [package_list, use_flags] = left.layout(&Layout::vertical(constraints)); + let [package_variants, metadata] = right.layout(&Layout::vertical(constraints)); + + let packages: Vec<&PackageKey> = + repo.available_packages().iter().map(|x| &x.atom).collect(); + + let selected_package_key = packages.get(self.pkg_list_state.selected().unwrap_or(0)); + let pkg: Option<&Package> = match selected_package_key { + Some(pkg_key) => repo.get_available_package(pkg_key), + None => None, + }; + + self.render_packages( + frame, + package_list, + packages.iter().map(|x| x.qualified_name()).collect(), + ); + + if let Some(pkg) = pkg { + self.render_package_variants(frame, package_variants, pkg.versions.iter().collect()); + + let selected_version = &pkg.versions[self.variant_list_state.selected().unwrap_or(0)]; + + render_use_flags( + frame, + use_flags, + selected_version.iuse.iter().collect(), + // Get default active use flags for package, and globally defines ones + HashSet::new(), + ); + + render_package_metadata( + frame, + metadata, + &selected_version.version, + &selected_version.metadata, + None, + None, + ); + } + } + + fn update( + &mut self, + key: ratatui::crossterm::event::KeyEvent, + _repo: &crate::gentoo::Portage, + ) -> Option { + 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(), + + _ => 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(), + + _ => {} + }, + }, + } + + None + } +} -- cgit 1.3.1