diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 3eac291099..27e760d6d4 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -833,17 +833,64 @@ unsafe extern "system" fn public_window_callback( } winuser::WM_NCLBUTTONDOWN => { - // jumpstart the modal loop - winuser::RedrawWindow( - window, - ptr::null(), - ptr::null_mut(), - winuser::RDW_INTERNALPAINT, - ); - if wparam == winuser::HTCAPTION as _ { - winuser::PostMessageW(window, winuser::WM_MOUSEMOVE, 0, 0); + match wparam as isize { + winuser::HTMAXBUTTON | winuser::HTMINBUTTON | winuser::HTCLOSE => { + // Discard the non-client button-down event and defer the processing until + // the non-client button-up event. This prevents a pause in the message loop + // while the user has the mouse button pressed on one of these buttons. + let mut window_state = subclass_input.window_state.lock(); + window_state.deferred_nclbuttondown_wparam = Some(wparam); + 0 + } + _ => { + // jumpstart the modal loop + winuser::RedrawWindow( + window, + ptr::null(), + ptr::null_mut(), + winuser::RDW_INTERNALPAINT, + ); + if wparam == winuser::HTCAPTION as _ { + winuser::PostMessageW(window, winuser::WM_MOUSEMOVE, 0, 0); + } + commctrl::DefSubclassProc(window, msg, wparam, lparam) + } + } + } + + winuser::WM_NCLBUTTONUP => { + let deferred_wparam = subclass_input + .window_state + .lock() + .deferred_nclbuttondown_wparam + .filter(|deferred_wparam| *deferred_wparam == wparam) + .map(|wparam| wparam as isize) + .take(); + // Manually apply min, max, restore, and close since we discarded the button down event, + // but only in the event that the user did not cancel the operation. If the user canelled + // the operation, WM_LBUTTONUP will fire and we'll clear the deferred state there as well. + match deferred_wparam { + Some(winuser::HTMAXBUTTON) => { + let window_state = subclass_input.window_state.lock(); + let window_flags = window_state.window_flags(); + drop(window_state); + if window_flags.contains(WindowFlags::MAXIMIZED) { + winuser::ShowWindow(window, winuser::SW_RESTORE); + } else { + winuser::ShowWindow(window, winuser::SW_MAXIMIZE); + } + 0 + } + Some(winuser::HTMINBUTTON) => { + winuser::ShowWindow(window, winuser::SW_MINIMIZE); + 0 + } + Some(winuser::HTCLOSE) => { + winuser::PostMessageW(window, winuser::WM_CLOSE, 0, 0); + 0 + } + _ => commctrl::DefSubclassProc(window, msg, wparam, lparam), } - commctrl::DefSubclassProc(window, msg, wparam, lparam) } winuser::WM_CLOSE => { @@ -1149,6 +1196,12 @@ unsafe extern "system" fn public_window_callback( ElementState::Released, MouseButton::Left, WindowEvent::MouseInput, }; + subclass_input + .window_state + .lock() + .deferred_nclbuttondown_wparam + .take(); + release_mouse(&mut *subclass_input.window_state.lock()); subclass_input.send_event(Event::WindowEvent { diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index 14cc71445d..b7201a2753 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -8,7 +8,7 @@ use parking_lot::MutexGuard; use std::{io, ptr}; use winapi::{ shared::{ - minwindef::DWORD, + minwindef::{DWORD, WPARAM}, windef::{HWND, RECT}, }, um::winuser, @@ -30,6 +30,7 @@ pub struct WindowState { pub dpi_factor: f64, pub fullscreen: Option, + pub deferred_nclbuttondown_wparam: Option, window_flags: WindowFlags, } @@ -111,6 +112,7 @@ impl WindowState { fullscreen: None, window_flags: WindowFlags::empty(), + deferred_nclbuttondown_wparam: None, } }