aboutsummaryrefslogtreecommitdiff
path: root/src/handlers
diff options
context:
space:
mode:
authorRubén Beltrán del Río <jj@r.bdr.sh>2026-01-16 00:08:41 +0100
committerRubén Beltrán del Río <jj@r.bdr.sh>2026-01-16 00:12:53 +0100
commit04b1e6080d99601b8d6343be3140f545cd8f9eae (patch)
tree2180b7d50f2442e5d18a99a87e56def29b02f68e /src/handlers
parentab6492a9f9b8a65121fcf10ff5a44660bd063af1 (diff)
Separate concerns
Diffstat (limited to 'src/handlers')
-rw-r--r--src/handlers/file.rs100
-rw-r--r--src/handlers/mod.rs3
-rw-r--r--src/handlers/view.rs20
-rw-r--r--src/handlers/zoom.rs25
4 files changed, 148 insertions, 0 deletions
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<AppModel>) {
+ 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<AppModel>) {
+ 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();
+ }
+}