From 2e675aa2ee5d411c8036997240b94703e1185345 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Tue, 4 Jul 2023 21:02:01 +0300 Subject: [PATCH 01/16] feat: split min/max size constraints, closes #138 --- examples/min_max_size.rs | 58 ++++++++++- src/dpi.rs | 171 +++++++++++++++++++++++++++---- src/platform_impl/android/mod.rs | 6 +- src/platform_impl/ios/window.rs | 16 ++- src/window.rs | 118 +++++++++++++++++++-- 5 files changed, 331 insertions(+), 38 deletions(-) diff --git a/examples/min_max_size.rs b/examples/min_max_size.rs index 03c3004a4..720932a6f 100644 --- a/examples/min_max_size.rs +++ b/examples/min_max_size.rs @@ -3,9 +3,10 @@ // SPDX-License-Identifier: Apache-2.0 use tao::{ - dpi::LogicalSize, - event::{Event, WindowEvent}, + dpi::LogicalUnit, + event::{ElementState, Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, + keyboard::Key, window::WindowBuilder, }; @@ -14,20 +15,67 @@ fn main() { env_logger::init(); let event_loop = EventLoop::new(); + let min_width = 400.0; + let mut min_width_set = false; + let max_width = 800.0; + let mut max_width_set = false; + let min_height = 200.0; + let mut min_height_set = false; + let max_height = 400.0; + let mut max_height_set = false; + let window = WindowBuilder::new().build(&event_loop).unwrap(); - window.set_min_inner_size(Some(LogicalSize::new(400.0, 200.0))); - window.set_max_inner_size(Some(LogicalSize::new(800.0, 400.0))); + eprintln!("constraint keys:"); + eprintln!(" (E) Toggle the min width"); + eprintln!(" (F) Toggle the max width"); + eprintln!(" (P) Toggle the min height"); + eprintln!(" (V) Toggle the max height"); event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; - println!("{:?}", event); match event { Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => *control_flow = ControlFlow::Exit, + + Event::WindowEvent { + event: + WindowEvent::KeyboardInput { + event: + KeyEvent { + logical_key: Key::Character(key_str), + state: ElementState::Released, + .. + }, + .. + }, + .. + } => match key_str { + "e" => { + min_width_set = !min_width_set; + let min_width: Option> = min_width_set.then_some(min_width.into()); + window.set_min_inner_width(min_width); + } + "f" => { + max_width_set = !max_width_set; + let max_width: Option> = max_width_set.then_some(max_width.into()); + window.set_max_inner_width(max_width); + } + "p" => { + min_height_set = !min_height_set; + let min_height: Option> = min_height_set.then_some(min_height.into()); + window.set_min_inner_height(min_height); + } + "v" => { + max_height_set = !max_height_set; + let max_height: Option> = max_height_set.then_some(max_height.into()); + window.set_max_inner_height(max_height); + } + _ => {} + }, _ => (), } }); diff --git a/src/dpi.rs b/src/dpi.rs index f86030433..eb18776ef 100644 --- a/src/dpi.rs +++ b/src/dpi.rs @@ -283,6 +283,49 @@ impl Into<[X; 2]> for PhysicalPosition

{ } } +/// A position that's either physical or logical. +#[non_exhaustive] +#[derive(Debug, Copy, Clone, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum Position { + Physical(PhysicalPosition), + Logical(LogicalPosition), +} + +impl Position { + pub fn new>(position: S) -> Position { + position.into() + } + + pub fn to_logical(&self, scale_factor: f64) -> LogicalPosition

{ + match *self { + Position::Physical(position) => position.to_logical(scale_factor), + Position::Logical(position) => position.cast(), + } + } + + pub fn to_physical(&self, scale_factor: f64) -> PhysicalPosition

{ + match *self { + Position::Physical(position) => position.cast(), + Position::Logical(position) => position.to_physical(scale_factor), + } + } +} + +impl From> for Position { + #[inline] + fn from(position: PhysicalPosition

) -> Position { + Position::Physical(position.cast()) + } +} + +impl From> for Position { + #[inline] + fn from(position: LogicalPosition

) -> Position { + Position::Logical(position.cast()) + } +} + /// A size represented in logical pixels. #[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -435,6 +478,20 @@ impl Size { } } + pub fn width(&self) -> Unit { + match *self { + Size::Physical(size) => Unit::Physical(size.width.into()), + Size::Logical(size) => Unit::Logical(size.width.into()), + } + } + + pub fn height(&self) -> Unit { + match *self { + Size::Physical(size) => Unit::Physical(size.height.into()), + Size::Logical(size) => Unit::Logical(size.height.into()), + } + } + pub fn clamp>(input: S, min: S, max: S, scale_factor: f64) -> Size { let (input, min, max) = ( input.into().to_physical::(scale_factor), @@ -473,45 +530,117 @@ impl From> for Size { } } -/// A position that's either physical or logical. -#[non_exhaustive] -#[derive(Debug, Copy, Clone, PartialEq)] +/// A unit represented in logical pixels. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, PartialOrd, Ord)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum Position { - Physical(PhysicalPosition), - Logical(LogicalPosition), +pub struct LogicalUnit

(pub P); + +impl

LogicalUnit

