can change window settings at runtime (#644)

can change window settings at runtime
This commit is contained in:
François 2020-10-15 20:42:19 +02:00 committed by GitHub
parent 9c48e5cccb
commit 76cc25823d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 251 additions and 60 deletions

View file

@ -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();
}

View file

@ -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);

View file

@ -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));
}

View file

@ -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

View file

@ -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!")
}
}

View file

@ -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);
}
}
}

View file

@ -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

View file

@ -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)]

View file

@ -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()
}
}

View file

@ -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 });
}

View file

@ -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::*;

View file

@ -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

View file

@ -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()
));
}