aboutsummaryrefslogtreecommitdiff
path: root/src/preferences/models.rs
diff options
context:
space:
mode:
authorRubén Beltrán del Río <jj@r.bdr.sh>2026-01-16 08:26:35 +0100
committerRubén Beltrán del Río <jj@r.bdr.sh>2026-01-16 11:57:13 +0100
commit81cd06b3334f2f91f6a1ab62a28e11c7d0c677eb (patch)
treef0b0cf37b406eda2dec8db4382aeb5f606cd1f0d /src/preferences/models.rs
parent4211b2ae06777d5bbe8261a1ab5c0dd057829a35 (diff)
Add preferences
Diffstat (limited to 'src/preferences/models.rs')
-rw-r--r--src/preferences/models.rs266
1 files changed, 266 insertions, 0 deletions
diff --git a/src/preferences/models.rs b/src/preferences/models.rs
new file mode 100644
index 0000000..2cf4665
--- /dev/null
+++ b/src/preferences/models.rs
@@ -0,0 +1,266 @@
+// Copyright (C) 2024 Rubén Beltrán del Río
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see https://map.tranquil.systems.
+
+use serde::{Deserialize, Serialize};
+use uuid::Uuid;
+
+/// Stage labels for custom stages (matches Swift Stage struct)
+#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
+pub struct Stage {
+ pub i: String,
+ pub ii: String,
+ pub iii: String,
+ pub iv: String,
+}
+
+/// Custom stage definition (matches Swift CustomStage)
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
+#[serde(rename_all = "camelCase")]
+pub struct CustomStage {
+ pub id: Uuid,
+ pub name: String,
+ pub stage: Stage,
+}
+
+impl Default for CustomStage {
+ fn default() -> Self {
+ Self {
+ id: Uuid::new_v4(),
+ name: "Custom Stage".to_string(),
+ stage: Stage::default(),
+ }
+ }
+}
+
+impl CustomStage {
+ pub fn cynefin_default() -> Self {
+ Self {
+ id: Uuid::new_v4(),
+ name: "Cynefin".to_string(),
+ stage: Stage {
+ i: "Chaotic".to_string(),
+ ii: "Complex".to_string(),
+ iii: "Complicated".to_string(),
+ iv: "Clear".to_string(),
+ },
+ }
+ }
+
+ pub fn placeholder_default() -> Self {
+ Self {
+ id: Uuid::new_v4(),
+ name: "New Stage".to_string(),
+ stage: Stage {
+ i: "Stage I".to_string(),
+ ii: "Stage II".to_string(),
+ iii: "Stage III".to_string(),
+ iv: "Stage IV".to_string(),
+ },
+ }
+ }
+}
+
+/// Template for new maps (matches Swift Template)
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
+#[serde(rename_all = "camelCase")]
+pub struct Template {
+ pub id: Uuid,
+ pub name: String,
+ pub content: String,
+ #[serde(default)]
+ pub is_default: bool,
+}
+
+impl Default for Template {
+ fn default() -> Self {
+ Self {
+ id: Uuid::new_v4(),
+ name: "Untitled".to_string(),
+ content: String::new(),
+ is_default: false,
+ }
+ }
+}
+
+impl Template {
+ pub fn new(name: String, content: String, is_default: bool) -> Self {
+ Self {
+ id: Uuid::new_v4(),
+ name,
+ content,
+ is_default,
+ }
+ }
+}
+
+/// JSON import/export format (matches Swift UserPreferencesJSON)
+/// All fields optional to support partial imports
+#[derive(Debug, Clone, Serialize, Deserialize, Default)]
+#[serde(rename_all = "camelCase")]
+pub struct UserPreferencesJson {
+ // Map preferences
+ pub show_map_background: Option<bool>,
+ pub use_custom_font: Option<bool>,
+ pub custom_font_name: Option<String>,
+ pub default_export_format: Option<String>,
+ pub use_smart_label_positioning: Option<bool>,
+
+ // Editor preferences
+ pub use_custom_editor_font: Option<bool>,
+ pub custom_editor_font_name: Option<String>,
+ pub editor_font_size: Option<f64>,
+ pub soft_wrap_lines: Option<bool>,
+
+ // Templates and stages
+ pub map_templates: Option<Vec<Template>>,
+ pub custom_stages: Option<Vec<CustomStage>>,
+
+ // UI preferences
+ pub view_style: Option<String>,
+ pub zoom: Option<f64>,
+}
+
+/// Main preferences struct stored in app
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
+#[serde(rename_all = "camelCase")]
+pub struct UserPreferences {
+ // Map preferences
+ pub show_map_background: bool,
+ pub use_custom_font: bool,
+ pub custom_font_name: String,
+ pub default_export_format: String,
+ pub use_smart_label_positioning: bool,
+
+ // Editor preferences
+ pub use_custom_editor_font: bool,
+ pub custom_editor_font_name: String,
+ pub editor_font_size: f64,
+ pub soft_wrap_lines: bool,
+
+ // Templates and stages
+ pub map_templates: Vec<Template>,
+ pub custom_stages: Vec<CustomStage>,
+
+ // UI preferences
+ pub view_style: String,
+ pub zoom: f64,
+}
+
+impl Default for UserPreferences {
+ fn default() -> Self {
+ Self {
+ show_map_background: true,
+ use_custom_font: false,
+ custom_font_name: "Sans".to_string(),
+ default_export_format: "png".to_string(),
+ use_smart_label_positioning: true,
+
+ use_custom_editor_font: false,
+ custom_editor_font_name: "Monospace".to_string(),
+ editor_font_size: 14.0,
+ soft_wrap_lines: false,
+
+ map_templates: vec![
+ Template::new(
+ "Default".to_string(),
+ "title My Map\n\nanchor Business [0.95, 0.63]\nanchor Public [0.95, 0.78]\ncomponent User Needs [0.9, 0.50]\n".to_string(),
+ true,
+ ),
+ Template::new("Empty".to_string(), String::new(), false),
+ ],
+ custom_stages: vec![CustomStage::cynefin_default()],
+
+ view_style: "horizontal".to_string(),
+ zoom: 1.0,
+ }
+ }
+}
+
+impl UserPreferences {
+ /// Convert to JSON export format
+ pub fn to_json(&self) -> UserPreferencesJson {
+ UserPreferencesJson {
+ show_map_background: Some(self.show_map_background),
+ use_custom_font: Some(self.use_custom_font),
+ custom_font_name: Some(self.custom_font_name.clone()),
+ default_export_format: Some(self.default_export_format.clone()),
+ use_smart_label_positioning: Some(self.use_smart_label_positioning),
+ use_custom_editor_font: Some(self.use_custom_editor_font),
+ custom_editor_font_name: Some(self.custom_editor_font_name.clone()),
+ editor_font_size: Some(self.editor_font_size),
+ soft_wrap_lines: Some(self.soft_wrap_lines),
+ map_templates: Some(self.map_templates.clone()),
+ custom_stages: Some(self.custom_stages.clone()),
+ view_style: Some(self.view_style.clone()),
+ zoom: Some(self.zoom),
+ }
+ }
+
+ /// Update from JSON import (partial updates supported)
+ pub fn update_from_json(&mut self, json: &UserPreferencesJson) {
+ if let Some(v) = json.show_map_background {
+ self.show_map_background = v;
+ }
+ if let Some(ref v) = json.use_custom_font {
+ self.use_custom_font = *v;
+ }
+ if let Some(ref v) = json.custom_font_name {
+ self.custom_font_name = v.clone();
+ }
+ if let Some(ref v) = json.default_export_format {
+ self.default_export_format = v.clone();
+ }
+ if let Some(v) = json.use_smart_label_positioning {
+ self.use_smart_label_positioning = v;
+ }
+ if let Some(ref v) = json.use_custom_editor_font {
+ self.use_custom_editor_font = *v;
+ }
+ if let Some(ref v) = json.custom_editor_font_name {
+ self.custom_editor_font_name = v.clone();
+ }
+ if let Some(v) = json.editor_font_size {
+ self.editor_font_size = v;
+ }
+ if let Some(v) = json.soft_wrap_lines {
+ self.soft_wrap_lines = v;
+ }
+ if let Some(ref v) = json.map_templates {
+ self.map_templates = v.clone();
+ }
+ if let Some(ref v) = json.custom_stages {
+ self.custom_stages = v.clone();
+ }
+ if let Some(ref v) = json.view_style {
+ self.view_style = v.clone();
+ }
+ if let Some(v) = json.zoom {
+ self.zoom = v;
+ }
+ }
+
+ /// Get the default template
+ #[allow(dead_code)]
+ pub fn default_template(&self) -> Option<&Template> {
+ self.map_templates.iter().find(|t| t.is_default)
+ }
+
+ /// Set a template as default (unsets others)
+ pub fn set_default_template(&mut self, id: Uuid) {
+ for template in &mut self.map_templates {
+ template.is_default = template.id == id;
+ }
+ }
+}