From dabcb1834d4f2c6761854d911ec17f1f01e3cc10 Mon Sep 17 00:00:00 2001 From: Rodrigodd <51273772+Rodrigodd@users.noreply.github.com> Date: Sat, 10 Apr 2021 10:47:19 -0300 Subject: [PATCH] On Windows, allow the creation of popup window (#1895) Add with_owner_window to WindowBuilderExtWindows. Add set_enable to WindowExtWindows. --- CHANGELOG.md | 2 + src/platform/windows.rs | 48 ++++++++++++++++++++++- src/platform_impl/windows/mod.rs | 11 +++++- src/platform_impl/windows/window.rs | 26 ++++++++---- src/platform_impl/windows/window_state.rs | 4 ++ 5 files changed, 80 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f18446b..d88de9d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,8 @@ - Added `WindowBuilder::with_position` to allow setting the position of a `Window` on creation. Supported on Windows, macOS and X11. - Added `Window::drag_window`. Implemented on Windows, macOS, X11 and Wayland. - On X11, bump `mio` to 0.7. +- On Windows, added `WindowBuilderExtWindows::with_owner_window` to allow creating popup windows. +- On Windows, added `WindowExtWindows::set_enable` to allow creating modal popup windows. - On macOS, emit `RedrawRequested` events immediately while the window is being resized. # 0.24.0 (2020-12-09) diff --git a/src/platform/windows.rs b/src/platform/windows.rs index f2752aa0..83a33c63 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -12,7 +12,7 @@ use crate::{ event::DeviceId, event_loop::EventLoop, monitor::MonitorHandle, - platform_impl::{EventLoop as WindowsEventLoop, WinIcon}, + platform_impl::{EventLoop as WindowsEventLoop, Parent, WinIcon}, window::{BadIcon, Icon, Theme, Window, WindowBuilder}, }; @@ -78,6 +78,21 @@ pub trait WindowExtWindows { /// The pointer will become invalid when the native window was destroyed. fn hwnd(&self) -> *mut libc::c_void; + /// Enables or disables mouse and keyboard input to the specified window. + /// + /// A window must be enabled before it can be activated. + /// If an application has create a modal dialog box by disabling its owner window + /// (as described in [`WindowBuilderExtWindows::with_owner_window`]), the application must enable + /// the owner window before destroying the dialog box. + /// Otherwise, another window will receive the keyboard focus and be activated. + /// + /// If a child window is disabled, it is ignored when the system tries to determine which + /// window should receive mouse messages. + /// + /// For more information, see + /// and + fn set_enable(&self, enabled: bool); + /// This sets `ICON_BIG`. A good ceiling here is 256x256. fn set_taskbar_icon(&self, taskbar_icon: Option); @@ -96,6 +111,13 @@ impl WindowExtWindows for Window { self.window.hwnd() as *mut _ } + #[inline] + fn set_enable(&self, enabled: bool) { + unsafe { + winapi::um::winuser::EnableWindow(self.hwnd() as _, enabled as _); + } + } + #[inline] fn set_taskbar_icon(&self, taskbar_icon: Option) { self.window.set_taskbar_icon(taskbar_icon) @@ -110,8 +132,24 @@ impl WindowExtWindows for Window { /// Additional methods on `WindowBuilder` that are specific to Windows. pub trait WindowBuilderExtWindows { /// Sets a parent to the window to be created. + /// + /// A child window has the WS_CHILD style and is confined to the client area of its parent window. + /// + /// For more information, see fn with_parent_window(self, parent: HWND) -> WindowBuilder; + /// Set an owner to the window to be created. Can be used to create a dialog box, for example. + /// Can be used in combination with [`WindowExtWindows::set_enable(false)`](WindowExtWindows::set_enable) + /// on the owner window to create a modal dialog box. + /// + /// From MSDN: + /// - An owned window is always above its owner in the z-order. + /// - The system automatically destroys an owned window when its owner is destroyed. + /// - An owned window is hidden when its owner is minimized. + /// + /// For more information, see + fn with_owner_window(self, parent: HWND) -> WindowBuilder; + /// Sets a menu on the window to be created. /// /// Parent and menu are mutually exclusive; a child window cannot have a menu! @@ -143,7 +181,13 @@ pub trait WindowBuilderExtWindows { impl WindowBuilderExtWindows for WindowBuilder { #[inline] fn with_parent_window(mut self, parent: HWND) -> WindowBuilder { - self.platform_specific.parent = Some(parent); + self.platform_specific.parent = Parent::ChildOf(parent); + self + } + + #[inline] + fn with_owner_window(mut self, parent: HWND) -> WindowBuilder { + self.platform_specific.parent = Parent::OwnedBy(parent); self } diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index d748e6f0..07629e6d 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -15,9 +15,16 @@ use crate::event::DeviceId as RootDeviceId; use crate::icon::Icon; use crate::window::Theme; +#[derive(Clone)] +pub enum Parent { + None, + ChildOf(HWND), + OwnedBy(HWND), +} + #[derive(Clone)] pub struct PlatformSpecificWindowBuilderAttributes { - pub parent: Option, + pub parent: Parent, pub menu: Option, pub taskbar_icon: Option, pub no_redirection_bitmap: bool, @@ -28,7 +35,7 @@ pub struct PlatformSpecificWindowBuilderAttributes { impl Default for PlatformSpecificWindowBuilderAttributes { fn default() -> Self { Self { - parent: None, + parent: Parent::None, menu: None, taskbar_icon: None, no_redirection_bitmap: false, diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index 21249593..2e5e8e57 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -44,7 +44,7 @@ use crate::{ icon::{self, IconType}, monitor, util, window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState}, - PlatformSpecificWindowBuilderAttributes, WindowId, + Parent, PlatformSpecificWindowBuilderAttributes, WindowId, }, window::{CursorIcon, Fullscreen, Theme, UserAttentionType, WindowAttributes}, }; @@ -733,12 +733,24 @@ unsafe fn init( window_flags.set(WindowFlags::TRANSPARENT, attributes.transparent); // WindowFlags::VISIBLE and MAXIMIZED are set down below after the window has been configured. window_flags.set(WindowFlags::RESIZABLE, attributes.resizable); - window_flags.set(WindowFlags::CHILD, pl_attribs.parent.is_some()); - window_flags.set(WindowFlags::ON_TASKBAR, true); - if pl_attribs.parent.is_some() && pl_attribs.menu.is_some() { - warn!("Setting a menu on windows that have a parent is unsupported"); - } + let parent = match pl_attribs.parent { + Parent::ChildOf(parent) => { + window_flags.set(WindowFlags::CHILD, true); + if pl_attribs.menu.is_some() { + warn!("Setting a menu on a child window is unsupported"); + } + Some(parent) + } + Parent::OwnedBy(parent) => { + window_flags.set(WindowFlags::POPUP, true); + Some(parent) + } + Parent::None => { + window_flags.set(WindowFlags::ON_TASKBAR, true); + None + } + }; // creating the real window this time, by using the functions in `extra_functions` let real_window = { @@ -752,7 +764,7 @@ unsafe fn init( winuser::CW_USEDEFAULT, winuser::CW_USEDEFAULT, winuser::CW_USEDEFAULT, - pl_attribs.parent.unwrap_or(ptr::null_mut()), + parent.unwrap_or(ptr::null_mut()), pl_attribs.menu.unwrap_or(ptr::null_mut()), libloaderapi::GetModuleHandleW(ptr::null()), ptr::null_mut(), diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index 9ef55232..9e41bd79 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -68,6 +68,7 @@ bitflags! { const TRANSPARENT = 1 << 6; const CHILD = 1 << 7; const MAXIMIZED = 1 << 8; + const POPUP = 1 << 14; /// Marker flag for fullscreen. Should always match `WindowState::fullscreen`, but is /// included here to make masking easier. @@ -213,6 +214,9 @@ impl WindowFlags { if self.contains(WindowFlags::CHILD) { style |= WS_CHILD; // This is incompatible with WS_POPUP if that gets added eventually. } + if self.contains(WindowFlags::POPUP) { + style |= WS_POPUP; + } if self.contains(WindowFlags::MINIMIZED) { style |= WS_MINIMIZE; }