Open xorgy opened 2 months ago
Not to say you said that, but to clarify: I think winit's interpretation of higher-order clicks should not artificially be limited to support at most triple-clicks. It should be unbound.
Note that, on Windows, you should now use GetSystemMetricsForDpi()
instead of GetSystemMetrics()
. For me, GetSystemMetricsForDpi()
on Windows 10 always returns 4
for SM_CXDOUBLECLK
and SM_CYDOUBLECLK
for the DPI values 1
to 0x7fff_ffff
. But Microsoft could, of couse, change that any time.
Also, Windows treats the down-action as the double-click event, not the up-action. When the window class style CS_DBLCLKS
is set, the messages sent are WM_LBUTTONDOWN
, WM_LBUTTONUP
, WM_LBUTTONDBLCLK
, WM_LBUTTONUP
(proof). CS_DBLCLKS
shouldn't be set, if you're interpreting double- and higher-order clicks yourself, or you'd alternatively need to treat WM_LBUTTONDBLCLK
the same as WM_LBUTTONUP
(same for other buttons). (I don't know if winit wants to be resilient against unexpected style changes from other code via SetClassLongPtr()
, in which case it could handle WM_LBUTTONDBLCLK
[and all others ending in ...DBLCLK
], even though it doesn't set CS_DBLCLKS
.)
Notably, however, browsers send the dblclick
DOM event on mouse-up, not on mouse-down (tested on Windows 10 with Firefox and Brave, which is Chromium, using this page).
I think, ideally, winit would adhere to the more snappy behavior of sending higher-order click events on mouse-down; at least on Windows where the OS sets this precedent.
The article "Implementing higher-order clicks" from Raymond Chen contains some valuable information, like on how SM_CXDOUBLECLK
and SM_CYDOUBLECLK
are to be interpreted.
GPT-4o thinks slow drifting during the performance of a higher-order click is allowed. According to it, the spacial constraints would only apply between two consecutive clicks, and not from the first click in the series to the one currently judged.
Custom-drawing UI frameworks like Slint would need to reasonably assess whether a higher-order click (which starts with double-clicks) happened on a certain widget. In the old days, the widgets were different child windows with their own class styles and window procedures, which meant that double-clicks where one click happened outside and the next inside the widget, weren't interpreted as double-clicks onto the widget.
But maybe a reset API would be appropriate, so coordinates would be checked one by one by the UI framework to identify the widget that was hit, and the framework would be responsible to reset winit's click count when the latest higher-order click event's widget isn't the same as the previous click event's widget. In this case, the UI framework would tell winit to reset the click count and treat the click event as a regular click.
No event should be added for that, it should be just a property of the existing button event telling how deep you're in the click sequence(or whatever you call it).
Users can figure out themselves how they want to handle double clicks, etc, we just should provide with that information, nothing more, at least for now.
I wasn't saying another event should be added. winit just needs to provide enough information, so higher-order clicks can behave in the best way possible, which is the case when the UI framework at the end of the line can safely attribute the event to a certain widget. I'm tending towards the approach outlined in my last paragraph.
It may only be a little awkward regarding the data that events bring with them, if on some platforms, higher-order clicks happen on mouse-down, and on other platforms on mouse-up. Having the same data (like click count) in both events, and having to handle them in both wouldn't be that great. But possible, if necessary, maybe as num_clicks: Option<NonZeroUsize>
, where the Option
would convey whether the event type (down/up) is the one on the platform that is associated with higher-order clicks as well as whether a higher-order click (double-clicks and up) happened. Then, the UI framework must converge mouse-down and mouse-up to a higher-order handler itself.
It's not that awkward. There is only one major environment where click count is directly provided on pointer related events (macOS). We should match the cases where it is available on macOS events, using a combination of platform settings (on Windows) and sensible defaults (on X11, Wayland, Android, etc.). It will most likely be Option<NonZero>
anyway (though usize
is gratuitous).
It is still possible for toolkits and applications to implement their own click counting, though generally it wouldn't be any better than this. Double click behavior is rarely based on widget bounds, it is usually based on a fixed maximum distance per click from the previous click; but if somebody wanted to implement that nobody is going to stop them.
The alternative here is that we may just provide a data to detect double clicks and let the application developer decide how they want to process all of that. Like e.g. define a region for double click and interval, but let the actual count be on the user.
Though, given how small it is I don't think it hurt to do in winit based on macOS as suggested.
The attraction to having it in the event from Winit for Masonry specifically is that it covers everything in our internal PointerEvent
once we have click counts. The other side is that it may not be possible to implement it correctly on macOS without taking the click count from the NSEvent.
Click counting for double and triple clicks is a common need, and on some platforms is hard to address if they aren't determined in winit. In particular the Mac doesn't have a readily accessible API for getting the double click interval or detection rectangle separate from an individual event.
Some notes on implementation:
NSEvent
hasclickCount
; easy. (there is also adoubleClickInterval
attached to theNSEvent
)GetDoubleClickTime
) and a standard double click rectangle (SM_CXDOUBLECLICK
/SM_CYDOUBLECLICK
accessible withGetSystemMetrics
), which is widely interpreted as extending to multiple clicks (e.g. in the WPF implementation and some browsers).MotionEvent
), which could be an issue for the minority of people who use mice with Android apps (on ChromeOS and DeX); could again just go with some well-known values (like Chromium and others do, when they need to synthesize a click count on a platform that doesn't have readily accessible settings)detail
property of mousedown/mouseup events, and browsers generally expose the best available platform behavior.Overall, I think this would be not terribly hard to implement in a useful way; and even on platforms where there are shortcomings, it's better than not having it at all.
Blocked on #3833.