Skip to main content

settings/
settings.rs

1//! Demonstrates persistence of settings.
2//!
3//! A counter is shown in the window. It can be incremented and decremented via input press.
4//! Its value persists between app sessions via settings.
5//!
6//! On desktop, if you quit the app and then restart it, the counter value should display
7//! the most recent value the app had before exiting. Settings are saved as TOML at
8//! `{preferences_dir}/org.bevy.examples.settings/settings.toml` (see [`SettingsPlugin`]).
9//! On web, if you navigate away and then come back to the window, the counter
10//! should display the most recent value the app had before navigating away.
11use std::time::Duration;
12
13use bevy::{
14    prelude::*,
15    settings::{
16        ReflectSettingsGroup, SaveSettingsDeferred, SaveSettingsSync, SettingsGroup, SettingsPlugin,
17    },
18    window::{ExitCondition, WindowCloseRequested},
19};
20
21fn main() {
22    App::new()
23        .add_plugins(DefaultPlugins.set(WindowPlugin {
24            // We want to intercept the exit so that we can save settings.
25            exit_condition: ExitCondition::DontExit,
26            primary_window: Some(Window {
27                title: "Settings Counter".into(),
28                ..default()
29            }),
30            ..default()
31        }))
32        .add_plugins(SettingsPlugin::new("org.bevy.examples.settings"))
33        .add_systems(Startup, setup)
34        .add_systems(Update, (show_count, change_count, on_window_close))
35        .run();
36}
37
38#[derive(Resource, SettingsGroup, Reflect, Default)]
39#[reflect(Resource, SettingsGroup, Default)]
40struct Counter {
41    count: i32,
42}
43
44/// A different settings group which has the same group name as the previous. The two groups will be
45/// merged into a single section in the config file.
46#[derive(Resource, SettingsGroup, Reflect)]
47#[reflect(Resource, SettingsGroup, Default)]
48#[settings_group(group = "counter")]
49struct OtherSettings {
50    enabled: bool,
51}
52
53impl Default for OtherSettings {
54    fn default() -> Self {
55        Self { enabled: true }
56    }
57}
58
59#[derive(Component)]
60struct CounterDisplay;
61
62fn setup(mut commands: Commands) {
63    commands.spawn((Camera::default(), Camera2d));
64    commands
65        .spawn(Node {
66            width: percent(100),
67            height: percent(100),
68            display: Display::Flex,
69            flex_direction: FlexDirection::Column,
70            align_items: AlignItems::Center,
71            justify_content: JustifyContent::Center,
72            ..default()
73        })
74        .with_children(|parent| {
75            parent.spawn((
76                Text::new("---"),
77                TextFont {
78                    font_size: FontSize::Px(33.0),
79                    ..default()
80                },
81                CounterDisplay,
82                TextColor(Color::srgb(0.9, 0.9, 0.9)),
83            ));
84            parent.spawn((
85                Text::new("Press SPACE to increment, BACKSPACE to decrement."),
86                TextFont {
87                    font_size: FontSize::Px(20.0),
88                    ..default()
89                },
90            ));
91        });
92}
93
94fn show_count(
95    mut query: Query<&mut Text, With<CounterDisplay>>,
96    counter: Res<Counter>,
97    other: Res<OtherSettings>,
98) {
99    if other.enabled {
100        if counter.is_changed() {
101            for mut text in query.iter_mut() {
102                text.0 = format!("Count: {}", counter.count);
103            }
104        }
105    } else {
106        for mut text in query.iter_mut() {
107            text.0 = "Disabled".into();
108        }
109    }
110}
111
112fn change_count(
113    mut counter: ResMut<Counter>,
114    keyboard: Res<ButtonInput<KeyCode>>,
115    mut commands: Commands,
116) {
117    let mut changed = false;
118    if keyboard.just_pressed(KeyCode::Space) {
119        counter.count += 1;
120        changed = true;
121    }
122    if keyboard.just_pressed(KeyCode::Backspace) || keyboard.just_pressed(KeyCode::Delete) {
123        counter.count -= 1;
124        changed = true;
125    }
126
127    if changed {
128        commands.queue(SaveSettingsDeferred(Duration::from_secs_f32(0.1)));
129    }
130}
131
132fn on_window_close(mut close: MessageReader<WindowCloseRequested>, mut commands: Commands) {
133    // Save settings immediately, then quit.
134    if let Some(_close_event) = close.read().next() {
135        commands.queue(SaveSettingsSync::IfChanged);
136        commands.write_message(AppExit::Success);
137    }
138}