Skip to main content

persisting_window_settings/
persisting_window_settings.rs

1//! Demonstrates persistence of window position settings.
2//!
3//! This app saves the app window’s settings. You may resize the window, move it
4//! around, and make it full screen; this example will remember those settings.
5//!
6//! If you close the app and restart it, the app should initialize back to the previous window position,
7//! size, and mode (i.e. fullscreen) it had at closing.
8use std::time::Duration;
9
10use bevy::{
11    prelude::*,
12    settings::{
13        ReflectSettingsGroup, SaveSettingsDeferred, SaveSettingsSync, SettingsGroup, SettingsPlugin,
14    },
15    window::{ExitCondition, WindowCloseRequested, WindowMode, WindowResized, WindowResolution},
16};
17
18fn main() {
19    App::new()
20        .add_plugins(DefaultPlugins.set(WindowPlugin {
21            // We want to intercept the exit so that we can save settings.
22            exit_condition: ExitCondition::DontExit,
23            primary_window: Some(Window {
24                title: "Settings Window".into(),
25                ..default()
26            }),
27            ..default()
28        }))
29        .add_plugins(SettingsPlugin::new(
30            "org.bevy.examples.persisting_window_settings",
31        ))
32        .add_systems(Startup, setup)
33        .add_systems(Update, (on_window_close, update_window_settings))
34        .add_plugins(init_window_pos)
35        .run();
36}
37
38/// Settings group which remembers the current window position and size
39#[derive(Resource, SettingsGroup, Reflect, Default, Clone, PartialEq)]
40#[reflect(Resource, SettingsGroup, Default)]
41#[settings_group(group = "window")]
42struct WindowSettings {
43    position: Option<IVec2>,
44    size: Option<UVec2>,
45    fullscreen: bool,
46}
47
48/// A "glue" plugin that copies the window settings to the actual window entity.
49fn init_window_pos(app: &mut App) {
50    let world = app.world_mut();
51    let Some(window_settings) = world.get_resource::<WindowSettings>() else {
52        return;
53    };
54    let window_settings = window_settings.clone();
55
56    let Ok(mut window) = world.query::<&mut Window>().single_mut(world) else {
57        warn!("window not found");
58        return;
59    };
60
61    if let Some(position) = window_settings.position {
62        window.position = WindowPosition::new(position);
63    }
64
65    if let Some(size) = window_settings.size {
66        window.resolution = WindowResolution::new(size.x, size.y);
67    }
68
69    window.mode = if window_settings.fullscreen {
70        WindowMode::BorderlessFullscreen(MonitorSelection::Current)
71    } else {
72        WindowMode::Windowed
73    };
74}
75
76fn setup(mut commands: Commands) {
77    commands.spawn((Camera::default(), Camera2d));
78    commands.spawn(Node {
79        width: percent(100),
80        height: percent(100),
81        display: Display::Flex,
82        flex_direction: FlexDirection::Column,
83        align_items: AlignItems::Center,
84        justify_content: JustifyContent::Center,
85        ..default()
86    });
87}
88
89/// System which keeps the window settings up to date when the user resizes or moves the window.
90fn update_window_settings(
91    mut move_events: MessageReader<WindowMoved>,
92    mut resize_events: MessageReader<WindowResized>,
93    windows: Query<&mut Window>,
94    window_settings: ResMut<WindowSettings>,
95    mut commands: Commands,
96) {
97    let Ok(window) = windows.single() else {
98        return;
99    };
100
101    let mut window_changed = false;
102    for _ in move_events.read() {
103        window_changed = true;
104    }
105
106    for _ in resize_events.read() {
107        window_changed = true;
108    }
109
110    if window_changed && store_window_settings(window_settings, window) {
111        commands.queue(SaveSettingsDeferred(Duration::from_secs_f32(0.5)));
112    }
113}
114
115fn store_window_settings(mut window_settings: ResMut<WindowSettings>, window: &Window) -> bool {
116    window_settings.set_if_neq(WindowSettings {
117        position: match window.position {
118            WindowPosition::At(pos) => Some(pos),
119            _ => None,
120        },
121        size: Some(UVec2::new(
122            window.resolution.width() as u32,
123            window.resolution.height() as u32,
124        )),
125        fullscreen: window.mode != WindowMode::Windowed,
126    })
127}
128
129fn on_window_close(mut close: MessageReader<WindowCloseRequested>, mut commands: Commands) {
130    // Save settings immediately, then quit.
131    if let Some(_close_event) = close.read().next() {
132        commands.queue(SaveSettingsSync::IfChanged);
133        commands.write_message(AppExit::Success);
134    }
135}