Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Add desktop shell support for text navigation key combinations #30725

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

Open
stuartmorgan-g opened this issue Apr 8, 2019 · 35 comments
Open

Add desktop shell support for text navigation key combinations #30725

stuartmorgan-g opened this issue Apr 8, 2019 · 35 comments
Labels
a: desktop Running on desktop c: new feature Nothing broken; request for a new capability c: proposal A detailed proposal for a change to Flutter e: glfw GLFW desktop embedding engine flutter/engine repository. See also e: labels. P2 Important issues not at the top of the work list platform-linux Building on or for Linux specifically platform-mac Building on or for macOS specifically platform-windows Building on or for Windows specifically team-text-input Owned by Text Input team triaged-text-input Triaged by Text Input team

Comments

@stuartmorgan-g
Copy link
Contributor

On macOS, typing cmd+right invokes moveToRightEndOfLine: These methods are already received by doCommandBySelector: so we we just need to implement the logic.

On glfw we could use modifier flags https://www.glfw.org/docs/latest/group__mods.html#ga14994d3196c290aaa347248e51740274

@stuartmorgan-g stuartmorgan-g added platform-mac Building on or for macOS specifically platform-windows Building on or for Windows specifically platform-linux Building on or for Linux specifically e: desktop labels Apr 8, 2019
@stuartmorgan-g stuartmorgan-g added the engine flutter/engine repository. See also e: labels. label Apr 8, 2019
@stuartmorgan-g
Copy link
Contributor Author

To clarify, the original description here is one such shortcut, but there are many that we should support across the desktop platforms. Other examples include things like forward/back word, delete word, etc.

@krisgiesing
Copy link
Contributor

krisgiesing commented May 6, 2019

Here's a list I compiled for NSResponder:

(void)moveBackwardAndModifySelection nullable id)sender;
(void)moveForwardAndModifySelection nullable id)sender;
(void)moveWordForwardAndModifySelection nullable id)sender;
(void)moveWordBackwardAndModifySelection nullable id)sender;
(void)moveUpAndModifySelection nullable id)sender;
(void)moveDownAndModifySelection nullable id)sender;
(void)moveToBeginningOfLineAndModifySelection nullable id)sender;
(void)moveToEndOfLineAndModifySelection nullable id)sender;
(void)moveToBeginningOfParagraphAndModifySelection nullable id)sender;
(void)moveToEndOfParagraphAndModifySelection nullable id)sender;
(void)moveToEndOfDocumentAndModifySelection nullable id)sender;
(void)moveToBeginningOfDocumentAndModifySelection nullable id)sender;
(void)pageDownAndModifySelection nullable id)sender;
(void)pageUpAndModifySelection nullable id)sender;
(void)moveParagraphForwardAndModifySelection nullable id)sender;
(void)moveParagraphBackwardAndModifySelection nullable id)sender;
(void)moveRightAndModifySelection nullable id)sender;
(void)moveLeftAndModifySelection nullable id)sender;
(void)moveWordRightAndModifySelection nullable id)sender;
(void)moveWordLeftAndModifySelection nullable id)sender;
(void)moveToLeftEndOfLineAndModifySelection nullable id)sender NS_AVAILABLE_MAC(10_6);
(void)moveToRightEndOfLineAndModifySelection nullable id)sender NS_AVAILABLE_MAC(10_6);

@stuartmorgan-g
Copy link
Contributor Author

See #34949 for some more specific cases that should be supported.

@stuartmorgan-g stuartmorgan-g added a: desktop Running on desktop and removed e: desktop labels Jul 26, 2019
@gazialankus
Copy link
Contributor

Paste shortcuts do not work either.

@stuartmorgan-g stuartmorgan-g added the e: glfw GLFW desktop embedding label Aug 8, 2019
@stuartmorgan-g stuartmorgan-g added this to the Goals milestone Aug 13, 2019
@gspencergoog
Copy link
Contributor

I'm curious: why do these key bindings need to be added on the engine side? Why can't we handle these keys on the framework side and just have it tell the shell how to modify the selection, etc.?

@stuartmorgan-g
Copy link
Contributor Author

stuartmorgan-g commented Nov 4, 2019

Well, on macOS at least there's
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/EventOverview/TextDefaultsBindings/TextDefaultsBindings.html
("To customize bindings, you create a file named DefaultKeyBinding.dict in ~/Library/KeyBindings/ and specify bindings to augment or replace the standard bindings.")

