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}