aboutsummaryrefslogtreecommitdiff
path: root/src/preferences/pages/stages.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/pages/stages.rs
parent4211b2ae06777d5bbe8261a1ab5c0dd057829a35 (diff)
Add preferences
Diffstat (limited to 'src/preferences/pages/stages.rs')
-rw-r--r--src/preferences/pages/stages.rs252
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
+ }
+}