mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
<!-- start_original_pr_link --> Reverts: flutter/flutter#163662 <!-- end_original_pr_link --> <!-- start_initiating_author --> Initiated by: hannah-hyj <!-- end_initiating_author --> <!-- start_revert_reason --> Reason for reverting: google 3 failure <!-- end_revert_reason --> <!-- start_original_pr_author --> Original PR Author: hannah-hyj <!-- end_original_pr_author --> <!-- start_reviewers --> Reviewed By: {yjbanov} <!-- end_reviewers --> <!-- start_revert_body --> This change reverts the following previous change: delete _childContainerElement , add the rect compensate and scrolling adjustment to the children ## Pre-launch Checklist - [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [ ] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [ ] I signed the [CLA]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I added new tests to check the change I am making, or this PR is [test-exempt]. - [ ] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md <!-- end_revert_body --> Co-authored-by: auto-submit[bot] <flutter-engprod-team@google.com>
This commit is contained in:
parent
155f6dc7f9
commit
4e5a2dbf1a
@ -70,7 +70,6 @@ class SemanticScrollable extends SemanticRole {
|
||||
final bool doScrollForward = _domScrollPosition > _effectiveNeutralScrollPosition;
|
||||
_neutralizeDomScrollPosition();
|
||||
semanticsObject.recomputePositionAndSize();
|
||||
semanticsObject.updateChildrenPositionAndSize();
|
||||
|
||||
final int semanticsId = semanticsObject.id;
|
||||
if (doScrollForward) {
|
||||
@ -132,7 +131,6 @@ class SemanticScrollable extends SemanticRole {
|
||||
semanticsObject.owner.addOneTimePostUpdateCallback(() {
|
||||
_neutralizeDomScrollPosition();
|
||||
semanticsObject.recomputePositionAndSize();
|
||||
semanticsObject.updateChildrenPositionAndSize();
|
||||
});
|
||||
|
||||
if (_scrollListener == null) {
|
||||
@ -205,8 +203,8 @@ class SemanticScrollable extends SemanticRole {
|
||||
// Read back because the effective value depends on the amount of content.
|
||||
_effectiveNeutralScrollPosition = element.scrollTop.toInt();
|
||||
semanticsObject
|
||||
..verticalScrollAdjustment = _effectiveNeutralScrollPosition.toDouble()
|
||||
..horizontalScrollAdjustment = 0.0;
|
||||
..verticalContainerAdjustment = _effectiveNeutralScrollPosition.toDouble()
|
||||
..horizontalContainerAdjustment = 0.0;
|
||||
} else {
|
||||
// Place the _scrollOverflowElement at the end of the content and
|
||||
// make sure that when we neutralize the scrolling position,
|
||||
@ -221,8 +219,8 @@ class SemanticScrollable extends SemanticRole {
|
||||
// Read back because the effective value depends on the amount of content.
|
||||
_effectiveNeutralScrollPosition = element.scrollLeft.toInt();
|
||||
semanticsObject
|
||||
..verticalScrollAdjustment = 0.0
|
||||
..horizontalScrollAdjustment = _effectiveNeutralScrollPosition.toDouble();
|
||||
..verticalContainerAdjustment = 0.0
|
||||
..horizontalContainerAdjustment = _effectiveNeutralScrollPosition.toDouble();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1358,6 +1358,33 @@ class SemanticsObject {
|
||||
/// The dom element of this semantics object.
|
||||
DomElement get element => semanticRole!.element;
|
||||
|
||||
/// Returns the HTML element that contains the HTML elements of direct
|
||||
/// children of this object.
|
||||
///
|
||||
/// The element is created lazily. When the child list is empty this element
|
||||
/// is not created. This is necessary for "aria-label" to function correctly.
|
||||
/// The browser will ignore the [label] of HTML element that contain child
|
||||
/// elements.
|
||||
DomElement? getOrCreateChildContainer() {
|
||||
if (_childContainerElement == null) {
|
||||
_childContainerElement = createDomElement('flt-semantics-container');
|
||||
_childContainerElement!.style
|
||||
..position = 'absolute'
|
||||
// Ignore pointer events on child container so that platform views
|
||||
// behind it can be reached.
|
||||
..pointerEvents = 'none';
|
||||
element.append(_childContainerElement!);
|
||||
}
|
||||
return _childContainerElement;
|
||||
}
|
||||
|
||||
/// The element that contains the elements belonging to the child semantics
|
||||
/// nodes.
|
||||
///
|
||||
/// This element is used to correct for [_rect] offsets. It is only non-`null`
|
||||
/// when there are non-zero children (i.e. when [hasChildren] is `true`).
|
||||
DomElement? _childContainerElement;
|
||||
|
||||
/// The parent of this semantics object.
|
||||
///
|
||||
/// This value is not final until the tree is finalized. It is not safe to
|
||||
@ -1655,15 +1682,22 @@ class SemanticsObject {
|
||||
// Trivial case: remove all children.
|
||||
if (_childrenInHitTestOrder == null || _childrenInHitTestOrder!.isEmpty) {
|
||||
if (_currentChildrenInRenderOrder == null || _currentChildrenInRenderOrder!.isEmpty) {
|
||||
// A container element must not have been created when child list is empty.
|
||||
assert(_childContainerElement == null);
|
||||
_currentChildrenInRenderOrder = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// A container element must have been created when child list is not empty.
|
||||
assert(_childContainerElement != null);
|
||||
|
||||
// Remove all children from this semantics object.
|
||||
final int len = _currentChildrenInRenderOrder!.length;
|
||||
for (int i = 0; i < len; i++) {
|
||||
owner._detachObject(_currentChildrenInRenderOrder![i].id);
|
||||
}
|
||||
_childContainerElement!.remove();
|
||||
_childContainerElement = null;
|
||||
_currentChildrenInRenderOrder = null;
|
||||
return;
|
||||
}
|
||||
@ -1672,6 +1706,7 @@ class SemanticsObject {
|
||||
final Int32List childrenInTraversalOrder = _childrenInTraversalOrder!;
|
||||
final Int32List childrenInHitTestOrder = _childrenInHitTestOrder!;
|
||||
final int childCount = childrenInHitTestOrder.length;
|
||||
final DomElement? containerElement = getOrCreateChildContainer();
|
||||
|
||||
assert(childrenInTraversalOrder.length == childrenInHitTestOrder.length);
|
||||
|
||||
@ -1703,7 +1738,7 @@ class SemanticsObject {
|
||||
// Trivial case: previous list was empty => just populate the container.
|
||||
if (_currentChildrenInRenderOrder == null || _currentChildrenInRenderOrder!.isEmpty) {
|
||||
for (final SemanticsObject child in childrenInRenderOrder) {
|
||||
element.append(child.element);
|
||||
containerElement!.append(child.element);
|
||||
owner._attachObject(parent: this, child: child);
|
||||
}
|
||||
_currentChildrenInRenderOrder = childrenInRenderOrder;
|
||||
@ -1787,9 +1822,9 @@ class SemanticsObject {
|
||||
final SemanticsObject child = childrenInRenderOrder[i];
|
||||
if (!stationaryIds.contains(child.id)) {
|
||||
if (refNode == null) {
|
||||
element.append(child.element);
|
||||
containerElement!.append(child.element);
|
||||
} else {
|
||||
element.insertBefore(child.element, refNode);
|
||||
containerElement!.insertBefore(child.element, refNode);
|
||||
}
|
||||
owner._attachObject(parent: this, child: child);
|
||||
} else {
|
||||
@ -1955,6 +1990,10 @@ class SemanticsObject {
|
||||
|
||||
// Reparent element.
|
||||
if (previousElement != element) {
|
||||
final DomElement? container = _childContainerElement;
|
||||
if (container != null) {
|
||||
element.append(container);
|
||||
}
|
||||
final DomElement? parent = previousElement?.parent;
|
||||
if (parent != null) {
|
||||
parent.insertBefore(element, previousElement);
|
||||
@ -2040,74 +2079,60 @@ class SemanticsObject {
|
||||
/// Indicates whether the node is currently expanded.
|
||||
bool get isExpanded => hasFlag(ui.SemanticsFlag.isExpanded);
|
||||
|
||||
/// Role-specific adjustment of the vertical position of the children.
|
||||
/// Role-specific adjustment of the vertical position of the child container.
|
||||
///
|
||||
/// This is used, for example, by the [SemanticScrollable] to compensate for the
|
||||
/// `scrollTop` offset in the DOM.
|
||||
///
|
||||
/// This field must not be null.
|
||||
double verticalScrollAdjustment = 0.0;
|
||||
double verticalContainerAdjustment = 0.0;
|
||||
|
||||
/// Role-specific adjustment of the horizontal position of children.
|
||||
/// Role-specific adjustment of the horizontal position of the child
|
||||
/// container.
|
||||
///
|
||||
/// This is used, for example, by the [SemanticScrollable] to compensate for the
|
||||
/// `scrollLeft` offset in the DOM.
|
||||
///
|
||||
/// This field must not be null.
|
||||
double horizontalScrollAdjustment = 0.0;
|
||||
|
||||
double verticalAdjustmentFromParent = 0.0;
|
||||
double horizontalAdjustmentFromParent = 0.0;
|
||||
double horizontalContainerAdjustment = 0.0;
|
||||
|
||||
/// Computes the size and position of [element] and, if this element
|
||||
/// [hasChildren], computes the parent adjustment for each child.
|
||||
/// [hasChildren], of [getOrCreateChildContainer].
|
||||
void recomputePositionAndSize() {
|
||||
element.style
|
||||
..width = '${_rect!.width}px'
|
||||
..height = '${_rect!.height}px';
|
||||
|
||||
final DomElement? containerElement = hasChildren ? getOrCreateChildContainer() : null;
|
||||
|
||||
final bool hasZeroRectOffset = _rect!.top == 0.0 && _rect!.left == 0.0;
|
||||
final Float32List? transform = _transform;
|
||||
final bool hasIdentityTransform =
|
||||
transform == null || isIdentityFloat32ListTransform(transform);
|
||||
|
||||
// If this node has children, we need to compensate for the parent's rect and
|
||||
// pass down the scroll adjustments.
|
||||
if (hasChildren) {
|
||||
final double translateX = -_rect!.left + horizontalScrollAdjustment;
|
||||
final double translateY = -_rect!.top + verticalScrollAdjustment;
|
||||
|
||||
for (final childIndex in _childrenInTraversalOrder!) {
|
||||
final child = owner._semanticsTree[childIndex];
|
||||
if (child == null) {
|
||||
continue;
|
||||
}
|
||||
child.horizontalAdjustmentFromParent = translateX;
|
||||
child.verticalAdjustmentFromParent = translateY;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasZeroRectOffset &&
|
||||
hasIdentityTransform &&
|
||||
verticalAdjustmentFromParent == 0.0 &&
|
||||
horizontalAdjustmentFromParent == 0.0) {
|
||||
verticalContainerAdjustment == 0.0 &&
|
||||
horizontalContainerAdjustment == 0.0) {
|
||||
_clearSemanticElementTransform(element);
|
||||
if (containerElement != null) {
|
||||
_clearSemanticElementTransform(containerElement);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
late Matrix4 effectiveTransform;
|
||||
bool effectiveTransformIsIdentity = true;
|
||||
|
||||
final double left = _rect!.left + horizontalAdjustmentFromParent;
|
||||
final double top = _rect!.top + verticalAdjustmentFromParent;
|
||||
|
||||
if (left != 0.0 || top != 0.0) {
|
||||
if (!hasZeroRectOffset) {
|
||||
if (transform == null) {
|
||||
final double left = _rect!.left;
|
||||
final double top = _rect!.top;
|
||||
effectiveTransform = Matrix4.translationValues(left, top, 0.0);
|
||||
effectiveTransformIsIdentity = false;
|
||||
effectiveTransformIsIdentity = left == 0.0 && top == 0.0;
|
||||
} else {
|
||||
// Clone to avoid mutating _transform.
|
||||
effectiveTransform = Matrix4.fromFloat32List(transform).clone()..translate(left, top);
|
||||
effectiveTransform =
|
||||
Matrix4.fromFloat32List(transform).clone()..translate(_rect!.left, _rect!.top);
|
||||
effectiveTransformIsIdentity = effectiveTransform.isIdentity();
|
||||
}
|
||||
} else if (!hasIdentityTransform) {
|
||||
@ -2122,16 +2147,19 @@ class SemanticsObject {
|
||||
} else {
|
||||
_clearSemanticElementTransform(element);
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the size and position of children.
|
||||
void updateChildrenPositionAndSize() {
|
||||
for (final childIndex in _childrenInTraversalOrder!) {
|
||||
final child = owner._semanticsTree[childIndex];
|
||||
if (child == null) {
|
||||
continue;
|
||||
if (containerElement != null) {
|
||||
if (!hasZeroRectOffset ||
|
||||
verticalContainerAdjustment != 0.0 ||
|
||||
horizontalContainerAdjustment != 0.0) {
|
||||
final double translateX = -_rect!.left + horizontalContainerAdjustment;
|
||||
final double translateY = -_rect!.top + verticalContainerAdjustment;
|
||||
containerElement.style
|
||||
..top = '${translateY}px'
|
||||
..left = '${translateX}px';
|
||||
} else {
|
||||
_clearSemanticElementTransform(containerElement);
|
||||
}
|
||||
child.recomputePositionAndSize();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2691,7 +2719,7 @@ class EngineSemanticsOwner {
|
||||
removals.add(node);
|
||||
} else {
|
||||
assert(node._parent == parent);
|
||||
assert(node.element.parentNode == parent.element);
|
||||
assert(node.element.parentNode == parent._childContainerElement);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
@ -2800,9 +2828,6 @@ class EngineSemanticsOwner {
|
||||
final SemanticsObject object = _semanticsTree[nodeUpdate.id]!;
|
||||
object.updateChildren();
|
||||
object._dirtyFields = 0;
|
||||
|
||||
object.recomputePositionAndSize();
|
||||
object.updateChildrenPositionAndSize();
|
||||
}
|
||||
|
||||
final SemanticsObject root = _semanticsTree[0]!;
|
||||
@ -2838,6 +2863,9 @@ AFTER: $description
|
||||
// Dirty fields should be cleared after the tree has been finalized.
|
||||
assert(object._dirtyFields == 0);
|
||||
|
||||
// Make sure a child container is created only when there are children.
|
||||
assert(object._childContainerElement == null || object.hasChildren);
|
||||
|
||||
// Ensure child ID list is consistent with the parent-child
|
||||
// relationship of the semantics tree.
|
||||
if (object._childrenInTraversalOrder != null) {
|
||||
|
||||
@ -274,6 +274,7 @@ class HtmlPatternMatcher extends Matcher {
|
||||
static bool _areTagsEqual(html.Element a, html.Element b) {
|
||||
const Map<String, String> synonyms = <String, String>{
|
||||
'sem': 'flt-semantics',
|
||||
'sem-c': 'flt-semantics-container',
|
||||
'sem-img': 'flt-semantics-img',
|
||||
'sem-tf': 'flt-semantics-text-field',
|
||||
};
|
||||
|
||||
@ -99,11 +99,15 @@ Future<void> testMain() async {
|
||||
// Test that each view renders its own semantics tree.
|
||||
expectSemanticsTree(view1.semantics, '''
|
||||
<sem style="filter: opacity(0%); color: rgba(0, 0, 0, 0)">
|
||||
<sem-c>
|
||||
<sem flt-tappable="" role="button"></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
expectSemanticsTree(view2.semantics, '''
|
||||
<sem style="filter: opacity(0%); color: rgba(0, 0, 0, 0)">
|
||||
<sem-c>
|
||||
<sem aria-label="d"><input aria-valuemax="1" aria-valuemin="1" aria-valuenow="1" aria-valuetext="" role="slider"></sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
''');
|
||||
|
||||
|
||||
@ -158,8 +158,10 @@ void _testSemanticRole() {
|
||||
|
||||
tester.expectSemantics('''
|
||||
<sem id="flt-semantic-node-0">
|
||||
<sem-c>
|
||||
<sem id="flt-semantic-node-372"></sem>
|
||||
<sem id="flt-semantic-node-599"></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
tester.updateNode(
|
||||
@ -173,8 +175,10 @@ void _testSemanticRole() {
|
||||
|
||||
tester.expectSemantics('''
|
||||
<sem id="flt-semantic-node-0">
|
||||
<sem-c>
|
||||
<sem id="flt-semantic-node-372" flt-semantics-identifier="test-id-123"></sem>
|
||||
<sem id="flt-semantic-node-599"></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
tester.updateNode(
|
||||
@ -189,9 +193,11 @@ void _testSemanticRole() {
|
||||
|
||||
tester.expectSemantics('''
|
||||
<sem id="flt-semantic-node-0">
|
||||
<sem-c>
|
||||
<sem id="flt-semantic-node-372"></sem>
|
||||
<sem id="flt-semantic-node-599" flt-semantics-identifier="test-id-211"></sem>
|
||||
<sem id="flt-semantic-node-612" flt-semantics-identifier="test-id-333"></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
});
|
||||
}
|
||||
@ -514,7 +520,9 @@ void _testEngineSemanticsOwner() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem><span>Hello</span></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
// Update
|
||||
@ -522,7 +530,9 @@ void _testEngineSemanticsOwner() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem><span>World</span></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
// Remove
|
||||
@ -530,7 +540,9 @@ void _testEngineSemanticsOwner() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
semantics().semanticsEnabled = false;
|
||||
@ -551,7 +563,9 @@ void _testEngineSemanticsOwner() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem><span>Hello</span></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
// Update
|
||||
@ -564,7 +578,9 @@ void _testEngineSemanticsOwner() {
|
||||
expect(tree[1]!.element.tagName.toLowerCase(), 'a');
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<a style="display: block;">Hello</a>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
expect(existingParent, tree[1]!.element.parent);
|
||||
|
||||
@ -586,7 +602,9 @@ void _testEngineSemanticsOwner() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem><span>tooltip</span></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
// Update
|
||||
@ -594,7 +612,9 @@ void _testEngineSemanticsOwner() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem><span>tooltip\nHello</span></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
// Remove
|
||||
@ -602,7 +622,9 @@ void _testEngineSemanticsOwner() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
semantics().semanticsEnabled = false;
|
||||
@ -827,7 +849,7 @@ void _testHeader() {
|
||||
|
||||
owner().updateSemantics(builder.build());
|
||||
expectSemanticsTree(owner(), '''
|
||||
<header aria-label="Header of the page"><sem></sem></header>
|
||||
<header aria-label="Header of the page"><sem-c><sem></sem></sem-c></header>
|
||||
''');
|
||||
|
||||
semantics().semanticsEnabled = false;
|
||||
@ -934,7 +956,7 @@ label hint''');
|
||||
}
|
||||
|
||||
void _testContainer() {
|
||||
test('child node has no transform when there is no rect offset', () async {
|
||||
test('container node has no transform when there is no rect offset', () async {
|
||||
semantics()
|
||||
..debugOverrideTimestampFunction(() => _testTime)
|
||||
..semanticsEnabled = true;
|
||||
@ -951,30 +973,35 @@ void _testContainer() {
|
||||
updateNode(builder, id: 1, transform: Matrix4.identity().toFloat64(), rect: zeroOffsetRect);
|
||||
|
||||
owner().updateSemantics(builder.build());
|
||||
expectSemanticsTree(owner(), '''<sem><sem></sem></sem>''');
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
final DomElement parentElement = owner().semanticsHost.querySelector('flt-semantics')!;
|
||||
final DomElement childElement = owner().semanticsHost.querySelector('#flt-semantic-node-1')!;
|
||||
final DomElement container = owner().semanticsHost.querySelector('flt-semantics-container')!;
|
||||
|
||||
if (isMacOrIOS) {
|
||||
expect(parentElement.style.top, '0px');
|
||||
expect(parentElement.style.left, '0px');
|
||||
expect(childElement.style.top, '0px');
|
||||
expect(childElement.style.left, '0px');
|
||||
expect(container.style.top, '0px');
|
||||
expect(container.style.left, '0px');
|
||||
} else {
|
||||
expect(parentElement.style.top, '');
|
||||
expect(parentElement.style.left, '');
|
||||
expect(childElement.style.top, '');
|
||||
expect(childElement.style.left, '');
|
||||
expect(container.style.top, '');
|
||||
expect(container.style.left, '');
|
||||
}
|
||||
expect(parentElement.style.transform, '');
|
||||
expect(parentElement.style.transformOrigin, '');
|
||||
expect(childElement.style.transform, '');
|
||||
expect(childElement.style.transformOrigin, '');
|
||||
expect(container.style.transform, '');
|
||||
expect(container.style.transformOrigin, '');
|
||||
semantics().semanticsEnabled = false;
|
||||
});
|
||||
|
||||
test('child node transform compensates for parent rect offset', () async {
|
||||
test('container node compensates for rect offset', () async {
|
||||
semantics()
|
||||
..debugOverrideTimestampFunction(() => _testTime)
|
||||
..semanticsEnabled = true;
|
||||
@ -991,14 +1018,19 @@ void _testContainer() {
|
||||
builder,
|
||||
id: 1,
|
||||
transform: Matrix4.identity().toFloat64(),
|
||||
rect: const ui.Rect.fromLTRB(0, 0, 5, 5),
|
||||
rect: const ui.Rect.fromLTRB(10, 10, 20, 20),
|
||||
);
|
||||
|
||||
owner().updateSemantics(builder.build());
|
||||
expectSemanticsTree(owner(), '''<sem><sem></sem></sem>''');
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
final DomElement parentElement = owner().semanticsHost.querySelector('flt-semantics')!;
|
||||
final DomElement childElement = owner().semanticsHost.querySelector('#flt-semantic-node-1')!;
|
||||
final DomElement container = owner().semanticsHost.querySelector('flt-semantics-container')!;
|
||||
|
||||
expect(parentElement.style.transform, 'matrix(1, 0, 0, 1, 10, 10)');
|
||||
if (isSafari) {
|
||||
@ -1007,103 +1039,63 @@ void _testContainer() {
|
||||
parentElement.style.transformOrigin,
|
||||
anyOf(contains('0px 0px 0px'), contains('0px 0px')),
|
||||
);
|
||||
expect(
|
||||
childElement.style.transformOrigin,
|
||||
anyOf(contains('0px 0px 0px'), contains('0px 0px')),
|
||||
);
|
||||
} else {
|
||||
expect(parentElement.style.transformOrigin, '0px 0px 0px');
|
||||
expect(childElement.style.transformOrigin, '0px 0px 0px');
|
||||
}
|
||||
expect(childElement.style.transform, 'matrix(1, 0, 0, 1, -10, -10)');
|
||||
expect(childElement.style.left == '0px' || childElement.style.left == '', isTrue);
|
||||
expect(childElement.style.top == '0px' || childElement.style.top == '', isTrue);
|
||||
|
||||
expect(container.style.top, '-10px');
|
||||
expect(container.style.left, '-10px');
|
||||
semantics().semanticsEnabled = false;
|
||||
});
|
||||
|
||||
test(
|
||||
'child node transform compensates for parent rect offset when parent rect changed',
|
||||
() async {
|
||||
semantics()
|
||||
..debugOverrideTimestampFunction(() => _testTime)
|
||||
..semanticsEnabled = true;
|
||||
test('0 offsets are not removed for voiceover', () async {
|
||||
semantics()
|
||||
..debugOverrideTimestampFunction(() => _testTime)
|
||||
..semanticsEnabled = true;
|
||||
|
||||
final ui.SemanticsUpdateBuilder builder = ui.SemanticsUpdateBuilder();
|
||||
updateNode(
|
||||
builder,
|
||||
transform: Matrix4.identity().toFloat64(),
|
||||
rect: const ui.Rect.fromLTRB(10, 10, 20, 20),
|
||||
childrenInHitTestOrder: Int32List.fromList(<int>[1]),
|
||||
childrenInTraversalOrder: Int32List.fromList(<int>[1]),
|
||||
);
|
||||
updateNode(
|
||||
builder,
|
||||
id: 1,
|
||||
transform: Matrix4.identity().toFloat64(),
|
||||
rect: const ui.Rect.fromLTRB(0, 0, 5, 5),
|
||||
);
|
||||
final ui.SemanticsUpdateBuilder builder = ui.SemanticsUpdateBuilder();
|
||||
updateNode(
|
||||
builder,
|
||||
transform: Matrix4.identity().toFloat64(),
|
||||
rect: const ui.Rect.fromLTRB(0, 0, 20, 20),
|
||||
childrenInHitTestOrder: Int32List.fromList(<int>[1]),
|
||||
childrenInTraversalOrder: Int32List.fromList(<int>[1]),
|
||||
);
|
||||
updateNode(
|
||||
builder,
|
||||
id: 1,
|
||||
transform: Matrix4.identity().toFloat64(),
|
||||
rect: const ui.Rect.fromLTRB(10, 10, 20, 20),
|
||||
);
|
||||
|
||||
owner().updateSemantics(builder.build());
|
||||
expectSemanticsTree(owner(), '''<sem><sem></sem></sem>''');
|
||||
owner().updateSemantics(builder.build());
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
final DomElement parentElement = owner().semanticsHost.querySelector('flt-semantics')!;
|
||||
final DomElement childElement = owner().semanticsHost.querySelector('#flt-semantic-node-1')!;
|
||||
final DomElement parentElement = owner().semanticsHost.querySelector('flt-semantics')!;
|
||||
final DomElement container = owner().semanticsHost.querySelector('flt-semantics-container')!;
|
||||
|
||||
expect(parentElement.style.transform, 'matrix(1, 0, 0, 1, 10, 10)');
|
||||
if (isSafari) {
|
||||
// macOS 13 returns different values than macOS 12.
|
||||
expect(
|
||||
parentElement.style.transformOrigin,
|
||||
anyOf(contains('0px 0px 0px'), contains('0px 0px')),
|
||||
);
|
||||
expect(
|
||||
childElement.style.transformOrigin,
|
||||
anyOf(contains('0px 0px 0px'), contains('0px 0px')),
|
||||
);
|
||||
} else {
|
||||
expect(parentElement.style.transformOrigin, '0px 0px 0px');
|
||||
expect(childElement.style.transformOrigin, '0px 0px 0px');
|
||||
}
|
||||
expect(childElement.style.transform, 'matrix(1, 0, 0, 1, -10, -10)');
|
||||
expect(childElement.style.left == '0px' || childElement.style.left == '', isTrue);
|
||||
expect(childElement.style.top == '0px' || childElement.style.top == '', isTrue);
|
||||
if (isMacOrIOS) {
|
||||
expect(parentElement.style.top, '0px');
|
||||
expect(parentElement.style.left, '0px');
|
||||
expect(container.style.top, '0px');
|
||||
expect(container.style.left, '0px');
|
||||
} else {
|
||||
expect(parentElement.style.top, '');
|
||||
expect(parentElement.style.left, '');
|
||||
expect(container.style.top, '');
|
||||
expect(container.style.left, '');
|
||||
}
|
||||
expect(parentElement.style.transform, '');
|
||||
expect(parentElement.style.transformOrigin, '');
|
||||
expect(container.style.transform, '');
|
||||
expect(container.style.transformOrigin, '');
|
||||
|
||||
final ui.SemanticsUpdateBuilder builder2 = ui.SemanticsUpdateBuilder();
|
||||
|
||||
updateNode(
|
||||
builder2,
|
||||
transform: Matrix4.identity().toFloat64(),
|
||||
rect: const ui.Rect.fromLTRB(33, 33, 20, 20),
|
||||
childrenInHitTestOrder: Int32List.fromList(<int>[1]),
|
||||
childrenInTraversalOrder: Int32List.fromList(<int>[1]),
|
||||
);
|
||||
|
||||
owner().updateSemantics(builder2.build());
|
||||
expectSemanticsTree(owner(), '''<sem><sem></sem></sem>''');
|
||||
|
||||
expect(parentElement.style.transform, 'matrix(1, 0, 0, 1, 33, 33)');
|
||||
if (isSafari) {
|
||||
// macOS 13 returns different values than macOS 12.
|
||||
expect(
|
||||
parentElement.style.transformOrigin,
|
||||
anyOf(contains('0px 0px 0px'), contains('0px 0px')),
|
||||
);
|
||||
expect(
|
||||
childElement.style.transformOrigin,
|
||||
anyOf(contains('0px 0px 0px'), contains('0px 0px')),
|
||||
);
|
||||
} else {
|
||||
expect(parentElement.style.transformOrigin, '0px 0px 0px');
|
||||
expect(childElement.style.transformOrigin, '0px 0px 0px');
|
||||
}
|
||||
expect(childElement.style.transform, 'matrix(1, 0, 0, 1, -33, -33)');
|
||||
expect(childElement.style.left == '0px' || childElement.style.left == '', isTrue);
|
||||
expect(childElement.style.top == '0px' || childElement.style.top == '', isTrue);
|
||||
|
||||
semantics().semanticsEnabled = false;
|
||||
},
|
||||
);
|
||||
semantics().semanticsEnabled = false;
|
||||
});
|
||||
|
||||
test('renders in traversal order, hit-tests in reverse z-index order', () async {
|
||||
semantics()
|
||||
@ -1126,10 +1118,12 @@ void _testContainer() {
|
||||
owner().updateSemantics(builder.build());
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem style="z-index: 4"></sem>
|
||||
<sem style="z-index: 2"></sem>
|
||||
<sem style="z-index: 3"></sem>
|
||||
<sem style="z-index: 1"></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
}
|
||||
|
||||
@ -1144,10 +1138,12 @@ void _testContainer() {
|
||||
owner().updateSemantics(builder.build());
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem style="z-index: 4"></sem>
|
||||
<sem style="z-index: 3"></sem>
|
||||
<sem style="z-index: 2"></sem>
|
||||
<sem style="z-index: 1"></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
}
|
||||
|
||||
@ -1162,10 +1158,12 @@ void _testContainer() {
|
||||
owner().updateSemantics(builder.build());
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem style="z-index: 1"></sem>
|
||||
<sem style="z-index: 3"></sem>
|
||||
<sem style="z-index: 2"></sem>
|
||||
<sem style="z-index: 4"></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
}
|
||||
|
||||
@ -1180,10 +1178,12 @@ void _testContainer() {
|
||||
owner().updateSemantics(builder.build());
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem style="z-index: 2"></sem>
|
||||
<sem style="z-index: 4"></sem>
|
||||
<sem style="z-index: 1"></sem>
|
||||
<sem style="z-index: 3"></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
}
|
||||
|
||||
@ -1207,8 +1207,10 @@ void _testContainer() {
|
||||
owner().updateSemantics(builder.build());
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem style="z-index: 2"></sem>
|
||||
<sem style="z-index: 1"></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
final DomElement root = owner().semanticsHost.querySelector('#flt-semantic-node-0')!;
|
||||
@ -1241,8 +1243,10 @@ void _testContainer() {
|
||||
owner().updateSemantics(builder.build());
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem style="z-index: 2"></sem>
|
||||
<sem style="z-index: 1"></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
final DomElement root = owner().semanticsHost.querySelector('#flt-semantic-node-0')!;
|
||||
@ -1250,6 +1254,7 @@ void _testContainer() {
|
||||
|
||||
semantics().semanticsEnabled = false;
|
||||
});
|
||||
|
||||
test('container can be opaque if it is a text field', () async {
|
||||
semantics()
|
||||
..debugOverrideTimestampFunction(() => _testTime)
|
||||
@ -1269,8 +1274,10 @@ void _testContainer() {
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<input>
|
||||
<sem-c>
|
||||
<sem style="z-index: 2"></sem>
|
||||
<sem style="z-index: 1"></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
final DomElement root = owner().semanticsHost.querySelector('#flt-semantic-node-0')!;
|
||||
@ -1311,14 +1318,20 @@ void _testContainer() {
|
||||
owner().updateSemantics(builder.build());
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem style="z-index: 2">
|
||||
<sem-c>
|
||||
<sem style="z-index: 2"></sem>
|
||||
<sem style="z-index: 1"></sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
<sem style="z-index: 1">
|
||||
<sem-c>
|
||||
<sem style="z-index: 2"></sem>
|
||||
<sem style="z-index: 1"></sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
expect(
|
||||
@ -1345,11 +1358,15 @@ void _testContainer() {
|
||||
owner().updateSemantics(builder.build());
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem style="z-index: 2">
|
||||
<sem-c>
|
||||
<sem style="z-index: 3"></sem>
|
||||
<sem style="z-index: 2"></sem>
|
||||
<sem style="z-index: 1"></sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
expect(owner().debugSemanticsTree!.keys.toList(), unorderedEquals(<int>[0, 1, 3, 4, 6]));
|
||||
@ -1435,7 +1452,9 @@ void _testVerticalScrolling() {
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem style="touch-action: none; overflow-y: scroll">
|
||||
<flt-semantics-scroll-overflow></flt-semantics-scroll-overflow>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
final DomElement scrollable = findScrollable(owner());
|
||||
@ -1489,9 +1508,11 @@ void _testVerticalScrolling() {
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem style="touch-action: none; overflow-y: scroll">
|
||||
<flt-semantics-scroll-overflow></flt-semantics-scroll-overflow>
|
||||
<sem-c>
|
||||
<sem style="z-index: 3"></sem>
|
||||
<sem style="z-index: 2"></sem>
|
||||
<sem style="z-index: 1"></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
final DomElement scrollable = owner().debugSemanticsTree![0]!.element;
|
||||
@ -1568,9 +1589,11 @@ void _testVerticalScrolling() {
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem style="touch-action: none; overflow-y: scroll">
|
||||
<flt-semantics-scroll-overflow></flt-semantics-scroll-overflow>
|
||||
<sem-c>
|
||||
<sem style="z-index: 3"></sem>
|
||||
<sem style="z-index: 2"></sem>
|
||||
<sem style="z-index: 1"></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
final DomElement scrollable = owner().debugSemanticsTree![0]!.element;
|
||||
@ -1656,7 +1679,9 @@ void _testHorizontalScrolling() {
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem style="touch-action: none; overflow-x: scroll">
|
||||
<flt-semantics-scroll-overflow></flt-semantics-scroll-overflow>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
final DomElement scrollable = findScrollable(owner());
|
||||
@ -1710,9 +1735,11 @@ void _testHorizontalScrolling() {
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem style="touch-action: none; overflow-x: scroll">
|
||||
<flt-semantics-scroll-overflow></flt-semantics-scroll-overflow>
|
||||
<sem-c>
|
||||
<sem style="z-index: 3"></sem>
|
||||
<sem style="z-index: 2"></sem>
|
||||
<sem style="z-index: 1"></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
final DomElement scrollable = findScrollable(owner());
|
||||
@ -2267,8 +2294,10 @@ void _testCheckables() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem role="radiogroup">
|
||||
<sem-c>
|
||||
<sem aria-checked="false"></sem>
|
||||
<sem aria-checked="true"></sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
''');
|
||||
|
||||
@ -2366,9 +2395,11 @@ void _testSelectables() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
<sem aria-selected="false"></sem>
|
||||
<sem aria-selected="true"></sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
''');
|
||||
|
||||
@ -2393,9 +2424,11 @@ void _testSelectables() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
<sem aria-selected="true"></sem>
|
||||
<sem aria-selected="false"></sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
''');
|
||||
|
||||
@ -2460,9 +2493,11 @@ void _testExpandables() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
<sem aria-expanded="false"></sem>
|
||||
<sem aria-expanded="true"></sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
''');
|
||||
|
||||
@ -2487,9 +2522,11 @@ void _testExpandables() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
<sem aria-expanded="true"></sem>
|
||||
<sem aria-expanded="false"></sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
''');
|
||||
|
||||
@ -2743,7 +2780,9 @@ void _testTappable() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem flt-tappable role="button">
|
||||
<sem-c>
|
||||
<sem flt-tappable role="button"></sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
''');
|
||||
|
||||
@ -2834,7 +2873,9 @@ void _testImage() {
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-img role="img" aria-label="Test Image Label"></sem-img>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
semantics().semanticsEnabled = false;
|
||||
@ -2884,7 +2925,9 @@ void _testImage() {
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-img role="img"></sem-img>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
semantics().semanticsEnabled = false;
|
||||
@ -3101,9 +3144,11 @@ void _testPlatformView() {
|
||||
owner().updateSemantics(builder.build());
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem style="z-index: 3"></sem>
|
||||
<sem style="z-index: 2" aria-owns="flt-pv-0"></sem>
|
||||
<sem style="z-index: 1"></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
final DomElement root = owner().semanticsHost.querySelector('#flt-semantic-node-0')!;
|
||||
@ -3197,7 +3242,7 @@ void _testGroup() {
|
||||
|
||||
owner().updateSemantics(builder.build());
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem role="group" aria-label="this is a label for a group of elements"><sem></sem></sem>
|
||||
<sem role="group" aria-label="this is a label for a group of elements"><sem-c><sem></sem></sem-c></sem>
|
||||
''');
|
||||
|
||||
semantics().semanticsEnabled = false;
|
||||
@ -3229,7 +3274,7 @@ void _testRoute() {
|
||||
|
||||
owner().updateSemantics(builder.build());
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem role="dialog" aria-label="this is a route label"><sem></sem></sem>
|
||||
<sem role="dialog" aria-label="this is a route label"><sem-c><sem></sem></sem-c></sem>
|
||||
''');
|
||||
|
||||
expect(owner().debugSemanticsTree![0]!.semanticRole?.kind, EngineSemanticsRole.route);
|
||||
@ -3268,7 +3313,7 @@ void _testRoute() {
|
||||
|
||||
// But still sets the dialog role.
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem role="dialog" aria-label=""><sem></sem></sem>
|
||||
<sem role="dialog" aria-label=""><sem-c><sem></sem></sem-c></sem>
|
||||
''');
|
||||
|
||||
expect(owner().debugSemanticsTree![0]!.semanticRole?.kind, EngineSemanticsRole.route);
|
||||
@ -3300,9 +3345,13 @@ void _testRoute() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem role="dialog" aria-describedby="flt-semantic-node-2">
|
||||
<sem-c>
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem><span>$label</span></sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
''');
|
||||
}
|
||||
@ -3362,9 +3411,13 @@ void _testRoute() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem><span>Hello</span></sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
''');
|
||||
|
||||
@ -3541,12 +3594,16 @@ void _testRoute() {
|
||||
|
||||
tester.expectSemantics('''
|
||||
<flt-semantics>
|
||||
<flt-semantics-container>
|
||||
<flt-semantics>
|
||||
<flt-semantics-container>
|
||||
<flt-semantics id="flt-semantic-node-2">
|
||||
<span tabindex="-1">Heading</span>
|
||||
</flt-semantics>
|
||||
<flt-semantics role="button" tabindex="0" flt-tappable="">Click me!</flt-semantics>
|
||||
</flt-semantics-container>
|
||||
</flt-semantics>
|
||||
</flt-semantics-container>
|
||||
</flt-semantics>''');
|
||||
|
||||
final DomElement span = owner().debugSemanticsTree![2]!.element.querySelectorAll('span').single;
|
||||
@ -3646,9 +3703,13 @@ void _testDialogs() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem role="dialog" aria-describedby="flt-semantic-node-2">
|
||||
<sem-c>
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem><span>$label</span></sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
''');
|
||||
}
|
||||
@ -3807,7 +3868,9 @@ void _testFocusable() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem><span>focusable text</span></sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
''');
|
||||
|
||||
@ -4198,9 +4261,11 @@ void _testRequirable() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
<sem aria-required="false"></sem>
|
||||
<sem aria-required="true"></sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
''');
|
||||
|
||||
@ -4225,9 +4290,11 @@ void _testRequirable() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
<sem aria-required="true"></sem>
|
||||
<sem aria-required="false"></sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
''');
|
||||
|
||||
@ -4238,9 +4305,11 @@ void _testRequirable() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
<sem></sem>
|
||||
<sem></sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
''');
|
||||
|
||||
|
||||
@ -110,7 +110,9 @@ Future<void> testMain() async {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem aria-label="I am a parent" role="group">
|
||||
<sem-c>
|
||||
<sem><span>I am a child</span></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
semantics().semanticsEnabled = false;
|
||||
@ -157,7 +159,9 @@ Future<void> testMain() async {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem aria-label="I am a parent" role="group">
|
||||
<sem-c>
|
||||
<sem><span>I am a child</span></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user