{ + #[inline] + pub const fn new(u: P) -> Self { + Self(u) + } } -impl Position { - pub fn new>(position: S) -> Position { - position.into() +impl LogicalUnit

{ + #[inline] + pub fn from_physical>, X: Pixel>(physical: T, scale_factor: f64) -> Self { + physical.into().to_logical(scale_factor) } - pub fn to_logical(&self, scale_factor: f64) -> LogicalPosition

{ + #[inline] + pub fn to_physical(&self, scale_factor: f64) -> PhysicalUnit { + assert!(validate_scale_factor(scale_factor)); + let u = self.0.into() * scale_factor; + PhysicalUnit::new(u).cast() + } + + #[inline] + pub fn cast(&self) -> LogicalUnit { + LogicalUnit(self.0.cast()) + } +} + +impl From

for LogicalUnit

{ + fn from(p: P) -> LogicalUnit

{ + LogicalUnit::new(p.cast()) + } +} + +/// A unit represented in physical pixels. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, PartialOrd, Ord)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct PhysicalUnit

(pub P); + +impl

PhysicalUnit

{ + #[inline] + pub const fn new(u: P) -> Self { + Self(u) + } +} + +impl PhysicalUnit

{ + #[inline] + pub fn from_logical>, X: Pixel>(logical: T, scale_factor: f64) -> Self { + logical.into().to_physical(scale_factor) + } + + #[inline] + pub fn to_logical(&self, scale_factor: f64) -> LogicalUnit { + assert!(validate_scale_factor(scale_factor)); + let u = self.0.into() / scale_factor; + LogicalUnit::new(u).cast() + } + + #[inline] + pub fn cast(&self) -> PhysicalUnit { + PhysicalUnit(self.0.cast()) + } +} + +impl From

for PhysicalUnit

{ + fn from(p: P) -> PhysicalUnit

{ + PhysicalUnit::new(p.cast()) + } +} + +#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum Unit { + Physical(PhysicalUnit), + Logical(LogicalUnit), +} + +impl Unit { + pub fn new>(val: S) -> Unit { + val.into() + } + + pub fn to_logical(&self, scale_factor: f64) -> LogicalUnit

{ match *self { - Position::Physical(position) => position.to_logical(scale_factor), - Position::Logical(position) => position.cast(), + Unit::Physical(val) => val.to_logical(scale_factor), + Unit::Logical(val) => val.cast(), } } - pub fn to_physical(&self, scale_factor: f64) -> PhysicalPosition

{ + pub fn to_physical(&self, scale_factor: f64) -> PhysicalUnit

{ match *self { - Position::Physical(position) => position.cast(), - Position::Logical(position) => position.to_physical(scale_factor), + Unit::Physical(val) => val.cast(), + Unit::Logical(val) => val.to_physical(scale_factor), } } } -impl From> for Position { +impl From> for Unit { #[inline] - fn from(position: PhysicalPosition

) -> Position { - Position::Physical(position.cast()) + fn from(val: PhysicalUnit

) -> Unit { + Unit::Physical(val.cast()) } } -impl From> for Position { +impl From> for Unit { #[inline] - fn from(position: LogicalPosition

) -> Position { - Position::Logical(position.cast()) + fn from(val: LogicalUnit

) -> Unit { + Unit::Logical(val.cast()) } } diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index fc6b5f849..c12840f63 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -5,7 +5,7 @@ #![cfg(target_os = "android")] use crate::{ accelerator::Accelerator, - dpi::{PhysicalPosition, PhysicalSize, Position, Size}, + dpi::{PhysicalPosition, PhysicalSize, Position, Size, Unit}, error, event, event_loop::{self, ControlFlow}, icon::Icon, @@ -601,8 +601,12 @@ impl Window { MonitorHandle.size() } + pub fn set_min_inner_width(&self, _: Option) {} + pub fn set_min_inner_height(&self, _: Option) {} pub fn set_min_inner_size(&self, _: Option) {} + pub fn set_max_inner_width(&self, _: Option) {} + pub fn set_max_inner_height(&self, _: Option) {} pub fn set_max_inner_size(&self, _: Option) {} pub fn set_title(&self, _title: &str) {} diff --git a/src/platform_impl/ios/window.rs b/src/platform_impl/ios/window.rs index 736ae592f..ff2604738 100644 --- a/src/platform_impl/ios/window.rs +++ b/src/platform_impl/ios/window.rs @@ -169,11 +169,23 @@ impl Inner { warn!("not clear what `Window::set_inner_size` means on iOS"); } - pub fn set_min_inner_size(&self, _dimensions: Option) { + pub fn set_min_inner_width(&self, _: Option) { + warn!("`Window::set_min_inner_width` is ignored on iOS") + } + pub fn set_min_inner_height(&self, _: Option) { + warn!("`Window::set_min_inner_height` is ignored on iOS") + } + pub fn set_min_inner_size(&self, _: Option) { warn!("`Window::set_min_inner_size` is ignored on iOS") } - pub fn set_max_inner_size(&self, _dimensions: Option) { + pub fn set_max_inner_width(&self, _: Option) { + warn!("`Window::set_max_inner_width` is ignored on iOS") + } + pub fn set_max_inner_height(&self, _: Option) { + warn!("`Window::set_max_inner_height` is ignored on iOS") + } + pub fn set_max_inner_size(&self, _: Option) { warn!("`Window::set_max_inner_size` is ignored on iOS") } diff --git a/src/window.rs b/src/window.rs index 050d35469..12fa1da11 100644 --- a/src/window.rs +++ b/src/window.rs @@ -8,7 +8,7 @@ use std::fmt; use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle}; use crate::{ - dpi::{PhysicalPosition, PhysicalSize, Position, Size}, + dpi::{PhysicalPosition, PhysicalSize, Position, Size, Unit}, error::{ExternalError, NotSupportedError, OsError}, event_loop::EventLoopWindowTarget, menu::MenuBar, @@ -115,15 +115,25 @@ pub struct WindowAttributes { /// The default is `None`. pub inner_size: Option, - /// The minimum dimensions a window can be, If this is `None`, the window will have no minimum dimensions (aside from reserved). + /// The minimum width a window can be, If this is `None`, the window will have no minimum width (aside from reserved). /// /// The default is `None`. - pub min_inner_size: Option, + pub min_inner_width: Option, - /// The maximum dimensions a window can be, If this is `None`, the maximum will have no maximum or will be set to the primary monitor's dimensions by the platform. + /// The minimum height a window can be, If this is `None`, the window will have no minimum height (aside from reserved). /// /// The default is `None`. - pub max_inner_size: Option, + pub min_inner_height: Option, + + /// The maximum width a window can be, If this is `None`, the maximum will have no maximum or will be set to the primary monitor's width by the platform. + /// + /// The default is `None`. + pub max_inner_width: Option, + + /// The maximum height a window can be, If this is `None`, the maximum will have no maximum or will be set to the primary monitor's height by the platform. + /// + /// The default is `None`. + pub max_inner_height: Option, /// The desired position of the window. If this is `None`, some platform-specific position /// will be chosen. @@ -257,8 +267,10 @@ impl Default for WindowAttributes { fn default() -> WindowAttributes { WindowAttributes { inner_size: None, - min_inner_size: None, - max_inner_size: None, + min_inner_width: None, + min_inner_height: None, + max_inner_width: None, + max_inner_height: None, position: None, resizable: true, minimizable: true, @@ -300,6 +312,28 @@ impl WindowBuilder { self } + /// Sets a minimum width for the window. + /// + /// See [`Window::set_min_inner_width`] for details. + /// + /// [`Window::set_min_inner_width`]: crate::window::Window::set_min_inner_width + #[inline] + pub fn with_min_inner_width>(mut self, width: U) -> Self { + self.window.min_inner_width = Some(width.into()); + self + } + + /// Sets a minimum height for the window. + /// + /// See [`Window::set_min_inner_height`] for details. + /// + /// [`Window::set_min_inner_height`]: crate::window::Window::set_min_inner_height + #[inline] + pub fn with_min_inner_height>(mut self, height: U) -> Self { + self.window.min_inner_height = Some(height.into()); + self + } + /// Sets a minimum dimension size for the window. /// /// See [`Window::set_min_inner_size`] for details. @@ -307,7 +341,31 @@ impl WindowBuilder { /// [`Window::set_min_inner_size`]: crate::window::Window::set_min_inner_size #[inline] pub fn with_min_inner_size>(mut self, min_size: S) -> Self { - self.window.min_inner_size = Some(min_size.into()); + let size = min_size.into(); + self.window.min_inner_width = Some(size.width()); + self.window.min_inner_height = Some(size.height()); + self + } + + /// Sets a maximum width for the window. + /// + /// See [`Window::set_max_inner_width`] for details. + /// + /// [`Window::set_max_inner_width`]: crate::window::Window::set_max_inner_width + #[inline] + pub fn with_max_inner_width>(mut self, width: U) -> Self { + self.window.max_inner_width = Some(width.into()); + self + } + + /// Sets a maximum height for the window. + /// + /// See [`Window::set_max_inner_height`] for details. + /// + /// [`Window::set_max_inner_height`]: crate::window::Window::set_max_inner_height + #[inline] + pub fn with_max_inner_height>(mut self, height: U) -> Self { + self.window.max_inner_height = Some(height.into()); self } @@ -318,7 +376,9 @@ impl WindowBuilder { /// [`Window::set_max_inner_size`]: crate::window::Window::set_max_inner_size #[inline] pub fn with_max_inner_size>(mut self, max_size: S) -> Self { - self.window.max_inner_size = Some(max_size.into()); + let size = max_size.into(); + self.window.max_inner_width = Some(size.width()); + self.window.max_inner_height = Some(size.height()); self } @@ -702,6 +762,26 @@ impl Window { self.window.outer_size() } + /// Sets a minimum width for the window. + /// + /// ## Platform-specific + /// + /// - **iOS / Android:** Unsupported. + #[inline] + pub fn set_min_inner_width>(&self, width: Option) { + self.window.set_min_inner_width(width.map(|s| s.into())) + } + + /// Sets a minimum height for the window. + /// + /// ## Platform-specific + /// + /// - **iOS / Android:** Unsupported. + #[inline] + pub fn set_min_inner_height>(&self, height: Option) { + self.window.set_min_inner_height(height.map(|s| s.into())) + } + /// Sets a minimum dimension size for the window. /// /// ## Platform-specific @@ -712,6 +792,26 @@ impl Window { self.window.set_min_inner_size(min_size.map(|s| s.into())) } + /// Sets a maximum width for the window. + /// + /// ## Platform-specific + /// + /// - **iOS / Android:** Unsupported. + #[inline] + pub fn set_max_inner_width>(&self, width: Option) { + self.window.set_max_inner_width(width.map(|s| s.into())) + } + + /// Sets a maximum height for the window. + /// + /// ## Platform-specific + /// + /// - **iOS / Android:** Unsupported. + #[inline] + pub fn set_max_inner_height>(&self, height: Option) { + self.window.set_max_inner_height(height.map(|s| s.into())) + } + /// Sets a maximum dimension size for the window. /// /// ## Platform-specific From 9753aa79a74db2f380110fbcb78d5d2c0f5ba7e9 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Tue, 4 Jul 2023 21:02:14 +0300 Subject: [PATCH 02/16] windows impl --- src/platform_impl/windows/event_loop.rs | 60 ++++++++++------- src/platform_impl/windows/window.rs | 80 ++++++++++++++++++++--- src/platform_impl/windows/window_state.rs | 14 ++-- 3 files changed, 117 insertions(+), 37 deletions(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 62217683a..92a3b5e71 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -1773,29 +1773,43 @@ unsafe fn public_window_callback_inner( let mmi = lparam.0 as *mut MINMAXINFO; let window_state = subclass_input.window_state.lock(); - - if window_state.min_size.is_some() || window_state.max_size.is_some() { - let is_decorated = window_state - .window_flags() - .contains(WindowFlags::MARKER_DECORATIONS); - if let Some(min_size) = window_state.min_size { - let min_size = min_size.to_physical(window_state.scale_factor); - let (width, height): (u32, u32) = - util::adjust_size(window, min_size, is_decorated).into(); - (*mmi).ptMinTrackSize = POINT { - x: width as i32, - y: height as i32, - }; - } - if let Some(max_size) = window_state.max_size { - let max_size = max_size.to_physical(window_state.scale_factor); - let (width, height): (u32, u32) = - util::adjust_size(window, max_size, is_decorated).into(); - (*mmi).ptMaxTrackSize = POINT { - x: width as i32, - y: height as i32, - }; - } + let is_decorated = window_state + .window_flags() + .contains(WindowFlags::MARKER_DECORATIONS); + + if window_state.min_width.is_some() || window_state.min_height.is_some() { + let min_size = PhysicalSize::new( + window_state + .min_width + .map(|w| w.to_physical(window_state.scale_factor).0) + .unwrap_or_default(), + window_state + .min_height + .map(|w| w.to_physical(window_state.scale_factor).0) + .unwrap_or_default(), + ); + let (width, height): (u32, u32) = util::adjust_size(window, min_size, is_decorated).into(); + (*mmi).ptMinTrackSize = POINT { + x: width as i32, + y: height as i32, + }; + } + if window_state.max_width.is_some() || window_state.max_height.is_some() { + let max_size = PhysicalSize::new( + window_state + .max_width + .map(|w| w.to_physical(window_state.scale_factor).0) + .unwrap_or_default(), + window_state + .max_height + .map(|w| w.to_physical(window_state.scale_factor).0) + .unwrap_or_default(), + ); + let (width, height): (u32, u32) = util::adjust_size(window, max_size, is_decorated).into(); + (*mmi).ptMaxTrackSize = POINT { + x: width as i32, + y: height as i32, + }; } result = ProcResult::Value(LRESULT(0)); diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index 56c607c44..2591e0a31 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -37,7 +37,7 @@ use windows::{ }; use crate::{ - dpi::{PhysicalPosition, PhysicalSize, Position, Size}, + dpi::{PhysicalPosition, PhysicalSize, PhysicalUnit, Position, Size, Unit}, error::{ExternalError, NotSupportedError, OsError as RootOsError}, icon::Icon, menu::MenuType, @@ -283,9 +283,45 @@ impl Window { util::set_inner_size_physical(self.window.0, width, height, is_decorated); } + #[inline] + pub fn set_min_inner_width(&self, width: Option) { + self.window_state.lock().min_width = width; + // Make windows re-check the window size bounds. + let size = self.inner_size(); + self.set_inner_size(size.into()); + } + + #[inline] + pub fn set_min_inner_height(&self, height: Option) { + self.window_state.lock().min_height = height; + // Make windows re-check the window size bounds. + let size = self.inner_size(); + self.set_inner_size(size.into()); + } + #[inline] pub fn set_min_inner_size(&self, size: Option) { - self.window_state.lock().min_size = size; + { + let mut window_state = self.window_state.lock(); + window_state.min_width = size.map(|s| s.width()); + window_state.min_height = size.map(|s| s.height()); + } + // Make windows re-check the window size bounds. + let size = self.inner_size(); + self.set_inner_size(size.into()); + } + + #[inline] + pub fn set_max_inner_width(&self, width: Option) { + self.window_state.lock().max_width = width; + // Make windows re-check the window size bounds. + let size = self.inner_size(); + self.set_inner_size(size.into()); + } + + #[inline] + pub fn set_max_inner_height(&self, height: Option) { + self.window_state.lock().max_height = height; // Make windows re-check the window size bounds. let size = self.inner_size(); self.set_inner_size(size.into()); @@ -293,7 +329,11 @@ impl Window { #[inline] pub fn set_max_inner_size(&self, size: Option) { - self.window_state.lock().max_size = size; + { + let mut window_state = self.window_state.lock(); + window_state.max_width = size.map(|s| s.width()); + window_state.max_height = size.map(|s| s.height()); + } // Make windows re-check the window size bounds. let size = self.inner_size(); self.set_inner_size(size.into()); @@ -1071,15 +1111,37 @@ unsafe fn init( win.set_fullscreen(attributes.fullscreen); force_window_active(win.window.0); } else { + let scale_factor = win.scale_factor(); let size = attributes .inner_size .unwrap_or_else(|| PhysicalSize::new(800, 600).into()); - let max_size = attributes - .max_inner_size - .unwrap_or_else(|| PhysicalSize::new(f64::MAX, f64::MAX).into()); - let min_size = attributes - .min_inner_size - .unwrap_or_else(|| PhysicalSize::new(0, 0).into()); + let max_size = PhysicalSize::new( + attributes + .max_inner_width + .unwrap_or_else(|| PhysicalUnit::new(f64::MAX).into()) + .to_physical::(scale_factor) + .0, + attributes + .max_inner_height + .unwrap_or_else(|| PhysicalUnit::new(f64::MAX).into()) + .to_physical(scale_factor) + .0, + ) + .into(); + let min_size = PhysicalSize::new( + attributes + .min_inner_width + .unwrap_or_else(|| PhysicalUnit::new(0).into()) + .to_physical::(scale_factor) + .0, + attributes + .min_inner_height + .unwrap_or_else(|| PhysicalUnit::new(0).into()) + .to_physical(scale_factor) + .0, + ) + .into(); + let clamped_size = Size::clamp(size, min_size, max_size, win.scale_factor()); win.set_inner_size(clamped_size); diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index d42433368..8d5e94f5b 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{ - dpi::{PhysicalPosition, Size}, + dpi::{PhysicalPosition, Unit}, icon::Icon, keyboard::ModifiersState, platform_impl::platform::{event_loop, minimal_ime::MinimalIme, util}, @@ -22,8 +22,10 @@ pub struct WindowState { pub mouse: MouseProperties, /// Used by `WM_GETMINMAXINFO`. - pub min_size: Option, - pub max_size: Option, + pub min_width: Option, + pub min_height: Option, + pub max_width: Option, + pub max_height: Option, pub window_icon: Option, pub taskbar_icon: Option, @@ -128,8 +130,10 @@ impl WindowState { last_position: None, }, - min_size: attributes.min_inner_size, - max_size: attributes.max_inner_size, + min_width: attributes.min_inner_width, + min_height: attributes.min_inner_height, + max_width: attributes.max_inner_width, + max_height: attributes.max_inner_height, window_icon: attributes.window_icon.clone(), taskbar_icon, From 1d4670292c690b087754f8961417698b3894056e Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Wed, 5 Jul 2023 02:21:50 +0300 Subject: [PATCH 03/16] linux impl --- src/platform_impl/linux/event_loop.rs | 25 ++++- src/platform_impl/linux/window.rs | 135 +++++++++++++++++--------- src/window.rs | 4 + 3 files changed, 115 insertions(+), 49 deletions(-) diff --git a/src/platform_impl/linux/event_loop.rs b/src/platform_impl/linux/event_loop.rs index a7a37941f..3deaf97d3 100644 --- a/src/platform_impl/linux/event_loop.rs +++ b/src/platform_impl/linux/event_loop.rs @@ -208,16 +208,31 @@ impl EventLoop { WindowRequest::Title(title) => window.set_title(&title), WindowRequest::Position((x, y)) => window.move_(x, y), WindowRequest::Size((w, h)) => window.resize(w, h), - WindowRequest::SizeConstraint { min, max } => { - let geom_mask = min + WindowRequest::SizeConstraint { + min_width, + min_height, + max_width, + max_height, + } => { + let geom_mask = min_width .map(|_| gdk::WindowHints::MIN_SIZE) .unwrap_or(gdk::WindowHints::empty()) - | max + | min_height + .map(|_| gdk::WindowHints::MIN_SIZE) + .unwrap_or(gdk::WindowHints::empty()) + | max_width + .map(|_| gdk::WindowHints::MAX_SIZE) + .unwrap_or(gdk::WindowHints::empty()) + | max_height .map(|_| gdk::WindowHints::MAX_SIZE) .unwrap_or(gdk::WindowHints::empty()); - let (min_width, min_height) = min.map(Into::into).unwrap_or_default(); - let (max_width, max_height) = max.map(Into::into).unwrap_or_default(); + let min_width = min_width.map(|u| u.0 as i32).unwrap_or_default(); + let min_height = min_height.map(|u| u.0 as i32).unwrap_or_default(); + let max_width = max_width.map(|u| u.0 as i32).unwrap_or_default(); + let max_height = max_height.map(|u| u.0 as i32).unwrap_or_default(); + + dbg!(min_width, min_height, max_width, max_height); let picky_none: Option<>k::Window> = None; window.set_geometry_hints( diff --git a/src/platform_impl/linux/window.rs b/src/platform_impl/linux/window.rs index 80878dfe0..6c06081eb 100644 --- a/src/platform_impl/linux/window.rs +++ b/src/platform_impl/linux/window.rs @@ -21,7 +21,9 @@ use raw_window_handle::{ }; use crate::{ - dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}, + dpi::{ + LogicalPosition, LogicalSize, LogicalUnit, PhysicalPosition, PhysicalSize, Position, Size, Unit, + }, error::{ExternalError, NotSupportedError, OsError as RootOsError}, icon::Icon, menu::{MenuId, MenuItem}, @@ -68,8 +70,10 @@ pub struct Window { maximized: Rc, minimized: Rc, fullscreen: RefCell>, - min_inner_size: RefCell>, - max_inner_size: RefCell>, + min_inner_width: RefCell>, + max_inner_width: RefCell>, + min_inner_height: RefCell>, + max_inner_height: RefCell>, /// Draw event Sender draw_tx: crossbeam_channel::Sender, } @@ -114,20 +118,37 @@ impl Window { // Set Min/Max Size let geom_mask = attributes - .min_inner_size + .min_inner_width .map(|_| gdk::WindowHints::MIN_SIZE) .unwrap_or(gdk::WindowHints::empty()) | attributes - .max_inner_size + .min_inner_height + .map(|_| gdk::WindowHints::MIN_SIZE) + .unwrap_or(gdk::WindowHints::empty()) + | attributes + .max_inner_width + .map(|_| gdk::WindowHints::MAX_SIZE) + .unwrap_or(gdk::WindowHints::empty()) + | attributes + .max_inner_height .map(|_| gdk::WindowHints::MAX_SIZE) .unwrap_or(gdk::WindowHints::empty()); - let (min_width, min_height) = attributes - .min_inner_size - .map(|size| size.to_logical::(win_scale_factor as f64).into()) + + let min_width = attributes + .min_inner_width + .map(|u| u.to_logical::(win_scale_factor as f64).0 as i32) .unwrap_or_default(); - let (max_width, max_height) = attributes - .max_inner_size - .map(|size| size.to_logical::(win_scale_factor as f64).into()) + let min_height = attributes + .min_inner_height + .map(|u| u.to_logical::(win_scale_factor as f64).0 as i32) + .unwrap_or_default(); + let max_width = attributes + .max_inner_width + .map(|u| u.to_logical::(win_scale_factor as f64).0 as i32) + .unwrap_or_default(); + let max_height = attributes + .max_inner_height + .map(|u| u.to_logical::(win_scale_factor as f64).0 as i32) .unwrap_or_default(); let picky_none: Option<>k::Window> = None; window.set_geometry_hints( @@ -360,8 +381,10 @@ impl Window { maximized, minimized, fullscreen: RefCell::new(attributes.fullscreen), - min_inner_size: RefCell::new(attributes.min_inner_size), - max_inner_size: RefCell::new(attributes.max_inner_size), + min_inner_width: RefCell::new(attributes.min_inner_width), + min_inner_height: RefCell::new(attributes.min_inner_height), + max_inner_width: RefCell::new(attributes.max_inner_width), + max_inner_height: RefCell::new(attributes.max_inner_height), }; win.set_skip_taskbar(pl_attribs.skip_taskbar); @@ -444,49 +467,71 @@ impl Window { .to_physical(self.scale_factor.load(Ordering::Acquire) as f64) } - pub fn set_min_inner_size>(&self, min_size: Option) { - *self.min_inner_size.borrow_mut() = min_size.map(Into::into); - + fn set_size_constraints(&self) { let scale_factor = self.scale_factor(); - let min = self - .min_inner_size + let min_width = self + .min_inner_width .borrow() .map(|s| s.to_logical::(scale_factor)); - let max = self - .max_inner_size + let min_height = self + .min_inner_height .borrow() .map(|s| s.to_logical::(scale_factor)); - - if let Err(e) = self - .window_requests_tx - .send((self.window_id, WindowRequest::SizeConstraint { min, max })) - { - log::warn!("Fail to send min size request: {}", e); - } - } - pub fn set_max_inner_size>(&self, max_size: Option) { - *self.max_inner_size.borrow_mut() = max_size.map(Into::into); - - let scale_factor = self.scale_factor(); - - let min = self - .min_inner_size + let max_width = self + .max_inner_width .borrow() .map(|s| s.to_logical::(scale_factor)); - let max = self - .max_inner_size + let max_height = self + .max_inner_height .borrow() .map(|s| s.to_logical::(scale_factor)); - if let Err(e) = self - .window_requests_tx - .send((self.window_id, WindowRequest::SizeConstraint { min, max })) - { - log::warn!("Fail to send max size request: {}", e); + if let Err(e) = self.window_requests_tx.send(( + self.window_id, + WindowRequest::SizeConstraint { + min_width, + min_height, + max_width, + max_height, + }, + )) { + log::warn!("Fail to send size constraint request: {}", e); } } + pub fn set_min_inner_width(&self, width: Option) { + *self.min_inner_width.borrow_mut() = width; + self.set_size_constraints() + } + + pub fn set_min_inner_height(&self, height: Option) { + *self.min_inner_height.borrow_mut() = height; + self.set_size_constraints() + } + + pub fn set_min_inner_size(&self, size: Option) { + *self.min_inner_width.borrow_mut() = size.map(|s| s.width()); + *self.min_inner_height.borrow_mut() = size.map(|s| s.height()); + self.set_size_constraints() + } + + pub fn set_max_inner_width(&self, width: Option) { + *self.max_inner_width.borrow_mut() = width; + self.set_size_constraints() + } + + pub fn set_max_inner_height(&self, height: Option) { + *self.max_inner_height.borrow_mut() = height; + self.set_size_constraints() + } + + pub fn set_max_inner_size(&self, size: Option) { + *self.max_inner_width.borrow_mut() = size.map(|s| s.width()); + *self.max_inner_height.borrow_mut() = size.map(|s| s.height()); + self.set_size_constraints() + } + pub fn set_title(&self, title: &str) { if let Err(e) = self .window_requests_tx @@ -883,8 +928,10 @@ pub enum WindowRequest { Position((i32, i32)), Size((i32, i32)), SizeConstraint { - min: Option>, - max: Option>, + min_width: Option>, + min_height: Option>, + max_width: Option>, + max_height: Option>, }, Visible(bool), Focus, diff --git a/src/window.rs b/src/window.rs index 12fa1da11..82adc50dc 100644 --- a/src/window.rs +++ b/src/window.rs @@ -766,6 +766,7 @@ impl Window { /// /// ## Platform-specific /// + /// **Linux**: Setting a minimum width without a minimum height can cause unwanted behaviors, instead, use [`WindowBuilder::set_min_inner_size`]. /// - **iOS / Android:** Unsupported. #[inline] pub fn set_min_inner_width>(&self, width: Option) { @@ -776,6 +777,7 @@ impl Window { /// /// ## Platform-specific /// + /// **Linux**: Setting a minimum height without a minimum width can cause unwanted behaviors, instead, use [`WindowBuilder::set_min_inner_size`]. /// - **iOS / Android:** Unsupported. #[inline] pub fn set_min_inner_height>(&self, height: Option) { @@ -786,6 +788,7 @@ impl Window { /// /// ## Platform-specific /// + /// **Linux**: Setting a minimum width without a minimum height can cause unwanted behaviors, instead, use [`WindowBuilder::set_max_inner_size`]. /// - **iOS / Android:** Unsupported. #[inline] pub fn set_min_inner_size>(&self, min_size: Option) { @@ -796,6 +799,7 @@ impl Window { /// /// ## Platform-specific /// + /// **Linux**: Setting a maximum height without a maximum width can cause unwanted behaviors, instead, use [`WindowBuilder::set_max_inner_size`]. /// - **iOS / Android:** Unsupported. #[inline] pub fn set_max_inner_width>(&self, width: Option) { From 64c150c5e9277e04b178d5f10612c1ed8101fbd0 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Wed, 5 Jul 2023 19:02:18 +0300 Subject: [PATCH 04/16] fix linux impl --- src/platform_impl/linux/event_loop.rs | 6 ++---- src/window.rs | 4 ---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/platform_impl/linux/event_loop.rs b/src/platform_impl/linux/event_loop.rs index 3deaf97d3..703725267 100644 --- a/src/platform_impl/linux/event_loop.rs +++ b/src/platform_impl/linux/event_loop.rs @@ -229,10 +229,8 @@ impl EventLoop { let min_width = min_width.map(|u| u.0 as i32).unwrap_or_default(); let min_height = min_height.map(|u| u.0 as i32).unwrap_or_default(); - let max_width = max_width.map(|u| u.0 as i32).unwrap_or_default(); - let max_height = max_height.map(|u| u.0 as i32).unwrap_or_default(); - - dbg!(min_width, min_height, max_width, max_height); + let max_width = max_width.map(|u| u.0 as i32).unwrap_or(i32::MAX); + let max_height = max_height.map(|u| u.0 as i32).unwrap_or(i32::MAX); let picky_none: Option<>k::Window> = None; window.set_geometry_hints( diff --git a/src/window.rs b/src/window.rs index 82adc50dc..12fa1da11 100644 --- a/src/window.rs +++ b/src/window.rs @@ -766,7 +766,6 @@ impl Window { /// /// ## Platform-specific /// - /// **Linux**: Setting a minimum width without a minimum height can cause unwanted behaviors, instead, use [`WindowBuilder::set_min_inner_size`]. /// - **iOS / Android:** Unsupported. #[inline] pub fn set_min_inner_width>(&self, width: Option) { @@ -777,7 +776,6 @@ impl Window { /// /// ## Platform-specific /// - /// **Linux**: Setting a minimum height without a minimum width can cause unwanted behaviors, instead, use [`WindowBuilder::set_min_inner_size`]. /// - **iOS / Android:** Unsupported. #[inline] pub fn set_min_inner_height>(&self, height: Option) { @@ -788,7 +786,6 @@ impl Window { /// /// ## Platform-specific /// - /// **Linux**: Setting a minimum width without a minimum height can cause unwanted behaviors, instead, use [`WindowBuilder::set_max_inner_size`]. /// - **iOS / Android:** Unsupported. #[inline] pub fn set_min_inner_size>(&self, min_size: Option) { @@ -799,7 +796,6 @@ impl Window { /// /// ## Platform-specific /// - /// **Linux**: Setting a maximum height without a maximum width can cause unwanted behaviors, instead, use [`WindowBuilder::set_max_inner_size`]. /// - **iOS / Android:** Unsupported. #[inline] pub fn set_max_inner_width>(&self, width: Option) { From 7b1284e294c6ef4e7252eb6e3fe943985e65c3b2 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Wed, 5 Jul 2023 19:42:27 +0300 Subject: [PATCH 05/16] cleanup linux impl --- src/platform_impl/linux/event_loop.rs | 37 +---------- src/platform_impl/linux/util.rs | 53 ++++++++++++++- src/platform_impl/linux/window.rs | 95 +++++---------------------- 3 files changed, 68 insertions(+), 117 deletions(-) diff --git a/src/platform_impl/linux/event_loop.rs b/src/platform_impl/linux/event_loop.rs index 703725267..2fe8fa3b2 100644 --- a/src/platform_impl/linux/event_loop.rs +++ b/src/platform_impl/linux/event_loop.rs @@ -214,42 +214,7 @@ impl EventLoop { max_width, max_height, } => { - let geom_mask = min_width - .map(|_| gdk::WindowHints::MIN_SIZE) - .unwrap_or(gdk::WindowHints::empty()) - | min_height - .map(|_| gdk::WindowHints::MIN_SIZE) - .unwrap_or(gdk::WindowHints::empty()) - | max_width - .map(|_| gdk::WindowHints::MAX_SIZE) - .unwrap_or(gdk::WindowHints::empty()) - | max_height - .map(|_| gdk::WindowHints::MAX_SIZE) - .unwrap_or(gdk::WindowHints::empty()); - - let min_width = min_width.map(|u| u.0 as i32).unwrap_or_default(); - let min_height = min_height.map(|u| u.0 as i32).unwrap_or_default(); - let max_width = max_width.map(|u| u.0 as i32).unwrap_or(i32::MAX); - let max_height = max_height.map(|u| u.0 as i32).unwrap_or(i32::MAX); - - let picky_none: Option<>k::Window> = None; - window.set_geometry_hints( - picky_none, - Some(&gdk::Geometry::new( - min_width, - min_height, - max_width, - max_height, - 0, - 0, - 0, - 0, - 0f64, - 0f64, - gdk::Gravity::Center, - )), - geom_mask, - ) + util::set_size_constraints(&window, min_width, min_height, max_width, max_height); } WindowRequest::Visible(visible) => { if visible { diff --git a/src/platform_impl/linux/util.rs b/src/platform_impl/linux/util.rs index ed1792c82..f6d0e3370 100644 --- a/src/platform_impl/linux/util.rs +++ b/src/platform_impl/linux/util.rs @@ -1,7 +1,8 @@ use gdk::Display; +use gtk::traits::{GtkWindowExt, WidgetExt}; use crate::{ - dpi::{LogicalPosition, PhysicalPosition}, + dpi::{LogicalPosition, PhysicalPosition, Unit}, error::ExternalError, }; @@ -27,3 +28,53 @@ pub fn cursor_position(is_wayland: bool) -> Result, Extern .ok_or(ExternalError::Os(os_error!(super::OsError)))? } } + +pub fn set_size_constraints( + window: &W, + min_width: Option, + min_height: Option, + max_width: Option, + max_height: Option, +) { + let mut geom_mask = gdk::WindowHints::empty(); + if min_width.is_some() || min_height.is_some() { + geom_mask |= gdk::WindowHints::MIN_SIZE; + } + if max_width.is_some() || max_height.is_some() { + geom_mask |= gdk::WindowHints::MAX_SIZE; + } + + let scale_factor = window.scale_factor() as f64; + + let min_width = min_width + .map(|u| u.to_logical::(scale_factor).0 as i32) + .unwrap_or_default(); + let min_height = min_height + .map(|u| u.to_logical::(scale_factor).0 as i32) + .unwrap_or_default(); + let max_width = max_width + .map(|u| u.to_logical::(scale_factor).0 as i32) + .unwrap_or(i32::MAX); + let max_height = max_height + .map(|u| u.to_logical::(scale_factor).0 as i32) + .unwrap_or(i32::MAX); + + let picky_none: Option<>k::Window> = None; + window.set_geometry_hints( + picky_none, + Some(&gdk::Geometry::new( + min_width, + min_height, + max_width, + max_height, + 0, + 0, + 0, + 0, + 0f64, + 0f64, + gdk::Gravity::Center, + )), + geom_mask, + ) +} diff --git a/src/platform_impl/linux/window.rs b/src/platform_impl/linux/window.rs index 6c06081eb..2853e29a9 100644 --- a/src/platform_impl/linux/window.rs +++ b/src/platform_impl/linux/window.rs @@ -21,9 +21,7 @@ use raw_window_handle::{ }; use crate::{ - dpi::{ - LogicalPosition, LogicalSize, LogicalUnit, PhysicalPosition, PhysicalSize, Position, Size, Unit, - }, + dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size, Unit}, error::{ExternalError, NotSupportedError, OsError as RootOsError}, icon::Icon, menu::{MenuId, MenuItem}, @@ -117,56 +115,12 @@ impl Window { window.set_deletable(attributes.closable); // Set Min/Max Size - let geom_mask = attributes - .min_inner_width - .map(|_| gdk::WindowHints::MIN_SIZE) - .unwrap_or(gdk::WindowHints::empty()) - | attributes - .min_inner_height - .map(|_| gdk::WindowHints::MIN_SIZE) - .unwrap_or(gdk::WindowHints::empty()) - | attributes - .max_inner_width - .map(|_| gdk::WindowHints::MAX_SIZE) - .unwrap_or(gdk::WindowHints::empty()) - | attributes - .max_inner_height - .map(|_| gdk::WindowHints::MAX_SIZE) - .unwrap_or(gdk::WindowHints::empty()); - - let min_width = attributes - .min_inner_width - .map(|u| u.to_logical::(win_scale_factor as f64).0 as i32) - .unwrap_or_default(); - let min_height = attributes - .min_inner_height - .map(|u| u.to_logical::(win_scale_factor as f64).0 as i32) - .unwrap_or_default(); - let max_width = attributes - .max_inner_width - .map(|u| u.to_logical::(win_scale_factor as f64).0 as i32) - .unwrap_or_default(); - let max_height = attributes - .max_inner_height - .map(|u| u.to_logical::(win_scale_factor as f64).0 as i32) - .unwrap_or_default(); - let picky_none: Option<>k::Window> = None; - window.set_geometry_hints( - picky_none, - Some(&gdk::Geometry::new( - min_width, - min_height, - max_width, - max_height, - 0, - 0, - 0, - 0, - 0f64, - 0f64, - gdk::Gravity::Center, - )), - geom_mask, + util::set_size_constraints( + &window, + attributes.min_inner_width, + attributes.min_inner_height, + attributes.max_inner_width, + attributes.max_inner_height, ); // Set Position @@ -468,32 +422,13 @@ impl Window { } fn set_size_constraints(&self) { - let scale_factor = self.scale_factor(); - - let min_width = self - .min_inner_width - .borrow() - .map(|s| s.to_logical::(scale_factor)); - let min_height = self - .min_inner_height - .borrow() - .map(|s| s.to_logical::(scale_factor)); - let max_width = self - .max_inner_width - .borrow() - .map(|s| s.to_logical::(scale_factor)); - let max_height = self - .max_inner_height - .borrow() - .map(|s| s.to_logical::(scale_factor)); - if let Err(e) = self.window_requests_tx.send(( self.window_id, WindowRequest::SizeConstraint { - min_width, - min_height, - max_width, - max_height, + min_width: *self.min_inner_width.borrow(), + min_height: *self.min_inner_height.borrow(), + max_width: *self.max_inner_width.borrow(), + max_height: *self.max_inner_height.borrow(), }, )) { log::warn!("Fail to send size constraint request: {}", e); @@ -928,10 +863,10 @@ pub enum WindowRequest { Position((i32, i32)), Size((i32, i32)), SizeConstraint { - min_width: Option>, - min_height: Option>, - max_width: Option>, - max_height: Option>, + min_width: Option, + min_height: Option, + max_width: Option, + max_height: Option, }, Visible(bool), Focus, From 52aa7ddf835716ef689eb30438501d679b90df71 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Wed, 5 Jul 2023 19:46:47 +0300 Subject: [PATCH 06/16] macOS impl --- src/platform_impl/macos/window.rs | 98 ++++++++++++++++++++++++++++--- 1 file changed, 90 insertions(+), 8 deletions(-) diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 3db5044fd..6571c4532 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -495,14 +495,32 @@ impl UnownedWindow { ns_window.setBackgroundColor_(NSColor::clearColor(nil)); } - win_attribs.min_inner_size.map(|dim| { - let logical_dim = dim.to_logical(scale_factor); - set_min_inner_size(*ns_window, logical_dim) - }); - win_attribs.max_inner_size.map(|dim| { - let logical_dim = dim.to_logical(scale_factor); - set_max_inner_size(*ns_window, logical_dim) - }); + if win_attribs.min_inner_width.is_some() || win_attribs.min_inner_height.is_some() { + let logical_dim = LogicalSize { + width: win_attribs + .min_inner_width + .map(|w| w.to_logical(scale_factor).0) + .unwrap_or_default(), + height: win_attribs + .min_inner_height + .map(|w| w.to_logical(scale_factor).0) + .unwrap_or_default(), + }; + set_min_inner_size(*ns_window, logical_dim); + } + if win_attribs.max_inner_width.is_some() || win_attribs.max_inner_height.is_some() { + let logical_dim = LogicalSize { + width: win_attribs + .max_inner_width + .map(|w| w.to_logical(scale_factor).0) + .unwrap_or_default(), + height: win_attribs + .max_inner_height + .map(|w| w.to_logical(scale_factor).0) + .unwrap_or_default(), + }; + set_max_inner_size(*ns_window, logical_dim); + } // register for drag and drop operations. let () = msg_send![ @@ -696,6 +714,38 @@ impl UnownedWindow { } } + pub fn set_min_inner_width(&self, width: Option) { + let scale_factor = self.scale_factor(); + let dimensions = width + .map(|w| { + Logical(LogicalSize { + width: w.to_logical(scale_factor), + height: 0.0, + }) + }) + .unwrap_or(Logical(LogicalSize { + width: 0.0, + height: 0.0, + })); + set_min_inner_size(*self.ns_window, dimensions.to_logical(scale_factor)); + } + + pub fn set_min_inner_height(&self, height: Option) { + let scale_factor = self.scale_factor(); + let dimensions = height + .map(|h| { + Logical(LogicalSize { + width: 0.0, + height: h.to_logical(scale_factor), + }) + }) + .unwrap_or(Logical(LogicalSize { + width: 0.0, + height: 0.0, + })); + set_min_inner_size(*self.ns_window, dimensions.to_logical(scale_factor)); + } + pub fn set_min_inner_size(&self, dimensions: Option) { unsafe { let dimensions = dimensions.unwrap_or(Logical(LogicalSize { @@ -707,6 +757,38 @@ impl UnownedWindow { } } + pub fn set_max_inner_width(&self, width: Option) { + let scale_factor = self.scale_factor(); + let dimensions = width + .map(|w| { + Logical(LogicalSize { + width: w.to_logical(scale_factor), + height: std::f32::MAX as f64, + }) + }) + .unwrap_or(Logical(LogicalSize { + width: std::f32::MAX as f64, + height: std::f32::MAX as f64, + })); + set_max_inner_size(*self.ns_window, dimensions.to_logical(scale_factor)); + } + + pub fn set_max_inner_height(&self, height: Option) { + let scale_factor = self.scale_factor(); + let dimensions = height + .map(|h| { + Logical(LogicalSize { + width: std::f32::MAX as f64, + height: h.to_logical(scale_factor), + }) + }) + .unwrap_or(Logical(LogicalSize { + width: std::f32::MAX as f64, + height: std::f32::MAX as f64, + })); + set_max_inner_size(*self.ns_window, dimensions.to_logical(scale_factor)); + } + pub fn set_max_inner_size(&self, dimensions: Option) { unsafe { let dimensions = dimensions.unwrap_or(Logical(LogicalSize { From 97f359b8e74462e536435cf9ddc939db9325aa82 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Wed, 5 Jul 2023 20:08:22 +0300 Subject: [PATCH 07/16] imports --- src/platform_impl/ios/window.rs | 2 +- src/platform_impl/macos/window.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/platform_impl/ios/window.rs b/src/platform_impl/ios/window.rs index ff2604738..134bed96d 100644 --- a/src/platform_impl/ios/window.rs +++ b/src/platform_impl/ios/window.rs @@ -11,7 +11,7 @@ use std::{ use objc::runtime::{Class, Object, BOOL, NO, YES}; use crate::{ - dpi::{self, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}, + dpi::{self, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size, Unit}, error::{ExternalError, NotSupportedError, OsError as RootOsError}, event::{Event, WindowEvent}, icon::Icon, diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 6571c4532..5d68f995a 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -20,6 +20,7 @@ use raw_window_handle::{ use crate::{ dpi::{ LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size, Size::Logical, + Unit, }, error::{ExternalError, NotSupportedError, OsError as RootOsError}, icon::Icon, From b314bd0b6bb232614422818362da9a053cb5ff6f Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Wed, 5 Jul 2023 20:26:41 +0300 Subject: [PATCH 08/16] ios build --- src/platform_impl/ios/window.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/platform_impl/ios/window.rs b/src/platform_impl/ios/window.rs index 134bed96d..875277b9c 100644 --- a/src/platform_impl/ios/window.rs +++ b/src/platform_impl/ios/window.rs @@ -473,11 +473,17 @@ impl Window { window_attributes: WindowAttributes, platform_attributes: PlatformSpecificWindowBuilderAttributes, ) -> Result { - if let Some(_) = window_attributes.min_inner_size { - warn!("`WindowAttributes::min_inner_size` is ignored on iOS"); + if let Some(_) = window_attributes.min_inner_width { + warn!("`WindowAttributes::min_inner_width` is ignored on iOS"); } - if let Some(_) = window_attributes.max_inner_size { - warn!("`WindowAttributes::max_inner_size` is ignored on iOS"); + if let Some(_) = window_attributes.min_inner_height { + warn!("`WindowAttributes::min_inner_height` is ignored on iOS"); + } + if let Some(_) = window_attributes.max_inner_width { + warn!("`WindowAttributes::max_inner_width` is ignored on iOS"); + } + if let Some(_) = window_attributes.max_inner_height { + warn!("`WindowAttributes::max_inner_height` is ignored on iOS"); } if window_attributes.always_on_top { warn!("`WindowAttributes::always_on_top` is unsupported on iOS"); From 0e3b48aee7e2906eededfca8479f4f80bccd2002 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Wed, 5 Jul 2023 20:27:53 +0300 Subject: [PATCH 09/16] macos build --- src/platform_impl/macos/window.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 5d68f995a..30c2a9c7e 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -720,7 +720,7 @@ impl UnownedWindow { let dimensions = width .map(|w| { Logical(LogicalSize { - width: w.to_logical(scale_factor), + width: w.to_logical(scale_factor).0, height: 0.0, }) }) @@ -737,7 +737,7 @@ impl UnownedWindow { .map(|h| { Logical(LogicalSize { width: 0.0, - height: h.to_logical(scale_factor), + height: h.to_logical(scale_factor).0, }) }) .unwrap_or(Logical(LogicalSize { @@ -763,7 +763,7 @@ impl UnownedWindow { let dimensions = width .map(|w| { Logical(LogicalSize { - width: w.to_logical(scale_factor), + width: w.to_logical(scale_factor).0, height: std::f32::MAX as f64, }) }) @@ -780,7 +780,7 @@ impl UnownedWindow { .map(|h| { Logical(LogicalSize { width: std::f32::MAX as f64, - height: h.to_logical(scale_factor), + height: h.to_logical(scale_factor).0, }) }) .unwrap_or(Logical(LogicalSize { From bdc43e2dbc42adbb6c0614f9de2f45fa57bafa17 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Wed, 5 Jul 2023 20:55:12 +0300 Subject: [PATCH 10/16] unsafe --- src/platform_impl/macos/window.rs | 104 ++++++++++++++++-------------- 1 file changed, 56 insertions(+), 48 deletions(-) diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 30c2a9c7e..9a6d57fbb 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -716,35 +716,39 @@ impl UnownedWindow { } pub fn set_min_inner_width(&self, width: Option) { - let scale_factor = self.scale_factor(); - let dimensions = width - .map(|w| { - Logical(LogicalSize { - width: w.to_logical(scale_factor).0, - height: 0.0, + unsafe { + let scale_factor = self.scale_factor(); + let dimensions = width + .map(|w| { + Logical(LogicalSize { + width: w.to_logical(scale_factor).0, + height: 0.0, + }) }) - }) - .unwrap_or(Logical(LogicalSize { - width: 0.0, - height: 0.0, - })); - set_min_inner_size(*self.ns_window, dimensions.to_logical(scale_factor)); + .unwrap_or(Logical(LogicalSize { + width: 0.0, + height: 0.0, + })); + set_min_inner_size(*self.ns_window, dimensions.to_logical(scale_factor)); + } } pub fn set_min_inner_height(&self, height: Option) { - let scale_factor = self.scale_factor(); - let dimensions = height - .map(|h| { - Logical(LogicalSize { - width: 0.0, - height: h.to_logical(scale_factor).0, + unsafe { + let scale_factor = self.scale_factor(); + let dimensions = height + .map(|h| { + Logical(LogicalSize { + width: 0.0, + height: h.to_logical(scale_factor).0, + }) }) - }) - .unwrap_or(Logical(LogicalSize { - width: 0.0, - height: 0.0, - })); - set_min_inner_size(*self.ns_window, dimensions.to_logical(scale_factor)); + .unwrap_or(Logical(LogicalSize { + width: 0.0, + height: 0.0, + })); + set_min_inner_size(*self.ns_window, dimensions.to_logical(scale_factor)); + } } pub fn set_min_inner_size(&self, dimensions: Option) { @@ -759,35 +763,39 @@ impl UnownedWindow { } pub fn set_max_inner_width(&self, width: Option) { - let scale_factor = self.scale_factor(); - let dimensions = width - .map(|w| { - Logical(LogicalSize { - width: w.to_logical(scale_factor).0, - height: std::f32::MAX as f64, + unsafe { + let scale_factor = self.scale_factor(); + let dimensions = width + .map(|w| { + Logical(LogicalSize { + width: w.to_logical(scale_factor).0, + height: std::f32::MAX as f64, + }) }) - }) - .unwrap_or(Logical(LogicalSize { - width: std::f32::MAX as f64, - height: std::f32::MAX as f64, - })); - set_max_inner_size(*self.ns_window, dimensions.to_logical(scale_factor)); + .unwrap_or(Logical(LogicalSize { + width: std::f32::MAX as f64, + height: std::f32::MAX as f64, + })); + set_max_inner_size(*self.ns_window, dimensions.to_logical(scale_factor)); + } } pub fn set_max_inner_height(&self, height: Option) { - let scale_factor = self.scale_factor(); - let dimensions = height - .map(|h| { - Logical(LogicalSize { - width: std::f32::MAX as f64, - height: h.to_logical(scale_factor).0, + unsafe { + let scale_factor = self.scale_factor(); + let dimensions = height + .map(|h| { + Logical(LogicalSize { + width: std::f32::MAX as f64, + height: h.to_logical(scale_factor).0, + }) }) - }) - .unwrap_or(Logical(LogicalSize { - width: std::f32::MAX as f64, - height: std::f32::MAX as f64, - })); - set_max_inner_size(*self.ns_window, dimensions.to_logical(scale_factor)); + .unwrap_or(Logical(LogicalSize { + width: std::f32::MAX as f64, + height: std::f32::MAX as f64, + })); + set_max_inner_size(*self.ns_window, dimensions.to_logical(scale_factor)); + } } pub fn set_max_inner_size(&self, dimensions: Option) { From eeaa8f0e28a2b4ef6a98ccb51d378cdfaa4049d2 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Thu, 6 Jul 2023 02:33:24 +0300 Subject: [PATCH 11/16] merge `set_min/max_width/height` into a single function --- examples/min_max_size.rs | 31 ++- src/dpi.rs | 54 +++-- src/platform_impl/android/mod.rs | 10 +- src/platform_impl/ios/window.rs | 31 +-- src/platform_impl/linux/event_loop.rs | 9 +- src/platform_impl/linux/util.rs | 34 +-- src/platform_impl/linux/window.rs | 72 ++----- src/platform_impl/macos/window.rs | 150 ++++--------- src/platform_impl/windows/event_loop.rs | 40 ++-- src/platform_impl/windows/system_tray.rs | 2 +- src/platform_impl/windows/window.rs | 84 ++------ src/platform_impl/windows/window_state.rs | 14 +- src/window.rs | 250 +++++++++++++--------- 13 files changed, 311 insertions(+), 470 deletions(-) diff --git a/examples/min_max_size.rs b/examples/min_max_size.rs index 720932a6f..85894e5f8 100644 --- a/examples/min_max_size.rs +++ b/examples/min_max_size.rs @@ -7,7 +7,7 @@ use tao::{ event::{ElementState, Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, keyboard::Key, - window::WindowBuilder, + window::{WindowBuilder, WindowSizeConstraints}, }; #[allow(clippy::single_match)] @@ -16,13 +16,10 @@ fn main() { let event_loop = EventLoop::new(); let min_width = 400.0; - let mut min_width_set = false; let max_width = 800.0; - let mut max_width_set = false; let min_height = 200.0; - let mut min_height_set = false; let max_height = 400.0; - let mut max_height_set = false; + let mut size_constraints = WindowSizeConstraints::default(); let window = WindowBuilder::new().build(&event_loop).unwrap(); @@ -55,24 +52,24 @@ fn main() { .. } => match key_str { "e" => { - min_width_set = !min_width_set; - let min_width: Option> = min_width_set.then_some(min_width.into()); - window.set_min_inner_width(min_width); + size_constraints.min_width = + (!size_constraints.min_width.is_some()).then_some(LogicalUnit::new(min_width).into()); + window.set_inner_size_constraints(size_constraints); } "f" => { - max_width_set = !max_width_set; - let max_width: Option> = max_width_set.then_some(max_width.into()); - window.set_max_inner_width(max_width); + size_constraints.max_width = + (!size_constraints.max_width.is_some()).then_some(LogicalUnit::new(max_width).into()); + window.set_inner_size_constraints(size_constraints); } "p" => { - min_height_set = !min_height_set; - let min_height: Option> = min_height_set.then_some(min_height.into()); - window.set_min_inner_height(min_height); + size_constraints.min_height = + (!size_constraints.min_height.is_some()).then_some(LogicalUnit::new(min_height).into()); + window.set_inner_size_constraints(size_constraints); } "v" => { - max_height_set = !max_height_set; - let max_height: Option> = max_height_set.then_some(max_height.into()); - window.set_max_inner_height(max_height); + size_constraints.max_height = + (!size_constraints.max_height.is_some()).then_some(LogicalUnit::new(max_height).into()); + window.set_inner_size_constraints(size_constraints); } _ => {} }, diff --git a/src/dpi.rs b/src/dpi.rs index eb18776ef..dd2bb91a8 100644 --- a/src/dpi.rs +++ b/src/dpi.rs @@ -93,6 +93,9 @@ pub trait Pixel: Copy + Into { fn from_f64(f: f64) -> Self; + fn inner(&self) -> Self { + *self + } fn cast(self) -> P { P::from_f64(self.into()) } @@ -492,25 +495,25 @@ impl Size { } } - pub fn clamp>(input: S, min: S, max: S, scale_factor: f64) -> Size { - let (input, min, max) = ( - input.into().to_physical::(scale_factor), + pub fn clamp>(desired_size: S, min: S, max: S, scale_factor: f64) -> Size { + let (desired_size, min, max) = ( + desired_size.into().to_physical::(scale_factor), min.into().to_physical::(scale_factor), max.into().to_physical::(scale_factor), ); - let clamp = |input: f64, min: f64, max: f64| { - if input < min { + let clamp = |desired_size: f64, min: f64, max: f64| { + if desired_size < min { min - } else if input > max { + } else if desired_size > max { max } else { - input + desired_size } }; - let width = clamp(input.width, min.width, max.width); - let height = clamp(input.height, min.height, max.height); + let width = clamp(desired_size.width, min.width, max.width); + let height = clamp(desired_size.height, min.height, max.height); PhysicalSize::new(width, height).into() } @@ -533,12 +536,14 @@ impl From> for Size { /// A unit represented in logical pixels. #[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, PartialOrd, Ord)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct LogicalUnit

(pub P); +pub struct LogicalUnit

{ + pub value: P, +} impl

LogicalUnit

{ #[inline] - pub const fn new(u: P) -> Self { - Self(u) + pub const fn new(value: P) -> Self { + Self { value } } } @@ -551,13 +556,13 @@ impl LogicalUnit

{ #[inline] pub fn to_physical(&self, scale_factor: f64) -> PhysicalUnit { assert!(validate_scale_factor(scale_factor)); - let u = self.0.into() * scale_factor; + let u = self.value.into() * scale_factor; PhysicalUnit::new(u).cast() } #[inline] pub fn cast(&self) -> LogicalUnit { - LogicalUnit(self.0.cast()) + LogicalUnit::new(self.value.cast()) } } @@ -570,12 +575,14 @@ impl From

for LogicalUnit

{ /// A unit represented in physical pixels. #[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, PartialOrd, Ord)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct PhysicalUnit

(pub P); +pub struct PhysicalUnit

{ + pub value: P, +} impl

PhysicalUnit

{ #[inline] - pub const fn new(u: P) -> Self { - Self(u) + pub const fn new(value: P) -> Self { + Self { value } } } @@ -588,13 +595,13 @@ impl PhysicalUnit

{ #[inline] pub fn to_logical(&self, scale_factor: f64) -> LogicalUnit { assert!(validate_scale_factor(scale_factor)); - let u = self.0.into() / scale_factor; + let u = self.value.into() / scale_factor; LogicalUnit::new(u).cast() } #[inline] pub fn cast(&self) -> PhysicalUnit { - PhysicalUnit(self.0.cast()) + PhysicalUnit::new(self.value.cast()) } } @@ -611,7 +618,16 @@ pub enum Unit { Logical(LogicalUnit), } +impl Default for Unit { + fn default() -> Self { + Self::Logical(LogicalUnit::new(f64::default())) + } +} + impl Unit { + pub const MIN: Unit = Unit::Logical(LogicalUnit::new(0.0)); + pub const MAX: Unit = Unit::Logical(LogicalUnit::new(f64::MAX)); + pub fn new>(val: S) -> Unit { val.into() } diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index c12840f63..62fe5a186 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -5,14 +5,14 @@ #![cfg(target_os = "android")] use crate::{ accelerator::Accelerator, - dpi::{PhysicalPosition, PhysicalSize, Position, Size, Unit}, + dpi::{PhysicalPosition, PhysicalSize, Position, Size}, error, event, event_loop::{self, ControlFlow}, icon::Icon, keyboard::{Key, KeyCode, KeyLocation, NativeKeyCode}, menu::{CustomMenuItem, MenuId, MenuItem, MenuType}, monitor, - window::{self, Theme}, + window::{self, Theme, WindowSizeConstraints}, }; use ndk::{ configuration::Configuration, @@ -601,13 +601,9 @@ impl Window { MonitorHandle.size() } - pub fn set_min_inner_width(&self, _: Option) {} - pub fn set_min_inner_height(&self, _: Option) {} pub fn set_min_inner_size(&self, _: Option) {} - - pub fn set_max_inner_width(&self, _: Option) {} - pub fn set_max_inner_height(&self, _: Option) {} pub fn set_max_inner_size(&self, _: Option) {} + pub fn set_inner_size_constraints(&self, _: WindowSizeConstraints) {} pub fn set_title(&self, _title: &str) {} pub fn title(&self) -> String { diff --git a/src/platform_impl/ios/window.rs b/src/platform_impl/ios/window.rs index 875277b9c..ebbf28312 100644 --- a/src/platform_impl/ios/window.rs +++ b/src/platform_impl/ios/window.rs @@ -11,7 +11,7 @@ use std::{ use objc::runtime::{Class, Object, BOOL, NO, YES}; use crate::{ - dpi::{self, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size, Unit}, + dpi::{self, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}, error::{ExternalError, NotSupportedError, OsError as RootOsError}, event::{Event, WindowEvent}, icon::Icon, @@ -28,6 +28,7 @@ use crate::{ }, window::{ CursorIcon, Fullscreen, Theme, UserAttentionType, WindowAttributes, WindowId as RootWindowId, + WindowSizeConstraints, }, }; @@ -169,25 +170,15 @@ impl Inner { warn!("not clear what `Window::set_inner_size` means on iOS"); } - pub fn set_min_inner_width(&self, _: Option) { - warn!("`Window::set_min_inner_width` is ignored on iOS") - } - pub fn set_min_inner_height(&self, _: Option) { - warn!("`Window::set_min_inner_height` is ignored on iOS") - } pub fn set_min_inner_size(&self, _: Option) { warn!("`Window::set_min_inner_size` is ignored on iOS") } - - pub fn set_max_inner_width(&self, _: Option) { - warn!("`Window::set_max_inner_width` is ignored on iOS") - } - pub fn set_max_inner_height(&self, _: Option) { - warn!("`Window::set_max_inner_height` is ignored on iOS") - } pub fn set_max_inner_size(&self, _: Option) { warn!("`Window::set_max_inner_size` is ignored on iOS") } + pub fn set_inner_size_constraints(&self, _: WindowSizeConstraints) { + warn!("`Window::set_inner_size_constraints` is ignored on iOS") + } pub fn set_resizable(&self, _resizable: bool) { warn!("`Window::set_resizable` is ignored on iOS") @@ -473,18 +464,6 @@ impl Window { window_attributes: WindowAttributes, platform_attributes: PlatformSpecificWindowBuilderAttributes, ) -> Result { - if let Some(_) = window_attributes.min_inner_width { - warn!("`WindowAttributes::min_inner_width` is ignored on iOS"); - } - if let Some(_) = window_attributes.min_inner_height { - warn!("`WindowAttributes::min_inner_height` is ignored on iOS"); - } - if let Some(_) = window_attributes.max_inner_width { - warn!("`WindowAttributes::max_inner_width` is ignored on iOS"); - } - if let Some(_) = window_attributes.max_inner_height { - warn!("`WindowAttributes::max_inner_height` is ignored on iOS"); - } if window_attributes.always_on_top { warn!("`WindowAttributes::always_on_top` is unsupported on iOS"); } diff --git a/src/platform_impl/linux/event_loop.rs b/src/platform_impl/linux/event_loop.rs index 2fe8fa3b2..edb7a3c5e 100644 --- a/src/platform_impl/linux/event_loop.rs +++ b/src/platform_impl/linux/event_loop.rs @@ -208,13 +208,8 @@ impl EventLoop { WindowRequest::Title(title) => window.set_title(&title), WindowRequest::Position((x, y)) => window.move_(x, y), WindowRequest::Size((w, h)) => window.resize(w, h), - WindowRequest::SizeConstraint { - min_width, - min_height, - max_width, - max_height, - } => { - util::set_size_constraints(&window, min_width, min_height, max_width, max_height); + WindowRequest::SizeConstraints(constraints) => { + util::set_size_constraints(&window, constraints); } WindowRequest::Visible(visible) => { if visible { diff --git a/src/platform_impl/linux/util.rs b/src/platform_impl/linux/util.rs index f6d0e3370..1e0a3d150 100644 --- a/src/platform_impl/linux/util.rs +++ b/src/platform_impl/linux/util.rs @@ -2,8 +2,9 @@ use gdk::Display; use gtk::traits::{GtkWindowExt, WidgetExt}; use crate::{ - dpi::{LogicalPosition, PhysicalPosition, Unit}, + dpi::{LogicalPosition, LogicalSize, PhysicalPosition}, error::ExternalError, + window::WindowSizeConstraints, }; #[inline] @@ -31,42 +32,29 @@ pub fn cursor_position(is_wayland: bool) -> Result, Extern pub fn set_size_constraints( window: &W, - min_width: Option, - min_height: Option, - max_width: Option, - max_height: Option, + constraints: WindowSizeConstraints, ) { let mut geom_mask = gdk::WindowHints::empty(); - if min_width.is_some() || min_height.is_some() { + if constraints.has_min() { geom_mask |= gdk::WindowHints::MIN_SIZE; } - if max_width.is_some() || max_height.is_some() { + if constraints.has_max() { geom_mask |= gdk::WindowHints::MAX_SIZE; } let scale_factor = window.scale_factor() as f64; - let min_width = min_width - .map(|u| u.to_logical::(scale_factor).0 as i32) - .unwrap_or_default(); - let min_height = min_height - .map(|u| u.to_logical::(scale_factor).0 as i32) - .unwrap_or_default(); - let max_width = max_width - .map(|u| u.to_logical::(scale_factor).0 as i32) - .unwrap_or(i32::MAX); - let max_height = max_height - .map(|u| u.to_logical::(scale_factor).0 as i32) - .unwrap_or(i32::MAX); + let min_size: LogicalSize = constraints.min_size_logical(scale_factor); + let max_size: LogicalSize = constraints.max_size_logical(scale_factor); let picky_none: Option<>k::Window> = None; window.set_geometry_hints( picky_none, Some(&gdk::Geometry::new( - min_width, - min_height, - max_width, - max_height, + min_size.width, + min_size.height, + max_size.width, + max_size.height, 0, 0, 0, diff --git a/src/platform_impl/linux/window.rs b/src/platform_impl/linux/window.rs index 2853e29a9..c13e45f90 100644 --- a/src/platform_impl/linux/window.rs +++ b/src/platform_impl/linux/window.rs @@ -21,13 +21,14 @@ use raw_window_handle::{ }; use crate::{ - dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size, Unit}, + dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}, error::{ExternalError, NotSupportedError, OsError as RootOsError}, icon::Icon, menu::{MenuId, MenuItem}, monitor::MonitorHandle as RootMonitorHandle, window::{ - CursorIcon, Fullscreen, Theme, UserAttentionType, WindowAttributes, BORDERLESS_RESIZE_INSET, + CursorIcon, Fullscreen, Theme, UserAttentionType, WindowAttributes, WindowSizeConstraints, + BORDERLESS_RESIZE_INSET, }, }; @@ -68,10 +69,7 @@ pub struct Window { maximized: Rc, minimized: Rc, fullscreen: RefCell>, - min_inner_width: RefCell>, - max_inner_width: RefCell>, - min_inner_height: RefCell>, - max_inner_height: RefCell>, + inner_size_constraints: RefCell, /// Draw event Sender draw_tx: crossbeam_channel::Sender, } @@ -115,13 +113,7 @@ impl Window { window.set_deletable(attributes.closable); // Set Min/Max Size - util::set_size_constraints( - &window, - attributes.min_inner_width, - attributes.min_inner_height, - attributes.max_inner_width, - attributes.max_inner_height, - ); + util::set_size_constraints(&window, attributes.inner_size_constraints); // Set Position if let Some(position) = attributes.position { @@ -287,12 +279,12 @@ impl Window { let maximized: Rc = Rc::new(w_max.into()); let max_clone = maximized.clone(); let minimized = Rc::new(AtomicBool::new(false)); - let min_clone = minimized.clone(); + let minimized_clone = minimized.clone(); window.connect_window_state_event(move |_, event| { let state = event.new_window_state(); max_clone.store(state.contains(WindowState::MAXIMIZED), Ordering::Release); - min_clone.store(state.contains(WindowState::ICONIFIED), Ordering::Release); + minimized_clone.store(state.contains(WindowState::ICONIFIED), Ordering::Release); Inhibit(false) }); @@ -335,10 +327,7 @@ impl Window { maximized, minimized, fullscreen: RefCell::new(attributes.fullscreen), - min_inner_width: RefCell::new(attributes.min_inner_width), - min_inner_height: RefCell::new(attributes.min_inner_height), - max_inner_width: RefCell::new(attributes.max_inner_width), - max_inner_height: RefCell::new(attributes.max_inner_height), + inner_size_constraints: RefCell::new(attributes.inner_size_constraints), }; win.set_skip_taskbar(pl_attribs.skip_taskbar); @@ -424,46 +413,28 @@ impl Window { fn set_size_constraints(&self) { if let Err(e) = self.window_requests_tx.send(( self.window_id, - WindowRequest::SizeConstraint { - min_width: *self.min_inner_width.borrow(), - min_height: *self.min_inner_height.borrow(), - max_width: *self.max_inner_width.borrow(), - max_height: *self.max_inner_height.borrow(), - }, + WindowRequest::SizeConstraints(*self.inner_size_constraints.borrow()), )) { log::warn!("Fail to send size constraint request: {}", e); } } - pub fn set_min_inner_width(&self, width: Option) { - *self.min_inner_width.borrow_mut() = width; - self.set_size_constraints() - } - - pub fn set_min_inner_height(&self, height: Option) { - *self.min_inner_height.borrow_mut() = height; - self.set_size_constraints() - } - pub fn set_min_inner_size(&self, size: Option) { - *self.min_inner_width.borrow_mut() = size.map(|s| s.width()); - *self.min_inner_height.borrow_mut() = size.map(|s| s.height()); + let mut size_constraints = self.inner_size_constraints.borrow_mut(); + size_constraints.min_width = size.map(|s| s.width()); + size_constraints.min_height = size.map(|s| s.height()); self.set_size_constraints() } - pub fn set_max_inner_width(&self, width: Option) { - *self.max_inner_width.borrow_mut() = width; - self.set_size_constraints() - } - - pub fn set_max_inner_height(&self, height: Option) { - *self.max_inner_height.borrow_mut() = height; + pub fn set_max_inner_size(&self, size: Option) { + let mut size_constraints = self.inner_size_constraints.borrow_mut(); + size_constraints.max_width = size.map(|s| s.width()); + size_constraints.max_height = size.map(|s| s.height()); self.set_size_constraints() } - pub fn set_max_inner_size(&self, size: Option) { - *self.max_inner_width.borrow_mut() = size.map(|s| s.width()); - *self.max_inner_height.borrow_mut() = size.map(|s| s.height()); + pub fn set_inner_size_constraints(&self, constraints: WindowSizeConstraints) { + *self.inner_size_constraints.borrow_mut() = constraints; self.set_size_constraints() } @@ -862,12 +833,7 @@ pub enum WindowRequest { Title(String), Position((i32, i32)), Size((i32, i32)), - SizeConstraint { - min_width: Option, - min_height: Option, - max_width: Option, - max_height: Option, - }, + SizeConstraints(WindowSizeConstraints), Visible(bool), Focus, Resizable(bool), diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 9a6d57fbb..ada950c84 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -20,7 +20,6 @@ use raw_window_handle::{ use crate::{ dpi::{ LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size, Size::Logical, - Unit, }, error::{ExternalError, NotSupportedError, OsError as RootOsError}, icon::Icon, @@ -37,6 +36,7 @@ use crate::{ }, window::{ CursorIcon, Fullscreen, Theme, UserAttentionType, WindowAttributes, WindowId as RootWindowId, + WindowSizeConstraints, }, }; use cocoa::{ @@ -163,13 +163,12 @@ fn create_window( None => { let screen = NSScreen::mainScreen(nil); let scale_factor = NSScreen::backingScaleFactor(screen) as f64; - let (width, height) = match attrs.inner_size { - Some(size) => { - let logical = size.to_logical(scale_factor); - (logical.width, logical.height) - } - None => (800.0, 600.0), - }; + let desired_size = attributes + .inner_size + .unwrap_or_else(|| PhysicalSize::new(800, 600).into()); + let size = attributes + .inner_size_constraints + .clamp(desired_size, scale_factor); let (left, bottom) = match attrs.position { Some(position) => { let logical = util::window_position(position.to_logical(scale_factor)); @@ -180,7 +179,10 @@ fn create_window( // This value is ignored by calling win.center() below None => (0.0, 0.0), }; - NSRect::new(NSPoint::new(left, bottom), NSSize::new(width, height)) + NSRect::new( + NSPoint::new(left, bottom), + NSSize::new(size.width, size.height), + ) } }; @@ -496,31 +498,17 @@ impl UnownedWindow { ns_window.setBackgroundColor_(NSColor::clearColor(nil)); } - if win_attribs.min_inner_width.is_some() || win_attribs.min_inner_height.is_some() { - let logical_dim = LogicalSize { - width: win_attribs - .min_inner_width - .map(|w| w.to_logical(scale_factor).0) - .unwrap_or_default(), - height: win_attribs - .min_inner_height - .map(|w| w.to_logical(scale_factor).0) - .unwrap_or_default(), - }; - set_min_inner_size(*ns_window, logical_dim); + if win_attribs.inner_size_constraints.has_min() { + let min_size = win_attribs + .inner_size_constraints + .min_size_logical(scale_factor); + set_min_inner_size(*ns_window, min_size); } - if win_attribs.max_inner_width.is_some() || win_attribs.max_inner_height.is_some() { - let logical_dim = LogicalSize { - width: win_attribs - .max_inner_width - .map(|w| w.to_logical(scale_factor).0) - .unwrap_or_default(), - height: win_attribs - .max_inner_height - .map(|w| w.to_logical(scale_factor).0) - .unwrap_or_default(), - }; - set_max_inner_size(*ns_window, logical_dim); + if win_attribs.inner_size_constraints.has_max() { + let max_size = win_attribs + .inner_size_constraints + .max_size_logical(scale_factor); + set_max_inner_size(*ns_window, max_size); } // register for drag and drop operations. @@ -715,97 +703,35 @@ impl UnownedWindow { } } - pub fn set_min_inner_width(&self, width: Option) { - unsafe { - let scale_factor = self.scale_factor(); - let dimensions = width - .map(|w| { - Logical(LogicalSize { - width: w.to_logical(scale_factor).0, - height: 0.0, - }) - }) - .unwrap_or(Logical(LogicalSize { - width: 0.0, - height: 0.0, - })); - set_min_inner_size(*self.ns_window, dimensions.to_logical(scale_factor)); - } - } - - pub fn set_min_inner_height(&self, height: Option) { - unsafe { - let scale_factor = self.scale_factor(); - let dimensions = height - .map(|h| { - Logical(LogicalSize { - width: 0.0, - height: h.to_logical(scale_factor).0, - }) - }) - .unwrap_or(Logical(LogicalSize { - width: 0.0, - height: 0.0, - })); - set_min_inner_size(*self.ns_window, dimensions.to_logical(scale_factor)); - } - } - pub fn set_min_inner_size(&self, dimensions: Option) { + let dimensions = dimensions.unwrap_or(Logical(LogicalSize { + width: 0.0, + height: 0.0, + })); + let scale_factor = self.scale_factor(); unsafe { - let dimensions = dimensions.unwrap_or(Logical(LogicalSize { - width: 0.0, - height: 0.0, - })); - let scale_factor = self.scale_factor(); set_min_inner_size(*self.ns_window, dimensions.to_logical(scale_factor)); } } - pub fn set_max_inner_width(&self, width: Option) { - unsafe { - let scale_factor = self.scale_factor(); - let dimensions = width - .map(|w| { - Logical(LogicalSize { - width: w.to_logical(scale_factor).0, - height: std::f32::MAX as f64, - }) - }) - .unwrap_or(Logical(LogicalSize { - width: std::f32::MAX as f64, - height: std::f32::MAX as f64, - })); - set_max_inner_size(*self.ns_window, dimensions.to_logical(scale_factor)); - } - } - - pub fn set_max_inner_height(&self, height: Option) { + pub fn set_max_inner_size(&self, dimensions: Option) { + let dimensions = dimensions.unwrap_or(Logical(LogicalSize { + width: std::f32::MAX as f64, + height: std::f32::MAX as f64, + })); + let scale_factor = self.scale_factor(); unsafe { - let scale_factor = self.scale_factor(); - let dimensions = height - .map(|h| { - Logical(LogicalSize { - width: std::f32::MAX as f64, - height: h.to_logical(scale_factor).0, - }) - }) - .unwrap_or(Logical(LogicalSize { - width: std::f32::MAX as f64, - height: std::f32::MAX as f64, - })); set_max_inner_size(*self.ns_window, dimensions.to_logical(scale_factor)); } } - pub fn set_max_inner_size(&self, dimensions: Option) { + pub fn set_inner_size_constraints(&self, constraints: WindowSizeConstraints) { + let scale_factor = self.scale_factor(); unsafe { - let dimensions = dimensions.unwrap_or(Logical(LogicalSize { - width: std::f32::MAX as f64, - height: std::f32::MAX as f64, - })); - let scale_factor = self.scale_factor(); - set_max_inner_size(*self.ns_window, dimensions.to_logical(scale_factor)); + let min_size = constraints.min_size_logical(scale_factor); + set_min_inner_size(*self.ns_window, min_size); + let max_size = constraints.max_size_logical(scale_factor); + set_max_inner_size(*self.ns_window, max_size); } } diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 92a3b5e71..c2e623b4c 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -44,7 +44,7 @@ use windows::{ use crate::{ accelerator::AcceleratorId, - dpi::{PhysicalPosition, PhysicalSize}, + dpi::{PhysicalPosition, PhysicalSize, Unit}, error::ExternalError, event::{DeviceEvent, Event, Force, RawKeyEvent, Touch, TouchPhase, WindowEvent}, event_loop::{ControlFlow, DeviceEventFilter, EventLoopClosed, EventLoopWindowTarget as RootELW}, @@ -1777,33 +1777,31 @@ unsafe fn public_window_callback_inner( .window_flags() .contains(WindowFlags::MARKER_DECORATIONS); - if window_state.min_width.is_some() || window_state.min_height.is_some() { - let min_size = PhysicalSize::new( - window_state - .min_width - .map(|w| w.to_physical(window_state.scale_factor).0) - .unwrap_or_default(), - window_state - .min_height - .map(|w| w.to_physical(window_state.scale_factor).0) - .unwrap_or_default(), - ); + let size_constraints = window_state.size_constraints; + + if size_constraints.has_min() { + let min_size = size_constraints.min_size_physical(window_state.scale_factor); let (width, height): (u32, u32) = util::adjust_size(window, min_size, is_decorated).into(); (*mmi).ptMinTrackSize = POINT { x: width as i32, y: height as i32, }; } - if window_state.max_width.is_some() || window_state.max_height.is_some() { + if size_constraints.has_max() { + // we can't use WindowSizeConstraints::max_size_physical because + // for Windows, in order to remove the max constraints, we need to fall to Unit::MIN (which is `0`) + // instead of Unit::MAX (which is f64::MAX) let max_size = PhysicalSize::new( - window_state + size_constraints .max_width - .map(|w| w.to_physical(window_state.scale_factor).0) - .unwrap_or_default(), - window_state + .unwrap_or(Unit::MIN) + .to_physical(window_state.scale_factor) + .value, + size_constraints .max_height - .map(|w| w.to_physical(window_state.scale_factor).0) - .unwrap_or_default(), + .unwrap_or(Unit::MIN) + .to_physical(window_state.scale_factor) + .value, ); let (width, height): (u32, u32) = util::adjust_size(window, max_size, is_decorated).into(); (*mmi).ptMaxTrackSize = POINT { @@ -1905,7 +1903,7 @@ unsafe fn public_window_callback_inner( false => old_physical_inner_size, }; - let _ = subclass_input.send_event(Event::WindowEvent { + subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window.0)), event: ScaleFactorChanged { scale_factor: new_scale_factor, @@ -2042,7 +2040,7 @@ unsafe fn public_window_callback_inner( let preferred_theme = subclass_input.window_state.lock().preferred_theme; - if preferred_theme == None { + if preferred_theme.is_none() { let new_theme = try_theme(window, preferred_theme); let mut window_state = subclass_input.window_state.lock(); diff --git a/src/platform_impl/windows/system_tray.rs b/src/platform_impl/windows/system_tray.rs index f44c5184c..aa2a43d77 100644 --- a/src/platform_impl/windows/system_tray.rs +++ b/src/platform_impl/windows/system_tray.rs @@ -114,7 +114,7 @@ impl SystemTrayBuilder { ))); } - let system_tray = SystemTray { hwnd: hwnd.clone() }; + let system_tray = SystemTray { hwnd }; // system_tray event handler let event_loop_runner = window_target.p.runner_shared.clone(); diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index 2591e0a31..11dd3b1e6 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -37,7 +37,7 @@ use windows::{ }; use crate::{ - dpi::{PhysicalPosition, PhysicalSize, PhysicalUnit, Position, Size, Unit}, + dpi::{PhysicalPosition, PhysicalSize, Position, Size}, error::{ExternalError, NotSupportedError, OsError as RootOsError}, icon::Icon, menu::MenuType, @@ -54,7 +54,7 @@ use crate::{ }, window::{ CursorIcon, Fullscreen, Theme, UserAttentionType, WindowAttributes, WindowId as RootWindowId, - BORDERLESS_RESIZE_INSET, + WindowSizeConstraints, BORDERLESS_RESIZE_INSET, }, }; @@ -283,28 +283,12 @@ impl Window { util::set_inner_size_physical(self.window.0, width, height, is_decorated); } - #[inline] - pub fn set_min_inner_width(&self, width: Option) { - self.window_state.lock().min_width = width; - // Make windows re-check the window size bounds. - let size = self.inner_size(); - self.set_inner_size(size.into()); - } - - #[inline] - pub fn set_min_inner_height(&self, height: Option) { - self.window_state.lock().min_height = height; - // Make windows re-check the window size bounds. - let size = self.inner_size(); - self.set_inner_size(size.into()); - } - #[inline] pub fn set_min_inner_size(&self, size: Option) { { let mut window_state = self.window_state.lock(); - window_state.min_width = size.map(|s| s.width()); - window_state.min_height = size.map(|s| s.height()); + window_state.size_constraints.min_width = size.map(|s| s.width()); + window_state.size_constraints.min_height = size.map(|s| s.height()); } // Make windows re-check the window size bounds. let size = self.inner_size(); @@ -312,28 +296,20 @@ impl Window { } #[inline] - pub fn set_max_inner_width(&self, width: Option) { - self.window_state.lock().max_width = width; - // Make windows re-check the window size bounds. - let size = self.inner_size(); - self.set_inner_size(size.into()); - } - - #[inline] - pub fn set_max_inner_height(&self, height: Option) { - self.window_state.lock().max_height = height; + pub fn set_max_inner_size(&self, size: Option) { + { + let mut window_state = self.window_state.lock(); + window_state.size_constraints.max_width = size.map(|s| s.width()); + window_state.size_constraints.max_height = size.map(|s| s.height()); + } // Make windows re-check the window size bounds. let size = self.inner_size(); self.set_inner_size(size.into()); } #[inline] - pub fn set_max_inner_size(&self, size: Option) { - { - let mut window_state = self.window_state.lock(); - window_state.max_width = size.map(|s| s.width()); - window_state.max_height = size.map(|s| s.height()); - } + pub fn set_inner_size_constraints(&self, constraints: WindowSizeConstraints) { + self.window_state.lock().size_constraints = constraints; // Make windows re-check the window size bounds. let size = self.inner_size(); self.set_inner_size(size.into()); @@ -1111,39 +1087,13 @@ unsafe fn init( win.set_fullscreen(attributes.fullscreen); force_window_active(win.window.0); } else { - let scale_factor = win.scale_factor(); - let size = attributes + let desired_size = attributes .inner_size .unwrap_or_else(|| PhysicalSize::new(800, 600).into()); - let max_size = PhysicalSize::new( - attributes - .max_inner_width - .unwrap_or_else(|| PhysicalUnit::new(f64::MAX).into()) - .to_physical::(scale_factor) - .0, - attributes - .max_inner_height - .unwrap_or_else(|| PhysicalUnit::new(f64::MAX).into()) - .to_physical(scale_factor) - .0, - ) - .into(); - let min_size = PhysicalSize::new( - attributes - .min_inner_width - .unwrap_or_else(|| PhysicalUnit::new(0).into()) - .to_physical::(scale_factor) - .0, - attributes - .min_inner_height - .unwrap_or_else(|| PhysicalUnit::new(0).into()) - .to_physical(scale_factor) - .0, - ) - .into(); - - let clamped_size = Size::clamp(size, min_size, max_size, win.scale_factor()); - win.set_inner_size(clamped_size); + let size = attributes + .inner_size_constraints + .clamp(desired_size, win.scale_factor()); + win.set_inner_size(size); if attributes.maximized { // Need to set MAXIMIZED after setting `inner_size` as diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index 8d5e94f5b..579add95e 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -3,11 +3,11 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{ - dpi::{PhysicalPosition, Unit}, + dpi::PhysicalPosition, icon::Icon, keyboard::ModifiersState, platform_impl::platform::{event_loop, minimal_ime::MinimalIme, util}, - window::{CursorIcon, Fullscreen, Theme, WindowAttributes}, + window::{CursorIcon, Fullscreen, Theme, WindowAttributes, WindowSizeConstraints}, }; use parking_lot::MutexGuard; use std::io; @@ -22,10 +22,7 @@ pub struct WindowState { pub mouse: MouseProperties, /// Used by `WM_GETMINMAXINFO`. - pub min_width: Option, - pub min_height: Option, - pub max_width: Option, - pub max_height: Option, + pub size_constraints: WindowSizeConstraints, pub window_icon: Option, pub taskbar_icon: Option, @@ -130,10 +127,7 @@ impl WindowState { last_position: None, }, - min_width: attributes.min_inner_width, - min_height: attributes.min_inner_height, - max_width: attributes.max_inner_width, - max_height: attributes.max_inner_height, + size_constraints: attributes.inner_size_constraints, window_icon: attributes.window_icon.clone(), taskbar_icon, diff --git a/src/window.rs b/src/window.rs index 12fa1da11..4d7a70946 100644 --- a/src/window.rs +++ b/src/window.rs @@ -8,7 +8,7 @@ use std::fmt; use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle}; use crate::{ - dpi::{PhysicalPosition, PhysicalSize, Position, Size, Unit}, + dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Pixel, Position, Size, Unit}, error::{ExternalError, NotSupportedError, OsError}, event_loop::EventLoopWindowTarget, menu::MenuBar, @@ -115,25 +115,8 @@ pub struct WindowAttributes { /// The default is `None`. pub inner_size: Option, - /// The minimum width a window can be, If this is `None`, the window will have no minimum width (aside from reserved). - /// - /// The default is `None`. - pub min_inner_width: Option, - - /// The minimum height a window can be, If this is `None`, the window will have no minimum height (aside from reserved). - /// - /// The default is `None`. - pub min_inner_height: Option, - - /// The maximum width a window can be, If this is `None`, the maximum will have no maximum or will be set to the primary monitor's width by the platform. - /// - /// The default is `None`. - pub max_inner_width: Option, - - /// The maximum height a window can be, If this is `None`, the maximum will have no maximum or will be set to the primary monitor's height by the platform. - /// - /// The default is `None`. - pub max_inner_height: Option, + /// The window size constraints + pub inner_size_constraints: WindowSizeConstraints, /// The desired position of the window. If this is `None`, some platform-specific position /// will be chosen. @@ -267,10 +250,7 @@ impl Default for WindowAttributes { fn default() -> WindowAttributes { WindowAttributes { inner_size: None, - min_inner_width: None, - min_inner_height: None, - max_inner_width: None, - max_inner_height: None, + inner_size_constraints: Default::default(), position: None, resizable: true, minimizable: true, @@ -312,28 +292,6 @@ impl WindowBuilder { self } - /// Sets a minimum width for the window. - /// - /// See [`Window::set_min_inner_width`] for details. - /// - /// [`Window::set_min_inner_width`]: crate::window::Window::set_min_inner_width - #[inline] - pub fn with_min_inner_width>(mut self, width: U) -> Self { - self.window.min_inner_width = Some(width.into()); - self - } - - /// Sets a minimum height for the window. - /// - /// See [`Window::set_min_inner_height`] for details. - /// - /// [`Window::set_min_inner_height`]: crate::window::Window::set_min_inner_height - #[inline] - pub fn with_min_inner_height>(mut self, height: U) -> Self { - self.window.min_inner_height = Some(height.into()); - self - } - /// Sets a minimum dimension size for the window. /// /// See [`Window::set_min_inner_size`] for details. @@ -342,43 +300,32 @@ impl WindowBuilder { #[inline] pub fn with_min_inner_size>(mut self, min_size: S) -> Self { let size = min_size.into(); - self.window.min_inner_width = Some(size.width()); - self.window.min_inner_height = Some(size.height()); - self - } - - /// Sets a maximum width for the window. - /// - /// See [`Window::set_max_inner_width`] for details. - /// - /// [`Window::set_max_inner_width`]: crate::window::Window::set_max_inner_width - #[inline] - pub fn with_max_inner_width>(mut self, width: U) -> Self { - self.window.max_inner_width = Some(width.into()); + self.window.inner_size_constraints.min_width = Some(size.width()); + self.window.inner_size_constraints.min_height = Some(size.height()); self } - /// Sets a maximum height for the window. + /// Sets a maximum dimension size for the window. /// - /// See [`Window::set_max_inner_height`] for details. + /// See [`Window::set_max_inner_size`] for details. /// - /// [`Window::set_max_inner_height`]: crate::window::Window::set_max_inner_height + /// [`Window::set_max_inner_size`]: crate::window::Window::set_max_inner_size #[inline] - pub fn with_max_inner_height>(mut self, height: U) -> Self { - self.window.max_inner_height = Some(height.into()); + pub fn with_max_inner_size>(mut self, max_size: S) -> Self { + let size = max_size.into(); + self.window.inner_size_constraints.max_width = Some(size.width()); + self.window.inner_size_constraints.max_height = Some(size.height()); self } - /// Sets a maximum dimension size for the window. + /// Sets inner size constraints for the window. /// - /// See [`Window::set_max_inner_size`] for details. + /// See [`Window::set_inner_size_constraints`] for details. /// - /// [`Window::set_max_inner_size`]: crate::window::Window::set_max_inner_size + /// [`Window::set_inner_size_constraints`]: crate::window::Window::set_inner_size_constraints #[inline] - pub fn with_max_inner_size>(mut self, max_size: S) -> Self { - let size = max_size.into(); - self.window.max_inner_width = Some(size.width()); - self.window.max_inner_height = Some(size.height()); + pub fn with_inner_size_constraints(mut self, constraints: WindowSizeConstraints) -> Self { + self.window.inner_size_constraints = constraints; self } @@ -762,26 +709,6 @@ impl Window { self.window.outer_size() } - /// Sets a minimum width for the window. - /// - /// ## Platform-specific - /// - /// - **iOS / Android:** Unsupported. - #[inline] - pub fn set_min_inner_width>(&self, width: Option) { - self.window.set_min_inner_width(width.map(|s| s.into())) - } - - /// Sets a minimum height for the window. - /// - /// ## Platform-specific - /// - /// - **iOS / Android:** Unsupported. - #[inline] - pub fn set_min_inner_height>(&self, height: Option) { - self.window.set_min_inner_height(height.map(|s| s.into())) - } - /// Sets a minimum dimension size for the window. /// /// ## Platform-specific @@ -792,34 +719,24 @@ impl Window { self.window.set_min_inner_size(min_size.map(|s| s.into())) } - /// Sets a maximum width for the window. - /// - /// ## Platform-specific - /// - /// - **iOS / Android:** Unsupported. - #[inline] - pub fn set_max_inner_width>(&self, width: Option) { - self.window.set_max_inner_width(width.map(|s| s.into())) - } - - /// Sets a maximum height for the window. + /// Sets a maximum dimension size for the window. /// /// ## Platform-specific /// /// - **iOS / Android:** Unsupported. #[inline] - pub fn set_max_inner_height>(&self, height: Option) { - self.window.set_max_inner_height(height.map(|s| s.into())) + pub fn set_max_inner_size>(&self, max_size: Option) { + self.window.set_max_inner_size(max_size.map(|s| s.into())) } - /// Sets a maximum dimension size for the window. + /// Sets inner size constraints for the window. /// /// ## Platform-specific /// /// - **iOS / Android:** Unsupported. #[inline] - pub fn set_max_inner_size>(&self, max_size: Option) { - self.window.set_max_inner_size(max_size.map(|s| s.into())) + pub fn set_inner_size_constraints(&self, constraints: WindowSizeConstraints) { + self.window.set_inner_size_constraints(constraints) } } @@ -1495,6 +1412,125 @@ impl Default for UserAttentionType { } } +/// Window size constraints +#[derive(Clone, Copy, PartialEq, PartialOrd, Debug, Default)] +pub struct WindowSizeConstraints { + /// The minimum width a window can be, If this is `None`, the window will have no minimum width (aside from reserved). + /// + /// The default is `None`. + pub min_width: Option, + /// The minimum height a window can be, If this is `None`, the window will have no minimum height (aside from reserved). + /// + /// The default is `None`. + pub min_height: Option, + /// The maximum width a window can be, If this is `None`, the window will have no maximum width (aside from reserved). + /// + /// The default is `None`. + pub max_width: Option, + /// The maximum height a window can be, If this is `None`, the window will have no maximum height (aside from reserved). + /// + /// The default is `None`. + pub max_height: Option, +} + +impl WindowSizeConstraints { + pub fn new( + min_width: Option, + min_height: Option, + max_width: Option, + max_height: Option, + ) -> Self { + Self { + min_width, + min_height, + max_width, + max_height, + } + } + + pub fn symmetrical(min: Option, max: Option) -> Self { + Self { + min_width: min.map(|s| s.width()), + min_height: min.map(|s| s.height()), + max_width: max.map(|s| s.width()), + max_height: max.map(|s| s.height()), + } + } + + pub fn has_min(&self) -> bool { + self.min_width.is_some() || self.min_height.is_some() + } + pub fn has_max(&self) -> bool { + self.max_width.is_some() || self.max_height.is_some() + } + + pub fn min_size_physical(&self, scale_factor: f64) -> PhysicalSize { + PhysicalSize::new( + self + .min_width + .unwrap_or_default() + .to_physical(scale_factor) + .value, + self + .min_height + .unwrap_or_default() + .to_physical(scale_factor) + .value, + ) + } + + pub fn min_size_logical(&self, scale_factor: f64) -> LogicalSize { + LogicalSize::new( + self + .min_width + .unwrap_or_default() + .to_logical(scale_factor) + .value, + self + .min_height + .unwrap_or_default() + .to_logical(scale_factor) + .value, + ) + } + + pub fn max_size_physical(&self, scale_factor: f64) -> PhysicalSize { + PhysicalSize::new( + self + .max_width + .unwrap_or(Unit::MAX) + .to_physical(scale_factor) + .value, + self + .max_height + .unwrap_or(Unit::MAX) + .to_physical(scale_factor) + .value, + ) + } + + pub fn max_size_logical(&self, scale_factor: f64) -> LogicalSize { + LogicalSize::new( + self + .max_width + .unwrap_or(Unit::MAX) + .to_logical(scale_factor) + .value, + self + .max_height + .unwrap_or(Unit::MAX) + .to_logical(scale_factor) + .value, + ) + } + + pub fn clamp(&self, desired_size: Size, scale_factor: f64) -> Size { + let min_size: PhysicalSize = self.min_size_physical(scale_factor); + let max_size: PhysicalSize = self.max_size_physical(scale_factor); + Size::clamp(desired_size, min_size.into(), max_size.into(), scale_factor) + } +} + /// A constant used to determine how much inside the window, the resize handler should appear (only used in Linux(gtk) and Windows). /// You probably need to scale it by the scale_factor of the window. pub const BORDERLESS_RESIZE_INSET: i32 = 5; From 574af0e5d6c0e3dbb48e96a1b6858caa936ffb51 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Thu, 6 Jul 2023 03:09:30 +0300 Subject: [PATCH 12/16] fix macos build --- src/platform_impl/macos/window.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index ada950c84..1e35452ed 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -163,12 +163,14 @@ fn create_window( None => { let screen = NSScreen::mainScreen(nil); let scale_factor = NSScreen::backingScaleFactor(screen) as f64; - let desired_size = attributes + let (width, height) = attrs .inner_size - .unwrap_or_else(|| PhysicalSize::new(800, 600).into()); - let size = attributes + .unwrap_or_else(|| PhysicalSize::new(800, 600).into()) + .into(); + let (width, height): (f64, f64) = attributes .inner_size_constraints - .clamp(desired_size, scale_factor); + .clamp(desired_size, scale_factor) + .to_logical::(scale_factor); let (left, bottom) = match attrs.position { Some(position) => { let logical = util::window_position(position.to_logical(scale_factor)); @@ -179,10 +181,7 @@ fn create_window( // This value is ignored by calling win.center() below None => (0.0, 0.0), }; - NSRect::new( - NSPoint::new(left, bottom), - NSSize::new(size.width, size.height), - ) + NSRect::new(NSPoint::new(left, bottom), NSSize::new(width, height)) } }; From b2b39d307b436920ce25e607a8b796a41ae349de Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Thu, 6 Jul 2023 03:51:59 +0300 Subject: [PATCH 13/16] macos again --- src/platform_impl/macos/window.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 1e35452ed..8c000c16d 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -163,14 +163,14 @@ fn create_window( None => { let screen = NSScreen::mainScreen(nil); let scale_factor = NSScreen::backingScaleFactor(screen) as f64; - let (width, height) = attrs + let desired_size = attrs .inner_size - .unwrap_or_else(|| PhysicalSize::new(800, 600).into()) - .into(); - let (width, height): (f64, f64) = attributes + .unwrap_or_else(|| PhysicalSize::new(800, 600).into()); + let (width, height): (f64, f64) = attrs .inner_size_constraints .clamp(desired_size, scale_factor) - .to_logical::(scale_factor); + .to_logical::(scale_factor) + .into(); let (left, bottom) = match attrs.position { Some(position) => { let logical = util::window_position(position.to_logical(scale_factor)); From c6c494e52e45c28ec04d8fbb953a26ac8555d7af Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Fri, 7 Jul 2023 04:01:41 +0300 Subject: [PATCH 14/16] use macros to generate DPI types ref: https://github.com/rust-windowing/winit/pull/2148 --- examples/min_max_size.rs | 14 +- src/dpi.rs | 707 ++++++++---------------- src/platform_impl/windows/event_loop.rs | 10 +- src/window.rs | 50 +- 4 files changed, 258 insertions(+), 523 deletions(-) diff --git a/examples/min_max_size.rs b/examples/min_max_size.rs index 85894e5f8..527519727 100644 --- a/examples/min_max_size.rs +++ b/examples/min_max_size.rs @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 use tao::{ - dpi::LogicalUnit, + dpi::LogicalPixel, event::{ElementState, Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, keyboard::Key, @@ -53,22 +53,22 @@ fn main() { } => match key_str { "e" => { size_constraints.min_width = - (!size_constraints.min_width.is_some()).then_some(LogicalUnit::new(min_width).into()); + (!size_constraints.min_width.is_some()).then_some(LogicalPixel::new(min_width).into()); window.set_inner_size_constraints(size_constraints); } "f" => { size_constraints.max_width = - (!size_constraints.max_width.is_some()).then_some(LogicalUnit::new(max_width).into()); + (!size_constraints.max_width.is_some()).then_some(LogicalPixel::new(max_width).into()); window.set_inner_size_constraints(size_constraints); } "p" => { - size_constraints.min_height = - (!size_constraints.min_height.is_some()).then_some(LogicalUnit::new(min_height).into()); + size_constraints.min_height = (!size_constraints.min_height.is_some()) + .then_some(LogicalPixel::new(min_height).into()); window.set_inner_size_constraints(size_constraints); } "v" => { - size_constraints.max_height = - (!size_constraints.max_height.is_some()).then_some(LogicalUnit::new(max_height).into()); + size_constraints.max_height = (!size_constraints.max_height.is_some()) + .then_some(LogicalPixel::new(max_height).into()); window.set_inner_size_constraints(size_constraints); } _ => {} diff --git a/src/dpi.rs b/src/dpi.rs index dd2bb91a8..3b4fb4ae1 100644 --- a/src/dpi.rs +++ b/src/dpi.rs @@ -93,55 +93,70 @@ pub trait Pixel: Copy + Into { fn from_f64(f: f64) -> Self; - fn inner(&self) -> Self { - *self - } fn cast(self) -> P { P::from_f64(self.into()) } } -impl Pixel for u8 { - fn from_f64(f: f64) -> Self { - f.round() as u8 - } -} -impl Pixel for u16 { - fn from_f64(f: f64) -> Self { - f.round() as u16 - } -} -impl Pixel for u32 { - fn from_f64(f: f64) -> Self { - f.round() as u32 - } -} -impl Pixel for i8 { - fn from_f64(f: f64) -> Self { - f.round() as i8 - } -} -impl Pixel for i16 { - fn from_f64(f: f64) -> Self { - f.round() as i16 - } -} -impl Pixel for i32 { - fn from_f64(f: f64) -> Self { - f.round() as i32 - } +macro_rules! pixel_int_impl { + ($($t:ty),*) => {$( + impl Pixel for $t { + fn from_f64(f: f64) -> Self { + f.round() as $t + } + } + )*} } + +pixel_int_impl!(u8, u16, u32, i8, i16, i32); + impl Pixel for f32 { fn from_f64(f: f64) -> Self { f as f32 } } + impl Pixel for f64 { fn from_f64(f: f64) -> Self { f } } +macro_rules! from_impls { + ($t:ident, $a:ident, $(,)? ) => { + impl From

for $t

{ + fn from($a: P) -> Self { + Self::new($a.cast()) + } + } + }; + ($t:ident, $a:ident, $b:ident$(,)? ) => { + impl From<(X, X)> for $t

{ + fn from(($a, $b): (X, X)) -> Self { + Self::new($a.cast(), $b.cast()) + } + } + + impl From<$t

> for (X, X) { + fn from(p: $t

) -> Self { + (p.$a.cast(), p.$b.cast()) + } + } + + impl From<[X; 2]> for $t

{ + fn from([$a, $b]: [X; 2]) -> Self { + Self::new($a.cast(), $b.cast()) + } + } + + impl From<$t

> for [X; 2] { + fn from(p: $t

) -> Self { + [p.$a.cast(), p.$b.cast()] + } + } + }; +} + /// Checks that the scale factor is a normal positive `f64`. /// /// All functions that take a scale factor assert that this will return `true`. If you're sourcing scale factors from @@ -152,349 +167,213 @@ pub fn validate_scale_factor(scale_factor: f64) -> bool { scale_factor.is_sign_positive() && scale_factor.is_normal() } -/// A position represented in logical pixels. -/// -/// The position is stored as floats, so please be careful. Casting floats to integers truncates the -/// fractional part, which can cause noticeable issues. To help with that, an `Into<(i32, i32)>` -/// implementation is provided which does the rounding for you. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct LogicalPosition

{ - pub x: P, - pub y: P, -} - -impl

LogicalPosition

{ - #[inline] - pub const fn new(x: P, y: P) -> Self { - LogicalPosition { x, y } - } -} - -impl LogicalPosition

{ - #[inline] - pub fn from_physical>, X: Pixel>( - physical: T, - scale_factor: f64, - ) -> Self { - physical.into().to_logical(scale_factor) - } - - #[inline] - pub fn to_physical(&self, scale_factor: f64) -> PhysicalPosition { - assert!(validate_scale_factor(scale_factor)); - let x = self.x.into() * scale_factor; - let y = self.y.into() * scale_factor; - PhysicalPosition::new(x, y).cast() - } - - #[inline] - pub fn cast(&self) -> LogicalPosition { - LogicalPosition { - x: self.x.cast(), - y: self.y.cast(), - } - } -} - -impl From<(X, X)> for LogicalPosition

{ - fn from((x, y): (X, X)) -> LogicalPosition

{ - LogicalPosition::new(x.cast(), y.cast()) - } -} - -impl Into<(X, X)> for LogicalPosition

{ - fn into(self) -> (X, X) { - (self.x.cast(), self.y.cast()) - } -} - -impl From<[X; 2]> for LogicalPosition

{ - fn from([x, y]: [X; 2]) -> LogicalPosition

{ - LogicalPosition::new(x.cast(), y.cast()) - } -} - -impl Into<[X; 2]> for LogicalPosition

{ - fn into(self) -> [X; 2] { - [self.x.cast(), self.y.cast()] - } -} - -/// A position represented in physical pixels. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct PhysicalPosition

{ - pub x: P, - pub y: P, -} - -impl

PhysicalPosition

{ - #[inline] - pub const fn new(x: P, y: P) -> Self { - PhysicalPosition { x, y } - } -} - -impl PhysicalPosition

{ - #[inline] - pub fn from_logical>, X: Pixel>( - logical: T, - scale_factor: f64, - ) -> Self { - logical.into().to_physical(scale_factor) - } - - #[inline] - pub fn to_logical(&self, scale_factor: f64) -> LogicalPosition { - assert!(validate_scale_factor(scale_factor)); - let x = self.x.into() / scale_factor; - let y = self.y.into() / scale_factor; - LogicalPosition::new(x, y).cast() - } - - #[inline] - pub fn cast(&self) -> PhysicalPosition { - PhysicalPosition { - x: self.x.cast(), - y: self.y.cast(), - } - } -} - -impl From<(X, X)> for PhysicalPosition

{ - fn from((x, y): (X, X)) -> PhysicalPosition

{ - PhysicalPosition::new(x.cast(), y.cast()) - } -} - -impl Into<(X, X)> for PhysicalPosition

{ - fn into(self) -> (X, X) { - (self.x.cast(), self.y.cast()) - } -} +macro_rules! dpi_type { + ( + $(let $a:ident;)* -impl From<[X; 2]> for PhysicalPosition

{ - fn from([x, y]: [X; 2]) -> PhysicalPosition

{ - PhysicalPosition::new(x.cast(), y.cast()) - } -} - -impl Into<[X; 2]> for PhysicalPosition

{ - fn into(self) -> [X; 2] { - [self.x.cast(), self.y.cast()] - } -} - -/// A position that's either physical or logical. -#[non_exhaustive] -#[derive(Debug, Copy, Clone, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum Position { - Physical(PhysicalPosition), - Logical(LogicalPosition), -} - -impl Position { - pub fn new>(position: S) -> Position { - position.into() - } + $(#[$logical_meta:meta])* + pub struct $LogicalType:ident; + $(#[$physical_meta:meta])* + pub struct $PhysicalType:ident; + $(#[$unified_meta:meta])* + pub enum $UnifiedType:ident { + Physical($unified_physical:ty), + Logical($unified_logical:ty), + } + ) => { + $(#[$logical_meta])* + #[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, PartialOrd, Ord)] + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct $LogicalType

{ + $(pub $a: P,)* + } - pub fn to_logical(&self, scale_factor: f64) -> LogicalPosition

{ - match *self { - Position::Physical(position) => position.to_logical(scale_factor), - Position::Logical(position) => position.cast(), - } - } + impl

$LogicalType

{ + #[inline] + pub const fn new($($a: P,)*) -> Self { + $LogicalType { $($a,)* } + } + } - pub fn to_physical(&self, scale_factor: f64) -> PhysicalPosition

{ - match *self { - Position::Physical(position) => position.cast(), - Position::Logical(position) => position.to_physical(scale_factor), - } - } -} + impl $LogicalType

{ + #[inline] + pub fn from_physical>, X: Pixel>( + physical: T, + scale_factor: f64, + ) -> Self { + physical.into().to_logical(scale_factor) + } + + #[inline] + pub fn to_physical(&self, scale_factor: f64) -> $PhysicalType { + assert!(validate_scale_factor(scale_factor)); + $(let $a = self.$a.into() * scale_factor;)* + $PhysicalType::new($($a,)*).cast() + } + + #[inline] + pub fn cast(&self) -> $LogicalType { + $LogicalType { + $($a: self.$a.cast(),)* + } + } + } -impl From> for Position { - #[inline] - fn from(position: PhysicalPosition

) -> Position { - Position::Physical(position.cast()) - } -} + from_impls!($LogicalType, $($a,)*); -impl From> for Position { - #[inline] - fn from(position: LogicalPosition

) -> Position { - Position::Logical(position.cast()) - } -} + $(#[$physical_meta])* + #[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, PartialOrd, Ord)] + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct $PhysicalType

{ + $(pub $a: P,)* + } -/// A size represented in logical pixels. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct LogicalSize

{ - pub width: P, - pub height: P, -} + impl

$PhysicalType

{ + #[inline] + pub const fn new($($a: P,)*) -> Self { + $PhysicalType { $($a,)* } + } + } -impl

LogicalSize

{ - #[inline] - pub const fn new(width: P, height: P) -> Self { - LogicalSize { width, height } - } -} + impl $PhysicalType

{ + #[inline] + pub fn from_logical>, X: Pixel>( + logical: T, + scale_factor: f64, + ) -> Self { + logical.into().to_physical(scale_factor) + } + + #[inline] + pub fn to_logical(&self, scale_factor: f64) -> $LogicalType { + assert!(validate_scale_factor(scale_factor)); + $(let $a = self.$a.into() / scale_factor;)* + $LogicalType::new($($a,)*).cast() + } + + #[inline] + pub fn cast(&self) -> $PhysicalType { + $PhysicalType { + $($a: self.$a.cast(),)* + } + } + } -impl LogicalSize

{ - #[inline] - pub fn from_physical>, X: Pixel>(physical: T, scale_factor: f64) -> Self { - physical.into().to_logical(scale_factor) - } + from_impls!($PhysicalType, $($a,)*); - #[inline] - pub fn to_physical(&self, scale_factor: f64) -> PhysicalSize { - assert!(validate_scale_factor(scale_factor)); - let width = self.width.into() * scale_factor; - let height = self.height.into() * scale_factor; - PhysicalSize::new(width, height).cast() - } + $(#[$unified_meta])* + #[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub enum $UnifiedType { + Physical($unified_physical), + Logical($unified_logical), + } - #[inline] - pub fn cast(&self) -> LogicalSize { - LogicalSize { - width: self.width.cast(), - height: self.height.cast(), - } - } -} + impl $UnifiedType { + pub fn new>(val: S) -> $UnifiedType { + val.into() + } + + pub fn to_logical(&self, scale_factor: f64) -> $LogicalType

{ + match *self { + $UnifiedType::Physical(val) => val.to_logical(scale_factor), + $UnifiedType::Logical(val) => val.cast(), + } + } + + pub fn to_physical(&self, scale_factor: f64) -> $PhysicalType

{ + match *self { + $UnifiedType::Physical(val) => val.cast(), + $UnifiedType::Logical(val) => val.to_physical(scale_factor), + } + } + + $(pub fn $a(&self) -> PixelUnit { + match *self { + $UnifiedType::Physical(any) => PixelUnit::Physical(any.$a.into()), + $UnifiedType::Logical(any) => PixelUnit::Logical(any.$a.into()), + } + })* + } -impl From<(X, X)> for LogicalSize

{ - fn from((x, y): (X, X)) -> LogicalSize

{ - LogicalSize::new(x.cast(), y.cast()) - } -} + impl From<$PhysicalType

> for $UnifiedType { + #[inline] + fn from(val: $PhysicalType

) -> $UnifiedType { + $UnifiedType::Physical(val.cast()) + } + } -impl Into<(X, X)> for LogicalSize

{ - fn into(self: LogicalSize

) -> (X, X) { - (self.width.cast(), self.height.cast()) - } + impl From<$LogicalType

> for $UnifiedType { + #[inline] + fn from(val: $LogicalType

) -> $UnifiedType { + $UnifiedType::Logical(val.cast()) + } + } + }; } -impl From<[X; 2]> for LogicalSize

{ - fn from([x, y]: [X; 2]) -> LogicalSize

{ - LogicalSize::new(x.cast(), y.cast()) - } -} +dpi_type! { + let value; -impl Into<[X; 2]> for LogicalSize

{ - fn into(self) -> [X; 2] { - [self.width.cast(), self.height.cast()] + /// A logical pixel. + pub struct LogicalPixel; + /// A physical pixel. + pub struct PhysicalPixel; + /// A pixel that's either physical or logical. + pub enum PixelUnit { + Physical(PhysicalPixel), + Logical(LogicalPixel), } } -/// A size represented in physical pixels. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct PhysicalSize

{ - pub width: P, - pub height: P, +impl PixelUnit { + /// Represents a minimum logical unit of `0` + pub const MIN: PixelUnit = PixelUnit::Logical(LogicalPixel::new(0.0)); + /// Represents a maximum logical unit that is equal to [`f64::MAX`] + pub const MAX: PixelUnit = PixelUnit::Logical(LogicalPixel::new(f64::MAX)); } -impl

PhysicalSize

{ - #[inline] - pub const fn new(width: P, height: P) -> Self { - PhysicalSize { width, height } - } -} - -impl PhysicalSize

{ - #[inline] - pub fn from_logical>, X: Pixel>(logical: T, scale_factor: f64) -> Self { - logical.into().to_physical(scale_factor) - } - - #[inline] - pub fn to_logical(&self, scale_factor: f64) -> LogicalSize { - assert!(validate_scale_factor(scale_factor)); - let width = self.width.into() / scale_factor; - let height = self.height.into() / scale_factor; - LogicalSize::new(width, height).cast() - } - - #[inline] - pub fn cast(&self) -> PhysicalSize { - PhysicalSize { - width: self.width.cast(), - height: self.height.cast(), - } +impl From for PhysicalPixel { + fn from(value: u32) -> Self { + Self::new(value.cast()) } } -impl From<(X, X)> for PhysicalSize

{ - fn from((x, y): (X, X)) -> PhysicalSize

{ - PhysicalSize::new(x.cast(), y.cast()) - } -} +dpi_type! { + let x; + let y; -impl Into<(X, X)> for PhysicalSize

{ - fn into(self) -> (X, X) { - (self.width.cast(), self.height.cast()) + /// A position represented in logical pixels. + /// + /// The position is stored as floats, so please be careful. Casting floats to integers truncates the + /// fractional part, which can cause noticable issues. To help with that, an `Into<(i32, i32)>` + /// implementation is provided which does the rounding for you. + pub struct LogicalPosition; + /// A position represented in physical pixels. + pub struct PhysicalPosition; + /// A position that's either physical or logical. + pub enum Position { + Physical(PhysicalPosition), + Logical(LogicalPosition), } } -impl From<[X; 2]> for PhysicalSize

{ - fn from([x, y]: [X; 2]) -> PhysicalSize

{ - PhysicalSize::new(x.cast(), y.cast()) - } -} +dpi_type! { + let width; + let height; -impl Into<[X; 2]> for PhysicalSize

{ - fn into(self) -> [X; 2] { - [self.width.cast(), self.height.cast()] + /// A size represented in logical pixels. + pub struct LogicalSize; + /// A size represented in physical pixels. + pub struct PhysicalSize; + /// A size that's either physical or logical. + pub enum Size { + Physical(PhysicalSize), + Logical(LogicalSize), } } -/// A size that's either physical or logical. -#[non_exhaustive] -#[derive(Debug, Copy, Clone, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum Size { - Physical(PhysicalSize), - Logical(LogicalSize), -} - impl Size { - pub fn new>(size: S) -> Size { - size.into() - } - - pub fn to_logical(&self, scale_factor: f64) -> LogicalSize

{ - match *self { - Size::Physical(size) => size.to_logical(scale_factor), - Size::Logical(size) => size.cast(), - } - } - - pub fn to_physical(&self, scale_factor: f64) -> PhysicalSize

{ - match *self { - Size::Physical(size) => size.cast(), - Size::Logical(size) => size.to_physical(scale_factor), - } - } - - pub fn width(&self) -> Unit { - match *self { - Size::Physical(size) => Unit::Physical(size.width.into()), - Size::Logical(size) => Unit::Logical(size.width.into()), - } - } - - pub fn height(&self) -> Unit { - match *self { - Size::Physical(size) => Unit::Physical(size.height.into()), - Size::Logical(size) => Unit::Logical(size.height.into()), - } - } - pub fn clamp>(desired_size: S, min: S, max: S, scale_factor: f64) -> Size { let (desired_size, min, max) = ( desired_size.into().to_physical::(scale_factor), @@ -518,145 +397,3 @@ impl Size { PhysicalSize::new(width, height).into() } } - -impl From> for Size { - #[inline] - fn from(size: PhysicalSize

) -> Size { - Size::Physical(size.cast()) - } -} - -impl From> for Size { - #[inline] - fn from(size: LogicalSize

) -> Size { - Size::Logical(size.cast()) - } -} - -/// A unit represented in logical pixels. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, PartialOrd, Ord)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct LogicalUnit

{ - pub value: P, -} - -impl

LogicalUnit

{ - #[inline] - pub const fn new(value: P) -> Self { - Self { value } - } -} - -impl LogicalUnit

{ - #[inline] - pub fn from_physical>, X: Pixel>(physical: T, scale_factor: f64) -> Self { - physical.into().to_logical(scale_factor) - } - - #[inline] - pub fn to_physical(&self, scale_factor: f64) -> PhysicalUnit { - assert!(validate_scale_factor(scale_factor)); - let u = self.value.into() * scale_factor; - PhysicalUnit::new(u).cast() - } - - #[inline] - pub fn cast(&self) -> LogicalUnit { - LogicalUnit::new(self.value.cast()) - } -} - -impl From

for LogicalUnit

{ - fn from(p: P) -> LogicalUnit

{ - LogicalUnit::new(p.cast()) - } -} - -/// A unit represented in physical pixels. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, PartialOrd, Ord)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct PhysicalUnit

{ - pub value: P, -} - -impl

PhysicalUnit

{ - #[inline] - pub const fn new(value: P) -> Self { - Self { value } - } -} - -impl PhysicalUnit

{ - #[inline] - pub fn from_logical>, X: Pixel>(logical: T, scale_factor: f64) -> Self { - logical.into().to_physical(scale_factor) - } - - #[inline] - pub fn to_logical(&self, scale_factor: f64) -> LogicalUnit { - assert!(validate_scale_factor(scale_factor)); - let u = self.value.into() / scale_factor; - LogicalUnit::new(u).cast() - } - - #[inline] - pub fn cast(&self) -> PhysicalUnit { - PhysicalUnit::new(self.value.cast()) - } -} - -impl From

for PhysicalUnit

{ - fn from(p: P) -> PhysicalUnit

{ - PhysicalUnit::new(p.cast()) - } -} - -#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum Unit { - Physical(PhysicalUnit), - Logical(LogicalUnit), -} - -impl Default for Unit { - fn default() -> Self { - Self::Logical(LogicalUnit::new(f64::default())) - } -} - -impl Unit { - pub const MIN: Unit = Unit::Logical(LogicalUnit::new(0.0)); - pub const MAX: Unit = Unit::Logical(LogicalUnit::new(f64::MAX)); - - pub fn new>(val: S) -> Unit { - val.into() - } - - pub fn to_logical(&self, scale_factor: f64) -> LogicalUnit

{ - match *self { - Unit::Physical(val) => val.to_logical(scale_factor), - Unit::Logical(val) => val.cast(), - } - } - - pub fn to_physical(&self, scale_factor: f64) -> PhysicalUnit

{ - match *self { - Unit::Physical(val) => val.cast(), - Unit::Logical(val) => val.to_physical(scale_factor), - } - } -} - -impl From> for Unit { - #[inline] - fn from(val: PhysicalUnit

) -> Unit { - Unit::Physical(val.cast()) - } -} - -impl From> for Unit { - #[inline] - fn from(val: LogicalUnit

) -> Unit { - Unit::Logical(val.cast()) - } -} diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index c2e623b4c..126c33afb 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -44,7 +44,7 @@ use windows::{ use crate::{ accelerator::AcceleratorId, - dpi::{PhysicalPosition, PhysicalSize, Unit}, + dpi::{PhysicalPosition, PhysicalSize, PixelUnit}, error::ExternalError, event::{DeviceEvent, Event, Force, RawKeyEvent, Touch, TouchPhase, WindowEvent}, event_loop::{ControlFlow, DeviceEventFilter, EventLoopClosed, EventLoopWindowTarget as RootELW}, @@ -1789,17 +1789,17 @@ unsafe fn public_window_callback_inner( } if size_constraints.has_max() { // we can't use WindowSizeConstraints::max_size_physical because - // for Windows, in order to remove the max constraints, we need to fall to Unit::MIN (which is `0`) - // instead of Unit::MAX (which is f64::MAX) + // for Windows, in order to remove the max constraints, we need to fall to PixelUnit::MIN (which is `0`) + // instead of PixelUnit::MAX (which is f64::MAX) let max_size = PhysicalSize::new( size_constraints .max_width - .unwrap_or(Unit::MIN) + .unwrap_or(PixelUnit::MIN) .to_physical(window_state.scale_factor) .value, size_constraints .max_height - .unwrap_or(Unit::MIN) + .unwrap_or(PixelUnit::MIN) .to_physical(window_state.scale_factor) .value, ); diff --git a/src/window.rs b/src/window.rs index 4d7a70946..b59782797 100644 --- a/src/window.rs +++ b/src/window.rs @@ -8,7 +8,7 @@ use std::fmt; use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle}; use crate::{ - dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Pixel, Position, Size, Unit}, + dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Pixel, PixelUnit, Position, Size}, error::{ExternalError, NotSupportedError, OsError}, event_loop::EventLoopWindowTarget, menu::MenuBar, @@ -1418,27 +1418,27 @@ pub struct WindowSizeConstraints { /// The minimum width a window can be, If this is `None`, the window will have no minimum width (aside from reserved). /// /// The default is `None`. - pub min_width: Option, + pub min_width: Option, /// The minimum height a window can be, If this is `None`, the window will have no minimum height (aside from reserved). /// /// The default is `None`. - pub min_height: Option, + pub min_height: Option, /// The maximum width a window can be, If this is `None`, the window will have no maximum width (aside from reserved). /// /// The default is `None`. - pub max_width: Option, + pub max_width: Option, /// The maximum height a window can be, If this is `None`, the window will have no maximum height (aside from reserved). /// /// The default is `None`. - pub max_height: Option, + pub max_height: Option, } impl WindowSizeConstraints { pub fn new( - min_width: Option, - min_height: Option, - max_width: Option, - max_height: Option, + min_width: Option, + min_height: Option, + max_width: Option, + max_height: Option, ) -> Self { Self { min_width, @@ -1448,82 +1448,80 @@ impl WindowSizeConstraints { } } - pub fn symmetrical(min: Option, max: Option) -> Self { - Self { - min_width: min.map(|s| s.width()), - min_height: min.map(|s| s.height()), - max_width: max.map(|s| s.width()), - max_height: max.map(|s| s.height()), - } - } - + /// Returns true if `min_width` or `min_height` is set. pub fn has_min(&self) -> bool { self.min_width.is_some() || self.min_height.is_some() } + /// Returns true if `max_width` or `max_height` is set. pub fn has_max(&self) -> bool { self.max_width.is_some() || self.max_height.is_some() } + /// Returns a physical size that represents the minimum constraints set and fallbacks to [`PixelUnit::MIN`] for unset values pub fn min_size_physical(&self, scale_factor: f64) -> PhysicalSize { PhysicalSize::new( self .min_width - .unwrap_or_default() + .unwrap_or(PixelUnit::MIN) .to_physical(scale_factor) .value, self .min_height - .unwrap_or_default() + .unwrap_or(PixelUnit::MIN) .to_physical(scale_factor) .value, ) } + /// Returns a logical size that represents the minimum constraints set and fallbacks to [`PixelUnit::MIN`] for unset values pub fn min_size_logical(&self, scale_factor: f64) -> LogicalSize { LogicalSize::new( self .min_width - .unwrap_or_default() + .unwrap_or(PixelUnit::MIN) .to_logical(scale_factor) .value, self .min_height - .unwrap_or_default() + .unwrap_or(PixelUnit::MIN) .to_logical(scale_factor) .value, ) } + /// Returns a physical size that represents the maximum constraints set and fallbacks to [`PixelUnit::MAX`] for unset values pub fn max_size_physical(&self, scale_factor: f64) -> PhysicalSize { PhysicalSize::new( self .max_width - .unwrap_or(Unit::MAX) + .unwrap_or(PixelUnit::MAX) .to_physical(scale_factor) .value, self .max_height - .unwrap_or(Unit::MAX) + .unwrap_or(PixelUnit::MAX) .to_physical(scale_factor) .value, ) } + /// Returns a logical size that represents the maximum constraints set and fallbacks to [`PixelUnit::MAX`] for unset values pub fn max_size_logical(&self, scale_factor: f64) -> LogicalSize { LogicalSize::new( self .max_width - .unwrap_or(Unit::MAX) + .unwrap_or(PixelUnit::MAX) .to_logical(scale_factor) .value, self .max_height - .unwrap_or(Unit::MAX) + .unwrap_or(PixelUnit::MAX) .to_logical(scale_factor) .value, ) } + /// Clamps the desired size based on the constraints set pub fn clamp(&self, desired_size: Size, scale_factor: f64) -> Size { let min_size: PhysicalSize = self.min_size_physical(scale_factor); let max_size: PhysicalSize = self.max_size_physical(scale_factor); From f4f5af0bf37faeead45e7baccbc77576dce4d19a Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Fri, 7 Jul 2023 04:04:39 +0300 Subject: [PATCH 15/16] fix windows impl --- src/platform_impl/windows/event_loop.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 126c33afb..596613c92 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -1780,7 +1780,18 @@ unsafe fn public_window_callback_inner( let size_constraints = window_state.size_constraints; if size_constraints.has_min() { - let min_size = size_constraints.min_size_physical(window_state.scale_factor); + let min_size = PhysicalSize::new( + size_constraints + .min_width + .unwrap_or_else(|| PixelUnit::Physical(GetSystemMetrics(SM_CXMINTRACK).into())) + .to_physical(window_state.scale_factor) + .value, + size_constraints + .min_height + .unwrap_or_else(|| PixelUnit::Physical(GetSystemMetrics(SM_CYMINTRACK).into())) + .to_physical(window_state.scale_factor) + .value, + ); let (width, height): (u32, u32) = util::adjust_size(window, min_size, is_decorated).into(); (*mmi).ptMinTrackSize = POINT { x: width as i32, @@ -1788,18 +1799,15 @@ unsafe fn public_window_callback_inner( }; } if size_constraints.has_max() { - // we can't use WindowSizeConstraints::max_size_physical because - // for Windows, in order to remove the max constraints, we need to fall to PixelUnit::MIN (which is `0`) - // instead of PixelUnit::MAX (which is f64::MAX) let max_size = PhysicalSize::new( size_constraints .max_width - .unwrap_or(PixelUnit::MIN) + .unwrap_or_else(|| PixelUnit::Physical(GetSystemMetrics(SM_CXMAXTRACK).into())) .to_physical(window_state.scale_factor) .value, size_constraints .max_height - .unwrap_or(PixelUnit::MIN) + .unwrap_or_else(|| PixelUnit::Physical(GetSystemMetrics(SM_CYMAXTRACK).into())) .to_physical(window_state.scale_factor) .value, ); From a848e591b4ce96294af1580792f2c7fc23d062a4 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Sat, 8 Jul 2023 18:33:50 +0300 Subject: [PATCH 16/16] fmt --- src/platform_impl/windows/window.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index 68493f9e0..87b3e6b0c 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -54,8 +54,7 @@ use crate::{ }, window::{ CursorIcon, Fullscreen, ProgressBarState, ProgressState, Theme, UserAttentionType, - WindowAttributes, WindowId as RootWindowId, WindowSizeConstraints, - BORDERLESS_RESIZE_INSET, + WindowAttributes, WindowId as RootWindowId, WindowSizeConstraints, BORDERLESS_RESIZE_INSET, }, };