mirror of
https://github.com/bevyengine/bevy
synced 2025-02-16 14:08:32 +00:00
can change window settings at runtime (#644)
can change window settings at runtime
This commit is contained in:
parent
9c48e5cccb
commit
76cc25823d
13 changed files with 251 additions and 60 deletions
|
@ -74,8 +74,8 @@ pub fn camera_system<T: CameraProjection + Component>(
|
|||
}
|
||||
for (entity, mut camera, mut camera_projection) in &mut query.iter() {
|
||||
if let Some(window) = windows.get(camera.window) {
|
||||
if changed_window_ids.contains(&window.id) || added_cameras.contains(&entity) {
|
||||
camera_projection.update(window.width as usize, window.height as usize);
|
||||
if changed_window_ids.contains(&window.id()) || added_cameras.contains(&entity) {
|
||||
camera_projection.update(window.width() as usize, window.height() as usize);
|
||||
camera.projection_matrix = camera_projection.get_projection_matrix();
|
||||
camera.depth_calculation = camera_projection.depth_calculation();
|
||||
}
|
||||
|
|
|
@ -56,11 +56,11 @@ impl Node for WindowSwapChainNode {
|
|||
// create window swapchain when window is resized or created
|
||||
if self
|
||||
.window_created_event_reader
|
||||
.find_latest(&window_created_events, |e| e.id == window.id)
|
||||
.find_latest(&window_created_events, |e| e.id == window.id())
|
||||
.is_some()
|
||||
|| self
|
||||
.window_resized_event_reader
|
||||
.find_latest(&window_resized_events, |e| e.id == window.id)
|
||||
.find_latest(&window_resized_events, |e| e.id == window.id())
|
||||
.is_some()
|
||||
{
|
||||
render_resource_context.create_swap_chain(window);
|
||||
|
|
|
@ -56,11 +56,11 @@ impl Node for WindowTextureNode {
|
|||
|
||||
if self
|
||||
.window_created_event_reader
|
||||
.find_latest(&window_created_events, |e| e.id == window.id)
|
||||
.find_latest(&window_created_events, |e| e.id == window.id())
|
||||
.is_some()
|
||||
|| self
|
||||
.window_resized_event_reader
|
||||
.find_latest(&window_resized_events, |e| e.id == window.id)
|
||||
.find_latest(&window_resized_events, |e| e.id == window.id())
|
||||
.is_some()
|
||||
{
|
||||
let render_resource_context = render_context.resources_mut();
|
||||
|
@ -68,8 +68,8 @@ impl Node for WindowTextureNode {
|
|||
render_resource_context.remove_texture(old_texture);
|
||||
}
|
||||
|
||||
self.descriptor.size.width = window.width;
|
||||
self.descriptor.size.height = window.height;
|
||||
self.descriptor.size.width = window.width();
|
||||
self.descriptor.size.height = window.height();
|
||||
let texture_resource = render_resource_context.create_texture(self.descriptor);
|
||||
output.set(WINDOW_TEXTURE, RenderResourceId::Texture(texture_resource));
|
||||
}
|
||||
|
|
|
@ -105,7 +105,7 @@ impl FlexSurface {
|
|||
|
||||
pub fn update_window(&mut self, window: &Window) {
|
||||
let stretch = &mut self.stretch;
|
||||
let node = self.window_nodes.entry(window.id).or_insert_with(|| {
|
||||
let node = self.window_nodes.entry(window.id()).or_insert_with(|| {
|
||||
stretch
|
||||
.new_node(stretch::style::Style::default(), Vec::new())
|
||||
.unwrap()
|
||||
|
@ -116,8 +116,8 @@ impl FlexSurface {
|
|||
*node,
|
||||
stretch::style::Style {
|
||||
size: stretch::geometry::Size {
|
||||
width: stretch::style::Dimension::Points(window.width as f32),
|
||||
height: stretch::style::Dimension::Points(window.height as f32),
|
||||
width: stretch::style::Dimension::Points(window.width() as f32),
|
||||
height: stretch::style::Dimension::Points(window.height() as f32),
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
|
@ -189,7 +189,7 @@ pub fn flex_node_system(
|
|||
|
||||
// update window children (for now assuming all Nodes live in the primary window)
|
||||
if let Some(primary_window) = windows.get_primary() {
|
||||
flex_surface.set_window_children(primary_window.id, root_node_query.iter().iter());
|
||||
flex_surface.set_window_children(primary_window.id(), root_node_query.iter().iter());
|
||||
}
|
||||
|
||||
// update children
|
||||
|
|
|
@ -274,22 +274,25 @@ impl RenderResourceContext for WgpuRenderResourceContext {
|
|||
|
||||
let swap_chain_descriptor: wgpu::SwapChainDescriptor = window.wgpu_into();
|
||||
let surface = surfaces
|
||||
.get(&window.id)
|
||||
.get(&window.id())
|
||||
.expect("No surface found for window");
|
||||
let swap_chain = self
|
||||
.device
|
||||
.create_swap_chain(surface, &swap_chain_descriptor);
|
||||
|
||||
window_swap_chains.insert(window.id, swap_chain);
|
||||
window_swap_chains.insert(window.id(), swap_chain);
|
||||
}
|
||||
|
||||
fn next_swap_chain_texture(&self, window: &bevy_window::Window) -> TextureId {
|
||||
if let Some(texture_id) = self.try_next_swap_chain_texture(window.id) {
|
||||
if let Some(texture_id) = self.try_next_swap_chain_texture(window.id()) {
|
||||
texture_id
|
||||
} else {
|
||||
self.resources.window_swap_chains.write().remove(&window.id);
|
||||
self.resources
|
||||
.window_swap_chains
|
||||
.write()
|
||||
.remove(&window.id());
|
||||
self.create_swap_chain(window);
|
||||
self.try_next_swap_chain_texture(window.id)
|
||||
self.try_next_swap_chain_texture(window.id())
|
||||
.expect("Failed to acquire next swap chain texture!")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,9 +74,9 @@ impl WgpuRenderer {
|
|||
#[cfg(feature = "bevy_winit")]
|
||||
{
|
||||
let winit_windows = resources.get::<bevy_winit::WinitWindows>().unwrap();
|
||||
let winit_window = winit_windows.get_window(window.id).unwrap();
|
||||
let winit_window = winit_windows.get_window(window.id()).unwrap();
|
||||
let surface = unsafe { self.instance.create_surface(winit_window.deref()) };
|
||||
render_resource_context.set_window_surface(window.id, surface);
|
||||
render_resource_context.set_window_surface(window.id(), surface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -563,9 +563,9 @@ impl WgpuFrom<&Window> for wgpu::SwapChainDescriptor {
|
|||
wgpu::SwapChainDescriptor {
|
||||
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
|
||||
format: wgpu::TextureFormat::Bgra8UnormSrgb,
|
||||
width: window.width,
|
||||
height: window.height,
|
||||
present_mode: if window.vsync {
|
||||
width: window.width(),
|
||||
height: window.height(),
|
||||
present_mode: if window.vsync() {
|
||||
wgpu::PresentMode::Fifo
|
||||
} else {
|
||||
wgpu::PresentMode::Immediate
|
||||
|
|
|
@ -33,16 +33,41 @@ impl Default for WindowId {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct Window {
|
||||
pub id: WindowId,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub title: String,
|
||||
pub vsync: bool,
|
||||
pub resizable: bool,
|
||||
pub decorations: bool,
|
||||
pub mode: WindowMode,
|
||||
id: WindowId,
|
||||
width: u32,
|
||||
height: u32,
|
||||
title: String,
|
||||
vsync: bool,
|
||||
resizable: bool,
|
||||
decorations: bool,
|
||||
mode: WindowMode,
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub canvas: Option<String>,
|
||||
command_queue: Vec<WindowCommand>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum WindowCommand {
|
||||
SetWindowMode {
|
||||
mode: WindowMode,
|
||||
resolution: (u32, u32),
|
||||
},
|
||||
SetTitle {
|
||||
title: String,
|
||||
},
|
||||
SetResolution {
|
||||
width: u32,
|
||||
height: u32,
|
||||
},
|
||||
SetVsync {
|
||||
vsync: bool,
|
||||
},
|
||||
SetResizable {
|
||||
resizable: bool,
|
||||
},
|
||||
SetDecorations {
|
||||
decorations: bool,
|
||||
},
|
||||
}
|
||||
|
||||
/// Defines the way a window is displayed
|
||||
|
@ -70,8 +95,91 @@ impl Window {
|
|||
mode: window_descriptor.mode,
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
canvas: window_descriptor.canvas.clone(),
|
||||
command_queue: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> WindowId {
|
||||
self.id
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn width(&self) -> u32 {
|
||||
self.width
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn height(&self) -> u32 {
|
||||
self.height
|
||||
}
|
||||
|
||||
pub fn set_resolution(&mut self, width: u32, height: u32) {
|
||||
self.width = width;
|
||||
self.height = height;
|
||||
self.command_queue
|
||||
.push(WindowCommand::SetResolution { width, height });
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn update_resolution_from_backend(&mut self, width: u32, height: u32) {
|
||||
self.width = width;
|
||||
self.height = height;
|
||||
}
|
||||
|
||||
pub fn title(&self) -> &str {
|
||||
&self.title
|
||||
}
|
||||
|
||||
pub fn set_title(&mut self, title: String) {
|
||||
self.title = title.to_string();
|
||||
self.command_queue.push(WindowCommand::SetTitle { title });
|
||||
}
|
||||
|
||||
pub fn vsync(&self) -> bool {
|
||||
self.vsync
|
||||
}
|
||||
|
||||
pub fn set_vsync(&mut self, vsync: bool) {
|
||||
self.vsync = vsync;
|
||||
self.command_queue.push(WindowCommand::SetVsync { vsync });
|
||||
}
|
||||
|
||||
pub fn resizable(&self) -> bool {
|
||||
self.resizable
|
||||
}
|
||||
|
||||
pub fn set_resizable(&mut self, resizable: bool) {
|
||||
self.resizable = resizable;
|
||||
self.command_queue
|
||||
.push(WindowCommand::SetResizable { resizable });
|
||||
}
|
||||
|
||||
pub fn decorations(&self) -> bool {
|
||||
self.decorations
|
||||
}
|
||||
|
||||
pub fn set_decorations(&mut self, decorations: bool) {
|
||||
self.decorations = decorations;
|
||||
self.command_queue
|
||||
.push(WindowCommand::SetDecorations { decorations });
|
||||
}
|
||||
|
||||
pub fn mode(&self) -> WindowMode {
|
||||
self.mode
|
||||
}
|
||||
|
||||
pub fn set_mode(&mut self, mode: WindowMode) {
|
||||
self.mode = mode;
|
||||
self.command_queue.push(WindowCommand::SetWindowMode {
|
||||
mode,
|
||||
resolution: (self.width, self.height),
|
||||
});
|
||||
}
|
||||
|
||||
pub fn drain_commands<'a>(&'a mut self) -> impl Iterator<Item = WindowCommand> + 'a {
|
||||
self.command_queue.drain(..)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
|
@ -8,7 +8,7 @@ pub struct Windows {
|
|||
|
||||
impl Windows {
|
||||
pub fn add(&mut self, window: Window) {
|
||||
self.windows.insert(window.id, window);
|
||||
self.windows.insert(window.id(), window);
|
||||
}
|
||||
|
||||
pub fn get(&self, id: WindowId) -> Option<&Window> {
|
||||
|
@ -23,7 +23,15 @@ impl Windows {
|
|||
self.get(WindowId::primary())
|
||||
}
|
||||
|
||||
pub fn get_primary_mut(&mut self) -> Option<&mut Window> {
|
||||
self.get_mut(WindowId::primary())
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = &Window> {
|
||||
self.windows.values()
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Window> {
|
||||
self.windows.values_mut()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ pub use winit_config::*;
|
|||
pub use winit_windows::*;
|
||||
|
||||
use bevy_app::{prelude::*, AppExit};
|
||||
use bevy_ecs::Resources;
|
||||
use bevy_ecs::{IntoThreadLocalSystem, Resources, World};
|
||||
use bevy_math::Vec2;
|
||||
use bevy_window::{
|
||||
CreateWindow, CursorMoved, Window, WindowCloseRequested, WindowCreated, WindowResized, Windows,
|
||||
|
@ -29,7 +29,60 @@ impl Plugin for WinitPlugin {
|
|||
// stopping us. there are plans to remove the lifetime: https://github.com/rust-windowing/winit/pull/1456
|
||||
// .add_event::<winit::event::WindowEvent>()
|
||||
.init_resource::<WinitWindows>()
|
||||
.set_runner(winit_runner);
|
||||
.set_runner(winit_runner)
|
||||
.add_system(change_window.thread_local_system());
|
||||
}
|
||||
}
|
||||
|
||||
fn change_window(_: &mut World, resources: &mut Resources) {
|
||||
let winit_windows = resources.get::<WinitWindows>().unwrap();
|
||||
let mut windows = resources.get_mut::<Windows>().unwrap();
|
||||
|
||||
for bevy_window in windows.iter_mut() {
|
||||
let id = bevy_window.id();
|
||||
for command in bevy_window.drain_commands() {
|
||||
match command {
|
||||
bevy_window::WindowCommand::SetWindowMode {
|
||||
mode,
|
||||
resolution: (width, height),
|
||||
} => {
|
||||
let window = winit_windows.get_window(id).unwrap();
|
||||
match mode {
|
||||
bevy_window::WindowMode::BorderlessFullscreen => {
|
||||
window.set_fullscreen(Some(winit::window::Fullscreen::Borderless(None)))
|
||||
}
|
||||
bevy_window::WindowMode::Fullscreen { use_size } => window.set_fullscreen(
|
||||
Some(winit::window::Fullscreen::Exclusive(match use_size {
|
||||
true => get_fitting_videomode(
|
||||
&window.current_monitor().unwrap(),
|
||||
width,
|
||||
height,
|
||||
),
|
||||
false => get_best_videomode(&window.current_monitor().unwrap()),
|
||||
})),
|
||||
),
|
||||
bevy_window::WindowMode::Windowed => window.set_fullscreen(None),
|
||||
}
|
||||
}
|
||||
bevy_window::WindowCommand::SetTitle { title } => {
|
||||
let window = winit_windows.get_window(id).unwrap();
|
||||
window.set_title(&title);
|
||||
}
|
||||
bevy_window::WindowCommand::SetResolution { width, height } => {
|
||||
let window = winit_windows.get_window(id).unwrap();
|
||||
window.set_inner_size(winit::dpi::PhysicalSize::new(width, height));
|
||||
}
|
||||
bevy_window::WindowCommand::SetVsync { .. } => (),
|
||||
bevy_window::WindowCommand::SetResizable { resizable } => {
|
||||
let window = winit_windows.get_window(id).unwrap();
|
||||
window.set_resizable(resizable);
|
||||
}
|
||||
bevy_window::WindowCommand::SetDecorations { decorations } => {
|
||||
let window = winit_windows.get_window(id).unwrap();
|
||||
window.set_decorations(decorations);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,15 +171,14 @@ pub fn winit_runner(mut app: App) {
|
|||
let winit_windows = app.resources.get_mut::<WinitWindows>().unwrap();
|
||||
let mut windows = app.resources.get_mut::<Windows>().unwrap();
|
||||
let window_id = winit_windows.get_window_id(winit_window_id).unwrap();
|
||||
let mut window = windows.get_mut(window_id).unwrap();
|
||||
window.width = size.width;
|
||||
window.height = size.height;
|
||||
let window = windows.get_mut(window_id).unwrap();
|
||||
window.update_resolution_from_backend(size.width, size.height);
|
||||
|
||||
let mut resize_events = app.resources.get_mut::<Events<WindowResized>>().unwrap();
|
||||
resize_events.send(WindowResized {
|
||||
id: window_id,
|
||||
height: window.height as usize,
|
||||
width: window.width as usize,
|
||||
height: window.height() as usize,
|
||||
width: window.width() as usize,
|
||||
});
|
||||
}
|
||||
event::Event::WindowEvent {
|
||||
|
@ -231,7 +283,7 @@ fn handle_create_window_events(
|
|||
for create_window_event in create_window_event_reader.iter(&create_window_events) {
|
||||
let window = Window::new(create_window_event.id, &create_window_event.descriptor);
|
||||
winit_windows.create_window(event_loop, &window);
|
||||
let window_id = window.id;
|
||||
let window_id = window.id();
|
||||
windows.add(window);
|
||||
window_created_events.send(WindowCreated { id: window_id });
|
||||
}
|
||||
|
|
|
@ -23,24 +23,31 @@ impl WinitWindows {
|
|||
#[cfg(not(target_os = "windows"))]
|
||||
let mut winit_window_builder = winit::window::WindowBuilder::new();
|
||||
|
||||
winit_window_builder = match window.mode {
|
||||
winit_window_builder = match window.mode() {
|
||||
WindowMode::BorderlessFullscreen => winit_window_builder.with_fullscreen(Some(
|
||||
winit::window::Fullscreen::Borderless(event_loop.primary_monitor()),
|
||||
)),
|
||||
WindowMode::Fullscreen { use_size } => winit_window_builder.with_fullscreen(Some(
|
||||
winit::window::Fullscreen::Exclusive(match use_size {
|
||||
true => get_fitting_videomode(&event_loop.primary_monitor().unwrap(), &window),
|
||||
true => get_fitting_videomode(
|
||||
&event_loop.primary_monitor().unwrap(),
|
||||
window.width(),
|
||||
window.height(),
|
||||
),
|
||||
false => get_best_videomode(&event_loop.primary_monitor().unwrap()),
|
||||
}),
|
||||
)),
|
||||
_ => winit_window_builder
|
||||
.with_inner_size(winit::dpi::PhysicalSize::new(window.width, window.height))
|
||||
.with_resizable(window.resizable)
|
||||
.with_decorations(window.decorations),
|
||||
.with_inner_size(winit::dpi::PhysicalSize::new(
|
||||
window.width(),
|
||||
window.height(),
|
||||
))
|
||||
.with_resizable(window.resizable())
|
||||
.with_decorations(window.decorations()),
|
||||
};
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut winit_window_builder = winit_window_builder.with_title(&window.title);
|
||||
let mut winit_window_builder = winit_window_builder.with_title(window.title());
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
|
@ -64,8 +71,10 @@ impl WinitWindows {
|
|||
|
||||
let winit_window = winit_window_builder.build(&event_loop).unwrap();
|
||||
|
||||
self.window_id_to_winit.insert(window.id, winit_window.id());
|
||||
self.winit_to_window_id.insert(winit_window.id(), window.id);
|
||||
self.window_id_to_winit
|
||||
.insert(window.id(), winit_window.id());
|
||||
self.winit_to_window_id
|
||||
.insert(winit_window.id(), window.id());
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
|
@ -96,9 +105,10 @@ impl WinitWindows {
|
|||
self.winit_to_window_id.get(&id).cloned()
|
||||
}
|
||||
}
|
||||
fn get_fitting_videomode(
|
||||
pub fn get_fitting_videomode(
|
||||
monitor: &winit::monitor::MonitorHandle,
|
||||
window: &Window,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) -> winit::monitor::VideoMode {
|
||||
let mut modes = monitor.video_modes().collect::<Vec<_>>();
|
||||
|
||||
|
@ -111,11 +121,9 @@ fn get_fitting_videomode(
|
|||
|
||||
modes.sort_by(|a, b| {
|
||||
use std::cmp::Ordering::*;
|
||||
match abs_diff(a.size().width, window.width).cmp(&abs_diff(b.size().width, window.width)) {
|
||||
match abs_diff(a.size().width, width).cmp(&abs_diff(b.size().width, width)) {
|
||||
Equal => {
|
||||
match abs_diff(a.size().height, window.height)
|
||||
.cmp(&abs_diff(b.size().height, window.height))
|
||||
{
|
||||
match abs_diff(a.size().height, height).cmp(&abs_diff(b.size().height, height)) {
|
||||
Equal => b.refresh_rate().cmp(&a.refresh_rate()),
|
||||
default => default,
|
||||
}
|
||||
|
@ -127,7 +135,7 @@ fn get_fitting_videomode(
|
|||
modes.first().unwrap().clone()
|
||||
}
|
||||
|
||||
fn get_best_videomode(monitor: &winit::monitor::MonitorHandle) -> winit::monitor::VideoMode {
|
||||
pub fn get_best_videomode(monitor: &winit::monitor::MonitorHandle) -> winit::monitor::VideoMode {
|
||||
let mut modes = monitor.video_modes().collect::<Vec<_>>();
|
||||
modes.sort_by(|a, b| {
|
||||
use std::cmp::Ordering::*;
|
||||
|
|
|
@ -48,11 +48,13 @@ fn bounce_system(
|
|||
windows: Res<Windows>,
|
||||
mut sprites: Query<(&Transform, &mut Velocity)>,
|
||||
) {
|
||||
let Window { width, height, .. } = windows.get_primary().expect("No primary window");
|
||||
let left = *width as f32 / -2.0;
|
||||
let right = *width as f32 / 2.0;
|
||||
let bottom = *height as f32 / -2.0;
|
||||
let top = *height as f32 / 2.0;
|
||||
let window = windows.get_primary().expect("No primary window");
|
||||
let width = window.width();
|
||||
let height = window.height();
|
||||
let left = width as f32 / -2.0;
|
||||
let right = width as f32 / 2.0;
|
||||
let bottom = height as f32 / -2.0;
|
||||
let top = height as f32 / 2.0;
|
||||
sprites
|
||||
.iter()
|
||||
// Batch size of 32 is chosen to limit the overhead of
|
||||
|
|
|
@ -5,12 +5,22 @@ fn main() {
|
|||
App::build()
|
||||
.add_resource(WindowDescriptor {
|
||||
title: "I am a window!".to_string(),
|
||||
width: 300,
|
||||
width: 500,
|
||||
height: 300,
|
||||
vsync: true,
|
||||
resizable: false,
|
||||
..Default::default()
|
||||
})
|
||||
.add_default_plugins()
|
||||
.add_system(change_title.system())
|
||||
.run();
|
||||
}
|
||||
|
||||
/// This system will then change the title during execution
|
||||
fn change_title(time: Res<Time>, mut windows: ResMut<Windows>) {
|
||||
let window = windows.get_primary_mut().unwrap();
|
||||
window.set_title(format!(
|
||||
"Seconds since startup: {}",
|
||||
time.seconds_since_startup.round()
|
||||
));
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue