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