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
|
|
@ -30,6 +30,7 @@ And please only add new entries to the top of this list, right below the `# Unre
|
||||||
- On Android, fix `DeviceId` to contain device id's.
|
- On Android, fix `DeviceId` to contain device id's.
|
||||||
- Add `Window::set_blur` to request a blur behind the window; implemented on Wayland for now.
|
- Add `Window::set_blur` to request a blur behind the window; implemented on Wayland for now.
|
||||||
- On Web, fix `ControlFlow::WaitUntil` to never wake up **before** the given time.
|
- On Web, fix `ControlFlow::WaitUntil` to never wake up **before** the given time.
|
||||||
|
- Add `Window::show_window_menu` to request a titlebar/system menu; implemented on Windows for now.
|
||||||
|
|
||||||
# 0.29.1-beta
|
# 0.29.1-beta
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||||
|
|
||||||
let mut switched = false;
|
let mut switched = false;
|
||||||
let mut entered_id = window_2.id();
|
let mut entered_id = window_2.id();
|
||||||
|
let mut cursor_location = None;
|
||||||
|
|
||||||
event_loop.run(move |event, elwt| match event {
|
event_loop.run(move |event, elwt| match event {
|
||||||
Event::NewEvents(StartCause::Init) => {
|
Event::NewEvents(StartCause::Init) => {
|
||||||
|
|
@ -27,11 +28,8 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||||
}
|
}
|
||||||
Event::WindowEvent { event, window_id } => match event {
|
Event::WindowEvent { event, window_id } => match event {
|
||||||
WindowEvent::CloseRequested => elwt.exit(),
|
WindowEvent::CloseRequested => elwt.exit(),
|
||||||
WindowEvent::MouseInput {
|
WindowEvent::CursorMoved { position, .. } => cursor_location = Some(position),
|
||||||
state: ElementState::Pressed,
|
WindowEvent::MouseInput { state, button, .. } => {
|
||||||
button: MouseButton::Left,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
let window = if (window_id == window_1.id() && switched)
|
let window = if (window_id == window_1.id() && switched)
|
||||||
|| (window_id == window_2.id() && !switched)
|
|| (window_id == window_2.id() && !switched)
|
||||||
{
|
{
|
||||||
|
|
@ -40,7 +38,15 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||||
&window_1
|
&window_1
|
||||||
};
|
};
|
||||||
|
|
||||||
window.drag_window().unwrap()
|
match (button, state) {
|
||||||
|
(MouseButton::Left, ElementState::Pressed) => window.drag_window().unwrap(),
|
||||||
|
(MouseButton::Right, ElementState::Released) => {
|
||||||
|
if let Some(position) = cursor_location {
|
||||||
|
window.show_window_menu(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
WindowEvent::CursorEntered { .. } => {
|
WindowEvent::CursorEntered { .. } => {
|
||||||
entered_id = window_id;
|
entered_id = window_id;
|
||||||
|
|
@ -54,11 +60,25 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||||
..
|
..
|
||||||
},
|
},
|
||||||
..
|
..
|
||||||
} if c == "x" => {
|
} => match c.as_str() {
|
||||||
switched = !switched;
|
"x" => {
|
||||||
name_windows(entered_id, switched, &window_1, &window_2);
|
switched = !switched;
|
||||||
println!("Switched!")
|
name_windows(entered_id, switched, &window_1, &window_2);
|
||||||
}
|
println!("Switched!")
|
||||||
|
}
|
||||||
|
"d" => {
|
||||||
|
let window = if (window_id == window_1.id() && switched)
|
||||||
|
|| (window_id == window_2.id() && !switched)
|
||||||
|
{
|
||||||
|
&window_2
|
||||||
|
} else {
|
||||||
|
&window_1
|
||||||
|
};
|
||||||
|
|
||||||
|
window.set_decorations(!window.is_decorated());
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
WindowEvent::RedrawRequested => {
|
WindowEvent::RedrawRequested => {
|
||||||
if window_id == window_1.id() {
|
if window_id == window_1.id() {
|
||||||
fill::fill_window(&window_1);
|
fill::fill_window(&window_1);
|
||||||
|
|
|
||||||
|
|
@ -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> {
|
pub fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), error::ExternalError> {
|
||||||
Err(error::ExternalError::NotSupported(
|
Err(error::ExternalError::NotSupported(
|
||||||
error::NotSupportedError::new(),
|
error::NotSupportedError::new(),
|
||||||
|
|
|
||||||
|
|
@ -198,6 +198,9 @@ impl Inner {
|
||||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn show_window_menu(&self, _position: Position) {}
|
||||||
|
|
||||||
pub fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), ExternalError> {
|
pub fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), ExternalError> {
|
||||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -444,6 +444,9 @@ impl Window {
|
||||||
x11_or_wayland!(match self; Window(window) => window.drag_resize_window(direction))
|
x11_or_wayland!(match self; Window(window) => window.drag_resize_window(direction))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn show_window_menu(&self, _position: Position) {}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
|
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
|
||||||
x11_or_wayland!(match self; Window(w) => w.set_cursor_hittest(hittest))
|
x11_or_wayland!(match self; Window(w) => w.set_cursor_hittest(hittest))
|
||||||
|
|
|
||||||
|
|
@ -893,6 +893,9 @@ impl WinitWindow {
|
||||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn show_window_menu(&self, _position: Position) {}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
|
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
|
||||||
self.setIgnoresMouseEvents(!hittest);
|
self.setIgnoresMouseEvents(!hittest);
|
||||||
|
|
|
||||||
|
|
@ -390,6 +390,9 @@ impl Window {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn show_window_menu(&self, _position: Position) {}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), error::ExternalError> {
|
pub fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), error::ExternalError> {
|
||||||
Err(error::ExternalError::NotSupported(
|
Err(error::ExternalError::NotSupported(
|
||||||
|
|
|
||||||
|
|
@ -244,6 +244,9 @@ impl Inner {
|
||||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn show_window_menu(&self, _position: Position) {}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), ExternalError> {
|
pub fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), ExternalError> {
|
||||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||||
|
|
|
||||||
|
|
@ -40,15 +40,19 @@ use windows_sys::Win32::{
|
||||||
Touch::{RegisterTouchWindow, TWF_WANTPALM},
|
Touch::{RegisterTouchWindow, TWF_WANTPALM},
|
||||||
},
|
},
|
||||||
WindowsAndMessaging::{
|
WindowsAndMessaging::{
|
||||||
CreateWindowExW, FlashWindowEx, GetClientRect, GetCursorPos, GetForegroundWindow,
|
CreateWindowExW, EnableMenuItem, FlashWindowEx, GetClientRect, GetCursorPos,
|
||||||
GetSystemMetrics, GetWindowPlacement, GetWindowTextLengthW, GetWindowTextW,
|
GetForegroundWindow, GetSystemMenu, GetSystemMetrics, GetWindowPlacement,
|
||||||
IsWindowVisible, LoadCursorW, PeekMessageW, PostMessageW, RegisterClassExW, SetCursor,
|
GetWindowTextLengthW, GetWindowTextW, IsWindowVisible, LoadCursorW, PeekMessageW,
|
||||||
SetCursorPos, SetForegroundWindow, SetWindowDisplayAffinity, SetWindowPlacement,
|
PostMessageW, RegisterClassExW, SetCursor, SetCursorPos, SetForegroundWindow,
|
||||||
SetWindowPos, SetWindowTextW, CS_HREDRAW, CS_VREDRAW, CW_USEDEFAULT, FLASHWINFO,
|
SetMenuDefaultItem, SetWindowDisplayAffinity, SetWindowPlacement, SetWindowPos,
|
||||||
|
SetWindowTextW, TrackPopupMenu, CS_HREDRAW, CS_VREDRAW, CW_USEDEFAULT, FLASHWINFO,
|
||||||
FLASHW_ALL, FLASHW_STOP, FLASHW_TIMERNOFG, FLASHW_TRAY, GWLP_HINSTANCE, HTBOTTOM,
|
FLASHW_ALL, FLASHW_STOP, FLASHW_TIMERNOFG, FLASHW_TRAY, GWLP_HINSTANCE, HTBOTTOM,
|
||||||
HTBOTTOMLEFT, HTBOTTOMRIGHT, HTCAPTION, HTLEFT, HTRIGHT, HTTOP, HTTOPLEFT, HTTOPRIGHT,
|
HTBOTTOMLEFT, HTBOTTOMRIGHT, HTCAPTION, HTLEFT, HTRIGHT, HTTOP, HTTOPLEFT, HTTOPRIGHT,
|
||||||
NID_READY, PM_NOREMOVE, SM_DIGITIZER, SWP_ASYNCWINDOWPOS, SWP_NOACTIVATE, SWP_NOSIZE,
|
MENU_ITEM_STATE, MFS_DISABLED, MFS_ENABLED, MF_BYCOMMAND, NID_READY, PM_NOREMOVE,
|
||||||
SWP_NOZORDER, WDA_EXCLUDEFROMCAPTURE, WDA_NONE, WM_NCLBUTTONDOWN, WNDCLASSEXW,
|
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(())
|
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]
|
#[inline]
|
||||||
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
|
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
|
||||||
let window = self.window.clone();
|
let window = self.window.clone();
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ pub use raw_window_handle;
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// use winit::{
|
/// use winit::{
|
||||||
/// event::{Event, WindowEvent},
|
/// event::{Event, WindowEvent},
|
||||||
/// event_loop::EventLoop,
|
/// event_loop::{ControlFlow, EventLoop},
|
||||||
/// window::Window,
|
/// window::Window,
|
||||||
/// };
|
/// };
|
||||||
///
|
///
|
||||||
|
|
@ -1423,6 +1423,21 @@ impl Window {
|
||||||
.maybe_wait_on_main(|w| w.drag_resize_window(direction))
|
.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.
|
/// Modifies whether the window catches cursor events.
|
||||||
///
|
///
|
||||||
/// If `true`, the window will catch the cursor events. If `false`, events are passed through
|
/// 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