use crate::RelmActionGroup; use relm4::*; use relm4::actions::RelmAction; use adw::prelude::*; use gtk::{gio, glib}; use crate::config::{APP_ID, PROFILE}; use crate::modals::{ about::AboutDialog, bluetooth::BluetoothModel, network::NetworkModel, wifi::WifiModel, }; use std::convert::identity; pub(super) struct App { _wifi: Controller, _network: Controller, _bluetooth: Controller, } #[derive(Debug)] pub enum AppMsg { Quit, } relm4::new_action_group!(pub(super) WindowActionGroup, "win"); relm4::new_stateless_action!(PreferencesAction, WindowActionGroup, "preferences"); relm4::new_stateless_action!(pub(super) ShortcutsAction, WindowActionGroup, "show-help-overlay"); relm4::new_stateless_action!(AboutAction, WindowActionGroup, "about"); #[relm4::component(pub)] impl SimpleComponent for App { type Init = (); type Input = AppMsg; type Output = (); type Widgets = AppWidgets; menu! { primary_menu: { section! { "_Preferences" => PreferencesAction, "_Keyboard" => ShortcutsAction, "_About GTK Rust Template" => AboutAction, } } } view! { #[root] main_window = adw::ApplicationWindow::new(&main_application()) { set_visible: true, connect_close_request[sender] => move |_| { sender.input(AppMsg::Quit); glib::Propagation::Stop }, #[wrap(Some)] set_help_overlay: shortcuts = >k::Builder::from_resource( "/net/bleur/GtkRustTemplate/gtk/help-overlay.ui" ) .object::("help_overlay") .unwrap() -> gtk::ShortcutsWindow { set_transient_for: Some(&main_window), set_application: Some(&main_application()), }, add_css_class?: if PROFILE == "Devel" { Some("devel") } else { None }, #[name(split_view)] adw::NavigationSplitView { #[wrap(Some)] set_sidebar = &adw::NavigationPage { set_title: "Settings", #[wrap(Some)] set_child = &adw::ToolbarView { add_top_bar = &adw::HeaderBar { pack_end = >k::MenuButton { set_icon_name: "open-menu-symbolic", set_menu_model: Some(&primary_menu), } }, #[wrap(Some)] set_content = >k::StackSidebar { set_stack: &stack, }, }, }, #[wrap(Some)] set_content = &adw::NavigationPage { // set_title: "Content", #[wrap(Some)] set_child = &adw::ToolbarView { // add_top_bar = &adw::HeaderBar {}, set_content: Some(&stack), } }, }, add_breakpoint = bp_with_setters( adw::Breakpoint::new( adw::BreakpointCondition::new_length( adw::BreakpointConditionLengthType::MaxWidth, 400.0, adw::LengthUnit::Sp, ) ), &[(&split_view, "collapsed", true)] ), }, stack = >k::Stack { add_titled: (wifi.widget(), Some("wifi"), "Wi-Fi"), add_titled: (network.widget(), Some("network"), "Network"), add_titled: (bluetooth.widget(), Some("bluetooth"), "Bluetooth"), set_vhomogeneous: false, } } fn init( _init: Self::Init, root: Self::Root, sender: ComponentSender, ) -> ComponentParts { let wifi = WifiModel::builder() .launch(()) .forward(sender.input_sender(), identity); let network = NetworkModel::builder() .launch(()) .forward(sender.input_sender(), identity); let bluetooth = BluetoothModel::builder() .launch(()) .forward(sender.input_sender(), identity); let widgets = view_output!(); // Set icon names for stack pages widgets .stack .page(wifi.widget()) .set_property("icon-name", "network-wireless-symbolic"); widgets .stack .page(network.widget()) .set_property("icon-name", "network-wired-symbolic"); widgets .stack .page(bluetooth.widget()) .set_property("icon-name", "bluetooth-symbolic"); let model = App { _wifi: wifi, _network: network, _bluetooth: bluetooth, }; widgets.stack.connect_visible_child_notify({ let split_view = widgets.split_view.clone(); move |_| { split_view.set_show_content(true); } }); let mut actions = RelmActionGroup::::new(); let shortcuts_action = { let shortcuts = widgets.shortcuts.clone(); RelmAction::::new_stateless(move |_| { shortcuts.present(); }) }; let about_action = { RelmAction::::new_stateless(move |_| { AboutDialog::builder().launch(()).detach(); }) }; actions.add_action(shortcuts_action); actions.add_action(about_action); actions.register_for_widget(&widgets.main_window); widgets.load_window_size(); ComponentParts { model, widgets } } fn update(&mut self, message: Self::Input, _sender: ComponentSender) { match message { AppMsg::Quit => main_application().quit(), } } fn shutdown(&mut self, widgets: &mut Self::Widgets, _output: relm4::Sender) { widgets.save_window_size().unwrap(); } } fn bp_with_setters( bp: adw::Breakpoint, additions: &[(&impl IsA, &str, impl ToValue)], ) -> adw::Breakpoint { bp.add_setters(additions); bp } impl AppWidgets { fn save_window_size(&self) -> Result<(), glib::BoolError> { let settings = gio::Settings::new(APP_ID); let (width, height) = self.main_window.default_size(); settings.set_int("window-width", width)?; settings.set_int("window-height", height)?; settings.set_boolean("is-maximized", self.main_window.is_maximized())?; Ok(()) } fn load_window_size(&self) { let settings = gio::Settings::new(APP_ID); let width = settings.int("window-width"); let height = settings.int("window-height"); let is_maximized = settings.boolean("is-maximized"); self.main_window.set_default_size(width, height); if is_maximized { self.main_window.maximize(); } } }