So we either need to create logic in the engine that understands all the customization options that each OS understands and then feed those to the framework at launch (and ideally watch for changes), or we need to let the OS handle that abstraction layer for us. The latter should be easier and more robust.

(Or we decide that we will not support any OS-level customization of keyboard handling, but that will permanently put us at a disadvantage relative to a standard Cocoa application.)

@gspencergoog
Copy link
Contributor

But don't we still have the problem then that we need to know what all of the bound operations and their key combinations for each OS are and support those on the framework side then?

Hypothetically, in this scenario, I should be thinking of building a MoveWordForwardAndModifySelectionAction in the framework that can then be invoked by a system channel message from the platform, right?

What happens if an app wants to change the binding of "command-rightArrow" on macOS and "control-rightArrow" on Linux to execute an action of their choice? Do they have to instead override and replace the MoveWordForwardAndModifySelectionAction, and ignore the "command-rightArrow" key event on macOS, but only if it matches the key binding on the platform side, and bind the key themselves if it isn't bound on the platform side?

Is there a macOS API to get the keys mapped in the bindings? Maybe we can make it more automatic by checking the list of system bindings, and doing the right thing in the shortcut manager?

@stuartmorgan-g
Copy link
Contributor Author

I'm not sure I follow. Cursor and selection mutation are currently handled in the engine layer, so I'm not sure why this would need custom actions in the framework. This bug is just about the fact that we currently don't actually perform the relevant mutations for many NSResponder methods.

@gspencergoog
Copy link
Contributor

gspencergoog commented Nov 5, 2019

We do the cursor and selection mutation in the engine? What about this code:
https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/rendering/editable.dart#L441

Isn't that doing the cursor and selection mutation? I mean, I just rewrote that code, but I was just converting it to use LogicalKeyboardKey instead of Android key events, I didn't really change the functionality.

@stuartmorgan-g
Copy link
Contributor Author

I'm not sure where that fits in. https://github.com/flutter/engine/blob/master/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm was necessary for text input to work (similar code exists in the other desktop platforms) and it's managing cursor and selection.

@gspencergoog
Copy link
Contributor

