[Merged by Bors] - Add Window Resize Constraints#1409
Conversation
| min_width: 360., | ||
| min_height: 240., | ||
| max_width: 23040., | ||
| max_height: 8640., |
There was a problem hiding this comment.
I think there should be no default maximum size (so no .with_max_inner_size call by default) and a smaller default minimum size.
There was a problem hiding this comment.
What minimum resolution do you propose for a window?
| #[cfg(not(target_os = "windows"))] | ||
| let mut winit_window_builder = winit::window::WindowBuilder::new(); | ||
|
|
||
| let default_resize_constraints = WindowResizeConstraints::default(); |
There was a problem hiding this comment.
This should use the WindowDescriptor, right?
There was a problem hiding this comment.
This is just for some checks to force the minunum size and fix that crash at 0 height
| requested_height: f32, | ||
| physical_width: u32, | ||
| physical_height: u32, | ||
| resize_constraints: WindowResizeConstraints, |
There was a problem hiding this comment.
There is no command to change this.
There was a problem hiding this comment.
Should I remove this or create a command?
There was a problem hiding this comment.
You should probably add a command to change it
|
I just found #545 which solves the crash in a completely different way. |
|
Yupyup. I think its worth doing both. This pr solves the problem for most cases, but if someone manually overrides the min size, then it would be good to handle the error gracefully. |
| let default_resize_constraints = WindowResizeConstraints::default(); | ||
| let mut final_resize_constraints = window_descriptor.resize_constraints.clone(); | ||
| if final_resize_constraints.min_width < default_resize_constraints.min_width { | ||
| final_resize_constraints.min_width = default_resize_constraints.min_width; | ||
| } | ||
| if final_resize_constraints.min_height < default_resize_constraints.min_height { | ||
| final_resize_constraints.min_height = default_resize_constraints.min_height; | ||
| } | ||
| if final_resize_constraints.max_width > default_resize_constraints.max_width { | ||
| final_resize_constraints.max_width = default_resize_constraints.max_width; | ||
| } | ||
| if final_resize_constraints.max_height > default_resize_constraints.max_height { | ||
| final_resize_constraints.max_height = default_resize_constraints.max_height; | ||
| } | ||
| if final_resize_constraints.max_width < final_resize_constraints.min_width { | ||
| final_resize_constraints.max_width = default_resize_constraints.max_width; | ||
| } | ||
| if final_resize_constraints.max_height < final_resize_constraints.min_height { | ||
| final_resize_constraints.max_height = default_resize_constraints.max_height; | ||
| } |
There was a problem hiding this comment.
Ignoring the user's minimum size when it's less than the provided defaults is probably not the behaviour we wish to indroduce.
| let default_resize_constraints = WindowResizeConstraints::default(); | |
| let mut final_resize_constraints = window_descriptor.resize_constraints.clone(); | |
| if final_resize_constraints.min_width < default_resize_constraints.min_width { | |
| final_resize_constraints.min_width = default_resize_constraints.min_width; | |
| } | |
| if final_resize_constraints.min_height < default_resize_constraints.min_height { | |
| final_resize_constraints.min_height = default_resize_constraints.min_height; | |
| } | |
| if final_resize_constraints.max_width > default_resize_constraints.max_width { | |
| final_resize_constraints.max_width = default_resize_constraints.max_width; | |
| } | |
| if final_resize_constraints.max_height > default_resize_constraints.max_height { | |
| final_resize_constraints.max_height = default_resize_constraints.max_height; | |
| } | |
| if final_resize_constraints.max_width < final_resize_constraints.min_width { | |
| final_resize_constraints.max_width = default_resize_constraints.max_width; | |
| } | |
| if final_resize_constraints.max_height < final_resize_constraints.min_height { | |
| final_resize_constraints.max_height = default_resize_constraints.max_height; | |
| } | |
| let WindowResizeConstraints { mut min_width, mut min_height, mut max_width, mut max_height } = window_descriptor.resize_constraints.clone(); | |
| 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; | |
| } |
| let min_inner_size = LogicalSize { | ||
| width: final_resize_constraints.min_width, | ||
| height: final_resize_constraints.min_height, | ||
| }; | ||
|
|
||
| let max_inner_size = LogicalSize { | ||
| width: final_resize_constraints.max_width, | ||
| height: final_resize_constraints.max_height, | ||
| }; |
There was a problem hiding this comment.
I agree with the use of LogicalSize here - nice job 👍
| let min_inner_size = LogicalSize { | |
| width: final_resize_constraints.min_width, | |
| height: final_resize_constraints.min_height, | |
| }; | |
| let max_inner_size = LogicalSize { | |
| width: final_resize_constraints.max_width, | |
| height: final_resize_constraints.max_height, | |
| }; | |
| let min_inner_size = LogicalSize { | |
| width: min_width, | |
| height: min_height, | |
| }; | |
| let max_inner_size = LogicalSize { | |
| width: max_width, | |
| height: max_height, | |
| }; |
| let mut winit_window_builder = if final_resize_constraints.max_width != f32::INFINITY | ||
| && final_resize_constraints.max_height != f32::INFINITY |
There was a problem hiding this comment.
| let mut winit_window_builder = if final_resize_constraints.max_width != f32::INFINITY | |
| && final_resize_constraints.max_height != f32::INFINITY | |
| let mut winit_window_builder = if max_width.is_finite() && max_height.is_finite() |
| } | ||
| } | ||
|
|
||
| use core::f32; |
There was a problem hiding this comment.
I'm not sure what the purpose of this line here is - the constants are also to the primitive types, which are already in the prelude
There was a problem hiding this comment.
Ups, the Rust plugin for VSCode added that.
| } | ||
|
|
||
| impl WinitWindows { | ||
| #[allow(clippy::float_cmp)] |
There was a problem hiding this comment.
With my other changes this should no longer be needed
There was a problem hiding this comment.
But there is: 'if max_width < min_width" and "if max_height < min_height"
There was a problem hiding this comment.
Oh true, yeah
I think it's fine, although what does clippy want us to do instead?
|
I will make the changes asap. Thanks! |
|
There is still an issue. When you maximize your window it doesn't care if it is larger than your max resize constraints. |
|
Related: rust-windowing/winit#588 In this case, I'm not actually sure what the use case for a window with a maximum size but which can be resized to be made smaller than that is - that is, it's good that we support it but I don't want to make the process perfect. That is, if people don't want their window to be maximised they should use a I'd be inclined to just document it as a winit limitation, and if someone decides their game needs it they can push the plumbing through themselves |
I propose this: |
|
|
Or maybe just add an maximizable boolean and a set_maximizable command. |
As I can see winit doesn't support it yet so I think this PR is done. |
|
I'd say we don't need to capture the set maximised command. Ideally, we'd detect maximisation to greater than the constratints and warn. But I don't think we should automatically demaximise, because that feels likely to be very janky. |
| } | ||
| } | ||
|
|
||
| #[derive(Debug, Clone)] |
There was a problem hiding this comment.
Since this is just plain old data and not especially big, I think it might be worth just making it Copy
| resize_constraints: WindowResizeConstraints, | ||
| ) -> WindowResizeConstraints { | ||
| let WindowResizeConstraints { | ||
| mut min_width, | ||
| mut min_height, | ||
| mut max_width, | ||
| mut max_height, | ||
| } = resize_constraints; |
There was a problem hiding this comment.
There's a clever thing you can do here, which is to use irrefutable pattern matching in the function argument. That is
| resize_constraints: WindowResizeConstraints, | |
| ) -> WindowResizeConstraints { | |
| let WindowResizeConstraints { | |
| mut min_width, | |
| mut min_height, | |
| mut max_width, | |
| mut max_height, | |
| } = resize_constraints; | |
| WindowResizeConstraints { | |
| mut min_width, | |
| mut min_height, | |
| mut max_width, | |
| mut max_height, | |
| }: WindowResizeConstraints, | |
| ) -> WindowResizeConstraints { |
Not sure how rustfmt will format it, but you get the idea.
There was a problem hiding this comment.
Nice, I didn't know about irrefutable pattern.
| use bevy_log::warn; | ||
| use bevy_window::WindowResizeConstraints; | ||
|
|
||
| #[allow(clippy::float_cmp)] |
There was a problem hiding this comment.
I think the float_cmp lint was only about the infinity check - removing it no longer gives an error.
Ideally, we'd have some way to handle unused lint supression
| .with_decorations(window_descriptor.decorations), | ||
| }; | ||
|
|
||
| let WindowResizeConstraints { |
There was a problem hiding this comment.
I'd be tempted to use a 4 tuple here, but I do think this is better code 👍
| #[allow(unused_mut)] | ||
| let mut winit_window_builder = if max_width.is_finite() && max_height.is_finite() { |
There was a problem hiding this comment.
In this case I don't think the mut will ever actually be used at all, since the builder is always consumed in the next clause
| #[allow(unused_mut)] | |
| let mut winit_window_builder = if max_width.is_finite() && max_height.is_finite() { | |
| let winit_window_builder = if max_width.is_finite() && max_height.is_finite() { |
| width: 500., | ||
| height: 300., | ||
| vsync: true, | ||
| resizable: true, |
There was a problem hiding this comment.
Not sure if this should be merged into WindowResizeConstraints - however that would be in a seperate PR anyway.
I suspect it shouldn't be just because we don't want to merge too many settings into one command.
|
I think it is ready to go now. |
DJMcNab
left a comment
There was a problem hiding this comment.
Just one suggestion for documentation.
| } | ||
|
|
||
| #[derive(Debug, Clone, Copy)] | ||
| pub struct WindowResizeConstraints { |
There was a problem hiding this comment.
A small documentation comment here would be nice. Just something like
/// The size limits on a `Window`. These values are 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 maximised it
/// may have a size outside of these limits. The functionality required to disable maximising
/// is not yet exposed by winit
|
@cart I think this is ready for sign off |
|
This looks good to me. I made one minor adjustment (removed the "util" mod/method in favor of |
|
bors r+ |
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>
|
Pull request successfully merged into main. Build succeeded: |

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.