Add Window Resize Constraints (#1409)

You should be able to set the minimum and maximum desired resolution of a system window.
This also fixes a bug on Windows operating system: When you try to resize to 0 on the height it crashes.

Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
Digital Seven 2021-03-03 02:56:50 +00:00
parent 4c1bc33b52
commit 8dcba7f4a1
4 changed files with 120 additions and 1 deletions

View file

@ -1,5 +1,5 @@
use bevy_math::{IVec2, Vec2};
use bevy_utils::Uuid;
use bevy_utils::{tracing::warn, Uuid};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct WindowId(Uuid);
@ -32,6 +32,64 @@ impl Default for WindowId {
}
}
/// The size limits on a window.
/// These values are measured in logical pixels, so the user's
/// scale factor does affect the size limits on the window.
/// Please note that if the window is resizable, then when the window is
/// maximized it may have a size outside of these limits. The functionality
/// required to disable maximizing is not yet exposed by winit.
#[derive(Debug, Clone, Copy)]
pub struct WindowResizeConstraints {
pub min_width: f32,
pub min_height: f32,
pub max_width: f32,
pub max_height: f32,
}
impl Default for WindowResizeConstraints {
fn default() -> Self {
Self {
min_width: 180.,
min_height: 120.,
max_width: f32::INFINITY,
max_height: f32::INFINITY,
}
}
}
impl WindowResizeConstraints {
pub fn check_constraints(&self) -> WindowResizeConstraints {
let WindowResizeConstraints {
mut min_width,
mut min_height,
mut max_width,
mut max_height,
} = self;
min_width = min_width.max(1.);
min_height = min_height.max(1.);
if max_width < min_width {
warn!(
"The given maximum width {} is smaller than the minimum width {}",
max_width, min_width
);
max_width = min_width;
}
if max_height < min_height {
warn!(
"The given maximum height {} is smaller than the minimum height {}",
max_height, min_height
);
max_height = min_height;
}
WindowResizeConstraints {
min_width,
min_height,
max_width,
max_height,
}
}
}
/// An operating system window that can present content and receive user input.
///
/// ## Window Sizes
@ -54,6 +112,7 @@ pub struct Window {
requested_height: f32,
physical_width: u32,
physical_height: u32,
resize_constraints: WindowResizeConstraints,
position: Option<IVec2>,
scale_factor_override: Option<f64>,
backend_scale_factor: f64,
@ -114,6 +173,9 @@ pub enum WindowCommand {
SetPosition {
position: IVec2,
},
SetResizeConstraints {
resize_constraints: WindowResizeConstraints,
},
}
/// Defines the way a window is displayed
@ -144,6 +206,7 @@ impl Window {
position,
physical_width,
physical_height,
resize_constraints: window_descriptor.resize_constraints,
scale_factor_override: window_descriptor.scale_factor_override,
backend_scale_factor: scale_factor,
title: window_descriptor.title.clone(),
@ -210,6 +273,12 @@ impl Window {
self.physical_height
}
/// The window's client resize constraint in logical pixels.
#[inline]
pub fn resize_constraints(&self) -> WindowResizeConstraints {
self.resize_constraints
}
/// The window's client position in physical pixels.
#[inline]
pub fn position(&self) -> Option<IVec2> {
@ -250,6 +319,13 @@ impl Window {
.push(WindowCommand::SetPosition { position })
}
/// Modifies the minimum and maximum window bounds for resizing in logical pixels.
#[inline]
pub fn set_resize_constraints(&mut self, resize_constraints: WindowResizeConstraints) {
self.command_queue
.push(WindowCommand::SetResizeConstraints { resize_constraints });
}
/// Request the OS to resize the window such the the client area matches the
/// specified width and height.
#[allow(clippy::float_cmp)]
@ -257,6 +333,7 @@ impl Window {
if self.requested_width == width && self.requested_height == height {
return;
}
self.requested_width = width;
self.requested_height = height;
self.command_queue.push(WindowCommand::SetResolution {
@ -437,6 +514,7 @@ impl Window {
pub struct WindowDescriptor {
pub width: f32,
pub height: f32,
pub resize_constraints: WindowResizeConstraints,
pub scale_factor_override: Option<f64>,
pub title: String,
pub vsync: bool,
@ -455,6 +533,7 @@ impl Default for WindowDescriptor {
title: "bevy".to_string(),
width: 1280.,
height: 720.,
resize_constraints: WindowResizeConstraints::default(),
scale_factor_override: None,
vsync: true,
resizable: true,

View file

@ -21,6 +21,7 @@ x11 = ["winit/x11"]
bevy_app = { path = "../bevy_app", version = "0.4.0" }
bevy_ecs = { path = "../bevy_ecs", version = "0.4.0" }
bevy_input = { path = "../bevy_input", version = "0.4.0" }
bevy_log = { path = "../bevy_log", version = "0.4.0" }
bevy_math = { path = "../bevy_math", version = "0.4.0" }
bevy_window = { path = "../bevy_window", version = "0.4.0" }
bevy_utils = { path = "../bevy_utils", version = "0.4.0" }

View file

@ -25,6 +25,7 @@ use winit::{
event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget},
};
use winit::dpi::LogicalSize;
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
@ -139,6 +140,24 @@ fn change_window(_: &mut World, resources: &mut Resources) {
y: position[1],
});
}
bevy_window::WindowCommand::SetResizeConstraints { resize_constraints } => {
let window = winit_windows.get_window(id).unwrap();
let constraints = 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,
};
if constraints.max_width.is_finite() && constraints.max_height.is_finite() {
window.set_min_inner_size(Some(min_inner_size));
window.set_max_inner_size(Some(max_inner_size));
} else {
window.set_min_inner_size(Some(min_inner_size));
}
}
}
}
}

View file

@ -1,6 +1,7 @@
use bevy_math::IVec2;
use bevy_utils::HashMap;
use bevy_window::{Window, WindowDescriptor, WindowId, WindowMode};
use winit::dpi::LogicalSize;
#[derive(Debug, Default)]
pub struct WinitWindows {
@ -59,6 +60,25 @@ impl WinitWindows {
.with_decorations(window_descriptor.decorations),
};
let constraints = window_descriptor.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,
};
let winit_window_builder =
if constraints.max_width.is_finite() && constraints.max_height.is_finite() {
winit_window_builder
.with_min_inner_size(min_inner_size)
.with_max_inner_size(max_inner_size)
} else {
winit_window_builder.with_min_inner_size(min_inner_size)
};
#[allow(unused_mut)]
let mut winit_window_builder = winit_window_builder.with_title(&window_descriptor.title);