-
Notifications
You must be signed in to change notification settings - Fork 6k
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
[web] PointerBinding per view #48248
Conversation
252b4df
to
df9bbd8
Compare
df9bbd8
to
bc10542
Compare
bc10542
to
7281436
Compare
DomWheelEvent? _lastWheelEvent; | ||
bool _lastWheelEventWasTrackpad = false; | ||
|
||
DomElement get _viewTarget => _view.dom.rootElement; | ||
DomEventTarget get _globalTarget { | ||
// When the Flutter app owns the full page, we want to listen on window for |
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.
Isn't returning thew view target implictily covering 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.
Nope. The root element is always a <flutter-view>
element that we insert somewhere. But in the implicitView
case, we want this to return window
.
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.
Looks good! I had a bunch of comments here and there, but I don't think they're blocking! (I hope! :P)
if (isIosSafari) { | ||
SafariPointerEventWorkaround.instance.workAroundMissingPointerEvents(); | ||
_safariWorkaround = safariWorkaround ?? SafariPointerEventWorkaround(); |
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.
Looks like this Safari workaround could be a singleton? It only needs to be applied once into the document, and not on every view, right?
(The only thing it's doing is adding an empty listener to document.ontouchstart
it seems?)
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 made the "default" workaround instance a static member on PointerBinding
(so we only ever create one). And the instance only attaches the event listener once (even it gets called multiple times).
KeyboardBinding.initInstance(); | ||
PointerBinding.initInstance( |
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.
So little stuff remaining in this reset function!!
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.
Yeah, I can't wait to get rid of it!
/// Resets global pointer state that's not tied to any single [PointerBinding] | ||
/// instance. | ||
static void resetGlobalState() { |
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 cannot find any prod code calling this, so I'm assuming it's only for tests?
Can this be called debugResetGlobalState
(or indicate that this is for tests in any other way, with @visibleForTesting
or documentation?)
if (_view == EnginePlatformDispatcher.instance.implicitView) { | ||
return domWindow; | ||
} |
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.
Maybe have an eventsTarget
getter on the view that returns the proper object? The embedding strategy for "full page" already has a bunch of domDocument
hardcoded in certain places, it could return a domWindow
very easily.
Also: do you think we could migrate the "embedded" mode to use the rootElement as its target in this PR as well? That way we'd only have two cases:
- embedded element -> rootElement
- whole window -> domWindow
Rather than distinguishing them by being an "implicitView" or not.
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 made the change but I'm not sure if I captured exactly what you wanted. Please see 1de6ab9
// Why `domWindow` you ask? See this fiddle: https://jsfiddle.net/ditman/7towxaqp | ||
_addPointerEventListener(domWindow, 'pointermove', (DomPointerEvent event) { | ||
_addPointerEventListener(_globalTarget, 'pointermove', (DomPointerEvent event) { |
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.
(Rant) I wonder if the solution to all this "listening to global events craze" is to have:
- A per-view listener that is targeted to the view itself for normal mouse moves
- A global listener on window that we can tap from as needed in special cases (when the mouse leaves the window, etc...)
That way we might simplify the general case of pointer handling :/
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.
We do need to figure this out at some point, yes. For example, do we care about hover events happening outside flutter views? What if the pointer is down and moves outside of the view? etc.
assert(() { | ||
registerHotRestartListener(reset); | ||
return 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.
Are we landing in the following pattern:
reset
for hot restart + testsdispose
for actual code paths that could happen at runtime?
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.
The way I think about these is reset
means the instance is back to its original state and can be used further. OTOH, dispose
means the object cleaned after itself and can't be used anymore.
assert(_pointers.containsKey(device)); | ||
final _PointerState state = _pointers[device]!; | ||
assert(globalPointerState.pointers.containsKey(device)); | ||
final _PointerDeviceState state = globalPointerState.pointers[device]!; |
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 mind globalPointerState being a global, but it could be modeled as a property of a PointerDevice object and do:
_PointerDeviceState state = pointerDevices[deviceId].state!;
But since we don't care about anything else of the pointerDevice -> ship it like this!
// Remove <body> margins to avoid messing up with all the test coordinates. | ||
domDocument.body!.style.margin = '0'; |
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 guess we didn't need this before because the (full-screen) implicitView has this?
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.
Exactly! The full page embedding strategy was doing this for us.
…139936) flutter/engine@5587d26...4c30919 2023-12-11 mdebbar@google.com [web] PointerBinding per view (flutter/engine#48248) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-engine-flutter-autoroll Please CC chinmaygarde@google.com,rmistry@google.com,zra@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
PointerBinding
/KeyboardBinding
out ofFlutterViewEmbedder
.computeEventOffsetToTarget
properly handles events within a given view.PointerBinding
operates on the given Flutter view (it still listens to somedomWindow
events for the implicit view).ui.window
,KeyboardBinding.instance
,SafariPointerEventWorkaround.instance
, etc.pointer_binding_test.dart
doesn't use globals either.clickDebouncer
is now a static property onPointerBinding
.Fixes flutter/flutter#137289