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}