From 1eaf8d9c9b2f6153d1d016f37206f81fb41645d6 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Fri, 28 Oct 2022 13:34:31 +0200 Subject: [PATCH 1/9] On Windows and macOS, add API to enable/disable window controls --- CHANGELOG.md | 1 + src/platform_impl/android/mod.rs | 18 +++ src/platform_impl/ios/window.rs | 27 +++++ src/platform_impl/linux/mod.rs | 27 +++++ src/platform_impl/linux/wayland/window/mod.rs | 24 ++++ src/platform_impl/linux/x11/window.rs | 24 ++++ src/platform_impl/macos/appkit/window.rs | 6 + src/platform_impl/macos/window.rs | 60 ++++++++++ src/platform_impl/web/window.rs | 27 +++++ src/platform_impl/windows/window.rs | 60 ++++++++++ src/platform_impl/windows/window_state.rs | 63 +++++++---- src/window.rs | 105 ++++++++++++++++++ 12 files changed, 423 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b745bc148..8f3853e0d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ And please only add new entries to the top of this list, right below the `# Unre # Unreleased +- On Windows and MacOS, add API to enable/disable window controls (close, minimize, ...etc). - On Windows, fix focusing menubar when pressing `Alt`. - On MacOS, made `accepts_first_mouse` configurable. - Migrated `WindowBuilderExtUnix::with_resize_increments` to `WindowBuilder`. diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index 5f402cf9db..eac6b19170 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -771,6 +771,24 @@ impl Window { false } + pub fn set_minimizable(&self, _minimizable: bool) {} + + pub fn is_minimizable(&self) -> bool { + false + } + + pub fn set_maximizable(&self, _maximizable: bool) {} + + pub fn is_maximizable(&self) -> bool { + false + } + + pub fn set_closable(&self, _closable: bool) {} + + pub fn is_closable(&self) -> bool { + false + } + pub fn set_minimized(&self, _minimized: bool) {} pub fn set_maximized(&self, _maximized: bool) {} diff --git a/src/platform_impl/ios/window.rs b/src/platform_impl/ios/window.rs index 2333d60fca..10eca55c18 100644 --- a/src/platform_impl/ios/window.rs +++ b/src/platform_impl/ios/window.rs @@ -172,6 +172,33 @@ impl Inner { false } + pub fn set_minimizable(&self, _minimizable: bool) { + warn!("`Window::set_minimizable` is ignored on iOS") + } + + pub fn is_minimizable(&self) -> bool { + warn!("`Window::is_minimizable` is ignored on iOS"); + false + } + + pub fn set_maximizable(&self, _maximizable: bool) { + warn!("`Window::set_maximizable` is ignored on iOS") + } + + pub fn is_maximizable(&self) -> bool { + warn!("`Window::is_maximizable` is ignored on iOS"); + false + } + + pub fn set_closable(&self, _closable: bool) { + warn!("`Window::set_closable` is ignored on iOS") + } + + pub fn is_closable(&self) -> bool { + warn!("`Window::is_closable` is ignored on iOS"); + false + } + pub fn scale_factor(&self) -> f64 { unsafe { let hidpi: CGFloat = msg_send![self.view, contentScaleFactor]; diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 4e5f7610cf..55bfb5240b 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -404,6 +404,33 @@ impl Window { pub fn is_resizable(&self) -> bool { x11_or_wayland!(match self; Window(w) => w.is_resizable()) } + #[inline] + pub fn set_minimizable(&self, minimizable: bool) { + x11_or_wayland!(match self; Window(w) => w.set_minimizable(minimizable)) + } + + #[inline] + pub fn is_minimizable(&self) -> bool { + x11_or_wayland!(match self; Window(w) => w.is_minimizable()) + } + #[inline] + pub fn set_maximizable(&self, maximizable: bool) { + x11_or_wayland!(match self; Window(w) => w.set_maximizable(maximizable)) + } + + #[inline] + pub fn is_maximizable(&self) -> bool { + x11_or_wayland!(match self; Window(w) => w.is_maximizable()) + } + #[inline] + pub fn set_closable(&self, closable: bool) { + x11_or_wayland!(match self; Window(w) => w.set_closable(closable)) + } + + #[inline] + pub fn is_closable(&self) -> bool { + x11_or_wayland!(match self; Window(w) => w.is_closable()) + } #[inline] pub fn set_cursor_icon(&self, cursor: CursorIcon) { diff --git a/src/platform_impl/linux/wayland/window/mod.rs b/src/platform_impl/linux/wayland/window/mod.rs index 18b84afa2a..d6c2309e8a 100644 --- a/src/platform_impl/linux/wayland/window/mod.rs +++ b/src/platform_impl/linux/wayland/window/mod.rs @@ -421,6 +421,30 @@ impl Window { self.resizeable.load(Ordering::Relaxed) } + #[inline] + pub fn set_maximizable(&self, maximizable: bool) {} + + #[inline] + pub fn is_maximizable(&self) -> bool { + true + } + + #[inline] + pub fn set_minimizable(&self, minimizable: bool) {} + + #[inline] + pub fn is_minimizable(&self) -> bool { + true + } + + #[inline] + pub fn set_closable(&self, closable: bool) {} + + #[inline] + pub fn is_closable(&self) -> bool { + true + } + #[inline] pub fn scale_factor(&self) -> u32 { // The scale factor from `get_surface_scale_factor` is always greater than zero, so diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index 2c600a81be..a594a54b75 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -1258,6 +1258,30 @@ impl UnownedWindow { self.shared_state_lock().is_resizable } + #[inline] + pub fn set_maximizable(&self, maximizable: bool) {} + + #[inline] + pub fn is_maximizable(&self) -> bool { + true + } + + #[inline] + pub fn set_minimizable(&self, minimizable: bool) {} + + #[inline] + pub fn is_minimizable(&self) -> bool { + true + } + + #[inline] + pub fn set_closable(&self, closable: bool) {} + + #[inline] + pub fn is_closable(&self) -> bool { + true + } + #[inline] pub fn xlib_display(&self) -> *mut c_void { self.xconn.display as _ diff --git a/src/platform_impl/macos/appkit/window.rs b/src/platform_impl/macos/appkit/window.rs index 8e504abd3b..c73f29c4b3 100644 --- a/src/platform_impl/macos/appkit/window.rs +++ b/src/platform_impl/macos/appkit/window.rs @@ -170,6 +170,12 @@ extern_methods!( #[sel(isResizable)] pub fn isResizable(&self) -> bool; + #[sel(isMiniaturizable)] + pub fn isMiniaturizable(&self) -> bool; + + #[sel(hasCloseBox)] + pub fn hasCloseBox(&self) -> bool; + #[sel(isMiniaturized)] pub fn isMiniaturized(&self) -> bool; diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 8723c2cb6b..e48f0695a1 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -267,6 +267,14 @@ impl WinitWindow { masks &= !NSWindowStyleMask::NSResizableWindowMask; } + if !attrs.minimizable { + masks &= !NSWindowStyleMask::NSMiniaturizableWindowMask; + } + + if !attrs.closable { + masks &= !NSWindowStyleMask::NSClosableWindowMask; + } + if pl_attrs.fullsize_content_view { masks |= NSWindowStyleMask::NSFullSizeContentViewWindowMask; } @@ -331,6 +339,12 @@ impl WinitWindow { this.setLevel(NSWindowLevel::Floating); } + if !attrs.maximizable { + if let Some(button) = this.standardWindowButton(NSWindowButton::Zoom) { + button.setEnabled(false); + } + } + if let Some(increments) = attrs.resize_increments { let increments = increments.to_logical(this.scale_factor()); let (w, h) = (increments.width, increments.height); @@ -620,6 +634,52 @@ impl WinitWindow { self.isResizable() } + #[inline] + pub fn set_minimizable(&self, minimizable: bool) { + let mut mask = unsafe { self.ns_window.styleMask() }; + if minimizable { + mask |= NSWindowStyleMask::NSMiniaturizableWindowMask; + } else { + mask &= !NSWindowStyleMask::NSMiniaturizableWindowMask; + } + self.set_style_mask_async(mask); + } + + #[inline] + pub fn is_minimizable(&self) -> bool { + self.isMiniaturizable() + } + + #[inline] + pub fn set_maximizable(&self, maximizable: bool) { + if let Some(button) = self.standardWindowButton(NSWindowButton::Zoom) { + button.setEnabled(maximizable); + } + } + + #[inline] + pub fn is_maximizable(&self) -> bool { + self.standardWindowButton(NSWindowButton::Zoom) + .map(|b| b.isEnabled()) + .unwrap_or(true) + } + + #[inline] + pub fn set_closable(&self, closable: bool) { + let mut mask = unsafe { self.ns_window.styleMask() }; + if closable { + mask |= NSWindowStyleMask::NSClosableWindowMask; + } else { + mask &= !NSWindowStyleMask::NSClosableWindowMask; + } + self.set_style_mask_async(mask); + } + + #[inline] + pub fn is_closable(&self) -> bool { + self.hasCloseBox() + } + pub fn set_cursor_icon(&self, icon: CursorIcon) { let view = self.view(); let mut cursor_state = view.state.cursor_state.lock().unwrap(); diff --git a/src/platform_impl/web/window.rs b/src/platform_impl/web/window.rs index 3dc7a5dccf..b37a788182 100644 --- a/src/platform_impl/web/window.rs +++ b/src/platform_impl/web/window.rs @@ -171,6 +171,33 @@ impl Window { true } + #[inline] + pub fn set_maximizable(&self, _maximizable: bool) { + // Intentionally a no-op: users can't maximize canvas elements + } + + pub fn is_maximizable(&self) -> bool { + true + } + + #[inline] + pub fn set_minimizable(&self, _minimizable: bool) { + // Intentionally a no-op: users can't minmiize canvas elements + } + + pub fn is_minimizable(&self) -> bool { + true + } + + #[inline] + pub fn set_closable(&self, _closable: bool) { + // Intentionally a no-op: users can't close canvas elements + } + + pub fn is_closable(&self) -> bool { + true + } + #[inline] pub fn scale_factor(&self) -> f64 { super::backend::scale_factor() diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index cb57a3ca8d..86f9a63a84 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -262,6 +262,59 @@ impl Window { window_state.window_flags.contains(WindowFlags::RESIZABLE) } + #[inline] + pub fn set_minimizable(&self, minimizable: bool) { + let window = self.window.clone(); + let window_state = Arc::clone(&self.window_state); + + self.thread_executor.execute_in_thread(move || { + WindowState::set_window_flags(window_state.lock().unwrap(), window.0, |f| { + f.set(WindowFlags::MINIMIZABLE, minimizable) + }); + }); + } + + #[inline] + pub fn is_minimizable(&self) -> bool { + let window_state = self.window_state_lock(); + window_state.window_flags.contains(WindowFlags::MINIMIZABLE) + } + + #[inline] + pub fn set_maximizable(&self, maximizable: bool) { + let window = self.window.clone(); + let window_state = Arc::clone(&self.window_state); + + self.thread_executor.execute_in_thread(move || { + WindowState::set_window_flags(window_state.lock().unwrap(), window.0, |f| { + f.set(WindowFlags::MAXIMIZABLE, maximizable) + }); + }); + } + + #[inline] + pub fn is_maximizable(&self) -> bool { + let window_state = self.window_state_lock(); + window_state.window_flags.contains(WindowFlags::MAXIMIZABLE) + } + + #[inline] + pub fn set_closable(&self, closable: bool) { + let window = self.window.clone(); + let window_state = Arc::clone(&self.window_state); + self.thread_executor.execute_in_thread(move || { + WindowState::set_window_flags(window_state.lock().unwrap(), window.0, |f| { + f.set(WindowFlags::CLOSABLE, closable) + }); + }); + } + + #[inline] + pub fn is_closable(&self) -> bool { + let window_state = self.window_state_lock(); + window_state.window_flags.contains(WindowFlags::CLOSABLE) + } + /// Returns the `hwnd` of this window. #[inline] pub fn hwnd(&self) -> HWND { @@ -903,6 +956,8 @@ impl<'a, T: 'static> InitData<'a, T> { // attribute is correctly applied. win.set_visible(attributes.visible); + win.set_closable(attributes.closable); + if attributes.fullscreen.is_some() { win.set_fullscreen(attributes.fullscreen); force_window_active(win.window.0); @@ -965,6 +1020,11 @@ where 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::MINIMIZABLE, attributes.minimizable); + window_flags.set(WindowFlags::MAXIMIZABLE, attributes.maximizable); + // will be changed later using `window.set_closable()` + // but we need to have a default for the diffing to work + window_flags.set(WindowFlags::CLOSABLE, true); let parent = match pl_attribs.parent { Parent::ChildOf(parent) => { diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index 4f3e88886f..c32a62b483 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -11,8 +11,9 @@ use windows_sys::Win32::{ Foundation::{HWND, RECT}, Graphics::Gdi::InvalidateRgn, UI::WindowsAndMessaging::{ - AdjustWindowRectEx, GetMenu, GetWindowLongW, SendMessageW, SetWindowLongW, SetWindowPos, - ShowWindow, GWL_EXSTYLE, GWL_STYLE, HWND_NOTOPMOST, HWND_TOPMOST, SWP_ASYNCWINDOWPOS, + AdjustWindowRectEx, EnableMenuItem, GetMenu, GetSystemMenu, GetWindowLongW, SendMessageW, + SetWindowLongW, SetWindowPos, ShowWindow, GWL_EXSTYLE, GWL_STYLE, HWND_NOTOPMOST, + HWND_TOPMOST, MF_BYCOMMAND, MF_DISABLED, MF_ENABLED, SC_CLOSE, SWP_ASYNCWINDOWPOS, SWP_FRAMECHANGED, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOREPOSITION, SWP_NOSIZE, SWP_NOZORDER, SW_HIDE, SW_MAXIMIZE, SW_MINIMIZE, SW_RESTORE, SW_SHOW, WINDOWPLACEMENT, WINDOW_EX_STYLE, WINDOW_STYLE, WS_BORDER, WS_CAPTION, WS_CHILD, WS_CLIPCHILDREN, WS_CLIPSIBLINGS, @@ -77,37 +78,40 @@ bitflags! { bitflags! { pub struct WindowFlags: u32 { const RESIZABLE = 1 << 0; - const VISIBLE = 1 << 1; - const ON_TASKBAR = 1 << 2; - const ALWAYS_ON_TOP = 1 << 3; - const NO_BACK_BUFFER = 1 << 4; - const TRANSPARENT = 1 << 5; - const CHILD = 1 << 6; - const MAXIMIZED = 1 << 7; - const POPUP = 1 << 8; + const MINIMIZABLE = 1 << 1; + const MAXIMIZABLE = 1 << 2; + const CLOSABLE = 1 << 3; + const VISIBLE = 1 << 4; + const ON_TASKBAR = 1 << 5; + const ALWAYS_ON_TOP = 1 << 6; + const NO_BACK_BUFFER = 1 << 7; + const TRANSPARENT = 1 << 8; + const CHILD = 1 << 9; + const MAXIMIZED = 1 << 10; + const POPUP = 1 << 11; /// Marker flag for fullscreen. Should always match `WindowState::fullscreen`, but is /// included here to make masking easier. - const MARKER_EXCLUSIVE_FULLSCREEN = 1 << 9; - const MARKER_BORDERLESS_FULLSCREEN = 1 << 10; + const MARKER_EXCLUSIVE_FULLSCREEN = 1 << 12; + const MARKER_BORDERLESS_FULLSCREEN = 1 << 13; /// The `WM_SIZE` event contains some parameters that can effect the state of `WindowFlags`. /// In most cases, it's okay to let those parameters change the state. However, when we're /// running the `WindowFlags::apply_diff` function, we *don't* want those parameters to /// effect our stored state, because the purpose of `apply_diff` is to update the actual /// window's state to match our stored state. This controls whether to accept those changes. - const MARKER_RETAIN_STATE_ON_SIZE = 1 << 11; + const MARKER_RETAIN_STATE_ON_SIZE = 1 << 14; - const MARKER_IN_SIZE_MOVE = 1 << 12; + const MARKER_IN_SIZE_MOVE = 1 << 15; - const MINIMIZED = 1 << 13; + const MINIMIZED = 1 << 16; - const IGNORE_CURSOR_EVENT = 1 << 14; + const IGNORE_CURSOR_EVENT = 1 << 17; /// Fully decorated window (incl. caption, border and drop shadow). - const MARKER_DECORATIONS = 1 << 15; + const MARKER_DECORATIONS = 1 << 18; /// Drop shadow for undecorated windows. - const MARKER_UNDECORATED_SHADOW = 1 << 16; + const MARKER_UNDECORATED_SHADOW = 1 << 19; const EXCLUSIVE_FULLSCREEN_OR_MASK = WindowFlags::ALWAYS_ON_TOP.bits; } @@ -246,7 +250,13 @@ impl WindowFlags { let mut style_ex = WS_EX_WINDOWEDGE | WS_EX_ACCEPTFILES; if self.contains(WindowFlags::RESIZABLE) { - style |= WS_SIZEBOX | WS_MAXIMIZEBOX; + style |= WS_SIZEBOX; + } + if self.contains(WindowFlags::MAXIMIZABLE) { + style |= WS_MAXIMIZEBOX; + } + if self.contains(WindowFlags::MINIMIZABLE) { + style |= WS_MINIMIZEBOX; } if self.contains(WindowFlags::VISIBLE) { style |= WS_VISIBLE; @@ -345,6 +355,21 @@ impl WindowFlags { } } + if diff.contains(WindowFlags::CLOSABLE) || new.contains(WindowFlags::CLOSABLE) { + unsafe { + EnableMenuItem( + GetSystemMenu(window, 0), + SC_CLOSE, + MF_BYCOMMAND + | if new.contains(WindowFlags::CLOSABLE) { + MF_ENABLED + } else { + MF_DISABLED + }, + ); + } + } + if !new.contains(WindowFlags::VISIBLE) { unsafe { ShowWindow(window, SW_HIDE); diff --git a/src/window.rs b/src/window.rs index 65e652d8b3..81c866aa93 100644 --- a/src/window.rs +++ b/src/window.rs @@ -126,6 +126,9 @@ pub(crate) struct WindowAttributes { pub max_inner_size: Option, pub position: Option, pub resizable: bool, + pub maximizable: bool, + pub minimizable: bool, + pub closable: bool, pub title: String, pub fullscreen: Option, pub maximized: bool, @@ -147,6 +150,9 @@ impl Default for WindowAttributes { max_inner_size: None, position: None, resizable: true, + minimizable: true, + maximizable: true, + closable: true, title: "winit window".to_owned(), maximized: false, fullscreen: None, @@ -242,6 +248,39 @@ impl WindowBuilder { self } + /// Sets whether the window is maximizable or not. + /// + /// The default is `true`. + /// + /// See [`Window::set_maximizable`] for details. + #[inline] + pub fn with_maximizable(mut self, maximizable: bool) -> Self { + self.window.maximizable = maximizable; + self + } + + /// Sets whether the window is minimizable or not. + /// + /// The default is `true`. + /// + /// See [`Window::set_minimizable`] for details. + #[inline] + pub fn with_minimizable(mut self, minimizable: bool) -> Self { + self.window.minimizable = minimizable; + self + } + + /// Sets whether the window is closable or not. + /// + /// The default is `true`. + /// + /// See [`Window::set_closable`] for details. + #[inline] + pub fn with_closable(mut self, closable: bool) -> Self { + self.window.closable = closable; + self + } + /// Sets the initial title of the window in the title bar. /// /// The default is `"winit window"`. @@ -732,6 +771,72 @@ impl Window { self.window.is_resizable() } + /// Sets whether the window is minimizable or not. + /// + /// ## Platform-specific + /// + /// - **Wayland / X11:** Not implemented. + /// - **Web / iOS / Android:** Unsupported. + #[inline] + pub fn set_minimizable(&self, minimizable: bool) { + self.window.set_minimizable(minimizable) + } + + /// Gets the window's current minimizable state. + /// + /// ## Platform-specific + /// + /// - **Wayland / X11:** Not implemented. + /// - **Web / iOS / Android:** Unsupported. + #[inline] + pub fn is_minimizable(&self) -> bool { + self.window.is_minimizable() + } + + /// Sets whether the window is maximizable or not. + /// + /// ## Platform-specific + /// + /// - **Wayland / X11:** Not implemented. + /// - **Web / iOS / Android:** Unsupported. + #[inline] + pub fn set_maximizable(&self, maximizable: bool) { + self.window.set_maximizable(maximizable) + } + + /// Gets the window's current maximizable state. + /// + /// ## Platform-specific + /// + /// - **Wayland / X11:** Not implemented. + /// - **Web / iOS / Android:** Unsupported. + #[inline] + pub fn is_maximizable(&self) -> bool { + self.window.is_maximizable() + } + + /// Sets whether the window is closable or not. + /// + /// ## Platform-specific + /// + /// - **Wayland / X11:** Not implemented. + /// - **Web / iOS / Android:** Unsupported. + #[inline] + pub fn set_closable(&self, closable: bool) { + self.window.set_closable(closable) + } + + /// Gets the window's current closable state. + /// + /// ## Platform-specific + /// + /// - **Wayland / X11:** Not implemented. + /// - **Web / iOS / Android:** Unsupported. + #[inline] + pub fn is_closable(&self) -> bool { + self.window.is_closable() + } + /// Sets the window to minimized or back /// /// ## Platform-specific From 7357bcebfec6e232d11fa79ec251735a8acca31e Mon Sep 17 00:00:00 2001 From: amrbashir Date: Fri, 28 Oct 2022 13:53:09 +0200 Subject: [PATCH 2/9] fix build --- src/platform_impl/linux/wayland/window/mod.rs | 6 +++--- src/platform_impl/linux/x11/window.rs | 6 +++--- src/platform_impl/macos/appkit/control.rs | 10 ++++++++++ src/platform_impl/macos/window.rs | 4 ++-- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/platform_impl/linux/wayland/window/mod.rs b/src/platform_impl/linux/wayland/window/mod.rs index d6c2309e8a..6fc9d86454 100644 --- a/src/platform_impl/linux/wayland/window/mod.rs +++ b/src/platform_impl/linux/wayland/window/mod.rs @@ -422,7 +422,7 @@ impl Window { } #[inline] - pub fn set_maximizable(&self, maximizable: bool) {} + pub fn set_maximizable(&self, _maximizable: bool) {} #[inline] pub fn is_maximizable(&self) -> bool { @@ -430,7 +430,7 @@ impl Window { } #[inline] - pub fn set_minimizable(&self, minimizable: bool) {} + pub fn set_minimizable(&self, _minimizable: bool) {} #[inline] pub fn is_minimizable(&self) -> bool { @@ -438,7 +438,7 @@ impl Window { } #[inline] - pub fn set_closable(&self, closable: bool) {} + pub fn set_closable(&self, _closable: bool) {} #[inline] pub fn is_closable(&self) -> bool { diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index a594a54b75..5e987887a3 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -1259,7 +1259,7 @@ impl UnownedWindow { } #[inline] - pub fn set_maximizable(&self, maximizable: bool) {} + pub fn set_maximizable(&self, _maximizable: bool) {} #[inline] pub fn is_maximizable(&self) -> bool { @@ -1267,7 +1267,7 @@ impl UnownedWindow { } #[inline] - pub fn set_minimizable(&self, minimizable: bool) {} + pub fn set_minimizable(&self, _minimizable: bool) {} #[inline] pub fn is_minimizable(&self) -> bool { @@ -1275,7 +1275,7 @@ impl UnownedWindow { } #[inline] - pub fn set_closable(&self, closable: bool) {} + pub fn set_closable(&self, _closable: bool) {} #[inline] pub fn is_closable(&self) -> bool { diff --git a/src/platform_impl/macos/appkit/control.rs b/src/platform_impl/macos/appkit/control.rs index 2dd2290288..f51f45bc5a 100644 --- a/src/platform_impl/macos/appkit/control.rs +++ b/src/platform_impl/macos/appkit/control.rs @@ -12,3 +12,13 @@ extern_class!( type Super = NSView; } ); + +extern_methods!( + unsafe impl NSControl { + #[sel(setEnabled:)] + pub fn setEnabled(&self, enabled: bool); + + #[sel(isEnabled)] + pub fn isEnabled(&self) -> bool; + } +); diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index e48f0695a1..f2baee109c 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -636,7 +636,7 @@ impl WinitWindow { #[inline] pub fn set_minimizable(&self, minimizable: bool) { - let mut mask = unsafe { self.ns_window.styleMask() }; + let mut mask = self.styleMask(); if minimizable { mask |= NSWindowStyleMask::NSMiniaturizableWindowMask; } else { @@ -666,7 +666,7 @@ impl WinitWindow { #[inline] pub fn set_closable(&self, closable: bool) { - let mut mask = unsafe { self.ns_window.styleMask() }; + let mut mask = self.styleMask(); if closable { mask |= NSWindowStyleMask::NSClosableWindowMask; } else { From 70e4008b88b13892dd694d5a46c26e159504e4c8 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Fri, 28 Oct 2022 14:15:39 +0200 Subject: [PATCH 3/9] missing import --- src/platform_impl/macos/appkit/control.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform_impl/macos/appkit/control.rs b/src/platform_impl/macos/appkit/control.rs index f51f45bc5a..1ca5219592 100644 --- a/src/platform_impl/macos/appkit/control.rs +++ b/src/platform_impl/macos/appkit/control.rs @@ -1,5 +1,5 @@ use objc2::foundation::NSObject; -use objc2::{extern_class, ClassType}; +use objc2::{extern_class, extern_methods, ClassType}; use super::{NSResponder, NSView}; From 2d63d37ab1ecf515b77d758f7838e99d867ccc77 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Sat, 29 Oct 2022 02:42:22 +0200 Subject: [PATCH 4/9] use `WindowButtons` flags --- src/platform_impl/android/mod.rs | 20 +--- src/platform_impl/ios/window.rs | 32 ++--- src/platform_impl/linux/mod.rs | 29 ++--- src/platform_impl/linux/wayland/window/mod.rs | 26 +---- src/platform_impl/linux/x11/window.rs | 26 +---- src/platform_impl/macos/window.rs | 60 +++++----- src/platform_impl/web/window.rs | 28 +---- src/platform_impl/windows/window.rs | 79 +++++-------- src/window.rs | 110 ++++-------------- 9 files changed, 119 insertions(+), 291 deletions(-) diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index eac6b19170..de43f5b2dd 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -24,7 +24,7 @@ use crate::{ error, event::{self, VirtualKeyCode}, event_loop::{self, ControlFlow}, - window::{self, CursorGrabMode, Theme}, + window::{self, CursorGrabMode, Theme, WindowButtons}, }; static CONFIG: Lazy> = Lazy::new(|| { @@ -771,22 +771,10 @@ impl Window { false } - pub fn set_minimizable(&self, _minimizable: bool) {} + pub fn set_window_buttons(&self, _buttons: WindowButtons) {} - pub fn is_minimizable(&self) -> bool { - false - } - - pub fn set_maximizable(&self, _maximizable: bool) {} - - pub fn is_maximizable(&self) -> bool { - false - } - - pub fn set_closable(&self, _closable: bool) {} - - pub fn is_closable(&self) -> bool { - false + pub fn window_buttons(&self) -> WindowButtons { + WindowButtons::all() } pub fn set_minimized(&self, _minimized: bool) {} diff --git a/src/platform_impl/ios/window.rs b/src/platform_impl/ios/window.rs index 10eca55c18..2bbb895efa 100644 --- a/src/platform_impl/ios/window.rs +++ b/src/platform_impl/ios/window.rs @@ -23,7 +23,7 @@ use crate::{ monitor, view, EventLoopWindowTarget, Fullscreen, MonitorHandle, }, window::{ - CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes, + CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes, WindowButtons, WindowId as RootWindowId, }, }; @@ -172,31 +172,15 @@ impl Inner { false } - pub fn set_minimizable(&self, _minimizable: bool) { - warn!("`Window::set_minimizable` is ignored on iOS") - } - - pub fn is_minimizable(&self) -> bool { - warn!("`Window::is_minimizable` is ignored on iOS"); - false - } - - pub fn set_maximizable(&self, _maximizable: bool) { - warn!("`Window::set_maximizable` is ignored on iOS") - } - - pub fn is_maximizable(&self) -> bool { - warn!("`Window::is_maximizable` is ignored on iOS"); - false - } - - pub fn set_closable(&self, _closable: bool) { - warn!("`Window::set_closable` is ignored on iOS") + #[inline] + pub fn set_window_buttons(&self, _buttons: WindowButtons) { + warn!("`Window::set_window_buttons` is ignored on iOS"); } - pub fn is_closable(&self) -> bool { - warn!("`Window::is_closable` is ignored on iOS"); - false + #[inline] + pub fn window_buttons(&self) -> WindowButtons { + warn!("`Window::window_buttons` is ignored on iOS"); + WindowButtons::all() } pub fn scale_factor(&self) -> f64 { diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 55bfb5240b..ec46f78df0 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -39,7 +39,9 @@ use crate::{ ControlFlow, DeviceEventFilter, EventLoopClosed, EventLoopWindowTarget as RootELW, }, icon::Icon, - window::{CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes}, + window::{ + CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes, WindowButtons, + }, }; pub(crate) use crate::icon::RgbaIcon as PlatformIcon; @@ -404,32 +406,15 @@ impl Window { pub fn is_resizable(&self) -> bool { x11_or_wayland!(match self; Window(w) => w.is_resizable()) } - #[inline] - pub fn set_minimizable(&self, minimizable: bool) { - x11_or_wayland!(match self; Window(w) => w.set_minimizable(minimizable)) - } - - #[inline] - pub fn is_minimizable(&self) -> bool { - x11_or_wayland!(match self; Window(w) => w.is_minimizable()) - } - #[inline] - pub fn set_maximizable(&self, maximizable: bool) { - x11_or_wayland!(match self; Window(w) => w.set_maximizable(maximizable)) - } #[inline] - pub fn is_maximizable(&self) -> bool { - x11_or_wayland!(match self; Window(w) => w.is_maximizable()) - } - #[inline] - pub fn set_closable(&self, closable: bool) { - x11_or_wayland!(match self; Window(w) => w.set_closable(closable)) + pub fn set_window_buttons(&self, buttons: WindowButtons) { + x11_or_wayland!(match self; Window(w) => w.set_window_buttons(buttons)) } #[inline] - pub fn is_closable(&self) -> bool { - x11_or_wayland!(match self; Window(w) => w.is_closable()) + pub fn window_buttons(&self) -> WindowButtons { + x11_or_wayland!(match self; Window(w) => w.window_buttons()) } #[inline] diff --git a/src/platform_impl/linux/wayland/window/mod.rs b/src/platform_impl/linux/wayland/window/mod.rs index 6fc9d86454..5f67a34a1b 100644 --- a/src/platform_impl/linux/wayland/window/mod.rs +++ b/src/platform_impl/linux/wayland/window/mod.rs @@ -18,7 +18,9 @@ use crate::platform_impl::{ Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError, PlatformSpecificWindowBuilderAttributes as PlatformAttributes, }; -use crate::window::{CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes}; +use crate::window::{ + CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes, WindowButtons, +}; use super::env::WindowingFeatures; use super::event_loop::WinitState; @@ -422,27 +424,11 @@ impl Window { } #[inline] - pub fn set_maximizable(&self, _maximizable: bool) {} - - #[inline] - pub fn is_maximizable(&self) -> bool { - true - } - - #[inline] - pub fn set_minimizable(&self, _minimizable: bool) {} - - #[inline] - pub fn is_minimizable(&self) -> bool { - true - } - - #[inline] - pub fn set_closable(&self, _closable: bool) {} + pub fn set_window_buttons(&self, _buttons: WindowButtons) {} #[inline] - pub fn is_closable(&self) -> bool { - true + pub fn window_buttons(&self) -> WindowButtons { + WindowButtons::all() } #[inline] diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index 5e987887a3..4b7829f92d 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -20,7 +20,9 @@ use crate::{ Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError, PlatformSpecificWindowBuilderAttributes, VideoMode as PlatformVideoMode, }, - window::{CursorGrabMode, CursorIcon, Icon, Theme, UserAttentionType, WindowAttributes}, + window::{ + CursorGrabMode, CursorIcon, Icon, Theme, UserAttentionType, WindowAttributes, WindowButtons, + }, }; use super::{ @@ -1259,27 +1261,11 @@ impl UnownedWindow { } #[inline] - pub fn set_maximizable(&self, _maximizable: bool) {} - - #[inline] - pub fn is_maximizable(&self) -> bool { - true - } - - #[inline] - pub fn set_minimizable(&self, _minimizable: bool) {} - - #[inline] - pub fn is_minimizable(&self) -> bool { - true - } - - #[inline] - pub fn set_closable(&self, _closable: bool) {} + pub fn set_window_buttons(&self, _buttons: WindowButtons) {} #[inline] - pub fn is_closable(&self) -> bool { - true + pub fn window_buttons(&self) -> WindowButtons { + WindowButtons::all() } #[inline] diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index f2baee109c..e02f76e66a 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -29,7 +29,7 @@ use crate::{ Fullscreen, OsError, }, window::{ - CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes, + CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes, WindowButtons, WindowId as RootWindowId, }, }; @@ -267,11 +267,11 @@ impl WinitWindow { masks &= !NSWindowStyleMask::NSResizableWindowMask; } - if !attrs.minimizable { + if !attrs.window_buttons.contains(WindowButtons::MINIMIZE) { masks &= !NSWindowStyleMask::NSMiniaturizableWindowMask; } - if !attrs.closable { + if !attrs.window_buttons.contains(WindowButtons::CLOSE) { masks &= !NSWindowStyleMask::NSClosableWindowMask; } @@ -339,7 +339,7 @@ impl WinitWindow { this.setLevel(NSWindowLevel::Floating); } - if !attrs.maximizable { + if !attrs.window_buttons.contains(WindowButtons::MAXIMIZE) { if let Some(button) = this.standardWindowButton(NSWindowButton::Zoom) { button.setEnabled(false); } @@ -635,49 +635,45 @@ impl WinitWindow { } #[inline] - pub fn set_minimizable(&self, minimizable: bool) { + pub fn set_window_buttons(&self, buttons: WindowButtons) { let mut mask = self.styleMask(); - if minimizable { + + if buttons.contains(WindowButtons::CLOSE) { + mask |= NSWindowStyleMask::NSClosableWindowMask; + } else { + mask &= !NSWindowStyleMask::NSClosableWindowMask; + } + + if buttons.contains(WindowButtons::MINIMIZE) { mask |= NSWindowStyleMask::NSMiniaturizableWindowMask; } else { mask &= !NSWindowStyleMask::NSMiniaturizableWindowMask; } - self.set_style_mask_async(mask); - } - #[inline] - pub fn is_minimizable(&self) -> bool { - self.isMiniaturizable() - } + self.set_style_mask_async(mask); - #[inline] - pub fn set_maximizable(&self, maximizable: bool) { if let Some(button) = self.standardWindowButton(NSWindowButton::Zoom) { - button.setEnabled(maximizable); + button.setEnabled(buttons.contains(WindowButtons::MAXIMIZE)); } } #[inline] - pub fn is_maximizable(&self) -> bool { - self.standardWindowButton(NSWindowButton::Zoom) + pub fn window_buttons(&self) -> WindowButtons { + let mut buttons = WindowButtons::empty(); + if self.isMiniaturizable() { + buttons |= WindowButtons::MINIMIZE; + } + if self + .standardWindowButton(NSWindowButton::Zoom) .map(|b| b.isEnabled()) .unwrap_or(true) - } - - #[inline] - pub fn set_closable(&self, closable: bool) { - let mut mask = self.styleMask(); - if closable { - mask |= NSWindowStyleMask::NSClosableWindowMask; - } else { - mask &= !NSWindowStyleMask::NSClosableWindowMask; + { + buttons |= WindowButtons::MAXIMIZE; } - self.set_style_mask_async(mask); - } - - #[inline] - pub fn is_closable(&self) -> bool { - self.hasCloseBox() + if self.hasCloseBox() { + buttons |= WindowButtons::CLOSE; + } + buttons } pub fn set_cursor_icon(&self, icon: CursorIcon) { diff --git a/src/platform_impl/web/window.rs b/src/platform_impl/web/window.rs index b37a788182..14e4f404ac 100644 --- a/src/platform_impl/web/window.rs +++ b/src/platform_impl/web/window.rs @@ -3,7 +3,8 @@ use crate::error::{ExternalError, NotSupportedError, OsError as RootOE}; use crate::event; use crate::icon::Icon; use crate::window::{ - CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes, WindowId as RootWI, + CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes, WindowButtons, + WindowId as RootWI, }; use raw_window_handle::{RawDisplayHandle, RawWindowHandle, WebDisplayHandle, WebWindowHandle}; @@ -172,30 +173,11 @@ impl Window { } #[inline] - pub fn set_maximizable(&self, _maximizable: bool) { - // Intentionally a no-op: users can't maximize canvas elements - } - - pub fn is_maximizable(&self) -> bool { - true - } + pub fn set_window_buttons(&self, _buttons: WindowButtons) {} #[inline] - pub fn set_minimizable(&self, _minimizable: bool) { - // Intentionally a no-op: users can't minmiize canvas elements - } - - pub fn is_minimizable(&self) -> bool { - true - } - - #[inline] - pub fn set_closable(&self, _closable: bool) { - // Intentionally a no-op: users can't close canvas elements - } - - pub fn is_closable(&self) -> bool { - true + pub fn window_buttons(&self) -> WindowButtons { + WindowButtons::all() } #[inline] diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index 86f9a63a84..23b5ddd2df 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -70,7 +70,9 @@ use crate::{ window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState}, Fullscreen, Parent, PlatformSpecificWindowBuilderAttributes, WindowId, }, - window::{CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes}, + window::{ + CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes, WindowButtons, + }, }; /// The Win32 implementation of the main `Window` object. @@ -263,58 +265,43 @@ impl Window { } #[inline] - pub fn set_minimizable(&self, minimizable: bool) { - let window = self.window.clone(); - let window_state = Arc::clone(&self.window_state); - - self.thread_executor.execute_in_thread(move || { - WindowState::set_window_flags(window_state.lock().unwrap(), window.0, |f| { - f.set(WindowFlags::MINIMIZABLE, minimizable) - }); - }); - } - - #[inline] - pub fn is_minimizable(&self) -> bool { - let window_state = self.window_state_lock(); - window_state.window_flags.contains(WindowFlags::MINIMIZABLE) - } - - #[inline] - pub fn set_maximizable(&self, maximizable: bool) { + pub fn set_window_buttons(&self, buttons: WindowButtons) { let window = self.window.clone(); let window_state = Arc::clone(&self.window_state); self.thread_executor.execute_in_thread(move || { + let _ = &window; WindowState::set_window_flags(window_state.lock().unwrap(), window.0, |f| { - f.set(WindowFlags::MAXIMIZABLE, maximizable) - }); - }); - } - - #[inline] - pub fn is_maximizable(&self) -> bool { - let window_state = self.window_state_lock(); - window_state.window_flags.contains(WindowFlags::MAXIMIZABLE) - } - - #[inline] - pub fn set_closable(&self, closable: bool) { - let window = self.window.clone(); - let window_state = Arc::clone(&self.window_state); - self.thread_executor.execute_in_thread(move || { - WindowState::set_window_flags(window_state.lock().unwrap(), window.0, |f| { - f.set(WindowFlags::CLOSABLE, closable) + f.set( + WindowFlags::MINIMIZABLE, + buttons.contains(WindowButtons::MINIMIZE), + ); + f.set( + WindowFlags::MAXIMIZABLE, + buttons.contains(WindowButtons::MAXIMIZE), + ); + f.set( + WindowFlags::CLOSABLE, + buttons.contains(WindowButtons::CLOSE), + ) }); }); } - #[inline] - pub fn is_closable(&self) -> bool { + pub fn window_buttons(&self) -> WindowButtons { + let mut buttons = WindowButtons::empty(); let window_state = self.window_state_lock(); - window_state.window_flags.contains(WindowFlags::CLOSABLE) + if window_state.window_flags.contains(WindowFlags::MINIMIZABLE) { + buttons |= WindowButtons::MINIMIZE; + } + if window_state.window_flags.contains(WindowFlags::MAXIMIZABLE) { + buttons |= WindowButtons::MAXIMIZE; + } + if window_state.window_flags.contains(WindowFlags::CLOSABLE) { + buttons |= WindowButtons::CLOSE; + } + buttons } - /// Returns the `hwnd` of this window. #[inline] pub fn hwnd(&self) -> HWND { @@ -956,7 +943,7 @@ impl<'a, T: 'static> InitData<'a, T> { // attribute is correctly applied. win.set_visible(attributes.visible); - win.set_closable(attributes.closable); + win.set_window_buttons(attributes.window_buttons); if attributes.fullscreen.is_some() { win.set_fullscreen(attributes.fullscreen); @@ -1020,10 +1007,8 @@ where 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::MINIMIZABLE, attributes.minimizable); - window_flags.set(WindowFlags::MAXIMIZABLE, attributes.maximizable); - // will be changed later using `window.set_closable()` - // but we need to have a default for the diffing to work + // Will be changed later using `window.set_window_buttons` but we need to set a default here + // so the diffing later can work window_flags.set(WindowFlags::CLOSABLE, true); let parent = match pl_attribs.parent { diff --git a/src/window.rs b/src/window.rs index 81c866aa93..f889ca1488 100644 --- a/src/window.rs +++ b/src/window.rs @@ -126,9 +126,7 @@ pub(crate) struct WindowAttributes { pub max_inner_size: Option, pub position: Option, pub resizable: bool, - pub maximizable: bool, - pub minimizable: bool, - pub closable: bool, + pub window_buttons: WindowButtons, pub title: String, pub fullscreen: Option, pub maximized: bool, @@ -150,9 +148,7 @@ impl Default for WindowAttributes { max_inner_size: None, position: None, resizable: true, - minimizable: true, - maximizable: true, - closable: true, + window_buttons: WindowButtons::all(), title: "winit window".to_owned(), maximized: false, fullscreen: None, @@ -248,36 +244,14 @@ impl WindowBuilder { self } - /// Sets whether the window is maximizable or not. + /// Sets the enabled window buttons. /// - /// The default is `true`. - /// - /// See [`Window::set_maximizable`] for details. - #[inline] - pub fn with_maximizable(mut self, maximizable: bool) -> Self { - self.window.maximizable = maximizable; - self - } - - /// Sets whether the window is minimizable or not. - /// - /// The default is `true`. + /// The default is [`WindowButtons::all`] /// - /// See [`Window::set_minimizable`] for details. + /// See [`Window::set_window_buttons`] for details. #[inline] - pub fn with_minimizable(mut self, minimizable: bool) -> Self { - self.window.minimizable = minimizable; - self - } - - /// Sets whether the window is closable or not. - /// - /// The default is `true`. - /// - /// See [`Window::set_closable`] for details. - #[inline] - pub fn with_closable(mut self, closable: bool) -> Self { - self.window.closable = closable; + pub fn with_window_buttons(mut self, buttons: WindowButtons) -> Self { + self.window.window_buttons = buttons; self } @@ -771,70 +745,24 @@ impl Window { self.window.is_resizable() } - /// Sets whether the window is minimizable or not. - /// - /// ## Platform-specific - /// - /// - **Wayland / X11:** Not implemented. - /// - **Web / iOS / Android:** Unsupported. - #[inline] - pub fn set_minimizable(&self, minimizable: bool) { - self.window.set_minimizable(minimizable) - } - - /// Gets the window's current minimizable state. - /// - /// ## Platform-specific - /// - /// - **Wayland / X11:** Not implemented. - /// - **Web / iOS / Android:** Unsupported. - #[inline] - pub fn is_minimizable(&self) -> bool { - self.window.is_minimizable() - } - - /// Sets whether the window is maximizable or not. + /// Sets the enabled window buttons. /// /// ## Platform-specific /// /// - **Wayland / X11:** Not implemented. /// - **Web / iOS / Android:** Unsupported. - #[inline] - pub fn set_maximizable(&self, maximizable: bool) { - self.window.set_maximizable(maximizable) + pub fn set_window_buttons(&self, buttons: WindowButtons) { + self.window.set_window_buttons(buttons) } - /// Gets the window's current maximizable state. + /// Gets the enabled window buttons. /// /// ## Platform-specific /// - /// - **Wayland / X11:** Not implemented. - /// - **Web / iOS / Android:** Unsupported. - #[inline] - pub fn is_maximizable(&self) -> bool { - self.window.is_maximizable() - } - - /// Sets whether the window is closable or not. - /// - /// ## Platform-specific - /// - /// - **Wayland / X11:** Not implemented. - /// - **Web / iOS / Android:** Unsupported. - #[inline] - pub fn set_closable(&self, closable: bool) { - self.window.set_closable(closable) - } - - /// Gets the window's current closable state. - /// - /// ## Platform-specific - /// - /// - **Wayland / X11:** Not implemented. - /// - **Web / iOS / Android:** Unsupported. - #[inline] - pub fn is_closable(&self) -> bool { - self.window.is_closable() + /// - **Wayland / X11:** Not implemented. Always returns [`WindowButtons::all`]. + /// - **Web / iOS / Android:** Unsupported. Always returns [`WindowButtons::all`]. + pub fn window_buttons(&self) -> WindowButtons { + self.window.window_buttons() } /// Sets the window to minimized or back @@ -1484,3 +1412,11 @@ impl Default for UserAttentionType { UserAttentionType::Informational } } + +bitflags! { + pub struct WindowButtons: u32 { + const CLOSE = 1 << 0; + const MINIMIZE = 1 << 1; + const MAXIMIZE = 1 << 2; + } +} From bb9bbcedfa337bcf3b0439f9a1beb82291cdd260 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Fri, 4 Nov 2022 14:53:13 +0200 Subject: [PATCH 5/9] rename to `[set_]enabled_buttons` --- CHANGELOG.md | 2 +- src/platform_impl/android/mod.rs | 4 ++-- src/platform_impl/ios/window.rs | 8 ++++---- src/platform_impl/linux/mod.rs | 8 ++++---- src/platform_impl/linux/wayland/window/mod.rs | 4 ++-- src/platform_impl/linux/x11/window.rs | 4 ++-- src/platform_impl/macos/window.rs | 10 +++++----- src/platform_impl/web/window.rs | 4 ++-- src/platform_impl/windows/window.rs | 8 ++++---- src/window.rs | 18 +++++++++--------- 10 files changed, 35 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f3853e0d7..c823bd7f9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ And please only add new entries to the top of this list, right below the `# Unre # Unreleased -- On Windows and MacOS, add API to enable/disable window controls (close, minimize, ...etc). +- On Windows and MacOS, add API to enable/disable window buttons (close, minimize, ...etc). - On Windows, fix focusing menubar when pressing `Alt`. - On MacOS, made `accepts_first_mouse` configurable. - Migrated `WindowBuilderExtUnix::with_resize_increments` to `WindowBuilder`. diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index de43f5b2dd..dc6bb1b941 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -771,9 +771,9 @@ impl Window { false } - pub fn set_window_buttons(&self, _buttons: WindowButtons) {} + pub fn set_enabled_buttons(&self, _buttons: WindowButtons) {} - pub fn window_buttons(&self) -> WindowButtons { + pub fn enabled_buttons(&self) -> WindowButtons { WindowButtons::all() } diff --git a/src/platform_impl/ios/window.rs b/src/platform_impl/ios/window.rs index 2bbb895efa..793c9606a3 100644 --- a/src/platform_impl/ios/window.rs +++ b/src/platform_impl/ios/window.rs @@ -173,13 +173,13 @@ impl Inner { } #[inline] - pub fn set_window_buttons(&self, _buttons: WindowButtons) { - warn!("`Window::set_window_buttons` is ignored on iOS"); + pub fn set_enabled_buttons(&self, _buttons: WindowButtons) { + warn!("`Window::set_enabled_buttons` is ignored on iOS"); } #[inline] - pub fn window_buttons(&self) -> WindowButtons { - warn!("`Window::window_buttons` is ignored on iOS"); + pub fn enabled_buttons(&self) -> WindowButtons { + warn!("`Window::enabled_buttons` is ignored on iOS"); WindowButtons::all() } diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index ec46f78df0..16499512b6 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -408,13 +408,13 @@ impl Window { } #[inline] - pub fn set_window_buttons(&self, buttons: WindowButtons) { - x11_or_wayland!(match self; Window(w) => w.set_window_buttons(buttons)) + pub fn set_enabled_buttons(&self, buttons: WindowButtons) { + x11_or_wayland!(match self; Window(w) => w.set_enabled_buttons(buttons)) } #[inline] - pub fn window_buttons(&self) -> WindowButtons { - x11_or_wayland!(match self; Window(w) => w.window_buttons()) + pub fn enabled_buttons(&self) -> WindowButtons { + x11_or_wayland!(match self; Window(w) => w.enabled_buttons()) } #[inline] diff --git a/src/platform_impl/linux/wayland/window/mod.rs b/src/platform_impl/linux/wayland/window/mod.rs index 5f67a34a1b..dc0b3ef7db 100644 --- a/src/platform_impl/linux/wayland/window/mod.rs +++ b/src/platform_impl/linux/wayland/window/mod.rs @@ -424,10 +424,10 @@ impl Window { } #[inline] - pub fn set_window_buttons(&self, _buttons: WindowButtons) {} + pub fn set_enabled_buttons(&self, _buttons: WindowButtons) {} #[inline] - pub fn window_buttons(&self) -> WindowButtons { + pub fn enabled_buttons(&self) -> WindowButtons { WindowButtons::all() } diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index 4b7829f92d..8e7ea02055 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -1261,10 +1261,10 @@ impl UnownedWindow { } #[inline] - pub fn set_window_buttons(&self, _buttons: WindowButtons) {} + pub fn set_enabled_buttons(&self, _buttons: WindowButtons) {} #[inline] - pub fn window_buttons(&self) -> WindowButtons { + pub fn enabled_buttons(&self) -> WindowButtons { WindowButtons::all() } diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index e02f76e66a..e6eb0c7e3c 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -267,11 +267,11 @@ impl WinitWindow { masks &= !NSWindowStyleMask::NSResizableWindowMask; } - if !attrs.window_buttons.contains(WindowButtons::MINIMIZE) { + if !attrs.enabled_buttons.contains(WindowButtons::MINIMIZE) { masks &= !NSWindowStyleMask::NSMiniaturizableWindowMask; } - if !attrs.window_buttons.contains(WindowButtons::CLOSE) { + if !attrs.enabled_buttons.contains(WindowButtons::CLOSE) { masks &= !NSWindowStyleMask::NSClosableWindowMask; } @@ -339,7 +339,7 @@ impl WinitWindow { this.setLevel(NSWindowLevel::Floating); } - if !attrs.window_buttons.contains(WindowButtons::MAXIMIZE) { + if !attrs.enabled_buttons.contains(WindowButtons::MAXIMIZE) { if let Some(button) = this.standardWindowButton(NSWindowButton::Zoom) { button.setEnabled(false); } @@ -635,7 +635,7 @@ impl WinitWindow { } #[inline] - pub fn set_window_buttons(&self, buttons: WindowButtons) { + pub fn set_enabled_buttons(&self, buttons: WindowButtons) { let mut mask = self.styleMask(); if buttons.contains(WindowButtons::CLOSE) { @@ -658,7 +658,7 @@ impl WinitWindow { } #[inline] - pub fn window_buttons(&self) -> WindowButtons { + pub fn enabled_buttons(&self) -> WindowButtons { let mut buttons = WindowButtons::empty(); if self.isMiniaturizable() { buttons |= WindowButtons::MINIMIZE; diff --git a/src/platform_impl/web/window.rs b/src/platform_impl/web/window.rs index 14e4f404ac..d6d331c1b9 100644 --- a/src/platform_impl/web/window.rs +++ b/src/platform_impl/web/window.rs @@ -173,10 +173,10 @@ impl Window { } #[inline] - pub fn set_window_buttons(&self, _buttons: WindowButtons) {} + pub fn set_enabled_buttons(&self, _buttons: WindowButtons) {} #[inline] - pub fn window_buttons(&self) -> WindowButtons { + pub fn enabled_buttons(&self) -> WindowButtons { WindowButtons::all() } diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index 23b5ddd2df..f90b17a4f0 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -265,7 +265,7 @@ impl Window { } #[inline] - pub fn set_window_buttons(&self, buttons: WindowButtons) { + pub fn set_enabled_buttons(&self, buttons: WindowButtons) { let window = self.window.clone(); let window_state = Arc::clone(&self.window_state); @@ -288,7 +288,7 @@ impl Window { }); } - pub fn window_buttons(&self) -> WindowButtons { + pub fn enabled_buttons(&self) -> WindowButtons { let mut buttons = WindowButtons::empty(); let window_state = self.window_state_lock(); if window_state.window_flags.contains(WindowFlags::MINIMIZABLE) { @@ -943,7 +943,7 @@ impl<'a, T: 'static> InitData<'a, T> { // attribute is correctly applied. win.set_visible(attributes.visible); - win.set_window_buttons(attributes.window_buttons); + win.set_enabled_buttons(attributes.enabled_buttons); if attributes.fullscreen.is_some() { win.set_fullscreen(attributes.fullscreen); @@ -1007,7 +1007,7 @@ where 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); - // Will be changed later using `window.set_window_buttons` but we need to set a default here + // Will be changed later using `window.set_enabled_buttons` but we need to set a default here // so the diffing later can work window_flags.set(WindowFlags::CLOSABLE, true); diff --git a/src/window.rs b/src/window.rs index f889ca1488..33748829b2 100644 --- a/src/window.rs +++ b/src/window.rs @@ -126,7 +126,7 @@ pub(crate) struct WindowAttributes { pub max_inner_size: Option, pub position: Option, pub resizable: bool, - pub window_buttons: WindowButtons, + pub enabled_buttons: WindowButtons, pub title: String, pub fullscreen: Option, pub maximized: bool, @@ -148,7 +148,7 @@ impl Default for WindowAttributes { max_inner_size: None, position: None, resizable: true, - window_buttons: WindowButtons::all(), + enabled_buttons: WindowButtons::all(), title: "winit window".to_owned(), maximized: false, fullscreen: None, @@ -248,10 +248,10 @@ impl WindowBuilder { /// /// The default is [`WindowButtons::all`] /// - /// See [`Window::set_window_buttons`] for details. + /// See [`Window::set_enabled_buttons`] for details. #[inline] - pub fn with_window_buttons(mut self, buttons: WindowButtons) -> Self { - self.window.window_buttons = buttons; + pub fn with_enabled_buttons(mut self, buttons: WindowButtons) -> Self { + self.window.enabled_buttons = buttons; self } @@ -751,8 +751,8 @@ impl Window { /// /// - **Wayland / X11:** Not implemented. /// - **Web / iOS / Android:** Unsupported. - pub fn set_window_buttons(&self, buttons: WindowButtons) { - self.window.set_window_buttons(buttons) + pub fn set_enabled_buttons(&self, buttons: WindowButtons) { + self.window.set_enabled_buttons(buttons) } /// Gets the enabled window buttons. @@ -761,8 +761,8 @@ impl Window { /// /// - **Wayland / X11:** Not implemented. Always returns [`WindowButtons::all`]. /// - **Web / iOS / Android:** Unsupported. Always returns [`WindowButtons::all`]. - pub fn window_buttons(&self) -> WindowButtons { - self.window.window_buttons() + pub fn enabled_buttons(&self) -> WindowButtons { + self.window.enabled_buttons() } /// Sets the window to minimized or back From 61acd7eeac40fda3a846d06f8267f447291cb48e Mon Sep 17 00:00:00 2001 From: amrbashir Date: Wed, 23 Nov 2022 13:35:12 +0200 Subject: [PATCH 6/9] add example, fix windows impl for minimize --- examples/window_buttons.rs | 68 +++++++++++++++++++++++ src/platform_impl/windows/window_state.rs | 7 +-- 2 files changed, 69 insertions(+), 6 deletions(-) create mode 100644 examples/window_buttons.rs diff --git a/examples/window_buttons.rs b/examples/window_buttons.rs new file mode 100644 index 0000000000..5d41144dbd --- /dev/null +++ b/examples/window_buttons.rs @@ -0,0 +1,68 @@ +#![allow(clippy::single_match)] + +// This example is used by developers to test various window functions. + +use simple_logger::SimpleLogger; +use winit::{ + dpi::LogicalSize, + event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}, + event_loop::{DeviceEventFilter, EventLoop}, + window::{WindowBuilder, WindowButtons}, +}; + +fn main() { + SimpleLogger::new().init().unwrap(); + let event_loop = EventLoop::new(); + + let window = WindowBuilder::new() + .with_title("A fantastic window!") + .with_inner_size(LogicalSize::new(300.0, 300.0)) + .build(&event_loop) + .unwrap(); + + eprintln!("Window Button keys:"); + eprintln!(" (F) Toggle close button"); + eprintln!(" (G) Toggle maximize button"); + eprintln!(" (H) Toggle minimize button"); + + event_loop.set_device_event_filter(DeviceEventFilter::Never); + + event_loop.run(move |event, _, control_flow| { + control_flow.set_wait(); + + match event { + Event::WindowEvent { + event: + WindowEvent::KeyboardInput { + input: + KeyboardInput { + virtual_keycode: Some(key), + state: ElementState::Pressed, + .. + }, + .. + }, + .. + } => match key { + VirtualKeyCode::F => { + let buttons = window.enabled_buttons(); + window.set_enabled_buttons(buttons ^ WindowButtons::CLOSE); + } + VirtualKeyCode::G => { + let buttons = window.enabled_buttons(); + window.set_enabled_buttons(buttons ^ WindowButtons::MAXIMIZE); + } + VirtualKeyCode::H => { + let buttons = window.enabled_buttons(); + window.set_enabled_buttons(buttons ^ WindowButtons::MINIMIZE); + } + _ => (), + }, + Event::WindowEvent { + event: WindowEvent::CloseRequested, + window_id, + } if window_id == window.id() => control_flow.set_exit(), + _ => (), + } + }); +} diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index c32a62b483..7a4a4b921b 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -241,12 +241,7 @@ impl WindowFlags { pub fn to_window_styles(self) -> (WINDOW_STYLE, WINDOW_EX_STYLE) { // Required styles to properly support common window functionality like aero snap. - let mut style = WS_CAPTION - | WS_MINIMIZEBOX - | WS_BORDER - | WS_CLIPSIBLINGS - | WS_CLIPCHILDREN - | WS_SYSMENU; + let mut style = WS_CAPTION | WS_BORDER | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_SYSMENU; let mut style_ex = WS_EX_WINDOWEDGE | WS_EX_ACCEPTFILES; if self.contains(WindowFlags::RESIZABLE) { From 99e8c241be38bb84500d7485f7b81c30c7cb8478 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 23 Nov 2022 13:38:28 +0100 Subject: [PATCH 7/9] macOS: Fix button enabling close/minimize while disabling maximized --- src/platform_impl/macos/window.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 307fdd3210..c853fc3107 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -651,8 +651,13 @@ impl WinitWindow { mask &= !NSWindowStyleMask::NSMiniaturizableWindowMask; } - self.set_style_mask_async(mask); + // This must happen before the button's "enabled" status has been set, + // hence we do it synchronously. + self.set_style_mask_sync(mask); + // We edit the button directly instead of using `NSResizableWindowMask`, + // since that mask also affect the resizability of the window (which is + // controllable by other means in `winit`). if let Some(button) = self.standardWindowButton(NSWindowButton::Zoom) { button.setEnabled(buttons.contains(WindowButtons::MAXIMIZE)); } From 47f34de4f2db5fc3a4883997417bf63dccaac535 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Sat, 26 Nov 2022 14:17:13 +0200 Subject: [PATCH 8/9] Update src/platform_impl/windows/window.rs Co-authored-by: Kirill Chibisov --- src/platform_impl/windows/window.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index ba02b72d4c..113f93c4a4 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -1017,7 +1017,7 @@ where // WindowFlags::VISIBLE and MAXIMIZED are set down below after the window has been configured. window_flags.set(WindowFlags::RESIZABLE, attributes.resizable); // Will be changed later using `window.set_enabled_buttons` but we need to set a default here - // so the diffing later can work + // so the diffing later can work. window_flags.set(WindowFlags::CLOSABLE, true); let parent = match pl_attribs.parent { From 4ff07333d7a68fdf655996e5962dcc7c8c1c579f Mon Sep 17 00:00:00 2001 From: amrbashir Date: Sat, 26 Nov 2022 14:26:15 +0200 Subject: [PATCH 9/9] compose the flags on a sep line, use `bool::then` --- src/platform_impl/windows/window_state.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index f044e31c82..165995f659 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -350,17 +350,14 @@ impl WindowFlags { } if diff.contains(WindowFlags::CLOSABLE) || new.contains(WindowFlags::CLOSABLE) { + let flags = MF_BYCOMMAND + | new + .contains(WindowFlags::CLOSABLE) + .then(|| MF_ENABLED) + .unwrap_or(MF_DISABLED); + unsafe { - EnableMenuItem( - GetSystemMenu(window, 0), - SC_CLOSE, - MF_BYCOMMAND - | if new.contains(WindowFlags::CLOSABLE) { - MF_ENABLED - } else { - MF_DISABLED - }, - ); + EnableMenuItem(GetSystemMenu(window, 0), SC_CLOSE, flags); } }