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:
YouKnow 2023-10-11 01:16:16 +03:30 committed by GitHub
parent 42c9b7e40e
commit 1ea41a2ee2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 154 additions and 19 deletions

View file

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