From 5fc5bb80e0175ea8da4ddbfed7e184ff604fa2ae Mon Sep 17 00:00:00 2001 From: rhzk Date: Mon, 15 Jun 2020 19:49:04 +0200 Subject: [PATCH 01/18] Windows: Added Screen module and monitor info, setting size and window position. --- druid-shell/src/lib.rs | 2 + druid-shell/src/platform/gtk/mod.rs | 1 + druid-shell/src/platform/gtk/screen.rs | 28 +++++++ druid-shell/src/platform/gtk/window.rs | 22 +++++ druid-shell/src/platform/mac/mod.rs | 1 + druid-shell/src/platform/mac/screen.rs | 28 +++++++ druid-shell/src/platform/mac/window.rs | 22 +++++ druid-shell/src/platform/web/mod.rs | 1 + druid-shell/src/platform/web/screen.rs | 28 +++++++ druid-shell/src/platform/web/window.rs | 22 +++++ druid-shell/src/platform/windows/mod.rs | 1 + druid-shell/src/platform/windows/screen.rs | 77 +++++++++++++++++ druid-shell/src/platform/windows/window.rs | 97 +++++++++++++++++++++- druid-shell/src/platform/x11/mod.rs | 1 + druid-shell/src/platform/x11/screen.rs | 28 +++++++ druid-shell/src/platform/x11/window.rs | 22 +++++ druid-shell/src/screen.rs | 64 ++++++++++++++ druid-shell/src/window.rs | 25 ++++++ druid/src/app.rs | 14 +++- druid/src/lib.rs | 2 +- 20 files changed, 481 insertions(+), 5 deletions(-) create mode 100644 druid-shell/src/platform/gtk/screen.rs create mode 100644 druid-shell/src/platform/mac/screen.rs create mode 100644 druid-shell/src/platform/web/screen.rs create mode 100644 druid-shell/src/platform/windows/screen.rs create mode 100644 druid-shell/src/platform/x11/screen.rs create mode 100644 druid-shell/src/screen.rs diff --git a/druid-shell/src/lib.rs b/druid-shell/src/lib.rs index 9821911486..179469a180 100644 --- a/druid-shell/src/lib.rs +++ b/druid-shell/src/lib.rs @@ -47,6 +47,7 @@ mod menu; mod mouse; mod platform; mod scale; +mod screen; mod window; pub use application::{AppHandler, Application}; @@ -59,6 +60,7 @@ pub use keyboard::{Code, IntoKey, KbKey, KeyEvent, KeyState, Location, Modifiers pub use menu::Menu; pub use mouse::{Cursor, MouseButton, MouseButtons, MouseEvent}; pub use scale::{Scalable, Scale, ScaledArea}; +pub use screen::Screen; pub use window::{ IdleHandle, IdleToken, Text, TimerToken, WinHandler, WindowBuilder, WindowHandle, }; diff --git a/druid-shell/src/platform/gtk/mod.rs b/druid-shell/src/platform/gtk/mod.rs index 60e5a46e73..1b66bb8bf6 100644 --- a/druid-shell/src/platform/gtk/mod.rs +++ b/druid-shell/src/platform/gtk/mod.rs @@ -22,3 +22,4 @@ pub mod keycodes; pub mod menu; pub mod util; pub mod window; +pub mod screen; diff --git a/druid-shell/src/platform/gtk/screen.rs b/druid-shell/src/platform/gtk/screen.rs new file mode 100644 index 0000000000..d4ff342a1a --- /dev/null +++ b/druid-shell/src/platform/gtk/screen.rs @@ -0,0 +1,28 @@ +// Copyright 2020 The druid Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! GTK Monitors and Screen information. + +use crate::screen::Monitor; +use crate::kurbo::Size; + +pub fn get_display_size() -> Size { + log::warn!("Screen::get_display_size() is currently unimplemented for gtk."); + Size::new(0.0, 0.0) +} + +pub fn get_monitors() -> Vec { + log::warn!("Screen::get_monitors() is currently unimplemented for gtk."); + Vec::::new() +} diff --git a/druid-shell/src/platform/gtk/window.rs b/druid-shell/src/platform/gtk/window.rs index 5a7b9c8aa6..2d10b3b573 100644 --- a/druid-shell/src/platform/gtk/window.rs +++ b/druid-shell/src/platform/gtk/window.rs @@ -156,6 +156,10 @@ impl WindowBuilder { self.show_titlebar = show_titlebar; } + pub fn set_position(&mut self, _position: Point) { + log::warn!("WindowBuilder::set_position is currently unimplemented for gtk."); + } + pub fn set_title(&mut self, title: impl Into) { self.title = title.into(); } @@ -553,6 +557,24 @@ impl WindowHandle { } } + pub fn set_position(&self, _position: Point) { + log::warn!("WindowHandle::set_position is currently unimplemented for gtk."); + } + + pub fn get_position(&self) -> Point { + log::warn!("WindowHandle::get_position is currently unimplemented for gtk."); + Point::new(0.0, 0.0) + } + + pub fn set_size(&self, _size: Size) { + log::warn!("WindowHandle::set_size is currently unimplemented for gtk."); + } + + pub fn get_size(&self) -> Size { + log::warn!("WindowHandle::get_size is currently unimplemented for gtk."); + Size::new(0.0, 0.0) + } + /// Close the window. pub fn close(&self) { if let Some(state) = self.state.upgrade() { diff --git a/druid-shell/src/platform/mac/mod.rs b/druid-shell/src/platform/mac/mod.rs index c041746b0c..be29bf7db4 100644 --- a/druid-shell/src/platform/mac/mod.rs +++ b/druid-shell/src/platform/mac/mod.rs @@ -25,3 +25,4 @@ mod keyboard; pub mod menu; pub mod util; pub mod window; +pub mod screen; diff --git a/druid-shell/src/platform/mac/screen.rs b/druid-shell/src/platform/mac/screen.rs new file mode 100644 index 0000000000..91fc59d360 --- /dev/null +++ b/druid-shell/src/platform/mac/screen.rs @@ -0,0 +1,28 @@ +// Copyright 2020 The druid Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! macOS Monitors and Screen information. + +use crate::screen::Monitor; +use crate::kurbo::Size; + +pub fn get_display_size() -> Size { + log::warn!("Screen::get_display_size() is currently unimplemented for Mac."); + Size::new(0.0, 0.0) +} + +pub fn get_monitors() -> Vec { + log::warn!("Screen::get_monitors() is currently unimplemented for Mac."); + Vec::::new() +} diff --git a/druid-shell/src/platform/mac/window.rs b/druid-shell/src/platform/mac/window.rs index 9cf146d01a..5546277213 100644 --- a/druid-shell/src/platform/mac/window.rs +++ b/druid-shell/src/platform/mac/window.rs @@ -147,6 +147,10 @@ impl WindowBuilder { self.show_titlebar = show_titlebar; } + pub fn set_position(&mut self, _position: Point) { + log::warn!("WindowBuilder::set_position is currently unimplemented for mac platforms."); + } + pub fn set_title(&mut self, title: impl Into) { self.title = title.into(); } @@ -838,6 +842,24 @@ impl WindowHandle { // TODO: Implement this pub fn show_titlebar(&self, _show_titlebar: bool) {} + pub fn set_position(&self, _position: Point) { + log::warn!("WindowHandle::set_position is currently unimplemented for Mac."); + } + + pub fn get_position(&self) -> Point { + log::warn!("WindowHandle::get_position is currently unimplemented for Mac."); + Point::new(0.0, 0.0) + } + + pub fn set_size(&self, _size: Size) { + log::warn!("WindowHandle::set_size is currently unimplemented for Mac."); + } + + pub fn get_size(&self) -> Size { + log::warn!("WindowHandle::get_size is currently unimplemented for Mac."); + Size::new(0.0, 0.0) + } + pub fn resizable(&self, resizable: bool) { unsafe { let window: id = msg_send![*self.nsview.load(), window]; diff --git a/druid-shell/src/platform/web/mod.rs b/druid-shell/src/platform/web/mod.rs index e0fbea1115..2d44f4d844 100644 --- a/druid-shell/src/platform/web/mod.rs +++ b/druid-shell/src/platform/web/mod.rs @@ -20,3 +20,4 @@ pub mod error; pub mod keycodes; pub mod menu; pub mod window; +pub mod screen; diff --git a/druid-shell/src/platform/web/screen.rs b/druid-shell/src/platform/web/screen.rs new file mode 100644 index 0000000000..b22de063d7 --- /dev/null +++ b/druid-shell/src/platform/web/screen.rs @@ -0,0 +1,28 @@ +// Copyright 2020 The druid Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Monitor and Screen information ignored for web. + +use crate::screen::Monitor; +use crate::kurbo::Size; + +pub fn get_display_size() -> Size { + //Ignore? + Size::new(0.0, 0.0) +} + +pub fn get_monitors() -> Vec { + //Ignore? + Vec::::new() +} diff --git a/druid-shell/src/platform/web/window.rs b/druid-shell/src/platform/web/window.rs index f28d592242..eeb2d57985 100644 --- a/druid-shell/src/platform/web/window.rs +++ b/druid-shell/src/platform/web/window.rs @@ -345,6 +345,10 @@ impl WindowBuilder { // Ignored } + pub fn set_position(&mut self, _position: Point) { + // Ignored + } + pub fn set_title>(&mut self, title: S) { self.title = title.into(); } @@ -430,6 +434,24 @@ impl WindowHandle { log::warn!("show_titlebar unimplemented for web"); } + pub fn set_position(&self, _position: Point) { + log::warn!("WindowHandle::set_position unimplemented for web"); + } + + pub fn get_position(&self) -> Point { + log::warn!("WindowHandle::get_position unimplemented for web."); + Point::new(0.0, 0.0) + } + + pub fn set_size(&self, _size: Size) { + log::warn!("WindowHandle::set_size unimplemented for web."); + } + + pub fn get_size(&self) -> Size { + log::warn!("WindowHandle::get_size unimplemented for web."); + Size::new(0.0, 0.0) + } + pub fn close(&self) { // TODO } diff --git a/druid-shell/src/platform/windows/mod.rs b/druid-shell/src/platform/windows/mod.rs index e5b330ba01..1d17d3f3a1 100644 --- a/druid-shell/src/platform/windows/mod.rs +++ b/druid-shell/src/platform/windows/mod.rs @@ -26,6 +26,7 @@ pub mod paint; mod timers; pub mod util; pub mod window; +pub mod screen; // https://docs.microsoft.com/en-us/windows/win32/direct2d/render-targets-overview // ID2D1RenderTarget is the interface. The other resources inherit from it. diff --git a/druid-shell/src/platform/windows/screen.rs b/druid-shell/src/platform/windows/screen.rs new file mode 100644 index 0000000000..3ef0018cfa --- /dev/null +++ b/druid-shell/src/platform/windows/screen.rs @@ -0,0 +1,77 @@ +// Copyright 2020 The druid Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Windows Monitors and Screen information. + +use log::warn; +use super::error::Error; +use winapi::shared::winerror::*; +use winapi::um::errhandlingapi::GetLastError; +use winapi::shared::minwindef::*; +use winapi::shared::windef::*; +use winapi::um::winuser::*; +use std::ptr::null_mut; +use std::mem::size_of; +use std::sync::Once; + +use crate::screen::Monitor; +use crate::kurbo::{Rect, Size}; + +pub fn get_display_size() -> Size { + unsafe { + let width = GetSystemMetrics(SM_CXVIRTUALSCREEN) as f64; + let height = GetSystemMetrics(SM_CYVIRTUALSCREEN) as f64; + if width == 0.0 || height == 0.0 { + warn!( + "Failed to get display size" + ); + } + Size::new(width, height) + } +} + +static mut MONITORS : Vec = Vec::new(); + +unsafe extern "system" fn monitorenumproc(hmonitor : HMONITOR, _hdc : HDC, _lprect : LPRECT, _lparam : LPARAM) -> BOOL { + let rect = RECT { left: 0, top: 0, right: 0, bottom: 0}; + let mut info = MONITORINFO { cbSize : size_of::() as u32, rcMonitor : rect, rcWork : rect, dwFlags : 0}; + if GetMonitorInfoW(hmonitor,&mut info) == 0 { + warn!( + "failed to get Monitor Info: {}", + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) + ); + }; + let primary = info.dwFlags == MONITORINFOF_PRIMARY; + let rect = Rect::new(info.rcMonitor.left as f64, info.rcMonitor.top as f64, info.rcMonitor.right as f64, info.rcMonitor.bottom as f64); + let work_rect = Rect::new(info.rcWork.left as f64, info.rcWork.top as f64, info.rcWork.right as f64, info.rcWork.bottom as f64); + let m = Monitor::new(primary, rect, work_rect); + MONITORS.push(m); + TRUE +} + + +pub fn get_monitors() -> Vec { + static START: Once = Once::new(); + unsafe { + START.call_once(|| { + if EnumDisplayMonitors(null_mut(), null_mut(), Some(monitorenumproc), 0) == 0{ + warn!( + "Failed to Enumerate Display Monitors: {}", + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) + ); + }; + }); + MONITORS.clone() + } +} diff --git a/druid-shell/src/platform/windows/window.rs b/druid-shell/src/platform/windows/window.rs index 82e6c4df66..92daac993d 100644 --- a/druid-shell/src/platform/windows/window.rs +++ b/druid-shell/src/platform/windows/window.rs @@ -85,6 +85,7 @@ pub(crate) struct WindowBuilder { show_titlebar: bool, size: Size, min_size: Option, + position: Point, } #[derive(Clone, Copy, PartialEq, Eq, Debug)] @@ -891,6 +892,7 @@ impl WindowBuilder { present_strategy: Default::default(), size: Size::new(500.0, 400.0), min_size: None, + position: Point::new(CW_USEDEFAULT as f64, CW_USEDEFAULT as f64), } } @@ -924,6 +926,10 @@ impl WindowBuilder { self.menu = Some(menu); } + pub fn set_position(&mut self, position : Point) { + self.position = position; + } + pub fn build(self) -> Result { unsafe { let class_name = super::util::CLASS_NAME.to_wide(); @@ -989,18 +995,21 @@ impl WindowBuilder { if !self.resizable { dwStyle &= !(WS_THICKFRAME | WS_MAXIMIZEBOX); } - + if !self.show_titlebar { + dwStyle = WS_POPUP + } let mut dwExStyle = 0; if self.present_strategy == PresentStrategy::Flip { dwExStyle |= WS_EX_NOREDIRECTIONBITMAP; } + let hwnd = create_window( dwExStyle, class_name.as_ptr(), self.title.to_wide().as_ptr(), dwStyle, - CW_USEDEFAULT, - CW_USEDEFAULT, + self.position.x as i32, + self.position.y as i32, size_px.width as i32, size_px.height as i32, 0 as HWND, @@ -1280,6 +1289,88 @@ impl WindowHandle { // TODO: Implement this pub fn show_titlebar(&self, _show_titlebar: bool) {} + pub fn set_position(&self, position: Point) { + if let Some(w) = self.state.upgrade() { + let hwnd = w.hwnd.get(); + unsafe { + let mut rect = RECT { left: 0, top: 0, right: 0, bottom: 0}; + + if GetWindowRect(hwnd, &mut rect) == 0 { + warn!( + "failed to get window rect: {}", + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) + ); + }; + let width = rect.right - rect.left; + let height = rect.bottom - rect.top; + if MoveWindow(hwnd, position.x as i32, position.y as i32, width, height, 0) == 0 { + warn!( + "failed to reposition window: {}", + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) + ); + }; + } + } + } + + pub fn get_position(&self) -> Point { + if let Some(w) = self.state.upgrade() { + let hwnd = w.hwnd.get(); + unsafe { + let mut rect = RECT { left: 0, top: 0, right: 0, bottom: 0}; + if GetWindowRect(hwnd, &mut rect) == 0 { + warn!( + "failed to get window rect: {}", + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) + ); + }; + return Point::new(rect.left as f64, rect.top as f64) + } + } + warn!( + "failed to get window hwnd" + ); + Point::new(0.0, 0.0) + } + + //FIXME: Currently the WM_SIZE message gets dropped and the GUI is not redrawn when size is changed + pub fn set_size(&self, size: Size) { + if let Some(w) = self.state.upgrade() { + let hwnd = w.hwnd.get(); + unsafe { + let position = self.get_position(); + if MoveWindow(hwnd, position.x as i32, position.y as i32, size.width as i32, size.height as i32, 1) == 0 { + warn!( + "failed to resize window: {}", + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) + ); + }; + } + } + } + + pub fn get_size(&self) -> Size { + if let Some(w) = self.state.upgrade() { + let hwnd = w.hwnd.get(); + unsafe { + let mut rect = RECT { left: 0, top: 0, right: 0, bottom: 0}; + if GetWindowRect(hwnd, &mut rect) == 0 { + warn!( + "failed to get window rect: {}", + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) + ); + }; + let width = rect.right - rect.left; + let height = rect.bottom - rect.top; + return Size::new(width as f64, height as f64); + } + } + warn!( + "failed to get window size" + ); + Size::new(0.0, 0.0) + } + pub fn resizable(&self, resizable: bool) { if let Some(w) = self.state.upgrade() { let hwnd = w.hwnd.get(); diff --git a/druid-shell/src/platform/x11/mod.rs b/druid-shell/src/platform/x11/mod.rs index d80a4cde82..b6d47debbd 100644 --- a/druid-shell/src/platform/x11/mod.rs +++ b/druid-shell/src/platform/x11/mod.rs @@ -39,3 +39,4 @@ pub mod error; pub mod keycodes; pub mod menu; pub mod window; +pub mod screen; diff --git a/druid-shell/src/platform/x11/screen.rs b/druid-shell/src/platform/x11/screen.rs new file mode 100644 index 0000000000..dd33ac4500 --- /dev/null +++ b/druid-shell/src/platform/x11/screen.rs @@ -0,0 +1,28 @@ +// Copyright 2020 The druid Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! X11 Monitors and Screen information. + +use crate::screen::Monitor; +use crate::kurbo::Size; + +pub fn get_display_size() -> Size { + log::warn!("Screen::get_display_size() is currently unimplemented for X11 platforms."); + Size::new(0.0, 0.0) +} + +pub fn get_monitors() -> Vec { + log::warn!("Screen::get_monitors() is currently unimplemented for X11 platforms."); + Vec::::new() +} diff --git a/druid-shell/src/platform/x11/window.rs b/druid-shell/src/platform/x11/window.rs index 10e149d437..025fe23b12 100644 --- a/druid-shell/src/platform/x11/window.rs +++ b/druid-shell/src/platform/x11/window.rs @@ -124,6 +124,10 @@ impl WindowBuilder { log::warn!("WindowBuilder::show_titlebar is currently unimplemented for X11 platforms."); } + pub fn set_position(&mut self, _position: Point) { + log::warn!("WindowBuilder::set_position is currently unimplemented for X11 platforms."); + } + pub fn set_title>(&mut self, title: S) { self.title = title.into(); } @@ -1251,6 +1255,24 @@ impl WindowHandle { } } + pub fn set_position(&self, _position: Point) { + log::warn!("WindowHandle::set_position is currently unimplemented for X11 platforms."); + } + + pub fn get_position(&self) -> Point { + log::warn!("WindowHandle::get_position is currently unimplemented for X11 platforms."); + Point::new(0.0, 0.0) + } + + pub fn set_size(&self, _size: Size) { + log::warn!("WindowHandle::set_size is currently unimplemented for X11 platforms."); + } + + pub fn get_size(&self) -> Size { + log::warn!("WindowHandle::get_size is currently unimplemented for X11 platforms."); + Size::new(0.0, 0.0) + } + pub fn bring_to_front_and_focus(&self) { if let Some(w) = self.window.upgrade() { w.bring_to_front_and_focus(); diff --git a/druid-shell/src/screen.rs b/druid-shell/src/screen.rs new file mode 100644 index 0000000000..0ccaf28722 --- /dev/null +++ b/druid-shell/src/screen.rs @@ -0,0 +1,64 @@ +// Copyright 2020 The druid Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Module to get information about monitors + +use crate::kurbo::{Rect, Size}; +use crate::platform; +use std::fmt; +use std::fmt::Display; + +#[derive(Clone)] +pub struct Monitor { + primary: bool, + rect: Rect, // The working Rectangle of the monitor in pixels related to Screen.size + work_rect: Rect, // The working Rectangle of the monitor in pixels related to Screen.size excluding reserved space (the taskbar) +} + +impl Monitor { + pub fn new(primary: bool, rect: Rect, work_rect: Rect) -> Self { + Monitor { + primary, + rect, + work_rect, + } + } +} + +impl Display for Monitor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.primary { + write!(f, "Primary ")?; + } else { + write!(f, "Secondary ")?; + } + write!( + f, + "({}, {})({}, {})", + self.rect.x0, self.rect.x1, self.rect.y0, self.rect.y1 + )?; + Ok(()) + } +} + +pub struct Screen {} +impl Screen { + pub fn get_monitors() -> Vec { + platform::screen::get_monitors() + } + + pub fn get_display_size() -> Size { + platform::screen::get_display_size() + } +} diff --git a/druid-shell/src/window.rs b/druid-shell/src/window.rs index 720c2643cb..fae2a5d1ac 100644 --- a/druid-shell/src/window.rs +++ b/druid-shell/src/window.rs @@ -123,6 +123,26 @@ impl WindowHandle { self.0.show_titlebar(show_titlebar) } + /// Set the position of the window + pub fn set_position(&self, position: Point) { + self.0.set_position(position) + } + + /// Get the position of the window + pub fn get_position(&self) -> Point { + self.0.get_position() + } + + /// Set the size of the window + pub fn set_size(&self, size: Size) { + self.0.set_size(size) + } + + /// Gets the size of the window + pub fn get_size(&self) -> Size { + self.0.get_size() + } + /// Bring this window to the front of the window stack and give it focus. pub fn bring_to_front_and_focus(&self) { self.0.bring_to_front_and_focus() @@ -269,6 +289,11 @@ impl WindowBuilder { self.0.show_titlebar(show_titlebar) } + /// Set the window's initial position. + pub fn set_position(&mut self, position: Point) { + self.0.set_position(position); + } + /// Set the window's initial title. pub fn set_title(&mut self, title: impl Into) { self.0.set_title(title) diff --git a/druid/src/app.rs b/druid/src/app.rs index b895be844f..804a56a84d 100644 --- a/druid/src/app.rs +++ b/druid/src/app.rs @@ -15,7 +15,7 @@ //! Window building and app lifecycle. use crate::ext_event::{ExtEventHost, ExtEventSink}; -use crate::kurbo::Size; +use crate::kurbo::{Point, Size}; use crate::shell::{Application, Error as PlatformError, WindowBuilder, WindowHandle}; use crate::widget::LabelText; use crate::win_handler::{AppHandler, AppState}; @@ -44,6 +44,7 @@ pub struct WindowDesc { pub(crate) title: LabelText, pub(crate) size: Option, pub(crate) min_size: Option, + pub(crate) position: Option, pub(crate) menu: Option>, pub(crate) resizable: bool, pub(crate) show_titlebar: bool, @@ -163,6 +164,7 @@ impl WindowDesc { title: LocalizedString::new("app-name").into(), size: None, min_size: None, + position: None, menu: MenuDesc::platform_default(), resizable: true, show_titlebar: true, @@ -234,6 +236,12 @@ impl WindowDesc { self } + /// Set the initial position for this window. + pub fn set_position(mut self, position: Point) -> Self { + self.position = Some(position); + self + } + /// Attempt to create a platform window from this `WindowDesc`. pub(crate) fn build_native( mut self, @@ -260,6 +268,10 @@ impl WindowDesc { builder.set_min_size(min_size); } + if let Some(position) = self.position { + builder.set_position(position); + } + builder.set_title(self.title.display_text()); if let Some(menu) = platform_menu { builder.set_menu(menu); diff --git a/druid/src/lib.rs b/druid/src/lib.rs index 0bb2244708..cf1bdfaf9f 100644 --- a/druid/src/lib.rs +++ b/druid/src/lib.rs @@ -171,7 +171,7 @@ pub use shell::keyboard_types; pub use shell::{ Application, Clipboard, ClipboardFormat, Code, Cursor, Error as PlatformError, FileDialogOptions, FileInfo, FileSpec, FormatId, HotKey, KbKey, KeyEvent, Location, Modifiers, - MouseButton, MouseButtons, RawMods, Scalable, Scale, SysMods, Text, TimerToken, WindowHandle, + MouseButton, MouseButtons, RawMods, Scalable, Scale, Screen, SysMods, Text, TimerToken, WindowHandle, }; pub use crate::core::WidgetPod; From 0c677efd19e43f3d37a6e803bae528c7f0811b5b Mon Sep 17 00:00:00 2001 From: rhzk Date: Wed, 1 Jul 2020 00:29:44 +0200 Subject: [PATCH 02/18] Windows: Added solution for re-entrancy problem when window size is changed. --- druid-shell/src/platform/gtk/screen.rs | 6 +- druid-shell/src/platform/mac/screen.rs | 6 +- druid-shell/src/platform/web/screen.rs | 10 +- .../src/platform/windows/application.rs | 9 +- druid-shell/src/platform/windows/screen.rs | 23 ++-- druid-shell/src/platform/windows/window.rs | 114 ++++++++++++------ druid-shell/src/platform/x11/screen.rs | 6 +- druid-shell/src/screen.rs | 32 ++++- druid-shell/src/window.rs | 18 ++- druid/src/app.rs | 5 +- 10 files changed, 157 insertions(+), 72 deletions(-) diff --git a/druid-shell/src/platform/gtk/screen.rs b/druid-shell/src/platform/gtk/screen.rs index d4ff342a1a..24f629f378 100644 --- a/druid-shell/src/platform/gtk/screen.rs +++ b/druid-shell/src/platform/gtk/screen.rs @@ -1,4 +1,4 @@ -// Copyright 2020 The druid Authors. +// Copyright 2020 The Druid Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -17,12 +17,12 @@ use crate::screen::Monitor; use crate::kurbo::Size; -pub fn get_display_size() -> Size { +pub(crate) fn get_display_size() -> Size { log::warn!("Screen::get_display_size() is currently unimplemented for gtk."); Size::new(0.0, 0.0) } -pub fn get_monitors() -> Vec { +pub(crate) fn get_monitors() -> Vec { log::warn!("Screen::get_monitors() is currently unimplemented for gtk."); Vec::::new() } diff --git a/druid-shell/src/platform/mac/screen.rs b/druid-shell/src/platform/mac/screen.rs index 91fc59d360..81e0a08ad3 100644 --- a/druid-shell/src/platform/mac/screen.rs +++ b/druid-shell/src/platform/mac/screen.rs @@ -1,4 +1,4 @@ -// Copyright 2020 The druid Authors. +// Copyright 2020 The Druid Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -17,12 +17,12 @@ use crate::screen::Monitor; use crate::kurbo::Size; -pub fn get_display_size() -> Size { +pub(crate) fn get_display_size() -> Size { log::warn!("Screen::get_display_size() is currently unimplemented for Mac."); Size::new(0.0, 0.0) } -pub fn get_monitors() -> Vec { +pub(crate) fn get_monitors() -> Vec { log::warn!("Screen::get_monitors() is currently unimplemented for Mac."); Vec::::new() } diff --git a/druid-shell/src/platform/web/screen.rs b/druid-shell/src/platform/web/screen.rs index b22de063d7..83142774d6 100644 --- a/druid-shell/src/platform/web/screen.rs +++ b/druid-shell/src/platform/web/screen.rs @@ -1,4 +1,4 @@ -// Copyright 2020 The druid Authors. +// Copyright 2020 The Druid Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -17,12 +17,12 @@ use crate::screen::Monitor; use crate::kurbo::Size; -pub fn get_display_size() -> Size { - //Ignore? +pub(crate) fn get_display_size() -> Size { + log::warn!("Screen::get_display_size() is not implemented for web."); Size::new(0.0, 0.0) } -pub fn get_monitors() -> Vec { - //Ignore? +pub(crate) fn get_monitors() -> Vec { + log::warn!("Screen::get_monitors() is not implemented for web."); Vec::::new() } diff --git a/druid-shell/src/platform/windows/application.rs b/druid-shell/src/platform/windows/application.rs index 14d7c8ca09..3de2a8faa6 100644 --- a/druid-shell/src/platform/windows/application.rs +++ b/druid-shell/src/platform/windows/application.rs @@ -29,7 +29,7 @@ use winapi::um::shellscalingapi::PROCESS_SYSTEM_DPI_AWARE; use winapi::um::winuser::{ DispatchMessageW, GetAncestor, GetMessageW, LoadIconW, PostMessageW, PostQuitMessage, RegisterClassW, TranslateAcceleratorW, TranslateMessage, GA_ROOT, IDI_APPLICATION, MSG, - WNDCLASSW, + WNDCLASSW, SendMessageW }; use crate::application::AppHandler; @@ -38,7 +38,7 @@ use super::accels; use super::clipboard::Clipboard; use super::error::Error; use super::util::{self, ToWide, CLASS_NAME, OPTIONAL_FUNCTIONS}; -use super::window::{self, DS_REQUEST_DESTROY}; +use super::window::{self, DS_REQUEST_DESTROY, DS_HANDLE_DROPPED}; #[derive(Clone)] pub(crate) struct Application { @@ -105,6 +105,11 @@ impl Application { unsafe { // Handle windows messages loop { + if let Ok(state) = self.state.try_borrow() { + for hwnd in &state.windows { + SendMessageW(*hwnd, DS_HANDLE_DROPPED, 0,0); + } + } let mut msg = mem::MaybeUninit::uninit(); let res = GetMessageW(msg.as_mut_ptr(), ptr::null_mut(), 0, 0); if res <= 0 { diff --git a/druid-shell/src/platform/windows/screen.rs b/druid-shell/src/platform/windows/screen.rs index 3ef0018cfa..839343bc8a 100644 --- a/druid-shell/src/platform/windows/screen.rs +++ b/druid-shell/src/platform/windows/screen.rs @@ -1,4 +1,4 @@ -// Copyright 2020 The druid Authors. +// Copyright 2020 The Druid Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -23,12 +23,11 @@ use winapi::shared::windef::*; use winapi::um::winuser::*; use std::ptr::null_mut; use std::mem::size_of; -use std::sync::Once; use crate::screen::Monitor; use crate::kurbo::{Rect, Size}; -pub fn get_display_size() -> Size { +pub(crate) fn get_display_size() -> Size { unsafe { let width = GetSystemMetrics(SM_CXVIRTUALSCREEN) as f64; let height = GetSystemMetrics(SM_CYVIRTUALSCREEN) as f64; @@ -61,17 +60,15 @@ unsafe extern "system" fn monitorenumproc(hmonitor : HMONITOR, _hdc : HDC, _lpre } -pub fn get_monitors() -> Vec { - static START: Once = Once::new(); +pub(crate) fn get_monitors() -> Vec { unsafe { - START.call_once(|| { - if EnumDisplayMonitors(null_mut(), null_mut(), Some(monitorenumproc), 0) == 0{ - warn!( - "Failed to Enumerate Display Monitors: {}", - Error::Hr(HRESULT_FROM_WIN32(GetLastError())) - ); - }; - }); + MONITORS = Vec::new(); + if EnumDisplayMonitors(null_mut(), null_mut(), Some(monitorenumproc), 0) == 0{ + warn!( + "Failed to Enumerate Display Monitors: {}", + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) + ); + }; MONITORS.clone() } } diff --git a/druid-shell/src/platform/windows/window.rs b/druid-shell/src/platform/windows/window.rs index 92daac993d..b369276cf4 100644 --- a/druid-shell/src/platform/windows/window.rs +++ b/druid-shell/src/platform/windows/window.rs @@ -122,6 +122,11 @@ pub struct WindowHandle { state: Weak, } +enum BlockingOp { + SetPosition(Point), + SetSize(Size), +} + /// A handle that can get used to schedule an idle handler. Note that /// this handle is thread safe. If the handle is used after the hwnd /// has been destroyed, probably not much will go wrong (the DS_RUN_IDLE @@ -148,6 +153,7 @@ struct WindowState { wndproc: Box, idle_queue: Arc>>, timers: Arc>, + blocked_queue: RefCell>, } /// Generic handler trait for the winapi window procedure entry point. @@ -213,6 +219,12 @@ const DS_RUN_IDLE: UINT = WM_USER; /// time it is handled, we can successfully borrow the handler. pub(crate) const DS_REQUEST_DESTROY: UINT = WM_USER + 1; +/// Message relaying a request to handle dropped messages. +/// +/// Rust borrow checker causes messages to be dropped +/// so as a temporary? solution we place them in a queue and handle them again +pub(crate) const DS_HANDLE_DROPPED: UINT = WM_USER + 2; + impl Default for PresentStrategy { fn default() -> PresentStrategy { // We probably want to change this, but we need GDI to work. Too bad about @@ -358,6 +370,57 @@ impl MyWndProc { fn has_menu(&self) -> bool { self.with_window_state(|state| state.has_menu.get()) } + + // Here we handle messages generated by WindowHandle + // that needs to run after borrow is released + fn handle_blocked(&self) { + if let Some(hwnd) = self.handle.borrow().get_hwnd() { + let q = self.with_window_state(move |state| state.blocked_queue.replace(Vec::new())); + for op in q.iter() { + match op { + BlockingOp::SetSize(size) => { + unsafe { + let mut rect = RECT { left: 0, top: 0, right: 0, bottom: 0}; + if GetWindowRect(hwnd, &mut rect) == 0 { + warn!( + "failed to get window rect: {}", + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) + ); + }; + let position = Point::new(rect.left as f64, rect.top as f64); + if MoveWindow(hwnd, position.x as i32, position.y as i32, size.width as i32, size.height as i32, 1) == 0 { + warn!( + "failed to resize window: {}", + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) + ); + }; + } + } + BlockingOp::SetPosition(position) => { + unsafe { + let mut rect = RECT { left: 0, top: 0, right: 0, bottom: 0}; + if GetWindowRect(hwnd, &mut rect) == 0 { + warn!( + "failed to get window rect: {}", + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) + ); + }; + let width = rect.right - rect.left; + let height = rect.bottom - rect.top; + if MoveWindow(hwnd, position.x as i32, position.y as i32, width as i32, height as i32, 1) == 0 { + warn!( + "failed to move window: {}", + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) + ); + }; + } + } + } + } + } else { + warn!("Could not get HWND"); + } + } } impl WndProc for MyWndProc { @@ -875,6 +938,10 @@ impl WndProc for MyWndProc { None } } + DS_HANDLE_DROPPED => { + self.handle_blocked(); + Some(0) + } _ => None, } } @@ -973,6 +1040,7 @@ impl WindowBuilder { wndproc: Box::new(wndproc), idle_queue: Default::default(), timers: Arc::new(Mutex::new(TimerSlots::new(1))), + blocked_queue: RefCell::new(Vec::new()), }; let win = Rc::new(window); let handle = WindowHandle { @@ -1291,24 +1359,12 @@ impl WindowHandle { pub fn set_position(&self, position: Point) { if let Some(w) = self.state.upgrade() { - let hwnd = w.hwnd.get(); - unsafe { - let mut rect = RECT { left: 0, top: 0, right: 0, bottom: 0}; - - if GetWindowRect(hwnd, &mut rect) == 0 { - warn!( - "failed to get window rect: {}", - Error::Hr(HRESULT_FROM_WIN32(GetLastError())) - ); - }; - let width = rect.right - rect.left; - let height = rect.bottom - rect.top; - if MoveWindow(hwnd, position.x as i32, position.y as i32, width, height, 0) == 0 { - warn!( - "failed to reposition window: {}", - Error::Hr(HRESULT_FROM_WIN32(GetLastError())) - ); - }; + if let Ok(mut q) = w.blocked_queue.try_borrow_mut() { + q.push(BlockingOp::SetPosition(position)) + } else { + warn!( + "failed to borrow blocked queue" + ); } } } @@ -1327,24 +1383,17 @@ impl WindowHandle { return Point::new(rect.left as f64, rect.top as f64) } } - warn!( - "failed to get window hwnd" - ); Point::new(0.0, 0.0) } - //FIXME: Currently the WM_SIZE message gets dropped and the GUI is not redrawn when size is changed pub fn set_size(&self, size: Size) { if let Some(w) = self.state.upgrade() { - let hwnd = w.hwnd.get(); - unsafe { - let position = self.get_position(); - if MoveWindow(hwnd, position.x as i32, position.y as i32, size.width as i32, size.height as i32, 1) == 0 { - warn!( - "failed to resize window: {}", - Error::Hr(HRESULT_FROM_WIN32(GetLastError())) - ); - }; + if let Ok(mut q) = w.blocked_queue.try_borrow_mut() { + q.push(BlockingOp::SetSize(size)) + } else { + warn!( + "failed to borrow blocked queue" + ); } } } @@ -1365,9 +1414,6 @@ impl WindowHandle { return Size::new(width as f64, height as f64); } } - warn!( - "failed to get window size" - ); Size::new(0.0, 0.0) } diff --git a/druid-shell/src/platform/x11/screen.rs b/druid-shell/src/platform/x11/screen.rs index dd33ac4500..e71f2b6627 100644 --- a/druid-shell/src/platform/x11/screen.rs +++ b/druid-shell/src/platform/x11/screen.rs @@ -1,4 +1,4 @@ -// Copyright 2020 The druid Authors. +// Copyright 2020 The Druid Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -17,12 +17,12 @@ use crate::screen::Monitor; use crate::kurbo::Size; -pub fn get_display_size() -> Size { +pub(crate) fn get_display_size() -> Size { log::warn!("Screen::get_display_size() is currently unimplemented for X11 platforms."); Size::new(0.0, 0.0) } -pub fn get_monitors() -> Vec { +pub(crate) fn get_monitors() -> Vec { log::warn!("Screen::get_monitors() is currently unimplemented for X11 platforms."); Vec::::new() } diff --git a/druid-shell/src/screen.rs b/druid-shell/src/screen.rs index 0ccaf28722..d27b05d5ac 100644 --- a/druid-shell/src/screen.rs +++ b/druid-shell/src/screen.rs @@ -1,4 +1,4 @@ -// Copyright 2020 The druid Authors. +// Copyright 2020 The Druid Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -22,18 +22,42 @@ use std::fmt::Display; #[derive(Clone)] pub struct Monitor { primary: bool, - rect: Rect, // The working Rectangle of the monitor in pixels related to Screen.size - work_rect: Rect, // The working Rectangle of the monitor in pixels related to Screen.size excluding reserved space (the taskbar) + rect: Rect, + // TODO: Work area, cross_platform + // https://developer.apple.com/documentation/appkit/nsscreen/1388369-visibleframe + // https://developer.gnome.org/gdk3/stable/GdkMonitor.html#gdk-monitor-get-workarea + // https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-monitorinfo + // Unsure about x11 + work_rect: Rect, } impl Monitor { - pub fn new(primary: bool, rect: Rect, work_rect: Rect) -> Self { + pub(crate) fn new(primary: bool, rect: Rect, work_rect: Rect) -> Self { Monitor { primary, rect, work_rect, } } + /// Returns true if the monitor is the primary monitor + /// a primary monitor has its top-left corner at (0,0) + /// in virtual screen coordinates. + pub fn is_primary(&self) -> bool { + self.primary + } + /// Returns a RECT of the monitor rectangle in virtual screen coordinates + /// meaning that it contains the monitors resolution + /// oriented around the origin point: (0,0) being the top-left corner + /// of the primary monitor, in pixels. + pub fn virtual_rect(&self) -> Rect { + self.rect + } + + /// Returns a RECT of the monitor working rectangle in virtual screen coordinates + /// The RECT excludes area occupied by things like the dock,menubar (mac). taskbar (windows) + pub fn virtual_work_rect(&self) -> Rect { + self.work_rect + } } impl Display for Monitor { diff --git a/druid-shell/src/window.rs b/druid-shell/src/window.rs index fae2a5d1ac..7e2f2d1ee0 100644 --- a/druid-shell/src/window.rs +++ b/druid-shell/src/window.rs @@ -123,22 +123,32 @@ impl WindowHandle { self.0.show_titlebar(show_titlebar) } - /// Set the position of the window + /// Sets the position of the window in virtual screen coordinates (Pixels) + /// [`Point`] Position in pixels + /// + /// [`Point`]: struct.Point.html pub fn set_position(&self, position: Point) { self.0.set_position(position) } - /// Get the position of the window + /// Returns [`Point`] Position in virtual screen coordinates (Pixels) + /// + /// [`Point`]: struct.Point.html pub fn get_position(&self) -> Point { self.0.get_position() } - /// Set the size of the window + /// Set the size of the window in Pixels + /// [`Size`] Struct representing the Size + /// + /// [`Size`]: struct.Size.html pub fn set_size(&self, size: Size) { self.0.set_size(size) } - /// Gets the size of the window + /// returns [`Size`] containing the window size in pixels + /// + /// [`Size`]: struct.Size.html pub fn get_size(&self) -> Size { self.0.get_size() } diff --git a/druid/src/app.rs b/druid/src/app.rs index 804a56a84d..0d9cc04cda 100644 --- a/druid/src/app.rs +++ b/druid/src/app.rs @@ -236,7 +236,10 @@ impl WindowDesc { self } - /// Set the initial position for this window. + /// Sets the initial window position in virtual screen coordinates + /// [`Point`] Position in pixels + /// + /// [`Point`]: struct.Point.html pub fn set_position(mut self, position: Point) -> Self { self.position = Some(position); self From 81412f1dc774c32f6aadd47bbf603409d9a7345d Mon Sep 17 00:00:00 2001 From: rhzk Date: Mon, 6 Jul 2020 04:31:58 +0200 Subject: [PATCH 03/18] Windows: Added handling of custom titlebar on windows, added maximize and minimize options to window. --- CHANGELOG.md | 9 + druid-shell/src/lib.rs | 1 + druid-shell/src/platform/gtk/window.rs | 20 ++ druid-shell/src/platform/mac/window.rs | 20 ++ druid-shell/src/platform/web/window.rs | 20 ++ druid-shell/src/platform/windows/window.rs | 321 +++++++++++++++++---- druid-shell/src/platform/x11/window.rs | 20 ++ druid-shell/src/screen.rs | 24 +- druid-shell/src/window.rs | 74 ++++- druid/src/app.rs | 32 +- druid/src/lib.rs | 3 +- 11 files changed, 456 insertions(+), 88 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e924b937d..f890de10c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,10 +9,17 @@ You can find its changes [documented below](#060---2020-06-01). ### Added +- Windows: Added Screen module to get information about monitors and the screen. ([#1037] by [@rhzk]) +- Added documentation to resizable() and show_titlebar() in WindowDesc. ([#1037] by [@rhzk]) +- Windows: Added internal functions to handle Re-entrancy. ([#1037] by [@rhzk]) +- Windows: WindowDesc: Create window with disabled titlebar, maximized or minimized and with position. ([#1037] by [@rhzk]) +- Windows: WindowHandle: Toggle maximize, minimize window. Toggle titlebar. Change size and position of window. ([#1037] by [@rhzk]) +- Windows: WindowHandle: Added handle_titlebar(), Allowing a custom titlebar to behave like the OS one. ([#1037] by [@rhzk]) - Added ctrl/shift key support to textbox. ([#1063] by [@vkahl]) ### Changed +- winOS: Window created with OS default size if not set. ([#1037] by [@rhzk]) - `Image` and `ImageData` exported by default. ([#1011] by [@covercash2]) - `Scale::from_scale` to `Scale::new`, and `Scale` methods `scale_x` / `scale_y` to `x` / `y`. ([#1042] by [@xStrom]) - Major rework of keyboard event handling ([#1049] by [@raphlinus]) @@ -222,6 +229,7 @@ Last release without a changelog :( ## 0.1.1 - 2018-11-02 ## 0.1.0 - 2018-11-02 +[@rhzk]: https://github.com/rhzk [@futurepaul]: https://github.com/futurepaul [@finnerale]: https://github.com/finnerale [@totsteps]: https://github.com/totsteps @@ -359,6 +367,7 @@ Last release without a changelog :( [#1062]: https://github.com/linebender/druid/pull/1062 [#1072]: https://github.com/linebender/druid/pull/1072 [#1081]: https://github.com/linebender/druid/pull/1081 +[#1037]: https://github.com/linebender/druid/pull/1037 [Unreleased]: https://github.com/linebender/druid/compare/v0.6.0...master [0.6.0]: https://github.com/linebender/druid/compare/v0.5.0...v0.6.0 diff --git a/druid-shell/src/lib.rs b/druid-shell/src/lib.rs index 179469a180..d588b94682 100644 --- a/druid-shell/src/lib.rs +++ b/druid-shell/src/lib.rs @@ -60,6 +60,7 @@ pub use keyboard::{Code, IntoKey, KbKey, KeyEvent, KeyState, Location, Modifiers pub use menu::Menu; pub use mouse::{Cursor, MouseButton, MouseButtons, MouseEvent}; pub use scale::{Scalable, Scale, ScaledArea}; +pub use screen::Monitor; pub use screen::Screen; pub use window::{ IdleHandle, IdleToken, Text, TimerToken, WinHandler, WindowBuilder, WindowHandle, diff --git a/druid-shell/src/platform/gtk/window.rs b/druid-shell/src/platform/gtk/window.rs index 2d10b3b573..bd4f29d2b8 100644 --- a/druid-shell/src/platform/gtk/window.rs +++ b/druid-shell/src/platform/gtk/window.rs @@ -160,6 +160,14 @@ impl WindowBuilder { log::warn!("WindowBuilder::set_position is currently unimplemented for gtk."); } + pub fn maximized(&self) { + log::warn!("WindowBuilder::maximized is currently unimplemented for gtk."); + } + + pub fn minimized(&self) { + log::warn!("WindowBuilder::minimized is currently unimplemented for gtk."); + } + pub fn set_title(&mut self, title: impl Into) { self.title = title.into(); } @@ -575,6 +583,18 @@ impl WindowHandle { Size::new(0.0, 0.0) } + pub fn maximize(&self) { + log::warn!("WindowHandle::maximize is currently unimplemented for gtk."); + } + + pub fn minimize(&self) { + log::warn!("WindowHandle::minimize is currently unimplemented for gtk."); + } + + pub fn handle_titlebar(&self, _val: bool) { + log::warn!("WindowHandle::handle_titlebar is currently unimplemented for gtk."); + } + /// Close the window. pub fn close(&self) { if let Some(state) = self.state.upgrade() { diff --git a/druid-shell/src/platform/mac/window.rs b/druid-shell/src/platform/mac/window.rs index 5546277213..291a0e866f 100644 --- a/druid-shell/src/platform/mac/window.rs +++ b/druid-shell/src/platform/mac/window.rs @@ -151,6 +151,14 @@ impl WindowBuilder { log::warn!("WindowBuilder::set_position is currently unimplemented for mac platforms."); } + pub fn maximized(&self) { + log::warn!("WindowBuilder::maximized is currently unimplemented for mac platforms."); + } + + pub fn minimized(&self) { + log::warn!("WindowBuilder::minimized is currently unimplemented for mac platforms."); + } + pub fn set_title(&mut self, title: impl Into) { self.title = title.into(); } @@ -860,6 +868,18 @@ impl WindowHandle { Size::new(0.0, 0.0) } + pub fn maximize(&self) { + log::warn!("WindowHandle::maximize is currently unimplemented for Mac."); + } + + pub fn minimize(&self) { + log::warn!("WindowHandle::minimize is currently unimplemented for Mac."); + } + + pub fn handle_titlebar(&self, _val: bool) { + log::warn!("WindowHandle::handle_titlebar is currently unimplemented for Mac."); + } + pub fn resizable(&self, resizable: bool) { unsafe { let window: id = msg_send![*self.nsview.load(), window]; diff --git a/druid-shell/src/platform/web/window.rs b/druid-shell/src/platform/web/window.rs index eeb2d57985..db1b7927f3 100644 --- a/druid-shell/src/platform/web/window.rs +++ b/druid-shell/src/platform/web/window.rs @@ -349,6 +349,14 @@ impl WindowBuilder { // Ignored } + pub fn maximized(&self) { + // Ignored + } + + pub fn minimized(&self) { + // Ignored + } + pub fn set_title>(&mut self, title: S) { self.title = title.into(); } @@ -452,6 +460,18 @@ impl WindowHandle { Size::new(0.0, 0.0) } + pub fn maximize(&self) { + log::warn!("WindowHandle::maximize unimplemented for web."); + } + + pub fn minimize(&self) { + log::warn!("WindowHandle::minimize unimplemented for web."); + } + + pub fn handle_titlebar(&self, _val: bool) { + log::warn!("WindowHandle::handle_titlebar unimplemented for web."); + } + pub fn close(&self) { // TODO } diff --git a/druid-shell/src/platform/windows/window.rs b/druid-shell/src/platform/windows/window.rs index b369276cf4..cff9e2253e 100644 --- a/druid-shell/src/platform/windows/window.rs +++ b/druid-shell/src/platform/windows/window.rs @@ -86,6 +86,8 @@ pub(crate) struct WindowBuilder { size: Size, min_size: Option, position: Point, + maximized: bool, + minimized: bool, } #[derive(Clone, Copy, PartialEq, Eq, Debug)] @@ -122,9 +124,17 @@ pub struct WindowHandle { state: Weak, } +enum WindowSizeState { + Maximize, + Minimize, +} + enum BlockingOp { SetPosition(Point), SetSize(Size), + DecorationChanged(), + // Needs a better name + SetWindowSizeState(WindowSizeState), } /// A handle that can get used to schedule an idle handler. Note that @@ -154,6 +164,10 @@ struct WindowState { idle_queue: Arc>>, timers: Arc>, blocked_queue: RefCell>, + has_titlebar: Cell, + // For resizable borders, window can still be resized with code. + is_resizable: Cell, + handle_titlebar: Cell, } /// Generic handler trait for the winapi window procedure entry point. @@ -371,56 +385,118 @@ impl MyWndProc { self.with_window_state(|state| state.has_menu.get()) } + fn has_titlebar(&self) -> bool { + self.with_window_state(|state| state.has_titlebar.get()) + } + + fn resizable(&self) -> bool { + self.with_window_state(|state| state.is_resizable.get()) + } + // Here we handle messages generated by WindowHandle // that needs to run after borrow is released - fn handle_blocked(&self) { + fn handle_blocked(&self, op : BlockingOp) { if let Some(hwnd) = self.handle.borrow().get_hwnd() { - let q = self.with_window_state(move |state| state.blocked_queue.replace(Vec::new())); - for op in q.iter() { - match op { - BlockingOp::SetSize(size) => { - unsafe { - let mut rect = RECT { left: 0, top: 0, right: 0, bottom: 0}; - if GetWindowRect(hwnd, &mut rect) == 0 { - warn!( - "failed to get window rect: {}", - Error::Hr(HRESULT_FROM_WIN32(GetLastError())) - ); - }; - let position = Point::new(rect.left as f64, rect.top as f64); - if MoveWindow(hwnd, position.x as i32, position.y as i32, size.width as i32, size.height as i32, 1) == 0 { - warn!( - "failed to resize window: {}", - Error::Hr(HRESULT_FROM_WIN32(GetLastError())) - ); - }; + match op { + BlockingOp::SetSize(size) => { + unsafe { + if SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, (size.width * self.scale().x()) as i32, (size.height * self.scale().y()) as i32, SWP_NOMOVE | SWP_NOZORDER) == 0 { + warn!( + "failed to move window: {}", + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) + ); + }; + } + }, + BlockingOp::SetPosition(position) => { + unsafe { + if SetWindowPos(hwnd, HWND_TOPMOST, position.x as i32, position.y as i32, 0, 0, SWP_NOSIZE | SWP_NOZORDER) == 0 { + warn!( + "failed to move window: {}", + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) + ); + }; + } + }, + BlockingOp::DecorationChanged() => { + unsafe { + let resizable = self.resizable(); + let titlebar = self.has_titlebar(); + + let mut style = GetWindowLongPtrW(hwnd, GWL_STYLE) as u32; + if style == 0 { + warn!( + "failed to get window style: {}", + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) + ); + return; } + + if !resizable { + style &= !(WS_THICKFRAME | WS_MAXIMIZEBOX); + } else { + style |= WS_THICKFRAME | WS_MAXIMIZEBOX; + } + if !titlebar { + style &= !(WS_MINIMIZEBOX | WS_SYSMENU | WS_OVERLAPPED); + } else { + style |= WS_MINIMIZEBOX | WS_SYSMENU | WS_OVERLAPPED; + } + if SetWindowLongPtrW(hwnd, GWL_STYLE, style as isize) == 0 { + warn!( + "failed to set the window style: {}", + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) + ); + } + if SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOSIZE) == 0 { + warn!( + "failed to update window style: {}", + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) + ); + }; } - BlockingOp::SetPosition(position) => { - unsafe { - let mut rect = RECT { left: 0, top: 0, right: 0, bottom: 0}; - if GetWindowRect(hwnd, &mut rect) == 0 { - warn!( - "failed to get window rect: {}", - Error::Hr(HRESULT_FROM_WIN32(GetLastError())) - ); - }; - let width = rect.right - rect.left; - let height = rect.bottom - rect.top; - if MoveWindow(hwnd, position.x as i32, position.y as i32, width as i32, height as i32, 1) == 0 { - warn!( - "failed to move window: {}", - Error::Hr(HRESULT_FROM_WIN32(GetLastError())) - ); - }; + }, + BlockingOp::SetWindowSizeState(val) => { + unsafe { + let style = GetWindowLongPtrW(hwnd, GWL_STYLE) as u32; + if style == 0 { + warn!( + "failed to get window style: {}", + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) + ); + return; } + let s = match val { + WindowSizeState::Maximize => { + if (style & WS_MAXIMIZE) != 0 { + SW_RESTORE + } else { + SW_MAXIMIZE + } + }, + WindowSizeState::Minimize => { + if (style & WS_MINIMIZE) != 0 { + SW_RESTORE + } else { + SW_MINIMIZE + } + }, + }; + ShowWindow(hwnd,s); } - } + }, } } else { warn!("Could not get HWND"); } } + + fn handle_blocked_queue(&self) { + let q = self.with_window_state(move |state| state.blocked_queue.replace(Vec::new())); + for op in q { + self.handle_blocked(op); + } + } } impl WndProc for MyWndProc { @@ -473,6 +549,17 @@ impl WndProc for MyWndProc { } Some(0) } + WM_ACTIVATE => { + unsafe { + if SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOSIZE) == 0 { + warn!( + "failed to update window style: {}", + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) + ); + }; + } + Some(0) + } WM_ERASEBKGND => Some(0), WM_SETFOCUS => { if let Ok(mut s) = self.state.try_borrow_mut() { @@ -518,6 +605,56 @@ impl WndProc for MyWndProc { } Some(0) }, + WM_NCCALCSIZE => unsafe { + // Hack to get rid of caption but keeping the borders created by it. + if !self.has_titlebar() { + let style = GetWindowLongPtrW(hwnd, GWL_STYLE) as u32; + if style == 0 { + warn!( + "failed to get window style: {}", + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) + ); + return Some(0); + } + + let s: *mut NCCALCSIZE_PARAMS = lparam as *mut NCCALCSIZE_PARAMS; + if let Some(mut s) = s.as_mut() { + s.rgrc[0].top -= 31; + if (style & WS_MAXIMIZE) != 0 { + s.rgrc[0].top += 7; + } + } + } + // Let the default WinProc handle the message. + Some(DefWindowProcW(hwnd, msg, wparam, lparam)) + }, + WM_NCHITTEST => unsafe { + let mut hit = DefWindowProcW(hwnd, msg, wparam, lparam); + if !self.has_titlebar() { + let mut rect = RECT { left: 0, top: 0, right: 0, bottom: 0}; + if GetWindowRect(hwnd, &mut rect) == 0 { + warn!( + "failed to get window rect: {}", + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) + ); + }; + let a = HIWORD(lparam as u32) as i32 - rect.top; + if (a == 0) && (hit != HTTOPLEFT) && (hit != HTTOPRIGHT) && self.resizable() { + hit = HTTOP; + } else { + let mouseDown = GetAsyncKeyState(VK_LBUTTON) < 0; + + if self.with_window_state(|state| state.handle_titlebar.get()) && !mouseDown { + self.with_window_state(move |state| state.handle_titlebar.set(false)); + }; + + if self.with_window_state(|state| state.handle_titlebar.get()) && hit == HTCLIENT { + hit = HTCAPTION; + } + } + } + Some(hit) + }, WM_ENTERSIZEMOVE => unsafe { if let Ok(mut s) = self.state.try_borrow_mut() { let s = s.as_mut().unwrap(); @@ -939,7 +1076,7 @@ impl WndProc for MyWndProc { } } DS_HANDLE_DROPPED => { - self.handle_blocked(); + self.handle_blocked_queue(); Some(0) } _ => None, @@ -957,9 +1094,11 @@ impl WindowBuilder { resizable: true, show_titlebar: true, present_strategy: Default::default(), - size: Size::new(500.0, 400.0), + size: Size::new(CW_USEDEFAULT as f64, CW_USEDEFAULT as f64), min_size: None, position: Point::new(CW_USEDEFAULT as f64, CW_USEDEFAULT as f64), + maximized: false, + minimized: false, } } @@ -997,6 +1136,16 @@ impl WindowBuilder { self.position = position; } + /// Creates the window maximized. + pub fn maximized(&mut self) { + self.maximized = true; + } + + /// Creates the window minimized. + pub fn minimized(&mut self) { + self.minimized = true; + } + pub fn build(self) -> Result { unsafe { let class_name = super::util::CLASS_NAME.to_wide(); @@ -1041,6 +1190,9 @@ impl WindowBuilder { idle_queue: Default::default(), timers: Arc::new(Mutex::new(TimerSlots::new(1))), blocked_queue: RefCell::new(Vec::new()), + has_titlebar: Cell::new(self.show_titlebar), + is_resizable: Cell::new(self.resizable), + handle_titlebar: Cell::new(false), }; let win = Rc::new(window); let handle = WindowHandle { @@ -1064,7 +1216,7 @@ impl WindowBuilder { dwStyle &= !(WS_THICKFRAME | WS_MAXIMIZEBOX); } if !self.show_titlebar { - dwStyle = WS_POPUP + dwStyle &= !(WS_MINIMIZEBOX | WS_SYSMENU | WS_OVERLAPPED); } let mut dwExStyle = 0; if self.present_strategy == PresentStrategy::Flip { @@ -1093,6 +1245,15 @@ impl WindowBuilder { if let Some(accels) = accels { register_accel(hwnd, &accels); } + + if self.maximized && !self.minimized { + handle.maximize(); + } + + if self.minimized && !self.maximized { + handle.minimize(); + } + Ok(handle) } } @@ -1354,9 +1515,20 @@ impl WindowHandle { } } - // TODO: Implement this - pub fn show_titlebar(&self, _show_titlebar: bool) {} + pub fn show_titlebar(&self, show_titlebar: bool) { + if let Some(w) = self.state.upgrade() { + w.has_titlebar.set(show_titlebar); + if let Ok(mut q) = w.blocked_queue.try_borrow_mut() { + q.push(BlockingOp::DecorationChanged()) + } else { + warn!( + "failed to borrow blocked queue" + ); + } + } + } + // Sets the position of the window in virtual screen coordinates pub fn set_position(&self, position: Point) { if let Some(w) = self.state.upgrade() { if let Ok(mut q) = w.blocked_queue.try_borrow_mut() { @@ -1369,6 +1541,7 @@ impl WindowHandle { } } + // Gets the position of the window in virtual screen coordinates pub fn get_position(&self) -> Point { if let Some(w) = self.state.upgrade() { let hwnd = w.hwnd.get(); @@ -1386,6 +1559,7 @@ impl WindowHandle { Point::new(0.0, 0.0) } + // Sets the size of the window in DP pub fn set_size(&self, size: Size) { if let Some(w) = self.state.upgrade() { if let Ok(mut q) = w.blocked_queue.try_borrow_mut() { @@ -1398,6 +1572,7 @@ impl WindowHandle { } } + // Gets the size of the window in pixels pub fn get_size(&self) -> Size { if let Some(w) = self.state.upgrade() { let hwnd = w.hwnd.get(); @@ -1419,33 +1594,51 @@ impl WindowHandle { pub fn resizable(&self, resizable: bool) { if let Some(w) = self.state.upgrade() { - let hwnd = w.hwnd.get(); - unsafe { - let mut style = GetWindowLongPtrW(hwnd, GWL_STYLE); - if style == 0 { - warn!( - "failed to get window style: {}", - Error::Hr(HRESULT_FROM_WIN32(GetLastError())) - ); - return; - } + w.is_resizable.set(resizable); + if let Ok(mut q) = w.blocked_queue.try_borrow_mut() { + q.push(BlockingOp::DecorationChanged()) + } else { + warn!( + "failed to borrow blocked queue" + ); + } + } + } - if resizable { - style |= (WS_THICKFRAME | WS_MAXIMIZEBOX) as WindowLongPtr; - } else { - style &= !(WS_THICKFRAME | WS_MAXIMIZEBOX) as WindowLongPtr; - } + // Sets the window as maximized if it is not, restores it if it was. + pub fn maximize(&self) { + if let Some(w) = self.state.upgrade() { + if let Ok(mut q) = w.blocked_queue.try_borrow_mut() { + q.push(BlockingOp::SetWindowSizeState(WindowSizeState::Maximize)) + } else { + warn!( + "failed to borrow blocked queue" + ); + } + } + } - if SetWindowLongPtrW(hwnd, GWL_STYLE, style) == 0 { - warn!( - "failed to set the window style: {}", - Error::Hr(HRESULT_FROM_WIN32(GetLastError())) - ); - } + // Sets the window as minimized if it is not, restores it if it was. + pub fn minimize(&self) { + if let Some(w) = self.state.upgrade() { + if let Ok(mut q) = w.blocked_queue.try_borrow_mut() { + q.push(BlockingOp::SetWindowSizeState(WindowSizeState::Minimize)) + } else { + warn!( + "failed to borrow blocked queue" + ); } } } + + // Allows windows to handle a custom titlebar like it was the default one. + pub fn handle_titlebar(&self, val: bool) { + if let Some(w) = self.state.upgrade() { + w.handle_titlebar.set(val); + } + } + pub fn set_menu(&self, menu: Menu) { let accels = menu.accels(); let hmenu = menu.into_hmenu(); diff --git a/druid-shell/src/platform/x11/window.rs b/druid-shell/src/platform/x11/window.rs index 025fe23b12..9671805595 100644 --- a/druid-shell/src/platform/x11/window.rs +++ b/druid-shell/src/platform/x11/window.rs @@ -128,6 +128,14 @@ impl WindowBuilder { log::warn!("WindowBuilder::set_position is currently unimplemented for X11 platforms."); } + pub fn maximized(&self) { + log::warn!("WindowBuilder::maximized is currently unimplemented for X11 platforms."); + } + + pub fn minimized(&self) { + log::warn!("WindowBuilder::minimized is currently unimplemented for X11 platforms."); + } + pub fn set_title>(&mut self, title: S) { self.title = title.into(); } @@ -1273,6 +1281,18 @@ impl WindowHandle { Size::new(0.0, 0.0) } + pub fn maximize(&self) { + log::warn!("WindowHandle::maximize is currently unimplemented for X11 platforms."); + } + + pub fn minimize(&self) { + log::warn!("WindowHandle::minimize is currently unimplemented for X11 platforms."); + } + + pub fn handle_titlebar(&self, _val: bool) { + log::warn!("WindowHandle::handle_titlebar is currently unimplemented for X11 platforms."); + } + pub fn bring_to_front_and_focus(&self) { if let Some(w) = self.window.upgrade() { w.bring_to_front_and_focus(); diff --git a/druid-shell/src/screen.rs b/druid-shell/src/screen.rs index d27b05d5ac..226f19840f 100644 --- a/druid-shell/src/screen.rs +++ b/druid-shell/src/screen.rs @@ -19,6 +19,9 @@ use crate::platform; use std::fmt; use std::fmt::Display; +/// Monitor struct containing data about a monitor on the system +/// +/// Use Screen::get_monitors() to return a Vec of all the monitors on the system #[derive(Clone)] pub struct Monitor { primary: bool, @@ -32,6 +35,7 @@ pub struct Monitor { } impl Monitor { + #[allow(dead_code)] pub(crate) fn new(primary: bool, rect: Rect, work_rect: Rect) -> Self { Monitor { primary, @@ -39,22 +43,19 @@ impl Monitor { work_rect, } } - /// Returns true if the monitor is the primary monitor - /// a primary monitor has its top-left corner at (0,0) - /// in virtual screen coordinates. + /// Returns true if the monitor is the primary monitor. + /// The primary monitor has its origin at (0, 0) in virtual screen coordinates. pub fn is_primary(&self) -> bool { self.primary } - /// Returns a RECT of the monitor rectangle in virtual screen coordinates - /// meaning that it contains the monitors resolution - /// oriented around the origin point: (0,0) being the top-left corner - /// of the primary monitor, in pixels. + /// Returns the monitor rectangle in virtual screen coordinates. pub fn virtual_rect(&self) -> Rect { self.rect } - /// Returns a RECT of the monitor working rectangle in virtual screen coordinates - /// The RECT excludes area occupied by things like the dock,menubar (mac). taskbar (windows) + /// Returns the monitor working rectangle in virtual screen coordinates. + /// The working rectangle excludes certain things like the dock and menubar on mac, + /// and the taskbar on windows. pub fn virtual_work_rect(&self) -> Rect { self.work_rect } @@ -76,12 +77,17 @@ impl Display for Monitor { } } +/// Information about the screen and monitors pub struct Screen {} impl Screen { + /// Returns a vector of all the [`monitors`] on the system. + /// + /// [`monitors`]: struct.Monitor.html pub fn get_monitors() -> Vec { platform::screen::get_monitors() } + /// Returns the total virtual screen space on the system, in pixels. pub fn get_display_size() -> Size { platform::screen::get_display_size() } diff --git a/druid-shell/src/window.rs b/druid-shell/src/window.rs index 7e2f2d1ee0..a90c400b33 100644 --- a/druid-shell/src/window.rs +++ b/druid-shell/src/window.rs @@ -118,35 +118,74 @@ impl WindowHandle { self.0.resizable(resizable) } + /// Toggles window maximized. + /// Note this might not work the same on all platforms + /// Note that on windows, if resizable is set to false + /// This will cause the window to be maximized above the taskbar + pub fn maximize(&self) { + self.0.maximize(); + } + + /// Toggles window minimized. + /// Note this might not work the same on all platforms + pub fn minimize(&self) { + self.0.minimize(); + } + + /// Allows the operating system to handle a custom titlebar + /// like the default one + /// + /// It should be used on Event::MouseMove in a widget: + /// `Event::MouseMove(_) => { + /// if ctx.is_hot() { + /// ctx.window().handle_titlebar(true); + /// } else { + /// ctx.window().handle_titlebar(false); + /// } + ///}` + /// + /// This might not work or behave the same across all platforms + pub fn handle_titlebar(&self, val: bool) { + self.0.handle_titlebar(val); + } + /// Set whether the window should show titlebar pub fn show_titlebar(&self, show_titlebar: bool) { self.0.show_titlebar(show_titlebar) } - /// Sets the position of the window in virtual screen coordinates (Pixels) - /// [`Point`] Position in pixels + /// Sets the position of the window in virtual screen coordinates. + /// [`position`] The position in pixels. /// - /// [`Point`]: struct.Point.html + /// [`position`]: struct.Point.html pub fn set_position(&self, position: Point) { self.0.set_position(position) } - /// Returns [`Point`] Position in virtual screen coordinates (Pixels) + /// Returns the position in virtual screen coordinates. + /// [`Point`] The position in pixels. /// /// [`Point`]: struct.Point.html pub fn get_position(&self) -> Point { self.0.get_position() } - /// Set the size of the window in Pixels - /// [`Size`] Struct representing the Size + /// Set the window's size in [display points]. /// - /// [`Size`]: struct.Size.html + /// The actual window size in pixels will depend on the platform DPI settings. + /// + /// This should be considered a request to the platform to set the size of the window. + /// The platform might increase the size a tiny bit due to DPI. + /// To know the actual size of the window you should handle the [`WinHandler::size`] method. + /// + /// [`WinHandler::size`]: trait.WinHandler.html#method.size + /// [display points]: struct.Scale.html pub fn set_size(&self, size: Size) { self.0.set_size(size) } - /// returns [`Size`] containing the window size in pixels + /// Gets the window size. + /// [`Size`] Window size in pixels. /// /// [`Size`]: struct.Size.html pub fn get_size(&self) -> Size { @@ -289,17 +328,20 @@ impl WindowBuilder { self.0.set_min_size(size) } - /// Set whether the window should be resizable + /// Set whether the window should be resizable. pub fn resizable(&mut self, resizable: bool) { self.0.resizable(resizable) } - /// Set whether the window should have a titlebar and decorations + /// Set whether the window should have a titlebar and decorations. pub fn show_titlebar(&mut self, show_titlebar: bool) { self.0.show_titlebar(show_titlebar) } - /// Set the window's initial position. + /// Sets the initial window position in virtual screen coordinates. + /// [`position`] Position in pixels. + /// + /// [`position`]: struct.Point.html pub fn set_position(&mut self, position: Point) { self.0.set_position(position); } @@ -314,6 +356,16 @@ impl WindowBuilder { self.0.set_menu(menu.into_inner()) } + /// Creates the window maximized. + pub fn maximized(&mut self) { + self.0.maximized(); + } + + /// Creates the window minimized. + pub fn minimized(&mut self) { + self.0.minimized(); + } + /// Attempt to construct the platform window. /// /// If this fails, your application should exit. diff --git a/druid/src/app.rs b/druid/src/app.rs index 0d9cc04cda..52b226613e 100644 --- a/druid/src/app.rs +++ b/druid/src/app.rs @@ -48,6 +48,8 @@ pub struct WindowDesc { pub(crate) menu: Option>, pub(crate) resizable: bool, pub(crate) show_titlebar: bool, + pub(crate) maximized: bool, + pub(crate) minimized: bool, /// The `WindowId` that will be assigned to this window. /// /// This can be used to track a window from when it is launched and when @@ -168,6 +170,8 @@ impl WindowDesc { menu: MenuDesc::platform_default(), resizable: true, show_titlebar: true, + maximized: false, + minimized: false, id: WindowId::next(), } } @@ -226,25 +230,39 @@ impl WindowDesc { self } + /// Set whether the window should be resizable. pub fn resizable(mut self, resizable: bool) -> Self { self.resizable = resizable; self } + /// Set whether the window should have a titlebar and decorations. pub fn show_titlebar(mut self, show_titlebar: bool) -> Self { self.show_titlebar = show_titlebar; self } - /// Sets the initial window position in virtual screen coordinates - /// [`Point`] Position in pixels + /// Sets the initial window position in virtual screen coordinates. + /// [`position`] Position in pixels. /// - /// [`Point`]: struct.Point.html + /// [`position`]: struct.Point.html pub fn set_position(mut self, position: Point) -> Self { self.position = Some(position); self } + /// Creates the window maximized. + pub fn maximized(mut self) -> Self { + self.maximized = true; + self + } + + /// Creates the window minimized. + pub fn minimized(mut self) -> Self { + self.minimized = true; + self + } + /// Attempt to create a platform window from this `WindowDesc`. pub(crate) fn build_native( mut self, @@ -275,6 +293,14 @@ impl WindowDesc { builder.set_position(position); } + if self.maximized { + builder.maximized(); + } + + if self.minimized { + builder.minimized(); + } + builder.set_title(self.title.display_text()); if let Some(menu) = platform_menu { builder.set_menu(menu); diff --git a/druid/src/lib.rs b/druid/src/lib.rs index cf1bdfaf9f..d9430aac42 100644 --- a/druid/src/lib.rs +++ b/druid/src/lib.rs @@ -171,7 +171,8 @@ pub use shell::keyboard_types; pub use shell::{ Application, Clipboard, ClipboardFormat, Code, Cursor, Error as PlatformError, FileDialogOptions, FileInfo, FileSpec, FormatId, HotKey, KbKey, KeyEvent, Location, Modifiers, - MouseButton, MouseButtons, RawMods, Scalable, Scale, Screen, SysMods, Text, TimerToken, WindowHandle, + Monitor, MouseButton, MouseButtons, RawMods, Scalable, Scale, Screen, SysMods, Text, TimerToken, + WindowHandle, }; pub use crate::core::WidgetPod; From e6aa4d45871fc51071e59d88bb31e63f32338ea6 Mon Sep 17 00:00:00 2001 From: rhzk Date: Fri, 17 Jul 2020 01:57:57 +0200 Subject: [PATCH 04/18] Better DPI support on windows --- CHANGELOG.md | 1 + .../src/platform/windows/application.rs | 7 +-- druid-shell/src/platform/windows/mod.rs | 17 +---- druid-shell/src/platform/windows/paint.rs | 16 ++++- druid-shell/src/platform/windows/util.rs | 18 ++++-- druid-shell/src/platform/windows/window.rs | 62 +++++++++++++++++-- 6 files changed, 91 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f890de10c4..27a39f81d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ You can find its changes [documented below](#060---2020-06-01). ### Changed +- Windows: Improved DPI handling. Druid should now redraw correctly when dpi changes. ([#1037] by [@rhzk]) - winOS: Window created with OS default size if not set. ([#1037] by [@rhzk]) - `Image` and `ImageData` exported by default. ([#1011] by [@covercash2]) - `Scale::from_scale` to `Scale::new`, and `Scale` methods `scale_x` / `scale_y` to `x` / `y`. ([#1042] by [@xStrom]) diff --git a/druid-shell/src/platform/windows/application.rs b/druid-shell/src/platform/windows/application.rs index 3de2a8faa6..4a6fec623b 100644 --- a/druid-shell/src/platform/windows/application.rs +++ b/druid-shell/src/platform/windows/application.rs @@ -22,10 +22,9 @@ use std::rc::Rc; use winapi::shared::minwindef::{FALSE, HINSTANCE}; use winapi::shared::ntdef::LPCWSTR; -use winapi::shared::windef::{HCURSOR, HWND}; +use winapi::shared::windef::{HCURSOR, HWND, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2}; use winapi::shared::winerror::HRESULT_FROM_WIN32; use winapi::um::errhandlingapi::GetLastError; -use winapi::um::shellscalingapi::PROCESS_SYSTEM_DPI_AWARE; use winapi::um::winuser::{ DispatchMessageW, GetAncestor, GetMessageW, LoadIconW, PostMessageW, PostQuitMessage, RegisterClassW, TranslateAcceleratorW, TranslateMessage, GA_ROOT, IDI_APPLICATION, MSG, @@ -64,10 +63,10 @@ impl Application { fn init() -> Result<(), Error> { // TODO: Report back an error instead of panicking util::attach_console(); - if let Some(func) = OPTIONAL_FUNCTIONS.SetProcessDpiAwareness { + if let Some(func) = OPTIONAL_FUNCTIONS.SetProcessDpiAwarenessContext { // This function is only supported on windows 10 unsafe { - func(PROCESS_SYSTEM_DPI_AWARE); // TODO: per monitor (much harder) + func(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); } } unsafe { diff --git a/druid-shell/src/platform/windows/mod.rs b/druid-shell/src/platform/windows/mod.rs index 1d17d3f3a1..e3df2f2133 100644 --- a/druid-shell/src/platform/windows/mod.rs +++ b/druid-shell/src/platform/windows/mod.rs @@ -54,7 +54,6 @@ use winapi::um::d2d1::{ ID2D1HwndRenderTarget, ID2D1RenderTarget, D2D1_HWND_RENDER_TARGET_PROPERTIES, D2D1_RENDER_TARGET_PROPERTIES, D2D1_SIZE_U, }; -use winapi::um::dcommon::D2D1_PIXEL_FORMAT; use wio::com::ComPtr; #[derive(Clone)] @@ -68,11 +67,12 @@ impl HwndRenderTarget { hwnd: HWND, width: u32, height: u32, + rt_props: D2D1_RENDER_TARGET_PROPERTIES, ) -> Result { // hardcode // - RenderTargetType::Default // - AlphaMode::Unknown - let rt_props = DEFAULT_PROPS; + //let rt_props = DEFAULT_PROPS; let mut hwnd_props = DEFAULT_HWND_PROPS; hwnd_props.hwnd = hwnd; @@ -113,19 +113,6 @@ impl HwndRenderTarget { } } -// props for creating hwnd render target -const DEFAULT_PROPS: D2D1_RENDER_TARGET_PROPERTIES = D2D1_RENDER_TARGET_PROPERTIES { - _type: 0u32, //RenderTargetType::Default - pixelFormat: D2D1_PIXEL_FORMAT { - format: 87u32, //Format::B8G8R8A8Unorm, see https://docs.rs/dxgi/0.3.0-alpha4/src/dxgi/enums/format.rs.html#631 - alphaMode: 0u32, //AlphaMode::Unknown - }, - dpiX: 0.0, - dpiY: 0.0, - usage: 0, - minLevel: 0, -}; - const DEFAULT_HWND_PROPS: D2D1_HWND_RENDER_TARGET_PROPERTIES = D2D1_HWND_RENDER_TARGET_PROPERTIES { hwnd: std::ptr::null_mut(), pixelSize: D2D1_SIZE_U { diff --git a/druid-shell/src/platform/windows/paint.rs b/druid-shell/src/platform/windows/paint.rs index 6fc165c7c1..66234df524 100644 --- a/druid-shell/src/platform/windows/paint.rs +++ b/druid-shell/src/platform/windows/paint.rs @@ -45,6 +45,7 @@ use super::window::SCALE_TARGET_DPI; pub(crate) unsafe fn create_render_target( d2d_factory: &D2DFactory, hwnd: HWND, + scale: Scale, ) -> Result { let mut rect: RECT = mem::zeroed(); if GetClientRect(hwnd, &mut rect) == 0 { @@ -53,7 +54,20 @@ pub(crate) unsafe fn create_render_target( } else { let width = (rect.right - rect.left) as u32; let height = (rect.bottom - rect.top) as u32; - let res = HwndRenderTarget::create(d2d_factory, hwnd, width, height); + + let props = D2D1_RENDER_TARGET_PROPERTIES { + _type: D2D1_RENDER_TARGET_TYPE_DEFAULT, + pixelFormat: D2D1_PIXEL_FORMAT { + format: DXGI_FORMAT_B8G8R8A8_UNORM, + alphaMode: D2D1_ALPHA_MODE_IGNORE, + }, + dpiX: (scale.x() * SCALE_TARGET_DPI) as f32, + dpiY: (scale.y() * SCALE_TARGET_DPI) as f32, + usage: D2D1_RENDER_TARGET_USAGE_NONE, + minLevel: D2D1_FEATURE_LEVEL_DEFAULT, + }; + + let res = HwndRenderTarget::create(d2d_factory, hwnd, width, height, props); if let Err(ref e) = res { log::error!("Creating hwnd render target failed: {:?}", e); diff --git a/druid-shell/src/platform/windows/util.rs b/druid-shell/src/platform/windows/util.rs index 9b0237755e..df9692b90d 100644 --- a/druid-shell/src/platform/windows/util.rs +++ b/druid-shell/src/platform/windows/util.rs @@ -26,9 +26,9 @@ use std::slice; use lazy_static::lazy_static; use winapi::ctypes::c_void; use winapi::shared::guiddef::REFIID; -use winapi::shared::minwindef::{HMODULE, UINT}; +use winapi::shared::minwindef::{HMODULE, UINT, BOOL}; use winapi::shared::ntdef::{HRESULT, LPWSTR}; -use winapi::shared::windef::{HMONITOR, RECT}; +use winapi::shared::windef::{HMONITOR, RECT, HWND}; use winapi::shared::winerror::SUCCEEDED; use winapi::um::fileapi::{CreateFileA, GetFileType, OPEN_EXISTING}; use winapi::um::handleapi::INVALID_HANDLE_VALUE; @@ -128,10 +128,12 @@ pub(crate) fn recti_to_rect(rect: RECT) -> Rect { } // Types for functions we want to load, which are only supported on newer windows versions -// from shcore.dll +// from user32.dll type GetDpiForSystem = unsafe extern "system" fn() -> UINT; +type GetDpiForWindow = unsafe extern "system" fn(HWND) -> UINT; +type SetProcessDpiAwarenessContext = unsafe extern "system" fn(winapi::shared::windef::DPI_AWARENESS_CONTEXT) -> BOOL; +// from shcore.dll type GetDpiForMonitor = unsafe extern "system" fn(HMONITOR, MONITOR_DPI_TYPE, *mut UINT, *mut UINT); -// from user32.dll type SetProcessDpiAwareness = unsafe extern "system" fn(PROCESS_DPI_AWARENESS) -> HRESULT; type DCompositionCreateDevice2 = unsafe extern "system" fn( renderingDevice: *const IUnknown, @@ -144,6 +146,8 @@ type CreateDXGIFactory2 = #[allow(non_snake_case)] // For member fields pub struct OptionalFunctions { pub GetDpiForSystem: Option, + pub GetDpiForWindow: Option, + pub SetProcessDpiAwarenessContext: Option, pub GetDpiForMonitor: Option, pub SetProcessDpiAwareness: Option, pub DCompositionCreateDevice2: Option, @@ -198,6 +202,8 @@ fn load_optional_functions() -> OptionalFunctions { let mut GetDpiForSystem = None; let mut GetDpiForMonitor = None; + let mut GetDpiForWindow = None; + let mut SetProcessDpiAwarenessContext = None; let mut SetProcessDpiAwareness = None; let mut DCompositionCreateDevice2 = None; let mut CreateDXGIFactory2 = None; @@ -213,6 +219,8 @@ fn load_optional_functions() -> OptionalFunctions { log::info!("No user32.dll"); } else { load_function!(user32, GetDpiForSystem, "10"); + load_function!(user32, GetDpiForWindow, "10"); + load_function!(user32, SetProcessDpiAwarenessContext, "10"); } if !dcomp.is_null() { @@ -225,6 +233,8 @@ fn load_optional_functions() -> OptionalFunctions { OptionalFunctions { GetDpiForSystem, + GetDpiForWindow, + SetProcessDpiAwarenessContext, GetDpiForMonitor, SetProcessDpiAwareness, DCompositionCreateDevice2, diff --git a/druid-shell/src/platform/windows/window.rs b/druid-shell/src/platform/windows/window.rs index cff9e2253e..af2601a4ff 100644 --- a/druid-shell/src/platform/windows/window.rs +++ b/druid-shell/src/platform/windows/window.rs @@ -373,6 +373,10 @@ impl MyWndProc { self.with_window_state(|state| state.scale.get()) } + fn set_scale(&self, scale: Scale) { + self.with_window_state(move |state| state.scale.set(scale)) + } + fn area(&self) -> ScaledArea { self.with_window_state(|state| state.area.get()) } @@ -537,8 +541,9 @@ impl WndProc for MyWndProc { }) }; if dcomp_state.is_none() { + let scale = self.scale(); unsafe { - let rt = paint::create_render_target(&self.d2d_factory, hwnd); + let rt = paint::create_render_target(&self.d2d_factory, hwnd, scale); state.render_target = rt.ok(); } } @@ -575,8 +580,9 @@ impl WndProc for MyWndProc { let mut rect: RECT = mem::zeroed(); GetUpdateRect(hwnd, &mut rect, FALSE); let s = s.as_mut().unwrap(); + let scale = self.scale(); if s.render_target.is_none() { - let rt = paint::create_render_target(&self.d2d_factory, hwnd); + let rt = paint::create_render_target(&self.d2d_factory, hwnd, scale); s.render_target = rt.ok(); } s.handler.rebuild_resources(); @@ -605,9 +611,52 @@ impl WndProc for MyWndProc { } Some(0) }, + WM_DPICHANGED => unsafe { + let x = HIWORD(wparam as u32) as f64 / SCALE_TARGET_DPI; + let y = LOWORD(wparam as u32) as f64 / SCALE_TARGET_DPI; + let scale = Scale::new(x, y); + self.set_scale(scale); + let rect: *mut RECT = lparam as *mut RECT; + SetWindowPos(hwnd, HWND_TOPMOST, (*rect).left, (*rect).top, (*rect).right - (*rect).left, (*rect).bottom - (*rect).top, SWP_NOZORDER | SWP_FRAMECHANGED | SWP_DRAWFRAME); + if let Ok(mut s) = self.state.try_borrow_mut() { + let s = s.as_mut().unwrap(); + if s.dcomp_state.is_some() { + let scale = self.scale(); + let rt = paint::create_render_target(&self.d2d_factory, hwnd, scale); + s.render_target = rt.ok(); + { + let rect_dp = self.area().size_dp().to_rect(); + s.handler.rebuild_resources(); + s.render( + &self.d2d_factory, + &self.dwrite_factory, + &self.handle, + rect_dp, + ); + } + + if let Some(ref mut ds) = s.dcomp_state { + let _ = ds.dcomp_target.clear_root(); + let _ = ds.dcomp_device.commit(); + ds.sizing = true; + } + } + } else { + self.log_dropped_msg(hwnd, msg, wparam, lparam); + } + Some(0) + }, WM_NCCALCSIZE => unsafe { // Hack to get rid of caption but keeping the borders created by it. if !self.has_titlebar() { + let scale_factor = if let Some(func) = OPTIONAL_FUNCTIONS.GetDpiForWindow { + // Only supported on Windows 10 + func(hwnd) as f64 / SCALE_TARGET_DPI + } else { + // TODO GetDpiForMonitor is supported on Windows 8.1, try falling back to that here + // Probably GetDeviceCaps(..., LOGPIXELSX) is the best to do pre-10 + 1.0 + }; let style = GetWindowLongPtrW(hwnd, GWL_STYLE) as u32; if style == 0 { warn!( @@ -616,12 +665,12 @@ impl WndProc for MyWndProc { ); return Some(0); } - + let s: *mut NCCALCSIZE_PARAMS = lparam as *mut NCCALCSIZE_PARAMS; if let Some(mut s) = s.as_mut() { - s.rgrc[0].top -= 31; + s.rgrc[0].top -= (31.0 * scale_factor) as i32; if (style & WS_MAXIMIZE) != 0 { - s.rgrc[0].top += 7; + s.rgrc[0].top += (7.0 * scale_factor) as i32; } } } @@ -659,7 +708,8 @@ impl WndProc for MyWndProc { if let Ok(mut s) = self.state.try_borrow_mut() { let s = s.as_mut().unwrap(); if s.dcomp_state.is_some() { - let rt = paint::create_render_target(&self.d2d_factory, hwnd); + let scale = self.scale(); + let rt = paint::create_render_target(&self.d2d_factory, hwnd, scale); s.render_target = rt.ok(); { let rect_dp = self.area().size_dp().to_rect(); From 98e823d4a52926c2bd8349208f56330c424244aa Mon Sep 17 00:00:00 2001 From: rhzk Date: Sat, 18 Jul 2020 05:17:46 +0200 Subject: [PATCH 05/18] Prevent warnings, cleanup --- druid-shell/src/platform/windows/mod.rs | 4 -- druid-shell/src/platform/windows/window.rs | 58 +++++++++++----------- druid/src/lib.rs | 4 +- 3 files changed, 30 insertions(+), 36 deletions(-) diff --git a/druid-shell/src/platform/windows/mod.rs b/druid-shell/src/platform/windows/mod.rs index e3df2f2133..bb361ad061 100644 --- a/druid-shell/src/platform/windows/mod.rs +++ b/druid-shell/src/platform/windows/mod.rs @@ -69,10 +69,6 @@ impl HwndRenderTarget { height: u32, rt_props: D2D1_RENDER_TARGET_PROPERTIES, ) -> Result { - // hardcode - // - RenderTargetType::Default - // - AlphaMode::Unknown - //let rt_props = DEFAULT_PROPS; let mut hwnd_props = DEFAULT_HWND_PROPS; hwnd_props.hwnd = hwnd; diff --git a/druid-shell/src/platform/windows/window.rs b/druid-shell/src/platform/windows/window.rs index af2601a4ff..6b082480ad 100644 --- a/druid-shell/src/platform/windows/window.rs +++ b/druid-shell/src/platform/windows/window.rs @@ -236,7 +236,7 @@ pub(crate) const DS_REQUEST_DESTROY: UINT = WM_USER + 1; /// Message relaying a request to handle dropped messages. /// /// Rust borrow checker causes messages to be dropped -/// so as a temporary? solution we place them in a queue and handle them again +/// so instead we place them in a queue and run them when the borrow is released. pub(crate) const DS_HANDLE_DROPPED: UINT = WM_USER + 2; impl Default for PresentStrategy { @@ -555,13 +555,15 @@ impl WndProc for MyWndProc { Some(0) } WM_ACTIVATE => { - unsafe { - if SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOSIZE) == 0 { - warn!( - "failed to update window style: {}", - Error::Hr(HRESULT_FROM_WIN32(GetLastError())) - ); - }; + if LOWORD(wparam as u32) as u32 != 0 { + unsafe { + if SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOSIZE) == 0 { + warn!( + "failed to update window style: {}", + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) + ); + }; + } } Some(0) } @@ -649,14 +651,6 @@ impl WndProc for MyWndProc { WM_NCCALCSIZE => unsafe { // Hack to get rid of caption but keeping the borders created by it. if !self.has_titlebar() { - let scale_factor = if let Some(func) = OPTIONAL_FUNCTIONS.GetDpiForWindow { - // Only supported on Windows 10 - func(hwnd) as f64 / SCALE_TARGET_DPI - } else { - // TODO GetDpiForMonitor is supported on Windows 8.1, try falling back to that here - // Probably GetDeviceCaps(..., LOGPIXELSX) is the best to do pre-10 - 1.0 - }; let style = GetWindowLongPtrW(hwnd, GWL_STYLE) as u32; if style == 0 { warn!( @@ -668,9 +662,9 @@ impl WndProc for MyWndProc { let s: *mut NCCALCSIZE_PARAMS = lparam as *mut NCCALCSIZE_PARAMS; if let Some(mut s) = s.as_mut() { - s.rgrc[0].top -= (31.0 * scale_factor) as i32; + s.rgrc[0].top -= (31.0 * self.scale().x()) as i32; if (style & WS_MAXIMIZE) != 0 { - s.rgrc[0].top += (7.0 * scale_factor) as i32; + s.rgrc[0].top += (7.0 * self.scale().x()) as i32; } } } @@ -687,19 +681,20 @@ impl WndProc for MyWndProc { Error::Hr(HRESULT_FROM_WIN32(GetLastError())) ); }; - let a = HIWORD(lparam as u32) as i32 - rect.top; + let a = HIWORD(lparam as u32) as i16 as i32 - rect.top; if (a == 0) && (hit != HTTOPLEFT) && (hit != HTTOPRIGHT) && self.resizable() { hit = HTTOP; - } else { - let mouseDown = GetAsyncKeyState(VK_LBUTTON) < 0; + } + } + if hit != HTTOP { + let mouseDown = GetAsyncKeyState(VK_LBUTTON) < 0; - if self.with_window_state(|state| state.handle_titlebar.get()) && !mouseDown { - self.with_window_state(move |state| state.handle_titlebar.set(false)); - }; - - if self.with_window_state(|state| state.handle_titlebar.get()) && hit == HTCLIENT { - hit = HTCAPTION; - } + if self.with_window_state(|state| state.handle_titlebar.get()) && !mouseDown { + self.with_window_state(move |state| state.handle_titlebar.set(false)); + }; + + if self.with_window_state(|state| state.handle_titlebar.get()) && hit == HTCLIENT { + hit = HTCAPTION; } } Some(hit) @@ -776,10 +771,13 @@ impl WndProc for MyWndProc { None }, WM_SIZE => unsafe { + let width = LOWORD(lparam as u32) as u32; + let height = HIWORD(lparam as u32) as u32; + if width == 0 || height == 0 { + return Some(0); + } if let Ok(mut s) = self.state.try_borrow_mut() { let s = s.as_mut().unwrap(); - let width = LOWORD(lparam as u32) as u32; - let height = HIWORD(lparam as u32) as u32; let scale = self.scale(); let area = ScaledArea::from_px((width as f64, height as f64), scale); let size_dp = area.size_dp(); diff --git a/druid/src/lib.rs b/druid/src/lib.rs index d9430aac42..472cf15ff6 100644 --- a/druid/src/lib.rs +++ b/druid/src/lib.rs @@ -171,8 +171,8 @@ pub use shell::keyboard_types; pub use shell::{ Application, Clipboard, ClipboardFormat, Code, Cursor, Error as PlatformError, FileDialogOptions, FileInfo, FileSpec, FormatId, HotKey, KbKey, KeyEvent, Location, Modifiers, - Monitor, MouseButton, MouseButtons, RawMods, Scalable, Scale, Screen, SysMods, Text, TimerToken, - WindowHandle, + Monitor, MouseButton, MouseButtons, RawMods, Scalable, Scale, Screen, SysMods, Text, + TimerToken, WindowHandle, }; pub use crate::core::WidgetPod; From 22b755d230f5aa1db039c34eec7fe23f83092289 Mon Sep 17 00:00:00 2001 From: rhzk Date: Sat, 18 Jul 2020 06:40:56 +0200 Subject: [PATCH 06/18] Cargo Clippy --- druid-shell/src/dialog.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/druid-shell/src/dialog.rs b/druid-shell/src/dialog.rs index e2c4c11d60..f3876e846f 100644 --- a/druid-shell/src/dialog.rs +++ b/druid-shell/src/dialog.rs @@ -32,6 +32,7 @@ pub enum FileDialogType { } /// Options for file dialogs. +#[non_exhaustive] #[derive(Debug, Clone, Default)] pub struct FileDialogOptions { pub show_hidden: bool, @@ -39,8 +40,6 @@ pub struct FileDialogOptions { pub default_type: Option, pub select_directories: bool, pub multi_selection: bool, - // we don't want a library user to be able to construct this type directly - __non_exhaustive: (), } /// A description of a filetype, for specifiying allowed types in a file dialog. From 3d52493fa8ab810f8001ac5ce233b57668708be7 Mon Sep 17 00:00:00 2001 From: rhzk Date: Sat, 15 Aug 2020 03:05:46 +0200 Subject: [PATCH 07/18] Added window state instead of maximize,minimize --- CHANGELOG.md | 4 +- druid-shell/src/lib.rs | 2 +- druid-shell/src/platform/gtk/screen.rs | 6 -- druid-shell/src/platform/gtk/window.rs | 19 +++-- druid-shell/src/platform/mac/screen.rs | 6 -- druid-shell/src/platform/mac/window.rs | 19 +++-- druid-shell/src/platform/web/screen.rs | 6 -- druid-shell/src/platform/web/window.rs | 17 ++-- druid-shell/src/platform/windows/screen.rs | 15 +--- druid-shell/src/platform/windows/window.rs | 93 +++++++++------------- druid-shell/src/platform/x11/screen.rs | 6 -- druid-shell/src/platform/x11/window.rs | 18 ++--- druid-shell/src/screen.rs | 11 ++- druid-shell/src/window.rs | 47 ++++++----- druid/src/app.rs | 28 ++----- druid/src/lib.rs | 2 +- 16 files changed, 118 insertions(+), 181 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27a39f81d9..e71ae72af1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,8 @@ You can find its changes [documented below](#060---2020-06-01). - Windows: Added Screen module to get information about monitors and the screen. ([#1037] by [@rhzk]) - Added documentation to resizable() and show_titlebar() in WindowDesc. ([#1037] by [@rhzk]) - Windows: Added internal functions to handle Re-entrancy. ([#1037] by [@rhzk]) -- Windows: WindowDesc: Create window with disabled titlebar, maximized or minimized and with position. ([#1037] by [@rhzk]) -- Windows: WindowHandle: Toggle maximize, minimize window. Toggle titlebar. Change size and position of window. ([#1037] by [@rhzk]) +- Windows: WindowDesc: Create window with disabled titlebar, maximized or minimized state, and with position. ([#1037] by [@rhzk]) +- Windows: WindowHandle: Change window state. Toggle titlebar. Change size and position of window. ([#1037] by [@rhzk]) - Windows: WindowHandle: Added handle_titlebar(), Allowing a custom titlebar to behave like the OS one. ([#1037] by [@rhzk]) - Added ctrl/shift key support to textbox. ([#1063] by [@vkahl]) diff --git a/druid-shell/src/lib.rs b/druid-shell/src/lib.rs index d588b94682..33a2d8b8c0 100644 --- a/druid-shell/src/lib.rs +++ b/druid-shell/src/lib.rs @@ -63,7 +63,7 @@ pub use scale::{Scalable, Scale, ScaledArea}; pub use screen::Monitor; pub use screen::Screen; pub use window::{ - IdleHandle, IdleToken, Text, TimerToken, WinHandler, WindowBuilder, WindowHandle, + IdleHandle, IdleToken, Text, TimerToken, WinHandler, WindowBuilder, WindowHandle, WindowState, }; pub use keyboard_types; diff --git a/druid-shell/src/platform/gtk/screen.rs b/druid-shell/src/platform/gtk/screen.rs index 24f629f378..7323db771b 100644 --- a/druid-shell/src/platform/gtk/screen.rs +++ b/druid-shell/src/platform/gtk/screen.rs @@ -15,12 +15,6 @@ //! GTK Monitors and Screen information. use crate::screen::Monitor; -use crate::kurbo::Size; - -pub(crate) fn get_display_size() -> Size { - log::warn!("Screen::get_display_size() is currently unimplemented for gtk."); - Size::new(0.0, 0.0) -} pub(crate) fn get_monitors() -> Vec { log::warn!("Screen::get_monitors() is currently unimplemented for gtk."); diff --git a/druid-shell/src/platform/gtk/window.rs b/druid-shell/src/platform/gtk/window.rs index bd4f29d2b8..c0584a4664 100644 --- a/druid-shell/src/platform/gtk/window.rs +++ b/druid-shell/src/platform/gtk/window.rs @@ -48,6 +48,8 @@ use super::keycodes; use super::menu::Menu; use super::util; +use crate::window::WindowState as WindowSizeState; // Avoid name conflict. + /// The platform target DPI. /// /// GTK considers 96 the default value which represents a 1.0 scale factor. @@ -160,12 +162,8 @@ impl WindowBuilder { log::warn!("WindowBuilder::set_position is currently unimplemented for gtk."); } - pub fn maximized(&self) { - log::warn!("WindowBuilder::maximized is currently unimplemented for gtk."); - } - - pub fn minimized(&self) { - log::warn!("WindowBuilder::minimized is currently unimplemented for gtk."); + pub fn set_window_state(&self, _state: WindowSizeState) { + log::warn!("WindowBuilder::set_window_state is currently unimplemented for gtk."); } pub fn set_title(&mut self, title: impl Into) { @@ -583,12 +581,13 @@ impl WindowHandle { Size::new(0.0, 0.0) } - pub fn maximize(&self) { - log::warn!("WindowHandle::maximize is currently unimplemented for gtk."); + pub fn set_window_state(&self, _state: WindowSizeState) { + log::warn!("WindowHandle::set_window_state is currently unimplemented for gtk."); } - pub fn minimize(&self) { - log::warn!("WindowHandle::minimize is currently unimplemented for gtk."); + pub fn get_window_state(&self) -> WindowSizeState { + log::warn!("WindowHandle::get_window_state is currently unimplemented for gtk."); + WindowSizeState::RESTORED } pub fn handle_titlebar(&self, _val: bool) { diff --git a/druid-shell/src/platform/mac/screen.rs b/druid-shell/src/platform/mac/screen.rs index 81e0a08ad3..7786f85d0a 100644 --- a/druid-shell/src/platform/mac/screen.rs +++ b/druid-shell/src/platform/mac/screen.rs @@ -15,12 +15,6 @@ //! macOS Monitors and Screen information. use crate::screen::Monitor; -use crate::kurbo::Size; - -pub(crate) fn get_display_size() -> Size { - log::warn!("Screen::get_display_size() is currently unimplemented for Mac."); - Size::new(0.0, 0.0) -} pub(crate) fn get_monitors() -> Vec { log::warn!("Screen::get_monitors() is currently unimplemented for Mac."); diff --git a/druid-shell/src/platform/mac/window.rs b/druid-shell/src/platform/mac/window.rs index 291a0e866f..b5f3b76380 100644 --- a/druid-shell/src/platform/mac/window.rs +++ b/druid-shell/src/platform/mac/window.rs @@ -58,6 +58,8 @@ use crate::scale::Scale; use crate::window::{IdleToken, Text, TimerToken, WinHandler}; use crate::Error; +use crate::window::WindowState as WindowSizeState; // Avoid name conflict. + #[allow(non_upper_case_globals)] const NSWindowDidBecomeKeyNotification: &str = "NSWindowDidBecomeKeyNotification"; @@ -151,12 +153,8 @@ impl WindowBuilder { log::warn!("WindowBuilder::set_position is currently unimplemented for mac platforms."); } - pub fn maximized(&self) { - log::warn!("WindowBuilder::maximized is currently unimplemented for mac platforms."); - } - - pub fn minimized(&self) { - log::warn!("WindowBuilder::minimized is currently unimplemented for mac platforms."); + pub fn set_window_state(&self, _state: WindowSizeState) { + log::warn!("WindowBuilder::set_window_state is currently unimplemented for mac platforms."); } pub fn set_title(&mut self, title: impl Into) { @@ -868,12 +866,13 @@ impl WindowHandle { Size::new(0.0, 0.0) } - pub fn maximize(&self) { - log::warn!("WindowHandle::maximize is currently unimplemented for Mac."); + pub fn set_window_state(&self, _state: WindowSizeState) { + log::warn!("WindowHandle::set_window_state is currently unimplemented for Mac."); } - pub fn minimize(&self) { - log::warn!("WindowHandle::minimize is currently unimplemented for Mac."); + pub fn get_window_state(&self) -> WindowSizeState { + log::warn!("WindowHandle::get_window_state is currently unimplemented for Mac."); + WindowSizeState::RESTORED } pub fn handle_titlebar(&self, _val: bool) { diff --git a/druid-shell/src/platform/web/screen.rs b/druid-shell/src/platform/web/screen.rs index 83142774d6..34788bae5c 100644 --- a/druid-shell/src/platform/web/screen.rs +++ b/druid-shell/src/platform/web/screen.rs @@ -15,12 +15,6 @@ //! Monitor and Screen information ignored for web. use crate::screen::Monitor; -use crate::kurbo::Size; - -pub(crate) fn get_display_size() -> Size { - log::warn!("Screen::get_display_size() is not implemented for web."); - Size::new(0.0, 0.0) -} pub(crate) fn get_monitors() -> Vec { log::warn!("Screen::get_monitors() is not implemented for web."); diff --git a/druid-shell/src/platform/web/window.rs b/druid-shell/src/platform/web/window.rs index db1b7927f3..1758e976a5 100644 --- a/druid-shell/src/platform/web/window.rs +++ b/druid-shell/src/platform/web/window.rs @@ -42,6 +42,8 @@ use crate::keyboard::{KbKey, KeyState, Modifiers}; use crate::mouse::{Cursor, MouseButton, MouseButtons, MouseEvent}; use crate::window::{IdleToken, Text, TimerToken, WinHandler}; +use crate::window::WindowState as WindowSizeState; // Avoid name conflict. + // This is a macro instead of a function since KeyboardEvent and MouseEvent has identical functions // to query modifier key states. macro_rules! get_modifiers { @@ -349,11 +351,7 @@ impl WindowBuilder { // Ignored } - pub fn maximized(&self) { - // Ignored - } - - pub fn minimized(&self) { + pub fn set_window_state(&self, _state: WindowSizeState) { // Ignored } @@ -460,12 +458,13 @@ impl WindowHandle { Size::new(0.0, 0.0) } - pub fn maximize(&self) { - log::warn!("WindowHandle::maximize unimplemented for web."); + pub fn set_window_state(&self, _state: WindowSizeState) { + log::warn!("WindowHandle::set_window_state unimplemented for web."); } - pub fn minimize(&self) { - log::warn!("WindowHandle::minimize unimplemented for web."); + pub fn get_window_state(&self) -> WindowSizeState { + log::warn!("WindowHandle::get_window_state unimplemented for web."); + WindowSizeState::RESTORED } pub fn handle_titlebar(&self, _val: bool) { diff --git a/druid-shell/src/platform/windows/screen.rs b/druid-shell/src/platform/windows/screen.rs index 839343bc8a..b29b7f84da 100644 --- a/druid-shell/src/platform/windows/screen.rs +++ b/druid-shell/src/platform/windows/screen.rs @@ -25,20 +25,7 @@ use std::ptr::null_mut; use std::mem::size_of; use crate::screen::Monitor; -use crate::kurbo::{Rect, Size}; - -pub(crate) fn get_display_size() -> Size { - unsafe { - let width = GetSystemMetrics(SM_CXVIRTUALSCREEN) as f64; - let height = GetSystemMetrics(SM_CYVIRTUALSCREEN) as f64; - if width == 0.0 || height == 0.0 { - warn!( - "Failed to get display size" - ); - } - Size::new(width, height) - } -} +use crate::kurbo::Rect; static mut MONITORS : Vec = Vec::new(); diff --git a/druid-shell/src/platform/windows/window.rs b/druid-shell/src/platform/windows/window.rs index 6b082480ad..bc0032f1af 100644 --- a/druid-shell/src/platform/windows/window.rs +++ b/druid-shell/src/platform/windows/window.rs @@ -65,6 +65,8 @@ use crate::mouse::{Cursor, MouseButton, MouseButtons, MouseEvent}; use crate::scale::{Scale, Scalable, ScaledArea}; use crate::window::{IdleToken, Text, TimerToken, WinHandler}; +use crate::window::WindowState as WindowSizeState; // Avoid name conflict. + /// The platform target DPI. /// /// Windows considers 96 the default value which represents a 1.0 scale factor. @@ -86,8 +88,7 @@ pub(crate) struct WindowBuilder { size: Size, min_size: Option, position: Point, - maximized: bool, - minimized: bool, + state: WindowSizeState, } #[derive(Clone, Copy, PartialEq, Eq, Debug)] @@ -124,11 +125,6 @@ pub struct WindowHandle { state: Weak, } -enum WindowSizeState { - Maximize, - Minimize, -} - enum BlockingOp { SetPosition(Point), SetSize(Size), @@ -462,29 +458,16 @@ impl MyWndProc { }, BlockingOp::SetWindowSizeState(val) => { unsafe { - let style = GetWindowLongPtrW(hwnd, GWL_STYLE) as u32; - if style == 0 { - warn!( - "failed to get window style: {}", - Error::Hr(HRESULT_FROM_WIN32(GetLastError())) - ); - return; - } let s = match val { - WindowSizeState::Maximize => { - if (style & WS_MAXIMIZE) != 0 { - SW_RESTORE - } else { - SW_MAXIMIZE - } + WindowSizeState::MAXIMIZED => { + SW_MAXIMIZE }, - WindowSizeState::Minimize => { - if (style & WS_MINIMIZE) != 0 { - SW_RESTORE - } else { - SW_MINIMIZE - } + WindowSizeState::MINIMIZED => { + SW_MINIMIZE }, + WindowSizeState::RESTORED => { + SW_RESTORE + } }; ShowWindow(hwnd,s); } @@ -1145,8 +1128,7 @@ impl WindowBuilder { size: Size::new(CW_USEDEFAULT as f64, CW_USEDEFAULT as f64), min_size: None, position: Point::new(CW_USEDEFAULT as f64, CW_USEDEFAULT as f64), - maximized: false, - minimized: false, + state: WindowSizeState::RESTORED, } } @@ -1184,14 +1166,8 @@ impl WindowBuilder { self.position = position; } - /// Creates the window maximized. - pub fn maximized(&mut self) { - self.maximized = true; - } - - /// Creates the window minimized. - pub fn minimized(&mut self) { - self.minimized = true; + pub fn set_window_state(&mut self, state: WindowSizeState) { + self.state = state; } pub fn build(self) -> Result { @@ -1294,13 +1270,7 @@ impl WindowBuilder { register_accel(hwnd, &accels); } - if self.maximized && !self.minimized { - handle.maximize(); - } - - if self.minimized && !self.maximized { - handle.minimize(); - } + handle.set_window_state(self.state); Ok(handle) } @@ -1653,11 +1623,11 @@ impl WindowHandle { } } - // Sets the window as maximized if it is not, restores it if it was. - pub fn maximize(&self) { + // Sets the window state. + pub fn set_window_state(&self, state : WindowSizeState) { if let Some(w) = self.state.upgrade() { if let Ok(mut q) = w.blocked_queue.try_borrow_mut() { - q.push(BlockingOp::SetWindowSizeState(WindowSizeState::Maximize)) + q.push(BlockingOp::SetWindowSizeState(state)) } else { warn!( "failed to borrow blocked queue" @@ -1666,16 +1636,29 @@ impl WindowHandle { } } - // Sets the window as minimized if it is not, restores it if it was. - pub fn minimize(&self) { + // Gets the window state. + pub fn get_window_state(&self) -> WindowSizeState { + // We cant store state internally because it could be modified externaly. if let Some(w) = self.state.upgrade() { - if let Ok(mut q) = w.blocked_queue.try_borrow_mut() { - q.push(BlockingOp::SetWindowSizeState(WindowSizeState::Minimize)) - } else { - warn!( - "failed to borrow blocked queue" - ); + let hwnd = w.hwnd.get(); + unsafe { + let style = GetWindowLongPtrW(hwnd, GWL_STYLE) as u32; + if style == 0 { + warn!( + "failed to get window style: {}", + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) + ); + } + if (style & WS_MAXIMIZE) != 0 { + WindowSizeState::MAXIMIZED + } else if (style & WS_MINIMIZE) != 0 { + WindowSizeState::MINIMIZED + } else { + WindowSizeState::RESTORED + } } + } else { + WindowSizeState::RESTORED } } diff --git a/druid-shell/src/platform/x11/screen.rs b/druid-shell/src/platform/x11/screen.rs index e71f2b6627..f7b4040e86 100644 --- a/druid-shell/src/platform/x11/screen.rs +++ b/druid-shell/src/platform/x11/screen.rs @@ -15,12 +15,6 @@ //! X11 Monitors and Screen information. use crate::screen::Monitor; -use crate::kurbo::Size; - -pub(crate) fn get_display_size() -> Size { - log::warn!("Screen::get_display_size() is currently unimplemented for X11 platforms."); - Size::new(0.0, 0.0) -} pub(crate) fn get_monitors() -> Vec { log::warn!("Screen::get_monitors() is currently unimplemented for X11 platforms."); diff --git a/druid-shell/src/platform/x11/window.rs b/druid-shell/src/platform/x11/window.rs index 9671805595..d7a8f70326 100644 --- a/druid-shell/src/platform/x11/window.rs +++ b/druid-shell/src/platform/x11/window.rs @@ -49,6 +49,8 @@ use super::keycodes; use super::menu::Menu; use super::util; +use crate::window::WindowState as WindowSizeState; // Avoid name conflict. + /// A version of XCB's `xcb_visualtype_t` struct. This was copied from the [example] in x11rb; it /// is used to interoperate with cairo. /// @@ -128,12 +130,8 @@ impl WindowBuilder { log::warn!("WindowBuilder::set_position is currently unimplemented for X11 platforms."); } - pub fn maximized(&self) { - log::warn!("WindowBuilder::maximized is currently unimplemented for X11 platforms."); - } - - pub fn minimized(&self) { - log::warn!("WindowBuilder::minimized is currently unimplemented for X11 platforms."); + pub fn set_window_state(&self, _state: WindowSizeState) { + log::warn!("WindowBuilder::set_window_state is currently unimplemented for X11 platforms."); } pub fn set_title>(&mut self, title: S) { @@ -1281,12 +1279,12 @@ impl WindowHandle { Size::new(0.0, 0.0) } - pub fn maximize(&self) { - log::warn!("WindowHandle::maximize is currently unimplemented for X11 platforms."); + pub fn set_window_state(&self, _state: WindowSizeState) { + log::warn!("WindowHandle::set_window_state is currently unimplemented for X11 platforms."); } - pub fn minimize(&self) { - log::warn!("WindowHandle::minimize is currently unimplemented for X11 platforms."); + pub fn get_window_state(&self) -> WindowSizeState { + log::warn!("WindowHandle::get_window_state is currently unimplemented for X11 platforms."); } pub fn handle_titlebar(&self, _val: bool) { diff --git a/druid-shell/src/screen.rs b/druid-shell/src/screen.rs index 226f19840f..84f407efe3 100644 --- a/druid-shell/src/screen.rs +++ b/druid-shell/src/screen.rs @@ -14,7 +14,7 @@ //! Module to get information about monitors -use crate::kurbo::{Rect, Size}; +use crate::kurbo::Rect; use crate::platform; use std::fmt; use std::fmt::Display; @@ -87,8 +87,11 @@ impl Screen { platform::screen::get_monitors() } - /// Returns the total virtual screen space on the system, in pixels. - pub fn get_display_size() -> Size { - platform::screen::get_display_size() + /// Returns the bounding rectangle of the total virtual screen space in pixels. + pub fn get_display_size() -> Rect { + Self::get_monitors() + .iter() + .map(|x| x.virtual_rect()) + .fold(Rect::ZERO, |a, b| a.union(b)) } } diff --git a/druid-shell/src/window.rs b/druid-shell/src/window.rs index a90c400b33..e44c7f1c69 100644 --- a/druid-shell/src/window.rs +++ b/druid-shell/src/window.rs @@ -95,6 +95,14 @@ impl IdleToken { } } +/// Contains the different states a Window can be in. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum WindowState { + MAXIMIZED, + MINIMIZED, + RESTORED, +} + /// A handle to a platform window object. #[derive(Clone, Default)] pub struct WindowHandle(platform::WindowHandle); @@ -118,22 +126,22 @@ impl WindowHandle { self.0.resizable(resizable) } - /// Toggles window maximized. - /// Note this might not work the same on all platforms - /// Note that on windows, if resizable is set to false - /// This will cause the window to be maximized above the taskbar - pub fn maximize(&self) { - self.0.maximize(); + /// Sets the state of the window. + /// + /// [`state`]: enum.WindowState.html + pub fn set_window_state(&self, state: WindowState) { + self.0.set_window_state(state); } - /// Toggles window minimized. - /// Note this might not work the same on all platforms - pub fn minimize(&self) { - self.0.minimize(); + /// Gets the state of the window. + /// + /// [`state`]: enum.WindowState.html + pub fn get_window_state(&self) -> WindowState { + self.0.get_window_state() } /// Allows the operating system to handle a custom titlebar - /// like the default one + /// like the default one. /// /// It should be used on Event::MouseMove in a widget: /// `Event::MouseMove(_) => { @@ -144,12 +152,12 @@ impl WindowHandle { /// } ///}` /// - /// This might not work or behave the same across all platforms + /// This might not work or behave the same across all platforms. pub fn handle_titlebar(&self, val: bool) { self.0.handle_titlebar(val); } - /// Set whether the window should show titlebar + /// Set whether the window should show titlebar. pub fn show_titlebar(&self, show_titlebar: bool) { self.0.show_titlebar(show_titlebar) } @@ -356,14 +364,11 @@ impl WindowBuilder { self.0.set_menu(menu.into_inner()) } - /// Creates the window maximized. - pub fn maximized(&mut self) { - self.0.maximized(); - } - - /// Creates the window minimized. - pub fn minimized(&mut self) { - self.0.minimized(); + /// Sets the initial state of the window. + /// + /// [`state`]: enum.WindowState.html + pub fn set_window_state(&mut self, state: WindowState) { + self.0.set_window_state(state); } /// Attempt to construct the platform window. diff --git a/druid/src/app.rs b/druid/src/app.rs index 52b226613e..7f3c3e4b5e 100644 --- a/druid/src/app.rs +++ b/druid/src/app.rs @@ -24,6 +24,8 @@ use crate::{ theme, AppDelegate, Data, DruidHandler, Env, LocalizedString, MenuDesc, Widget, WidgetExt, }; +use druid_shell::WindowState; + /// A function that modifies the initial environment. type EnvSetupFn = dyn FnOnce(&mut Env, &T); @@ -48,8 +50,7 @@ pub struct WindowDesc { pub(crate) menu: Option>, pub(crate) resizable: bool, pub(crate) show_titlebar: bool, - pub(crate) maximized: bool, - pub(crate) minimized: bool, + pub(crate) state: WindowState, /// The `WindowId` that will be assigned to this window. /// /// This can be used to track a window from when it is launched and when @@ -170,8 +171,7 @@ impl WindowDesc { menu: MenuDesc::platform_default(), resizable: true, show_titlebar: true, - maximized: false, - minimized: false, + state: WindowState::RESTORED, id: WindowId::next(), } } @@ -251,15 +251,9 @@ impl WindowDesc { self } - /// Creates the window maximized. - pub fn maximized(mut self) -> Self { - self.maximized = true; - self - } - - /// Creates the window minimized. - pub fn minimized(mut self) -> Self { - self.minimized = true; + /// Set initial state for the window. + pub fn set_window_state(mut self, state: WindowState) -> Self { + self.state = state; self } @@ -293,13 +287,7 @@ impl WindowDesc { builder.set_position(position); } - if self.maximized { - builder.maximized(); - } - - if self.minimized { - builder.minimized(); - } + builder.set_window_state(self.state); builder.set_title(self.title.display_text()); if let Some(menu) = platform_menu { diff --git a/druid/src/lib.rs b/druid/src/lib.rs index 472cf15ff6..d9affbe12a 100644 --- a/druid/src/lib.rs +++ b/druid/src/lib.rs @@ -172,7 +172,7 @@ pub use shell::{ Application, Clipboard, ClipboardFormat, Code, Cursor, Error as PlatformError, FileDialogOptions, FileInfo, FileSpec, FormatId, HotKey, KbKey, KeyEvent, Location, Modifiers, Monitor, MouseButton, MouseButtons, RawMods, Scalable, Scale, Screen, SysMods, Text, - TimerToken, WindowHandle, + TimerToken, WindowHandle, WindowState, }; pub use crate::core::WidgetPod; From 262abc7513265ef609422b2c2374f025294a6f5a Mon Sep 17 00:00:00 2001 From: rhzk Date: Sat, 15 Aug 2020 12:24:30 +0200 Subject: [PATCH 08/18] Removed duplicated line from merge --- druid-shell/src/dialog.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/druid-shell/src/dialog.rs b/druid-shell/src/dialog.rs index 389ceeb0a2..b443361387 100644 --- a/druid-shell/src/dialog.rs +++ b/druid-shell/src/dialog.rs @@ -32,7 +32,6 @@ pub enum FileDialogType { } /// Options for file dialogs. -#[non_exhaustive] #[derive(Debug, Clone, Default)] #[non_exhaustive] pub struct FileDialogOptions { From 69fddf5556b642a0a00e7e1efa2762ada4fab3bb Mon Sep 17 00:00:00 2001 From: rhzk Date: Sat, 15 Aug 2020 12:36:47 +0200 Subject: [PATCH 09/18] rename get_display_size to get_display_rect --- druid-shell/src/screen.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/druid-shell/src/screen.rs b/druid-shell/src/screen.rs index 84f407efe3..a39b412e90 100644 --- a/druid-shell/src/screen.rs +++ b/druid-shell/src/screen.rs @@ -88,7 +88,7 @@ impl Screen { } /// Returns the bounding rectangle of the total virtual screen space in pixels. - pub fn get_display_size() -> Rect { + pub fn get_display_rect() -> Rect { Self::get_monitors() .iter() .map(|x| x.virtual_rect()) From 196fbe90671f9b382a1eb4d4329533636dce0dd6 Mon Sep 17 00:00:00 2001 From: rhzk Date: Sat, 15 Aug 2020 12:40:13 +0200 Subject: [PATCH 10/18] Added return value x11 --- druid-shell/src/platform/x11/window.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/druid-shell/src/platform/x11/window.rs b/druid-shell/src/platform/x11/window.rs index 9975304b50..5514ddce6d 100644 --- a/druid-shell/src/platform/x11/window.rs +++ b/druid-shell/src/platform/x11/window.rs @@ -1385,6 +1385,7 @@ impl WindowHandle { pub fn get_window_state(&self) -> WindowSizeState { log::warn!("WindowHandle::get_window_state is currently unimplemented for X11 platforms."); + WindowSizeState::RESTORED } pub fn handle_titlebar(&self, _val: bool) { From adcac2d69bdffd53b877e8c2316c54e2dcad8626 Mon Sep 17 00:00:00 2001 From: rhzk Date: Mon, 17 Aug 2020 04:23:10 +0200 Subject: [PATCH 11/18] Bugfix for when window setting and style mismatch --- druid-shell/src/platform/windows/window.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/druid-shell/src/platform/windows/window.rs b/druid-shell/src/platform/windows/window.rs index bc0032f1af..4d32c6b4a5 100644 --- a/druid-shell/src/platform/windows/window.rs +++ b/druid-shell/src/platform/windows/window.rs @@ -633,16 +633,15 @@ impl WndProc for MyWndProc { }, WM_NCCALCSIZE => unsafe { // Hack to get rid of caption but keeping the borders created by it. - if !self.has_titlebar() { - let style = GetWindowLongPtrW(hwnd, GWL_STYLE) as u32; - if style == 0 { - warn!( - "failed to get window style: {}", - Error::Hr(HRESULT_FROM_WIN32(GetLastError())) - ); - return Some(0); - } - + let style = GetWindowLongPtrW(hwnd, GWL_STYLE) as u32; + if style == 0 { + warn!( + "failed to get window style: {}", + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) + ); + return Some(0); + } + if !self.has_titlebar() && (style & WS_CAPTION) != 0 { let s: *mut NCCALCSIZE_PARAMS = lparam as *mut NCCALCSIZE_PARAMS; if let Some(mut s) = s.as_mut() { s.rgrc[0].top -= (31.0 * self.scale().x()) as i32; From 11838fc5e9a929c7c829e1d87c6957442ea0bceb Mon Sep 17 00:00:00 2001 From: rhzk Date: Fri, 11 Sep 2020 01:35:54 +0200 Subject: [PATCH 12/18] Renamed blocked queue to deferred queue. Improved DPI for windows 8.1. Minor fixes --- druid-shell/src/platform/gtk/screen.rs | 2 +- druid-shell/src/platform/gtk/window.rs | 11 +- druid-shell/src/platform/mac/screen.rs | 2 +- druid-shell/src/platform/mac/window.rs | 10 +- druid-shell/src/platform/web/screen.rs | 2 +- druid-shell/src/platform/web/window.rs | 11 +- .../src/platform/windows/application.rs | 12 +- druid-shell/src/platform/windows/screen.rs | 14 +- druid-shell/src/platform/windows/util.rs | 5 + druid-shell/src/platform/windows/window.rs | 385 ++++++++++-------- druid-shell/src/platform/x11/screen.rs | 2 +- druid-shell/src/platform/x11/window.rs | 10 +- 12 files changed, 263 insertions(+), 203 deletions(-) diff --git a/druid-shell/src/platform/gtk/screen.rs b/druid-shell/src/platform/gtk/screen.rs index 7323db771b..8f97d4f48a 100644 --- a/druid-shell/src/platform/gtk/screen.rs +++ b/druid-shell/src/platform/gtk/screen.rs @@ -18,5 +18,5 @@ use crate::screen::Monitor; pub(crate) fn get_monitors() -> Vec { log::warn!("Screen::get_monitors() is currently unimplemented for gtk."); - Vec::::new() + Vec::new() } diff --git a/druid-shell/src/platform/gtk/window.rs b/druid-shell/src/platform/gtk/window.rs index 8c3f3a85c8..e4c3a1e1d7 100644 --- a/druid-shell/src/platform/gtk/window.rs +++ b/druid-shell/src/platform/gtk/window.rs @@ -41,6 +41,7 @@ use crate::keyboard::{KbKey, KeyState, KeyEvent, Modifiers}; use crate::mouse::{Cursor, MouseButton, MouseButtons, MouseEvent}; use crate::scale::{Scale, Scalable, ScaledArea}; use crate::window::{IdleToken, Text, TimerToken, WinHandler}; +use crate::window; use super::application::Application; use super::dialog; @@ -48,8 +49,6 @@ use super::keycodes; use super::menu::Menu; use super::util; -use crate::window::WindowState as WindowSizeState; // Avoid name conflict. - /// The platform target DPI. /// /// GTK considers 96 the default value which represents a 1.0 scale factor. @@ -162,7 +161,7 @@ impl WindowBuilder { log::warn!("WindowBuilder::set_position is currently unimplemented for gtk."); } - pub fn set_window_state(&self, _state: WindowSizeState) { + pub fn set_window_state(&self, _state: window::WindowState) { log::warn!("WindowBuilder::set_window_state is currently unimplemented for gtk."); } @@ -581,13 +580,13 @@ impl WindowHandle { Size::new(0.0, 0.0) } - pub fn set_window_state(&self, _state: WindowSizeState) { + pub fn set_window_state(&self, _state: window::WindowState) { log::warn!("WindowHandle::set_window_state is currently unimplemented for gtk."); } - pub fn get_window_state(&self) -> WindowSizeState { + pub fn get_window_state(&self) -> window::WindowState { log::warn!("WindowHandle::get_window_state is currently unimplemented for gtk."); - WindowSizeState::RESTORED + window::WindowState::RESTORED } pub fn handle_titlebar(&self, _val: bool) { diff --git a/druid-shell/src/platform/mac/screen.rs b/druid-shell/src/platform/mac/screen.rs index 7786f85d0a..c293c6bfc3 100644 --- a/druid-shell/src/platform/mac/screen.rs +++ b/druid-shell/src/platform/mac/screen.rs @@ -18,5 +18,5 @@ use crate::screen::Monitor; pub(crate) fn get_monitors() -> Vec { log::warn!("Screen::get_monitors() is currently unimplemented for Mac."); - Vec::::new() + Vec::new() } diff --git a/druid-shell/src/platform/mac/window.rs b/druid-shell/src/platform/mac/window.rs index b5f3b76380..53943ef298 100644 --- a/druid-shell/src/platform/mac/window.rs +++ b/druid-shell/src/platform/mac/window.rs @@ -56,9 +56,9 @@ use crate::keyboard_types::KeyState; use crate::mouse::{Cursor, MouseButton, MouseButtons, MouseEvent}; use crate::scale::Scale; use crate::window::{IdleToken, Text, TimerToken, WinHandler}; +use crate::window; use crate::Error; -use crate::window::WindowState as WindowSizeState; // Avoid name conflict. #[allow(non_upper_case_globals)] const NSWindowDidBecomeKeyNotification: &str = "NSWindowDidBecomeKeyNotification"; @@ -153,7 +153,7 @@ impl WindowBuilder { log::warn!("WindowBuilder::set_position is currently unimplemented for mac platforms."); } - pub fn set_window_state(&self, _state: WindowSizeState) { + pub fn set_window_state(&self, _state: window::WindowState) { log::warn!("WindowBuilder::set_window_state is currently unimplemented for mac platforms."); } @@ -866,13 +866,13 @@ impl WindowHandle { Size::new(0.0, 0.0) } - pub fn set_window_state(&self, _state: WindowSizeState) { + pub fn set_window_state(&self, _state: window::WindowState) { log::warn!("WindowHandle::set_window_state is currently unimplemented for Mac."); } - pub fn get_window_state(&self) -> WindowSizeState { + pub fn get_window_state(&self) -> window::WindowState { log::warn!("WindowHandle::get_window_state is currently unimplemented for Mac."); - WindowSizeState::RESTORED + window::WindowState::RESTORED } pub fn handle_titlebar(&self, _val: bool) { diff --git a/druid-shell/src/platform/web/screen.rs b/druid-shell/src/platform/web/screen.rs index 34788bae5c..6240153c7b 100644 --- a/druid-shell/src/platform/web/screen.rs +++ b/druid-shell/src/platform/web/screen.rs @@ -18,5 +18,5 @@ use crate::screen::Monitor; pub(crate) fn get_monitors() -> Vec { log::warn!("Screen::get_monitors() is not implemented for web."); - Vec::::new() + Vec::new() } diff --git a/druid-shell/src/platform/web/window.rs b/druid-shell/src/platform/web/window.rs index 1758e976a5..2e0714ed07 100644 --- a/druid-shell/src/platform/web/window.rs +++ b/druid-shell/src/platform/web/window.rs @@ -41,8 +41,7 @@ use crate::scale::{Scale, ScaledArea}; use crate::keyboard::{KbKey, KeyState, Modifiers}; use crate::mouse::{Cursor, MouseButton, MouseButtons, MouseEvent}; use crate::window::{IdleToken, Text, TimerToken, WinHandler}; - -use crate::window::WindowState as WindowSizeState; // Avoid name conflict. +use crate::window; // This is a macro instead of a function since KeyboardEvent and MouseEvent has identical functions // to query modifier key states. @@ -351,7 +350,7 @@ impl WindowBuilder { // Ignored } - pub fn set_window_state(&self, _state: WindowSizeState) { + pub fn set_window_state(&self, _state: window::WindowState) { // Ignored } @@ -458,13 +457,13 @@ impl WindowHandle { Size::new(0.0, 0.0) } - pub fn set_window_state(&self, _state: WindowSizeState) { + pub fn set_window_state(&self, _state: window::WindowState) { log::warn!("WindowHandle::set_window_state unimplemented for web."); } - pub fn get_window_state(&self) -> WindowSizeState { + pub fn get_window_state(&self) -> window::WindowState { log::warn!("WindowHandle::get_window_state unimplemented for web."); - WindowSizeState::RESTORED + window::WindowState::RESTORED } pub fn handle_titlebar(&self, _val: bool) { diff --git a/druid-shell/src/platform/windows/application.rs b/druid-shell/src/platform/windows/application.rs index 4a6fec623b..39cddbf7c8 100644 --- a/druid-shell/src/platform/windows/application.rs +++ b/druid-shell/src/platform/windows/application.rs @@ -25,6 +25,7 @@ use winapi::shared::ntdef::LPCWSTR; use winapi::shared::windef::{HCURSOR, HWND, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2}; use winapi::shared::winerror::HRESULT_FROM_WIN32; use winapi::um::errhandlingapi::GetLastError; +use winapi::um::shellscalingapi::PROCESS_PER_MONITOR_DPI_AWARE; use winapi::um::winuser::{ DispatchMessageW, GetAncestor, GetMessageW, LoadIconW, PostMessageW, PostQuitMessage, RegisterClassW, TranslateAcceleratorW, TranslateMessage, GA_ROOT, IDI_APPLICATION, MSG, @@ -37,7 +38,7 @@ use super::accels; use super::clipboard::Clipboard; use super::error::Error; use super::util::{self, ToWide, CLASS_NAME, OPTIONAL_FUNCTIONS}; -use super::window::{self, DS_REQUEST_DESTROY, DS_HANDLE_DROPPED}; +use super::window::{self, DS_REQUEST_DESTROY, DS_HANDLE_DEFERRED}; #[derive(Clone)] pub(crate) struct Application { @@ -49,6 +50,8 @@ struct State { windows: HashSet, } + + impl Application { pub fn new() -> Result { Application::init()?; @@ -69,6 +72,11 @@ impl Application { func(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); } } + else if let Some(func) = OPTIONAL_FUNCTIONS.SetProcessDpiAwareness { + unsafe { + func(PROCESS_PER_MONITOR_DPI_AWARE); + } + } unsafe { let class_name = CLASS_NAME.to_wide(); let icon = LoadIconW(0 as HINSTANCE, IDI_APPLICATION); @@ -106,7 +114,7 @@ impl Application { loop { if let Ok(state) = self.state.try_borrow() { for hwnd in &state.windows { - SendMessageW(*hwnd, DS_HANDLE_DROPPED, 0,0); + SendMessageW(*hwnd, DS_HANDLE_DEFERRED, 0,0); } } let mut msg = mem::MaybeUninit::uninit(); diff --git a/druid-shell/src/platform/windows/screen.rs b/druid-shell/src/platform/windows/screen.rs index b29b7f84da..7a6ef01f01 100644 --- a/druid-shell/src/platform/windows/screen.rs +++ b/druid-shell/src/platform/windows/screen.rs @@ -27,8 +27,6 @@ use std::mem::size_of; use crate::screen::Monitor; use crate::kurbo::Rect; -static mut MONITORS : Vec = Vec::new(); - unsafe extern "system" fn monitorenumproc(hmonitor : HMONITOR, _hdc : HDC, _lprect : LPRECT, _lparam : LPARAM) -> BOOL { let rect = RECT { left: 0, top: 0, right: 0, bottom: 0}; let mut info = MONITORINFO { cbSize : size_of::() as u32, rcMonitor : rect, rcWork : rect, dwFlags : 0}; @@ -41,21 +39,21 @@ unsafe extern "system" fn monitorenumproc(hmonitor : HMONITOR, _hdc : HDC, _lpre let primary = info.dwFlags == MONITORINFOF_PRIMARY; let rect = Rect::new(info.rcMonitor.left as f64, info.rcMonitor.top as f64, info.rcMonitor.right as f64, info.rcMonitor.bottom as f64); let work_rect = Rect::new(info.rcWork.left as f64, info.rcWork.top as f64, info.rcWork.right as f64, info.rcWork.bottom as f64); - let m = Monitor::new(primary, rect, work_rect); - MONITORS.push(m); + let monitors = _lparam as *mut Vec::; + (*monitors).push(Monitor::new(primary, rect, work_rect)); TRUE } - pub(crate) fn get_monitors() -> Vec { unsafe { - MONITORS = Vec::new(); - if EnumDisplayMonitors(null_mut(), null_mut(), Some(monitorenumproc), 0) == 0{ + let monitors = Vec::::new(); + let ptr = &monitors as *const Vec::; + if EnumDisplayMonitors(null_mut(), null_mut(), Some(monitorenumproc), ptr as isize) == 0{ warn!( "Failed to Enumerate Display Monitors: {}", Error::Hr(HRESULT_FROM_WIN32(GetLastError())) ); }; - MONITORS.clone() + monitors } } diff --git a/druid-shell/src/platform/windows/util.rs b/druid-shell/src/platform/windows/util.rs index df9692b90d..4ce3c2731f 100644 --- a/druid-shell/src/platform/windows/util.rs +++ b/druid-shell/src/platform/windows/util.rs @@ -132,6 +132,7 @@ pub(crate) fn recti_to_rect(rect: RECT) -> Rect { type GetDpiForSystem = unsafe extern "system" fn() -> UINT; type GetDpiForWindow = unsafe extern "system" fn(HWND) -> UINT; type SetProcessDpiAwarenessContext = unsafe extern "system" fn(winapi::shared::windef::DPI_AWARENESS_CONTEXT) -> BOOL; +type GetSystemMetricsForDpi = unsafe extern "system" fn(winapi::ctypes::c_int, UINT) -> winapi::ctypes::c_int; // from shcore.dll type GetDpiForMonitor = unsafe extern "system" fn(HMONITOR, MONITOR_DPI_TYPE, *mut UINT, *mut UINT); type SetProcessDpiAwareness = unsafe extern "system" fn(PROCESS_DPI_AWARENESS) -> HRESULT; @@ -150,6 +151,7 @@ pub struct OptionalFunctions { pub SetProcessDpiAwarenessContext: Option, pub GetDpiForMonitor: Option, pub SetProcessDpiAwareness: Option, + pub GetSystemMetricsForDpi: Option, pub DCompositionCreateDevice2: Option, pub CreateDXGIFactory2: Option, } @@ -205,6 +207,7 @@ fn load_optional_functions() -> OptionalFunctions { let mut GetDpiForWindow = None; let mut SetProcessDpiAwarenessContext = None; let mut SetProcessDpiAwareness = None; + let mut GetSystemMetricsForDpi = None; let mut DCompositionCreateDevice2 = None; let mut CreateDXGIFactory2 = None; @@ -221,6 +224,7 @@ fn load_optional_functions() -> OptionalFunctions { load_function!(user32, GetDpiForSystem, "10"); load_function!(user32, GetDpiForWindow, "10"); load_function!(user32, SetProcessDpiAwarenessContext, "10"); + load_function!(user32, GetSystemMetricsForDpi, "10"); } if !dcomp.is_null() { @@ -237,6 +241,7 @@ fn load_optional_functions() -> OptionalFunctions { SetProcessDpiAwarenessContext, GetDpiForMonitor, SetProcessDpiAwareness, + GetSystemMetricsForDpi, DCompositionCreateDevice2, CreateDXGIFactory2, } diff --git a/druid-shell/src/platform/windows/window.rs b/druid-shell/src/platform/windows/window.rs index 4d32c6b4a5..ebdfba7529 100644 --- a/druid-shell/src/platform/windows/window.rs +++ b/druid-shell/src/platform/windows/window.rs @@ -37,6 +37,7 @@ use winapi::um::errhandlingapi::GetLastError; use winapi::um::unknwnbase::*; use winapi::um::winnt::*; use winapi::um::winuser::*; +use winapi::um::shellscalingapi::MDT_EFFECTIVE_DPI; use piet_common::d2d::{D2DFactory, DeviceContext}; use piet_common::dwrite::DwriteFactory; @@ -64,8 +65,8 @@ use crate::keyboard::{KbKey, KeyState}; use crate::mouse::{Cursor, MouseButton, MouseButtons, MouseEvent}; use crate::scale::{Scale, Scalable, ScaledArea}; use crate::window::{IdleToken, Text, TimerToken, WinHandler}; +use crate::window; -use crate::window::WindowState as WindowSizeState; // Avoid name conflict. /// The platform target DPI. /// @@ -88,7 +89,7 @@ pub(crate) struct WindowBuilder { size: Size, min_size: Option, position: Point, - state: WindowSizeState, + state: window::WindowState, } #[derive(Clone, Copy, PartialEq, Eq, Debug)] @@ -125,12 +126,134 @@ pub struct WindowHandle { state: Weak, } -enum BlockingOp { +#[derive(Clone)] +enum DeferredOp { SetPosition(Point), SetSize(Size), DecorationChanged(), // Needs a better name - SetWindowSizeState(WindowSizeState), + SetWindowSizeState(window::WindowState), +} + +struct DeferredQueue { + queue: Vec, +} + +impl DeferredQueue { + pub fn new() -> Self { + DeferredQueue { + queue: Vec::new() + } + } + + /// Adds a DeferredOp message to the queue. + /// The message will be run at a later time. + pub fn add(state: Weak, message: DeferredOp) { + if let Some(w) = state.upgrade() { + if let Ok(mut q) = w.deferred_queue.try_borrow_mut() { + q.queue.push(message) + } else { + warn!( + "failed to borrow deferred queue" + ); + } + } + } + + /// Empties the current message queue, returning a queue with the messages. + pub fn empty(&mut self) -> Vec { + let ret = self.queue.clone(); + self.queue = Vec::new(); + ret + } + + + // Here we handle messages generated by WindowHandle + // that needs to run after borrow is released + fn handle_deferred(proc: &MyWndProc, op : DeferredOp) { + if let Some(hwnd) = proc.handle.borrow().get_hwnd() { + match op { + DeferredOp::SetSize(size) => { + unsafe { + if SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, (size.width * proc.scale().x()) as i32, (size.height * proc.scale().y()) as i32, SWP_NOMOVE | SWP_NOZORDER) == 0 { + warn!( + "failed to move window: {}", + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) + ); + }; + } + }, + DeferredOp::SetPosition(position) => { + unsafe { + if SetWindowPos(hwnd, HWND_TOPMOST, position.x as i32, position.y as i32, 0, 0, SWP_NOSIZE | SWP_NOZORDER) == 0 { + warn!( + "failed to move window: {}", + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) + ); + }; + } + }, + DeferredOp::DecorationChanged() => { + unsafe { + let resizable = proc.resizable(); + let titlebar = proc.has_titlebar(); + + let mut style = GetWindowLongPtrW(hwnd, GWL_STYLE) as u32; + if style == 0 { + warn!( + "failed to get window style: {}", + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) + ); + return; + } + + if !resizable { + style &= !(WS_THICKFRAME | WS_MAXIMIZEBOX); + } else { + style |= WS_THICKFRAME | WS_MAXIMIZEBOX; + } + if !titlebar { + style &= !(WS_MINIMIZEBOX | WS_SYSMENU | WS_OVERLAPPED); + } else { + style |= WS_MINIMIZEBOX | WS_SYSMENU | WS_OVERLAPPED; + } + if SetWindowLongPtrW(hwnd, GWL_STYLE, style as isize) == 0 { + warn!( + "failed to set the window style: {}", + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) + ); + } + if SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOSIZE) == 0 { + warn!( + "failed to update window style: {}", + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) + ); + }; + } + }, + DeferredOp::SetWindowSizeState(val) => { + unsafe { + let s = match val { + window::WindowState::MAXIMIZED => { + SW_MAXIMIZE + }, + window::WindowState::MINIMIZED => { + SW_MINIMIZE + }, + window::WindowState::RESTORED => { + SW_RESTORE + } + }; + ShowWindow(hwnd,s); + } + }, + } + } else { + warn!("Could not get HWND"); + } + } + + } /// A handle that can get used to schedule an idle handler. Note that @@ -159,7 +282,7 @@ struct WindowState { wndproc: Box, idle_queue: Arc>>, timers: Arc>, - blocked_queue: RefCell>, + deferred_queue: RefCell, has_titlebar: Cell, // For resizable borders, window can still be resized with code. is_resizable: Cell, @@ -229,11 +352,20 @@ const DS_RUN_IDLE: UINT = WM_USER; /// time it is handled, we can successfully borrow the handler. pub(crate) const DS_REQUEST_DESTROY: UINT = WM_USER + 1; -/// Message relaying a request to handle dropped messages. +/// A message which must be delivered deferred due to reentrancy. +/// +/// Due to the architecture of Windows, it sometimes delivers messages +/// reentrantly. This is problematic when the mutable state for the window +/// is borrowed, as it's not possible to safely dispatch the message. The two +/// choices are to drop the message, or put it in a queue for deferred +/// processing. /// -/// Rust borrow checker causes messages to be dropped -/// so instead we place them in a queue and run them when the borrow is released. -pub(crate) const DS_HANDLE_DROPPED: UINT = WM_USER + 2; +/// This mechanism should be used very sparingly, as delivery of these +/// deferred messages can not be guaranteed in a timely and reliable +/// fashion. In particular, we do not run our own runloop when handling +/// live resize, when a modal dialog is open, or when the application is a +/// a guest (such as a VST plugin). +pub(crate) const DS_HANDLE_DEFERRED: UINT = WM_USER + 2; impl Default for PresentStrategy { fn default() -> PresentStrategy { @@ -393,95 +525,10 @@ impl MyWndProc { self.with_window_state(|state| state.is_resizable.get()) } - // Here we handle messages generated by WindowHandle - // that needs to run after borrow is released - fn handle_blocked(&self, op : BlockingOp) { - if let Some(hwnd) = self.handle.borrow().get_hwnd() { - match op { - BlockingOp::SetSize(size) => { - unsafe { - if SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, (size.width * self.scale().x()) as i32, (size.height * self.scale().y()) as i32, SWP_NOMOVE | SWP_NOZORDER) == 0 { - warn!( - "failed to move window: {}", - Error::Hr(HRESULT_FROM_WIN32(GetLastError())) - ); - }; - } - }, - BlockingOp::SetPosition(position) => { - unsafe { - if SetWindowPos(hwnd, HWND_TOPMOST, position.x as i32, position.y as i32, 0, 0, SWP_NOSIZE | SWP_NOZORDER) == 0 { - warn!( - "failed to move window: {}", - Error::Hr(HRESULT_FROM_WIN32(GetLastError())) - ); - }; - } - }, - BlockingOp::DecorationChanged() => { - unsafe { - let resizable = self.resizable(); - let titlebar = self.has_titlebar(); - - let mut style = GetWindowLongPtrW(hwnd, GWL_STYLE) as u32; - if style == 0 { - warn!( - "failed to get window style: {}", - Error::Hr(HRESULT_FROM_WIN32(GetLastError())) - ); - return; - } - - if !resizable { - style &= !(WS_THICKFRAME | WS_MAXIMIZEBOX); - } else { - style |= WS_THICKFRAME | WS_MAXIMIZEBOX; - } - if !titlebar { - style &= !(WS_MINIMIZEBOX | WS_SYSMENU | WS_OVERLAPPED); - } else { - style |= WS_MINIMIZEBOX | WS_SYSMENU | WS_OVERLAPPED; - } - if SetWindowLongPtrW(hwnd, GWL_STYLE, style as isize) == 0 { - warn!( - "failed to set the window style: {}", - Error::Hr(HRESULT_FROM_WIN32(GetLastError())) - ); - } - if SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOSIZE) == 0 { - warn!( - "failed to update window style: {}", - Error::Hr(HRESULT_FROM_WIN32(GetLastError())) - ); - }; - } - }, - BlockingOp::SetWindowSizeState(val) => { - unsafe { - let s = match val { - WindowSizeState::MAXIMIZED => { - SW_MAXIMIZE - }, - WindowSizeState::MINIMIZED => { - SW_MINIMIZE - }, - WindowSizeState::RESTORED => { - SW_RESTORE - } - }; - ShowWindow(hwnd,s); - } - }, - } - } else { - warn!("Could not get HWND"); - } - } - - fn handle_blocked_queue(&self) { - let q = self.with_window_state(move |state| state.blocked_queue.replace(Vec::new())); + fn handle_deferred_queue(&self) { + let q = self.with_window_state(move |state| state.deferred_queue.borrow_mut().empty()); for op in q { - self.handle_blocked(op); + DeferredQueue::handle_deferred(self, op); } } } @@ -513,6 +560,28 @@ impl WndProc for MyWndProc { //println!("wndproc msg: {}", msg); match msg { WM_CREATE => { + // Only supported on Windows 10, Could remove this as the 8.1 version below also works on 10.. + let scale_factor = if let Some(func) = OPTIONAL_FUNCTIONS.GetDpiForWindow { + unsafe { + func(hwnd) as f64 / SCALE_TARGET_DPI + } + } + // Windows 8.1 Support + else if let Some(func) = OPTIONAL_FUNCTIONS.GetDpiForMonitor{ + unsafe { + let monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); + let mut dpiX = 0; + let mut dpiY = 0; + func(monitor, MDT_EFFECTIVE_DPI, &mut dpiX, &mut dpiY); + dpiX as f64 / SCALE_TARGET_DPI + } + } else { + 1.0 + }; + + let scale = Scale::new(scale_factor, scale_factor); + self.set_scale(scale); + if let Some(state) = self.handle.borrow().state.upgrade() { state.hwnd.set(hwnd); } @@ -632,7 +701,7 @@ impl WndProc for MyWndProc { Some(0) }, WM_NCCALCSIZE => unsafe { - // Hack to get rid of caption but keeping the borders created by it. + // Workaround to get rid of caption but keeping the borders created by it. let style = GetWindowLongPtrW(hwnd, GWL_STYLE) as u32; if style == 0 { warn!( @@ -641,17 +710,45 @@ impl WndProc for MyWndProc { ); return Some(0); } + if !self.has_titlebar() && (style & WS_CAPTION) != 0 { let s: *mut NCCALCSIZE_PARAMS = lparam as *mut NCCALCSIZE_PARAMS; if let Some(mut s) = s.as_mut() { - s.rgrc[0].top -= (31.0 * self.scale().x()) as i32; - if (style & WS_MAXIMIZE) != 0 { - s.rgrc[0].top += (7.0 * self.scale().x()) as i32; + if let Some(func) = OPTIONAL_FUNCTIONS.GetSystemMetricsForDpi { + // This function is only supported on windows 10 + let dpi = self.scale().x() * 96.; + // Height of the different parts that make the titlebar + let border = func(SM_CXPADDEDBORDER, dpi as u32); + let frame = func(SM_CYSIZEFRAME, dpi as u32); + let caption = func(SM_CYCAPTION, dpi as u32); + // Maximized window titlebar height is just the caption + if (style & WS_MAXIMIZE) != 0 { + s.rgrc[0].top -= (caption) as i32; + } + // Normal window titlebar height is a combination of border frame and caption + else { + s.rgrc[0].top -= (border+frame+caption) as i32; + } + + } + // Support for windows 8.1 + else { + // Note: With SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE) that we use on 8.1, + // Windows will not scale the titlebar area when DPI changes. + // Instead it is "stuck" at the DPI setting the window was created with. + // GetSystemMetrics() also reports values with the DPI the window was created with. + let border = GetSystemMetrics(SM_CXPADDEDBORDER); + let frame = GetSystemMetrics(SM_CYSIZEFRAME); + let caption = GetSystemMetrics(SM_CYCAPTION); + if (style & WS_MAXIMIZE) != 0 { + s.rgrc[0].top -= (caption) as i32; + } else { + s.rgrc[0].top -= (border+frame+caption) as i32; + } } } } - // Let the default WinProc handle the message. - Some(DefWindowProcW(hwnd, msg, wparam, lparam)) + None }, WM_NCHITTEST => unsafe { let mut hit = DefWindowProcW(hwnd, msg, wparam, lparam); @@ -1105,8 +1202,8 @@ impl WndProc for MyWndProc { None } } - DS_HANDLE_DROPPED => { - self.handle_blocked_queue(); + DS_HANDLE_DEFERRED => { + self.handle_deferred_queue(); Some(0) } _ => None, @@ -1127,7 +1224,7 @@ impl WindowBuilder { size: Size::new(CW_USEDEFAULT as f64, CW_USEDEFAULT as f64), min_size: None, position: Point::new(CW_USEDEFAULT as f64, CW_USEDEFAULT as f64), - state: WindowSizeState::RESTORED, + state: window::WindowState::RESTORED, } } @@ -1149,7 +1246,6 @@ impl WindowBuilder { } pub fn show_titlebar(&mut self, show_titlebar: bool) { - // TODO: Use this in `self.build` self.show_titlebar = show_titlebar; } @@ -1165,7 +1261,7 @@ impl WindowBuilder { self.position = position; } - pub fn set_window_state(&mut self, state: WindowSizeState) { + pub fn set_window_state(&mut self, state: window::WindowState) { self.state = state; } @@ -1183,16 +1279,7 @@ impl WindowBuilder { present_strategy: self.present_strategy, }; - // Simple scaling based on System DPI - let scale_factor = if let Some(func) = OPTIONAL_FUNCTIONS.GetDpiForSystem { - // Only supported on Windows 10 - func() as f64 / SCALE_TARGET_DPI - } else { - // TODO GetDpiForMonitor is supported on Windows 8.1, try falling back to that here - // Probably GetDeviceCaps(..., LOGPIXELSX) is the best to do pre-10 - 1.0 - }; - let scale = Scale::new(scale_factor, scale_factor); + let scale = Scale::new(1.0, 1.0); let area = ScaledArea::from_dp(self.size, scale); let size_px = area.size_px(); @@ -1212,7 +1299,7 @@ impl WindowBuilder { wndproc: Box::new(wndproc), idle_queue: Default::default(), timers: Arc::new(Mutex::new(TimerSlots::new(1))), - blocked_queue: RefCell::new(Vec::new()), + deferred_queue: RefCell::new(DeferredQueue::new()), has_titlebar: Cell::new(self.show_titlebar), is_resizable: Cell::new(self.resizable), handle_titlebar: Cell::new(false), @@ -1535,27 +1622,13 @@ impl WindowHandle { pub fn show_titlebar(&self, show_titlebar: bool) { if let Some(w) = self.state.upgrade() { w.has_titlebar.set(show_titlebar); - if let Ok(mut q) = w.blocked_queue.try_borrow_mut() { - q.push(BlockingOp::DecorationChanged()) - } else { - warn!( - "failed to borrow blocked queue" - ); - } + DeferredQueue::add(self.state.clone(), DeferredOp::DecorationChanged()); } } // Sets the position of the window in virtual screen coordinates pub fn set_position(&self, position: Point) { - if let Some(w) = self.state.upgrade() { - if let Ok(mut q) = w.blocked_queue.try_borrow_mut() { - q.push(BlockingOp::SetPosition(position)) - } else { - warn!( - "failed to borrow blocked queue" - ); - } - } + DeferredQueue::add(self.state.clone(), DeferredOp::SetPosition(position)); } // Gets the position of the window in virtual screen coordinates @@ -1578,15 +1651,7 @@ impl WindowHandle { // Sets the size of the window in DP pub fn set_size(&self, size: Size) { - if let Some(w) = self.state.upgrade() { - if let Ok(mut q) = w.blocked_queue.try_borrow_mut() { - q.push(BlockingOp::SetSize(size)) - } else { - warn!( - "failed to borrow blocked queue" - ); - } - } + DeferredQueue::add(self.state.clone(), DeferredOp::SetSize(size)); } // Gets the size of the window in pixels @@ -1612,32 +1677,18 @@ impl WindowHandle { pub fn resizable(&self, resizable: bool) { if let Some(w) = self.state.upgrade() { w.is_resizable.set(resizable); - if let Ok(mut q) = w.blocked_queue.try_borrow_mut() { - q.push(BlockingOp::DecorationChanged()) - } else { - warn!( - "failed to borrow blocked queue" - ); - } + DeferredQueue::add(self.state.clone(), DeferredOp::DecorationChanged()); } } // Sets the window state. - pub fn set_window_state(&self, state : WindowSizeState) { - if let Some(w) = self.state.upgrade() { - if let Ok(mut q) = w.blocked_queue.try_borrow_mut() { - q.push(BlockingOp::SetWindowSizeState(state)) - } else { - warn!( - "failed to borrow blocked queue" - ); - } - } + pub fn set_window_state(&self, state : window::WindowState) { + DeferredQueue::add(self.state.clone(), DeferredOp::SetWindowSizeState(state)); } // Gets the window state. - pub fn get_window_state(&self) -> WindowSizeState { - // We cant store state internally because it could be modified externaly. + pub fn get_window_state(&self) -> window::WindowState { + // We can not store state internally because it could be modified externally. if let Some(w) = self.state.upgrade() { let hwnd = w.hwnd.get(); unsafe { @@ -1649,15 +1700,15 @@ impl WindowHandle { ); } if (style & WS_MAXIMIZE) != 0 { - WindowSizeState::MAXIMIZED + window::WindowState::MAXIMIZED } else if (style & WS_MINIMIZE) != 0 { - WindowSizeState::MINIMIZED + window::WindowState::MINIMIZED } else { - WindowSizeState::RESTORED + window::WindowState::RESTORED } } } else { - WindowSizeState::RESTORED + window::WindowState::RESTORED } } diff --git a/druid-shell/src/platform/x11/screen.rs b/druid-shell/src/platform/x11/screen.rs index f7b4040e86..843b267714 100644 --- a/druid-shell/src/platform/x11/screen.rs +++ b/druid-shell/src/platform/x11/screen.rs @@ -18,5 +18,5 @@ use crate::screen::Monitor; pub(crate) fn get_monitors() -> Vec { log::warn!("Screen::get_monitors() is currently unimplemented for X11 platforms."); - Vec::::new() + Vec::new() } diff --git a/druid-shell/src/platform/x11/window.rs b/druid-shell/src/platform/x11/window.rs index 5514ddce6d..0733867c85 100644 --- a/druid-shell/src/platform/x11/window.rs +++ b/druid-shell/src/platform/x11/window.rs @@ -45,13 +45,13 @@ use crate::mouse::{Cursor, MouseButton, MouseButtons, MouseEvent}; use crate::piet::{Piet, RenderContext}; use crate::scale::Scale; use crate::window::{IdleToken, Text, TimerToken, WinHandler}; +use crate::window; use super::application::Application; use super::keycodes; use super::menu::Menu; use super::util::{self, Timer}; -use crate::window::WindowState as WindowSizeState; // Avoid name conflict. /// A version of XCB's `xcb_visualtype_t` struct. This was copied from the [example] in x11rb; it /// is used to interoperate with cairo. @@ -132,7 +132,7 @@ impl WindowBuilder { log::warn!("WindowBuilder::set_position is currently unimplemented for X11 platforms."); } - pub fn set_window_state(&self, _state: WindowSizeState) { + pub fn set_window_state(&self, _state: window::WindowState) { log::warn!("WindowBuilder::set_window_state is currently unimplemented for X11 platforms."); } @@ -1379,13 +1379,13 @@ impl WindowHandle { Size::new(0.0, 0.0) } - pub fn set_window_state(&self, _state: WindowSizeState) { + pub fn set_window_state(&self, _state: window::WindowState) { log::warn!("WindowHandle::set_window_state is currently unimplemented for X11 platforms."); } - pub fn get_window_state(&self) -> WindowSizeState { + pub fn get_window_state(&self) -> window::WindowState { log::warn!("WindowHandle::get_window_state is currently unimplemented for X11 platforms."); - WindowSizeState::RESTORED + window::WindowState::RESTORED } pub fn handle_titlebar(&self, _val: bool) { From 0e9dbacf2ebbbb259a092d13194db4e9951ff1f4 Mon Sep 17 00:00:00 2001 From: rhzk Date: Fri, 11 Sep 2020 02:40:55 +0200 Subject: [PATCH 13/18] Resolve merge --- druid-shell/src/platform/windows/window.rs | 14 ++++---------- druid/src/lib.rs | 3 ++- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/druid-shell/src/platform/windows/window.rs b/druid-shell/src/platform/windows/window.rs index 17ddcbfb12..1476b7469a 100644 --- a/druid-shell/src/platform/windows/window.rs +++ b/druid-shell/src/platform/windows/window.rs @@ -645,9 +645,7 @@ impl WndProc for MyWndProc { let mut rect: RECT = mem::zeroed(); // TODO: use GetUpdateRgn for more conservative invalidation GetUpdateRect(hwnd, &mut rect, FALSE); - ValidateRect(hwnd, null_mut()); - let rect_dp = util::recti_to_rect(rect).to_dp(self.scale()); if rect_dp.area() != 0.0 { self.invalidate_rect(rect_dp); @@ -655,7 +653,7 @@ impl WndProc for MyWndProc { let invalid = self.take_invalid(); if !invalid.rects().is_empty() { if s.render_target.is_none() { - let rt = paint::create_render_target(&self.d2d_factory, hwnd); + let rt = paint::create_render_target(&self.d2d_factory, hwnd, self.scale()); s.render_target = rt.ok(); } s.handler.rebuild_resources(); @@ -695,12 +693,8 @@ impl WndProc for MyWndProc { { let rect_dp = self.area().size_dp().to_rect(); s.handler.rebuild_resources(); - s.render( - &self.d2d_factory, - &self.dwrite_factory, - &self.handle, - rect_dp, - ); + s.render(&self.d2d_factory, &self.dwrite_factory, &rect_dp.into()); + self.clear_invalid(); } if let Some(ref mut ds) = s.dcomp_state { @@ -730,7 +724,7 @@ impl WndProc for MyWndProc { if let Some(mut s) = s.as_mut() { if let Some(func) = OPTIONAL_FUNCTIONS.GetSystemMetricsForDpi { // This function is only supported on windows 10 - let dpi = self.scale().x() * 96.; + let dpi = self.scale().x() * SCALE_TARGET_DPI; // Height of the different parts that make the titlebar let border = func(SM_CXPADDEDBORDER, dpi as u32); let frame = func(SM_CYSIZEFRAME, dpi as u32); diff --git a/druid/src/lib.rs b/druid/src/lib.rs index 66f4e09e83..c9d4bb2006 100644 --- a/druid/src/lib.rs +++ b/druid/src/lib.rs @@ -179,7 +179,8 @@ pub use shell::keyboard_types; pub use shell::{ Application, Clipboard, ClipboardFormat, Code, Cursor, Error as PlatformError, FileDialogOptions, FileInfo, FileSpec, FormatId, HotKey, KbKey, KeyEvent, Location, Modifiers, - Monitor, MouseButton, MouseButtons, RawMods, Region, Scalable, Scale, Screen, SysMods, TimerToken, WindowHandle, WindowState, + Monitor, MouseButton, MouseButtons, RawMods, Region, Scalable, Scale, Screen, SysMods, + TimerToken, WindowHandle, WindowState, }; pub use crate::core::WidgetPod; From 0f3f3190414415e7b54d1178027c687cfe932599 Mon Sep 17 00:00:00 2001 From: rhzk Date: Fri, 11 Sep 2020 02:43:34 +0200 Subject: [PATCH 14/18] Resolve merge --- druid-shell/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/druid-shell/src/lib.rs b/druid-shell/src/lib.rs index a59e7c77eb..0e18bd3cc7 100644 --- a/druid-shell/src/lib.rs +++ b/druid-shell/src/lib.rs @@ -63,6 +63,8 @@ pub use mouse::{Cursor, MouseButton, MouseButtons, MouseEvent}; pub use region::Region; pub use scale::{Scalable, Scale, ScaledArea}; pub use screen::{Monitor, Screen}; -pub use window::{IdleHandle, IdleToken, TimerToken, WinHandler, WindowBuilder, WindowHandle, WindowState}; +pub use window::{ + IdleHandle, IdleToken, TimerToken, WinHandler, WindowBuilder, WindowHandle, WindowState, +}; pub use keyboard_types; From e7f0b56ccd9173c8053858be4dac1f149a8d6f09 Mon Sep 17 00:00:00 2001 From: rhzk Date: Fri, 11 Sep 2020 03:01:02 +0200 Subject: [PATCH 15/18] Resolve merge --- druid-shell/src/platform/gtk/window.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/druid-shell/src/platform/gtk/window.rs b/druid-shell/src/platform/gtk/window.rs index 248f85e523..54bf9bce8d 100644 --- a/druid-shell/src/platform/gtk/window.rs +++ b/druid-shell/src/platform/gtk/window.rs @@ -39,7 +39,7 @@ use crate::common_util::IdleCallback; use crate::dialog::{FileDialogOptions, FileDialogType, FileInfo}; use crate::error::Error as ShellError; use crate::keyboard::{KbKey, KeyEvent, KeyState, Modifiers}; -use crate::mouse::{Cursor, MouseButton, MouseButtons, MouseEvent} +use crate::mouse::{Cursor, MouseButton, MouseButtons, MouseEvent}; use crate::region::Region; use crate::scale::{Scalable, Scale, ScaledArea}; use crate::window::{IdleToken, TimerToken, WinHandler}; From 8444bfc138d030f4821b0d3827bb65db02053c92 Mon Sep 17 00:00:00 2001 From: rhzk Date: Fri, 11 Sep 2020 03:55:20 +0200 Subject: [PATCH 16/18] Fixed size not scaled to DPI on startup --- druid-shell/src/platform/windows/window.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/druid-shell/src/platform/windows/window.rs b/druid-shell/src/platform/windows/window.rs index 1476b7469a..e8cb30acf1 100644 --- a/druid-shell/src/platform/windows/window.rs +++ b/druid-shell/src/platform/windows/window.rs @@ -178,7 +178,7 @@ impl DeferredQueue { unsafe { if SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, (size.width * proc.scale().x()) as i32, (size.height * proc.scale().y()) as i32, SWP_NOMOVE | SWP_NOZORDER) == 0 { warn!( - "failed to move window: {}", + "failed to resize window: {}", Error::Hr(HRESULT_FROM_WIN32(GetLastError())) ); }; @@ -1376,6 +1376,7 @@ impl WindowBuilder { register_accel(hwnd, &accels); } + handle.set_size(size_px); handle.set_window_state(self.state); Ok(handle) From 83b2759f36c7cc4ba25025e4704910507a35e80a Mon Sep 17 00:00:00 2001 From: rhzk Date: Fri, 11 Sep 2020 04:03:57 +0200 Subject: [PATCH 17/18] Unmaximized window when position is changed --- druid-shell/src/platform/windows/window.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/druid-shell/src/platform/windows/window.rs b/druid-shell/src/platform/windows/window.rs index e8cb30acf1..25c4d5d01f 100644 --- a/druid-shell/src/platform/windows/window.rs +++ b/druid-shell/src/platform/windows/window.rs @@ -1658,6 +1658,7 @@ impl WindowHandle { // Sets the position of the window in virtual screen coordinates pub fn set_position(&self, position: Point) { + DeferredQueue::add(self.state.clone(), DeferredOp::SetWindowSizeState(window::WindowState::RESTORED)); DeferredQueue::add(self.state.clone(), DeferredOp::SetPosition(position)); } From ffb743f34df261088bebe801d26ff48d2db519c6 Mon Sep 17 00:00:00 2001 From: rhzk Date: Fri, 11 Sep 2020 04:35:28 +0200 Subject: [PATCH 18/18] Solved issue with Windows default size --- druid-shell/src/platform/windows/window.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/druid-shell/src/platform/windows/window.rs b/druid-shell/src/platform/windows/window.rs index 25c4d5d01f..9275038e60 100644 --- a/druid-shell/src/platform/windows/window.rs +++ b/druid-shell/src/platform/windows/window.rs @@ -1376,7 +1376,7 @@ impl WindowBuilder { register_accel(hwnd, &accels); } - handle.set_size(size_px); + handle.set_size(handle.get_size()); handle.set_window_state(self.state); Ok(handle)