fyne-io / fyne

Cross platform GUI toolkit in Go inspired by Material Design
https://fyne.io/
Other
25.15k stars 1.4k forks source link

API to receive mouse back/forward button events (mouse buttons 4 and 5) #3812

Open dweymouth opened 1 year ago

dweymouth commented 1 year ago

Checklist

Is your feature request related to a problem?

There is currently no way to react to mouse button events other than the first three buttons and the scroll wheel.

Is it possible to construct a solution with the existing API?

No response

Describe the solution you'd like to see.

Introduce a new API for responding to additional mouse button events. New constants should be added to the desktop.MouseButton enum representing the back/forward mouse buttons (button numbers 4 and 5). It's unclear what the names should be. MouseButtonBack/Forward is inflexible, but continuing the existing naming scheme is also awkward (MouseButtonQuaternary, MouseButtonQuinary)?

Also unclear is where the events should be surfaced. A natural option would be through the existing desktop.Mouseable interface and the MouseEvent type, but a potential complication is that the mouse back/forward buttons are usually best responded to at the window level, and with this API any existing Mouseable CanvasObject would "swallow" the event, even if it can't meaningfully respond to it, preventing it from reaching the top-level window canvas. Perhaps exposing a new interface desktop.ExtendedMouseable or something like that so that existing Mouseable objects wouldn't receive them, and then adding new SetOnMouseDown, SetOnMouseUp functions to the desktop Canvas that receive any mouse events (from the normal or extended buttons) that were not sent to a CanvasObject.

andydotxyz commented 1 year ago

Is this a standard? My understanding was that buttons >3 were mapped to OS functions by the mouse driver.

This is a nice addition idea, but it's implementation/naming needs to match a naming / function scheme that works across all OS.

dweymouth commented 1 year ago

I think though I could be wrong that by default those buttons are unmapped and just issue mouseButton events that it's up to the application to handle (which is mapped to forward/back in web browsers and other apps with navigation). But the user can override them at the driver level to have it issue a key press event or other event type to remap the behavior.

I can try to dig a bit more into this later this week. I did see that GLFW defines constants for mouse buttons 1-8

andydotxyz commented 1 year ago

More info would be good - I could not find any consensus online. If we have to expose button numbers so be it, but a consistent intent api would be better.

dweymouth commented 9 months ago

In Win32, mouse buttons 4 and 5 are delivered to the application to respond to: https://learn.microsoft.com/en-us/windows/win32/learnwin32/mouse-clicks

Cocoa dispatches them with NSEventTypeOtherMouseDown and then has a buttonNumber property in the payload: https://stackoverflow.com/questions/69030392/how-to-differentiate-side-and-wheel-buttons-with-cocoa

Qt has APIs for responding to buttons >3 as well: https://stackoverflow.com/questions/30822134/qml-forwards-back-mouse-buttons-handling

So I think it's clear that these events, by default, are dispatched to applications, though many mouse drivers allow them to be remapped. So I think Fyne should have an API to receive these. I like the ExtendedMouseable idea to keep existing Mouseable widgets that don't respond to these from "swallowing" the event and preventing it to reach the window canvas, since as mentioned, by far the most common use case for these is forward/back navigation, which is usually at the top window level in an app. (e.g. doesn't matter what component you click mouse button 4 on, it always goes back)

andydotxyz commented 9 months ago

Yeah. It seems that the intent is not something we can rely on and may just have to fall back to numbering. Where they should land is a tougher one though, and probably impacts how we should implement them too. The "delivered to canvas" is more like a shortcut than a mouse action... and maybe that is what it is? But then again some applications (CAD etc) like to use mouse taps in the context of the widget to control items in the app (like zoom, pan etc?). I wonder if there is any sort of best practice we can pull on here, or perhaps we need the "ExtendedMouseable" to be available on a desktop.Window or desktop.Canvas as well as widgets?

dweymouth commented 8 months ago

Another idea is a new interface that widgets can implement to specify which mouse buttons they can receive, and default for widgets that don't implement it is they receive buttons 1, 2, and 3 only (as is currently the case). Then in the future, in 3.0 we could merge the two interfaces together so any Mouseable widget now has to say which buttons it's interested in

andydotxyz commented 8 months ago

I think that approach only works for 3.0 as in the 2.x if you implement the interface you handle the events - no event handling is optional once you opt in at the code level...

dweymouth commented 8 months ago

my thought was (naming all TBD):

2.x:

// a Mousable component that is not ExtendedMousable only receives buttons 1, 2, 3 // (as is all that is currently supported) Mousable interface { MouseDown(*evt) MouseUp(*evt) }

ExtendedMousable interface { InterestedButtons() []MouseButton } // register to be able to receive additional buttons

3.x:

Combine both functions in Mousable, and force components to be explicit about which mouse buttons they are interested in

andydotxyz commented 8 months ago

This doesn't feel very clean. If you are simply tappable but the thing you are on top of has a popup menu it would be undesirable for a right-click on the child to show the pop-up associated with the widget you are not currently interacting with.

It feels more like these "meta buttons" are special in some way - are they back/forward in the app or are they "apply a fourth action" on a widget. Looking at it that way I wonder if they are more like modifiers or keys than mouse buttons. Whichever way we go I don't think that the API design should break how mouse interactions currently work.

dweymouth commented 8 months ago

They are mostly back/forward within the app but I'm not sure you can make the assumption that they would always be tied to that functionality (games, etc).

But I have no rush for getting this out in a release I guess since it would be easy for me to add temporary support for Supersonic only in a fork.

pjanx commented 6 days ago

Many mice are rather reprogrammable and can send keyboard events. It is safe to assume that buttons 4 and 5 are for history use, but you can theoretically also have more buttons than that (reported as mouse buttons in terms of USB HID).