diff options
| author | Rubén Beltrán del Río <jj@r.bdr.sh> | 2026-01-16 14:22:21 +0100 |
|---|---|---|
| committer | Rubén Beltrán del Río <jj@r.bdr.sh> | 2026-01-16 14:30:06 +0100 |
| commit | 5a902f8b350f8b2b33c4e19c70f3425802faaa1b (patch) | |
| tree | 968db76028c56a52639fa8af19dd0ef004752812 /src/main.rs | |
| parent | 783d379bfaabe8a0eb6fe368bb0ba47c905b1a80 (diff) | |
Use templates
Diffstat (limited to 'src/main.rs')
| -rw-r--r-- | src/main.rs | 138 |
1 files changed, 110 insertions, 28 deletions
diff --git a/src/main.rs b/src/main.rs index 1b15b22..4ba95c1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,6 +22,7 @@ use crate::icon_names::shipped::{ ZOOM_OUT_REGULAR, }; use cairo::ImageSurface; +use gtk::gio; use gtk::glib; use gtk::prelude::*; use relm4::{ @@ -151,11 +152,34 @@ fn setup_actions(window: gtk::Window, sender: ComponentSender<AppModel>) { ); action_group.register_for_widget(&window); + + // Register new-from-template action with string parameter (template ID) + let template_action = + gio::SimpleAction::new("new-from-template", Some(glib::VariantTy::STRING)); + { + let sender = sender.clone(); + template_action.connect_activate(move |_, param| { + if let Some(variant) = param + && let Some(template_id) = variant.get::<String>() + { + sender.input(Action::NewFromTemplateById(template_id)); + } + }); + } + window.insert_action_group( + "win", + Some(&{ + let group = gio::SimpleActionGroup::new(); + group.add_action(&template_action); + group + }), + ); } struct AppInit { zoom: f64, file_path: Option<PathBuf>, + initial_content: Option<String>, } struct AppModel { @@ -180,6 +204,8 @@ struct AppModel { preferences: preferences::UserPreferences, preferences_window: Option<relm4::Controller<preferences::PreferencesWindow>>, editor_css_provider: gtk::CssProvider, + templates_menu: gio::Menu, + main_menu: gio::Menu, } impl AppModel { @@ -306,11 +332,23 @@ impl AppModel { self.available_stage_types = stage_types; } - pub fn open_new_window(file_path: Option<PathBuf>) { + fn rebuild_templates_menu(&self) { + self.templates_menu.remove_all(); + for template in &self.preferences.map_templates { + let item = gio::MenuItem::new( + Some(&template.name), + Some(&format!("win.new-from-template::{}", template.id)), + ); + self.templates_menu.append_item(&item); + } + } + + pub fn open_new_window(file_path: Option<PathBuf>, initial_content: Option<String>) { let controller = AppModel::builder() .launch(AppInit { zoom: 1.0, file_path, + initial_content, }) .detach(); @@ -374,7 +412,7 @@ impl SimpleComponent for AppModel { gtk::MenuButton { set_icon_name: MENU_LARGE, #[wrap(Some)] - set_popover = >k::PopoverMenu::from_model(Some(&main_menu)) {} + set_popover = >k::PopoverMenu::from_model(Some(&model.main_menu)) {} } } }, @@ -451,30 +489,6 @@ impl SimpleComponent for AppModel { } } - menu! { - main_menu: { - "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, - }, - section! { - "Preferences" => PreferencesAction, - }, - } - } - fn init( init: Self::Init, root: Self::Root, @@ -500,7 +514,7 @@ impl SimpleComponent for AppModel { Err(_) => (String::new(), None), } } else { - (String::new(), None) + (init.initial_content.unwrap_or_default(), None) }; source.set_text(&initial_content); @@ -543,6 +557,43 @@ impl SimpleComponent for AppModel { // Load preferences let prefs = preferences::storage::load(); + let templates_menu = gio::Menu::new(); + + // Populate templates menu from preferences + for template in &prefs.map_templates { + let item = gio::MenuItem::new( + Some(&template.name), + Some(&format!("win.new-from-template::{}", template.id)), + ); + templates_menu.append_item(&item); + } + + // Build main menu + let main_menu = gio::Menu::new(); + main_menu.append(Some("New Map"), Some("window.new")); + main_menu.append_submenu(Some("New from Template"), &templates_menu); + main_menu.append(Some("Open..."), Some("window.open")); + main_menu.append(Some("Save"), Some("window.save")); + main_menu.append(Some("Save As..."), Some("window.save-as")); + main_menu.append(Some("Close"), Some("window.close")); + main_menu.append(Some("Export Map as Image"), Some("window.export-image")); + + let layout_section = gio::Menu::new(); + layout_section.append( + Some("Use Vertical Layout"), + Some("window.change-orientation"), + ); + main_menu.append_section(None, &layout_section); + + let zoom_section = gio::Menu::new(); + zoom_section.append(Some("Zoom In"), Some("window.zoom-in")); + zoom_section.append(Some("Zoom Out"), Some("window.zoom-out")); + main_menu.append_section(None, &zoom_section); + + let prefs_section = gio::Menu::new(); + prefs_section.append(Some("Preferences"), Some("window.preferences")); + main_menu.append_section(None, &prefs_section); + let mut model = AppModel { orientation: gtk::Orientation::Horizontal, stage_type_list, @@ -565,6 +616,8 @@ impl SimpleComponent for AppModel { preferences: prefs, preferences_window: None, editor_css_provider, + templates_menu, + main_menu, }; // Apply preferences and rebuild stage type list @@ -627,7 +680,24 @@ impl SimpleComponent for AppModel { } // File actions - Action::New => Self::open_new_window(None), + Action::New => { + // Use default template if one exists + let content = self + .preferences + .map_templates + .iter() + .find(|t| t.is_default) + .map(|t| t.content.clone()); + Self::open_new_window(None, content); + } + Action::NewFromTemplateById(template_id) => { + if let Ok(id) = uuid::Uuid::parse_str(&template_id) + && let Some(template) = + self.preferences.map_templates.iter().find(|t| t.id == id) + { + Self::open_new_window(None, Some(template.content.clone())); + } + } Action::Open => { dialogs::show_open_dialog(&self.window, self.is_empty_document(), sender) } @@ -671,6 +741,7 @@ impl SimpleComponent for AppModel { self.preferences = prefs; self.apply_preferences(); self.rebuild_stage_type_list(); + self.rebuild_templates_menu(); self.update_image(); // Broadcast to all other windows to reload preferences file_registry::broadcast_to_all_windows(Action::ReloadPreferences); @@ -683,6 +754,7 @@ impl SimpleComponent for AppModel { self.preferences = preferences::storage::load(); self.apply_preferences(); self.rebuild_stage_type_list(); + self.rebuild_templates_menu(); self.update_image(); } } @@ -691,9 +763,19 @@ impl SimpleComponent for AppModel { fn main() { relm4_icons::initialize_icons(icon_names::GRESOURCE_BYTES, icon_names::RESOURCE_PREFIX); + + // Load preferences to get default template for initial window + let prefs = preferences::storage::load(); + let initial_content = prefs + .map_templates + .iter() + .find(|t| t.is_default) + .map(|t| t.content.clone()); + let application = RelmApp::new(constants::APP_ID); application.run::<AppModel>(AppInit { zoom: 1.0, file_path: None, + initial_content, }); } |