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 general;
13pub mod identity;
14pub mod import;
15pub mod intermission;
16pub mod library;
17pub mod reader;
18pub mod telemetry;
19
20pub use identity::SettingIdentity;
21
22use crate::settings::Settings;
23use crate::view::{Bus, EntryId, EntryKind, Event, ViewId};
24
25/// Identifies which boolean setting a toggle widget controls.
26///
27/// Used in [`ToggleEvent::Setting`](crate::view::ToggleEvent) so that
28/// [`CategoryEditor`](crate::view::settings_editor::CategoryEditor) can dispatch
29/// to the correct toggle handler without coupling to UI view IDs.
30#[derive(Debug, Clone, PartialEq, Eq)]
31pub enum ToggleSettings {
32 /// Sleep cover enable/disable setting
33 SleepCover,
34 /// Auto-share enable/disable setting
35 AutoShare,
36 /// Button scheme selection (natural or inverted)
37 ButtonScheme,
38 /// Logging enabled setting
39 LoggingEnabled,
40 /// Import on startup enable/disable setting
41 ImportStartupTrigger,
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.
56pub enum WidgetKind {
57 /// A tappable label that opens a free-form editor (e.g. a text input dialog).
58 ///
59 /// The inner event is fired when the label is tapped.
60 ActionLabel(Event),
61 /// A two-state toggle switch.
62 Toggle {
63 /// Label shown on the left (the "on" side).
64 left_label: String,
65 /// Label shown on the right (the "off" side).
66 right_label: String,
67 /// Whether the toggle is currently in the left/enabled state.
68 enabled: bool,
69 /// Event fired when the toggle is tapped.
70 tap_event: Event,
71 },
72 /// A tappable label that opens a sub-menu with the given entries.
73 ///
74 /// The entries (e.g. radio buttons) are stored here so that the widget is
75 /// fully self-contained.
76 SubMenu(Vec<EntryKind>),
77}
78
79/// All data needed to build and update the value side of a setting row.
80pub struct SettingData {
81 /// Text representation of the current value (shown in the widget).
82 pub value: String,
83 /// Which widget type to render, including all tap/event data for that widget.
84 pub widget: WidgetKind,
85}
86
87/// A self-contained description of a single setting.
88///
89/// Implementing this trait is sufficient to add a new setting to the editor.
90pub trait SettingKind {
91 /// Unique identity used to route [`SettingsEvent::UpdateValue`](crate::view::settings_editor::SettingsEvent::UpdateValue) to the
92 /// correct [`SettingValue`](crate::view::settings_editor::SettingValue) view.
93 fn identity(&self) -> SettingIdentity;
94
95 /// Human-readable label shown on the left side of the setting row.
96 ///
97 /// `settings` is provided for dynamic labels (e.g. library names).
98 fn label(&self, settings: &Settings) -> String;
99
100 /// Fetch the current display value and widget configuration from `settings`.
101 fn fetch(&self, settings: &Settings) -> SettingData;
102
103 /// Handle an incoming event that may apply a change to this setting.
104 ///
105 /// Mutates `settings` if the event is relevant and returns the updated
106 /// display string. Returns `None` if this event does not apply to this setting.
107 /// `bus` is available for settings that need to propagate side-effects.
108 fn handle(&self, _evt: &Event, _settings: &mut Settings, _bus: &mut Bus) -> Option<String> {
109 None
110 }
111
112 /// Returns this setting as an [`InputSettingKind`] if it supports text input.
113 ///
114 /// [`InputSettingKind`] implementors override this to return `Some(self)`.
115 /// All other settings inherit the default `None`.
116 fn as_input_kind(&self) -> Option<&dyn InputSettingKind> {
117 None
118 }
119
120 /// Returns the [`EntryId`] that triggers opening a file chooser for this setting.
121 ///
122 /// Default `None`. Implement on settings that offer a "Custom Image..." option
123 /// (currently the three intermission kinds).
124 fn file_chooser_entry_id(&self) -> Option<EntryId> {
125 None
126 }
127}
128
129impl<T: SettingKind + ?Sized> SettingKind for &T {
130 fn identity(&self) -> SettingIdentity {
131 (**self).identity()
132 }
133
134 fn label(&self, settings: &Settings) -> String {
135 (**self).label(settings)
136 }
137
138 fn fetch(&self, settings: &Settings) -> SettingData {
139 (**self).fetch(settings)
140 }
141
142 fn handle(&self, evt: &Event, settings: &mut Settings, bus: &mut Bus) -> Option<String> {
143 (**self).handle(evt, settings, bus)
144 }
145
146 fn as_input_kind(&self) -> Option<&dyn InputSettingKind> {
147 (**self).as_input_kind()
148 }
149
150 fn file_chooser_entry_id(&self) -> Option<EntryId> {
151 (**self).file_chooser_entry_id()
152 }
153}
154
155impl<T: SettingKind + ?Sized> SettingKind for Box<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(&self, evt: &Event, settings: &mut Settings, bus: &mut Bus) -> Option<String> {
169 (**self).handle(evt, settings, bus)
170 }
171
172 fn as_input_kind(&self) -> Option<&dyn InputSettingKind> {
173 (**self).as_input_kind()
174 }
175
176 fn file_chooser_entry_id(&self) -> Option<EntryId> {
177 (**self).file_chooser_entry_id()
178 }
179}
180
181/// Extended trait for settings that accept free-form text input via a [`NamedInput`] overlay.
182///
183/// [`NamedInput`]: crate::view::named_input::NamedInput
184pub trait InputSettingKind: SettingKind {
185 /// The [`ViewId`] used by this setting's [`NamedInput`] and its submit event.
186 ///
187 /// [`NamedInput`]: crate::view::named_input::NamedInput
188 fn submit_view_id(&self) -> ViewId;
189
190 /// The [`EntryId`] event that opens this setting's input dialog when tapped.
191 fn open_entry_id(&self) -> EntryId;
192
193 /// Label shown inside the [`NamedInput`] dialog.
194 ///
195 /// [`NamedInput`]: crate::view::named_input::NamedInput
196 fn input_label(&self) -> String;
197
198 /// Maximum number of characters the input accepts.
199 fn input_max_chars(&self) -> usize;
200
201 /// The current value as a string to pre-populate the input field.
202 fn current_text(&self, settings: &Settings) -> String;
203
204 /// Parse `text` from the input, mutate `settings`, and return the display string.
205 fn apply_text(&self, text: &str, settings: &mut Settings) -> String;
206}