diff options
| author | Rubén Beltrán del Río <jj@r.bdr.sh> | 2026-01-16 08:26:35 +0100 |
|---|---|---|
| committer | Rubén Beltrán del Río <jj@r.bdr.sh> | 2026-01-16 11:57:13 +0100 |
| commit | 81cd06b3334f2f91f6a1ab62a28e11c7d0c677eb (patch) | |
| tree | f0b0cf37b406eda2dec8db4382aeb5f606cd1f0d /src/preferences/pages/stages.rs | |
| parent | 4211b2ae06777d5bbe8261a1ab5c0dd057829a35 (diff) | |
Add preferences
Diffstat (limited to 'src/preferences/pages/stages.rs')
| -rw-r--r-- | src/preferences/pages/stages.rs | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/src/preferences/pages/stages.rs b/src/preferences/pages/stages.rs new file mode 100644 index 0000000..c7fa494 --- /dev/null +++ b/src/preferences/pages/stages.rs @@ -0,0 +1,252 @@ +// 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 gtk::prelude::*; +use relm4::Sender; +use relm4::gtk; +use uuid::Uuid; + +use super::super::models::UserPreferences; +use super::super::window::{PreferencesInput, StageLabel}; + +pub struct StagesPage { + container: gtk::Box, + list_box: gtk::ListBox, + sender: Sender<PreferencesInput>, +} + +impl StagesPage { + pub fn new(prefs: &UserPreferences, sender: Sender<PreferencesInput>) -> Self { + let container = gtk::Box::new(gtk::Orientation::Vertical, 10); + container.set_margin_top(20); + container.set_margin_bottom(20); + container.set_margin_start(20); + container.set_margin_end(20); + + // Header + let header_label = gtk::Label::new(Some("Custom Stages")); + header_label.add_css_class("heading"); + header_label.set_halign(gtk::Align::Start); + container.append(&header_label); + + let help_label = gtk::Label::new(Some( + "Custom stages allow you to define your own evolution axis labels. These will be available for rendering when the renderer supports custom stages.", + )); + help_label.add_css_class("dim-label"); + help_label.set_halign(gtk::Align::Start); + help_label.set_wrap(true); + help_label.set_margin_bottom(10); + container.append(&help_label); + + // Column headers + let header_row = gtk::Box::new(gtk::Orientation::Horizontal, 5); + header_row.add_css_class("dim-label"); + + let name_header = gtk::Label::new(Some("Name")); + name_header.set_width_chars(15); + name_header.set_xalign(0.0); + header_row.append(&name_header); + + let i_header = gtk::Label::new(Some("Stage I")); + i_header.set_width_chars(12); + i_header.set_xalign(0.0); + header_row.append(&i_header); + + let ii_header = gtk::Label::new(Some("Stage II")); + ii_header.set_width_chars(12); + ii_header.set_xalign(0.0); + header_row.append(&ii_header); + + let iii_header = gtk::Label::new(Some("Stage III")); + iii_header.set_width_chars(12); + iii_header.set_xalign(0.0); + header_row.append(&iii_header); + + let iv_header = gtk::Label::new(Some("Stage IV")); + iv_header.set_width_chars(12); + iv_header.set_xalign(0.0); + header_row.append(&iv_header); + + // Spacer for delete button column + let spacer = gtk::Box::new(gtk::Orientation::Horizontal, 0); + spacer.set_width_request(32); + header_row.append(&spacer); + + container.append(&header_row); + + // Scrollable list area + let scrolled = gtk::ScrolledWindow::new(); + scrolled.set_vexpand(true); + scrolled.set_min_content_height(200); + + let list_box = gtk::ListBox::new(); + list_box.set_selection_mode(gtk::SelectionMode::None); + list_box.add_css_class("boxed-list"); + + scrolled.set_child(Some(&list_box)); + container.append(&scrolled); + + // Add button + let button_box = gtk::Box::new(gtk::Orientation::Horizontal, 10); + button_box.set_halign(gtk::Align::End); + + let add_button = gtk::Button::from_icon_name("list-add-symbolic"); + add_button.set_tooltip_text(Some("Add custom stage")); + { + let sender = sender.clone(); + add_button.connect_clicked(move |_| { + sender.emit(PreferencesInput::AddCustomStage); + }); + } + button_box.append(&add_button); + container.append(&button_box); + + let page = Self { + container, + list_box, + sender, + }; + + page.populate_stages(prefs); + page + } + + pub fn widget(&self) -> gtk::Box { + self.container.clone() + } + + pub fn refresh(&self, prefs: &UserPreferences) { + // Clear existing rows + while let Some(child) = self.list_box.first_child() { + self.list_box.remove(&child); + } + self.populate_stages(prefs); + } + + fn populate_stages(&self, prefs: &UserPreferences) { + for stage in &prefs.custom_stages { + let row = self.create_stage_row(stage.id, &stage.name, &stage.stage); + self.list_box.append(&row); + } + } + + fn create_stage_row( + &self, + id: Uuid, + name: &str, + stage: &super::super::models::Stage, + ) -> gtk::ListBoxRow { + let row = gtk::ListBoxRow::new(); + let hbox = gtk::Box::new(gtk::Orientation::Horizontal, 5); + hbox.set_margin_top(5); + hbox.set_margin_bottom(5); + hbox.set_margin_start(5); + hbox.set_margin_end(5); + + // Name entry + let name_entry = gtk::Entry::new(); + name_entry.set_text(name); + name_entry.set_width_chars(15); + { + let sender = self.sender.clone(); + name_entry.connect_changed(move |entry| { + sender.emit(PreferencesInput::SetCustomStageName( + id, + entry.text().to_string(), + )); + }); + } + hbox.append(&name_entry); + + // Stage I entry + let i_entry = gtk::Entry::new(); + i_entry.set_text(&stage.i); + i_entry.set_width_chars(12); + { + let sender = self.sender.clone(); + i_entry.connect_changed(move |entry| { + sender.emit(PreferencesInput::SetCustomStageLabel( + id, + StageLabel::I, + entry.text().to_string(), + )); + }); + } + hbox.append(&i_entry); + + // Stage II entry + let ii_entry = gtk::Entry::new(); + ii_entry.set_text(&stage.ii); + ii_entry.set_width_chars(12); + { + let sender = self.sender.clone(); + ii_entry.connect_changed(move |entry| { + sender.emit(PreferencesInput::SetCustomStageLabel( + id, + StageLabel::II, + entry.text().to_string(), + )); + }); + } + hbox.append(&ii_entry); + + // Stage III entry + let iii_entry = gtk::Entry::new(); + iii_entry.set_text(&stage.iii); + iii_entry.set_width_chars(12); + { + let sender = self.sender.clone(); + iii_entry.connect_changed(move |entry| { + sender.emit(PreferencesInput::SetCustomStageLabel( + id, + StageLabel::III, + entry.text().to_string(), + )); + }); + } + hbox.append(&iii_entry); + + // Stage IV entry + let iv_entry = gtk::Entry::new(); + iv_entry.set_text(&stage.iv); + iv_entry.set_width_chars(12); + { + let sender = self.sender.clone(); + iv_entry.connect_changed(move |entry| { + sender.emit(PreferencesInput::SetCustomStageLabel( + id, + StageLabel::IV, + entry.text().to_string(), + )); + }); + } + hbox.append(&iv_entry); + + // Delete button + let delete_button = gtk::Button::from_icon_name("list-remove-symbolic"); + delete_button.add_css_class("flat"); + delete_button.set_tooltip_text(Some("Remove stage")); + { + let sender = self.sender.clone(); + delete_button.connect_clicked(move |_| { + sender.emit(PreferencesInput::RemoveCustomStage(id)); + }); + } + hbox.append(&delete_button); + + row.set_child(Some(&hbox)); + row + } +} |