aboutsummaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
authorRubén Beltrán del Río <jj@r.bdr.sh>2025-12-28 15:33:43 +0100
committerRubén Beltrán del Río <jj@r.bdr.sh>2025-12-29 16:10:08 +0100
commit727dd5db0a123e57e3873a90705a90c849b95b77 (patch)
tree2594f1eaffacb82319e8aa15cd0706cb5c253c55 /src/main.rs
parentfa29265150bbbcf0b45616884d995e27cfa15763 (diff)
Add actions
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs170
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 = &gtk::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 = &gtk::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 = &gtk::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");