cadmus_core/view/settings_editor/kinds/
mod.rs

1//! Trait and supporting types for defining individual settings.
2//!
3//! Each setting is a small struct that implements [`SettingKind`]. The trait
4//! encapsulates everything a [`SettingRow`](crate::view::settings_editor::SettingRow)
5//! needs to know: the row label, the current display value, which widget to
6//! render (`ActionLabel`, `Toggle`, or `SubMenu`), and which event to fire on tap.
7//!
8//! [`SettingIdentity`] is the single, deduplicated identity enum used by
9//! [`SettingsEvent::UpdateValue`](crate::view::settings_editor::SettingsEvent) to
10//! target the correct [`SettingValue`](crate::view::settings_editor::SettingValue) view.
11
12pub mod general;
13pub mod identity;
14pub mod import;
15pub mod intermission;
16pub mod library;
17pub mod reader;
18pub mod telemetry;
19
20pub use identity::SettingIdentity;
21
22use crate::settings::Settings;
23use crate::view::{Bus, EntryId, EntryKind, Event, ViewId};
24
25/// Identifies which boolean setting a toggle widget controls.
26///
27/// Used in [`ToggleEvent::Setting`](crate::view::ToggleEvent) so that
28/// [`CategoryEditor`](crate::view::settings_editor::CategoryEditor) can dispatch
29/// to the correct toggle handler without coupling to UI view IDs.
30#[derive(Debug, Clone, PartialEq, Eq)]
31pub enum ToggleSettings {
32    /// Sleep cover enable/disable setting
33    SleepCover,
34    /// Auto-share enable/disable setting
35    AutoShare,
36    /// Button scheme selection (natural or inverted)
37    ButtonScheme,
38    /// Logging enabled setting
39    LoggingEnabled,
40    /// Import on startup enable/disable setting
41    ImportStartupTrigger,
42    /// Sync metadata enable/disable setting
43    ImportSyncMetadata,
44    /// Kernel logging enabled setting (test + kobo builds only)
45    #[cfg(all(feature = "test", feature = "kobo"))]
46    EnableKernLog,
47    /// D-Bus logging enabled setting (test + kobo builds only)
48    #[cfg(all(feature = "test", feature = "kobo"))]
49    EnableDbusLog,
50}
51
52/// Describes how the value side of a setting row should be rendered.
53///
54/// Each variant is fully self-contained: it carries everything needed to build
55/// the widget, including the tap event or sub-menu entries.
56pub enum WidgetKind {
57    /// A tappable label that opens a free-form editor (e.g. a text input dialog).
58    ///
59    /// The inner event is fired when the label is tapped.
60    ActionLabel(Event),
61    /// A two-state toggle switch.
62    Toggle {
63        /// Label shown on the left (the "on" side).
64        left_label: String,
65        /// Label shown on the right (the "off" side).
66        right_label: String,
67        /// Whether the toggle is currently in the left/enabled state.
68        enabled: bool,
69        /// Event fired when the toggle is tapped.
70        tap_event: Event,
71    },
72    /// A tappable label that opens a sub-menu with the given entries.
73    ///
74    /// The entries (e.g. radio buttons) are stored here so that the widget is
75    /// fully self-contained.
76    SubMenu(Vec<EntryKind>),
77}
78
79/// All data needed to build and update the value side of a setting row.
80pub struct SettingData {
81    /// Text representation of the current value (shown in the widget).
82    pub value: String,
83    /// Which widget type to render, including all tap/event data for that widget.
84    pub widget: WidgetKind,
85}
86
87/// A self-contained description of a single setting.
88///
89/// Implementing this trait is sufficient to add a new setting to the editor.
90pub trait SettingKind {
91    /// Unique identity used to route [`SettingsEvent::UpdateValue`](crate::view::settings_editor::SettingsEvent::UpdateValue) to the
92    /// correct [`SettingValue`](crate::view::settings_editor::SettingValue) view.
93    fn identity(&self) -> SettingIdentity;
94
95    /// Human-readable label shown on the left side of the setting row.
96    ///
97    /// `settings` is provided for dynamic labels (e.g. library names).
98    fn label(&self, settings: &Settings) -> String;
99
100    /// Fetch the current display value and widget configuration from `settings`.
101    fn fetch(&self, settings: &Settings) -> SettingData;
102
103    /// Handle an incoming event that may apply a change to this setting.
104    ///
105    /// Mutates `settings` if the event is relevant and returns the updated
106    /// display string. Returns `None` if this event does not apply to this setting.
107    /// `bus` is available for settings that need to propagate side-effects.
108    fn handle(&self, _evt: &Event, _settings: &mut Settings, _bus: &mut Bus) -> Option<String> {
109        None
110    }
111
112    /// Returns this setting as an [`InputSettingKind`] if it supports text input.
113    ///
114    /// [`InputSettingKind`] implementors override this to return `Some(self)`.
115    /// All other settings inherit the default `None`.
116    fn as_input_kind(&self) -> Option<&dyn InputSettingKind> {
117        None
118    }
119
120    /// Returns the [`EntryId`] that triggers opening a file chooser for this setting.
121    ///
122    /// Default `None`. Implement on settings that offer a "Custom Image..." option
123    /// (currently the three intermission kinds).
124    fn file_chooser_entry_id(&self) -> Option<EntryId> {
125        None
126    }
127}
128
129impl<T: SettingKind + ?Sized> SettingKind for &T {
130    fn identity(&self) -> SettingIdentity {
131        (**self).identity()
132    }
133
134    fn label(&self, settings: &Settings) -> String {
135        (**self).label(settings)
136    }
137
138    fn fetch(&self, settings: &Settings) -> SettingData {
139        (**self).fetch(settings)
140    }
141
142    fn handle(&self, evt: &Event, settings: &mut Settings, bus: &mut Bus) -> Option<String> {
143        (**self).handle(evt, settings, bus)
144    }
145
146    fn as_input_kind(&self) -> Option<&dyn InputSettingKind> {
147        (**self).as_input_kind()
148    }
149
150    fn file_chooser_entry_id(&self) -> Option<EntryId> {
151        (**self).file_chooser_entry_id()
152    }
153}
154
155impl<T: SettingKind + ?Sized> SettingKind for Box<T> {
156    fn identity(&self) -> SettingIdentity {
157        (**self).identity()
158    }
159
160    fn label(&self, settings: &Settings) -> String {
161        (**self).label(settings)
162    }
163
164    fn fetch(&self, settings: &Settings) -> SettingData {
165        (**self).fetch(settings)
166    }
167
168    fn handle(&self, evt: &Event, settings: &mut Settings, bus: &mut Bus) -> Option<String> {
169        (**self).handle(evt, settings, bus)
170    }
171
172    fn as_input_kind(&self) -> Option<&dyn InputSettingKind> {
173        (**self).as_input_kind()
174    }
175
176    fn file_chooser_entry_id(&self) -> Option<EntryId> {
177        (**self).file_chooser_entry_id()
178    }
179}
180
181/// Extended trait for settings that accept free-form text input via a [`NamedInput`] overlay.
182///
183/// [`NamedInput`]: crate::view::named_input::NamedInput
184pub trait InputSettingKind: SettingKind {
185    /// The [`ViewId`] used by this setting's [`NamedInput`] and its submit event.
186    ///
187    /// [`NamedInput`]: crate::view::named_input::NamedInput
188    fn submit_view_id(&self) -> ViewId;
189
190    /// The [`EntryId`] event that opens this setting's input dialog when tapped.
191    fn open_entry_id(&self) -> EntryId;
192
193    /// Label shown inside the [`NamedInput`] dialog.
194    ///
195    /// [`NamedInput`]: crate::view::named_input::NamedInput
196    fn input_label(&self) -> String;
197
198    /// Maximum number of characters the input accepts.
199    fn input_max_chars(&self) -> usize;
200
201    /// The current value as a string to pre-populate the input field.
202    fn current_text(&self, settings: &Settings) -> String;
203
204    /// Parse `text` from the input, mutate `settings`, and return the display string.
205    fn apply_text(&self, text: &str, settings: &mut Settings) -> String;
206}