aboutsummaryrefslogtreecommitdiff
path: root/src/screens/available_packages.rs
diff options
context:
space:
mode:
authorpablu <me@pablu.de>2026-06-03 14:10:49 +0200
committerpablu <me@pablu.de>2026-06-03 14:10:49 +0200
commiteaa1b915da8480bf3a86960c94f3667f386aa0f4 (patch)
treeb85e1d2edb0a507ba480926d12fab074d52bb961 /src/screens/available_packages.rs
parent81cc9a0b347255629d411323e7ff8fee4d5ec61d (diff)
wip: browser screen implementation
Diffstat (limited to 'src/screens/available_packages.rs')
-rw-r--r--src/screens/available_packages.rs196
1 files changed, 196 insertions, 0 deletions
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<String>) {
+ // 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<crate::signal::Signal> {
+ 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
+ }
+}