use adw::prelude::*; use outlay_core::db::Database; use std::rc::Rc; use crate::budgets_view::BudgetsView; use crate::charts_view::ChartsView; use crate::history_view::HistoryView; use crate::log_view::LogView; pub struct MainWindow { pub window: adw::ApplicationWindow, pub split_view: adw::NavigationSplitView, pub content_stack: gtk::Stack, pub log_view: LogView, } struct SidebarItem { id: &'static str, label: &'static str, icon: &'static str, } const SIDEBAR_ITEMS: &[SidebarItem] = &[ SidebarItem { id: "log", label: "Log", icon: "list-add-symbolic" }, SidebarItem { id: "history", label: "History", icon: "document-open-recent-symbolic" }, SidebarItem { id: "charts", label: "Charts", icon: "utilities-system-monitor-symbolic" }, SidebarItem { id: "budgets", label: "Budgets", icon: "wallet2-symbolic" }, SidebarItem { id: "recurring", label: "Recurring", icon: "view-refresh-symbolic" }, SidebarItem { id: "settings", label: "Settings", icon: "emblem-system-symbolic" }, ]; impl MainWindow { pub fn new(app: &adw::Application, db: Rc) -> Self { let content_stack = gtk::Stack::new(); content_stack.set_transition_type(gtk::StackTransitionType::Crossfade); // Log view let log_view = LogView::new(db.clone(), app); let log_scroll = gtk::ScrolledWindow::builder() .hscrollbar_policy(gtk::PolicyType::Never) .child(&log_view.container) .build(); content_stack.add_named(&log_scroll, Some("log")); // History view let history_view = HistoryView::new(db.clone()); let history_scroll = gtk::ScrolledWindow::builder() .hscrollbar_policy(gtk::PolicyType::Never) .child(&history_view.container) .build(); content_stack.add_named(&history_scroll, Some("history")); // Charts view let charts_view = ChartsView::new(db.clone()); content_stack.add_named(&charts_view.container, Some("charts")); // Budgets view let budgets_view = BudgetsView::new(db.clone()); content_stack.add_named(&budgets_view.container, Some("budgets")); // Remaining pages are placeholders for now for item in &SIDEBAR_ITEMS[4..] { let page = adw::StatusPage::builder() .title(item.label) .icon_name(item.icon) .build(); content_stack.add_named(&page, Some(item.id)); } let sidebar_list = gtk::ListBox::new(); sidebar_list.set_selection_mode(gtk::SelectionMode::Single); sidebar_list.add_css_class("navigation-sidebar"); for item in SIDEBAR_ITEMS { let row = Self::make_sidebar_row(item); sidebar_list.append(&row); } let content_stack_ref = content_stack.clone(); sidebar_list.connect_row_selected(move |_, row| { if let Some(row) = row { let idx = row.index() as usize; if idx < SIDEBAR_ITEMS.len() { content_stack_ref.set_visible_child_name(SIDEBAR_ITEMS[idx].id); } } }); // Select the first row by default if let Some(first_row) = sidebar_list.row_at_index(0) { sidebar_list.select_row(Some(&first_row)); } let sidebar_scroll = gtk::ScrolledWindow::builder() .hscrollbar_policy(gtk::PolicyType::Never) .vexpand(true) .child(&sidebar_list) .build(); let sidebar_toolbar = adw::ToolbarView::new(); sidebar_toolbar.add_top_bar(&adw::HeaderBar::new()); sidebar_toolbar.set_content(Some(&sidebar_scroll)); let sidebar_page = adw::NavigationPage::builder() .title("Outlay") .child(&sidebar_toolbar) .build(); let content_toolbar = adw::ToolbarView::new(); content_toolbar.add_top_bar(&adw::HeaderBar::new()); content_toolbar.set_content(Some(&content_stack)); let content_page = adw::NavigationPage::builder() .title("Outlay") .child(&content_toolbar) .build(); let split_view = adw::NavigationSplitView::new(); split_view.set_sidebar(Some(&sidebar_page)); split_view.set_content(Some(&content_page)); let window = adw::ApplicationWindow::builder() .application(app) .title("Outlay") .default_width(900) .default_height(600) .content(&split_view) .build(); MainWindow { window, split_view, content_stack, log_view, } } fn make_sidebar_row(item: &SidebarItem) -> gtk::ListBoxRow { let hbox = gtk::Box::new(gtk::Orientation::Horizontal, 12); hbox.set_margin_top(8); hbox.set_margin_bottom(8); hbox.set_margin_start(12); hbox.set_margin_end(12); let icon = gtk::Image::from_icon_name(item.icon); let label = gtk::Label::new(Some(item.label)); label.set_halign(gtk::Align::Start); hbox.append(&icon); hbox.append(&label); let row = gtk::ListBoxRow::new(); row.set_child(Some(&hbox)); row } }