From bbed41f01654c5a68e198a7703529b7fc2d91fe4 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Mon, 24 Aug 2020 11:10:57 -0700 Subject: [PATCH] Multi-click mouse events on Windows This is a partial fix for #859, addressing only the Windows platform. But in general it should represent how we want to handle this. --- CHANGELOG.md | 2 ++ druid-shell/src/platform/windows/window.rs | 35 ++++++++++++++++------ 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d933a1d8d0..52672529d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,7 @@ You can find its changes [documented below](#060---2020-06-01). - Allow submit_command from the layout method in Widgets ([#1119] by [@rjwittams]) - Allow derivation of lenses for generic types ([#1120]) by [@rjwittams]) - Switch widget: Toggle animation being window refresh rate dependent ([#1145] by [@ForLoveOfCats]) +- Multi-click on Windows, partial fix for #859 ([#1157] by [@raphlinus]) ### Visual @@ -399,6 +400,7 @@ Last release without a changelog :( [#1143]: https://github.com/linebender/druid/pull/1143 [#1145]: https://github.com/linebender/druid/pull/1145 [#1152]: https://github.com/linebender/druid/pull/1152 +[#1157]: https://github.com/linebender/druid/pull/1157 [Unreleased]: https://github.com/linebender/druid/compare/v0.6.0...master [0.6.0]: https://github.com/linebender/druid/compare/v0.5.0...v0.6.0 diff --git a/druid-shell/src/platform/windows/window.rs b/druid-shell/src/platform/windows/window.rs index 25ec77a33c..d450f47a7f 100644 --- a/druid-shell/src/platform/windows/window.rs +++ b/druid-shell/src/platform/windows/window.rs @@ -22,6 +22,7 @@ use std::mem; use std::ptr::{null, null_mut}; use std::rc::{Rc, Weak}; use std::sync::{Arc, Mutex}; +use std::time::{Duration, Instant}; use log::{debug, error, warn}; use winapi::ctypes::{c_int, c_void}; @@ -186,6 +187,8 @@ struct WndState { // Is this window the topmost window under the mouse cursor has_mouse_focus: bool, //TODO: track surrogate orphan + last_click_time: Instant, + click_count: u8, } /// State for DirectComposition. This is optional because it is only supported @@ -741,8 +744,10 @@ impl WndProc for MyWndProc { } Some(0) } - // TODO: not clear where double-click processing should happen. Currently disabled - // because CS_DBLCLKS is not set + // Note: we handle the double-click events out of caution here, but we don't expect + // to actually receive any, because we don't set CS_DBLCLKS on the window class style. + // And the reason for that is that we want click counts that go above 2, so it just + // makes a lot more sense to do the click count logic ourselves. WM_LBUTTONDBLCLK | WM_LBUTTONDOWN | WM_LBUTTONUP | WM_RBUTTONDBLCLK | WM_RBUTTONDOWN | WM_RBUTTONUP | WM_MBUTTONDBLCLK | WM_MBUTTONDOWN | WM_MBUTTONUP | WM_XBUTTONDBLCLK | WM_XBUTTONDOWN | WM_XBUTTONUP => { @@ -766,18 +771,28 @@ impl WndProc for MyWndProc { } { if let Ok(mut s) = self.state.try_borrow_mut() { let s = s.as_mut().unwrap(); - let count = match msg { - WM_LBUTTONDOWN | WM_MBUTTONDOWN | WM_RBUTTONDOWN | WM_XBUTTONDOWN => 1, - WM_LBUTTONDBLCLK | WM_MBUTTONDBLCLK | WM_RBUTTONDBLCLK - | WM_XBUTTONDBLCLK => 2, - WM_LBUTTONUP | WM_MBUTTONUP | WM_RBUTTONUP | WM_XBUTTONUP => 0, - _ => unreachable!(), - }; + let down = matches!(msg, WM_LBUTTONDOWN | WM_MBUTTONDOWN | WM_RBUTTONDOWN | WM_XBUTTONDOWN | + WM_LBUTTONDBLCLK | WM_MBUTTONDBLCLK | WM_RBUTTONDBLCLK + | WM_XBUTTONDBLCLK); let x = LOWORD(lparam as u32) as i16 as i32; let y = HIWORD(lparam as u32) as i16 as i32; let pos = Point::new(x as f64, y as f64).to_dp(self.scale()); let mods = s.keyboard_state.get_modifiers(); let buttons = get_buttons(wparam); + let dct = unsafe { GetDoubleClickTime() }; + let threshold = Duration::from_millis(dct as u64); + let count = if down { + // TODO: it may be more precise to use the timestamp from the event. + let this_click = Instant::now(); + if this_click - s.last_click_time >= threshold { + s.click_count = 0; + } + s.click_count = s.click_count.saturating_add(1); + s.last_click_time = this_click; + s.click_count + } else { + 0 + }; let event = MouseEvent { pos, buttons, @@ -991,6 +1006,8 @@ impl WindowBuilder { keyboard_state: KeyboardState::new(), captured_mouse_buttons: MouseButtons::new(), has_mouse_focus: false, + last_click_time: Instant::now(), + click_count: 0, }; win.wndproc.connect(&handle, state);