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