diff --git a/lib/web_ui/lib/src/engine/semantics/semantics.dart b/lib/web_ui/lib/src/engine/semantics/semantics.dart index 67fc749e901e485a0261d4ddccce17e43ca1cf9a..791caed14cfb2a7dc62ffcd77814a159489d4be8 100644 --- a/lib/web_ui/lib/src/engine/semantics/semantics.dart +++ b/lib/web_ui/lib/src/engine/semantics/semantics.dart @@ -851,13 +851,25 @@ class SemanticsObject { hasIdentityTransform && verticalContainerAdjustment == 0.0 && horizontalContainerAdjustment == 0.0) { - element.style - ..removeProperty('transform-origin') - ..removeProperty('transform'); - if (containerElement != null) { - containerElement.style + if (isDesktop) { + element.style ..removeProperty('transform-origin') ..removeProperty('transform'); + } else { + element.style + ..removeProperty('top') + ..removeProperty('left'); + } + if (containerElement != null) { + if (isDesktop) { + containerElement.style + ..removeProperty('transform-origin') + ..removeProperty('transform'); + } else { + containerElement.style + ..removeProperty('top') + ..removeProperty('left'); + } } return; } @@ -882,13 +894,36 @@ class SemanticsObject { } if (!effectiveTransformIsIdentity) { - element.style - ..transformOrigin = '0 0 0' - ..transform = matrix4ToCssTransform(effectiveTransform); + if (isDesktop) { + element.style + ..transformOrigin = '0 0 0' + ..transform = matrix4ToCssTransform(effectiveTransform); + } else { + // Mobile screen readers observed to have errors while calculating the + // semantics focus borders if css `transform` properties are used. + // See: https://github.com/flutter/flutter/issues/68225 + // Therefore we are calculating a bounding rectangle for the + // effective transform and use that rectangle to set TLWH css style + // properties. + // Note: Identity matrix is not using this code path. + final ui.Rect rect = + computeBoundingRectangleFromMatrix(effectiveTransform, _rect!); + element.style + ..top = '${rect.top}px' + ..left = '${rect.left}px' + ..width = '${rect.width}px' + ..height = '${rect.height}px'; + } } else { - element.style - ..removeProperty('transform-origin') - ..removeProperty('transform'); + if (isDesktop) { + element.style + ..removeProperty('transform-origin') + ..removeProperty('transform'); + } else { + element.style + ..removeProperty('top') + ..removeProperty('left'); + } } if (containerElement != null) { @@ -897,13 +932,25 @@ class SemanticsObject { horizontalContainerAdjustment != 0.0) { final double translateX = -_rect!.left + horizontalContainerAdjustment; final double translateY = -_rect!.top + verticalContainerAdjustment; - containerElement.style - ..transformOrigin = '0 0 0' - ..transform = 'translate(${translateX}px, ${translateY}px)'; + if (isDesktop) { + containerElement.style + ..transformOrigin = '0 0 0' + ..transform = 'translate(${translateX}px, ${translateY}px)'; + } else { + containerElement.style + ..top = '${translateY}px' + ..left = '${translateX}px'; + } } else { - containerElement.style - ..removeProperty('transform-origin') - ..removeProperty('transform'); + if (isDesktop) { + containerElement.style + ..removeProperty('transform-origin') + ..removeProperty('transform'); + } else { + containerElement.style + ..removeProperty('top') + ..removeProperty('left'); + } } } } diff --git a/lib/web_ui/lib/src/engine/util.dart b/lib/web_ui/lib/src/engine/util.dart index 0338a6f2d7ea3ed6db224c699f526d3150a8eed1..413eb153ebce58facd9b7e25d71fe7aad2d8463b 100644 --- a/lib/web_ui/lib/src/engine/util.dart +++ b/lib/web_ui/lib/src/engine/util.dart @@ -596,3 +596,47 @@ int clampInt(int value, int min, int max) { return value; } } + +ui.Rect computeBoundingRectangleFromMatrix(Matrix4 transform, ui.Rect rect) { + final Float32List m = transform.storage; + // Apply perspective transform to all 4 corners. Can't use left,top, bottom, + // right since for example rotating 45 degrees would yield inaccurate size. + double x = rect.left; + double y = rect.top; + double wp = 1.0 / ((m[3] * x) + (m[7] * y) + m[15]); + double xp = ((m[0] * x) + (m[4] * y) + m[12]) * wp; + double yp = ((m[1] * x) + (m[5] * y) + m[13]) * wp; + double minX = xp, maxX = xp; + double minY =yp, maxY = yp; + x = rect.right; + y = rect.bottom; + wp = 1.0 / ((m[3] * x) + (m[7] * y) + m[15]); + xp = ((m[0] * x) + (m[4] * y) + m[12]) * wp; + yp = ((m[1] * x) + (m[5] * y) + m[13]) * wp; + + minX = math.min(minX, xp); + maxX = math.max(maxX, xp); + minY = math.min(minY, yp); + maxY = math.max(maxY, yp); + + x = rect.left; + y = rect.bottom; + wp = 1.0 / ((m[3] * x) + (m[7] * y) + m[15]); + xp = ((m[0] * x) + (m[4] * y) + m[12]) * wp; + yp = ((m[1] * x) + (m[5] * y) + m[13]) * wp; + minX = math.min(minX, xp); + maxX = math.max(maxX, xp); + minY = math.min(minY, yp); + maxY = math.max(maxY, yp); + + x = rect.right; + y = rect.top; + wp = 1.0 / ((m[3] * x) + (m[7] * y) + m[15]); + xp = ((m[0] * x) + (m[4] * y) + m[12]) * wp; + yp = ((m[1] * x) + (m[5] * y) + m[13]) * wp; + minX = math.min(minX, xp); + maxX = math.max(maxX, xp); + minY = math.min(minY, yp); + maxY = math.max(maxY, yp); + return ui.Rect.fromLTWH(minX, minY, maxX-minX, maxY-minY); + }