Skip to content

Conversation

@justinmc
Copy link
Contributor

@justinmc justinmc commented Jan 23, 2020

Description

The text selection menu overflows in some fairly common cases. This PR will fix it by handling it the same way that native does.

This PR is only for Android, and we'll need to do a similar approach for iOS.

Related Issues

#35826
Closes #50935

Tests

In material/text_selection_test.dart, I tested various overflow cases and the position of the menu above and below the anchor. I also noticed an edge case in showing the menu when a large block of text was selected, and I wrote a test for that too.

Breaking change

This is a breaking change, see the design doc.

@justinmc justinmc added a: text input Entering text in a text field or keyboard related problems framework flutter/packages/flutter repository. See also f: labels. work in progress; do not review labels Jan 23, 2020
@justinmc justinmc self-assigned this Jan 23, 2020
@fluttergithubbot fluttergithubbot added the f: material design flutter/packages/flutter/material repository. label Jan 23, 2020
@fluttergithubbot
Copy link
Contributor

It looks like this pull request may not have tests. Please make sure to add tests before merging. If you need an exemption to this rule, contact Hixie.

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

@fluttergithubbot
Copy link
Contributor

Golden file changes remain available for triage from new commit, Click here to view.

Changes reported for pull request #49391 at sha 5340d2128b82fa100165da8b26ff6e0773a51783

@justinmc justinmc force-pushed the selection-overflow branch from 5340d21 to bd7f9b4 Compare March 9, 2020 17:02
@fluttergithubbot
Copy link
Contributor

Golden file changes remain available for triage from new commit, Click here to view.

Changes reported for pull request #49391 at sha bd7f9b4

@justinmc
Copy link
Contributor Author

justinmc commented Mar 9, 2020

Design doc

Breaking change email:

Hello Flutter,

If your app doesn't involve selectable or editable text, you can stop reading now.

I'm modernizing a few things about the text selection menu, and if you use any visual diff tests with the text selection menu open, you may encounter failures. See the full design doc.

Summary of Changes
The text selection menu has a positioning bug when selecting multiline text. The position will be changed to match native.

The visual appearance of Android's menu will be updated slightly to match the latest versions of Android.

In the case that the menu overflows, such as on narrow devices or locales with long menu items, the menu will place overflowing items in an overflow menu in line with the native platform on iOS and Android.

Migration Guide
No code changes are needed. If you have affected visual diff tests, update the tests to recognize the new visuals.

Thanks! Please see the design doc for more information and to leave feedback.

Justin (@justinmc)

@fluttergithubbot
Copy link
Contributor

Golden file changes remain available for triage from new commit, Click here to view.

Changes reported for pull request #49391 at sha 2f0327b

Copy link
Member

@goderbauer goderbauer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM after i18n issue is resolved.

|| ((widget.handleSelectAll == null) != (oldWidget.handleSelectAll == null))) {
// Change _TextSelectionToolbarContainer's key when the menu changes in
// order to cause it to rebuild. This lets it recalculate its
// saved width for the new set of children.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... and also avoids animating any changes in size (or something like that)?

// saved width for the new set of children.
_containerKey = UniqueKey();
// If the menu items change, make sure the overflow menu is closed. This
// prevents an empty overflow menu and an incorrect saved width.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"the incorrect saved width" part is now handled by the key above. Maybe remove that here?

_overflowOpen = !_overflowOpen;
});
},
tooltip: _overflowOpen ? 'Back' : 'More',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These strings need to be internationalized.


// Save the width when the menu is closed. If the menu changes, this width
// is invalid, so it's important that this RenderBox be recreated in that
// case.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe mention that we achieve this by providing a new key to the corresponding widget.

}

class _ToolbarParentData extends ContainerBoxParentData<RenderBox> {
// Whether or not this child is painted.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: use /// instead of // (this is a doc-quality comment).

}
}

class _TextSelectionToolbarItemsRenderBox extends RenderBox with ContainerRenderObjectMixin<RenderBox, _ToolbarParentData>, RenderBoxContainerDefaultsMixin<RenderBox, _ToolbarParentData> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I missed it, but do you actually use anything from RenderBoxContainerDefaultsMixin?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope not anymore it looks like, good catch!

nextSize = Size(nextSize.width + navButton.size.width, nextSize.height);
}
} else {
navButtonParentData.shouldPaint = false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't you already do this above in line 435? Maybe change line 435 to just be:

if (renderObjectChild == firstChild) {
  // Deal with the navigation button later.
  return;
}

... and then figure out what to do with the nav button right here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do. I agree this way is misleading now that I look at it.


// The navigation button is placed after iterating all children, and there
// is no need to place children that won't be painted.
if (renderObjectChild == firstChild
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

compare this to navButton instead of firstChild to make the code slightly more readable?

handleCopy: canCopy(delegate) ? () => handleCopy(delegate) : null,
handlePaste: canPaste(delegate) ? () => handlePaste(delegate) : null,
handleSelectAll: canSelectAll(delegate) ? () => handleSelectAll(delegate) : null,
isAbove: fitsAbove,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is fitsAbove updated when the textfield is in a scrollable and I scroll while the toolbar is shown? Is that even possible? What does native do?

Copy link
Contributor Author

@justinmc justinmc Mar 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed a couple of problems while looking into this. I'm going to create two new issues and defer them until after this PR, if that's alright with you. I checked and the behavior was the same before this PR.

  1. ✅ Scrolling a multiline text field properly updates fitsAbove immediately. The position of the menu changes as well as the orientation of the back button.
  2. ❌ Scrolling the entire TextField as an item in a ListView does not update fitsAbove. In a ListView, the text selection menu doesn't update its orientation during scrolling. #52426
  3. ❌ On native Android, the menu fades out during scrolling and back in afterwards, but Flutter doesn't do this. Text selection menu should fade during scroll #52425

@goderbauer
Copy link
Member

Looks like cirrus is unhappy, though.

return FlatButton(
child: Text(label),
onPressed: () {
setState(() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a use case that we want to close the overflow when we select a button? If i remember correctly, the callback will remove the overlay entry anyway?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, the overlay entry appears to be closed in all current cases. Also maybe in the future when we've got more/custom selection menu buttons, users will want to handle that themselves. I'll remove this setState.

@skia-gold
Copy link

Gold has detected one or more untriaged digests on patchset 28.
Please triage them at https://flutter-gold.skia.org/search?issue=49391.

1 similar comment
@skia-gold
Copy link

Gold has detected one or more untriaged digests on patchset 28.
Please triage them at https://flutter-gold.skia.org/search?issue=49391.

@fluttergithubbot
Copy link
Contributor

Golden file changes remain available for triage from new commit, Click here to view.

Changes reported for pull request #49391 at sha 1f681e1b2359d2cbb6bfe4cecdbf544d958ceb5c

@justinmc justinmc force-pushed the selection-overflow branch from 1f681e1 to 9606e62 Compare March 11, 2020 16:53
@skia-gold
Copy link

Gold has detected one or more untriaged digests on patchset 28.
Please triage them at https://flutter-gold.skia.org/search?issue=49391.

@justinmc justinmc merged commit 4841a7e into flutter:master Mar 11, 2020
@justinmc justinmc deleted the selection-overflow branch March 11, 2020 20:36
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 1, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

a: text input Entering text in a text field or keyboard related problems c: API break Backwards-incompatible API changes f: material design flutter/packages/flutter/material repository. framework flutter/packages/flutter repository. See also f: labels. will affect goldens Changes to golden files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Incorrect Cut/Copy/Paste button positioning above text field

7 participants