Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Global shortcuts don’t work when a shortcutable widget (like Entry) is focused #2627

Open
toaster opened this issue Nov 7, 2021 · 13 comments
Assignees
Labels
bug Something isn't working

Comments

@toaster
Copy link
Member

toaster commented Nov 7, 2021

Describe the bug:

Currently, we have two kind of shortcut handlers: the global one (canvas) and local ones (shortcutable widgets like Entry). When the driver handles a shortcut it sends it to the focused widget if this is a Shortcutable and to the canvas if not. If the shortcut is not handled by the widget, nothing happens. Thus, global shortcuts don’t work when a shortcutable widget is focused.

With the upcoming shortcut support for menus (which only display the shortcuts which still have to be registered with the canvas or handled by a widget) this behaviour gets inconsistent because on MacOS, the OS will trigger the action regardless if a shortcutable widget is focused or not. For non-native menus the unexpected behaviour stays the same.

@toaster toaster added the unverified A bug that has been reported but not verified label Nov 7, 2021
@toaster toaster self-assigned this Nov 7, 2021
@thenick775
Copy link

thenick775 commented Jan 8, 2022

I've worked around something similar to this by attaching shortcut handlers to my local entry widgets as well as having desktop handlers, just in case anyone sees this and needs an immediate solution!

func setupDesktopShortcuts(w fyne.Window) { //for desktop
	ctrlFind := desktop.CustomShortcut{KeyName: fyne.KeyF, Modifier: desktop.SuperModifier}
	w.Canvas().AddShortcut(&ctrlFind, superFind)
	//...
        //...
}


func (m *inquiryEntry) TypedShortcut(s fyne.Shortcut) { //for local
	if _, ok := s.(*desktop.CustomShortcut); !ok {
		m.Entry.TypedShortcut(s)
		return
	} else if ok {
		t := s.(*desktop.CustomShortcut)
		fmt.Println("shortcut name:", s.ShortcutName(), s.(*desktop.CustomShortcut).KeyName, s.(*desktop.CustomShortcut).Modifier)
		if t.Modifier == desktop.SuperModifier {
			switch t.KeyName {
			case fyne.KeyG:
				//your local shortcuts
			case fyne.KeyE:
				 //...
			case fyne.KeyI:
		                //...
			}
		}
	}
}

@Jacalz Jacalz added bug Something isn't working and removed unverified A bug that has been reported but not verified labels Aug 20, 2023
@ftl
Copy link

ftl commented Dec 24, 2024

Is there a reason, why there is no mechanism to cascade event handling in fyne? Or is it just "the simplest thing that could possibly work" and we are now uncovering some limitations of this solution?

@andydotxyz
Copy link
Member

Cascading events is confusing and leads to a much harder programming model.
However the global shortcuts being in the menu and us using them despite focus (to be done still) is simple to program and benefits from better UX as the features are discoverable.

This is how the apps work in macOS already as that's system-wide.

@ftl
Copy link

ftl commented Dec 25, 2024

Ok, got it, thank you for making this clear. Do you have already a solution in mind? As far as I understood, the Shortcutable somehow must indicate if it already handled a shortcut so that the triggersShortcut can decide if it should send it to Canvas or not.

Is this the right place to implement this, or am I missing something completely?

@andydotxyz
Copy link
Member

I don't think we have to do it that way - thankfully, because it would probably be a breaking change in the API.
What we can do is work through the menu registered shortcuts and if none match then fire it on the current focus widget or canvas as before.

@dweymouth
Copy link
Contributor

Doesn't that mean that it's a behavior change if a shortcutable widget, say Entry, has a shortcut and the menu also has the same shortcut bound to a different action? In that case, currently it would go to the widget, which is desirable, but with this proposal the menu would get precedence?

@andydotxyz
Copy link
Member

It would be a change, yes - but would it be bad?

Should a widget and a menu really have the same shortcut but different functionality attached?

@dweymouth
Copy link
Contributor

Should a widget and a menu really have the same shortcut but different functionality attached?

Probably not, but we can't control what shortcuts app developers assign. The thing that concerns me is that this change wouldn't follow the "principle of least surprise". If a Shortcutable widget is focused, you'd expect it to receive shortcuts, similarly to how a focused widget receives key events even if the canvas has a key handler assigned that could receive them. If making this change, we need to document it on the Shortcutable interface I think that shortcuts registered in the app main menu will not be sent to Shortcutable widgets and will always go to the menu handler. Some app code may have to change to accommodate this.

@dweymouth
Copy link
Contributor

Also, would the scope of this be only menu shortcuts or also window canvas shortcuts registered with mainWindow.Canvas().AddShortcut(...)?

@andydotxyz
Copy link
Member

Menu specifically - so it's menu -> widget -> canvas fallback.

This is how macOS already works, see linked #5355 noting that we don't handle it right on all platforms currently.

If a Shortcutable widget is focused, you'd expect it to receive shortcuts, similarly to how a focused widget receives key events even if the canvas has a key handler assigned that could receive them

This isn't strictly true if you step back a level - there are keys that the OS reserves which will never be sent to your app. This is less peculiar because the developer has set up the shortcut so they at least will get their code executing when the shortcut is typed.

Yes it should be documented, and should not be part of a bugfix release, but it feels like a "better state" than what we have at the moment. And is more consistent in the long-run / cross-platform ambition.

@dweymouth
Copy link
Contributor

dweymouth commented Jan 3, 2025

OK, good that it's menu-specific since we have to be sure to not apply this solution to the canvas case as there are cases where a shortcut is valid at multiple scopes. And I have such a case in my app (though I do not use a main menu and register them on the window canvas instead):

  • Ctrl+A within an Entry selects all text as expected
  • Ctrl+A with nothing focused selects all rows of the currently visible tracklist view

@dweymouth
Copy link
Contributor

This ticket should also probably remain open under the 3.0 milestone, since the original ask here was for canvas shortcuts, not menu shortcuts, and we cannot solve this for the canvas case without a breaking change to the Shortcutable API.

@dweymouth
Copy link
Contributor

dweymouth commented Jan 3, 2025

Hmm, I guess a technically not breaking but not super-clean approach to solving the general Canvas case would be to introduce a new static API call that shortcutable widgets can invoke in their handler to "mark" the current shortcut as not handled, allowing the driver to propagate it to the next receiver. All the standard widgets could be updated to call this function for the non-applicable shortcuts they receive.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

6 participants