22// Use of this source code is governed by a BSD-style license that can be
33// found in the LICENSE file.
44
5+ import 'dart:typed_data' ;
6+
7+ import 'package:ui/src/engine/embedder.dart' ;
8+ import 'package:ui/src/engine/text_editing/text_editing.dart' ;
9+ import 'package:ui/src/engine/vector_math.dart' ;
510import 'package:ui/ui.dart' as ui show Offset;
611
712import '../dom.dart' ;
@@ -24,48 +29,50 @@ ui.Offset computeEventOffsetToTarget(DomMouseEvent event, DomElement actualTarge
2429 return _computeOffsetForTalkbackEvent (event, actualTarget);
2530 }
2631
32+ // On one of our text-editing nodes
33+ final bool isInput = flutterViewEmbedder.textEditingHostNode.contains (event.target! as DomNode );
34+ if (isInput) {
35+ final EditableTextGeometry ? inputGeometry = textEditing.strategy.geometry;
36+ if (inputGeometry != null ) {
37+ return _computeOffsetForInputs (event, inputGeometry);
38+ }
39+ }
40+
41+ // On another DOM Element (normally a platform view)
2742 final bool isTargetOutsideOfShadowDOM = event.target != actualTarget;
2843 if (isTargetOutsideOfShadowDOM) {
29- return _computeOffsetRelativeToActualTarget (event, actualTarget);
44+ final DomRect origin = actualTarget.getBoundingClientRect ();
45+ // event.clientX/Y and origin.x/y are relative **to the viewport**.
46+ // (This doesn't work with 3D translations of the parent element.)
47+ // TODO(dit): Make this understand 3D transforms, https://github.com/flutter/flutter/issues/117091
48+ return ui.Offset (event.clientX - origin.x, event.clientY - origin.y);
3049 }
50+
3151 // Return the offsetX/Y in the normal case.
3252 // (This works with 3D translations of the parent element.)
3353 return ui.Offset (event.offsetX, event.offsetY);
3454}
3555
36- /// Computes the event offset when hovering over any nodes that don't exist in
37- /// the shadowDOM such as platform views or text editing nodes.
38- ///
39- /// This still uses offsetX/Y, but adds the offset from the top/left corner of the
40- /// platform view to the Flutter View ( `actualTarget` ).
56+ /// Computes the offsets for input nodes, which live outside of the shadowDOM.
57+ /// Since inputs can be transformed (scaled, translated, etc), we can't rely on
58+ /// `_computeOffsetRelativeToActualTarget` to calculate accurate coordinates, as
59+ /// it only handles the case where inputs are translated, but will have issues
60+ /// for scaled inputs (see: https://github.com/flutter/flutter/issues/125948 ).
4161///
42- /// ×--FlutterView(actualTarget)--------------+
43- /// |\ |
44- /// | x1,y1 |
45- /// | |
46- /// | |
47- /// | ×-PlatformView(target)---------+ |
48- /// | |\ | |
49- /// | | x2,y2 | |
50- /// | | | |
51- /// | | × (event) | |
52- /// | | \ | |
53- /// | | offsetX, offsetY | |
54- /// | | (Relative to PlatformView) | |
55- /// | +------------------------------+ |
56- /// +-----------------------------------------+
57- ///
58- /// Offset between PlatformView and FlutterView (xP, yP) = (x2 - x1, y2 - y1)
59- ///
60- /// Event offset relative to FlutterView = (offsetX + xP, offsetY + yP)
61- // TODO(dit): Make this understand 3D transforms, https://github.com/flutter/flutter/issues/117091
62- ui.Offset _computeOffsetRelativeToActualTarget (DomMouseEvent event, DomElement actualTarget) {
63- final DomElement target = event.target! as DomElement ;
64- final DomRect targetRect = target.getBoundingClientRect ();
65- final DomRect actualTargetRect = actualTarget.getBoundingClientRect ();
66- final double offsetTop = targetRect.y - actualTargetRect.y;
67- final double offsetLeft = targetRect.x - actualTargetRect.x;
68- return ui.Offset (event.offsetX + offsetLeft, event.offsetY + offsetTop);
62+ /// We compute the offsets here by using the text input geometry data that is
63+ /// sent from the framework, which includes information on how to transform the
64+ /// underlying input element. We transform the `event.offset` points we receive
65+ /// using the values from the input's transform matrix.
66+ ui.Offset _computeOffsetForInputs (DomMouseEvent event, EditableTextGeometry inputGeometry) {
67+ final DomElement targetElement = event.target! as DomHTMLElement ;
68+ final DomHTMLElement domElement = textEditing.strategy.activeDomElement;
69+ assert (targetElement == domElement, 'The targeted input element must be the active input element' );
70+ final Float32List transformValues = inputGeometry.globalTransform;
71+ assert (transformValues.length == 16 );
72+ final Matrix4 transform = Matrix4 .fromFloat32List (transformValues);
73+ final Vector3 transformedPoint = transform.perspectiveTransform (x: event.offsetX, y: event.offsetY, z: 0 );
74+
75+ return ui.Offset (transformedPoint.x, transformedPoint.y);
6976}
7077
7178/// Computes the event offset when TalkBack is firing the event.
0 commit comments