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