Skip to content

Fix issues #22576: Text edit tootip arrow is misplaced - #31676

Closed
jiisd wants to merge 21 commits into
flutter:masterfrom
jiisd:fix/issues_22576
Closed

jiisd wants to merge 21 commits into
flutter:masterfrom
jiisd:fix/issues_22576

Conversation

@jiisd

@jiisd jiisd commented Apr 26, 2019 •

Copy link
Copy Markdown
Contributor

Fixed an issue described in this link
#11274
#22576

Description

Fix the problem that the triangle attached to the toolbar is always in the middle.
Now looks like iOS, if the toolbar squishes to the side, the arrow attempts to stay as close as possible to the selected text.

However, the problem of dividing the line has not yet found a reasonable solution to solve.
#24784

This PR was based on the #29809 PR.

iPhone X (Flutter, this branch):
fix_flutter_issues_22576_1

GIF example --> fix_flutter_issues_22576_3

jiisd added 14 commits March 23, 2019 02:28
* master: (209 commits)
  Allow mouse hover to only respond to some mouse events but not all. (flutter#30886)
  Fix issue 23527: Exception: RenderViewport exceeded its maximum number of layout cycles (flutter#30809)
  Keep hover annotation layers in sync with the mouse detector. (flutter#30829)
  Use identical instead of '==' in a constant expression. (flutter#30921)
  Add toggle for debugProfileWidgetBuilds (flutter#30867)
  Revert "Manual engine roll with disabled service authentication codes (flutter#30919)" (flutter#30930)
  Manual engine roll with disabled service authentication codes (flutter#30919)
  Baseline Aligned Row (flutter#30746)
  [Material] Fix showDialog crasher caused by old contexts (flutter#30754)
  Let `sliver.dart` `_createErrorWidget` work with other Widgets (flutter#30880)
  Add more dialog doc cross-reference (flutter#30887)
  Allow downloading of desktop embedding artifacts (flutter#30648)
  CupertinoDatePicker initialDateTime accounts for minuteInterval  (flutter#30862)
  add golden tests for CupertinoDatePicker (flutter#30828)
  Simplify toImage future handling (flutter#30876)
  Fixed Table flex column layout error flutter#30437 (flutter#30470)
  Fix iTunes Transporter quirk (flutter#30883)
  Bump Android build tools to 28.0.3 in Dockerfile (flutter#30832)
  Update the upload key which seems to have trouble for some reason (flutter#30871)
  Check for invalid elevations (flutter#30215)
  ...
@jiisd jiisd changed the title Fix/issues 22576: Text edit tootip arrow is misplaced https://github.com/flutter/flutter/issues/22576 Fix/issues #22576: Text edit tootip arrow is misplaced Apr 26, 2019
@jiisd jiisd changed the title Fix/issues #22576: Text edit tootip arrow is misplaced Fix issues #22576: Text edit tootip arrow is misplaced Apr 26, 2019
@HansMuller

Copy link
Copy Markdown
Contributor

CC @justinmc

@goderbauer goderbauer added p: cupertino_ui cupertino_ui package in flutter/packages p: material_ui material_ui package in flutter/packages framework flutter/packages/flutter repository. See also f: labels. labels Apr 29, 2019

@justinmc justinmc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

A few improvements can be made I think, but this is pretty close. I checked out your branch locally and tried this myself and it works great.


/// The direction of the triangle attached to the toolbar.
///
/// Defaults to showing the triangle downwards if sufficient space is available

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think that you shouldn't describe something like a default here where the enum is defined, because it's far from where the code that defines the default is actually located. This enum could be reused somewhere else with a different default, or the default behavior could change below and the author might forget to update this documentation.

@override
bool shouldRepaint(_TextSelectionToolbarNotchPainter oldPainter) => false;
bool shouldRepaint(_TextSelectionToolbarNotchPainter oldPainter) {
return triangleOffsetX != oldPainter.triangleOffsetX;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

triangleOffsetX is final, so it can never change, and this check shouldn't be necessary, right? Same for arrowDirection.


/// If the setState method is called immediately,
/// the toolbar display position is wrong.
_timer = Timer(const Duration(milliseconds: 50), () {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you explain this more? I worry about adding a timer like this.

@LongCatIsLooong LongCatIsLooong May 9, 2019 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This timer workaround looks like a hack to me.
Maybe the toolbar should use multi child layout, so you can have the size of the toolbar ready when you position the triangle?

}

class _TextSelectionToolbarState extends State<_TextSelectionToolbar> {
double _triangleOffsetX = double.nan;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could this be provided in the constructor instead of starting as nan and needing to call updateTriangleOffsetX?


void _updateTriangleOffsetX(double triangleOffsetX) {
if (_toolbarStateGlobalKey.currentState != null) {
_toolbarStateGlobalKey.currentState.updateTriangleOffsetX(triangleOffsetX);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This seems to me like it should just be passed into _TextSelectionToolbar like a normal parameter rather than manually updated like this. Maybe you were running into problems that caused you to use this pattern and a timer?

@jiisd jiisd May 2, 2019 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The exact position of the triangle requires the true width of the toolbar to be determined to prevent the triangle from appearing outside the toolbar.
fix_flutter_issues_22576_4

Within the _TextSelectionToolbar class, we only know the number and type of menus to display but don't know the true width of the toolbar. Only the childSize parameter in the Offset getPositionForChild(Size size, Size childSize) method in _TextSelectionToolbarLayout class is the final true width of the toolbar, so it is necessary in _TextSelectionToolbarLayout class to calculate the true position of the triangle and pass the data out.

As for why a Timer is needed to delay execution of the setState(() {}); method by 50 milliseconds? Immediate execution causes the toolbar to always appear in the upper left corner of the screen. : (
fix_flutter_issues_22576_5

Of course, the current implementation is not a very good solution, I hope we can discuss a better one. : )

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@jiisd I just dug into this and I think I agree with @LongCatIsLooong that MultiChildLayoutDelegate is the way to go. You should be able to position the triangle inside the delegate without having to pass around triangleOffsetX and without having to use a timer.

That will require a bit of refactoring existing code. Let me know if you don't mind taking that on, otherwise I can take over the PR and do it myself if you want.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Although this solution now achieves this effect, it is very difficult to understand, and the responsibilities of the class are not clear.;(

I think the MultiChildLayoutDelegate approach is better than the current one. Of course, it would be better if you had time to work on it together.🤝

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hi @jiisd sorry for the late response. That sounds good to me! I'm going to work on this and create a pull request based on your changes. I'll let you know when the PR is open.

Comment thread packages/flutter/lib/src/material/text_selection.dart Outdated
class _TextSelectionToolbarNotchPainter extends CustomPainter {
const _TextSelectionToolbarNotchPainter(
this.arrowDirection,
this.triangleOffsetX,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I see 3 names here for the same thing: notch, arrow, and triangle. It looks like we use multiple names in the existing code too though :( Maybe try to choose the most common one and stick with it if you think it will help.

});

testWidgets(
'Check the toolbar appears below the TextField when there is not enough space above the TextField to show it',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This seems like it's from your other PR (#29809) and it verifies the top/bottom position, but you should write a test that verifies the arrow position.

I think if you just cover the 3 cases it should be fine. Test that selecting in the middle of an input puts the arrow in the middle (as before), selecting the first word puts the arrow left of center, and selecting the last word puts the arrow right of center.

It might be hard to find the arrow in a test, but I think you can do it. Here is a snippet from another test that finds something by name and descendant, it should help here:

Finder findFadeUpwardsPageTransition() {
  return find.descendant(
    of: find.byType(MaterialApp),
    matching: find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_FadeUpwardsPageTransition'),
  );
}

@LongCatIsLooong

Copy link
Copy Markdown
Contributor

hi @jiisd could you rebase or merge the master branch now that #29809 has been merged?

@LongCatIsLooong

Copy link
Copy Markdown
Contributor

Closing this PR as the issue has been (mostly) addressed.

@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Aug 6, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

framework flutter/packages/flutter repository. See also f: labels. p: cupertino_ui cupertino_ui package in flutter/packages p: material_ui material_ui package in flutter/packages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants