diff options
| author | Rubén Beltrán del Río <jj@r.bdr.sh> | 2025-12-28 15:33:43 +0100 |
|---|---|---|
| committer | Rubén Beltrán del Río <jj@r.bdr.sh> | 2025-12-29 16:10:08 +0100 |
| commit | 727dd5db0a123e57e3873a90705a90c849b95b77 (patch) | |
| tree | 2594f1eaffacb82319e8aa15cd0706cb5c253c55 /src/main.rs | |
| parent | fa29265150bbbcf0b45616884d995e27cfa15763 (diff) | |
Add actions
Diffstat (limited to 'src/main.rs')
| -rw-r--r-- | src/main.rs | 170 |
1 files changed, 134 insertions, 36 deletions
diff --git a/src/main.rs b/src/main.rs index 1bf0e71..5539caf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,13 +6,12 @@ mod icon_names { } use crate::icon_names::shipped::{ - IMAGE_REGULAR, PLUS, SPLIT_HORIZONTAL_REGULAR, SPLIT_VERTICAL_REGULAR, ZOOM_IN_REGULAR, - ZOOM_OUT_REGULAR, + IMAGE_REGULAR, MENU_LARGE, SPLIT_HORIZONTAL_REGULAR, + SPLIT_VERTICAL_REGULAR, ZOOM_IN_REGULAR, ZOOM_OUT_REGULAR, }; use cairo::ImageSurface; use gtk::prelude::*; -use gtk::{gdk, glib}; -use relm4::{ComponentParts, ComponentSender, RelmApp, RelmWidgetExt, SimpleComponent, gtk}; +use relm4::{ComponentParts, ComponentSender, RelmApp, RelmWidgetExt, SimpleComponent, gtk, actions::{AccelsPlus, ActionablePlus, RelmAction, RelmActionGroup}}; use sourceview5::LanguageManager; use sourceview5::prelude::{BufferExt, TextBufferExt, TextViewExt}; use stages::all_stages; @@ -68,6 +67,75 @@ impl AppModel { self.drawing_area.queue_draw(); } } + + /* + * Sets up actions and keybinds / accelerators. Buttons and menu items + * should all use actions. + */ + fn setup_actions(root: gtk::Window, sender: ComponentSender<AppModel>) { + let app = relm4::main_application(); + let mut action_group = RelmActionGroup::<WindowActionGroup>::new(); + + let cloned_sender = sender.clone(); + let zoom_in: RelmAction<ZoomInAction> = RelmAction::new_stateless(move |_| { + cloned_sender.input(AppAction::ZoomIn); + }); + action_group.add_action(zoom_in); + app.set_accelerators_for_action::<ZoomInAction>(&["<Primary>equal", "<Primary>plus"]); + + let cloned_sender = sender.clone(); + let zoom_out: RelmAction<ZoomOutAction> = RelmAction::new_stateless(move |_| { + cloned_sender.input(AppAction::ZoomOut); + }); + app.set_accelerators_for_action::<ZoomOutAction>(&["<Primary>minus"]); + action_group.add_action(zoom_out); + + let cloned_sender = sender.clone(); + let change_orientation: RelmAction<ChangeOrientationAction> = RelmAction::new_stateless(move |_| { + cloned_sender.input(AppAction::ChangeOrientation); + }); + app.set_accelerators_for_action::<ChangeOrientationAction>(&["<Primary>l"]); + action_group.add_action(change_orientation); + + let cloned_sender = sender.clone(); + let export_image: RelmAction<ExportImageAction> = RelmAction::new_stateless(move |_| { + cloned_sender.input(AppAction::ExportImage); + }); + app.set_accelerators_for_action::<ExportImageAction>(&["<Primary>e"]); + action_group.add_action(export_image); + + let cloned_sender = sender.clone(); + let new: RelmAction<NewAction> = RelmAction::new_stateless(move |_| { + println!("Not yet implemented!"); + }); + app.set_accelerators_for_action::<NewAction>(&["<Primary>n"]); + action_group.add_action(new); + + let cloned_sender = sender.clone(); + let save: RelmAction<SaveAction> = RelmAction::new_stateless(move |_| { + println!("Not yet implemented!"); + }); + app.set_accelerators_for_action::<SaveAction>(&["<Primary>s"]); + action_group.add_action(save); + + let cloned_sender = sender.clone(); + let save_as: RelmAction<SaveAsAction> = RelmAction::new_stateless(move |_| { + println!("Not yet implemented!"); + }); + app.set_accelerators_for_action::<SaveAsAction>(&["<Primary><Shift>s"]); + action_group.add_action(save_as); + + let cloned_sender = sender.clone(); + let open: RelmAction<OpenAction> = RelmAction::new_stateless(move |_| { + println!("Not yet implemented!"); + }); + app.set_accelerators_for_action::<OpenAction>(&["<Primary>o"]); + action_group.add_action(open); + + app.set_accelerators_for_action::<CloseAction>(&["<Primary>w"]); + + action_group.register_for_widget(&root); + } } #[relm4::component] @@ -80,35 +148,12 @@ impl SimpleComponent for AppModel { type Init = f64; view! { + #[root] gtk::Window { set_title: Some("Map"), set_default_width: 300, set_default_height: 100, - add_controller = gtk::EventControllerKey::new() { - connect_key_pressed[sender] => move |_, key, _, modifier| { - match (key, modifier) { - (gdk::Key::plus | gdk::Key::equal, gdk::ModifierType::CONTROL_MASK) => { - sender.input(AppAction::ZoomIn); - glib::Propagation::Stop - }, - (gdk::Key::minus, gdk::ModifierType::CONTROL_MASK) => { - sender.input(AppAction::ZoomOut); - glib::Propagation::Stop - }, - (gdk::Key::l, gdk::ModifierType::CONTROL_MASK) => { - sender.input(AppAction::ChangeOrientation); - glib::Propagation::Stop - }, - (gdk::Key::e, gdk::ModifierType::CONTROL_MASK) => { - sender.input(AppAction::ExportImage); - glib::Propagation::Stop - }, - _ => glib::Propagation::Proceed - } - } - }, - gtk::Box { set_orientation: gtk::Orientation::Vertical, set_spacing: 0, @@ -117,16 +162,13 @@ impl SimpleComponent for AppModel { pack_start = >k::Box { set_orientation: gtk::Orientation::Horizontal, - gtk::Button::with_label("New") { - set_icon_name: PLUS - }, gtk::Button::with_label("Change Orientation") { #[watch] set_icon_name: match model.orientation { gtk::Orientation::Horizontal => SPLIT_VERTICAL_REGULAR, _ => SPLIT_HORIZONTAL_REGULAR }, - connect_clicked => AppAction::ChangeOrientation, + ActionablePlus::set_stateless_action::<ChangeOrientationAction>: &(), } }, pack_end = >k::Box { @@ -134,7 +176,7 @@ impl SimpleComponent for AppModel { gtk::Button::with_label("Export as Image") { set_icon_name: IMAGE_REGULAR, - connect_clicked => AppAction::ExportImage + ActionablePlus::set_stateless_action::<ExportImageAction>: &(), }, gtk::DropDown { @@ -145,8 +187,14 @@ impl SimpleComponent for AppModel { let selected = dropdown.selected(); sender.input(AppAction::StageTypeSelected(selected as usize)); }, + }, + gtk::MenuButton { + set_icon_name: MENU_LARGE, + #[wrap(Some)] + set_popover = >k::PopoverMenu::from_model(Some(&main_menu)) { + add_child: (&popover_child, "my_widget"), + } } - } }, @@ -192,7 +240,7 @@ impl SimpleComponent for AppModel { add_css_class: "flat", add_css_class: "zoom", add_css_class: "zoom-out", - connect_clicked => AppAction::ZoomOut + ActionablePlus::set_stateless_action::<ZoomOutAction>: &(), }, gtk::Scale { @@ -214,11 +262,36 @@ impl SimpleComponent for AppModel { add_css_class: "flat", add_css_class: "zoom", add_css_class: "zoom-in", - connect_clicked => AppAction::ZoomIn + ActionablePlus::set_stateless_action::<ZoomInAction>: &(), }, } } } + }, + popover_child = gtk::Spinner { + set_spinning: true, + } + } + + menu! { + main_menu: { + custom: "Map", + "New Map" => NewAction, + "Open..." => OpenAction, + "Save" => SaveAction, + "Save As..." => SaveAsAction, + "Close" => CloseAction, + "Export Map as Image" => ExportImageAction, + section!{ + match model.orientation { + gtk::Orientation::Horizontal => "Use Vertical Layout", + _ => "Use Horizontal Layout" + } => ChangeOrientationAction, + }, + section! { + "Zoom In" => ZoomInAction, + "Zoom Out" => ZoomOutAction, + }, } } @@ -248,6 +321,10 @@ impl SimpleComponent for AppModel { if let Some(language) = language_manager.language("wmap") { source.set_language(Some(&language)); } + /* Attach Actions ----------------------------------------------------*/ + AppModel::setup_actions(root.clone(), sender.clone()); + + /* Initialize Model --------------------------------------------------*/ let drawing_area = gtk::DrawingArea::new(); let map = parse(""); @@ -320,6 +397,27 @@ impl SimpleComponent for AppModel { } } +/* ACTIONS ********************************************************************/ + +relm4::new_action_group!(WindowActionGroup, "window"); + +/* Map File */ +relm4::new_stateless_action!(NewAction, WindowActionGroup, "new"); +relm4::new_stateless_action!(OpenAction, WindowActionGroup, "open"); +relm4::new_stateless_action!(SaveAction, WindowActionGroup, "save"); +relm4::new_stateless_action!(SaveAsAction, WindowActionGroup, "save-as"); +relm4::new_stateless_action!(CloseAction, WindowActionGroup, "close"); +relm4::new_stateless_action!(ExportImageAction, WindowActionGroup, "export-image"); + +/* Layout */ +relm4::new_stateless_action!(ChangeOrientationAction, WindowActionGroup, "change-orientation"); + +/* Map View */ +relm4::new_stateless_action!(ZoomInAction, WindowActionGroup, "zoom-in"); +relm4::new_stateless_action!(ZoomOutAction, WindowActionGroup, "zoom-out"); + +/* MAIN ***********************************************************************/ + fn main() { relm4_icons::initialize_icons(icon_names::GRESOURCE_BYTES, icon_names::RESOURCE_PREFIX); let app = RelmApp::new("relm4.test.simple_manual"); |