// 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;
pub struct EditorPage {
container: gtk::Box,
font_size_spin: gtk::SpinButton,
soft_wrap_switch: gtk::Switch,
custom_font_switch: gtk::Switch,
font_button: gtk::FontButton,
}
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);
// Font Size section
let font_size_label = gtk::Label::new(Some(&tr!("preferences.editor.font_size.title")));
font_size_label.add_css_class("heading");
font_size_label.set_halign(gtk::Align::Start);
container.append(&font_size_label);
let font_size_box = gtk::Box::new(gtk::Orientation::Horizontal, 10);
let font_size_spin = gtk::SpinButton::with_range(7.0, 21.0, 1.0);
font_size_spin.set_value(preferences.editor_font_size);
{
let sender = sender.clone();
font_size_spin.connect_value_changed(move |spin| {
sender.emit(PreferencesInput::SetEditorFontSize(spin.value()));
});
}
font_size_box.append(&font_size_spin);
font_size_box.append(>k::Label::new(Some(&tr!(
"preferences.editor.font_size.unit"
))));
container.append(&font_size_box);
// Editor Style section
let style_label = gtk::Label::new(Some(&tr!("preferences.editor.editor_style.title")));
style_label.add_css_class("heading");
style_label.set_halign(gtk::Align::Start);
style_label.set_margin_top(10);
container.append(&style_label);
// Soft Wrap Lines
let soft_wrap_row = create_switch_row(
&tr!("preferences.editor.editor_style.soft_wrap_lines"),
preferences.soft_wrap_lines,
);
let soft_wrap_switch = soft_wrap_row.1.clone();
{
let sender = sender.clone();
soft_wrap_switch.connect_state_set(move |_, state| {
sender.emit(PreferencesInput::SetSoftWrapLines(state));
gtk::glib::Propagation::Proceed
});
}
container.append(&soft_wrap_row.0);
// Use Custom Font
let custom_font_row = create_switch_row(
&tr!("preferences.editor.editor_style.use_custom_font"),
preferences.use_custom_editor_font,
);
let custom_font_switch = custom_font_row.1.clone();
container.append(&custom_font_row.0);
// Font selection
let font_row = gtk::Box::new(gtk::Orientation::Horizontal, 10);
font_row.append(>k::Label::new(Some(&tr!("preferences.editor.font"))));
let font_button = gtk::FontButton::new();
font_button.set_use_size(false);
#[allow(clippy::cast_possible_truncation)]
font_button.set_font(&format!(
"{} {}",
preferences.custom_editor_font_name, preferences.editor_font_size as i32
));
font_button.set_sensitive(preferences.use_custom_editor_font);
font_button.set_hexpand(true);
font_button.set_use_font(true);
{
let sender = sender.clone();
font_button.connect_font_set(move |button| {
if let Some(font) = button.font() {
// Extract font family from the font string
let font_desc = gtk::pango::FontDescription::from_string(&font);
if let Some(family) = font_desc.family() {
sender.emit(PreferencesInput::SetCustomEditorFontName(
family.to_string(),
));
}
}
});
}
font_row.append(&font_button);
container.append(&font_row);
// Connect custom font switch to font button sensitivity
{
let font_button_clone = font_button.clone();
let sender = sender.clone();
custom_font_switch.connect_state_set(move |_, state| {
font_button_clone.set_sensitive(state);
sender.emit(PreferencesInput::SetUseCustomEditorFont(state));
gtk::glib::Propagation::Proceed
});
}
Self {
container,
font_size_spin,
soft_wrap_switch,
custom_font_switch,
font_button,
}
}
pub fn widget(&self) -> gtk::Box {
self.container.clone()
}
pub fn sync_from(&self, preferences: &UserPreferences) {
self.font_size_spin.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)] // Font size is always a small positive number
self.font_button.set_font(&format!(
"{} {}",
preferences.custom_editor_font_name, preferences.editor_font_size as i32
));
}
}