-
Notifications
You must be signed in to change notification settings - Fork 29.7k
CupertinoContextMenu improvement #131030
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
CupertinoContextMenu improvement #131030
Conversation
|
I'm not sure ask who to review my code. Can you please give me some help? @MitchellGoodwin |
|
@xhzq233 it looks like the failing check has to do with a plugin that this PR should not be interacting with. Would you mind rebasing to the current master branch to see if that fixes the check? |
Merged. Now it has become a Windows build check error.= = |
Looks like there was some other issues. Another rebase to current master might work. |
| await gesture.down(tester.getCenter(find.byKey(const Key('container')))); | ||
| await tester.pump(); | ||
| // simulate a press with specified duration | ||
| for (int i = 0; i < 100; i++) { |
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.
Did you try await tester.pump(const Duration(milliseconds: pressDuration)); first? Is this for loop more consistent?
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.
Yes, I tried it first. But _openAnimation won't reach midPoint since a pump only triggers one frame.
I need more frames to drive _openAnimation to reach the midPoint, so the route can be opened.
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 my main concern is that this loop just looks a little forced. Would pumpFrames work for this case?
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.
pumpFrames use the same methodology as me, and it will pump another widget. So I think it probably isn't necessary to use pumpFrames.
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.
Does it really matter that 100 frames need to be pumped, not just 1 frame over the same duration? I wouldn't think that would affect the gesture.
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.
It aims to simulate the actual situation: the user keeps pressing and requesting frames. If there is only one frame, the animation is mutant and cannot drive the value of the animation controller.
Maybe the value of 100 is unreasonable. This is what I came up with casually.
| await tester.pumpAndSettle(); | ||
|
|
||
| // judge whether _ContextMenuRouteStatic present on the screen | ||
| final Finder static = find.byWidgetPredicate( |
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.
static is a protected variable in Dart. Could you change the name of this to something else?
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.
Done 👍🏻
MitchellGoodwin
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. Thank you for answering my questions and putting this together.
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 with nits 👍
| }); | ||
| }); | ||
|
|
||
| testWidgets('Is gesture conflicts of ctx_menu resolved', |
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: What is ctx_menu? Maybe change this to: testWidgets('Conflicting gesture detectors',
| int? onPointerDownTime; | ||
| int? onPointerUpTime; | ||
| bool insideTapTriggered = false; | ||
| // 500ms < duration < 900ms |
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: Maybe explain where these values come from here.
| final TestGesture gesture = await tester.createGesture(); | ||
| await gesture.down(tester.getCenter(find.byKey(const Key('container')))); | ||
| await tester.pump(); | ||
| // simulate a press with specified duration |
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: Capital letter at the beginning and period at the end of your comments here and below.
| (Widget w) => '${w.runtimeType}' == '_ContextMenuRouteStatic', | ||
| ); | ||
| // can't present together | ||
| if (insideTapTriggered) { |
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 is this an if, does it happen differently on different platforms?
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 is used to detect whether the click gesture is detected by GestureDetector, so there will be no difference between different platforms.
| await gesture.down(tester.getCenter(find.byKey(const Key('container')))); | ||
| await tester.pump(); | ||
| // simulate a press with specified duration | ||
| for (int i = 0; i < 100; i++) { |
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.
Does it really matter that 100 frames need to be pumped, not just 1 frame over the same duration? I wouldn't think that would affect the gesture.
|
Done. It is my first time contributing to Flutter. Thank you for your review and guidance. |
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.
Renewing my LGTM and merging. Thanks for fixing this!
flutter/flutter@ad0aa8d...436df69 2023-08-08 [email protected] Roll Flutter Engine from 9c83d90b01bd to 146c4c9487fc (6 revisions) (flutter/flutter#132112) 2023-08-08 [email protected] Roll Flutter Engine from c27109291e22 to 9c83d90b01bd (5 revisions) (flutter/flutter#132108) 2023-08-08 [email protected] Roll Flutter Engine from be085f6699b6 to c27109291e22 (3 revisions) (flutter/flutter#132086) 2023-08-08 [email protected] Revert "Replace TextField.canRequestFocus with TextField.focusNode.canRequestFocus" (flutter/flutter#132104) 2023-08-08 [email protected] [Impeller] add drawVertices and drawAtlas benchmarks. (flutter/flutter#132080) 2023-08-07 [email protected] Adds more documentations around ignoreSemantics deprecations. (flutter/flutter#131287) 2023-08-07 [email protected] [web] New HtmlElementView.fromTagName constructor (flutter/flutter#130513) 2023-08-07 [email protected] Move mock canvas to flutter_test (flutter/flutter#131631) 2023-08-07 [email protected] Add static_path_tessellation macrobenchmark (flutter/flutter#131837) 2023-08-07 [email protected] [web] Remove usage of `ui.webOnlyInitializePlatform()` (flutter/flutter#131344) 2023-08-07 [email protected] Roll Flutter Engine from 39a575f65d50 to be085f6699b6 (1 revision) (flutter/flutter#132069) 2023-08-07 [email protected] Android context menu theming and visual update (flutter/flutter#131816) 2023-08-07 [email protected] Roll Flutter Engine from 39ce1c097bce to 39a575f65d50 (2 revisions) (flutter/flutter#132064) 2023-08-07 [email protected] CupertinoContextMenu improvement (flutter/flutter#131030) 2023-08-07 [email protected] Roll Flutter Engine from 5b47c0577060 to 39ce1c097bce (3 revisions) (flutter/flutter#132057) 2023-08-07 [email protected] Slider should check `mounted` before start interaction (flutter/flutter#132010) 2023-08-07 [email protected] Roll Packages from ce53da1 to d7ee75a (7 revisions) (flutter/flutter#132058) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC [email protected],[email protected],[email protected] on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
Fixes #70716, #81057
Related ##52226
Why the conflict
If we put a
TapGestureRecognizeratCupertinoContextMenu's subtree.For simplicity, we name the
TapGestureRecognizerinsideCupertinoContextMenuas tg2, theTapGestureRecognizeratCupertinoContextMenu's subtree as tg1, and the press duration as t1.When
kPressTimeout+_previewLongPressTimeout/2(500ms) < t1 <kPressTimeout+_previewLongPressTimeout(900ms), this momentAlthough tg2 was rejected,
_ContextMenuRoutewas still opened.And the onTap method of tg1 is triggered
The problem is that the GestureRecognizer in this ContextMenu is TapGestureRecognizer instead of LongPressGestureRecognizer, and Tap does not claim to victory, but GestureArena selects the deepest Tap to win.
When t1 is greater than 500ms, it means that it has passed
PrimaryPointerGestureRecognizer.deadlineplus half of theopenController's duration, means that_ContextMenuRouteis about to be opened. AfterGestureArenareceived pointer up event, tg1 competed with tg2. The default "child-victory" worked, and the onTap of tg1 was called, so both were triggered.When t1 is less than 900ms, because route is opened at this time, and both will be rejected by arena.
Solution
The reason is that
TapGestureRecognizerwill not declare victory by itself. Add the following code.Pre-launch Checklist
///).