Skip to content

Commit

Permalink
feat: bump bevy to v0.14
Browse files Browse the repository at this point in the history
  • Loading branch information
umut-sahin committed Jul 9, 2024
1 parent 529c5f5 commit d9e32ad
Show file tree
Hide file tree
Showing 10 changed files with 28 additions and 210 deletions.
8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ authors = [
]

[dependencies]
bevy = { version = "0.13.1", features = ["serialize"] }
bevy-persistent = { version = "0.5" }
bevy = { version = "0.14", features = ["serialize"] }
bevy-persistent = { version = "0.6" }
serde = { version = "1.0", features = ["derive"] }
winit = { version = "0.29", default-features = false }
winit = { version = "0.30", default-features = false }

[dev-dependencies]
dirs = { version = "5.0" }
bevy-persistent = { version = "0.5", features = ["toml"] }
bevy-persistent = { version = "0.6", features = ["toml"] }

[features]
default = []
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ let state_directory = dirs::data_dir()
Time to create the primary window:

```rust
app.world.spawn((
app.world_mut().spawn((
PrimaryWindow,
PersistentWindowBundle {
window: Window { title: "I am the primary window!".to_owned(), ..Default::default() },
Expand Down
2 changes: 1 addition & 1 deletion examples/controlling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ fn main() {
.unwrap_or(Path::new("session").join("data").join("state"))
.join("primary");

app.world.spawn((
app.world_mut().spawn((
PrimaryWindow,
PersistentWindowBundle {
window: Window {
Expand Down
2 changes: 1 addition & 1 deletion examples/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ fn main() {
.unwrap_or(Path::new("session").join("data").join("state"))
.join("setup");

app.world.spawn((
app.world_mut().spawn((
PrimaryWindow,
PersistentWindowBundle {
window: Window { title: "I persist!".to_owned(), ..Default::default() },
Expand Down
2 changes: 1 addition & 1 deletion examples/spawning.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ fn main() {
.unwrap_or(Path::new("session").join("data").join("state"))
.join("primary");

app.world.spawn((
app.world_mut().spawn((
PrimaryWindow,
PersistentWindowBundle {
window: Window { title: "I am primary...".to_owned(), ..Default::default() },
Expand Down
8 changes: 0 additions & 8 deletions src/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ pub struct WindowState {
/// Mode of the window.
pub mode: WindowMode,

/// Name of the monitor that the window is in.
/// (`None` means pick the best monitor)
pub monitor: Option<String>,

/// Resolution of the window.
/// (`None` means pick the best resolution)
pub resolution: Option<(u32, u32)>,
Expand All @@ -34,7 +30,6 @@ impl WindowState {
pub fn borderless_fullscreen() -> WindowState {
WindowState {
mode: WindowMode::BorderlessFullscreen,
monitor: None,
resolution: None,
scale: None,
position: None,
Expand All @@ -46,7 +41,6 @@ impl WindowState {
pub fn fullscreen() -> WindowState {
WindowState {
mode: WindowMode::Fullscreen,
monitor: None,
resolution: None,
scale: None,
position: None,
Expand All @@ -57,7 +51,6 @@ impl WindowState {
pub fn sized_fullscreen() -> WindowState {
WindowState {
mode: WindowMode::SizedFullscreen,
monitor: None,
resolution: None,
scale: None,
position: None,
Expand All @@ -69,7 +62,6 @@ impl WindowState {
pub fn windowed(width: u32, height: u32) -> WindowState {
WindowState {
mode: WindowMode::Windowed,
monitor: None,
resolution: Some((width, height)),
scale: None,
position: None,
Expand Down
39 changes: 6 additions & 33 deletions src/plugins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,46 +10,19 @@ use crate::prelude::*;
/// # Panics
///
/// - Panics if it's added to the [App]
/// before [WinitPlugin](bevy::winit::WinitPlugin),
/// which is in the [DefaultPlugins].
/// before [WinitPlugin](bevy::winit::WinitPlugin),
/// which is in the [DefaultPlugins].
pub struct PersistentWindowsPlugin;

impl Plugin for PersistentWindowsPlugin {
fn build(&self, app: &mut App) {
let event_loop = app
.world
.get_non_send_resource::<EventLoop<RequestRedraw>>()
.expect("persistent windows plugin is added before winit plugin");
let mut persistent_windows =
app.world_mut().query::<(&mut Window, &Persistent<WindowState>)>();

match utils::available_monitors(event_loop) {
Some(available_monitors) => {
let best_monitor = utils::best_monitor(&available_monitors);

let mut persistent_windows =
app.world.query::<(&mut Window, &mut Persistent<WindowState>)>();

for (mut window, mut state) in persistent_windows.iter_mut(&mut app.world) {
utils::adjust_to_monitor(
&available_monitors,
best_monitor,
&mut window,
&mut state,
);
}
},
None => {
log::error!("unable to persist windows as no monitors are available");
},
for (mut window, state) in persistent_windows.iter_mut(app.world_mut()) {
utils::apply_state_to_window(&mut window, state);
}

let event_loop = app.world.remove_non_send_resource::<EventLoop<RequestRedraw>>().unwrap();

let mut create_window = SystemState::<CreateWindowParams>::from_world(&mut app.world);
create_windows(event_loop.deref(), create_window.get_mut(&mut app.world));
create_window.apply(&mut app.world);

app.insert_non_send_resource(event_loop);

app.add_systems(
PreUpdate,
(
Expand Down
19 changes: 2 additions & 17 deletions src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,21 @@ pub(crate) use crate::{
utils,
};
pub(crate) use bevy::{
ecs::system::SystemState,
log,
prelude::*,
utils::HashMap,
window::{
RequestRedraw,
WindowMode,
WindowResized,
WindowResolution,
WindowScaleFactorChanged,
},
winit::{
create_windows,
CreateWindowParams,
WinitWindows,
},
winit::WinitWindows,
};
pub(crate) use bevy_persistent::prelude::*;
pub(crate) use serde::{
Deserialize,
Serialize,
};
pub(crate) use std::ops::{
Deref,
DerefMut,
};
pub(crate) use winit::{
event_loop::EventLoop,
monitor::MonitorHandle,
};
pub(crate) use std::ops::DerefMut;

pub use crate::{
PersistentWindowBundle,
Expand Down
2 changes: 1 addition & 1 deletion src/systems.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,6 @@ pub fn on_persistent_window_state_changed(
}

state.persist().ok();
utils::apply_state_to_window(window.deref_mut(), state.deref_mut(), None);
utils::apply_state_to_window(window.deref_mut(), state.deref_mut());
}
}
154 changes: 11 additions & 143 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,152 +2,21 @@
use crate::prelude::*;

/// Gets available monitors from the event loop.
///
/// Result is a mapping from monitor name to monitor index and monitor handle.
///
/// Returns [None] if no monitors are available.
pub fn available_monitors(
event_loop: &EventLoop<RequestRedraw>,
) -> Option<HashMap<String, (usize, MonitorHandle)>> {
let available_monitors = event_loop
.available_monitors()
.enumerate()
.filter_map(|(index, monitor)| monitor.name().map(|name| (name, (index, monitor))))
.collect::<HashMap<String, (usize, MonitorHandle)>>();

if !available_monitors.is_empty() { Some(available_monitors) } else { None }
}

/// Gets the best monitor from available monitors.
///
/// Best monitor is selected according to:
/// - Width `(Bigger is Better)`
/// - Height `(Bigger is Better)`
/// - Refresh rate `(Bigger is Better)`
/// - Monitor index `(Smaller is Better)`
/// in that order.
///
/// If refresh rate information is not available, monitor is not preferred.
///
/// Result is a tuple of `monitor name` and a tuple of `monitor index` and `monitor handle`.
///
/// # Panics
///
/// - Panics if `available_monitors` is empty.
pub fn best_monitor(
available_monitors: &HashMap<String, (usize, MonitorHandle)>,
) -> (&String, &(usize, MonitorHandle)) {
let mut available_monitors = available_monitors.iter();

let mut selected_monitor = available_monitors.next().unwrap();
for current_monitor in available_monitors {
let selected_monitor_handle = &selected_monitor.1.1;
let current_monitor_handle = &current_monitor.1.1;

let selected_monitor_size = selected_monitor_handle.size();
let current_monitor_size = current_monitor_handle.size();

if current_monitor_size.width < selected_monitor_size.width {
continue;
}
if current_monitor_size.height < selected_monitor_size.height {
continue;
}

let selected_monitor_refresh_rate = selected_monitor_handle.refresh_rate_millihertz();
let current_monitor_refresh_rate = current_monitor_handle.refresh_rate_millihertz();

match (current_monitor_refresh_rate, selected_monitor_refresh_rate) {
(Some(_), None) => {},
(None, Some(_)) => continue,

(Some(current_monitor_refresh_rate), Some(selected_monitor_refresh_rate))
if current_monitor_refresh_rate != selected_monitor_refresh_rate =>
{
if current_monitor_refresh_rate < selected_monitor_refresh_rate {
continue;
}
},

_ => {
let selected_monitor_index = &selected_monitor.1.0;
let current_monitor_index = &current_monitor.1.0;
/// Propagates the changes made to the state to the window.
pub fn apply_state_to_window(window: &mut Window, state: &Persistent<WindowState>) {
window.mode = state.mode;

if current_monitor_index > selected_monitor_index {
continue;
}
},
if let Some((width, height)) = state.resolution {
window.resolution = WindowResolution::default();
if let Some(scale) = state.scale {
window.resolution.set_scale_factor_override(Some(scale as f32));
}

selected_monitor = current_monitor;
window.resolution.set_physical_resolution(width, height);
}
selected_monitor
}

/// Adjusts a persistent window for a monitor.
///
/// If state already has an existing monitor, it's used.
/// Otherwise, the best monitor is used.
///
/// Changes made by this function persist immediately.
pub fn adjust_to_monitor(
available_monitors: &HashMap<String, (usize, MonitorHandle)>,
best_monitor: (&String, &(usize, MonitorHandle)),
window: &mut Window,
state: &mut Persistent<WindowState>,
) {
let (name, (index, handle)) = state
.monitor
.as_ref()
.and_then(|name| available_monitors.get(name).map(|monitor| (name, monitor)))
.unwrap_or(best_monitor);

let new_state = WindowState {
monitor: Some(name.to_string()),
mode: state.mode,
resolution: Some(state.resolution.unwrap_or_else(|| {
let best_video_mode = bevy::winit::get_best_videomode(handle);
let best_resolution = best_video_mode.size();
(best_resolution.width, best_resolution.height)
})),
scale: Some(state.scale.unwrap_or_else(|| handle.scale_factor())),
position: state.position,
sync: state.sync,
};

if &new_state != state.get() {
state.set(new_state).ok();
}

apply_state_to_window(window, state, Some(*index))
}

/// Propagates the changes made to the state to the window.
pub fn apply_state_to_window(
window: &mut Window,
state: &Persistent<WindowState>,
monitor_index: Option<usize>,
) {
let mode = state.mode;

let mut resolution = WindowResolution::default();
if let Some(scale) = state.scale {
resolution.set_scale_factor_override(Some(scale as f32));
if let Some(position) = state.position {
window.position = WindowPosition::new(position.into());
}
resolution.set_physical_resolution(state.resolution.unwrap().0, state.resolution.unwrap().1);

let position = if let Some(position) = state.position {
WindowPosition::new(position.into())
} else {
monitor_index
.map(|monitor_index| WindowPosition::Centered(MonitorSelection::Index(monitor_index)))
.unwrap_or(WindowPosition::Automatic)
};

window.mode = mode;
window.resolution = resolution;
window.position = position;
}

/// Propagates the changes made to the window to the state.
Expand All @@ -156,7 +25,6 @@ pub fn apply_window_to_state(
state: &mut Persistent<WindowState>,
winit_window: &winit::window::Window,
) {
let monitor = winit_window.current_monitor().and_then(|monitor| monitor.name());
let mode = window.mode;
let resolution = Some((winit_window.inner_size().width, winit_window.inner_size().height));
let scale = Some(window.scale_factor() as f64);
Expand All @@ -170,7 +38,7 @@ pub fn apply_window_to_state(
}
});

let new_state = WindowState { monitor, mode, resolution, scale, position, sync: state.sync };
let new_state = WindowState { mode, resolution, scale, position, sync: state.sync };
if new_state != *state.get() {
state.set(new_state).ok();
state.sync = false;
Expand Down

0 comments on commit d9e32ad

Please sign in to comment.