docs/bevy/examples/app/settings.rs 4.2 K · 138 lines · raw · history

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.
11 use std::time::Duration;
12
13 use bevy::{
14 prelude::*,
15 settings::{
16 ReflectSettingsGroup, SaveSettingsDeferred, SaveSettingsSync, SettingsGroup, SettingsPlugin,
17 },
18 window::{ExitCondition, WindowCloseRequested},
19 };
20
21 fn 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)]
40 struct 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")]
49 struct OtherSettings {
50 enabled: bool,
51 }
52
53 impl Default for OtherSettings {
54 fn default() -> Self {
55 Self { enabled: true }
56 }
57 }
58
59 #[derive(Component)]
60 struct CounterDisplay;
61
62 fn 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
94 fn 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
112 fn 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
132 fn 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 }