diff --git a/lib/web_ui/lib/src/engine/dom.dart b/lib/web_ui/lib/src/engine/dom.dart index cd2c9a9b9af91..750587774c313 100644 --- a/lib/web_ui/lib/src/engine/dom.dart +++ b/lib/web_ui/lib/src/engine/dom.dart @@ -2475,13 +2475,6 @@ DomPath2D createDomPath2D([Object? path]) { } } -@JS('MouseEvent') -@staticInterop -class DomMouseEvent extends DomUIEvent { - external factory DomMouseEvent.arg1(JSString type); - external factory DomMouseEvent.arg2(JSString type, JSAny initDict); -} - @JS('InputEvent') @staticInterop class DomInputEvent extends DomUIEvent { @@ -2489,6 +2482,21 @@ class DomInputEvent extends DomUIEvent { external factory DomInputEvent.arg2(JSString type, JSAny initDict); } +@JS('FocusEvent') +@staticInterop +class DomFocusEvent extends DomUIEvent {} + +extension DomFocusEventExtension on DomFocusEvent { + external DomEventTarget? get relatedTarget; +} + +@JS('MouseEvent') +@staticInterop +class DomMouseEvent extends DomUIEvent { + external factory DomMouseEvent.arg1(JSString type); + external factory DomMouseEvent.arg2(JSString type, JSAny initDict); +} + extension DomMouseEventExtension on DomMouseEvent { @JS('clientX') external JSNumber get _clientX; diff --git a/lib/web_ui/lib/src/engine/platform_dispatcher/view_focus_binding.dart b/lib/web_ui/lib/src/engine/platform_dispatcher/view_focus_binding.dart index 7a24036a64beb..d66264e2494ef 100644 --- a/lib/web_ui/lib/src/engine/platform_dispatcher/view_focus_binding.dart +++ b/lib/web_ui/lib/src/engine/platform_dispatcher/view_focus_binding.dart @@ -18,8 +18,8 @@ final class ViewFocusBinding { /// Subscribes the [listener] to [ui.ViewFocusEvent] events. void addListener(ui.ViewFocusChangeCallback listener) { if (_listeners.isEmpty) { - domDocument.body?.addEventListener(_focusin, _focusChangeHandler, true); - domDocument.body?.addEventListener(_focusout, _focusChangeHandler, true); + domDocument.body?.addEventListener(_focusin, _handleFocusin, true); + domDocument.body?.addEventListener(_focusout, _handleFocusout, true); } _listeners.add(listener); } @@ -28,8 +28,8 @@ final class ViewFocusBinding { void removeListener(ui.ViewFocusChangeCallback listener) { _listeners.remove(listener); if (_listeners.isEmpty) { - domDocument.body?.removeEventListener(_focusin, _focusChangeHandler, true); - domDocument.body?.removeEventListener(_focusout, _focusChangeHandler, true); + domDocument.body?.removeEventListener(_focusin, _handleFocusin, true); + domDocument.body?.removeEventListener(_focusout, _handleFocusout, true); } } @@ -39,9 +39,17 @@ final class ViewFocusBinding { } } + late final DomEventListener _handleFocusin = createDomEventListener( + (DomEvent event) => _handleFocusChange(event.target as DomElement?), + ); + + late final DomEventListener _handleFocusout = createDomEventListener( + (DomEvent event) => _handleFocusChange((event as DomFocusEvent).relatedTarget as DomElement?), + ); + int? _lastViewId; - late final DomEventListener _focusChangeHandler = createDomEventListener((DomEvent event) { - final int? viewId = _viewId(domDocument.activeElement); + void _handleFocusChange(DomElement? focusedElement) { + final int? viewId = _viewId(focusedElement); if (viewId == _lastViewId) { return; } @@ -62,7 +70,7 @@ final class ViewFocusBinding { } _lastViewId = viewId; _notify(event); - }); + } static int? _viewId(DomElement? element) { final DomElement? viewElement = element?.closest( diff --git a/lib/web_ui/test/engine/platform_dispatcher/view_focus_binding_test.dart b/lib/web_ui/test/engine/platform_dispatcher/view_focus_binding_test.dart index ecac6a35489db..9342595b0fca8 100644 --- a/lib/web_ui/test/engine/platform_dispatcher/view_focus_binding_test.dart +++ b/lib/web_ui/test/engine/platform_dispatcher/view_focus_binding_test.dart @@ -86,19 +86,15 @@ void testMain() { focusableViewElement1.focus(); focusableViewElement2.focus(); - expect(viewFocusEvents, hasLength(3)); + expect(viewFocusEvents, hasLength(2)); expect(viewFocusEvents[0].viewId, view1.viewId); expect(viewFocusEvents[0].state, ui.ViewFocusState.focused); expect(viewFocusEvents[0].direction, ui.ViewFocusDirection.forward); - expect(viewFocusEvents[1].viewId, view1.viewId); - expect(viewFocusEvents[1].state, ui.ViewFocusState.unfocused); - expect(viewFocusEvents[1].direction, ui.ViewFocusDirection.undefined); - - expect(viewFocusEvents[2].viewId, view2.viewId); - expect(viewFocusEvents[2].state, ui.ViewFocusState.focused); - expect(viewFocusEvents[2].direction, ui.ViewFocusDirection.forward); + expect(viewFocusEvents[1].viewId, view2.viewId); + expect(viewFocusEvents[1].state, ui.ViewFocusState.focused); + expect(viewFocusEvents[1].direction, ui.ViewFocusDirection.forward); }); test('fires a focus event - focus transitions on and off views', () async { @@ -125,23 +121,19 @@ void testMain() { focusableViewElement2.focus(); focusableViewElement2.blur(); - expect(viewFocusEvents, hasLength(4)); + expect(viewFocusEvents, hasLength(3)); expect(viewFocusEvents[0].viewId, view1.viewId); expect(viewFocusEvents[0].state, ui.ViewFocusState.focused); expect(viewFocusEvents[0].direction, ui.ViewFocusDirection.forward); - expect(viewFocusEvents[1].viewId, view1.viewId); - expect(viewFocusEvents[1].state, ui.ViewFocusState.unfocused); - expect(viewFocusEvents[1].direction, ui.ViewFocusDirection.undefined); + expect(viewFocusEvents[1].viewId, view2.viewId); + expect(viewFocusEvents[1].state, ui.ViewFocusState.focused); + expect(viewFocusEvents[1].direction, ui.ViewFocusDirection.forward); expect(viewFocusEvents[2].viewId, view2.viewId); - expect(viewFocusEvents[2].state, ui.ViewFocusState.focused); - expect(viewFocusEvents[2].direction, ui.ViewFocusDirection.forward); - - expect(viewFocusEvents[3].viewId, view2.viewId); - expect(viewFocusEvents[3].state, ui.ViewFocusState.unfocused); - expect(viewFocusEvents[3].direction, ui.ViewFocusDirection.undefined); + expect(viewFocusEvents[2].state, ui.ViewFocusState.unfocused); + expect(viewFocusEvents[2].direction, ui.ViewFocusDirection.undefined); }); }); }