diff options
Diffstat (limited to 'src/main.rs')
| -rw-r--r-- | src/main.rs | 278 |
1 files changed, 137 insertions, 141 deletions
diff --git a/src/main.rs b/src/main.rs index 4a659e9..30f2381 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ mod actions; mod constants; mod dialogs; mod file_registry; +mod handlers; mod stages; mod icon_names { @@ -17,8 +18,8 @@ use crate::icon_names::shipped::{ ZOOM_OUT_REGULAR, }; use cairo::ImageSurface; -use gtk::prelude::*; use gtk::glib; +use gtk::prelude::*; use relm4::{ Component, ComponentController, ComponentParts, ComponentSender, RelmApp, RelmWidgetExt, SimpleComponent, @@ -27,7 +28,7 @@ use relm4::{ }; use sourceview5::LanguageManager; use sourceview5::prelude::{BufferExt, TextBufferExt, TextViewExt}; -use stages::all_stages; +use stages::ALL_STAGE_TYPES; use wmap_parser::{Map, parse}; use wmap_renderer::{Configuration, StageType, render_to_surface}; @@ -39,7 +40,11 @@ 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"); -relm4::new_stateless_action!(ChangeOrientationAction, WindowActionGroup, "change-orientation"); +relm4::new_stateless_action!( + ChangeOrientationAction, + WindowActionGroup, + "change-orientation" +); relm4::new_stateless_action!(ZoomInAction, WindowActionGroup, "zoom-in"); relm4::new_stateless_action!(ZoomOutAction, WindowActionGroup, "zoom-out"); @@ -59,15 +64,78 @@ fn setup_actions(window: gtk::Window, sender: ComponentSender<AppModel>) { let application = relm4::main_application(); let mut action_group = RelmActionGroup::<WindowActionGroup>::new(); - register_action!(action_group, application, sender, ZoomInAction, Action::ZoomIn, &["<Primary>equal", "<Primary>plus"]); - register_action!(action_group, application, sender, ZoomOutAction, Action::ZoomOut, &["<Primary>minus"]); - register_action!(action_group, application, sender, ChangeOrientationAction, Action::ChangeOrientation, &["<Primary>l"]); - register_action!(action_group, application, sender, ExportImageAction, Action::ExportImage, &["<Primary>e"]); - register_action!(action_group, application, sender, NewAction, Action::New, &["<Primary>n"]); - register_action!(action_group, application, sender, SaveAction, Action::Save { close_after: false }, &["<Primary>s"]); - register_action!(action_group, application, sender, SaveAsAction, Action::SaveAs { close_after: false }, &["<Primary><Shift>s"]); - register_action!(action_group, application, sender, OpenAction, Action::Open, &["<Primary>o"]); - register_action!(action_group, application, sender, CloseAction, Action::CloseWindow, &["<Primary>w"]); + register_action!( + action_group, + application, + sender, + ZoomInAction, + Action::ZoomIn, + &["<Primary>equal", "<Primary>plus"] + ); + register_action!( + action_group, + application, + sender, + ZoomOutAction, + Action::ZoomOut, + &["<Primary>minus"] + ); + register_action!( + action_group, + application, + sender, + ChangeOrientationAction, + Action::ChangeOrientation, + &["<Primary>l"] + ); + register_action!( + action_group, + application, + sender, + ExportImageAction, + Action::ExportImage, + &["<Primary>e"] + ); + register_action!( + action_group, + application, + sender, + NewAction, + Action::New, + &["<Primary>n"] + ); + register_action!( + action_group, + application, + sender, + SaveAction, + Action::Save { close_after: false }, + &["<Primary>s"] + ); + register_action!( + action_group, + application, + sender, + SaveAsAction, + Action::SaveAs { close_after: false }, + &["<Primary><Shift>s"] + ); + register_action!( + action_group, + application, + sender, + OpenAction, + Action::Open, + &["<Primary>o"] + ); + register_action!( + action_group, + application, + sender, + CloseAction, + Action::CloseWindow, + &["<Primary>w"] + ); action_group.register_for_widget(&window); } @@ -177,8 +245,8 @@ impl SimpleComponent for AppModel { gtk::Window { #[watch] set_title: Some(&model.window_title()), - set_default_width: 300, - set_default_height: 100, + set_default_width: constants::DEFAULT_WINDOW_WIDTH, + set_default_height: constants::DEFAULT_WINDOW_HEIGHT, gtk::Box { set_orientation: gtk::Orientation::Vertical, @@ -320,7 +388,7 @@ impl SimpleComponent for AppModel { root: Self::Root, sender: ComponentSender<Self>, ) -> ComponentParts<Self> { - let stage_types: Vec<&str> = all_stages() + let stage_types: Vec<&str> = ALL_STAGE_TYPES .iter() .map(|stage_type| stage_type.name()) .collect(); @@ -345,10 +413,12 @@ impl SimpleComponent for AppModel { source.set_text(&initial_content); - let sender_for_source = sender.clone(); - source.connect_changed(move |_| { - sender_for_source.input(Action::SourceChanged); - }); + { + let sender = sender.clone(); + source.connect_changed(move |_| { + sender.input(Action::SourceChanged); + }); + } setup_actions(root.clone(), sender.clone()); @@ -387,16 +457,20 @@ impl SimpleComponent for AppModel { }; model.update_image(); - let sender_for_init = sender.clone(); - glib::idle_add_local_once(move || { - sender_for_init.input(Action::MarkInitialized); - }); + { + let sender = sender.clone(); + glib::idle_add_local_once(move || { + sender.input(Action::MarkInitialized); + }); + } - let sender_for_close = sender.clone(); - root.connect_close_request(move |_| { - sender_for_close.input(Action::CloseWindow); - glib::Propagation::Stop - }); + { + let sender = sender.clone(); + root.connect_close_request(move |_| { + sender.input(Action::CloseWindow); + glib::Propagation::Stop + }); + } let widgets = view_output!(); @@ -405,19 +479,11 @@ impl SimpleComponent for AppModel { fn update(&mut self, message: Self::Input, sender: ComponentSender<Self>) { match message { - Action::ChangeOrientation => { - self.orientation = match self.orientation { - gtk::Orientation::Horizontal => gtk::Orientation::Vertical, - _ => gtk::Orientation::Horizontal, - } - } - Action::StageTypeSelected(index) => { - let all_stages = all_stages(); - if index < all_stages.len() { - self.stage_type = all_stages[index]; - } - self.update_image(); - } + // View actions + Action::ChangeOrientation => handlers::view::change_orientation(self), + Action::StageTypeSelected(index) => handlers::view::stage_type_selected(self, index), + + // Source changes Action::SourceChanged => { let pending = *self.pending_load_events.borrow(); if pending > 0 { @@ -428,117 +494,47 @@ impl SimpleComponent for AppModel { self.map = parse(&self.source_text()); self.update_image(); } - Action::Zoom(new_zoom) => { - self.zoom = new_zoom; - self.update_image(); - } - Action::ZoomOut => { - if self.zoom - constants::ZOOM_STEP >= constants::MIN_ZOOM { - self.zoom -= constants::ZOOM_STEP; - self.update_image(); - } - } - Action::ZoomIn => { - if self.zoom < constants::MAX_ZOOM { - self.zoom += constants::ZOOM_STEP; - self.update_image(); - } - } - Action::ExportImage => {} - Action::New => { - Self::open_new_window(None); - } - Action::Open => { - dialogs::show_open_dialog(&self.window, self.is_empty_document(), sender); - } - Action::Save { close_after } => { - if let Some(path) = self.current_file.clone() { - sender.input(Action::SaveToFile { path, close_after }); - } else { - sender.input(Action::SaveAs { close_after }); - } - } - Action::SaveAs { close_after } => { - dialogs::show_save_dialog( - &self.window, - self.current_file.clone(), - close_after, - sender, - ); - } - Action::LoadFile(path) => { - match std::fs::read_to_string(&path) { - Ok(content) => { - if let Some(ref old_path) = self.current_file { - file_registry::unregister_file(old_path); - } - self.set_source_text(&content); - file_registry::register_file(&path, &self.window); - self.current_file = Some(path); - self.modified = false; - } - Err(error) => { - dialogs::show_error(&self.window, "Error Opening File", &error.to_string()); - } - } - } - Action::SaveToFile { path, close_after } => { - let content = self.source_text(); - let path = if path.extension().is_none() - || path.extension().unwrap_or_default() != constants::FILE_EXTENSION - { - path.with_extension(constants::FILE_EXTENSION) - } else { - path - }; + // Zoom actions + Action::Zoom(level) => handlers::zoom::zoom(self, level), + Action::ZoomIn => handlers::zoom::zoom_in(self), + Action::ZoomOut => handlers::zoom::zoom_out(self), - match std::fs::write(&path, content.as_str()) { - Ok(()) => { - if close_after { - file_registry::unregister_file(&path); - self.modified = false; - self.window.close(); - } else { - if self.current_file.as_ref() != Some(&path) { - if let Some(ref old_path) = self.current_file { - file_registry::unregister_file(old_path); - } - file_registry::register_file(&path, &self.window); - } - self.current_file = Some(path); - self.modified = false; - } - } - Err(error) => { - dialogs::show_error(&self.window, "Error Saving File", &error.to_string()); - } - } + // Export (not yet implemented) + Action::ExportImage => { + // TODO: Implement image export functionality + // - Show save dialog with PNG/SVG format picker + // - Default filename: current file name with .png or .svg extension + // - Call renderer's render_to_png() or render_to_svg() based on selection } - Action::MarkInitialized => { - self.initialized = true; + + // File actions + Action::New => Self::open_new_window(None), + Action::Open => { + dialogs::show_open_dialog(&self.window, self.is_empty_document(), sender) } - Action::CloseWindow => { - if self.modified { - dialogs::show_unsaved_changes_dialog( - &self.window, - self.current_file.clone(), - sender, - ); - } else { - if let Some(ref path) = self.current_file { - file_registry::unregister_file(path); - } - self.window.destroy(); - } + Action::Save { close_after } => handlers::file::save(self, close_after, &sender), + Action::SaveAs { close_after } => dialogs::show_save_dialog( + &self.window, + self.current_file.clone(), + close_after, + sender, + ), + Action::LoadFile(path) => handlers::file::load_file(self, path), + Action::SaveToFile { path, close_after } => { + handlers::file::save_to_file(self, path, close_after) } + Action::CloseWindow => handlers::file::close_window(self, &sender), + + // Internal actions + Action::MarkInitialized => self.initialized = true, } } } fn main() { relm4_icons::initialize_icons(icon_names::GRESOURCE_BYTES, icon_names::RESOURCE_PREFIX); - let application = RelmApp::new("systems.tranquil.map"); + let application = RelmApp::new(constants::APP_ID); application.run::<AppModel>(AppInit { zoom: 1.0, file_path: None, |