2023-01-19 00:38:28 +00:00
|
|
|
use bevy_ecs::{
|
|
|
|
entity::Entity,
|
|
|
|
event::EventWriter,
|
2024-01-28 21:09:23 +00:00
|
|
|
prelude::{Changed, Component},
|
|
|
|
query::QueryFilter,
|
2023-02-04 20:53:37 +00:00
|
|
|
removal_detection::RemovedComponents,
|
2024-01-28 21:09:23 +00:00
|
|
|
system::{NonSendMut, Query, SystemParamItem},
|
2023-01-19 00:38:28 +00:00
|
|
|
};
|
2024-01-28 21:09:23 +00:00
|
|
|
use bevy_utils::tracing::{error, info, warn};
|
2024-02-10 20:17:04 +00:00
|
|
|
use bevy_window::{
|
|
|
|
RawHandleWrapper, Window, WindowClosed, WindowCreated, WindowMode, WindowResized,
|
|
|
|
};
|
2023-01-19 00:38:28 +00:00
|
|
|
|
|
|
|
use winit::{
|
2023-01-29 20:27:29 +00:00
|
|
|
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize},
|
2023-01-19 00:38:28 +00:00
|
|
|
event_loop::EventLoopWindowTarget,
|
|
|
|
};
|
|
|
|
|
2024-03-03 14:33:30 +00:00
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
|
|
use winit::platform::web::WindowExtWebSys;
|
|
|
|
|
2023-02-03 16:41:39 +00:00
|
|
|
use crate::{
|
2023-07-23 01:02:40 +00:00
|
|
|
converters::{
|
|
|
|
self, convert_enabled_buttons, convert_window_level, convert_window_theme,
|
|
|
|
convert_winit_theme,
|
|
|
|
},
|
2024-01-28 21:09:23 +00:00
|
|
|
get_best_videomode, get_fitting_videomode, CreateWindowParams, WinitWindows,
|
2023-02-03 16:41:39 +00:00
|
|
|
};
|
2023-01-19 00:38:28 +00:00
|
|
|
|
2023-07-31 21:41:59 +00:00
|
|
|
/// Creates new windows on the [`winit`] backend for each entity with a newly-added
|
|
|
|
/// [`Window`] component.
|
2023-01-19 00:38:28 +00:00
|
|
|
///
|
2023-07-31 21:41:59 +00:00
|
|
|
/// If any of these entities are missing required components, those will be added with their
|
|
|
|
/// default values.
|
2023-03-01 22:45:04 +00:00
|
|
|
#[allow(clippy::too_many_arguments)]
|
2024-03-12 14:54:06 +00:00
|
|
|
pub fn create_windows<F: QueryFilter + 'static>(
|
2024-03-04 17:17:17 +00:00
|
|
|
event_loop: &EventLoopWindowTarget<crate::UserEvent>,
|
2024-01-28 21:09:23 +00:00
|
|
|
(
|
|
|
|
mut commands,
|
|
|
|
mut created_windows,
|
|
|
|
mut window_created_events,
|
|
|
|
mut winit_windows,
|
|
|
|
mut adapters,
|
|
|
|
mut handlers,
|
|
|
|
accessibility_requested,
|
|
|
|
): SystemParamItem<CreateWindowParams<F>>,
|
2023-01-19 00:38:28 +00:00
|
|
|
) {
|
2024-01-28 21:09:23 +00:00
|
|
|
for (entity, mut window) in &mut created_windows {
|
2023-01-19 00:38:28 +00:00
|
|
|
if winit_windows.get_window(entity).is_some() {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
info!(
|
|
|
|
"Creating new window {:?} ({:?})",
|
2023-02-07 14:18:13 +00:00
|
|
|
window.title.as_str(),
|
2023-01-19 00:38:28 +00:00
|
|
|
entity
|
|
|
|
);
|
|
|
|
|
2023-03-01 22:45:04 +00:00
|
|
|
let winit_window = winit_windows.create_window(
|
|
|
|
event_loop,
|
|
|
|
entity,
|
|
|
|
&window,
|
|
|
|
&mut adapters,
|
|
|
|
&mut handlers,
|
2023-09-02 18:35:06 +00:00
|
|
|
&accessibility_requested,
|
2023-03-01 22:45:04 +00:00
|
|
|
);
|
2023-06-05 21:04:22 +00:00
|
|
|
|
|
|
|
if let Some(theme) = winit_window.theme() {
|
|
|
|
window.window_theme = Some(convert_winit_theme(theme));
|
|
|
|
}
|
|
|
|
|
2023-02-07 14:18:13 +00:00
|
|
|
window
|
2023-01-19 00:38:28 +00:00
|
|
|
.resolution
|
2023-12-14 14:56:40 +00:00
|
|
|
.set_scale_factor(winit_window.scale_factor() as f32);
|
2023-01-19 00:38:28 +00:00
|
|
|
commands
|
|
|
|
.entity(entity)
|
2024-04-30 14:13:07 +00:00
|
|
|
.insert(RawHandleWrapper::new(winit_window).unwrap())
|
2023-02-07 14:18:13 +00:00
|
|
|
.insert(CachedWindow {
|
|
|
|
window: window.clone(),
|
2023-01-19 00:38:28 +00:00
|
|
|
});
|
|
|
|
|
2024-03-03 14:33:30 +00:00
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
|
|
{
|
|
|
|
if window.fit_canvas_to_parent {
|
|
|
|
let canvas = winit_window
|
|
|
|
.canvas()
|
|
|
|
.expect("window.canvas() can only be called in main thread.");
|
|
|
|
let style = canvas.style();
|
|
|
|
style.set_property("width", "100%").unwrap();
|
|
|
|
style.set_property("height", "100%").unwrap();
|
|
|
|
}
|
|
|
|
}
|
2024-01-28 21:09:23 +00:00
|
|
|
window_created_events.send(WindowCreated { window: entity });
|
2023-01-19 00:38:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-31 21:41:59 +00:00
|
|
|
pub(crate) fn despawn_windows(
|
2023-02-04 20:53:37 +00:00
|
|
|
mut closed: RemovedComponents<Window>,
|
2023-01-31 01:47:00 +00:00
|
|
|
window_entities: Query<&Window>,
|
2023-01-19 00:38:28 +00:00
|
|
|
mut close_events: EventWriter<WindowClosed>,
|
|
|
|
mut winit_windows: NonSendMut<WinitWindows>,
|
|
|
|
) {
|
2023-09-15 12:37:20 +00:00
|
|
|
for window in closed.read() {
|
2023-01-19 00:38:28 +00:00
|
|
|
info!("Closing window {:?}", window);
|
2023-01-31 01:47:00 +00:00
|
|
|
// Guard to verify that the window is in fact actually gone,
|
|
|
|
// rather than having the component added and removed in the same frame.
|
|
|
|
if !window_entities.contains(window) {
|
|
|
|
winit_windows.remove_window(window);
|
|
|
|
close_events.send(WindowClosed { window });
|
|
|
|
}
|
2023-01-19 00:38:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-07 14:18:13 +00:00
|
|
|
/// The cached state of the window so we can check which properties were changed from within the app.
|
2023-01-19 00:38:28 +00:00
|
|
|
#[derive(Debug, Clone, Component)]
|
2023-02-07 14:18:13 +00:00
|
|
|
pub struct CachedWindow {
|
|
|
|
pub window: Window,
|
2023-01-19 00:38:28 +00:00
|
|
|
}
|
|
|
|
|
2023-07-31 21:41:59 +00:00
|
|
|
/// Propagates changes from [`Window`] entities to the [`winit`] backend.
|
|
|
|
///
|
|
|
|
/// # Notes
|
|
|
|
///
|
|
|
|
/// - [`Window::present_mode`] and [`Window::composite_alpha_mode`] changes are handled by the `bevy_render` crate.
|
|
|
|
/// - [`Window::transparent`] cannot be changed after the window is created.
|
|
|
|
/// - [`Window::canvas`] cannot be changed after the window is created.
|
|
|
|
/// - [`Window::focused`] cannot be manually changed to `false` after the window is created.
|
|
|
|
pub(crate) fn changed_windows(
|
2023-02-07 14:18:13 +00:00
|
|
|
mut changed_windows: Query<(Entity, &mut Window, &mut CachedWindow), Changed<Window>>,
|
2023-01-19 00:38:28 +00:00
|
|
|
winit_windows: NonSendMut<WinitWindows>,
|
2024-01-28 21:09:23 +00:00
|
|
|
mut window_resized: EventWriter<WindowResized>,
|
2023-01-19 00:38:28 +00:00
|
|
|
) {
|
2023-02-07 14:18:13 +00:00
|
|
|
for (entity, mut window, mut cache) in &mut changed_windows {
|
2024-02-10 20:17:04 +00:00
|
|
|
let Some(winit_window) = winit_windows.get_window(entity) else {
|
|
|
|
continue;
|
|
|
|
};
|
2023-01-19 00:38:28 +00:00
|
|
|
|
2024-02-10 20:17:04 +00:00
|
|
|
if window.title != cache.window.title {
|
|
|
|
winit_window.set_title(window.title.as_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
if window.mode != cache.window.mode {
|
|
|
|
let new_mode = match window.mode {
|
|
|
|
WindowMode::BorderlessFullscreen => {
|
|
|
|
Some(Some(winit::window::Fullscreen::Borderless(None)))
|
|
|
|
}
|
|
|
|
mode @ (WindowMode::Fullscreen | WindowMode::SizedFullscreen) => {
|
|
|
|
if let Some(current_monitor) = winit_window.current_monitor() {
|
|
|
|
let videomode = match mode {
|
|
|
|
WindowMode::Fullscreen => get_best_videomode(¤t_monitor),
|
|
|
|
WindowMode::SizedFullscreen => get_fitting_videomode(
|
|
|
|
¤t_monitor,
|
|
|
|
window.width() as u32,
|
|
|
|
window.height() as u32,
|
|
|
|
),
|
|
|
|
_ => unreachable!(),
|
|
|
|
};
|
|
|
|
|
|
|
|
Some(Some(winit::window::Fullscreen::Exclusive(videomode)))
|
|
|
|
} else {
|
|
|
|
warn!("Could not determine current monitor, ignoring exclusive fullscreen request for window {:?}", window.title);
|
|
|
|
None
|
2023-01-19 00:38:28 +00:00
|
|
|
}
|
2024-02-10 20:17:04 +00:00
|
|
|
}
|
|
|
|
WindowMode::Windowed => Some(None),
|
|
|
|
};
|
2023-01-19 00:38:28 +00:00
|
|
|
|
2024-02-10 20:17:04 +00:00
|
|
|
if let Some(new_mode) = new_mode {
|
2023-01-19 00:38:28 +00:00
|
|
|
if winit_window.fullscreen() != new_mode {
|
|
|
|
winit_window.set_fullscreen(new_mode);
|
|
|
|
}
|
|
|
|
}
|
2024-02-10 20:17:04 +00:00
|
|
|
}
|
|
|
|
if window.resolution != cache.window.resolution {
|
|
|
|
let physical_size = PhysicalSize::new(
|
|
|
|
window.resolution.physical_width(),
|
|
|
|
window.resolution.physical_height(),
|
|
|
|
);
|
|
|
|
if let Some(size_now) = winit_window.request_inner_size(physical_size) {
|
|
|
|
crate::react_to_resize(&mut window, size_now, &mut window_resized, entity);
|
2023-01-19 00:38:28 +00:00
|
|
|
}
|
2024-02-10 20:17:04 +00:00
|
|
|
}
|
2023-01-19 00:38:28 +00:00
|
|
|
|
2024-02-10 20:17:04 +00:00
|
|
|
if window.physical_cursor_position() != cache.window.physical_cursor_position() {
|
|
|
|
if let Some(physical_position) = window.physical_cursor_position() {
|
|
|
|
let position = PhysicalPosition::new(physical_position.x, physical_position.y);
|
2023-01-19 00:38:28 +00:00
|
|
|
|
2024-02-10 20:17:04 +00:00
|
|
|
if let Err(err) = winit_window.set_cursor_position(position) {
|
|
|
|
error!("could not set cursor position: {:?}", err);
|
2023-01-19 00:38:28 +00:00
|
|
|
}
|
|
|
|
}
|
2024-02-10 20:17:04 +00:00
|
|
|
}
|
2023-01-19 00:38:28 +00:00
|
|
|
|
2024-02-10 20:17:04 +00:00
|
|
|
if window.cursor.icon != cache.window.cursor.icon {
|
|
|
|
winit_window.set_cursor_icon(converters::convert_cursor_icon(window.cursor.icon));
|
|
|
|
}
|
2023-01-19 00:38:28 +00:00
|
|
|
|
2024-02-10 20:17:04 +00:00
|
|
|
if window.cursor.grab_mode != cache.window.cursor.grab_mode {
|
|
|
|
crate::winit_windows::attempt_grab(winit_window, window.cursor.grab_mode);
|
|
|
|
}
|
2023-01-19 00:38:28 +00:00
|
|
|
|
2024-02-10 20:17:04 +00:00
|
|
|
if window.cursor.visible != cache.window.cursor.visible {
|
|
|
|
winit_window.set_cursor_visible(window.cursor.visible);
|
|
|
|
}
|
2023-01-19 00:38:28 +00:00
|
|
|
|
2024-02-10 20:17:04 +00:00
|
|
|
if window.cursor.hit_test != cache.window.cursor.hit_test {
|
|
|
|
if let Err(err) = winit_window.set_cursor_hittest(window.cursor.hit_test) {
|
|
|
|
window.cursor.hit_test = cache.window.cursor.hit_test;
|
|
|
|
warn!(
|
|
|
|
"Could not set cursor hit test for window {:?}: {:?}",
|
|
|
|
window.title, err
|
|
|
|
);
|
2023-01-19 00:38:28 +00:00
|
|
|
}
|
2024-02-10 20:17:04 +00:00
|
|
|
}
|
2023-01-19 00:38:28 +00:00
|
|
|
|
2024-02-10 20:17:04 +00:00
|
|
|
if window.decorations != cache.window.decorations
|
|
|
|
&& window.decorations != winit_window.is_decorated()
|
|
|
|
{
|
|
|
|
winit_window.set_decorations(window.decorations);
|
|
|
|
}
|
2023-01-19 00:38:28 +00:00
|
|
|
|
2024-02-10 20:17:04 +00:00
|
|
|
if window.resizable != cache.window.resizable
|
|
|
|
&& window.resizable != winit_window.is_resizable()
|
|
|
|
{
|
|
|
|
winit_window.set_resizable(window.resizable);
|
|
|
|
}
|
|
|
|
|
|
|
|
if window.enabled_buttons != cache.window.enabled_buttons {
|
|
|
|
winit_window.set_enabled_buttons(convert_enabled_buttons(window.enabled_buttons));
|
|
|
|
}
|
2023-01-19 00:38:28 +00:00
|
|
|
|
2024-02-10 20:17:04 +00:00
|
|
|
if window.resize_constraints != cache.window.resize_constraints {
|
|
|
|
let constraints = window.resize_constraints.check_constraints();
|
|
|
|
let min_inner_size = LogicalSize {
|
|
|
|
width: constraints.min_width,
|
|
|
|
height: constraints.min_height,
|
|
|
|
};
|
|
|
|
let max_inner_size = LogicalSize {
|
|
|
|
width: constraints.max_width,
|
|
|
|
height: constraints.max_height,
|
|
|
|
};
|
|
|
|
|
|
|
|
winit_window.set_min_inner_size(Some(min_inner_size));
|
|
|
|
if constraints.max_width.is_finite() && constraints.max_height.is_finite() {
|
|
|
|
winit_window.set_max_inner_size(Some(max_inner_size));
|
2023-07-23 01:02:40 +00:00
|
|
|
}
|
2024-02-10 20:17:04 +00:00
|
|
|
}
|
2023-07-23 01:02:40 +00:00
|
|
|
|
2024-02-10 20:17:04 +00:00
|
|
|
if window.position != cache.window.position {
|
|
|
|
if let Some(position) = crate::winit_window_position(
|
|
|
|
&window.position,
|
|
|
|
&window.resolution,
|
|
|
|
winit_window.available_monitors(),
|
|
|
|
winit_window.primary_monitor(),
|
|
|
|
winit_window.current_monitor(),
|
|
|
|
) {
|
|
|
|
let should_set = match winit_window.outer_position() {
|
|
|
|
Ok(current_position) => current_position != position,
|
|
|
|
_ => true,
|
2023-01-19 00:38:28 +00:00
|
|
|
};
|
|
|
|
|
2024-02-10 20:17:04 +00:00
|
|
|
if should_set {
|
|
|
|
winit_window.set_outer_position(position);
|
2023-01-19 00:38:28 +00:00
|
|
|
}
|
|
|
|
}
|
2024-02-10 20:17:04 +00:00
|
|
|
}
|
2023-01-19 00:38:28 +00:00
|
|
|
|
2024-02-10 20:17:04 +00:00
|
|
|
if let Some(maximized) = window.internal.take_maximize_request() {
|
|
|
|
winit_window.set_maximized(maximized);
|
|
|
|
}
|
2023-01-19 00:38:28 +00:00
|
|
|
|
2024-02-10 20:17:04 +00:00
|
|
|
if let Some(minimized) = window.internal.take_minimize_request() {
|
|
|
|
winit_window.set_minimized(minimized);
|
|
|
|
}
|
2023-01-19 00:38:28 +00:00
|
|
|
|
2024-02-10 20:17:04 +00:00
|
|
|
if window.focused != cache.window.focused && window.focused {
|
|
|
|
winit_window.focus_window();
|
|
|
|
}
|
2023-01-19 00:38:28 +00:00
|
|
|
|
2024-02-10 20:17:04 +00:00
|
|
|
if window.window_level != cache.window.window_level {
|
|
|
|
winit_window.set_window_level(convert_window_level(window.window_level));
|
|
|
|
}
|
2023-01-19 00:38:28 +00:00
|
|
|
|
2024-02-10 20:17:04 +00:00
|
|
|
// Currently unsupported changes
|
|
|
|
if window.transparent != cache.window.transparent {
|
|
|
|
window.transparent = cache.window.transparent;
|
|
|
|
warn!("Winit does not currently support updating transparency after window creation.");
|
|
|
|
}
|
2023-01-19 00:38:28 +00:00
|
|
|
|
2024-02-10 20:17:04 +00:00
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
|
|
if window.canvas != cache.window.canvas {
|
2024-04-20 09:15:42 +00:00
|
|
|
window.canvas.clone_from(&cache.window.canvas);
|
2024-02-10 20:17:04 +00:00
|
|
|
warn!(
|
|
|
|
"Bevy currently doesn't support modifying the window canvas after initialization."
|
|
|
|
);
|
|
|
|
}
|
2023-01-29 20:27:29 +00:00
|
|
|
|
2024-02-10 20:17:04 +00:00
|
|
|
if window.ime_enabled != cache.window.ime_enabled {
|
|
|
|
winit_window.set_ime_allowed(window.ime_enabled);
|
|
|
|
}
|
2023-01-29 20:27:29 +00:00
|
|
|
|
2024-02-10 20:17:04 +00:00
|
|
|
if window.ime_position != cache.window.ime_position {
|
|
|
|
winit_window.set_ime_cursor_area(
|
|
|
|
LogicalPosition::new(window.ime_position.x, window.ime_position.y),
|
|
|
|
PhysicalSize::new(10, 10),
|
|
|
|
);
|
|
|
|
}
|
2023-06-05 21:04:22 +00:00
|
|
|
|
2024-02-10 20:17:04 +00:00
|
|
|
if window.window_theme != cache.window.window_theme {
|
|
|
|
winit_window.set_theme(window.window_theme.map(convert_window_theme));
|
|
|
|
}
|
2023-08-20 22:42:07 +00:00
|
|
|
|
2024-02-10 20:17:04 +00:00
|
|
|
if window.visible != cache.window.visible {
|
|
|
|
winit_window.set_visible(window.visible);
|
2023-01-19 00:38:28 +00:00
|
|
|
}
|
2024-02-10 20:17:04 +00:00
|
|
|
|
|
|
|
cache.window = window.clone();
|
2023-01-19 00:38:28 +00:00
|
|
|
}
|
|
|
|
}
|