On Windows, allow the creation of popup window (#1895)

Add with_owner_window to WindowBuilderExtWindows.
Add set_enable to WindowExtWindows.
This commit is contained in:
Rodrigodd 2021-04-10 10:47:19 -03:00 committed by GitHub
parent 629cd86c7c
commit dabcb1834d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 80 additions and 11 deletions

View file

@ -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)

View file

@ -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 <https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enablewindow#remarks>
/// and <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#disabled-windows>
fn set_enable(&self, enabled: bool);
/// This sets `ICON_BIG`. A good ceiling here is 256x256.
fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>);
@ -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<Icon>) {
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 <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#child-windows>
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 <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#owned-windows>
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
}

View file

@ -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<HWND>,
pub parent: Parent,
pub menu: Option<HMENU>,
pub taskbar_icon: Option<Icon>,
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,

View file

@ -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<T: 'static>(
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<T: 'static>(
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(),

View file

@ -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;
}