Hide toolbar when selection is out of view#98152
Hide toolbar when selection is out of view#98152Renzo-Olivares merged 16 commits intoflutter:masterfrom
Conversation
| ); | ||
| renderObject.selectionStartInViewport.addListener(_updateHandleVisibilities); | ||
| renderObject.selectionEndInViewport.addListener(_updateHandleVisibilities); | ||
| renderObject.selectionStartInViewport.addListener(_updateToolbarVisibility); |
There was a problem hiding this comment.
I would merge _updateHandleVisibilities and _updateToolbarVisibility into one callback, and rename the function to be more generic.
| } | ||
|
|
||
| /// Final cleanup. | ||
| void dispose() { |
There was a problem hiding this comment.
I think i missed it in previous PR we should also dispose
_effectiveStartHandleVisibility
_effectiveEndHandleVisibility
as well as the new
_effectiveToolbarVisibility
in the dispose method
There was a problem hiding this comment.
they should be put after renderObject.selectionStartInViewport.removeListener
| @override | ||
| void didUpdateWidget(_SelectionToolbarOverlay oldWidget) { | ||
| super.didUpdateWidget(oldWidget); | ||
| oldWidget.visibility.removeListener(_toolbarVisibilityChanged); |
There was a problem hiding this comment.
we should check whether visibility change or not before remove/add listener, or simply assert they won't change if this widget would not be reused in the future
There was a problem hiding this comment.
True, it's possible that visibility is the same before and after right?
There was a problem hiding this comment.
For my understanding, why should we do this?
There was a problem hiding this comment.
This method will be called whenever _SelectionToolbarOverlay or its parents rebuilds regardless whether the visibility property changes or not.
There was a problem hiding this comment.
and based on the code, the visibility is passed in from the SelectionOverlay, and it should never change.
There was a problem hiding this comment.
Thank you that makes sense!
justinmc
left a comment
There was a problem hiding this comment.
Sounds like this is on hold until some refactoring is done in text_selection.dart? Otherwise it looks fine to me besides a few small comments below.
| } | ||
| } | ||
|
|
||
| /// This widget represents a text selection toolbar. |
There was a problem hiding this comment.
Nit: No triple slash on a private class? Or maybe it doesn't matter.
There was a problem hiding this comment.
According to https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#use--for-public-quality-private-documentation if we plan on making this public in the future it is fine (as long as the documentation is quality). I think this would still be flagged if we make it public because the class fields are not documented. I'm okay with either method.
There was a problem hiding this comment.
Ah I say keep it triple then. Thanks for looking that up, I never knew exactly what the policy was.
| @override | ||
| void didUpdateWidget(_SelectionToolbarOverlay oldWidget) { | ||
| super.didUpdateWidget(oldWidget); | ||
| oldWidget.visibility.removeListener(_toolbarVisibilityChanged); |
There was a problem hiding this comment.
True, it's possible that visibility is the same before and after right?
| } | ||
|
|
||
| /// This widget represents a text selection toolbar. | ||
| class _SelectionToolbarOverlay extends StatefulWidget { |
There was a problem hiding this comment.
Nit: Is there a better name for this class? Or maybe just describe what it does in the comment above in a bit more detail. I just wouldn't understand the difference between _SelectionToolbarOverlay and TextSelectionOverlay at a glance. And it doesn't seem to have to do with Overlay directly.
I really like the idea of splitting this out into its own class like this by the way, it seems cleaner to me.
There was a problem hiding this comment.
_SelectionToolbarPlacement? Something like that, naming is hard...
There was a problem hiding this comment.
For this I just followed the naming scheme of _SelectionHandleOverlay since this will be the widget created when we call
_toolbar = OverlayEntry(builder: _buildToolbar); , and _buildToolbar will return _SelectionToolbarOverlay.
to build the toolbar Overlay.
There was a problem hiding this comment.
Ah right, that makes sense 👍
| )); | ||
|
|
||
| final EditableTextState state = | ||
| tester.state<EditableTextState>(find.byType(EditableText)); |
There was a problem hiding this comment.
I think this should be indented.
|
|
||
| // On web, we don't show the Flutter toolbar and instead rely on the browser | ||
| // toolbar. Until we change that, this test should remain skipped. | ||
| }, skip: kIsWeb); // [intended] |
There was a problem hiding this comment.
Does this apply in the same way to desktop?
There was a problem hiding this comment.
| MacOS With Hiding | MacOS Without Hiding |
|---|---|
![]() |
![]() |
The native behavior (MacOS) at least in the notes app, is to disable scrolling entirely while the selection menu is present.
Edit: Tested on Windows 11 in the notepad app and scrolling is also disabled while the selection menu is present. Also tested on Ubuntu 21.10 on the gedit app and can observe the same behavior (scrolling is disabled).
I think for now the hiding behavior would be preferred so the selection menu does not obscure any text. And when the native behavior of disabling scroll is implemented then this won't be an issue as scroll will be disabled.
There was a problem hiding this comment.
Sounds good. I'm working on the "context menu anywhere" stuff and I think it will work out that scrolling will be disabled while the menu is up.
chunhtai
left a comment
There was a problem hiding this comment.
LGTM, just nit on the dispose order
| _effectiveToolbarVisibility.dispose(); | ||
| _effectiveStartHandleVisibility.dispose(); | ||
| _effectiveEndHandleVisibility.dispose(); | ||
| _selectionOverlay.dispose(); |
There was a problem hiding this comment.
It is more systematically correct to dispose _selectionOverlay first before cleaning up all the visibility objects since the selection overlay may be using the visibility objects. One patten I follow is the dispose order is usually the reverse of the initState order.
There was a problem hiding this comment.
That makes sense. Thank you for the explanation!
* Hide toolbar when selection is out of view * properly dispose of toolbar visibility listener * Add test * rename toolbarvisibility * Make visibility for toolbar nullable * Properly dispose of toolbar visibility listener * Merge visibility methods into one * properly dispose of start selection view listener * Add some docs * remove unnecessary null check * more docs * Update dispose order Co-authored-by: Renzo Olivares <[email protected]>


Description
This change makes it so that the text selection toolbar hides when the text selection has moved outside of the view.
Related Issues
Fixes #77889
Tests
Pre-launch Checklist
///).