// Map, wardley map editor for linux // Copyright (C) 2026 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 Affero 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 Affero General Public License for more details. // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . use gtk::prelude::*; use relm4::Sender; use relm4::gtk; use crate::tr; use crate::ui_helpers::create_switch_row; use super::super::models::UserPreferences; use super::super::window::PreferencesInput; const DEFAULT_FONT_SIZE: f64 = 14.0; pub struct EditorPage { container: gtk::Box, font_size_scale: gtk::Scale, soft_wrap_switch: gtk::Switch, custom_font_switch: gtk::Switch, font_button: gtk::FontDialogButton, } impl EditorPage { pub fn new(preferences: &UserPreferences, sender: &Sender) -> Self { let container = gtk::Box::new(gtk::Orientation::Vertical, 20); container.set_margin_top(20); container.set_margin_bottom(20); container.set_margin_start(20); container.set_margin_end(20); container.set_valign(gtk::Align::Start); let (font_size_box, font_size_scale) = Self::create_font_size_section(preferences); container.append(&font_size_box); let (soft_wrap_switch, custom_font_switch) = Self::create_style_section(&container, preferences); let font_button = Self::create_font_button(preferences); container.append(&Self::create_font_row(&font_button)); Self::connect_signals( sender, &font_size_scale, &soft_wrap_switch, &custom_font_switch, &font_button, ); Self { container, font_size_scale, soft_wrap_switch, custom_font_switch, font_button, } } fn create_font_size_section(preferences: &UserPreferences) -> (gtk::Box, gtk::Scale) { let label = gtk::Label::new(Some(&tr!("preferences.editor.font_size.title"))); label.add_css_class("heading"); label.set_halign(gtk::Align::Start); let scale = gtk::Scale::with_range(gtk::Orientation::Horizontal, 7.0, 21.0, 1.0); scale.set_value(preferences.editor_font_size); scale.set_draw_value(true); scale.set_hexpand(true); #[allow(clippy::cast_possible_truncation)] scale.set_format_value_func(|_, value| { #[allow(clippy::float_cmp)] if value == DEFAULT_FONT_SIZE { format!( "{} ({})", value as i32, tr!("preferences.editor.default_font_size") ) } else { format!("{}", value as i32) } }); let vbox = gtk::Box::new(gtk::Orientation::Vertical, 10); vbox.append(&label); vbox.append(&scale); (vbox, scale) } fn create_style_section( container: >k::Box, preferences: &UserPreferences, ) -> (gtk::Switch, gtk::Switch) { let label = gtk::Label::new(Some(&tr!("preferences.editor.editor_style.title"))); label.add_css_class("heading"); label.set_halign(gtk::Align::Start); label.set_margin_top(10); container.append(&label); let soft_wrap_row = create_switch_row( &tr!("preferences.editor.editor_style.soft_wrap_lines"), preferences.soft_wrap_lines, ); container.append(&soft_wrap_row.0); let custom_font_row = create_switch_row( &tr!("preferences.editor.editor_style.use_custom_font"), preferences.use_custom_editor_font, ); container.append(&custom_font_row.0); (soft_wrap_row.1, custom_font_row.1) } fn create_font_button(preferences: &UserPreferences) -> gtk::FontDialogButton { let font_dialog = gtk::FontDialog::new(); let font_button = gtk::FontDialogButton::new(Some(font_dialog)); #[allow(clippy::cast_possible_truncation)] let font_desc = gtk::pango::FontDescription::from_string(&format!( "{} {}", preferences.custom_editor_font_name, preferences.editor_font_size as i32 )); font_button.set_font_desc(&font_desc); font_button.set_sensitive(preferences.use_custom_editor_font); font_button.set_hexpand(true); font_button.set_use_font(true); font_button.set_use_size(false); font_button } fn create_font_row(font_button: >k::FontDialogButton) -> gtk::Box { let row = gtk::Box::new(gtk::Orientation::Horizontal, 10); row.append(>k::Label::new(Some(&tr!("preferences.editor.font")))); row.append(font_button); row } fn connect_signals( sender: &Sender, font_size_scale: >k::Scale, soft_wrap_switch: >k::Switch, custom_font_switch: >k::Switch, font_button: >k::FontDialogButton, ) { let s = sender.clone(); let fb = font_button.clone(); font_size_scale.connect_value_changed(move |scale| { s.emit(PreferencesInput::SetEditorFontSize(scale.value())); Self::update_font_button_size(&fb, scale.value()); }); let s = sender.clone(); soft_wrap_switch.connect_state_set(move |_, state| { s.emit(PreferencesInput::SetSoftWrapLines(state)); gtk::glib::Propagation::Proceed }); let s = sender.clone(); let scale = font_size_scale.clone(); font_button.connect_font_desc_notify(move |button| { if let Some(font) = button.font_desc() { if let Some(family) = font.family() { s.emit(PreferencesInput::SetCustomEditorFontName( family.to_string(), )); } let size = font.size() / gtk::pango::SCALE; if size > 0 { scale.set_value(f64::from(size)); } } }); let s = sender.clone(); let fb = font_button.clone(); custom_font_switch.connect_state_set(move |_, state| { fb.set_sensitive(state); s.emit(PreferencesInput::SetUseCustomEditorFont(state)); gtk::glib::Propagation::Proceed }); } fn update_font_button_size(font_button: >k::FontDialogButton, size: f64) { if let Some(mut font_desc) = font_button.font_desc() { #[allow(clippy::cast_possible_truncation)] font_desc.set_size(size as i32 * gtk::pango::SCALE); font_button.set_font_desc(&font_desc); } } pub fn widget(&self) -> gtk::Box { self.container.clone() } pub fn sync_from(&self, preferences: &UserPreferences) { self.font_size_scale.set_value(preferences.editor_font_size); self.soft_wrap_switch .set_active(preferences.soft_wrap_lines); self.custom_font_switch .set_active(preferences.use_custom_editor_font); self.font_button .set_sensitive(preferences.use_custom_editor_font); #[allow(clippy::cast_possible_truncation)] let font_desc = gtk::pango::FontDescription::from_string(&format!( "{} {}", preferences.custom_editor_font_name, preferences.editor_font_size as i32 )); self.font_button.set_font_desc(&font_desc); } }