Skip to main content

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