aboutsummaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
authorRubén Beltrán del Río <jj@r.bdr.sh>2026-01-16 14:22:21 +0100
committerRubén Beltrán del Río <jj@r.bdr.sh>2026-01-16 14:30:06 +0100
commit5a902f8b350f8b2b33c4e19c70f3425802faaa1b (patch)
tree968db76028c56a52639fa8af19dd0ef004752812 /src/main.rs
parent783d379bfaabe8a0eb6fe368bb0ba47c905b1a80 (diff)
Use templates
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs138
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 = &gtk::PopoverMenu::from_model(Some(&main_menu)) {}
+ set_popover = &gtk::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,
});
}