mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2024-12-23 22:01:31 +11:00
On Windows and macOS, add API to enable/disable window controls (#2537)
* On Windows and macOS, add API to enable/disable window controls * fix build * missing import * use `WindowButtons` flags * rename to `[set_]enabled_buttons` * add example, fix windows impl for minimize * macOS: Fix button enabling close/minimize while disabling maximized * Update src/platform_impl/windows/window.rs Co-authored-by: Kirill Chibisov <contact@kchibisov.com> * compose the flags on a sep line, use `bool::then` Co-authored-by: Mads Marquart <mads@marquart.dk> Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
This commit is contained in:
parent
28e34c2e1b
commit
94688a62f0
|
@ -8,6 +8,7 @@ And please only add new entries to the top of this list, right below the `# Unre
|
||||||
|
|
||||||
# Unreleased
|
# Unreleased
|
||||||
|
|
||||||
|
- On Windows and MacOS, add API to enable/disable window buttons (close, minimize, ...etc).
|
||||||
- On Windows, macOS, X11 and Wayland, add `Window::set_theme`.
|
- On Windows, macOS, X11 and Wayland, add `Window::set_theme`.
|
||||||
- **Breaking:** Remove `WindowExtWayland::wayland_set_csd_theme` and `WindowBuilderExtX11::with_gtk_theme_variant`.
|
- **Breaking:** Remove `WindowExtWayland::wayland_set_csd_theme` and `WindowBuilderExtX11::with_gtk_theme_variant`.
|
||||||
- On Windows, revert window background to an empty brush to avoid white flashes when changing scaling.
|
- On Windows, revert window background to an empty brush to avoid white flashes when changing scaling.
|
||||||
|
|
68
examples/window_buttons.rs
Normal file
68
examples/window_buttons.rs
Normal file
|
@ -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(),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
|
@ -24,7 +24,7 @@ use crate::{
|
||||||
error,
|
error,
|
||||||
event::{self, StartCause, VirtualKeyCode},
|
event::{self, StartCause, VirtualKeyCode},
|
||||||
event_loop::{self, ControlFlow, EventLoopWindowTarget as RootELW},
|
event_loop::{self, ControlFlow, EventLoopWindowTarget as RootELW},
|
||||||
window::{self, CursorGrabMode, Theme, WindowLevel},
|
window::{self, CursorGrabMode, Theme, WindowButtons, WindowLevel},
|
||||||
};
|
};
|
||||||
|
|
||||||
fn ndk_keycode_to_virtualkeycode(keycode: Keycode) -> Option<event::VirtualKeyCode> {
|
fn ndk_keycode_to_virtualkeycode(keycode: Keycode) -> Option<event::VirtualKeyCode> {
|
||||||
|
@ -959,6 +959,12 @@ impl Window {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_enabled_buttons(&self, _buttons: WindowButtons) {}
|
||||||
|
|
||||||
|
pub fn enabled_buttons(&self) -> WindowButtons {
|
||||||
|
WindowButtons::all()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_minimized(&self, _minimized: bool) {}
|
pub fn set_minimized(&self, _minimized: bool) {}
|
||||||
|
|
||||||
pub fn set_maximized(&self, _maximized: bool) {}
|
pub fn set_maximized(&self, _maximized: bool) {}
|
||||||
|
|
|
@ -23,7 +23,7 @@ use crate::{
|
||||||
monitor, view, EventLoopWindowTarget, Fullscreen, MonitorHandle,
|
monitor, view, EventLoopWindowTarget, Fullscreen, MonitorHandle,
|
||||||
},
|
},
|
||||||
window::{
|
window::{
|
||||||
CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes,
|
CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes, WindowButtons,
|
||||||
WindowId as RootWindowId, WindowLevel,
|
WindowId as RootWindowId, WindowLevel,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -172,6 +172,17 @@ impl Inner {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_enabled_buttons(&self, _buttons: WindowButtons) {
|
||||||
|
warn!("`Window::set_enabled_buttons` is ignored on iOS");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn enabled_buttons(&self) -> WindowButtons {
|
||||||
|
warn!("`Window::enabled_buttons` is ignored on iOS");
|
||||||
|
WindowButtons::all()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn scale_factor(&self) -> f64 {
|
pub fn scale_factor(&self) -> f64 {
|
||||||
unsafe {
|
unsafe {
|
||||||
let hidpi: CGFloat = msg_send![self.view, contentScaleFactor];
|
let hidpi: CGFloat = msg_send![self.view, contentScaleFactor];
|
||||||
|
|
|
@ -39,7 +39,10 @@ use crate::{
|
||||||
ControlFlow, DeviceEventFilter, EventLoopClosed, EventLoopWindowTarget as RootELW,
|
ControlFlow, DeviceEventFilter, EventLoopClosed, EventLoopWindowTarget as RootELW,
|
||||||
},
|
},
|
||||||
icon::Icon,
|
icon::Icon,
|
||||||
window::{CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes, WindowLevel},
|
window::{
|
||||||
|
CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes, WindowButtons,
|
||||||
|
WindowLevel,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) use crate::icon::RgbaIcon as PlatformIcon;
|
pub(crate) use crate::icon::RgbaIcon as PlatformIcon;
|
||||||
|
@ -401,6 +404,16 @@ impl Window {
|
||||||
x11_or_wayland!(match self; Window(w) => w.is_resizable())
|
x11_or_wayland!(match self; Window(w) => w.is_resizable())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_enabled_buttons(&self, buttons: WindowButtons) {
|
||||||
|
x11_or_wayland!(match self; Window(w) => w.set_enabled_buttons(buttons))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn enabled_buttons(&self) -> WindowButtons {
|
||||||
|
x11_or_wayland!(match self; Window(w) => w.enabled_buttons())
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_cursor_icon(&self, cursor: CursorIcon) {
|
pub fn set_cursor_icon(&self, cursor: CursorIcon) {
|
||||||
x11_or_wayland!(match self; Window(w) => w.set_cursor_icon(cursor))
|
x11_or_wayland!(match self; Window(w) => w.set_cursor_icon(cursor))
|
||||||
|
|
|
@ -18,7 +18,9 @@ use crate::platform_impl::{
|
||||||
Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError,
|
Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError,
|
||||||
PlatformSpecificWindowBuilderAttributes as PlatformAttributes,
|
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::env::WindowingFeatures;
|
||||||
use super::event_loop::WinitState;
|
use super::event_loop::WinitState;
|
||||||
|
@ -421,6 +423,14 @@ impl Window {
|
||||||
self.resizeable.load(Ordering::Relaxed)
|
self.resizeable.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_enabled_buttons(&self, _buttons: WindowButtons) {}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn enabled_buttons(&self) -> WindowButtons {
|
||||||
|
WindowButtons::all()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn scale_factor(&self) -> u32 {
|
pub fn scale_factor(&self) -> u32 {
|
||||||
// The scale factor from `get_surface_scale_factor` is always greater than zero, so
|
// The scale factor from `get_surface_scale_factor` is always greater than zero, so
|
||||||
|
|
|
@ -21,7 +21,8 @@ use crate::{
|
||||||
PlatformSpecificWindowBuilderAttributes, VideoMode as PlatformVideoMode,
|
PlatformSpecificWindowBuilderAttributes, VideoMode as PlatformVideoMode,
|
||||||
},
|
},
|
||||||
window::{
|
window::{
|
||||||
CursorGrabMode, CursorIcon, Icon, Theme, UserAttentionType, WindowAttributes, WindowLevel,
|
CursorGrabMode, CursorIcon, Icon, Theme, UserAttentionType, WindowAttributes,
|
||||||
|
WindowButtons, WindowLevel,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1280,6 +1281,14 @@ impl UnownedWindow {
|
||||||
self.shared_state_lock().is_resizable
|
self.shared_state_lock().is_resizable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_enabled_buttons(&self, _buttons: WindowButtons) {}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn enabled_buttons(&self) -> WindowButtons {
|
||||||
|
WindowButtons::all()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn xlib_display(&self) -> *mut c_void {
|
pub fn xlib_display(&self) -> *mut c_void {
|
||||||
self.xconn.display as _
|
self.xconn.display as _
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use objc2::foundation::NSObject;
|
use objc2::foundation::NSObject;
|
||||||
use objc2::{extern_class, ClassType};
|
use objc2::{extern_class, extern_methods, ClassType};
|
||||||
|
|
||||||
use super::{NSResponder, NSView};
|
use super::{NSResponder, NSView};
|
||||||
|
|
||||||
|
@ -12,3 +12,13 @@ extern_class!(
|
||||||
type Super = NSView;
|
type Super = NSView;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
extern_methods!(
|
||||||
|
unsafe impl NSControl {
|
||||||
|
#[sel(setEnabled:)]
|
||||||
|
pub fn setEnabled(&self, enabled: bool);
|
||||||
|
|
||||||
|
#[sel(isEnabled)]
|
||||||
|
pub fn isEnabled(&self) -> bool;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
|
@ -180,6 +180,12 @@ extern_methods!(
|
||||||
#[sel(isResizable)]
|
#[sel(isResizable)]
|
||||||
pub fn isResizable(&self) -> bool;
|
pub fn isResizable(&self) -> bool;
|
||||||
|
|
||||||
|
#[sel(isMiniaturizable)]
|
||||||
|
pub fn isMiniaturizable(&self) -> bool;
|
||||||
|
|
||||||
|
#[sel(hasCloseBox)]
|
||||||
|
pub fn hasCloseBox(&self) -> bool;
|
||||||
|
|
||||||
#[sel(isMiniaturized)]
|
#[sel(isMiniaturized)]
|
||||||
pub fn isMiniaturized(&self) -> bool;
|
pub fn isMiniaturized(&self) -> bool;
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ use crate::{
|
||||||
Fullscreen, OsError,
|
Fullscreen, OsError,
|
||||||
},
|
},
|
||||||
window::{
|
window::{
|
||||||
CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes,
|
CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes, WindowButtons,
|
||||||
WindowId as RootWindowId, WindowLevel,
|
WindowId as RootWindowId, WindowLevel,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -269,6 +269,14 @@ impl WinitWindow {
|
||||||
masks &= !NSWindowStyleMask::NSResizableWindowMask;
|
masks &= !NSWindowStyleMask::NSResizableWindowMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !attrs.enabled_buttons.contains(WindowButtons::MINIMIZE) {
|
||||||
|
masks &= !NSWindowStyleMask::NSMiniaturizableWindowMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !attrs.enabled_buttons.contains(WindowButtons::CLOSE) {
|
||||||
|
masks &= !NSWindowStyleMask::NSClosableWindowMask;
|
||||||
|
}
|
||||||
|
|
||||||
if pl_attrs.fullsize_content_view {
|
if pl_attrs.fullsize_content_view {
|
||||||
masks |= NSWindowStyleMask::NSFullSizeContentViewWindowMask;
|
masks |= NSWindowStyleMask::NSFullSizeContentViewWindowMask;
|
||||||
}
|
}
|
||||||
|
@ -333,6 +341,12 @@ impl WinitWindow {
|
||||||
this.setMovableByWindowBackground(true);
|
this.setMovableByWindowBackground(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !attrs.enabled_buttons.contains(WindowButtons::MAXIMIZE) {
|
||||||
|
if let Some(button) = this.standardWindowButton(NSWindowButton::Zoom) {
|
||||||
|
button.setEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(increments) = attrs.resize_increments {
|
if let Some(increments) = attrs.resize_increments {
|
||||||
let increments = increments.to_logical(this.scale_factor());
|
let increments = increments.to_logical(this.scale_factor());
|
||||||
let (w, h) = (increments.width, increments.height);
|
let (w, h) = (increments.width, increments.height);
|
||||||
|
@ -624,6 +638,53 @@ impl WinitWindow {
|
||||||
self.isResizable()
|
self.isResizable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_enabled_buttons(&self, buttons: WindowButtons) {
|
||||||
|
let mut mask = self.styleMask();
|
||||||
|
|
||||||
|
if buttons.contains(WindowButtons::CLOSE) {
|
||||||
|
mask |= NSWindowStyleMask::NSClosableWindowMask;
|
||||||
|
} else {
|
||||||
|
mask &= !NSWindowStyleMask::NSClosableWindowMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
if buttons.contains(WindowButtons::MINIMIZE) {
|
||||||
|
mask |= NSWindowStyleMask::NSMiniaturizableWindowMask;
|
||||||
|
} else {
|
||||||
|
mask &= !NSWindowStyleMask::NSMiniaturizableWindowMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn enabled_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)
|
||||||
|
{
|
||||||
|
buttons |= WindowButtons::MAXIMIZE;
|
||||||
|
}
|
||||||
|
if self.hasCloseBox() {
|
||||||
|
buttons |= WindowButtons::CLOSE;
|
||||||
|
}
|
||||||
|
buttons
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_cursor_icon(&self, icon: CursorIcon) {
|
pub fn set_cursor_icon(&self, icon: CursorIcon) {
|
||||||
let view = self.view();
|
let view = self.view();
|
||||||
let mut cursor_state = view.state.cursor_state.lock().unwrap();
|
let mut cursor_state = view.state.cursor_state.lock().unwrap();
|
||||||
|
|
|
@ -3,8 +3,8 @@ use crate::error::{ExternalError, NotSupportedError, OsError as RootOE};
|
||||||
use crate::event;
|
use crate::event;
|
||||||
use crate::icon::Icon;
|
use crate::icon::Icon;
|
||||||
use crate::window::{
|
use crate::window::{
|
||||||
CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes, WindowId as RootWI,
|
CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes, WindowButtons,
|
||||||
WindowLevel,
|
WindowId as RootWI, WindowLevel,
|
||||||
};
|
};
|
||||||
|
|
||||||
use raw_window_handle::{RawDisplayHandle, RawWindowHandle, WebDisplayHandle, WebWindowHandle};
|
use raw_window_handle::{RawDisplayHandle, RawWindowHandle, WebDisplayHandle, WebWindowHandle};
|
||||||
|
@ -172,6 +172,14 @@ impl Window {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_enabled_buttons(&self, _buttons: WindowButtons) {}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn enabled_buttons(&self) -> WindowButtons {
|
||||||
|
WindowButtons::all()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn scale_factor(&self) -> f64 {
|
pub fn scale_factor(&self) -> f64 {
|
||||||
super::backend::scale_factor()
|
super::backend::scale_factor()
|
||||||
|
|
|
@ -71,7 +71,10 @@ use crate::{
|
||||||
window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState},
|
window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState},
|
||||||
Fullscreen, Parent, PlatformSpecificWindowBuilderAttributes, WindowId,
|
Fullscreen, Parent, PlatformSpecificWindowBuilderAttributes, WindowId,
|
||||||
},
|
},
|
||||||
window::{CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes, WindowLevel},
|
window::{
|
||||||
|
CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes, WindowButtons,
|
||||||
|
WindowLevel,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The Win32 implementation of the main `Window` object.
|
/// The Win32 implementation of the main `Window` object.
|
||||||
|
@ -263,6 +266,44 @@ impl Window {
|
||||||
window_state.window_flags.contains(WindowFlags::RESIZABLE)
|
window_state.window_flags.contains(WindowFlags::RESIZABLE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_enabled_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::MINIMIZABLE,
|
||||||
|
buttons.contains(WindowButtons::MINIMIZE),
|
||||||
|
);
|
||||||
|
f.set(
|
||||||
|
WindowFlags::MAXIMIZABLE,
|
||||||
|
buttons.contains(WindowButtons::MAXIMIZE),
|
||||||
|
);
|
||||||
|
f.set(
|
||||||
|
WindowFlags::CLOSABLE,
|
||||||
|
buttons.contains(WindowButtons::CLOSE),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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.
|
/// Returns the `hwnd` of this window.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn hwnd(&self) -> HWND {
|
pub fn hwnd(&self) -> HWND {
|
||||||
|
@ -943,6 +984,8 @@ impl<'a, T: 'static> InitData<'a, T> {
|
||||||
// attribute is correctly applied.
|
// attribute is correctly applied.
|
||||||
win.set_visible(attributes.visible);
|
win.set_visible(attributes.visible);
|
||||||
|
|
||||||
|
win.set_enabled_buttons(attributes.enabled_buttons);
|
||||||
|
|
||||||
if attributes.fullscreen.is_some() {
|
if attributes.fullscreen.is_some() {
|
||||||
win.set_fullscreen(attributes.fullscreen);
|
win.set_fullscreen(attributes.fullscreen);
|
||||||
force_window_active(win.window.0);
|
force_window_active(win.window.0);
|
||||||
|
@ -1012,6 +1055,9 @@ where
|
||||||
window_flags.set(WindowFlags::TRANSPARENT, attributes.transparent);
|
window_flags.set(WindowFlags::TRANSPARENT, attributes.transparent);
|
||||||
// WindowFlags::VISIBLE and MAXIMIZED are set down below after the window has been configured.
|
// 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::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.
|
||||||
|
window_flags.set(WindowFlags::CLOSABLE, true);
|
||||||
|
|
||||||
let parent = match pl_attribs.parent {
|
let parent = match pl_attribs.parent {
|
||||||
Parent::ChildOf(parent) => {
|
Parent::ChildOf(parent) => {
|
||||||
|
|
|
@ -11,8 +11,9 @@ use windows_sys::Win32::{
|
||||||
Foundation::{HWND, RECT},
|
Foundation::{HWND, RECT},
|
||||||
Graphics::Gdi::InvalidateRgn,
|
Graphics::Gdi::InvalidateRgn,
|
||||||
UI::WindowsAndMessaging::{
|
UI::WindowsAndMessaging::{
|
||||||
AdjustWindowRectEx, GetMenu, GetWindowLongW, SendMessageW, SetWindowLongW, SetWindowPos,
|
AdjustWindowRectEx, EnableMenuItem, GetMenu, GetSystemMenu, GetWindowLongW, SendMessageW,
|
||||||
ShowWindow, GWL_EXSTYLE, GWL_STYLE, HWND_BOTTOM, HWND_NOTOPMOST, HWND_TOPMOST,
|
SetWindowLongW, SetWindowPos, ShowWindow, GWL_EXSTYLE, GWL_STYLE, HWND_BOTTOM,
|
||||||
|
HWND_NOTOPMOST, HWND_TOPMOST, MF_BYCOMMAND, MF_DISABLED, MF_ENABLED, SC_CLOSE,
|
||||||
SWP_ASYNCWINDOWPOS, SWP_FRAMECHANGED, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOREPOSITION,
|
SWP_ASYNCWINDOWPOS, SWP_FRAMECHANGED, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOREPOSITION,
|
||||||
SWP_NOSIZE, SWP_NOZORDER, SW_HIDE, SW_MAXIMIZE, SW_MINIMIZE, SW_RESTORE, SW_SHOW,
|
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,
|
WINDOWPLACEMENT, WINDOW_EX_STYLE, WINDOW_STYLE, WS_BORDER, WS_CAPTION, WS_CHILD,
|
||||||
|
@ -77,38 +78,41 @@ bitflags! {
|
||||||
bitflags! {
|
bitflags! {
|
||||||
pub struct WindowFlags: u32 {
|
pub struct WindowFlags: u32 {
|
||||||
const RESIZABLE = 1 << 0;
|
const RESIZABLE = 1 << 0;
|
||||||
const VISIBLE = 1 << 1;
|
const MINIMIZABLE = 1 << 1;
|
||||||
const ON_TASKBAR = 1 << 2;
|
const MAXIMIZABLE = 1 << 2;
|
||||||
const ALWAYS_ON_TOP = 1 << 3;
|
const CLOSABLE = 1 << 3;
|
||||||
const ALWAYS_ON_BOTTOM = 1 << 4;
|
const VISIBLE = 1 << 4;
|
||||||
const NO_BACK_BUFFER = 1 << 5;
|
const ON_TASKBAR = 1 << 5;
|
||||||
const TRANSPARENT = 1 << 6;
|
const ALWAYS_ON_TOP = 1 << 6;
|
||||||
const CHILD = 1 << 7;
|
const ALWAYS_ON_BOTTOM = 1 << 7;
|
||||||
const MAXIMIZED = 1 << 8;
|
const NO_BACK_BUFFER = 1 << 8;
|
||||||
const POPUP = 1 << 9;
|
const TRANSPARENT = 1 << 9;
|
||||||
|
const CHILD = 1 << 10;
|
||||||
|
const MAXIMIZED = 1 << 11;
|
||||||
|
const POPUP = 1 << 12;
|
||||||
|
|
||||||
/// Marker flag for fullscreen. Should always match `WindowState::fullscreen`, but is
|
/// Marker flag for fullscreen. Should always match `WindowState::fullscreen`, but is
|
||||||
/// included here to make masking easier.
|
/// included here to make masking easier.
|
||||||
const MARKER_EXCLUSIVE_FULLSCREEN = 1 << 10;
|
const MARKER_EXCLUSIVE_FULLSCREEN = 1 << 13;
|
||||||
const MARKER_BORDERLESS_FULLSCREEN = 1 << 11;
|
const MARKER_BORDERLESS_FULLSCREEN = 1 << 14;
|
||||||
|
|
||||||
/// The `WM_SIZE` event contains some parameters that can effect the state of `WindowFlags`.
|
/// 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
|
/// 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
|
/// 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
|
/// 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.
|
/// window's state to match our stored state. This controls whether to accept those changes.
|
||||||
const MARKER_RETAIN_STATE_ON_SIZE = 1 << 12;
|
const MARKER_RETAIN_STATE_ON_SIZE = 1 << 15;
|
||||||
|
|
||||||
const MARKER_IN_SIZE_MOVE = 1 << 13;
|
const MARKER_IN_SIZE_MOVE = 1 << 16;
|
||||||
|
|
||||||
const MINIMIZED = 1 << 14;
|
const MINIMIZED = 1 << 17;
|
||||||
|
|
||||||
const IGNORE_CURSOR_EVENT = 1 << 15;
|
const IGNORE_CURSOR_EVENT = 1 << 18;
|
||||||
|
|
||||||
/// Fully decorated window (incl. caption, border and drop shadow).
|
/// Fully decorated window (incl. caption, border and drop shadow).
|
||||||
const MARKER_DECORATIONS = 1 << 16;
|
const MARKER_DECORATIONS = 1 << 19;
|
||||||
/// Drop shadow for undecorated windows.
|
/// Drop shadow for undecorated windows.
|
||||||
const MARKER_UNDECORATED_SHADOW = 1 << 17;
|
const MARKER_UNDECORATED_SHADOW = 1 << 20;
|
||||||
|
|
||||||
const EXCLUSIVE_FULLSCREEN_OR_MASK = WindowFlags::ALWAYS_ON_TOP.bits;
|
const EXCLUSIVE_FULLSCREEN_OR_MASK = WindowFlags::ALWAYS_ON_TOP.bits;
|
||||||
}
|
}
|
||||||
|
@ -237,16 +241,17 @@ impl WindowFlags {
|
||||||
|
|
||||||
pub fn to_window_styles(self) -> (WINDOW_STYLE, WINDOW_EX_STYLE) {
|
pub fn to_window_styles(self) -> (WINDOW_STYLE, WINDOW_EX_STYLE) {
|
||||||
// Required styles to properly support common window functionality like aero snap.
|
// Required styles to properly support common window functionality like aero snap.
|
||||||
let mut style = WS_CAPTION
|
let mut style = WS_CAPTION | WS_BORDER | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_SYSMENU;
|
||||||
| WS_MINIMIZEBOX
|
|
||||||
| WS_BORDER
|
|
||||||
| WS_CLIPSIBLINGS
|
|
||||||
| WS_CLIPCHILDREN
|
|
||||||
| WS_SYSMENU;
|
|
||||||
let mut style_ex = WS_EX_WINDOWEDGE | WS_EX_ACCEPTFILES;
|
let mut style_ex = WS_EX_WINDOWEDGE | WS_EX_ACCEPTFILES;
|
||||||
|
|
||||||
if self.contains(WindowFlags::RESIZABLE) {
|
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) {
|
if self.contains(WindowFlags::VISIBLE) {
|
||||||
style |= WS_VISIBLE;
|
style |= WS_VISIBLE;
|
||||||
|
@ -350,6 +355,18 @@ 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, flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !new.contains(WindowFlags::VISIBLE) {
|
if !new.contains(WindowFlags::VISIBLE) {
|
||||||
unsafe {
|
unsafe {
|
||||||
ShowWindow(window, SW_HIDE);
|
ShowWindow(window, SW_HIDE);
|
||||||
|
|
|
@ -126,6 +126,7 @@ pub(crate) struct WindowAttributes {
|
||||||
pub max_inner_size: Option<Size>,
|
pub max_inner_size: Option<Size>,
|
||||||
pub position: Option<Position>,
|
pub position: Option<Position>,
|
||||||
pub resizable: bool,
|
pub resizable: bool,
|
||||||
|
pub enabled_buttons: WindowButtons,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub fullscreen: Option<platform_impl::Fullscreen>,
|
pub fullscreen: Option<platform_impl::Fullscreen>,
|
||||||
pub maximized: bool,
|
pub maximized: bool,
|
||||||
|
@ -148,6 +149,7 @@ impl Default for WindowAttributes {
|
||||||
max_inner_size: None,
|
max_inner_size: None,
|
||||||
position: None,
|
position: None,
|
||||||
resizable: true,
|
resizable: true,
|
||||||
|
enabled_buttons: WindowButtons::all(),
|
||||||
title: "winit window".to_owned(),
|
title: "winit window".to_owned(),
|
||||||
maximized: false,
|
maximized: false,
|
||||||
fullscreen: None,
|
fullscreen: None,
|
||||||
|
@ -244,6 +246,17 @@ impl WindowBuilder {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the enabled window buttons.
|
||||||
|
///
|
||||||
|
/// The default is [`WindowButtons::all`]
|
||||||
|
///
|
||||||
|
/// See [`Window::set_enabled_buttons`] for details.
|
||||||
|
#[inline]
|
||||||
|
pub fn with_enabled_buttons(mut self, buttons: WindowButtons) -> Self {
|
||||||
|
self.window.enabled_buttons = buttons;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets the initial title of the window in the title bar.
|
/// Sets the initial title of the window in the title bar.
|
||||||
///
|
///
|
||||||
/// The default is `"winit window"`.
|
/// The default is `"winit window"`.
|
||||||
|
@ -755,6 +768,26 @@ impl Window {
|
||||||
self.window.is_resizable()
|
self.window.is_resizable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the enabled window buttons.
|
||||||
|
///
|
||||||
|
/// ## Platform-specific
|
||||||
|
///
|
||||||
|
/// - **Wayland / X11:** Not implemented.
|
||||||
|
/// - **Web / iOS / Android:** Unsupported.
|
||||||
|
pub fn set_enabled_buttons(&self, buttons: WindowButtons) {
|
||||||
|
self.window.set_enabled_buttons(buttons)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the enabled window buttons.
|
||||||
|
///
|
||||||
|
/// ## Platform-specific
|
||||||
|
///
|
||||||
|
/// - **Wayland / X11:** Not implemented. Always returns [`WindowButtons::all`].
|
||||||
|
/// - **Web / iOS / Android:** Unsupported. Always returns [`WindowButtons::all`].
|
||||||
|
pub fn enabled_buttons(&self) -> WindowButtons {
|
||||||
|
self.window.enabled_buttons()
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets the window to minimized or back
|
/// Sets the window to minimized or back
|
||||||
///
|
///
|
||||||
/// ## Platform-specific
|
/// ## Platform-specific
|
||||||
|
@ -1441,6 +1474,14 @@ impl Default for UserAttentionType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
pub struct WindowButtons: u32 {
|
||||||
|
const CLOSE = 1 << 0;
|
||||||
|
const MINIMIZE = 1 << 1;
|
||||||
|
const MAXIMIZE = 1 << 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A window level groups windows with respect to their z-position.
|
/// A window level groups windows with respect to their z-position.
|
||||||
///
|
///
|
||||||
/// The relative ordering between windows in different window levels is fixed.
|
/// The relative ordering between windows in different window levels is fixed.
|
||||||
|
|
Loading…
Reference in a new issue