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}