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    /// Whether a submenu should remain open after this setting handles a selection.
147    fn keep_menu_open(&self) -> bool {
148        false
149    }
150}
151
152impl<T: SettingKind + ?Sized> SettingKind for &T {
153    fn identity(&self) -> SettingIdentity {
154        (**self).identity()
155    }
156
157    fn label(&self, settings: &Settings) -> String {
158        (**self).label(settings)
159    }
160
161    fn fetch(&self, settings: &Settings) -> SettingData {
162        (**self).fetch(settings)
163    }
164
165    fn handle(
166        &self,
167        evt: &Event,
168        settings: &mut Settings,
169        bus: &mut Bus,
170    ) -> (Option<String>, bool) {
171        (**self).handle(evt, settings, bus)
172    }
173
174    fn as_input_kind(&self) -> Option<&dyn InputSettingKind> {
175        (**self).as_input_kind()
176    }
177
178    fn file_chooser_entry_id(&self) -> Option<EntryId> {
179        (**self).file_chooser_entry_id()
180    }
181
182    fn hold_event(&self, rect: Rectangle) -> Option<Event> {
183        (**self).hold_event(rect)
184    }
185
186    fn keep_menu_open(&self) -> bool {
187        (**self).keep_menu_open()
188    }
189}
190
191impl<T: SettingKind + ?Sized> SettingKind for Box<T> {
192    fn identity(&self) -> SettingIdentity {
193        (**self).identity()
194    }
195
196    fn label(&self, settings: &Settings) -> String {
197        (**self).label(settings)
198    }
199
200    fn fetch(&self, settings: &Settings) -> SettingData {
201        (**self).fetch(settings)
202    }
203
204    fn handle(
205        &self,
206        evt: &Event,
207        settings: &mut Settings,
208        bus: &mut Bus,
209    ) -> (Option<String>, bool) {
210        (**self).handle(evt, settings, bus)
211    }
212
213    fn as_input_kind(&self) -> Option<&dyn InputSettingKind> {
214        (**self).as_input_kind()
215    }
216
217    fn file_chooser_entry_id(&self) -> Option<EntryId> {
218        (**self).file_chooser_entry_id()
219    }
220
221    fn hold_event(&self, rect: Rectangle) -> Option<Event> {
222        (**self).hold_event(rect)
223    }
224
225    fn keep_menu_open(&self) -> bool {
226        (**self).keep_menu_open()
227    }
228}
229
230/// Extended trait for settings that accept free-form text input via a [`NamedInput`] overlay.
231///
232/// [`NamedInput`]: crate::view::named_input::NamedInput
233pub trait InputSettingKind: SettingKind {
234    /// The [`ViewId`] used by this setting's [`NamedInput`] and its submit event.
235    ///
236    /// [`NamedInput`]: crate::view::named_input::NamedInput
237    fn submit_view_id(&self) -> ViewId;
238
239    /// The [`EntryId`] event that opens this setting's input dialog when tapped.
240    fn open_entry_id(&self) -> EntryId;
241
242    /// Label shown inside the [`NamedInput`] dialog.
243    ///
244    /// [`NamedInput`]: crate::view::named_input::NamedInput
245    fn input_label(&self) -> String;
246
247    /// Maximum number of characters the input accepts.
248    fn input_max_chars(&self) -> usize;
249
250    /// The current value as a string to pre-populate the input field.
251    fn current_text(&self, settings: &Settings) -> String;
252
253    /// Parse `text` from the input, mutate `settings`, and return the display string.
254    fn apply_text(&self, text: &str, settings: &mut Settings) -> String;
255}