zng/
config.rs

1#![cfg(feature = "config")]
2
3//! Config service, sources and other types.
4//!
5//! The configuration service [`CONFIG`] separates config using from config writing. A config
6//! is a variable of a serializable type, widgets and other components request a config using an unique text name and
7//! then simply use the variable like any other. The app optionally sets one or more config sources that are automatically
8//! updated when a config variable changes and are monitored for changes that are propagated back to the config variables.
9//!
10//! # Sources
11//!
12//! The default config source is the [`MemoryConfig`] that only lives for the app process lifetime, this can
13//! be used to connect different UI components, more importantly it also means that the [`CONFIG`] service always works.
14//!
15//! ```
16//! use zng::prelude::*;
17//!
18//! fn txt_input() -> UiNode {
19//!     TextInput!(CONFIG.get("example-txt", Txt::from("")))
20//! }
21//!
22//! fn txt_display() -> UiNode {
23//!     Text!(CONFIG.get("example-txt", Txt::from("")))
24//! }
25//!
26//! # fn main() { }
27//! # fn demo() {
28//! # let _ =
29//! Container! {
30//!     child = txt_input();
31//!     child_spacing = 20;
32//!     child_bottom = txt_display();
33//! }
34//! # ; }
35//! ```
36//!
37//! The example above uses a config key `"example-txt"`, no config source is set so this config will only last for the
38//! duration of the app instance, but both widgets are synchronized because they are bound to the same config.
39//!
40//! The example below setups a [`JsonConfig`] that persists the configs to a JSON file. The file updates when
41//! a config variable is modified and the variables are updated when the file is modified externally.
42//!
43//! ```
44//! # use zng::prelude::*;
45//! # fn main() { }
46//! # fn demo() {
47//! let cfg = zng::config::JsonConfig::sync("target/tmp/example.config.json");
48//! CONFIG.load(cfg);
49//! # }
50//! ```
51//!
52//! ## Other Sources
53//!
54//! The JSON, TOML, YAML and RON are available behind a feature flags, you can also implement your own source.
55//!
56//! Some *meta* sources are also provided, they enables composite sources, such as having two sources,
57//! *default config* and *user config* where the user config file only records the non-default values.
58//!
59//! The next example demonstrates a more complex setup:
60//!
61//! ```
62//! use zng::config::*;
63//!
64//! fn load_config() -> Box<dyn FallbackConfigReset> {
65//!     // config file for the app, keys with prefix "main." are saved here.
66//!     let user_cfg = JsonConfig::sync("target/tmp/example.config.json");
67//!     // entries not found in `user_cfg` bind to this file first before going to embedded fallback.
68//!     let default_cfg = JsonConfig::read("examples/config/res/defaults.json");
69//!
70//!     // the app settings.
71//!     let main_cfg = FallbackConfig::new(user_cfg, default_cfg);
72//!
73//!     // Clone a ref that can be used to reset specific entries.
74//!     let main_ref = main_cfg.clone_boxed();
75//!
76//!     // any other configs (Window::save_state for example)
77//!     let other_cfg = JsonConfig::sync("target/tmp/example.config.other.json");
78//!
79//!     CONFIG.load(SwitchConfig::new().with_prefix("main.", main_cfg).with_prefix("", other_cfg));
80//!
81//!     main_ref
82//! }
83//! ```
84//!
85//! # Full API
86//!
87//! See [`zng_ext_config`] for the full config API.
88
89pub use zng_ext_config::{
90    AnyConfig, CONFIG, Config, ConfigKey, ConfigStatus, ConfigValue, FallbackConfig, FallbackConfigReset, MemoryConfig, RawConfigValue,
91    ReadOnlyConfig, SwapConfig, SwitchConfig,
92};
93
94#[cfg(feature = "window")]
95pub use zng_wgt_window::{SaveState, save_state_node};
96
97#[cfg(feature = "config_json")]
98pub use zng_ext_config::JsonConfig;
99
100#[cfg(feature = "config_ron")]
101pub use zng_ext_config::RonConfig;
102
103#[cfg(feature = "config_toml")]
104pub use zng_ext_config::TomlConfig;
105
106#[cfg(feature = "config_yaml")]
107pub use zng_ext_config::YamlConfig;
108
109/// Settings metadata model.
110///
111/// Settings are the [`CONFIG`] the user can directly edit, they have associated metadata such as display name and description,
112/// and will usually be editable in a special settings window. This module provides a basic settings data model, with category grouping,
113/// sorting and filtering. A default settings editor window is also provided.
114///
115/// ```
116/// # use zng::config::settings::*;
117/// # use zng::prelude::*;
118/// fn register_categories() {
119///     SETTINGS.register_categories(|c| {
120///         c.entry("cat-example", |c| c.name("Example Settings"));
121///     });
122/// }
123///
124/// fn register_settings() {
125///     SETTINGS.register(|s| {
126///         s.entry("settings.value", "cat-example", |s| {
127///             s.name("Value")
128///                 .description("Example using EDITORS provided editor.")
129///                 .value(Txt::default())
130///         });
131///         s.entry("settings.custom", "cat-example", |s| {
132///             s.name("Custom")
133///                 .description("Example using custom editor.")
134///                 .editor_fn(wgt_fn!(|_setting| {
135///                     TextInput! {
136///                         txt = CONFIG.get("settings.custom", Txt::default());
137///                     }
138///                 }))
139///         });
140///     });
141/// }
142/// ```
143///
144/// The example above demonstrates how to register a settings category and two values, one using a default editor, the other
145/// using a custom editor. When no [`editor_fn`] is set and the [`value`] config is called the [`zng::widget::EDITORS`] service is used
146/// to find an editor for the config. The editor closure parameter is a [`Setting`] that is not used in this case, as the editor already
147/// binds to the config directly.
148///
149/// Note that the `name` and `description` accepts variable inputs, in a full app use the [`l10n!`] macro to declare localized metadata.
150///
151/// In the default `APP` the [`SETTINGS_CMD`] command is handled and shows a [`settings::editor`] window that implements search, edit and reset
152/// features. See the [config example] for a full demonstration of settings.
153///
154/// # Reset
155///
156/// Restoring settings to default is a common feature, you can simply update the value to a *default*, or with some setup, you can
157/// actually remove the config from the user file.
158///
159/// ```
160/// # use zng::config::*;
161/// # use zng::config::settings::*;
162/// # use zng::prelude::*;
163/// #
164/// fn load_config() -> Box<dyn FallbackConfigReset> {
165///     // user edited config (settings.)
166///     let user = JsonConfig::sync(zng::env::config("settings.json"));
167///     let default = JsonConfig::read(zng::env::res("default-settings.json"));
168///     let settings = FallbackConfig::new(user, default);
169///     let settings_ref = settings.clone_boxed();
170///
171///     // any other configs (Window::save_state for example)
172///     let other = JsonConfig::sync(zng::env::config("config.json"));
173///
174///     CONFIG.load(SwitchConfig::new().with_prefix("settings.", settings).with_prefix("", other));
175///
176///     settings_ref
177/// }
178/// fn register_settings(reset: Box<dyn FallbackConfigReset>) {
179///     SETTINGS.register(move |s| {
180///         s.entry("settings.value", "cat-example", |s| {
181///             s.name("Value").value(Txt::default()).reset(reset.clone_boxed(), "settings.")
182///         });
183///     });
184/// }
185/// ```
186///
187/// The example above declares a config system with three files, the relevant ones are `"default-settings.json"` and `"settings.json"`.
188/// The default file is deployed with the app resources and is read-only, the user file is created in the user data directory and
189/// contains only the custom values set by the user. The [`FallbackConfigReset`] has access to the user file and can remove entries from it.
190///
191/// The [`FallbackConfigReset::can_reset`] variable tracks the presence of the config in the user file. The default settings widget
192/// uses this to show a little reset arrow button that users can click to reset.
193///
194/// The [`FallbackConfig`] handles entry removal by updating the config variable back to the fallback file entry.
195///
196/// # Full API
197///
198/// See [`zng_ext_config::settings`] for the full settings API.
199///
200/// [`editor_fn`]: crate::config::settings::editor::SettingBuilderEditorExt::editor_fn
201/// [`value`]: crate::config::settings::SettingBuilder::value
202/// [`Setting`]: crate::config::settings::Setting
203/// [`SETTINGS_CMD`]: crate::config::settings::SETTINGS_CMD
204/// [`l10n!`]: crate::l10n::l10n
205/// [config example]: https://github.com/zng-ui/zng/blob/main/examples/config/src/main.rs
206pub mod settings {
207    pub use zng_ext_config::settings::{
208        CategoriesBuilder, Category, CategoryBuilder, CategoryId, SETTINGS, Setting, SettingBuilder, SettingsBuilder,
209    };
210    pub use zng_wgt_input::cmd::{SETTINGS_CMD, can_settings, on_pre_settings, on_settings};
211
212    /// Settings editor widget.
213    ///
214    /// # Full API
215    ///
216    /// See [`zng_wgt_settings`] for the full settings editor API.
217    #[cfg(feature = "settings_editor")]
218    pub mod editor {
219        pub use zng_wgt_settings::{
220            CategoriesListArgs, CategoryHeaderArgs, CategoryItemArgs, SettingArgs, SettingBuilderEditorExt, SettingsArgs, SettingsCtxExt,
221            SettingsEditor, categories_list_fn, category_header_fn, category_item_fn, reset_button, setting_fn, settings_fn,
222        };
223    }
224}