OK, I spoke with Jason, and he told me how these things relate. I think the quote was something like "basically, they both try and mutate the text, and then fling updates at each other to stay in sync." It seems like the two systems are supposed to cooperatively and asynchronously edit the same text, which makes it hard to decide who gets to set the key bindings, since the engine might have some set of bindings that mutate the text (that it maybe absorbs and doesn't send to the framework), and the framework could be looking for a different binding that might also mutate the text, but doesn't match the platform bindings.

@gspencergoog
Copy link
Contributor

And, for instance, who gets to do the "cut/copy/paste" mutations? I guess I'll take a look at the Android code in the engine and see what it does in those cases.

@krisgiesing
Copy link
Contributor

We want content modifications to be visible enough to the Dart layer to support undo.

@franciscojma86 franciscojma86 removed their assignment Apr 3, 2020
@kf6gpe kf6gpe added the P2 Important issues not at the top of the work list label May 29, 2020
@Hixie Hixie removed this from the None. milestone Aug 17, 2020
@justinmc
Copy link
Contributor

This is great analysis, thanks for digging up all of this. I think solving this is probably the right way to solve all of these Mac composing issues: #78061, #91859, #85328

Your comment was written in terms of Mac but will this be the approach for all other platforms too? So does solution no. 1 include moving all of the framework keyboard shortcut handling with Shortcuts and moving it to these new Intent-ish messages for all platforms?

Also does your solution no. 1 always call [NSTextInputContext handleEvent:]? Does it not call flutter/keyevent if it detects a keyboard shortcut with NSKeybindingManager?

I agree that 1 is not ideal but it should be straightforward to transition from 1 to 2 or 3 if we decide to do that.

@LongCatIsLooong
Copy link
Contributor

will this be the approach for all other platforms too

I think macOS is unique regarding text editing shortcuts handling in the sense that the -[NSTextInputContext handleEvent:] method always (as far as I can tell) claim the incoming event for itself, should we decide to route a key event to that method. This wouldn't have been a problem if the said method correctly returns false if it does not handle a key event.

Also does your solution no. 1 always call [NSTextInputContext handleEvent:]?

Yes and if a text field is the current responder no key events will be sent to the framework. This is actually kinda similar to how soft keyboards work on other platforms. I was thinking maybe mac catalyst does not have the this problem.

@justinmc
Copy link
Contributor

Got it. So we wouldn't have to remove the whole Shortcuts setup for text editing keyboard shortcuts, but it would be a strange experience that it would stop working for Mac while continuing to work for all other platforms.

It looks like this same problem does exist on iOS as well in the case that you connect a physical keyboard to a physical device. I was suspicious that it would because the framework handles things like the delete key similarly on iOS (code) and the order of primary and secondary key responders seems the same in the iOS engine (code), and indeed after testing it I get the same behavior that the IME isn't updated by physical keyboard keys. This doesn't happen with the simulator and a physical keyboard, I'm guessing because it's treated as a soft keyboard.

It's probably not as high priority as fixing this for Mac, but I imagine there are many people using physical keyboards on iPad.

@justinmc
Copy link
Contributor

@gspencergoog @stuartmorgan I'm curious what you guys think about the approaches in #30725 (comment).

@justinmc
Copy link
Contributor

justinmc commented Oct 21, 2021

When #80899 is merged, web will also inherit these problems (at least on Mac/iOS).

Update: That PR was closed, but still something to keep in mind if that's the plan for web long term.

@LongCatIsLooong
Copy link
Contributor

LongCatIsLooong commented Oct 28, 2021

Webkit seems to simply call private APIs for sending key events to the IME: https://github.com/WebKit/WebKit/blob/56c4388c36c4752b25d959dc881d89183f93aee4/Source/WebKit/UIProcess/Cocoa/WebViewImpl.mm#L5072-L5087

Chromium does something more complex which is similar to the approach 2 described in the previous comment, but it requires some intricate bookkeeping and edge case handling: https://source.chromium.org/chromium/chromium/src/+/main:content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm;l=904-1135?q=_handlingKeyDown&ss=chromium%2Fchromium%2Fsrc (it seems doCommandBySelector: is invoked synchronously by interpretKeyEvents:. Since the application has to communicate with the IME via IPC I thought it had to be asynchronous?).
Also they had to implement a method that seems to be private to override key interface control (https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/EventOverview/EventArchitecture/EventArchitecture.html#//apple_ref/doc/uid/10000060i-CH3-SW10) so keybindings like C-tab and C-← can be properly handled by the browser.

I will try to verify that shortcuts like C-xk work with this approach since overall it seems better.

@byw
Copy link

byw commented Nov 12, 2021

In

static const Map<ShortcutActivator, Intent> _macShortcuts = <ShortcutActivator, Intent>{

I noticed these (Emacs-derived?) shortcuts are not added for MacOS:

  • ctrl-a (to start of line)
  • ctrl-e (to end of line)
  • ctrl-f (forward one character)
  • ctrl-b (backward one character)

Any specific reasons for this?

Not sure if others use these, but for me, it feels kinda jarring when they are not there.

@LongCatIsLooong
Copy link
Contributor

@byw _macShortcuts will soon be removed and the macOS system shortcuts will be honored that will hopefully enable emacs shortcuts for text editing on macOS.

@cbracken
Copy link
Member

To be clear, the current state of this issue is that the default {cmd,option}[-shift]-arrow keyboard shortcuts do work in Flutter text fields, however, the support for personalised keyboard bindings (and the ctrl-a/ctrl-e etc. emacs-like bindings) are not yet wired up.

Bumping this to the post-stable project for tracking there.

@flutter-triage-bot
Copy link

The triaged-desktop label is irrelevant if there is no team-desktop label or fyi-desktop label.

@LouiseHsu LouiseHsu added the triaged-macos Triaged by the macOS platform team label Jun 18, 2024
@flutter-triage-bot flutter-triage-bot bot removed fyi-macos For the attention of macOS platform team triaged-macos Triaged by the macOS platform team labels Jun 18, 2024
@justinmc justinmc added the triaged-text-input Triaged by Text Input team label Jun 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a: desktop Running on desktop c: new feature Nothing broken; request for a new capability c: proposal A detailed proposal for a change to Flutter e: glfw GLFW desktop embedding engine flutter/engine repository. See also e: labels. P2 Important issues not at the top of the work list platform-linux Building on or for Linux specifically platform-mac Building on or for macOS specifically platform-windows Building on or for Windows specifically team-text-input Owned by Text Input team triaged-text-input Triaged by Text Input team
Projects
None yet
Development

Successfully merging a pull request may close this issue.