From 9b34207a140525b1611d24c81a4cf2eed76bdf1b Mon Sep 17 00:00:00 2001 From: nturgut Date: Fri, 30 Oct 2020 15:28:46 -0700 Subject: [PATCH] Fixing semantics borders on mobile web (#21856) * logging * fixing positions with wrong a11y borders screenreader-on/mobile browsers * remove logs from the window class * work on unit tests * using reviewer suggestion for translations * compute bounding matrix * compute bounding matrix * addding more comments * reenable failing test case --- .../lib/src/engine/semantics/semantics.dart | 81 +++++++++++++++---- lib/web_ui/lib/src/engine/util.dart | 44 ++++++++++ 2 files changed, 108 insertions(+), 17 deletions(-) diff --git a/lib/web_ui/lib/src/engine/semantics/semantics.dart b/lib/web_ui/lib/src/engine/semantics/semantics.dart index 67fc749e9..791caed14 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 0338a6f2d..413eb153e 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); + } -- GitLab