Reland #163662 with some changes 1. when a node's role changes and reparenting it, append children to it 2. Merge optimization from /pull/165352 fix: https://github.com/flutter/flutter/issues/45205 ## 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
This commit is contained in:
parent
66df85a254
commit
efa81a7af6
@ -70,6 +70,7 @@ class SemanticScrollable extends SemanticRole {
|
||||
final bool doScrollForward = _domScrollPosition > _effectiveNeutralScrollPosition;
|
||||
_neutralizeDomScrollPosition();
|
||||
semanticsObject.recomputePositionAndSize();
|
||||
semanticsObject.updateChildrenPositionAndSize();
|
||||
|
||||
final int semanticsId = semanticsObject.id;
|
||||
if (doScrollForward) {
|
||||
@ -131,6 +132,7 @@ class SemanticScrollable extends SemanticRole {
|
||||
semanticsObject.owner.addOneTimePostUpdateCallback(() {
|
||||
_neutralizeDomScrollPosition();
|
||||
semanticsObject.recomputePositionAndSize();
|
||||
semanticsObject.updateChildrenPositionAndSize();
|
||||
});
|
||||
|
||||
if (_scrollListener == null) {
|
||||
@ -203,8 +205,8 @@ class SemanticScrollable extends SemanticRole {
|
||||
// Read back because the effective value depends on the amount of content.
|
||||
_effectiveNeutralScrollPosition = element.scrollTop.toInt();
|
||||
semanticsObject
|
||||
..verticalContainerAdjustment = _effectiveNeutralScrollPosition.toDouble()
|
||||
..horizontalContainerAdjustment = 0.0;
|
||||
..verticalScrollAdjustment = _effectiveNeutralScrollPosition.toDouble()
|
||||
..horizontalScrollAdjustment = 0.0;
|
||||
} else {
|
||||
// Place the _scrollOverflowElement at the end of the content and
|
||||
// make sure that when we neutralize the scrolling position,
|
||||
@ -219,8 +221,8 @@ class SemanticScrollable extends SemanticRole {
|
||||
// Read back because the effective value depends on the amount of content.
|
||||
_effectiveNeutralScrollPosition = element.scrollLeft.toInt();
|
||||
semanticsObject
|
||||
..verticalContainerAdjustment = 0.0
|
||||
..horizontalContainerAdjustment = _effectiveNeutralScrollPosition.toDouble();
|
||||
..verticalScrollAdjustment = 0.0
|
||||
..horizontalScrollAdjustment = _effectiveNeutralScrollPosition.toDouble();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1383,33 +1383,6 @@ 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
|
||||
@ -1677,12 +1650,6 @@ class SemanticsObject {
|
||||
// Apply updates to the DOM.
|
||||
_updateRole();
|
||||
|
||||
// All properties that affect positioning and sizing are checked together
|
||||
// any one of them triggers position and size recomputation.
|
||||
if (isRectDirty || isTransformDirty || isScrollPositionDirty) {
|
||||
recomputePositionAndSize();
|
||||
}
|
||||
|
||||
if (semanticRole!.acceptsPointerEvents) {
|
||||
element.style.pointerEvents = 'all';
|
||||
} else {
|
||||
@ -1710,22 +1677,15 @@ 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;
|
||||
}
|
||||
@ -1734,7 +1694,6 @@ class SemanticsObject {
|
||||
final Int32List childrenInTraversalOrder = _childrenInTraversalOrder!;
|
||||
final Int32List childrenInHitTestOrder = _childrenInHitTestOrder!;
|
||||
final int childCount = childrenInHitTestOrder.length;
|
||||
final DomElement? containerElement = getOrCreateChildContainer();
|
||||
|
||||
assert(childrenInTraversalOrder.length == childrenInHitTestOrder.length);
|
||||
|
||||
@ -1766,7 +1725,7 @@ class SemanticsObject {
|
||||
// Trivial case: previous list was empty => just populate the container.
|
||||
if (_currentChildrenInRenderOrder == null || _currentChildrenInRenderOrder!.isEmpty) {
|
||||
for (final SemanticsObject child in childrenInRenderOrder) {
|
||||
containerElement!.append(child.element);
|
||||
element.append(child.element);
|
||||
owner._attachObject(parent: this, child: child);
|
||||
}
|
||||
_currentChildrenInRenderOrder = childrenInRenderOrder;
|
||||
@ -1850,9 +1809,9 @@ class SemanticsObject {
|
||||
final SemanticsObject child = childrenInRenderOrder[i];
|
||||
if (!stationaryIds.contains(child.id)) {
|
||||
if (refNode == null) {
|
||||
containerElement!.append(child.element);
|
||||
element.append(child.element);
|
||||
} else {
|
||||
containerElement!.insertBefore(child.element, refNode);
|
||||
element.insertBefore(child.element, refNode);
|
||||
}
|
||||
owner._attachObject(parent: this, child: child);
|
||||
} else {
|
||||
@ -2030,9 +1989,10 @@ class SemanticsObject {
|
||||
|
||||
// Reparent element.
|
||||
if (previousElement != element) {
|
||||
final DomElement? container = _childContainerElement;
|
||||
if (container != null) {
|
||||
element.append(container);
|
||||
if (_currentChildrenInRenderOrder != null) {
|
||||
for (final child in _currentChildrenInRenderOrder!) {
|
||||
element.append(child.element);
|
||||
}
|
||||
}
|
||||
final DomElement? parent = previousElement?.parent;
|
||||
if (parent != null) {
|
||||
@ -2127,32 +2087,53 @@ 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 child container.
|
||||
/// Role-specific adjustment of the vertical position of the children.
|
||||
///
|
||||
/// This is used, for example, by the [SemanticScrollable] to compensate for the
|
||||
/// `scrollTop` offset in the DOM.
|
||||
///
|
||||
/// This field must not be null.
|
||||
double verticalContainerAdjustment = 0.0;
|
||||
double verticalScrollAdjustment = 0.0;
|
||||
|
||||
/// Role-specific adjustment of the horizontal position of the child
|
||||
/// container.
|
||||
/// Role-specific adjustment of the horizontal position of children.
|
||||
///
|
||||
/// This is used, for example, by the [SemanticScrollable] to compensate for the
|
||||
/// `scrollLeft` offset in the DOM.
|
||||
///
|
||||
/// This field must not be null.
|
||||
double horizontalContainerAdjustment = 0.0;
|
||||
double horizontalScrollAdjustment = 0.0;
|
||||
|
||||
/// Computes the size and position of [element] and, if this element
|
||||
/// [hasChildren], of [getOrCreateChildContainer].
|
||||
double verticalAdjustmentFromParent = 0.0;
|
||||
double horizontalAdjustmentFromParent = 0.0;
|
||||
|
||||
/// If this element [hasChildren], computes the parent adjustment for each child.
|
||||
void recomputeChildrenAdjustment(Set<SemanticsObject> dirtyNodes) {
|
||||
if (!hasChildren) {
|
||||
return;
|
||||
}
|
||||
// If this node has children, we need to compensate for the parent's rect and
|
||||
// pass down the scroll adjustments.
|
||||
final double translateX = -_rect!.left + horizontalScrollAdjustment;
|
||||
final double translateY = -_rect!.top + verticalScrollAdjustment;
|
||||
|
||||
for (final childIndex in _childrenInTraversalOrder!) {
|
||||
final child = owner._semanticsTree[childIndex]!;
|
||||
|
||||
if (child.horizontalAdjustmentFromParent != translateX ||
|
||||
child.verticalAdjustmentFromParent != translateY) {
|
||||
child.horizontalAdjustmentFromParent = translateX;
|
||||
child.verticalAdjustmentFromParent = translateY;
|
||||
dirtyNodes.add(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the size and position of [element]
|
||||
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 =
|
||||
@ -2160,27 +2141,25 @@ class SemanticsObject {
|
||||
|
||||
if (hasZeroRectOffset &&
|
||||
hasIdentityTransform &&
|
||||
verticalContainerAdjustment == 0.0 &&
|
||||
horizontalContainerAdjustment == 0.0) {
|
||||
verticalAdjustmentFromParent == 0.0 &&
|
||||
horizontalAdjustmentFromParent == 0.0) {
|
||||
_clearSemanticElementTransform(element);
|
||||
if (containerElement != null) {
|
||||
_clearSemanticElementTransform(containerElement);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
late Matrix4 effectiveTransform;
|
||||
bool effectiveTransformIsIdentity = true;
|
||||
if (!hasZeroRectOffset) {
|
||||
|
||||
final double left = _rect!.left + horizontalAdjustmentFromParent;
|
||||
final double top = _rect!.top + verticalAdjustmentFromParent;
|
||||
|
||||
if (left != 0.0 || top != 0.0) {
|
||||
if (transform == null) {
|
||||
final double left = _rect!.left;
|
||||
final double top = _rect!.top;
|
||||
effectiveTransform = Matrix4.translationValues(left, top, 0.0);
|
||||
effectiveTransformIsIdentity = left == 0.0 && top == 0.0;
|
||||
effectiveTransformIsIdentity = false;
|
||||
} else {
|
||||
// Clone to avoid mutating _transform.
|
||||
effectiveTransform =
|
||||
Matrix4.fromFloat32List(transform).clone()..translate(_rect!.left, _rect!.top);
|
||||
effectiveTransform = Matrix4.fromFloat32List(transform).clone()..translate(left, top);
|
||||
effectiveTransformIsIdentity = effectiveTransform.isIdentity();
|
||||
}
|
||||
} else if (!hasIdentityTransform) {
|
||||
@ -2195,19 +2174,15 @@ class SemanticsObject {
|
||||
} else {
|
||||
_clearSemanticElementTransform(element);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/// Computes the size and position of children.
|
||||
void updateChildrenPositionAndSize() {
|
||||
final Set<SemanticsObject> dirtyNodes = <SemanticsObject>{};
|
||||
recomputeChildrenAdjustment(dirtyNodes);
|
||||
|
||||
for (final node in dirtyNodes) {
|
||||
node.recomputePositionAndSize();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2769,7 +2744,7 @@ class EngineSemanticsOwner {
|
||||
removals.add(node);
|
||||
} else {
|
||||
assert(node._parent == parent);
|
||||
assert(node.element.parentNode == parent._childContainerElement);
|
||||
assert(node.element.parentNode == parent.element);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
@ -2872,14 +2847,29 @@ class EngineSemanticsOwner {
|
||||
object.updateSelf(nodeUpdate);
|
||||
}
|
||||
|
||||
final Set<SemanticsObject> nodesWithDirtyPositionsAndSizes = <SemanticsObject>{};
|
||||
// Second, fix the tree structure. This is moved out into its own loop,
|
||||
// because each object's own information must be updated first.
|
||||
for (final SemanticsNodeUpdate nodeUpdate in nodeUpdates) {
|
||||
final SemanticsObject object = _semanticsTree[nodeUpdate.id]!;
|
||||
object.updateChildren();
|
||||
|
||||
if (object.isRectDirty ||
|
||||
object.isTransformDirty ||
|
||||
object.isScrollPositionDirty ||
|
||||
object.isChildrenInTraversalOrderDirty) {
|
||||
nodesWithDirtyPositionsAndSizes.add(object);
|
||||
|
||||
object.recomputeChildrenAdjustment(nodesWithDirtyPositionsAndSizes);
|
||||
}
|
||||
|
||||
object._dirtyFields = 0;
|
||||
}
|
||||
|
||||
for (final node in nodesWithDirtyPositionsAndSizes) {
|
||||
node.recomputePositionAndSize();
|
||||
}
|
||||
|
||||
final SemanticsObject root = _semanticsTree[0]!;
|
||||
if (_rootSemanticsElement == null) {
|
||||
_rootSemanticsElement = root.element;
|
||||
@ -2913,9 +2903,6 @@ 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,7 +274,6 @@ 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,15 +99,11 @@ 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>
|
||||
''');
|
||||
|
||||
|
@ -161,10 +161,8 @@ 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(
|
||||
@ -178,10 +176,8 @@ 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(
|
||||
@ -196,11 +192,9 @@ 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>''');
|
||||
});
|
||||
}
|
||||
@ -523,9 +517,7 @@ void _testEngineSemanticsOwner() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem><span>Hello</span></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
// Update
|
||||
@ -533,9 +525,7 @@ void _testEngineSemanticsOwner() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem><span>World</span></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
// Remove
|
||||
@ -543,9 +533,7 @@ void _testEngineSemanticsOwner() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
semantics().semanticsEnabled = false;
|
||||
@ -566,9 +554,7 @@ void _testEngineSemanticsOwner() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem><span>Hello</span></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
// Update
|
||||
@ -581,9 +567,7 @@ 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);
|
||||
|
||||
@ -605,9 +589,7 @@ void _testEngineSemanticsOwner() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem><span>tooltip</span></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
// Update
|
||||
@ -615,9 +597,7 @@ void _testEngineSemanticsOwner() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem><span>tooltip\nHello</span></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
// Remove
|
||||
@ -625,9 +605,7 @@ void _testEngineSemanticsOwner() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
semantics().semanticsEnabled = false;
|
||||
@ -852,7 +830,7 @@ void _testHeader() {
|
||||
|
||||
owner().updateSemantics(builder.build());
|
||||
expectSemanticsTree(owner(), '''
|
||||
<header aria-label="Header of the page"><sem-c><sem></sem></sem-c></header>
|
||||
<header aria-label="Header of the page"><sem></sem></header>
|
||||
''');
|
||||
|
||||
semantics().semanticsEnabled = false;
|
||||
@ -959,7 +937,7 @@ label hint''');
|
||||
}
|
||||
|
||||
void _testContainer() {
|
||||
test('container node has no transform when there is no rect offset', () async {
|
||||
test('child node has no transform when there is no rect offset', () async {
|
||||
semantics()
|
||||
..debugOverrideTimestampFunction(() => _testTime)
|
||||
..semanticsEnabled = true;
|
||||
@ -976,35 +954,30 @@ void _testContainer() {
|
||||
updateNode(builder, id: 1, transform: Matrix4.identity().toFloat64(), rect: zeroOffsetRect);
|
||||
|
||||
owner().updateSemantics(builder.build());
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
expectSemanticsTree(owner(), '''<sem><sem></sem></sem>''');
|
||||
|
||||
final DomElement parentElement = owner().semanticsHost.querySelector('flt-semantics')!;
|
||||
final DomElement container = owner().semanticsHost.querySelector('flt-semantics-container')!;
|
||||
final DomElement childElement = owner().semanticsHost.querySelector('#flt-semantic-node-1')!;
|
||||
|
||||
if (isMacOrIOS) {
|
||||
expect(parentElement.style.top, '0px');
|
||||
expect(parentElement.style.left, '0px');
|
||||
expect(container.style.top, '0px');
|
||||
expect(container.style.left, '0px');
|
||||
expect(childElement.style.top, '0px');
|
||||
expect(childElement.style.left, '0px');
|
||||
} else {
|
||||
expect(parentElement.style.top, '');
|
||||
expect(parentElement.style.left, '');
|
||||
expect(container.style.top, '');
|
||||
expect(container.style.left, '');
|
||||
expect(childElement.style.top, '');
|
||||
expect(childElement.style.left, '');
|
||||
}
|
||||
expect(parentElement.style.transform, '');
|
||||
expect(parentElement.style.transformOrigin, '');
|
||||
expect(container.style.transform, '');
|
||||
expect(container.style.transformOrigin, '');
|
||||
expect(childElement.style.transform, '');
|
||||
expect(childElement.style.transformOrigin, '');
|
||||
semantics().semanticsEnabled = false;
|
||||
});
|
||||
|
||||
test('container node compensates for rect offset', () async {
|
||||
test('child node transform compensates for parent rect offset', () async {
|
||||
semantics()
|
||||
..debugOverrideTimestampFunction(() => _testTime)
|
||||
..semanticsEnabled = true;
|
||||
@ -1021,19 +994,14 @@ void _testContainer() {
|
||||
builder,
|
||||
id: 1,
|
||||
transform: Matrix4.identity().toFloat64(),
|
||||
rect: const ui.Rect.fromLTRB(10, 10, 20, 20),
|
||||
rect: const ui.Rect.fromLTRB(0, 0, 5, 5),
|
||||
);
|
||||
|
||||
owner().updateSemantics(builder.build());
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
expectSemanticsTree(owner(), '''<sem><sem></sem></sem>''');
|
||||
|
||||
final DomElement parentElement = owner().semanticsHost.querySelector('flt-semantics')!;
|
||||
final DomElement container = owner().semanticsHost.querySelector('flt-semantics-container')!;
|
||||
final DomElement childElement = owner().semanticsHost.querySelector('#flt-semantic-node-1')!;
|
||||
|
||||
expect(parentElement.style.transform, 'matrix(1, 0, 0, 1, 10, 10)');
|
||||
if (isSafari) {
|
||||
@ -1042,15 +1010,24 @@ 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(container.style.top, '-10px');
|
||||
expect(container.style.left, '-10px');
|
||||
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);
|
||||
|
||||
semantics().semanticsEnabled = false;
|
||||
});
|
||||
|
||||
test('0 offsets are not removed for voiceover', () async {
|
||||
test(
|
||||
'child node transform compensates for parent rect offset when parent rect changed',
|
||||
() async {
|
||||
semantics()
|
||||
..debugOverrideTimestampFunction(() => _testTime)
|
||||
..semanticsEnabled = true;
|
||||
@ -1059,7 +1036,7 @@ void _testContainer() {
|
||||
updateNode(
|
||||
builder,
|
||||
transform: Matrix4.identity().toFloat64(),
|
||||
rect: const ui.Rect.fromLTRB(0, 0, 20, 20),
|
||||
rect: const ui.Rect.fromLTRB(10, 10, 20, 20),
|
||||
childrenInHitTestOrder: Int32List.fromList(<int>[1]),
|
||||
childrenInTraversalOrder: Int32List.fromList(<int>[1]),
|
||||
);
|
||||
@ -1067,38 +1044,69 @@ void _testContainer() {
|
||||
builder,
|
||||
id: 1,
|
||||
transform: Matrix4.identity().toFloat64(),
|
||||
rect: const ui.Rect.fromLTRB(10, 10, 20, 20),
|
||||
rect: const ui.Rect.fromLTRB(0, 0, 5, 5),
|
||||
);
|
||||
|
||||
owner().updateSemantics(builder.build());
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
expectSemanticsTree(owner(), '''<sem><sem></sem></sem>''');
|
||||
|
||||
final DomElement parentElement = owner().semanticsHost.querySelector('flt-semantics')!;
|
||||
final DomElement container = owner().semanticsHost.querySelector('flt-semantics-container')!;
|
||||
final DomElement childElement = owner().semanticsHost.querySelector('#flt-semantic-node-1')!;
|
||||
|
||||
if (isMacOrIOS) {
|
||||
expect(parentElement.style.top, '0px');
|
||||
expect(parentElement.style.left, '0px');
|
||||
expect(container.style.top, '0px');
|
||||
expect(container.style.left, '0px');
|
||||
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.top, '');
|
||||
expect(parentElement.style.left, '');
|
||||
expect(container.style.top, '');
|
||||
expect(container.style.left, '');
|
||||
expect(parentElement.style.transformOrigin, '0px 0px 0px');
|
||||
expect(childElement.style.transformOrigin, '0px 0px 0px');
|
||||
}
|
||||
expect(parentElement.style.transform, '');
|
||||
expect(parentElement.style.transformOrigin, '');
|
||||
expect(container.style.transform, '');
|
||||
expect(container.style.transformOrigin, '');
|
||||
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);
|
||||
|
||||
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;
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test('renders in traversal order, hit-tests in reverse z-index order', () async {
|
||||
semantics()
|
||||
@ -1121,12 +1129,10 @@ 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>''');
|
||||
}
|
||||
|
||||
@ -1141,12 +1147,10 @@ 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>''');
|
||||
}
|
||||
|
||||
@ -1161,12 +1165,10 @@ 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>''');
|
||||
}
|
||||
|
||||
@ -1181,12 +1183,10 @@ 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>''');
|
||||
}
|
||||
|
||||
@ -1210,10 +1210,8 @@ 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')!;
|
||||
@ -1246,10 +1244,8 @@ 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')!;
|
||||
@ -1257,7 +1253,6 @@ void _testContainer() {
|
||||
|
||||
semantics().semanticsEnabled = false;
|
||||
});
|
||||
|
||||
test('container can be opaque if it is a text field', () async {
|
||||
semantics()
|
||||
..debugOverrideTimestampFunction(() => _testTime)
|
||||
@ -1277,10 +1272,8 @@ 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')!;
|
||||
@ -1321,20 +1314,14 @@ 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(
|
||||
@ -1361,15 +1348,11 @@ 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]));
|
||||
@ -1377,6 +1360,179 @@ void _testContainer() {
|
||||
|
||||
semantics().semanticsEnabled = false;
|
||||
});
|
||||
|
||||
test('node updated with role change', () async {
|
||||
semantics()
|
||||
..debugOverrideTimestampFunction(() => _testTime)
|
||||
..semanticsEnabled = true;
|
||||
|
||||
{
|
||||
final ui.SemanticsUpdateBuilder builder = ui.SemanticsUpdateBuilder();
|
||||
updateNode(
|
||||
builder,
|
||||
childrenInTraversalOrder: Int32List.fromList(<int>[1, 2]),
|
||||
childrenInHitTestOrder: Int32List.fromList(<int>[1, 2]),
|
||||
);
|
||||
updateNode(
|
||||
builder,
|
||||
id: 1,
|
||||
childrenInTraversalOrder: Int32List.fromList(<int>[3, 4]),
|
||||
childrenInHitTestOrder: Int32List.fromList(<int>[3, 4]),
|
||||
);
|
||||
updateNode(
|
||||
builder,
|
||||
id: 2,
|
||||
childrenInTraversalOrder: Int32List.fromList(<int>[5, 6]),
|
||||
childrenInHitTestOrder: Int32List.fromList(<int>[5, 6]),
|
||||
);
|
||||
updateNode(builder, id: 3);
|
||||
updateNode(builder, id: 4);
|
||||
updateNode(builder, id: 5);
|
||||
updateNode(builder, id: 6);
|
||||
|
||||
owner().updateSemantics(builder.build());
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem style="z-index: 2">
|
||||
<sem style="z-index: 2"></sem>
|
||||
<sem style="z-index: 1"></sem>
|
||||
</sem>
|
||||
<sem style="z-index: 1">
|
||||
<sem style="z-index: 2"></sem>
|
||||
<sem style="z-index: 1"></sem>
|
||||
</sem>
|
||||
</sem>''');
|
||||
|
||||
expect(
|
||||
owner().debugSemanticsTree!.keys.toList(),
|
||||
unorderedEquals(<int>[0, 1, 2, 3, 4, 5, 6]),
|
||||
);
|
||||
}
|
||||
|
||||
// update node #2 with a new role
|
||||
{
|
||||
final ui.SemanticsUpdateBuilder builder = ui.SemanticsUpdateBuilder();
|
||||
updateNode(
|
||||
builder,
|
||||
childrenInTraversalOrder: Int32List.fromList(<int>[1, 2]),
|
||||
childrenInHitTestOrder: Int32List.fromList(<int>[1, 2]),
|
||||
);
|
||||
updateNode(
|
||||
builder,
|
||||
id: 1,
|
||||
childrenInTraversalOrder: Int32List.fromList(<int>[3, 4]),
|
||||
childrenInHitTestOrder: Int32List.fromList(<int>[3, 4]),
|
||||
);
|
||||
updateNode(
|
||||
builder,
|
||||
id: 2,
|
||||
role: ui.SemanticsRole.table,
|
||||
childrenInTraversalOrder: Int32List.fromList(<int>[5, 6]),
|
||||
childrenInHitTestOrder: Int32List.fromList(<int>[5, 6]),
|
||||
);
|
||||
updateNode(builder, id: 3);
|
||||
updateNode(builder, id: 4);
|
||||
updateNode(builder, id: 5);
|
||||
updateNode(builder, id: 6);
|
||||
|
||||
owner().updateSemantics(builder.build());
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem style="z-index: 2">
|
||||
<sem style="z-index: 2"></sem>
|
||||
<sem style="z-index: 1"></sem>
|
||||
</sem>
|
||||
<sem style="z-index: 1">
|
||||
<sem style="z-index: 2"></sem>
|
||||
<sem style="z-index: 1"></sem>
|
||||
</sem>
|
||||
</sem>''');
|
||||
|
||||
expect(
|
||||
owner().debugSemanticsTree!.keys.toList(),
|
||||
unorderedEquals(<int>[0, 1, 2, 3, 4, 5, 6]),
|
||||
);
|
||||
}
|
||||
|
||||
semantics().semanticsEnabled = false;
|
||||
});
|
||||
|
||||
test('reparented nodes have correct transform and position', () async {
|
||||
semantics()
|
||||
..debugOverrideTimestampFunction(() => _testTime)
|
||||
..semanticsEnabled = true;
|
||||
{
|
||||
final ui.SemanticsUpdateBuilder builder = ui.SemanticsUpdateBuilder();
|
||||
updateNode(
|
||||
builder,
|
||||
childrenInTraversalOrder: Int32List.fromList(<int>[1, 2]),
|
||||
childrenInHitTestOrder: Int32List.fromList(<int>[1, 2]),
|
||||
rect: const ui.Rect.fromLTRB(11, 11, 111, 111),
|
||||
);
|
||||
updateNode(builder, id: 1, rect: const ui.Rect.fromLTRB(10, 10, 20, 20));
|
||||
updateNode(
|
||||
builder,
|
||||
id: 2,
|
||||
childrenInTraversalOrder: Int32List.fromList(<int>[3]),
|
||||
childrenInHitTestOrder: Int32List.fromList(<int>[3]),
|
||||
rect: const ui.Rect.fromLTRB(22, 22, 222, 222),
|
||||
);
|
||||
|
||||
updateNode(builder, id: 3);
|
||||
|
||||
owner().updateSemantics(builder.build());
|
||||
|
||||
final DomElement childElement = owner().semanticsHost.querySelector('#flt-semantic-node-3')!;
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem style="z-index: 2"></sem>
|
||||
<sem style="z-index: 1">
|
||||
<sem></sem>
|
||||
</sem>
|
||||
</sem>''');
|
||||
|
||||
expect(owner().debugSemanticsTree!.keys.toList(), unorderedEquals(<int>[0, 1, 2, 3]));
|
||||
|
||||
expect(childElement.style.transform, 'matrix(1, 0, 0, 1, -22, -22)');
|
||||
expect(childElement.style.left == '0px' || childElement.style.left == '', isTrue);
|
||||
expect(childElement.style.top == '0px' || childElement.style.top == '', isTrue);
|
||||
}
|
||||
|
||||
// Remove node #2 => expect nodes #2 to be removed and #3 reparented.
|
||||
{
|
||||
final ui.SemanticsUpdateBuilder builder = ui.SemanticsUpdateBuilder();
|
||||
updateNode(
|
||||
builder,
|
||||
childrenInTraversalOrder: Int32List.fromList(<int>[1]),
|
||||
childrenInHitTestOrder: Int32List.fromList(<int>[1]),
|
||||
);
|
||||
updateNode(
|
||||
builder,
|
||||
id: 1,
|
||||
childrenInTraversalOrder: Int32List.fromList(<int>[3]),
|
||||
childrenInHitTestOrder: Int32List.fromList(<int>[3]),
|
||||
rect: const ui.Rect.fromLTRB(11, 11, 111, 111),
|
||||
);
|
||||
updateNode(builder, id: 3);
|
||||
owner().updateSemantics(builder.build());
|
||||
|
||||
final DomElement childElement = owner().semanticsHost.querySelector('#flt-semantic-node-3')!;
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem style="z-index: 2">
|
||||
<sem ></sem>
|
||||
</sem>
|
||||
</sem>''');
|
||||
|
||||
expect(owner().debugSemanticsTree!.keys.toList(), unorderedEquals(<int>[0, 1, 3]));
|
||||
expect(childElement.style.transform, 'matrix(1, 0, 0, 1, -11, -11)');
|
||||
expect(childElement.style.left == '0px' || childElement.style.left == '', isTrue);
|
||||
expect(childElement.style.top == '0px' || childElement.style.top == '', isTrue);
|
||||
}
|
||||
|
||||
semantics().semanticsEnabled = false;
|
||||
});
|
||||
}
|
||||
|
||||
void _testVerticalScrolling() {
|
||||
@ -1455,9 +1611,7 @@ 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());
|
||||
@ -1511,11 +1665,9 @@ 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;
|
||||
@ -1592,11 +1744,9 @@ 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;
|
||||
@ -1682,9 +1832,7 @@ 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());
|
||||
@ -1738,11 +1886,9 @@ 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());
|
||||
@ -2297,10 +2443,8 @@ void _testCheckables() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem role="radiogroup">
|
||||
<sem-c>
|
||||
<sem aria-checked="false"></sem>
|
||||
<sem aria-checked="true"></sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
''');
|
||||
|
||||
@ -2398,11 +2542,9 @@ void _testSelectables() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
<sem aria-selected="false"></sem>
|
||||
<sem aria-selected="true"></sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
''');
|
||||
|
||||
@ -2427,11 +2569,9 @@ void _testSelectables() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
<sem aria-selected="true"></sem>
|
||||
<sem aria-selected="false"></sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
''');
|
||||
|
||||
@ -2496,11 +2636,9 @@ void _testExpandables() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
<sem aria-expanded="false"></sem>
|
||||
<sem aria-expanded="true"></sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
''');
|
||||
|
||||
@ -2525,11 +2663,9 @@ void _testExpandables() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
<sem aria-expanded="true"></sem>
|
||||
<sem aria-expanded="false"></sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
''');
|
||||
|
||||
@ -2783,9 +2919,7 @@ void _testTappable() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem flt-tappable role="button">
|
||||
<sem-c>
|
||||
<sem flt-tappable role="button"></sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
''');
|
||||
|
||||
@ -2876,9 +3010,7 @@ 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;
|
||||
@ -2928,9 +3060,7 @@ void _testImage() {
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-img role="img"></sem-img>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
</sem-c>
|
||||
</sem>''');
|
||||
|
||||
semantics().semanticsEnabled = false;
|
||||
@ -3147,11 +3277,9 @@ 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')!;
|
||||
@ -3245,7 +3373,7 @@ void _testGroup() {
|
||||
|
||||
owner().updateSemantics(builder.build());
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem role="group" aria-label="this is a label for a group of elements"><sem-c><sem></sem></sem-c></sem>
|
||||
<sem role="group" aria-label="this is a label for a group of elements"><sem></sem></sem>
|
||||
''');
|
||||
|
||||
semantics().semanticsEnabled = false;
|
||||
@ -3277,7 +3405,7 @@ void _testRoute() {
|
||||
|
||||
owner().updateSemantics(builder.build());
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem role="dialog" aria-label="this is a route label"><sem-c><sem></sem></sem-c></sem>
|
||||
<sem role="dialog" aria-label="this is a route label"><sem></sem></sem>
|
||||
''');
|
||||
|
||||
expect(owner().debugSemanticsTree![0]!.semanticRole?.kind, EngineSemanticsRole.route);
|
||||
@ -3316,7 +3444,7 @@ void _testRoute() {
|
||||
|
||||
// But still sets the dialog role.
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem role="dialog" aria-label=""><sem-c><sem></sem></sem-c></sem>
|
||||
<sem role="dialog" aria-label=""><sem></sem></sem>
|
||||
''');
|
||||
|
||||
expect(owner().debugSemanticsTree![0]!.semanticRole?.kind, EngineSemanticsRole.route);
|
||||
@ -3348,13 +3476,9 @@ 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>
|
||||
''');
|
||||
}
|
||||
@ -3414,13 +3538,9 @@ void _testRoute() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem><span>Hello</span></sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
''');
|
||||
|
||||
@ -3597,16 +3717,12 @@ 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;
|
||||
@ -3706,13 +3822,9 @@ 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>
|
||||
''');
|
||||
}
|
||||
@ -3871,9 +3983,7 @@ void _testFocusable() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem><span>focusable text</span></sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
''');
|
||||
|
||||
@ -4568,11 +4678,9 @@ void _testRequirable() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
<sem aria-required="false"></sem>
|
||||
<sem aria-required="true"></sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
''');
|
||||
|
||||
@ -4597,11 +4705,9 @@ void _testRequirable() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
<sem aria-required="true"></sem>
|
||||
<sem aria-required="false"></sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
''');
|
||||
|
||||
@ -4612,11 +4718,9 @@ void _testRequirable() {
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
<sem></sem>
|
||||
<sem></sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
''');
|
||||
|
||||
@ -4671,6 +4775,7 @@ void updateNode(
|
||||
int headingLevel = 0,
|
||||
String? linkUrl,
|
||||
List<String>? controlsNodes,
|
||||
ui.SemanticsRole role = ui.SemanticsRole.none,
|
||||
}) {
|
||||
transform ??= Float64List.fromList(Matrix4.identity().storage);
|
||||
childrenInTraversalOrder ??= Int32List(0);
|
||||
@ -4678,6 +4783,7 @@ void updateNode(
|
||||
additionalActions ??= Int32List(0);
|
||||
builder.updateNode(
|
||||
id: id,
|
||||
role: role,
|
||||
flags: flags,
|
||||
actions: actions,
|
||||
maxValueLength: maxValueLength,
|
||||
|
@ -110,9 +110,7 @@ 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;
|
||||
@ -159,9 +157,7 @@ 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