-
Notifications
You must be signed in to change notification settings - Fork 29.7k
[EditableText] call onSelectionChanged only when there are actual selection/cause changes
#87971
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
[EditableText] call onSelectionChanged only when there are actual selection/cause changes
#87971
Conversation
selection/cause changes Fixes flutter#76349. Also cleaned up the `updateEditingValue`/`userUpdateEditingValue` paths a bit. Hopefully we will be able to merge the two.
onSelectionChanged when there's actual selection/cause changesonSelectionChanged only when there're actual selection/cause changes
| bool _wasSelectingVerticallyWithKeyboard = false; | ||
|
|
||
| void _setTextEditingValue(TextEditingValue newValue, SelectionChangedCause cause) { | ||
| textSelectionDelegate.textEditingValue = newValue; |
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 kept this for backward compatibility, we should only remove it once we remove the deprecated setter textEditingValue
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 you elaborate? People are required to implement userUpdateTextEditingValue right? It's part of the TextSelectionDelegate interface.
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 didn't notice userUpdateTextEditingValue is abstract method. yeah this should be fine.
| ScrollController? _scrollController; | ||
|
|
||
| late AnimationController _cursorBlinkOpacityController; | ||
| late final AnimationController _cursorBlinkOpacityController = AnimationController(vsync: this, duration: _fadeDuration); |
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 think we missed this but it should be disposed in the dispose method.
same for _floatingCursorResetController. I think the existing format is cleaner, it is weird the we give a default value in here and then attach listener in initState
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.
Whoops good catch about the dispose.
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.
These should be fixed in #87973
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.
moved the addListener to the lazy initializer. My initial goal was to mark the property as final. I'll do the same for other animation controllers in #87973.
| ? _value.selection != value.selection | ||
| : _value != value; | ||
| if (shouldShowCaret) { | ||
| _scheduleShowCaretOnScreen(); |
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 didnt get the logic, the _scheduleShowCaretOnScreen will be called in line 1770 anyway?
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 I forgot to remove the existing _sheduleShowCaretOnScreen when copied the other one over.
| await gesture.moveBy(const Offset(600, 0)); | ||
| // To the edge of the screen basically. | ||
| await tester.pump(); | ||
| await tester.pumpAndSettle(); |
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.
where do we introduce frame delays?
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.
There's no need for these changes. Removed.
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 👍 . This was badly needed, thanks for cleaning this up.
| void onDoubleTapDown(TapDownDetails details) { | ||
| if (delegate.selectionEnabled) { | ||
| renderEditable.selectWord(cause: SelectionChangedCause.tap); | ||
| renderEditable.selectWord(cause: SelectionChangedCause.doubleTap); |
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.
Good catch
|
|
||
| await testTextEditing(tester, targetPlatform: defaultTargetPlatform); | ||
|
|
||
| debugKeyEventSimulatorTransitModeOverride = null; |
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 remove this line now that you have the teardown?
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.
Same with the next test below.
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 doesn't seem to work, _verifyInvariants is called before the the tear down callbacks are invoked.
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 ok interesting, best to keep both then 👍
| index++; | ||
| } | ||
|
|
||
| expect( |
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.
Slick 😎
| expect(selection, const TextSelection(baseOffset: 1, extentOffset: 2)); | ||
| expect(cause, SelectionChangedCause.keyboard); | ||
|
|
||
| // Selection and cause both changed |
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: period here.
| _handleSelectionChanged(TextSelection.collapsed(offset: _lastTextPosition!.offset), SelectionChangedCause.forcePress); | ||
| final TextEditingValue newValue = _value.copyWith( | ||
| selection: TextSelection.fromPosition(_lastTextPosition!), | ||
| ); |
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 new code preserves the TextAffinity of _lastTextPosition, but the old code didn't. Probably better this way but something to be aware of.
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 floating cursor stuff currently doesn't care much about affinity (e.g., final TextPosition currentTextPosition = TextPosition(offset: renderEditable.selection!.baseOffset);). I'm hoping we can take cursor handling out of EditableTextState and maybe clean floating cursor a bit in the process.
| 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); |
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.
Why was this assert removed?
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 must have thought getRectForComposingRange only returns null only when !composingRange.isValid || composingRange.isCollapsed is true but if getBoxesForSelection returns an empty list that method returns null too. It's triggered in one of the tests so I removed it.
| // This method is similar to updateEditingValue, but is used to handle user | ||
| // input caused by hardware keyboard events and gesture events, while | ||
| // updateEditingValue handles IME/software keyboard input. |
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.
Thank you for adding the comment here!
| _currentPromptRectRange = null; | ||
|
|
||
| if (_hasInputConnection) { | ||
| if (widget.obscureText && value.text.length == _value.text.length + 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.
I got really worried about extended grapheme clusters and obscured fields when I saw this + 1 here (even though I know it has nothing to do with this PR). Indeed they show up as multiple characters when entered into an obscured field and don't have a delay, but it turns out that's the behavior on native too 🤷 . All is well.
| ScrollController? _scrollController; | ||
|
|
||
| late AnimationController _cursorBlinkOpacityController; | ||
| late final AnimationController _cursorBlinkOpacityController = AnimationController(vsync: this, duration: _fadeDuration); |
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.
Whoops good catch about the dispose.
b04b362 to
d751b73
Compare
d751b73 to
f674e17
Compare
…lection/cause changes (flutter#87971)
…ctual selection/cause changes (flutter#87971)" (flutter#88183)
onSelectionChanged only when there're actual selection/cause changesonSelectionChanged only when there are actual selection/cause changes
Fixes #76349.
Also cleaned up the
updateEditingValue/userUpdateEditingValuepaths a bit. Hopefully we will be able to merge the two.
Replace this paragraph with a description of what this PR is changing or adding, and why. Consider including before/after screenshots.
List which issues are fixed by this PR. You must list at least one issue.
If you had to change anything in the flutter/tests repo, include a link to the migration guide as per the breaking change policy.
Pre-launch Checklist
///).If you need help, consider asking for advice on the #hackers-new channel on Discord.