-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Provide caret rect to text input plugin #77608
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
Provide caret rect to text input plugin #77608
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
selection can be null or !isValid, in either case there's no caret.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You may also want to make sure selection.isCollapsed is true, because the caret doesn't show up (in addition to the 2 aforementioned cases) if there's an actual non-empty selection.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like we are skipping reporting the caret rect to the engine when there's no caret. Does the engine need to know that the caret is gone (instead of using the out-of-date caret rect that was previously sent to it)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The purpose of caret rect is to position the IME candidate popup. The popup is displayed after long pressing a key (or possibly while typing with some languages). So if there is any selection it will be replaced by a character before the popup is displayed. I'm not aware of a way to get the IME popup visible without caret present. And should that ever happen, last caret position seems to me like a good enough fallback.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't the accent popup show up at the wrong location if you:
- change the selection to somewhere in the document that's far away from the current caret location (the framework won't send anything in this case because it's a non-collapsed selection).
- long press a key to trigger the popup
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Long pressing key first clears the selection replacing it with pressed character, moving caret to new position. I don't think this would cause any issues. The way this works is that the popup shows the moment character would normally start repeating (after a long key press).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The communication between the text input plugin and the framework is asynchronous. When the software keyboard clears the selection and changes the caret's position, the text input plugin needs to query the new caret coordinates from the framework. There seems to be a chance that the framework can't respond in time, resulting in the outdated caret coordinates being reported in the firstRect method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not entirely sure what do you mean by software keyboard? if you mean mean the selection popup, that is shown as response to previous keypress (that would clear the selection). The time between the initial key press and showing popup (when long pressing the key) is about a second, plenty of time for framework to notify plugin of cursor location.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah sorry forgot this is macOS & hardware keyboard, I meant the key event. Sounds good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: is this CGRectNull? Could you put a code comment that explains why this invalid rect is specifically chosen?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't trying to match CGRectNull (i think that's [inf, inf, 0, 0]), just to be consistent with setComposingRect.
|
Looks like @cbracken is about to land a patch that addresses something similar. Maybe we should wait for that patch to land? I'm not familiar with how accents work on macos. Does long-pressing a key initiate a composing region (that highlights the base character) on macos? |
Sure. I wasn't aware @cbracken was working on this when I opened the PR. Don't mean to step on anyone's toes :)
Not necessarily. The text input controller suppresses normal key-repeat behavior, showing selection popup on first key repeat instead. However it only enters compositing mode after you change the character selection within the popup. Which currently doesn't work in flutter due to way caret movement and arrow events are handled (but you can still pick up option directly by pressing a number, which works). In which case it generates text |
cbracken
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you provide a bit more context on why this is required? TextInput.setMarkedTextRect should provide the caret position when not composing.
flutter/packages/flutter/lib/src/widgets/editable_text.dart
Lines 2442 to 2463 in 48f5674
| // Sends the current composing rect to the iOS text input plugin via the text | |
| // input channel. We need to keep sending the information even if no text is | |
| // currently marked, as the information usually lags behind. The text input | |
| // plugin needs to estimate the composing rect based on the latest caret rect, | |
| // when the composing rect info didn't arrive in time. | |
| void _updateComposingRectIfNeeded() { | |
| final TextRange composingRange = _value.composing; | |
| if (_hasInputConnection) { | |
| assert(mounted); | |
| Rect? composingRect = renderEditable.getRectForComposingRange(composingRange); | |
| // Send the caret location instead if there's no marked text yet. | |
| if (composingRect == null) { | |
| assert(!composingRange.isValid || composingRange.isCollapsed); | |
| final int offset = composingRange.isValid ? composingRange.start : 0; | |
| composingRect = renderEditable.getLocalRectForCaret(TextPosition(offset: offset)); | |
| } | |
| assert(composingRect != null); | |
| _textInputConnection!.setComposingRect(composingRect); | |
| SchedulerBinding.instance! | |
| .addPostFrameCallback((Duration _) => _updateComposingRectIfNeeded()); | |
| } | |
| } |
I'm not aware of any cases where both an IME candidate menu and the accent pop-up are in use at the same time -- in local testing with Japanese and Korean text, I get direct input of the vowel in question (e.g. あ) and no accent menu.
Ah no worries at all! Thanks so much for sending these patches! I was working on landing Did you try with just the handlers for the above two messages, and if so, what was the result? As noted, despite its unfortunate name, the |
|
@cbracken, I did try |
Ah interesting; yep looking at this line, @LongCatIsLooong any concerns with the general approach? From my point of view this seems like a reasonable message to add, and I think this actually solves an additional issue with CJK candidate menu positioning. On some platforms, the candidate dropdown movies with the caret, on others it remains positioned at the bottom left corner of the composing rect. Currently on platforms that track the caret, we track the bottom-right corner of the composing rect as a proxy for that. An alternative would be to have the |
justinmc
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll defer to @LongCatIsLooong on this, but it seems to make sense to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes me worry about an infinite loop (well at least doing this unnecessarily once every frame). Are we sure that can never happen? Like for example if the selection is (-1,-1).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TextInputConnection checks if the rect is same, and so skips the message.
I'm not quite sure how infinite loop could happen, it is started in _openInputConnection and repeated every frame for as long as there is input connection. It is exactly the same behavior as _updateComposingRectIfNeeded, so if that doesn't cause infinite loops neither should this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, _updateComposingRectIfNeeded does seem to do the same thing so I think it's ok 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we test that the Rect that gets passed is the right one somewhere?
LongCatIsLooong
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM modulo comments from other reviewers
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: s/only used/used/ just in case we do start using it in another embedder at some point. IIRC we have similar wording about iOS somewhere that is now out-of-date.
Also super-pedantic nit, but add a period at the end.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've sent a fix for the other case in #77951.
|
(PR triage) @LongCatIsLooong @justinmc @knopp What's the status of this PR? |
e3a1965 to
a499cc9
Compare
|
I rebased the PR and added unit tests (as requested by @justinmc). Anything else needed here? |
justinmc
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 👍
The build is currently broken, but this can be merged when it's green.

#77545
Adds required code to provide current caret rectangle to text input plugin. This rect, with addition to current editable transform can be used to position the accent selection menu on macOS similarly to how IME popup is positioned on iOS.
Pre-launch Checklist
///).If you need help, consider asking for advice on the #hackers-new channel on Discord.