Skip to main content

teksilo_settings/
ext.rs

1// SPDX-License-Identifier: MPL-2.0
2// SPDX-FileCopyrightText: 2026 FernTech
3
4//! Extension traits exposing settings services on `BuildContext` and
5//! `EventContext`.
6//!
7//! `teksilo-settings` cannot live below `teksilo-core` (it depends on
8//! `teksilo-core` for `Signal`, `ObserverHandle`, etc.), so the
9//! convenience accessors `ctx.settings()` / `ctx.window_state()` /
10//! `ctx.mru::<T>()` ship as an extension trait that apps `use`
11//! explicitly:
12//!
13//! ```ignore
14//! use teksilo_settings::SettingsExt;
15//!
16//! // inside any handler / build method:
17//! let store = ctx.settings();
18//! let recents = ctx.mru::<RecentProject>();
19//! ```
20//!
21//! Each accessor wraps the existing `app_state::<T>()` lookup. The
22//! mandatory accessors panic with a clear message if the service has
23//! not been registered; the `try_*` variants return `Option`.
24//!
25//! Window-geometry persistence is **not** an extension method: when a
26//! `WindowStateService` is registered via `TeksiloAppBuilder::settings`,
27//! every `WindowConfig` carrying an `id(...)` is automatically
28//! restored on creation and recorded on every change by `teksilo-app`'s
29//! window manager. No widget-side wiring needed.
30
31use teksilo_core::BuildContext;
32use teksilo_core::widget::EventContext;
33
34use crate::mru::{MruEntry, MruList};
35use crate::store::SettingsStore;
36use crate::window_state::WindowStateService;
37
38/// Convenience accessors for settings services attached to the app's
39/// `app_state` registry.
40pub trait SettingsExt {
41    /// The K/V settings store. Panics if `TeksiloAppBuilder::settings(...)`
42    /// was not called.
43    fn settings(&self) -> &SettingsStore {
44        self.try_settings().unwrap_or_else(|| {
45            panic!(
46                "SettingsExt::settings(): no SettingsStore registered. \
47                 Call TeksiloAppBuilder::settings(SettingsBundle::new()) at startup."
48            )
49        })
50    }
51
52    /// The window-state service. Panics if not registered.
53    fn window_state(&self) -> &WindowStateService {
54        self.try_window_state().unwrap_or_else(|| {
55            panic!(
56                "SettingsExt::window_state(): no WindowStateService registered. \
57                 Call .with_window_state(true) on the SettingsBundle."
58            )
59        })
60    }
61
62    /// An app-defined MRU list. Panics if no `MruList<T>` was
63    /// registered for that exact `T` via
64    /// `TeksiloAppBuilder::app_state(mru_handle.clone())`.
65    fn mru<T: MruEntry>(&self) -> &MruList<T> {
66        self.try_mru::<T>().unwrap_or_else(|| {
67            panic!(
68                "SettingsExt::mru::<{}>(): no MruList registered. \
69                 Construct one with MruList::open(...) and register via \
70                 TeksiloAppBuilder::app_state(mru_handle.clone()).",
71                std::any::type_name::<T>(),
72            )
73        })
74    }
75
76    /// Returns the K/V settings store, or `None` if not registered.
77    fn try_settings(&self) -> Option<&SettingsStore>;
78    /// Returns the window-state service, or `None` if not registered.
79    fn try_window_state(&self) -> Option<&WindowStateService>;
80    /// Returns the MRU list for `T`, or `None` if no `MruList<T>` was registered.
81    fn try_mru<T: MruEntry>(&self) -> Option<&MruList<T>>;
82}
83
84impl<'a> SettingsExt for BuildContext<'a> {
85    fn try_settings(&self) -> Option<&SettingsStore> {
86        self.app_state::<SettingsStore>()
87    }
88    fn try_window_state(&self) -> Option<&WindowStateService> {
89        self.app_state::<WindowStateService>()
90    }
91    fn try_mru<T: MruEntry>(&self) -> Option<&MruList<T>> {
92        self.app_state::<MruList<T>>()
93    }
94}
95
96impl<'a> SettingsExt for EventContext<'a> {
97    fn try_settings(&self) -> Option<&SettingsStore> {
98        self.app_state::<SettingsStore>()
99    }
100    fn try_window_state(&self) -> Option<&WindowStateService> {
101        self.app_state::<WindowStateService>()
102    }
103    fn try_mru<T: MruEntry>(&self) -> Option<&MruList<T>> {
104        self.app_state::<MruList<T>>()
105    }
106}