-
Notifications
You must be signed in to change notification settings - Fork 28.7k
[flutter] when primary mouse pointers don't contact a focused node, reset the focus #82575
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
Conversation
This currently only applies for keyboard events, I'd need to do more research into how touch events should behave |
Gold has detected about 2 untriaged digest(s) on patchset 2. |
adding another widget here seems to be changing the layout slightly, which is unfortunate... |
This pull request has been changed to a draft. The currently pending flutter-gold status will not be able to resolve until a new commit is pushed or the change is marked ready for review again. For more guidance, visit Writing a golden file test for Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing. |
@@ -841,11 +843,11 @@ class _ModalScopeState<T> extends State<_ModalScope<T>> { | |||
key: widget.route._subtreeKey, // immutable | |||
child: Builder( | |||
builder: (BuildContext context) { | |||
return widget.route.buildPage( | |||
return _FocusTrap(child: widget.route.buildPage( |
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.
any reason we scope at route level?
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 scoped it at the route level so that I can have this and the shortcuts widget used by the SelectionArea widget use the same focus node.
FocusNode get focusNode => widget.focusNode ?? _focusNode; | ||
|
||
void _stealFocus(TapDownDetails details) { | ||
if (details.kind != PointerDeviceKind.mouse) |
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.
do you know what is the native behavior for mobile if you have an external mouse?
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 don't have access to the hardware to test this. we could definitely just disable the behavior on iOS/android until we could test it
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.
(disabled for non-desktop platforms for now)
Just an outsiders thought: This looks great and all, but should it really be necessary to add a |
I agree, I'm not sure that this is the right solution exactly but i'm not sure how else you would do it. Widgets can't unfocus themselves if they aren't hit 🤔 |
In HTML Web every element can be focused. Even if it is not editable, correct? I am not a flutter developer. So this is only an idea from outside but I wanted to post it. For me it is rather important to get some working solution in the next two weeks than to get a really good solution. |
An html element isn't equivalent to a flutter widget. A widget not have any UI, only configuring other widgets themselves. Even making every widget with uI focusable would make pure focus traversal painful. |
Based on some discussion with @gspencergoog , I've updated this to use the unfocus functionality of the modal route's FocusScopeNode. Of course, using the gesture recognizer the way that I am is breaking the world, so I'll need to find a way to make this change not compete with any other recognizers somehow... |
Gold has detected about 3 untriaged digest(s) on patchset 4. |
@@ -136,8 +136,8 @@ typedef GestureTapCancelCallback = void Function(); | |||
/// any buttons. | |||
abstract class BaseTapGestureRecognizer extends PrimaryPointerGestureRecognizer { | |||
/// Creates a tap gesture recognizer. | |||
BaseTapGestureRecognizer({ Object? debugOwner }) | |||
: super(deadline: kPressTimeout , debugOwner: debugOwner); | |||
BaseTapGestureRecognizer({ Object? debugOwner, Set<PointerDeviceKind>? supportedDevices }) |
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.
Will revert, unrelated
_requestKeyboard(); | ||
}, | ||
onDidGainAccessibilityFocus: handleDidGainAccessibilityFocus, | ||
return FocusArea( |
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.
Because we want clicks on the decorations of at text field to retain focus
PTAL at the current approach |
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.
|| event.buttons != kPrimaryButton | ||
|| event.kind != PointerDeviceKind.mouse | ||
|| _shouldIgnoreEvents | ||
|| currentFocus == null) |
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: Can you put braces on this conditional block? Having a really long multi-line conditional just reads weird without the braces.
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.
will do
ignoring: ignoreEvents, | ||
child: child, | ||
child: _FocusTrap( | ||
focusScopeNode: focusScopeNode, |
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 I'm OK with the current structure: the widget using the node is a private class, so we have control over its lifetime, and it doesn't appear to be doing anything that would attach the node to the focus tree.
FYI @renyou , not sure what is up with the google testing on this PR |
It seems some google internal tests may be very slow and I am going to increase the timeout threshold. Hopefully it can resolve this. |
@jonahwilliams Did you trigger a full test on this PR? |
yes, since it is a potentially a substantial change I wanted to be sure we ran as many tests as possible... |
Yes! The tests are actually all done, just took longer to analyze all the test results. I manually set the results. Everything looks good! |
🎊 |
@jonahwilliams - does this PR address #74354? |
This only handles clicks via mouse on desktop and mobile web, not taps - though the PR also mentions clicks. I'm not sure if that is trying to fix the same issue as this or a different input connection based focus issue |
|
If we want this behaviour, why not just put a Focus widget around the route? ...but I'm not convinced we want this behaviour. It's not what happens on Windows and macOS as far as I can tell. |
I think this is just something we want on Web, and on Web the behaviour is just that the root of the tree is itself focusable, no need to check whether a tap would have focused a node and all that. |
… node, reset the focus (flutter#82575)" This reverts commit 85be4f6.
Fixes #82515
Fixes #32433
Introduces a widget that checks if a pointer down event has hit the currently focused nodes' render object. This leads to odd behavior with text field decorations due to the focus being only owned by the inner editable text widget.
The modal route's focus scoped node is used to "reset" the focus when pointer did not contact either.
This is limited to mouse pointers on desktop and mobile web.