Add Window::show_window_menu
Add a method to request a system menu. The implementation is provided only on Windows for now. Co-authored-by: daxpedda <daxpedda@gmail.com> Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
This commit is contained in:
parent
42c9b7e40e
commit
1ea41a2ee2
10 changed files with 154 additions and 19 deletions
|
|
@ -929,6 +929,9 @@ impl Window {
|
|||
))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show_window_menu(&self, _position: Position) {}
|
||||
|
||||
pub fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), error::ExternalError> {
|
||||
Err(error::ExternalError::NotSupported(
|
||||
error::NotSupportedError::new(),
|
||||
|
|
|
|||
|
|
@ -198,6 +198,9 @@ impl Inner {
|
|||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show_window_menu(&self, _position: Position) {}
|
||||
|
||||
pub fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), ExternalError> {
|
||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -444,6 +444,9 @@ impl Window {
|
|||
x11_or_wayland!(match self; Window(window) => window.drag_resize_window(direction))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show_window_menu(&self, _position: Position) {}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_cursor_hittest(hittest))
|
||||
|
|
|
|||
|
|
@ -893,6 +893,9 @@ impl WinitWindow {
|
|||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show_window_menu(&self, _position: Position) {}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
|
||||
self.setIgnoresMouseEvents(!hittest);
|
||||
|
|
|
|||
|
|
@ -390,6 +390,9 @@ impl Window {
|
|||
))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show_window_menu(&self, _position: Position) {}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), error::ExternalError> {
|
||||
Err(error::ExternalError::NotSupported(
|
||||
|
|
|
|||
|
|
@ -244,6 +244,9 @@ impl Inner {
|
|||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show_window_menu(&self, _position: Position) {}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), ExternalError> {
|
||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
|
|
|
|||
|
|
@ -40,15 +40,19 @@ use windows_sys::Win32::{
|
|||
Touch::{RegisterTouchWindow, TWF_WANTPALM},
|
||||
},
|
||||
WindowsAndMessaging::{
|
||||
CreateWindowExW, FlashWindowEx, GetClientRect, GetCursorPos, GetForegroundWindow,
|
||||
GetSystemMetrics, GetWindowPlacement, GetWindowTextLengthW, GetWindowTextW,
|
||||
IsWindowVisible, LoadCursorW, PeekMessageW, PostMessageW, RegisterClassExW, SetCursor,
|
||||
SetCursorPos, SetForegroundWindow, SetWindowDisplayAffinity, SetWindowPlacement,
|
||||
SetWindowPos, SetWindowTextW, CS_HREDRAW, CS_VREDRAW, CW_USEDEFAULT, FLASHWINFO,
|
||||
CreateWindowExW, EnableMenuItem, FlashWindowEx, GetClientRect, GetCursorPos,
|
||||
GetForegroundWindow, GetSystemMenu, GetSystemMetrics, GetWindowPlacement,
|
||||
GetWindowTextLengthW, GetWindowTextW, IsWindowVisible, LoadCursorW, PeekMessageW,
|
||||
PostMessageW, RegisterClassExW, SetCursor, SetCursorPos, SetForegroundWindow,
|
||||
SetMenuDefaultItem, SetWindowDisplayAffinity, SetWindowPlacement, SetWindowPos,
|
||||
SetWindowTextW, TrackPopupMenu, CS_HREDRAW, CS_VREDRAW, CW_USEDEFAULT, FLASHWINFO,
|
||||
FLASHW_ALL, FLASHW_STOP, FLASHW_TIMERNOFG, FLASHW_TRAY, GWLP_HINSTANCE, HTBOTTOM,
|
||||
HTBOTTOMLEFT, HTBOTTOMRIGHT, HTCAPTION, HTLEFT, HTRIGHT, HTTOP, HTTOPLEFT, HTTOPRIGHT,
|
||||
NID_READY, PM_NOREMOVE, SM_DIGITIZER, SWP_ASYNCWINDOWPOS, SWP_NOACTIVATE, SWP_NOSIZE,
|
||||
SWP_NOZORDER, WDA_EXCLUDEFROMCAPTURE, WDA_NONE, WM_NCLBUTTONDOWN, WNDCLASSEXW,
|
||||
MENU_ITEM_STATE, MFS_DISABLED, MFS_ENABLED, MF_BYCOMMAND, NID_READY, PM_NOREMOVE,
|
||||
SC_CLOSE, SC_MAXIMIZE, SC_MINIMIZE, SC_MOVE, SC_RESTORE, SC_SIZE, SM_DIGITIZER,
|
||||
SWP_ASYNCWINDOWPOS, SWP_NOACTIVATE, SWP_NOSIZE, SWP_NOZORDER, TPM_LEFTALIGN,
|
||||
TPM_RETURNCMD, WDA_EXCLUDEFROMCAPTURE, WDA_NONE, WM_NCLBUTTONDOWN, WM_SYSCOMMAND,
|
||||
WNDCLASSEXW,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -475,6 +479,83 @@ impl Window {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn handle_showing_window_menu(&self, position: Position) {
|
||||
unsafe {
|
||||
let point = {
|
||||
let mut point = POINT { x: 0, y: 0 };
|
||||
let scale_factor = self.scale_factor();
|
||||
let (x, y) = position.to_physical::<i32>(scale_factor).into();
|
||||
point.x = x;
|
||||
point.y = y;
|
||||
if ClientToScreen(self.hwnd(), &mut point) == false.into() {
|
||||
warn!("Can't convert client-area coordinates to screen coordinates when showing window menu.");
|
||||
return;
|
||||
}
|
||||
point
|
||||
};
|
||||
|
||||
// get the current system menu
|
||||
let h_menu = GetSystemMenu(self.hwnd(), 0);
|
||||
if h_menu == 0 {
|
||||
warn!("The corresponding window doesn't have a system menu");
|
||||
// This situation should not be treated as an error so just return without showing menu.
|
||||
return;
|
||||
}
|
||||
|
||||
fn enable(b: bool) -> MENU_ITEM_STATE {
|
||||
if b {
|
||||
MFS_ENABLED
|
||||
} else {
|
||||
MFS_DISABLED
|
||||
}
|
||||
}
|
||||
|
||||
// Change the menu items according to the current window status.
|
||||
|
||||
let restore_btn = enable(self.is_maximized() && self.is_resizable());
|
||||
let size_btn = enable(!self.is_maximized() && self.is_resizable());
|
||||
let maximize_btn = enable(!self.is_maximized() && self.is_resizable());
|
||||
|
||||
EnableMenuItem(h_menu, SC_RESTORE, MF_BYCOMMAND | restore_btn);
|
||||
EnableMenuItem(h_menu, SC_MOVE, MF_BYCOMMAND | enable(!self.is_maximized()));
|
||||
EnableMenuItem(h_menu, SC_SIZE, MF_BYCOMMAND | size_btn);
|
||||
EnableMenuItem(h_menu, SC_MINIMIZE, MF_BYCOMMAND | MFS_ENABLED);
|
||||
EnableMenuItem(h_menu, SC_MAXIMIZE, MF_BYCOMMAND | maximize_btn);
|
||||
EnableMenuItem(h_menu, SC_CLOSE, MF_BYCOMMAND | MFS_ENABLED);
|
||||
|
||||
// Set the default menu item.
|
||||
SetMenuDefaultItem(h_menu, SC_CLOSE, 0);
|
||||
|
||||
// Popup the system menu at the position.
|
||||
let result = TrackPopupMenu(
|
||||
h_menu,
|
||||
TPM_RETURNCMD | TPM_LEFTALIGN, // for now im using LTR, but we have to use user layout direction
|
||||
point.x,
|
||||
point.y,
|
||||
0,
|
||||
self.hwnd(),
|
||||
std::ptr::null_mut(),
|
||||
);
|
||||
|
||||
if result == 0 {
|
||||
// User canceled the menu, no need to continue.
|
||||
return;
|
||||
}
|
||||
|
||||
// Send the command that the user select to the corresponding window.
|
||||
if PostMessageW(self.hwnd(), WM_SYSCOMMAND, result as _, 0) == 0 {
|
||||
warn!("Can't post the system menu message to the window.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show_window_menu(&self, position: Position) {
|
||||
unsafe {
|
||||
self.handle_showing_window_menu(position);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
|
||||
let window = self.window.clone();
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ pub use raw_window_handle;
|
|||
/// ```no_run
|
||||
/// use winit::{
|
||||
/// event::{Event, WindowEvent},
|
||||
/// event_loop::EventLoop,
|
||||
/// event_loop::{ControlFlow, EventLoop},
|
||||
/// window::Window,
|
||||
/// };
|
||||
///
|
||||
|
|
@ -1423,6 +1423,21 @@ impl Window {
|
|||
.maybe_wait_on_main(|w| w.drag_resize_window(direction))
|
||||
}
|
||||
|
||||
/// Show [window menu] at a specified position .
|
||||
///
|
||||
/// This is the context menu that is normally shown when interacting with
|
||||
/// the title bar. This is useful when implementing custom decorations.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
/// **Android / iOS / macOS / Orbital / Wayland / Web / X11:** Unsupported.
|
||||
///
|
||||
/// [window menu]: https://en.wikipedia.org/wiki/Common_menus_in_Microsoft_Windows#System_menu
|
||||
pub fn show_window_menu(&self, position: impl Into<Position>) {
|
||||
let position = position.into();
|
||||
self.window
|
||||
.maybe_queue_on_main(move |w| w.show_window_menu(position))
|
||||
}
|
||||
|
||||
/// Modifies whether the window catches cursor events.
|
||||
///
|
||||
/// If `true`, the window will catch the cursor events. If `false`, events are passed through
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue