From 04b1e6080d99601b8d6343be3140f545cd8f9eae Mon Sep 17 00:00:00 2001 From: Rubén Beltrán del Río Date: Fri, 16 Jan 2026 00:08:41 +0100 Subject: Separate concerns --- src/handlers/file.rs | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/handlers/mod.rs | 3 ++ src/handlers/view.rs | 20 +++++++++++ src/handlers/zoom.rs | 25 +++++++++++++ 4 files changed, 148 insertions(+) create mode 100644 src/handlers/file.rs create mode 100644 src/handlers/mod.rs create mode 100644 src/handlers/view.rs create mode 100644 src/handlers/zoom.rs (limited to 'src/handlers') diff --git a/src/handlers/file.rs b/src/handlers/file.rs new file mode 100644 index 0000000..55eaffa --- /dev/null +++ b/src/handlers/file.rs @@ -0,0 +1,100 @@ +use std::path::PathBuf; + +use gtk::prelude::*; +use relm4::{ComponentSender, gtk}; + +use crate::AppModel; +use crate::actions::Action; +use crate::constants; +use crate::dialogs; +use crate::file_registry; + +/// Loads a file into the current document, updating the file registry. +pub fn load_file(model: &mut AppModel, path: PathBuf) { + match std::fs::read_to_string(&path) { + Ok(content) => { + unregister_current_file(model); + model.set_source_text(&content); + file_registry::register_file(&path, &model.window); + model.current_file = Some(path); + model.modified = false; + } + Err(error) => { + dialogs::show_error(&model.window, "Error Opening File", &error.to_string()); + } + } +} + +/// Saves the document to the specified path, optionally closing after. +pub fn save_to_file(model: &mut AppModel, path: PathBuf, close_after: bool) { + let content = model.source_text(); + let path = ensure_wmap_extension(path); + + match std::fs::write(&path, content.as_str()) { + Ok(()) => { + if close_after { + file_registry::unregister_file(&path); + model.modified = false; + model.window.close(); + } else { + update_file_registration(model, &path); + model.current_file = Some(path); + model.modified = false; + } + } + Err(error) => { + dialogs::show_error(&model.window, "Error Saving File", &error.to_string()); + } + } +} + +/// Handles window close, prompting for unsaved changes if needed. +pub fn close_window(model: &mut AppModel, sender: &ComponentSender) { + if model.modified { + dialogs::show_unsaved_changes_dialog( + &model.window, + model.current_file.clone(), + sender.clone(), + ); + } else { + unregister_current_file(model); + model.window.destroy(); + } +} + +/// Dispatches save action based on whether file already has a path. +pub fn save(model: &AppModel, close_after: bool, sender: &ComponentSender) { + if let Some(path) = model.current_file.clone() { + sender.input(Action::SaveToFile { path, close_after }); + } else { + sender.input(Action::SaveAs { close_after }); + } +} + +// === Helper Functions === + +/// Unregisters the current file from the registry if one is open. +fn unregister_current_file(model: &AppModel) { + if let Some(ref path) = model.current_file { + file_registry::unregister_file(path); + } +} + +/// Updates file registration when saving to a new path. +fn update_file_registration(model: &mut AppModel, new_path: &PathBuf) { + if model.current_file.as_ref() != Some(new_path) { + unregister_current_file(model); + file_registry::register_file(new_path, &model.window); + } +} + +/// Ensures the path has the .wmap extension. +fn ensure_wmap_extension(path: PathBuf) -> PathBuf { + if path.extension().is_none() + || path.extension().unwrap_or_default() != constants::FILE_EXTENSION + { + path.with_extension(constants::FILE_EXTENSION) + } else { + path + } +} diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs new file mode 100644 index 0000000..7d7cfec --- /dev/null +++ b/src/handlers/mod.rs @@ -0,0 +1,3 @@ +pub mod file; +pub mod view; +pub mod zoom; diff --git a/src/handlers/view.rs b/src/handlers/view.rs new file mode 100644 index 0000000..64aeb98 --- /dev/null +++ b/src/handlers/view.rs @@ -0,0 +1,20 @@ +use relm4::gtk; + +use crate::AppModel; +use crate::stages::ALL_STAGE_TYPES; + +/// Toggles between horizontal and vertical layout orientation. +pub fn change_orientation(model: &mut AppModel) { + model.orientation = match model.orientation { + gtk::Orientation::Horizontal => gtk::Orientation::Vertical, + _ => gtk::Orientation::Horizontal, + }; +} + +/// Updates the selected stage type from dropdown selection. +pub fn stage_type_selected(model: &mut AppModel, index: usize) { + if index < ALL_STAGE_TYPES.len() { + model.stage_type = ALL_STAGE_TYPES[index]; + model.update_image(); + } +} diff --git a/src/handlers/zoom.rs b/src/handlers/zoom.rs new file mode 100644 index 0000000..3acbb2c --- /dev/null +++ b/src/handlers/zoom.rs @@ -0,0 +1,25 @@ +use crate::AppModel; +use crate::constants; + +/// Sets the zoom level and updates the rendered image. +pub fn zoom(model: &mut AppModel, level: f64) { + model.zoom = level; + model.update_image(); +} + +/// Decreases zoom by one step if above minimum. +pub fn zoom_out(model: &mut AppModel) { + let new_zoom = model.zoom - constants::ZOOM_STEP; + if new_zoom >= constants::MIN_ZOOM { + model.zoom = new_zoom; + model.update_image(); + } +} + +/// Increases zoom by one step if below maximum. +pub fn zoom_in(model: &mut AppModel) { + if model.zoom < constants::MAX_ZOOM { + model.zoom += constants::ZOOM_STEP; + model.update_image(); + } +} -- cgit