-
Notifications
You must be signed in to change notification settings - Fork 29.2k
Cupertino text selection menu customization #73578
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
Cupertino text selection menu customization #73578
Conversation
Gold has detected about 3 untriaged digest(s) on patchset 19. |
@@ -0,0 +1,86 @@ | |||
import 'dart:math' as math; |
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.
Is widgets
the right place for this class? I could also package this as a widget or something.
@@ -117,66 +131,6 @@ class TextSelectionToolbar extends StatelessWidget { | |||
} | |||
} | |||
|
|||
// Positions the toolbar at the given anchor, ensuring that it remains on | |||
// screen. | |||
class _TextSelectionToolbarLayoutDelegate extends SingleChildLayoutDelegate { |
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 has been moved to its own file and made public to be shared here and with CupertinoTextSelectionToolbar. See packages/flutter/lib/src/widgets/text_selection_toolbar_layout_delegate.dart.
final double paddingTop = MediaQuery.of(context).padding.top | ||
final double paddingAbove = MediaQuery.of(context).padding.top | ||
+ _kToolbarScreenPadding; | ||
final double availableHeight = anchorAbove.dy - paddingTop; | ||
final double availableHeight = anchorAbove.dy - paddingAbove; | ||
final bool fitsAbove = _kToolbarHeight <= availableHeight; | ||
final Offset anchor = fitsAbove ? anchorAbove : anchorBelow; | ||
final Offset localAnchor = Offset( | ||
anchor.dx - _kToolbarScreenPadding, | ||
anchor.dy - paddingTop, | ||
); | ||
final Offset localAdjustment = Offset(_kToolbarScreenPadding, paddingAbove); |
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.
Trying to make things look the same as in the Cupertino version.
return _CupertinoTextSelectionToolbarWrapper( | ||
arrowTipX: arrowTipX, | ||
barTopY: localBarTopY + globalEditableRegion.top, | ||
return _CupertinoTextSelectionControlsToolbar( |
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 moved all of the deleted math above into this class here to be consistent with Material. I'd like to refactor this in the long term (see #73574), but for now the idea is to encapsulate the complexity and keep the public APIs nice and clean.
@@ -570,694 +313,5 @@ class _CupertinoTextSelectionControls extends TextSelectionControls { | |||
} | |||
} | |||
|
|||
// Renders the content of the selection menu and maintains the page state. |
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.
All of the deleted code here and below was moved to packages/flutter/lib/src/cupertino/text_selection_toolbar.dart and sometimes refactored.
_kToolbarScreenPadding, | ||
), | ||
child: CustomSingleChildLayout( | ||
delegate: TextSelectionToolbarLayoutDelegate( |
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 logic is now shared with Material via TextSelectionToolbarLayoutDelegate. I deleted the duplicate logic that did this for Cupertino.
Gold has detected about 3 untriaged digest(s) on patchset 21. |
// toolbar, with rounded corners and an arrow pointing at the anchor. | ||
// | ||
// The anchor should be in global coordinates. | ||
class _CupertinoTextSelectionToolbarShape extends SingleChildRenderObjectWidget { |
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.
Most of the rest of the code in this file was copied over from cupertino/text_selection.dart with a little refactoring. This class in particular was separated out from the positioning logic.
Gold has detected about 3 untriaged digest(s) on patchset 23. |
Gold has detected about 6 untriaged digest(s) on patchset 24. |
…ll when no children
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 but just to make sure I understand this correctly:
To add custom buttons one must create a new TextSelectionControls
whose buiildToolbar
returns a custom CupertinoTextSelectionToolbar
with custom children? Is there an easy way to just add a new selection menu item, in addition to whatever control buttons (cut/copy/paste etc.) the system provides?
} | ||
_anchor = value; | ||
markNeedsLayout(); | ||
markNeedsSemanticsUpdate(); |
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: I think we can get rid of both markNeedsSemanticsUpdate
s.
final Widget child; | ||
|
||
/// Called when this button is pressed. | ||
final VoidCallback? onPressed; |
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.
should we may this non-nullable? If a menu item is not available shouldn't we take it off from the menu?
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.
/// | ||
/// Pass the resulting widget into the [child] parameter when using a | ||
/// CupertinoTextSelectionToolbarButton in a CupertinoTextSelectionToolbar. | ||
static Text getText(String string, [bool enabled = true]) { |
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: getText sounds like we're retrieving something that has already been created. Maybe turn this into a named constructor.
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.
Thanks, that was a great suggestion. The code is a lot simpler and users can still easily create text buttons or other things like icon buttons.
@LongCatIsLooong That's right, it's still overly complicated to make simple changes to the text selection toolbar even with this PR (and same for Material). Here I've attempted to hit the major wins without making any breaking changes. Long term I'd like to also refactor TextSelectionControls, maybe even get rid of it altogether. I'm tracking this in #73574. |
It shows:
When using it... |
@RAHEYO I just tried my example of customizing the Cupertino menu on Flutter's master branch and it works for me: https://github.com/justinmc/flutter-text-selection-menu-examples/blob/master/lib/custom_cupertino_menu_page.dart Can you try that and let me know if it still doesn't work? |
It's now possible to customize the buttons and the appearance of our iOS text selection menu.
This follows my previous work to do the same thing for Material. The API is as identical as possible.
The API for creating custom menus is still not ideal, though, because I limited these two PRs to non-breaking changes for now. In the long term, I'd like to refactor TextSelectionControls (tracked in #73574).
I've created a separate example repo with full examples of how to create custom menus in the meantime.
New public classes
CupertinoTextSelectionToolbar
A fully-featured iOS-style toolbar, including overflow handling and positioning. Since you still must use TextSelectionControls to render this for text selection, there is a bit of extra work to do to get the numbers right (see #73574).
CupertinoTextSelectionToolbarButton
A simple wrapper around CupertinoButton to look exactly like the default iOS text selection toolbar button.
TextSelectionToolbarLayoutDelegate
Positions the toolbar at an anchor, while keeping the menu on the screen. This was abstracted from the Cupertino and Material versions of this and used by both.
Related issues
Closes #36985
Closes #31412
Closes #24407
Tests
Breaking change
Not a breaking change, just exposes new API.
Screen.Recording.2021-01-07.at.1.52.01.PM.mov