From b23f9e14f8912e7c7d72cbdd8cc4d97df8002f7b Mon Sep 17 00:00:00 2001 From: lashman Date: Sat, 28 Feb 2026 01:33:43 +0200 Subject: [PATCH] Replace single NavigationView with ViewSwitcher (Installed, Catalog, Updates) Restructure the app from a single NavigationView into an AdwViewStack with 3 top-level pages: Installed, Catalog, and Updates. Each page keeps its own header bar while an AdwViewSwitcherBar at the bottom provides tab-style navigation between them. The Installed page retains its own NavigationView for detail drill-down. The catalog action now switches the ViewStack tab instead of pushing a page. --- src/window.rs | 79 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 21 deletions(-) diff --git a/src/window.rs b/src/window.rs index 9c56b45..58d0f91 100644 --- a/src/window.rs +++ b/src/window.rs @@ -27,6 +27,7 @@ use crate::ui::library_view::{LibraryState, LibraryView}; use crate::ui::preferences; use crate::ui::security_report; use crate::ui::update_dialog; +use crate::ui::updates_view; use crate::ui::widgets; mod imp { @@ -35,7 +36,8 @@ mod imp { pub struct DriftwoodWindow { pub settings: OnceCell, pub toast_overlay: OnceCell, - pub navigation_view: OnceCell, + pub view_stack: OnceCell, + pub installed_nav: OnceCell, pub library_view: OnceCell, pub database: OnceCell>, pub drop_overlay: OnceCell, @@ -48,7 +50,8 @@ mod imp { Self { settings: OnceCell::new(), toast_overlay: OnceCell::new(), - navigation_view: OnceCell::new(), + view_stack: OnceCell::new(), + installed_nav: OnceCell::new(), library_view: OnceCell::new(), database: OnceCell::new(), drop_overlay: OnceCell::new(), @@ -162,9 +165,41 @@ impl DriftwoodWindow { // Library view (contains header bar, search, grid/list, empty state) let library_view = LibraryView::new(&menu); - // Navigation view - let navigation_view = adw::NavigationView::new(); - navigation_view.push(&library_view.page); + // Installed view: NavigationView for drill-down (detail, dashboard, etc.) + let installed_nav = adw::NavigationView::new(); + installed_nav.push(&library_view.page); + + // Catalog view + let catalog_page = catalog_view::build_catalog_page(self.database()); + + // Updates view + let updates_toolbar = updates_view::build_updates_view(self.database()); + + // ViewStack with 3 top-level pages + let view_stack = adw::ViewStack::new(); + view_stack.set_vexpand(true); + + let installed_vs_page = view_stack.add_titled(&installed_nav, Some("installed"), &i18n("Installed")); + installed_vs_page.set_icon_name(Some("view-grid-symbolic")); + + let catalog_vs_page = view_stack.add_titled(&catalog_page, Some("catalog"), &i18n("Catalog")); + catalog_vs_page.set_icon_name(Some("system-software-install-symbolic")); + + let updates_vs_page = view_stack.add_titled(&updates_toolbar, Some("updates"), &i18n("Updates")); + updates_vs_page.set_icon_name(Some("software-update-available-symbolic")); + + // ViewSwitcherBar at the bottom for tab navigation + let view_switcher_bar = adw::ViewSwitcherBar::builder() + .stack(&view_stack) + .reveal(true) + .build(); + + // Main content box: ViewStack + bottom switcher bar + let main_box = gtk::Box::builder() + .orientation(gtk::Orientation::Vertical) + .build(); + main_box.append(&view_stack); + main_box.append(&view_switcher_bar); // Drop overlay - centered opaque card over a dimmed scrim let drop_overlay_icon = gtk::Image::builder() @@ -253,9 +288,9 @@ impl DriftwoodWindow { drop_overlay_content.add_controller(click); } - // Overlay wraps navigation view so the drop indicator sits on top + // Overlay wraps main content so the drop indicator sits on top let overlay = gtk::Overlay::new(); - overlay.set_child(Some(&navigation_view)); + overlay.set_child(Some(&main_box)); overlay.add_overlay(&drop_overlay_content); // Toast overlay wraps the overlay @@ -353,7 +388,7 @@ impl DriftwoodWindow { // Wire up card/row activation to push detail view (or toggle selection) { - let nav = navigation_view.clone(); + let nav = installed_nav.clone(); let db = self.database().clone(); let window_weak = self.downgrade(); library_view.connect_grid_activated(move |record_id| { @@ -371,7 +406,7 @@ impl DriftwoodWindow { }); } { - let nav = navigation_view.clone(); + let nav = installed_nav.clone(); let db = self.database().clone(); let window_weak = self.downgrade(); library_view.connect_list_activated(move |record_id| { @@ -394,7 +429,7 @@ impl DriftwoodWindow { { let db = self.database().clone(); let window_weak = self.downgrade(); - navigation_view.connect_popped(move |_nav, page| { + installed_nav.connect_popped(move |_nav, page| { if page.tag().as_deref() == Some("detail") { if let Some(window) = window_weak.upgrade() { // Update window title for accessibility (WCAG 2.4.8) @@ -413,7 +448,7 @@ impl DriftwoodWindow { // Update window title when navigating to sub-pages (WCAG 2.4.8 Location) { let window_weak = self.downgrade(); - navigation_view.connect_pushed(move |nav| { + installed_nav.connect_pushed(move |nav| { if let Some(window) = window_weak.upgrade() { if let Some(page) = nav.visible_page() { let page_title = page.title(); @@ -439,9 +474,13 @@ impl DriftwoodWindow { .set(toast_overlay) .expect("ToastOverlay already set"); self.imp() - .navigation_view - .set(navigation_view) - .expect("NavigationView already set"); + .view_stack + .set(view_stack) + .expect("ViewStack already set"); + self.imp() + .installed_nav + .set(installed_nav) + .expect("InstalledNav already set"); if self.imp().library_view.set(library_view).is_err() { panic!("LibraryView already set"); } @@ -454,7 +493,7 @@ impl DriftwoodWindow { let dashboard_action = gio::ActionEntry::builder("dashboard") .activate(|window: &Self, _, _| { let db = window.database().clone(); - let nav = window.imp().navigation_view.get().unwrap(); + let nav = window.imp().installed_nav.get().unwrap(); let page = dashboard::build_dashboard_page(&db); nav.push(&page); }) @@ -554,7 +593,7 @@ impl DriftwoodWindow { let security_report_action = gio::ActionEntry::builder("security-report") .activate(|window: &Self, _, _| { let db = window.database().clone(); - let nav = window.imp().navigation_view.get().unwrap(); + let nav = window.imp().installed_nav.get().unwrap(); let page = security_report::build_security_report_page(&db); nav.push(&page); }) @@ -568,13 +607,11 @@ impl DriftwoodWindow { }) .build(); - // Catalog browser action + // Catalog browser action - switches to catalog tab let catalog_action = gio::ActionEntry::builder("catalog") .activate(|window: &Self, _, _| { - let db = window.database().clone(); - let catalog_page = catalog_view::build_catalog_page(&db); - let nav = window.imp().navigation_view.get().unwrap(); - nav.push(&catalog_page); + let view_stack = window.imp().view_stack.get().unwrap(); + view_stack.set_visible_child_name("catalog"); }) .build();