Remove semantics boundary from gesture detector AND MORE (#13983)
`RenderSemanticsGestureHandler` is no longer a semantics boundary, which allows us to correctly mark disabled buttons as disabled without having their semantics size and semantics node id change unexpectedly. Fixes https://github.com/flutter/flutter/issues/12589. Fixes https://github.com/flutter/flutter/issues/11991. See also https://github.com/flutter/flutter/issues/11993. This change also required some refactoring to how we deal with `twoPaneSemantics` scrolling as it previously relied on `RenderSemanticsGestureHandler` being a semantics boundary. This should also make the underlying logic easier to understand. In addition, the following minor changes are included in this PR: * Removal of orphaned and unused `SemanticsConfiguration.isMergingDescendantsIntoOneNode`. * Logic optimizations for `markNeedsSemanticsUpdate` . * Fix for edge case where `MergeSemantics` failed to merge semantics. * Use of emojis to better indicate leaf merging in the printed semantics tree. * Better assert message for adding invisible child semantics nodes. * Make some semantics tests robuster by not relying on creation order of SemanticsNode ids across test boundaries. Fixes https://github.com/flutter/flutter/issues/13943.
This commit is contained in:
parent
33d8a03545
commit
8a6e973739
@ -332,7 +332,7 @@ class _MaterialButtonState extends State<MaterialButton> {
|
||||
child: new Center(
|
||||
widthFactor: 1.0,
|
||||
heightFactor: 1.0,
|
||||
child: new Semantics(button: true, child: widget.child),
|
||||
child: widget.child,
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -352,12 +352,17 @@ class _MaterialButtonState extends State<MaterialButton> {
|
||||
child: contents
|
||||
);
|
||||
}
|
||||
return new ConstrainedBox(
|
||||
constraints: new BoxConstraints(
|
||||
minWidth: widget.minWidth ?? buttonTheme.minWidth,
|
||||
minHeight: height,
|
||||
return new Semantics(
|
||||
container: true,
|
||||
button: true,
|
||||
enabled: widget.enabled,
|
||||
child: new ConstrainedBox(
|
||||
constraints: new BoxConstraints(
|
||||
minWidth: widget.minWidth ?? buttonTheme.minWidth,
|
||||
minHeight: height,
|
||||
),
|
||||
child: contents
|
||||
),
|
||||
child: contents
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -2243,7 +2243,7 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
|
||||
// RenderObject are still up-to date. Therefore, we will later only rebuild
|
||||
// the semantics subtree starting at th identified semantics boundary.
|
||||
|
||||
final bool wasSemanticsBoundary = _cachedSemanticsConfiguration?.isSemanticBoundary == true;
|
||||
final bool wasSemanticsBoundary = _semantics != null && _cachedSemanticsConfiguration?.isSemanticBoundary == true;
|
||||
_cachedSemanticsConfiguration = null;
|
||||
bool isEffectiveSemanticsBoundary = _semanticsConfiguration.isSemanticBoundary && wasSemanticsBoundary;
|
||||
RenderObject node = this;
|
||||
@ -2254,7 +2254,6 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
|
||||
node._needsSemanticsUpdate = true;
|
||||
|
||||
node = node.parent;
|
||||
node._cachedSemanticsConfiguration = null;
|
||||
isEffectiveSemanticsBoundary = node._semanticsConfiguration.isSemanticBoundary;
|
||||
if (isEffectiveSemanticsBoundary && node._semantics == null) {
|
||||
// We have reached a semantics boundary that doesn't own a semantics node.
|
||||
@ -2274,10 +2273,6 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
|
||||
owner._nodesNeedingSemantics.remove(this);
|
||||
}
|
||||
if (!node._needsSemanticsUpdate) {
|
||||
if (node != this) {
|
||||
// Reset for `this` happened above already.
|
||||
node._cachedSemanticsConfiguration = null;
|
||||
}
|
||||
node._needsSemanticsUpdate = true;
|
||||
if (owner != null) {
|
||||
assert(node._semanticsConfiguration.isSemanticBoundary || node.parent is! RenderObject);
|
||||
|
@ -2565,37 +2565,6 @@ class RenderSemanticsGestureHandler extends RenderProxyBox {
|
||||
_onVerticalDragUpdate = onVerticalDragUpdate,
|
||||
super(child);
|
||||
|
||||
/// When a [SemanticsNode] that is a direct child of this object's
|
||||
/// [SemanticsNode] is tagged with [excludeFromScrolling] it will not be
|
||||
/// part of the scrolling area for semantic purposes.
|
||||
///
|
||||
/// This behavior is only active if the [SemanticsNode] of this
|
||||
/// [RenderSemanticsGestureHandler] is tagged with [useTwoPaneSemantics].
|
||||
/// Otherwise, the [excludeFromScrolling] tag is ignored.
|
||||
///
|
||||
/// As an example, a [RenderSliver] that stays on the screen within a
|
||||
/// [Scrollable] even though the user has scrolled past it (e.g. a pinned app
|
||||
/// bar) can tag its [SemanticsNode] with [excludeFromScrolling] to indicate
|
||||
/// that it should no longer be considered for semantic actions related to
|
||||
/// scrolling.
|
||||
static const SemanticsTag excludeFromScrolling = const SemanticsTag('RenderSemanticsGestureHandler.excludeFromScrolling');
|
||||
|
||||
/// If the [SemanticsNode] of this [RenderSemanticsGestureHandler] is tagged
|
||||
/// with [useTwoPaneSemantics], two semantics nodes will be used to represent
|
||||
/// this render object in the semantics tree.
|
||||
///
|
||||
/// Two semantics nodes are necessary to exclude certain child nodes (via the
|
||||
/// [excludeFromScrolling] tag) from the scrollable area for semantic
|
||||
/// purposes.
|
||||
///
|
||||
/// If this tag is used, the first "outer" semantics node is the regular node
|
||||
/// of this object. The second "inner" node is introduced as a child to that
|
||||
/// node. All scrollable children become children of the inner node, which has
|
||||
/// the semantic scrolling logic enabled. All children that have been
|
||||
/// excluded from scrolling with [excludeFromScrolling] are turned into
|
||||
/// children of the outer node.
|
||||
static const SemanticsTag useTwoPaneSemantics = const SemanticsTag('RenderSemanticsGestureHandler.twoPane');
|
||||
|
||||
/// If non-null, the set of actions to allow. Other actions will be omitted,
|
||||
/// even if their callback is provided.
|
||||
///
|
||||
@ -2673,24 +2642,10 @@ class RenderSemanticsGestureHandler extends RenderProxyBox {
|
||||
/// leftwards drag.
|
||||
double scrollFactor;
|
||||
|
||||
bool get _hasHandlers {
|
||||
return onTap != null
|
||||
|| onLongPress != null
|
||||
|| onHorizontalDragUpdate != null
|
||||
|| onVerticalDragUpdate != null;
|
||||
}
|
||||
|
||||
@override
|
||||
void describeSemanticsConfiguration(SemanticsConfiguration config) {
|
||||
super.describeSemanticsConfiguration(config);
|
||||
|
||||
config.isSemanticBoundary = _hasHandlers;
|
||||
|
||||
// TODO(goderbauer): this needs to be set even when there is only potential
|
||||
// for this to become a scroll view.
|
||||
config.explicitChildNodes = onHorizontalDragUpdate != null
|
||||
|| onVerticalDragUpdate != null;
|
||||
|
||||
if (onTap != null && _isValidAction(SemanticsAction.tap))
|
||||
config.onTap = onTap;
|
||||
if (onLongPress != null && _isValidAction(SemanticsAction.longPress))
|
||||
@ -2713,42 +2668,6 @@ class RenderSemanticsGestureHandler extends RenderProxyBox {
|
||||
return validActions == null || validActions.contains(action);
|
||||
}
|
||||
|
||||
SemanticsNode _innerNode;
|
||||
SemanticsNode _annotatedNode;
|
||||
|
||||
/// Sends a [SemanticsEvent] in the context of the [SemanticsNode] that is
|
||||
/// annotated with this object's semantics information.
|
||||
void sendSemanticsEvent(SemanticsEvent event) {
|
||||
_annotatedNode?.sendEvent(event);
|
||||
}
|
||||
|
||||
@override
|
||||
void assembleSemanticsNode(SemanticsNode node, SemanticsConfiguration config, Iterable<SemanticsNode> children) {
|
||||
if (children.isEmpty || !children.first.isTagged(useTwoPaneSemantics)) {
|
||||
_annotatedNode = node;
|
||||
super.assembleSemanticsNode(node, config, children);
|
||||
return;
|
||||
}
|
||||
|
||||
_innerNode ??= new SemanticsNode(showOnScreen: showOnScreen);
|
||||
_innerNode
|
||||
..isMergedIntoParent = node.isPartOfNodeMerging
|
||||
..rect = Offset.zero & node.rect.size;
|
||||
_annotatedNode = _innerNode;
|
||||
|
||||
final List<SemanticsNode> excluded = <SemanticsNode>[_innerNode];
|
||||
final List<SemanticsNode> included = <SemanticsNode>[];
|
||||
for (SemanticsNode child in children) {
|
||||
assert(child.isTagged(useTwoPaneSemantics));
|
||||
if (child.isTagged(excludeFromScrolling))
|
||||
excluded.add(child);
|
||||
else
|
||||
included.add(child);
|
||||
}
|
||||
node.updateWith(config: null, childrenInInversePaintOrder: excluded);
|
||||
_innerNode.updateWith(config: config, childrenInInversePaintOrder: included);
|
||||
}
|
||||
|
||||
void _performSemanticScrollLeft() {
|
||||
if (onHorizontalDragUpdate != null) {
|
||||
final double primaryDelta = size.width * -scrollFactor;
|
||||
|
@ -14,8 +14,8 @@ import 'package:vector_math/vector_math_64.dart';
|
||||
import 'binding.dart';
|
||||
import 'box.dart';
|
||||
import 'object.dart';
|
||||
import 'proxy_box.dart';
|
||||
import 'sliver.dart';
|
||||
import 'viewport.dart';
|
||||
import 'viewport_offset.dart';
|
||||
|
||||
/// A base class for slivers that have a [RenderBox] child which scrolls
|
||||
@ -225,7 +225,7 @@ abstract class RenderSliverPersistentHeader extends RenderSliver with RenderObje
|
||||
super.describeSemanticsConfiguration(config);
|
||||
|
||||
if (_excludeFromSemanticsScrolling)
|
||||
config.addTagForChildren(RenderSemanticsGestureHandler.excludeFromScrolling);
|
||||
config.addTagForChildren(RenderViewport.excludeFromScrolling);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -12,7 +12,6 @@ import 'package:vector_math/vector_math_64.dart';
|
||||
import 'binding.dart';
|
||||
import 'box.dart';
|
||||
import 'object.dart';
|
||||
import 'proxy_box.dart';
|
||||
import 'sliver.dart';
|
||||
import 'viewport_offset.dart';
|
||||
|
||||
@ -91,12 +90,11 @@ abstract class RenderViewportBase<ParentDataClass extends ContainerParentDataMix
|
||||
_crossAxisDirection = crossAxisDirection,
|
||||
_offset = offset;
|
||||
|
||||
|
||||
@override
|
||||
void describeSemanticsConfiguration(SemanticsConfiguration config) {
|
||||
super.describeSemanticsConfiguration(config);
|
||||
|
||||
config.addTagForChildren(RenderSemanticsGestureHandler.useTwoPaneSemantics);
|
||||
config.addTagForChildren(RenderViewport.useTwoPaneSemantics);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -746,6 +744,36 @@ class RenderViewport extends RenderViewportBase<SliverPhysicalContainerParentDat
|
||||
_center = firstChild;
|
||||
}
|
||||
|
||||
/// If a [RenderAbstractViewport] overrides
|
||||
/// [RenderObject.describeSemanticsConfiguration] to add the [SemanticsTag]
|
||||
/// [useTwoPaneSemantics] to its [SemanticsConfiguration], two semantics nodes
|
||||
/// will be used to represent the viewport with its associated scrolling
|
||||
/// actions in the semantics tree.
|
||||
///
|
||||
/// Two semantics nodes (an inner and an outer node) are necessary to exclude
|
||||
/// certain child nodes (via the [excludeFromScrolling] tag) from the
|
||||
/// scrollable area for semantic purposes: The [SemanticsNode]s of children
|
||||
/// that should be excluded from scrolling will be attached to the outer node.
|
||||
/// The semantic scrolling actions and the [SemanticsNode]s of scrollable
|
||||
/// children will be attached to the inner node, which itself is a child of
|
||||
/// the outer node.
|
||||
static const SemanticsTag useTwoPaneSemantics = const SemanticsTag('RenderViewport.twoPane');
|
||||
|
||||
/// When a top-level [SemanticsNode] below a [RenderAbstractViewport] is
|
||||
/// tagged with [excludeFromScrolling] it will not be part of the scrolling
|
||||
/// area for semantic purposes.
|
||||
///
|
||||
/// This behavior is only active if the [RenderAbstractViewport]
|
||||
/// tagged its [SemanticsConfiguration] with [useTwoPaneSemantics].
|
||||
/// Otherwise, the [excludeFromScrolling] tag is ignored.
|
||||
///
|
||||
/// As an example, a [RenderSliver] that stays on the screen within a
|
||||
/// [Scrollable] even though the user has scrolled past it (e.g. a pinned app
|
||||
/// bar) can tag its [SemanticsNode] with [excludeFromScrolling] to indicate
|
||||
/// that it should no longer be considered for semantic actions related to
|
||||
/// scrolling.
|
||||
static const SemanticsTag excludeFromScrolling = const SemanticsTag('RenderViewport.excludeFromScrolling');
|
||||
|
||||
@override
|
||||
void setupParentData(RenderObject child) {
|
||||
if (child.parentData is! SliverPhysicalContainerParentData)
|
||||
|
@ -654,6 +654,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
|
||||
);
|
||||
}
|
||||
}
|
||||
assert(!newChildren.any((SemanticsNode node) => node.isMergedIntoParent) || isPartOfNodeMerging);
|
||||
|
||||
_debugPreviousSnapshot = new List<SemanticsNode>.from(newChildren);
|
||||
|
||||
@ -677,7 +678,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
|
||||
}
|
||||
if (newChildren != null) {
|
||||
for (SemanticsNode child in newChildren) {
|
||||
assert(!child.isInvisible, 'Child with id ${child.id} is invisible and should not be added to tree.');
|
||||
assert(!child.isInvisible, '$child is invisible and should not be added as child of $this.');
|
||||
child._dead = false;
|
||||
}
|
||||
}
|
||||
@ -1072,7 +1073,8 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
|
||||
hideOwner = inDirtyNodes;
|
||||
}
|
||||
properties.add(new DiagnosticsProperty<SemanticsOwner>('owner', owner, level: hideOwner ? DiagnosticLevel.hidden : DiagnosticLevel.info));
|
||||
properties.add(new FlagProperty('isPartOfNodeMerging', value: isPartOfNodeMerging, ifTrue: 'leaf merge'));
|
||||
properties.add(new FlagProperty('isMergedIntoParent', value: isMergedIntoParent, ifTrue: 'merged up ⬆️'));
|
||||
properties.add(new FlagProperty('mergeAllDescendantsIntoThisNode', value: mergeAllDescendantsIntoThisNode, ifTrue: 'merge boundary ⛔️'));
|
||||
final Offset offset = transform != null ? MatrixUtils.getAsTranslation(transform) : null;
|
||||
if (offset != null) {
|
||||
properties.add(new DiagnosticsProperty<Rect>('rect', rect.shift(offset), showName: false));
|
||||
@ -1344,7 +1346,7 @@ class SemanticsConfiguration {
|
||||
bool get isSemanticBoundary => _isSemanticBoundary;
|
||||
bool _isSemanticBoundary = false;
|
||||
set isSemanticBoundary(bool value) {
|
||||
assert(!isMergingDescendantsIntoOneNode || value);
|
||||
assert(!isMergingSemanticsOfDescendants || value);
|
||||
_isSemanticBoundary = value;
|
||||
}
|
||||
|
||||
@ -1380,20 +1382,6 @@ class SemanticsConfiguration {
|
||||
/// determine if a node is previous to this one.
|
||||
bool isBlockingSemanticsOfPreviouslyPaintedNodes = false;
|
||||
|
||||
/// Whether the semantics information of all descendants should be merged
|
||||
/// into the owning [RenderObject] semantics node.
|
||||
///
|
||||
/// When this is set to true the [SemanticsNode] of the owning [RenderObject]
|
||||
/// will not have any children.
|
||||
///
|
||||
/// Setting this to true requires that [isSemanticBoundary] is also true.
|
||||
bool get isMergingDescendantsIntoOneNode => _isMergingDescendantsIntoOneNode;
|
||||
bool _isMergingDescendantsIntoOneNode = false;
|
||||
set isMergingDescendantsIntoOneNode(bool value) {
|
||||
assert(isSemanticBoundary);
|
||||
_isMergingDescendantsIntoOneNode = value;
|
||||
}
|
||||
|
||||
// SEMANTIC ANNOTATIONS
|
||||
// These will end up on [SemanticNode]s generated from
|
||||
// [SemanticsConfiguration]s.
|
||||
@ -1645,9 +1633,12 @@ class SemanticsConfiguration {
|
||||
/// If set to true, the descendants of the owning [RenderObject]'s
|
||||
/// [SemanticsNode] will merge their semantic information into the
|
||||
/// [SemanticsNode] representing the owning [RenderObject].
|
||||
///
|
||||
/// Setting this to true requires that [isSemanticBoundary] is also true.
|
||||
bool get isMergingSemanticsOfDescendants => _isMergingSemanticsOfDescendants;
|
||||
bool _isMergingSemanticsOfDescendants = false;
|
||||
set isMergingSemanticsOfDescendants(bool value) {
|
||||
assert(isSemanticBoundary);
|
||||
_isMergingSemanticsOfDescendants = value;
|
||||
_hasBeenAnnotated = true;
|
||||
}
|
||||
@ -1910,9 +1901,11 @@ class SemanticsConfiguration {
|
||||
/// Returns an exact copy of this configuration.
|
||||
SemanticsConfiguration copy() {
|
||||
return new SemanticsConfiguration()
|
||||
..isSemanticBoundary = isSemanticBoundary
|
||||
.._isSemanticBoundary = _isSemanticBoundary
|
||||
..explicitChildNodes = explicitChildNodes
|
||||
..isBlockingSemanticsOfPreviouslyPaintedNodes = isBlockingSemanticsOfPreviouslyPaintedNodes
|
||||
.._hasBeenAnnotated = _hasBeenAnnotated
|
||||
.._isMergingSemanticsOfDescendants = _isMergingSemanticsOfDescendants
|
||||
.._textDirection = _textDirection
|
||||
.._label = _label
|
||||
.._increasedValue = _increasedValue
|
||||
@ -1920,6 +1913,7 @@ class SemanticsConfiguration {
|
||||
.._decreasedValue = _decreasedValue
|
||||
.._hint = _hint
|
||||
.._flags = _flags
|
||||
.._tagsForChildren = _tagsForChildren
|
||||
.._actionsAsBits = _actionsAsBits
|
||||
.._actions.addAll(_actions);
|
||||
}
|
||||
|
@ -570,21 +570,6 @@ class RawGestureDetectorState extends State<RawGestureDetector> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends a [SemanticsEvent] in the context of the [SemanticsNode] that is
|
||||
/// annotated with this object's semantics information.
|
||||
///
|
||||
/// The event can be interpreted by assistive technologies to provide
|
||||
/// additional feedback to the user about the state of the UI.
|
||||
///
|
||||
/// The event will not be sent if [RawGestureDetector.excludeFromSemantics] is
|
||||
/// set to true.
|
||||
void sendSemanticsEvent(SemanticsEvent event) {
|
||||
if (!widget.excludeFromSemantics) {
|
||||
final RenderSemanticsGestureHandler semanticsGestureHandler = context.findRenderObject();
|
||||
semanticsGestureHandler.sendSemanticsEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
for (GestureRecognizer recognizer in _recognizers.values)
|
||||
|
@ -281,7 +281,8 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin
|
||||
if (_semanticsScrollEventScheduled)
|
||||
return;
|
||||
SchedulerBinding.instance.addPostFrameCallback((Duration timestamp) {
|
||||
_gestureDetectorKey.currentState?.sendSemanticsEvent(new ScrollCompletedSemanticsEvent(
|
||||
final _RenderExcludableScrollSemantics render = _excludableScrollSemanticsKey.currentContext?.findRenderObject();
|
||||
render?.sendSemanticsEvent(new ScrollCompletedSemanticsEvent(
|
||||
axis: position.axis,
|
||||
pixels: position.pixels,
|
||||
minScrollExtent: position.minScrollExtent,
|
||||
@ -332,7 +333,9 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin
|
||||
}
|
||||
|
||||
|
||||
// SEMANTICS ACTIONS
|
||||
// SEMANTICS
|
||||
|
||||
final GlobalKey _excludableScrollSemanticsKey = new GlobalKey();
|
||||
|
||||
@override
|
||||
@protected
|
||||
@ -487,18 +490,24 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin
|
||||
Widget build(BuildContext context) {
|
||||
assert(position != null);
|
||||
// TODO(ianh): Having all these global keys is sad.
|
||||
final Widget result = new RawGestureDetector(
|
||||
key: _gestureDetectorKey,
|
||||
gestures: _gestureRecognizers,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: new IgnorePointer(
|
||||
key: _ignorePointerKey,
|
||||
ignoring: _shouldIgnorePointer,
|
||||
ignoringSemantics: false,
|
||||
child: new _ScrollableScope(
|
||||
scrollable: this,
|
||||
position: position,
|
||||
child: widget.viewportBuilder(context, position),
|
||||
final Widget result = new _ExcludableScrollSemantics(
|
||||
key: _excludableScrollSemanticsKey,
|
||||
child: new RawGestureDetector(
|
||||
key: _gestureDetectorKey,
|
||||
gestures: _gestureRecognizers,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: new Semantics(
|
||||
explicitChildNodes: true,
|
||||
child: new IgnorePointer(
|
||||
key: _ignorePointerKey,
|
||||
ignoring: _shouldIgnorePointer,
|
||||
ignoringSemantics: false,
|
||||
child: new _ScrollableScope(
|
||||
scrollable: this,
|
||||
position: position,
|
||||
child: widget.viewportBuilder(context, position),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -511,3 +520,70 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin
|
||||
description.add(new DiagnosticsProperty<ScrollPosition>('position', position));
|
||||
}
|
||||
}
|
||||
|
||||
/// With [_ExcludableScrollSemantics] certain child [SemanticsNode]s can be
|
||||
/// excluded from the scrollable area for semantics purposes.
|
||||
///
|
||||
/// Nodes, that are to be excluded, have to be tagged with
|
||||
/// [RenderViewport.excludeFromScrolling] and the [RenderAbstractViewport] in
|
||||
/// use has to add the [RenderViewport.useTwoPaneSemantics] tag to its
|
||||
/// [SemanticsConfiguration] by overriding
|
||||
/// [RenderObject.describeSemanticsConfiguration].
|
||||
///
|
||||
/// If the tag [RenderViewport.useTwoPaneSemantics] is present on the viewport,
|
||||
/// two semantics nodes will be used to represent the [Scrollable]: The outer
|
||||
/// node will contain all children, that are excluded from scrolling. The inner
|
||||
/// node, which is annotated with the scrolling actions, will house the
|
||||
/// scrollable children.
|
||||
class _ExcludableScrollSemantics extends SingleChildRenderObjectWidget {
|
||||
const _ExcludableScrollSemantics({ Key key, Widget child }) : super(key: key, child: child);
|
||||
|
||||
@override
|
||||
_RenderExcludableScrollSemantics createRenderObject(BuildContext context) => new _RenderExcludableScrollSemantics();
|
||||
}
|
||||
|
||||
class _RenderExcludableScrollSemantics extends RenderProxyBox {
|
||||
_RenderExcludableScrollSemantics({ RenderBox child }) : super(child);
|
||||
|
||||
@override
|
||||
void describeSemanticsConfiguration(SemanticsConfiguration config) {
|
||||
super.describeSemanticsConfiguration(config);
|
||||
config.isSemanticBoundary = true;
|
||||
}
|
||||
|
||||
SemanticsNode _innerNode;
|
||||
SemanticsNode _annotatedNode;
|
||||
|
||||
@override
|
||||
void assembleSemanticsNode(SemanticsNode node, SemanticsConfiguration config, Iterable<SemanticsNode> children) {
|
||||
if (children.isEmpty || !children.first.isTagged(RenderViewport.useTwoPaneSemantics)) {
|
||||
_annotatedNode = node;
|
||||
super.assembleSemanticsNode(node, config, children);
|
||||
return;
|
||||
}
|
||||
|
||||
_innerNode ??= new SemanticsNode(showOnScreen: showOnScreen);
|
||||
_innerNode
|
||||
..isMergedIntoParent = node.isPartOfNodeMerging
|
||||
..rect = Offset.zero & node.rect.size;
|
||||
_annotatedNode = _innerNode;
|
||||
|
||||
final List<SemanticsNode> excluded = <SemanticsNode>[_innerNode];
|
||||
final List<SemanticsNode> included = <SemanticsNode>[];
|
||||
for (SemanticsNode child in children) {
|
||||
assert(child.isTagged(RenderViewport.useTwoPaneSemantics));
|
||||
if (child.isTagged(RenderViewport.excludeFromScrolling))
|
||||
excluded.add(child);
|
||||
else
|
||||
included.add(child);
|
||||
}
|
||||
node.updateWith(config: null, childrenInInversePaintOrder: excluded);
|
||||
_innerNode.updateWith(config: config, childrenInInversePaintOrder: included);
|
||||
}
|
||||
|
||||
/// Sends a [SemanticsEvent] in the context of the [SemanticsNode] that is
|
||||
/// annotated with this object's semantics information.
|
||||
void sendSemanticsEvent(SemanticsEvent event) {
|
||||
_annotatedNode?.sendEvent(event);
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,10 @@ import '../rendering/mock_canvas.dart';
|
||||
import '../widgets/semantics_tester.dart';
|
||||
|
||||
void main() {
|
||||
setUp(() {
|
||||
debugResetSemanticsIdCounter();
|
||||
});
|
||||
|
||||
testWidgets('Does FlatButton contribute semantics', (WidgetTester tester) async {
|
||||
final SemanticsTester semantics = new SemanticsTester(tester);
|
||||
await tester.pumpWidget(
|
||||
@ -33,11 +37,17 @@ void main() {
|
||||
new TestSemantics.root(
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics.rootChild(
|
||||
actions: SemanticsAction.tap.index,
|
||||
actions: <SemanticsAction>[
|
||||
SemanticsAction.tap,
|
||||
],
|
||||
label: 'ABC',
|
||||
rect: new Rect.fromLTRB(0.0, 0.0, 88.0, 36.0),
|
||||
transform: new Matrix4.translationValues(356.0, 282.0, 0.0),
|
||||
flags: SemanticsFlag.isButton.index,
|
||||
flags: <SemanticsFlag>[
|
||||
SemanticsFlag.isButton,
|
||||
SemanticsFlag.hasEnabledState,
|
||||
SemanticsFlag.isEnabled,
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
@ -67,11 +77,17 @@ void main() {
|
||||
new TestSemantics.root(
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics.rootChild(
|
||||
actions: SemanticsAction.tap.index,
|
||||
actions: <SemanticsAction>[
|
||||
SemanticsAction.tap,
|
||||
],
|
||||
label: 'ABC',
|
||||
rect: new Rect.fromLTRB(0.0, 0.0, 88.0, 36.0),
|
||||
transform: new Matrix4.translationValues(356.0, 282.0, 0.0),
|
||||
flags: SemanticsFlag.isButton.index,
|
||||
flags: <SemanticsFlag>[
|
||||
SemanticsFlag.isButton,
|
||||
SemanticsFlag.hasEnabledState,
|
||||
SemanticsFlag.isEnabled,
|
||||
],
|
||||
)
|
||||
]
|
||||
),
|
||||
@ -248,4 +264,83 @@ void main() {
|
||||
await gesture.up();
|
||||
});
|
||||
|
||||
testWidgets('Disabled MaterialButton has same semantic size as enabled and exposes disabled semantics', (WidgetTester tester) async {
|
||||
final SemanticsTester semantics = new SemanticsTester(tester);
|
||||
|
||||
final Rect expectedButtonSize = new Rect.fromLTRB(0.0, 0.0, 116.0, 36.0);
|
||||
// Button is in center of screen
|
||||
final Matrix4 expectedButtonTransform = new Matrix4.identity()
|
||||
..translate(
|
||||
TestSemantics.fullScreen.width / 2 - expectedButtonSize.width /2,
|
||||
TestSemantics.fullScreen.height / 2 - expectedButtonSize.height /2,
|
||||
);
|
||||
|
||||
// enabled button
|
||||
await tester.pumpWidget(new Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: new Material(
|
||||
child: new Center(
|
||||
child: new MaterialButton(
|
||||
child: const Text('Button'),
|
||||
onPressed: () { /* to make sure the button is enabled */ },
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
expect(semantics, hasSemantics(
|
||||
new TestSemantics.root(
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics.rootChild(
|
||||
id: 1,
|
||||
rect: expectedButtonSize,
|
||||
transform: expectedButtonTransform,
|
||||
label: 'Button',
|
||||
actions: <SemanticsAction>[
|
||||
SemanticsAction.tap,
|
||||
],
|
||||
flags: <SemanticsFlag>[
|
||||
SemanticsFlag.isButton,
|
||||
SemanticsFlag.hasEnabledState,
|
||||
SemanticsFlag.isEnabled,
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
));
|
||||
|
||||
// disabled button
|
||||
await tester.pumpWidget(const Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: const Material(
|
||||
child: const Center(
|
||||
child: const MaterialButton(
|
||||
child: const Text('Button'),
|
||||
onPressed: null, // button is disabled
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
expect(semantics, hasSemantics(
|
||||
new TestSemantics.root(
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics.rootChild(
|
||||
id: 1,
|
||||
rect: expectedButtonSize,
|
||||
transform: expectedButtonTransform,
|
||||
label: 'Button',
|
||||
flags: <SemanticsFlag>[
|
||||
SemanticsFlag.isButton,
|
||||
SemanticsFlag.hasEnabledState,
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
));
|
||||
|
||||
|
||||
semantics.dispose();
|
||||
});
|
||||
|
||||
}
|
||||
|
@ -48,8 +48,14 @@ void main() {
|
||||
id: 2,
|
||||
label: 'Button',
|
||||
textDirection: TextDirection.ltr,
|
||||
actions: SemanticsAction.tap.index,
|
||||
flags: SemanticsFlag.isButton.index,
|
||||
actions: <SemanticsAction>[
|
||||
SemanticsAction.tap,
|
||||
],
|
||||
flags: <SemanticsFlag>[
|
||||
SemanticsFlag.isButton,
|
||||
SemanticsFlag.hasEnabledState,
|
||||
SemanticsFlag.isEnabled,
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -94,11 +94,11 @@ void main() {
|
||||
],
|
||||
),
|
||||
));
|
||||
|
||||
// This test verifies that the label and the control get merged.
|
||||
expect(semantics, hasSemantics(new TestSemantics.root(
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics.rootChild(
|
||||
id: 1,
|
||||
rect: new Rect.fromLTWH(0.0, 0.0, 800.0, 56.0),
|
||||
transform: null,
|
||||
flags: <SemanticsFlag>[
|
||||
@ -111,7 +111,6 @@ void main() {
|
||||
label: 'aaa\nAAA',
|
||||
),
|
||||
new TestSemantics.rootChild(
|
||||
id: 4,
|
||||
rect: new Rect.fromLTWH(0.0, 0.0, 800.0, 56.0),
|
||||
transform: new Matrix4.translationValues(0.0, 56.0, 0.0),
|
||||
flags: <SemanticsFlag>[
|
||||
@ -124,7 +123,6 @@ void main() {
|
||||
label: 'bbb\nBBB',
|
||||
),
|
||||
new TestSemantics.rootChild(
|
||||
id: 7,
|
||||
rect: new Rect.fromLTWH(0.0, 0.0, 800.0, 56.0),
|
||||
transform: new Matrix4.translationValues(0.0, 112.0, 0.0),
|
||||
flags: <SemanticsFlag>[
|
||||
@ -136,7 +134,7 @@ void main() {
|
||||
label: 'CCC\nccc',
|
||||
),
|
||||
],
|
||||
)));
|
||||
), ignoreId: true));
|
||||
});
|
||||
|
||||
}
|
||||
|
@ -585,13 +585,21 @@ void _tests() {
|
||||
textDirection: TextDirection.ltr,
|
||||
),
|
||||
new TestSemantics(
|
||||
flags: <SemanticsFlag>[SemanticsFlag.isButton],
|
||||
flags: <SemanticsFlag>[
|
||||
SemanticsFlag.isButton,
|
||||
SemanticsFlag.hasEnabledState,
|
||||
SemanticsFlag.isEnabled,
|
||||
],
|
||||
actions: <SemanticsAction>[SemanticsAction.tap],
|
||||
label: r'CANCEL',
|
||||
textDirection: TextDirection.ltr,
|
||||
),
|
||||
new TestSemantics(
|
||||
flags: <SemanticsFlag>[SemanticsFlag.isButton],
|
||||
flags: <SemanticsFlag>[
|
||||
SemanticsFlag.isButton,
|
||||
SemanticsFlag.hasEnabledState,
|
||||
SemanticsFlag.isEnabled,
|
||||
],
|
||||
actions: <SemanticsAction>[SemanticsAction.tap],
|
||||
label: r'OK',
|
||||
textDirection: TextDirection.ltr,
|
||||
|
@ -165,6 +165,10 @@ class TestScrollPhysics extends ScrollPhysics {
|
||||
}
|
||||
|
||||
void main() {
|
||||
setUp(() {
|
||||
debugResetSemanticsIdCounter();
|
||||
});
|
||||
|
||||
testWidgets('TabBar tap selects tab', (WidgetTester tester) async {
|
||||
final List<String> tabs = <String>['A', 'B', 'C'];
|
||||
|
||||
@ -1213,19 +1217,25 @@ void main() {
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics(
|
||||
id: 2,
|
||||
actions: SemanticsAction.tap.index,
|
||||
flags: SemanticsFlag.isSelected.index,
|
||||
label: 'TAB #0\nTab 1 of 2',
|
||||
rect: new Rect.fromLTRB(0.0, 0.0, 108.0, kTextTabBarHeight),
|
||||
transform: new Matrix4.translationValues(0.0, 276.0, 0.0),
|
||||
),
|
||||
new TestSemantics(
|
||||
id: 3,
|
||||
actions: SemanticsAction.tap.index,
|
||||
label: 'TAB #1\nTab 2 of 2',
|
||||
rect: new Rect.fromLTRB(0.0, 0.0, 108.0, kTextTabBarHeight),
|
||||
transform: new Matrix4.translationValues(108.0, 276.0, 0.0),
|
||||
),
|
||||
rect: TestSemantics.fullScreen,
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics(
|
||||
id: 3,
|
||||
actions: SemanticsAction.tap.index,
|
||||
flags: SemanticsFlag.isSelected.index,
|
||||
label: 'TAB #0\nTab 1 of 2',
|
||||
rect: new Rect.fromLTRB(0.0, 0.0, 108.0, kTextTabBarHeight),
|
||||
transform: new Matrix4.translationValues(0.0, 276.0, 0.0),
|
||||
),
|
||||
new TestSemantics(
|
||||
id: 4,
|
||||
actions: SemanticsAction.tap.index,
|
||||
label: 'TAB #1\nTab 2 of 2',
|
||||
rect: new Rect.fromLTRB(0.0, 0.0, 108.0, kTextTabBarHeight),
|
||||
transform: new Matrix4.translationValues(108.0, 276.0, 0.0),
|
||||
),
|
||||
]
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
@ -1458,24 +1468,30 @@ void main() {
|
||||
final TestSemantics expectedSemantics = new TestSemantics.root(
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics.rootChild(
|
||||
id: 23,
|
||||
id: 1,
|
||||
rect: TestSemantics.fullScreen,
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics(
|
||||
id: 24,
|
||||
actions: SemanticsAction.tap.index,
|
||||
flags: SemanticsFlag.isSelected.index,
|
||||
label: 'Semantics override 0\nTab 1 of 2',
|
||||
rect: new Rect.fromLTRB(0.0, 0.0, 108.0, kTextTabBarHeight),
|
||||
transform: new Matrix4.translationValues(0.0, 276.0, 0.0),
|
||||
),
|
||||
new TestSemantics(
|
||||
id: 25,
|
||||
actions: SemanticsAction.tap.index,
|
||||
label: 'Semantics override 1\nTab 2 of 2',
|
||||
rect: new Rect.fromLTRB(0.0, 0.0, 108.0, kTextTabBarHeight),
|
||||
transform: new Matrix4.translationValues(108.0, 276.0, 0.0),
|
||||
),
|
||||
id: 2,
|
||||
rect: TestSemantics.fullScreen,
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics(
|
||||
id: 3,
|
||||
actions: SemanticsAction.tap.index,
|
||||
flags: SemanticsFlag.isSelected.index,
|
||||
label: 'Semantics override 0\nTab 1 of 2',
|
||||
rect: new Rect.fromLTRB(0.0, 0.0, 108.0, kTextTabBarHeight),
|
||||
transform: new Matrix4.translationValues(0.0, 276.0, 0.0),
|
||||
),
|
||||
new TestSemantics(
|
||||
id: 4,
|
||||
actions: SemanticsAction.tap.index,
|
||||
label: 'Semantics override 1\nTab 2 of 2',
|
||||
rect: new Rect.fromLTRB(0.0, 0.0, 108.0, kTextTabBarHeight),
|
||||
transform: new Matrix4.translationValues(108.0, 276.0, 0.0),
|
||||
),
|
||||
]
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
|
@ -11,6 +11,10 @@ import '../rendering/rendering_tester.dart';
|
||||
|
||||
|
||||
void main() {
|
||||
setUp(() {
|
||||
debugResetSemanticsIdCounter();
|
||||
});
|
||||
|
||||
group('SemanticsNode', () {
|
||||
const SemanticsTag tag1 = const SemanticsTag('Tag One');
|
||||
const SemanticsTag tag2 = const SemanticsTag('Tag Two');
|
||||
@ -45,6 +49,7 @@ void main() {
|
||||
tags.add(tag3);
|
||||
|
||||
final SemanticsConfiguration config = new SemanticsConfiguration()
|
||||
..isSemanticBoundary = true
|
||||
..isMergingSemanticsOfDescendants = true;
|
||||
|
||||
node.updateWith(
|
||||
@ -121,9 +126,9 @@ void main() {
|
||||
|
||||
expect(
|
||||
root.toStringDeep(childOrder: DebugSemanticsDumpOrder.traversal),
|
||||
'SemanticsNode#8(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 10.0, 5.0))\n'
|
||||
'├SemanticsNode#6(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 5.0, 5.0))\n'
|
||||
'└SemanticsNode#7(STALE, owner: null, Rect.fromLTRB(5.0, 0.0, 10.0, 5.0))\n',
|
||||
'SemanticsNode#3(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 10.0, 5.0))\n'
|
||||
'├SemanticsNode#1(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 5.0, 5.0))\n'
|
||||
'└SemanticsNode#2(STALE, owner: null, Rect.fromLTRB(5.0, 0.0, 10.0, 5.0))\n',
|
||||
);
|
||||
});
|
||||
|
||||
@ -140,16 +145,16 @@ void main() {
|
||||
);
|
||||
expect(
|
||||
root.toStringDeep(childOrder: DebugSemanticsDumpOrder.traversal),
|
||||
'SemanticsNode#11(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 20.0, 5.0))\n'
|
||||
'├SemanticsNode#10(STALE, owner: null, Rect.fromLTRB(10.0, 0.0, 15.0, 5.0))\n'
|
||||
'└SemanticsNode#9(STALE, owner: null, Rect.fromLTRB(15.0, 0.0, 20.0, 5.0))\n',
|
||||
'SemanticsNode#3(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 20.0, 5.0))\n'
|
||||
'├SemanticsNode#2(STALE, owner: null, Rect.fromLTRB(10.0, 0.0, 15.0, 5.0))\n'
|
||||
'└SemanticsNode#1(STALE, owner: null, Rect.fromLTRB(15.0, 0.0, 20.0, 5.0))\n',
|
||||
);
|
||||
|
||||
expect(
|
||||
root.toStringDeep(childOrder: DebugSemanticsDumpOrder.inverseHitTest),
|
||||
'SemanticsNode#11(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 20.0, 5.0))\n'
|
||||
'├SemanticsNode#9(STALE, owner: null, Rect.fromLTRB(15.0, 0.0, 20.0, 5.0))\n'
|
||||
'└SemanticsNode#10(STALE, owner: null, Rect.fromLTRB(10.0, 0.0, 15.0, 5.0))\n',
|
||||
'SemanticsNode#3(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 20.0, 5.0))\n'
|
||||
'├SemanticsNode#1(STALE, owner: null, Rect.fromLTRB(15.0, 0.0, 20.0, 5.0))\n'
|
||||
'└SemanticsNode#2(STALE, owner: null, Rect.fromLTRB(10.0, 0.0, 15.0, 5.0))\n',
|
||||
);
|
||||
|
||||
final SemanticsNode child3 = new SemanticsNode()
|
||||
@ -173,22 +178,22 @@ void main() {
|
||||
|
||||
expect(
|
||||
rootComplex.toStringDeep(childOrder: DebugSemanticsDumpOrder.traversal),
|
||||
'SemanticsNode#15(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 25.0, 5.0))\n'
|
||||
'├SemanticsNode#12(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 10.0, 5.0))\n'
|
||||
'│├SemanticsNode#14(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 5.0, 5.0))\n'
|
||||
'│└SemanticsNode#13(STALE, owner: null, Rect.fromLTRB(5.0, 0.0, 10.0, 5.0))\n'
|
||||
'├SemanticsNode#10(STALE, owner: null, Rect.fromLTRB(10.0, 0.0, 15.0, 5.0))\n'
|
||||
'└SemanticsNode#9(STALE, owner: null, Rect.fromLTRB(15.0, 0.0, 20.0, 5.0))\n',
|
||||
'SemanticsNode#7(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 25.0, 5.0))\n'
|
||||
'├SemanticsNode#4(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 10.0, 5.0))\n'
|
||||
'│├SemanticsNode#6(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 5.0, 5.0))\n'
|
||||
'│└SemanticsNode#5(STALE, owner: null, Rect.fromLTRB(5.0, 0.0, 10.0, 5.0))\n'
|
||||
'├SemanticsNode#2(STALE, owner: null, Rect.fromLTRB(10.0, 0.0, 15.0, 5.0))\n'
|
||||
'└SemanticsNode#1(STALE, owner: null, Rect.fromLTRB(15.0, 0.0, 20.0, 5.0))\n',
|
||||
);
|
||||
|
||||
expect(
|
||||
rootComplex.toStringDeep(childOrder: DebugSemanticsDumpOrder.inverseHitTest),
|
||||
'SemanticsNode#15(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 25.0, 5.0))\n'
|
||||
'├SemanticsNode#9(STALE, owner: null, Rect.fromLTRB(15.0, 0.0, 20.0, 5.0))\n'
|
||||
'├SemanticsNode#10(STALE, owner: null, Rect.fromLTRB(10.0, 0.0, 15.0, 5.0))\n'
|
||||
'└SemanticsNode#12(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 10.0, 5.0))\n'
|
||||
' ├SemanticsNode#13(STALE, owner: null, Rect.fromLTRB(5.0, 0.0, 10.0, 5.0))\n'
|
||||
' └SemanticsNode#14(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 5.0, 5.0))\n',
|
||||
'SemanticsNode#7(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 25.0, 5.0))\n'
|
||||
'├SemanticsNode#1(STALE, owner: null, Rect.fromLTRB(15.0, 0.0, 20.0, 5.0))\n'
|
||||
'├SemanticsNode#2(STALE, owner: null, Rect.fromLTRB(10.0, 0.0, 15.0, 5.0))\n'
|
||||
'└SemanticsNode#4(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 10.0, 5.0))\n'
|
||||
' ├SemanticsNode#5(STALE, owner: null, Rect.fromLTRB(5.0, 0.0, 10.0, 5.0))\n'
|
||||
' └SemanticsNode#6(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 5.0, 5.0))\n',
|
||||
);
|
||||
});
|
||||
|
||||
@ -196,15 +201,16 @@ void main() {
|
||||
final SemanticsNode minimalProperties = new SemanticsNode();
|
||||
expect(
|
||||
minimalProperties.toStringDeep(),
|
||||
'SemanticsNode#16(Rect.fromLTRB(0.0, 0.0, 0.0, 0.0))\n',
|
||||
'SemanticsNode#1(Rect.fromLTRB(0.0, 0.0, 0.0, 0.0))\n',
|
||||
);
|
||||
|
||||
expect(
|
||||
minimalProperties.toStringDeep(minLevel: DiagnosticLevel.hidden),
|
||||
'SemanticsNode#16(owner: null, isPartOfNodeMerging: false, Rect.fromLTRB(0.0, 0.0, 0.0, 0.0), actions: [], isSelected: false, isFocused: false, isButton: false, isTextField: false, label: "", value: "", increasedValue: "", decreasedValue: "", hint: "", textDirection: null)\n'
|
||||
'SemanticsNode#1(owner: null, isMergedIntoParent: false, mergeAllDescendantsIntoThisNode: false, Rect.fromLTRB(0.0, 0.0, 0.0, 0.0), actions: [], isSelected: false, isFocused: false, isButton: false, isTextField: false, label: "", value: "", increasedValue: "", decreasedValue: "", hint: "", textDirection: null)\n'
|
||||
);
|
||||
|
||||
final SemanticsConfiguration config = new SemanticsConfiguration()
|
||||
..isSemanticBoundary = true
|
||||
..isMergingSemanticsOfDescendants = true
|
||||
..onScrollUp = () { }
|
||||
..onLongPress = () { }
|
||||
@ -220,7 +226,7 @@ void main() {
|
||||
..updateWith(config: config, childrenInInversePaintOrder: null);
|
||||
expect(
|
||||
allProperties.toStringDeep(),
|
||||
'SemanticsNode#17(STALE, owner: null, leaf merge, Rect.fromLTRB(60.0, 20.0, 80.0, 50.0), actions: [longPress, scrollUp, showOnScreen], unchecked, selected, button, label: "Use all the properties", textDirection: rtl)\n',
|
||||
'SemanticsNode#2(STALE, owner: null, merge boundary ⛔️, Rect.fromLTRB(60.0, 20.0, 80.0, 50.0), actions: [longPress, scrollUp, showOnScreen], unchecked, selected, button, label: "Use all the properties", textDirection: rtl)\n',
|
||||
);
|
||||
expect(
|
||||
allProperties.getSemanticsData().toString(),
|
||||
@ -232,7 +238,7 @@ void main() {
|
||||
..transform = new Matrix4.diagonal3(new Vector3(10.0, 10.0, 1.0));
|
||||
expect(
|
||||
scaled.toStringDeep(),
|
||||
'SemanticsNode#18(STALE, owner: null, Rect.fromLTRB(50.0, 10.0, 70.0, 40.0) scaled by 10.0x)\n',
|
||||
'SemanticsNode#3(STALE, owner: null, Rect.fromLTRB(50.0, 10.0, 70.0, 40.0) scaled by 10.0x)\n',
|
||||
);
|
||||
expect(
|
||||
scaled.getSemanticsData().toString(),
|
||||
@ -251,7 +257,6 @@ void main() {
|
||||
expect(config.isSelected, isFalse);
|
||||
expect(config.isBlockingSemanticsOfPreviouslyPaintedNodes, isFalse);
|
||||
expect(config.isFocused, isFalse);
|
||||
expect(config.isMergingDescendantsIntoOneNode, isFalse);
|
||||
expect(config.isTextField, isFalse);
|
||||
|
||||
expect(config.onShowOnScreen, isNull);
|
||||
@ -274,7 +279,6 @@ void main() {
|
||||
config.isSelected = true;
|
||||
config.isBlockingSemanticsOfPreviouslyPaintedNodes = true;
|
||||
config.isFocused = true;
|
||||
config.isMergingDescendantsIntoOneNode = true;
|
||||
config.isTextField = true;
|
||||
|
||||
final VoidCallback onShowOnScreen = () { };
|
||||
@ -309,7 +313,6 @@ void main() {
|
||||
expect(config.isSelected, isTrue);
|
||||
expect(config.isBlockingSemanticsOfPreviouslyPaintedNodes, isTrue);
|
||||
expect(config.isFocused, isTrue);
|
||||
expect(config.isMergingDescendantsIntoOneNode, isTrue);
|
||||
expect(config.isTextField, isTrue);
|
||||
|
||||
expect(config.onShowOnScreen, same(onShowOnScreen));
|
||||
|
@ -220,73 +220,84 @@ void main() {
|
||||
' │ diagnosis: insufficient data to draw conclusion (less than five\n'
|
||||
' │ repaints)\n'
|
||||
' │\n'
|
||||
' └─child: RenderSemanticsGestureHandler#00000\n'
|
||||
' └─child: _RenderExcludableScrollSemantics#00000\n'
|
||||
' │ parentData: <none> (can use size)\n'
|
||||
' │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
|
||||
' │ semantic boundary\n'
|
||||
' │ size: Size(800.0, 600.0)\n'
|
||||
' │ gestures: vertical scroll\n'
|
||||
' │\n'
|
||||
' └─child: RenderPointerListener#00000\n'
|
||||
' └─child: RenderSemanticsGestureHandler#00000\n'
|
||||
' │ parentData: <none> (can use size)\n'
|
||||
' │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
|
||||
' │ size: Size(800.0, 600.0)\n'
|
||||
' │ behavior: opaque\n'
|
||||
' │ listeners: down\n'
|
||||
' │ gestures: vertical scroll\n'
|
||||
' │\n'
|
||||
' └─child: RenderIgnorePointer#00000\n'
|
||||
' └─child: RenderPointerListener#00000\n'
|
||||
' │ parentData: <none> (can use size)\n'
|
||||
' │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
|
||||
' │ size: Size(800.0, 600.0)\n'
|
||||
' │ ignoring: false\n'
|
||||
' │ ignoringSemantics: false\n'
|
||||
' │ behavior: opaque\n'
|
||||
' │ listeners: down\n'
|
||||
' │\n'
|
||||
' └─child: RenderViewport#00000\n'
|
||||
' └─child: RenderSemanticsAnnotations#00000\n'
|
||||
' │ parentData: <none> (can use size)\n'
|
||||
' │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
|
||||
' │ layer: OffsetLayer#00000\n'
|
||||
' │ size: Size(800.0, 600.0)\n'
|
||||
' │ axisDirection: down\n'
|
||||
' │ crossAxisDirection: right\n'
|
||||
' │ offset: ScrollPositionWithSingleContext#00000(offset: 0.0, range:\n'
|
||||
' │ 0.0..39400.0, viewport: 600.0, ScrollableState,\n'
|
||||
' │ AlwaysScrollableScrollPhysics -> ClampingScrollPhysics,\n'
|
||||
' │ IdleScrollActivity#00000, ScrollDirection.idle)\n'
|
||||
' │ anchor: 0.0\n'
|
||||
' │\n'
|
||||
' └─center child: RenderSliverFixedExtentList#00000 relayoutBoundary=up1\n'
|
||||
' │ parentData: paintOffset=Offset(0.0, 0.0) (can use size)\n'
|
||||
' │ constraints: SliverConstraints(AxisDirection.down,\n'
|
||||
' │ GrowthDirection.forward, ScrollDirection.idle, scrollOffset:\n'
|
||||
' │ 0.0, remainingPaintExtent: 600.0, crossAxisExtent: 800.0,\n'
|
||||
' │ crossAxisDirection: AxisDirection.right,\n'
|
||||
' │ viewportMainAxisExtent: 600.0)\n'
|
||||
' │ geometry: SliverGeometry(scrollExtent: 40000.0, paintExtent:\n'
|
||||
' │ 600.0, maxPaintExtent: 40000.0, hasVisualOverflow: true)\n'
|
||||
' │ currently live children: 0 to 1\n'
|
||||
' └─child: RenderIgnorePointer#00000\n'
|
||||
' │ parentData: <none> (can use size)\n'
|
||||
' │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
|
||||
' │ size: Size(800.0, 600.0)\n'
|
||||
' │ ignoring: false\n'
|
||||
' │ ignoringSemantics: false\n'
|
||||
' │\n'
|
||||
' ├─child with index 0: RenderLimitedBox#00000\n'
|
||||
' │ │ parentData: index=0; layoutOffset=0.0\n'
|
||||
' │ │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
|
||||
' │ │ size: Size(800.0, 400.0)\n'
|
||||
' │ │ maxWidth: 400.0\n'
|
||||
' │ │ maxHeight: 400.0\n'
|
||||
' │ │\n'
|
||||
' │ └─child: RenderCustomPaint#00000\n'
|
||||
' │ parentData: <none> (can use size)\n'
|
||||
' │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
|
||||
' │ size: Size(800.0, 400.0)\n'
|
||||
' │\n'
|
||||
' └─child with index 1: RenderLimitedBox#00000\n' // <----- no dashed line starts here
|
||||
' │ parentData: index=1; layoutOffset=400.0\n'
|
||||
' │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
|
||||
' │ size: Size(800.0, 400.0)\n'
|
||||
' │ maxWidth: 400.0\n'
|
||||
' │ maxHeight: 400.0\n'
|
||||
' └─child: RenderViewport#00000\n'
|
||||
' │ parentData: <none> (can use size)\n'
|
||||
' │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
|
||||
' │ layer: OffsetLayer#00000\n'
|
||||
' │ size: Size(800.0, 600.0)\n'
|
||||
' │ axisDirection: down\n'
|
||||
' │ crossAxisDirection: right\n'
|
||||
' │ offset: ScrollPositionWithSingleContext#00000(offset: 0.0, range:\n'
|
||||
' │ 0.0..39400.0, viewport: 600.0, ScrollableState,\n'
|
||||
' │ AlwaysScrollableScrollPhysics -> ClampingScrollPhysics,\n'
|
||||
' │ IdleScrollActivity#00000, ScrollDirection.idle)\n'
|
||||
' │ anchor: 0.0\n'
|
||||
' │\n'
|
||||
' └─child: RenderCustomPaint#00000\n'
|
||||
' parentData: <none> (can use size)\n'
|
||||
' constraints: BoxConstraints(w=800.0, h=400.0)\n'
|
||||
' size: Size(800.0, 400.0)\n'
|
||||
' └─center child: RenderSliverFixedExtentList#00000 relayoutBoundary=up1\n'
|
||||
' │ parentData: paintOffset=Offset(0.0, 0.0) (can use size)\n'
|
||||
' │ constraints: SliverConstraints(AxisDirection.down,\n'
|
||||
' │ GrowthDirection.forward, ScrollDirection.idle, scrollOffset:\n'
|
||||
' │ 0.0, remainingPaintExtent: 600.0, crossAxisExtent: 800.0,\n'
|
||||
' │ crossAxisDirection: AxisDirection.right,\n'
|
||||
' │ viewportMainAxisExtent: 600.0)\n'
|
||||
' │ geometry: SliverGeometry(scrollExtent: 40000.0, paintExtent:\n'
|
||||
' │ 600.0, maxPaintExtent: 40000.0, hasVisualOverflow: true)\n'
|
||||
' │ currently live children: 0 to 1\n'
|
||||
' │\n'
|
||||
' ├─child with index 0: RenderLimitedBox#00000\n'
|
||||
' │ │ parentData: index=0; layoutOffset=0.0\n'
|
||||
' │ │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
|
||||
' │ │ size: Size(800.0, 400.0)\n'
|
||||
' │ │ maxWidth: 400.0\n'
|
||||
' │ │ maxHeight: 400.0\n'
|
||||
' │ │\n'
|
||||
' │ └─child: RenderCustomPaint#00000\n'
|
||||
' │ parentData: <none> (can use size)\n'
|
||||
' │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
|
||||
' │ size: Size(800.0, 400.0)\n'
|
||||
' │\n'
|
||||
' └─child with index 1: RenderLimitedBox#00000\n' // <----- no dashed line starts here
|
||||
' │ parentData: index=1; layoutOffset=400.0\n'
|
||||
' │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
|
||||
' │ size: Size(800.0, 400.0)\n'
|
||||
' │ maxWidth: 400.0\n'
|
||||
' │ maxHeight: 400.0\n'
|
||||
' │\n'
|
||||
' └─child: RenderCustomPaint#00000\n'
|
||||
' parentData: <none> (can use size)\n'
|
||||
' constraints: BoxConstraints(w=800.0, h=400.0)\n'
|
||||
' size: Size(800.0, 400.0)\n'
|
||||
));
|
||||
const GlobalObjectKey<_LeafState>(0).currentState.setKeepAlive(true);
|
||||
await tester.drag(find.byType(ListView), const Offset(0.0, -1000.0));
|
||||
@ -324,97 +335,108 @@ void main() {
|
||||
' │ diagnosis: insufficient data to draw conclusion (less than five\n'
|
||||
' │ repaints)\n'
|
||||
' │\n'
|
||||
' └─child: RenderSemanticsGestureHandler#00000\n'
|
||||
' └─child: _RenderExcludableScrollSemantics#00000\n'
|
||||
' │ parentData: <none> (can use size)\n'
|
||||
' │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
|
||||
' │ semantic boundary\n'
|
||||
' │ size: Size(800.0, 600.0)\n'
|
||||
' │ gestures: vertical scroll\n'
|
||||
' │\n'
|
||||
' └─child: RenderPointerListener#00000\n'
|
||||
' └─child: RenderSemanticsGestureHandler#00000\n'
|
||||
' │ parentData: <none> (can use size)\n'
|
||||
' │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
|
||||
' │ size: Size(800.0, 600.0)\n'
|
||||
' │ behavior: opaque\n'
|
||||
' │ listeners: down\n'
|
||||
' │ gestures: vertical scroll\n'
|
||||
' │\n'
|
||||
' └─child: RenderIgnorePointer#00000\n'
|
||||
' └─child: RenderPointerListener#00000\n'
|
||||
' │ parentData: <none> (can use size)\n'
|
||||
' │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
|
||||
' │ size: Size(800.0, 600.0)\n'
|
||||
' │ ignoring: false\n'
|
||||
' │ ignoringSemantics: false\n'
|
||||
' │ behavior: opaque\n'
|
||||
' │ listeners: down\n'
|
||||
' │\n'
|
||||
' └─child: RenderViewport#00000\n'
|
||||
' └─child: RenderSemanticsAnnotations#00000\n'
|
||||
' │ parentData: <none> (can use size)\n'
|
||||
' │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
|
||||
' │ layer: OffsetLayer#00000\n'
|
||||
' │ size: Size(800.0, 600.0)\n'
|
||||
' │ axisDirection: down\n'
|
||||
' │ crossAxisDirection: right\n'
|
||||
' │ offset: ScrollPositionWithSingleContext#00000(offset: 2000.0,\n'
|
||||
' │ range: 0.0..39400.0, viewport: 600.0, ScrollableState,\n'
|
||||
' │ AlwaysScrollableScrollPhysics -> ClampingScrollPhysics,\n'
|
||||
' │ IdleScrollActivity#00000, ScrollDirection.idle)\n'
|
||||
' │ anchor: 0.0\n'
|
||||
' │\n'
|
||||
' └─center child: RenderSliverFixedExtentList#00000 relayoutBoundary=up1\n'
|
||||
' │ parentData: paintOffset=Offset(0.0, 0.0) (can use size)\n'
|
||||
' │ constraints: SliverConstraints(AxisDirection.down,\n'
|
||||
' │ GrowthDirection.forward, ScrollDirection.idle, scrollOffset:\n'
|
||||
' │ 2000.0, remainingPaintExtent: 600.0, crossAxisExtent: 800.0,\n'
|
||||
' │ crossAxisDirection: AxisDirection.right,\n'
|
||||
' │ viewportMainAxisExtent: 600.0)\n'
|
||||
' │ geometry: SliverGeometry(scrollExtent: 40000.0, paintExtent:\n'
|
||||
' │ 600.0, maxPaintExtent: 40000.0, hasVisualOverflow: true)\n'
|
||||
' │ currently live children: 5 to 6\n'
|
||||
' └─child: RenderIgnorePointer#00000\n'
|
||||
' │ parentData: <none> (can use size)\n'
|
||||
' │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
|
||||
' │ size: Size(800.0, 600.0)\n'
|
||||
' │ ignoring: false\n'
|
||||
' │ ignoringSemantics: false\n'
|
||||
' │\n'
|
||||
' ├─child with index 5: RenderLimitedBox#00000\n' // <----- this is index 5, not 0
|
||||
' │ │ parentData: index=5; layoutOffset=2000.0\n'
|
||||
' │ │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
|
||||
' │ │ size: Size(800.0, 400.0)\n'
|
||||
' │ │ maxWidth: 400.0\n'
|
||||
' │ │ maxHeight: 400.0\n'
|
||||
' │ │\n'
|
||||
' │ └─child: RenderCustomPaint#00000\n'
|
||||
' │ parentData: <none> (can use size)\n'
|
||||
' │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
|
||||
' │ size: Size(800.0, 400.0)\n'
|
||||
' │\n'
|
||||
' ├─child with index 6: RenderLimitedBox#00000\n'
|
||||
' ╎ │ parentData: index=6; layoutOffset=2400.0\n'
|
||||
' ╎ │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
|
||||
' ╎ │ size: Size(800.0, 400.0)\n'
|
||||
' ╎ │ maxWidth: 400.0\n'
|
||||
' ╎ │ maxHeight: 400.0\n'
|
||||
' ╎ │\n'
|
||||
' ╎ └─child: RenderCustomPaint#00000\n'
|
||||
' ╎ parentData: <none> (can use size)\n'
|
||||
' ╎ constraints: BoxConstraints(w=800.0, h=400.0)\n'
|
||||
' ╎ size: Size(800.0, 400.0)\n'
|
||||
' ╎\n'
|
||||
' ╎╌child with index 0 (kept alive offstage): RenderLimitedBox#00000\n' // <----- this one is index 0 and is marked as being offstage
|
||||
' ╎ │ parentData: index=0; keepAlive; layoutOffset=0.0\n'
|
||||
' ╎ │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
|
||||
' ╎ │ size: Size(800.0, 400.0)\n'
|
||||
' ╎ │ maxWidth: 400.0\n'
|
||||
' ╎ │ maxHeight: 400.0\n'
|
||||
' ╎ │\n'
|
||||
' ╎ └─child: RenderCustomPaint#00000\n'
|
||||
' ╎ parentData: <none> (can use size)\n'
|
||||
' ╎ constraints: BoxConstraints(w=800.0, h=400.0)\n'
|
||||
' ╎ size: Size(800.0, 400.0)\n'
|
||||
' ╎\n' // <----- dashed line ends here
|
||||
' └╌child with index 3 (kept alive offstage): RenderLimitedBox#00000\n'
|
||||
' │ parentData: index=3; keepAlive; layoutOffset=1200.0\n'
|
||||
' │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
|
||||
' │ size: Size(800.0, 400.0)\n'
|
||||
' │ maxWidth: 400.0\n'
|
||||
' │ maxHeight: 400.0\n'
|
||||
' └─child: RenderViewport#00000\n'
|
||||
' │ parentData: <none> (can use size)\n'
|
||||
' │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
|
||||
' │ layer: OffsetLayer#00000\n'
|
||||
' │ size: Size(800.0, 600.0)\n'
|
||||
' │ axisDirection: down\n'
|
||||
' │ crossAxisDirection: right\n'
|
||||
' │ offset: ScrollPositionWithSingleContext#00000(offset: 2000.0,\n'
|
||||
' │ range: 0.0..39400.0, viewport: 600.0, ScrollableState,\n'
|
||||
' │ AlwaysScrollableScrollPhysics -> ClampingScrollPhysics,\n'
|
||||
' │ IdleScrollActivity#00000, ScrollDirection.idle)\n'
|
||||
' │ anchor: 0.0\n'
|
||||
' │\n'
|
||||
' └─child: RenderCustomPaint#00000\n'
|
||||
' parentData: <none> (can use size)\n'
|
||||
' constraints: BoxConstraints(w=800.0, h=400.0)\n'
|
||||
' size: Size(800.0, 400.0)\n'
|
||||
' └─center child: RenderSliverFixedExtentList#00000 relayoutBoundary=up1\n'
|
||||
' │ parentData: paintOffset=Offset(0.0, 0.0) (can use size)\n'
|
||||
' │ constraints: SliverConstraints(AxisDirection.down,\n'
|
||||
' │ GrowthDirection.forward, ScrollDirection.idle, scrollOffset:\n'
|
||||
' │ 2000.0, remainingPaintExtent: 600.0, crossAxisExtent: 800.0,\n'
|
||||
' │ crossAxisDirection: AxisDirection.right,\n'
|
||||
' │ viewportMainAxisExtent: 600.0)\n'
|
||||
' │ geometry: SliverGeometry(scrollExtent: 40000.0, paintExtent:\n'
|
||||
' │ 600.0, maxPaintExtent: 40000.0, hasVisualOverflow: true)\n'
|
||||
' │ currently live children: 5 to 6\n'
|
||||
' │\n'
|
||||
' ├─child with index 5: RenderLimitedBox#00000\n' // <----- this is index 5, not 0
|
||||
' │ │ parentData: index=5; layoutOffset=2000.0\n'
|
||||
' │ │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
|
||||
' │ │ size: Size(800.0, 400.0)\n'
|
||||
' │ │ maxWidth: 400.0\n'
|
||||
' │ │ maxHeight: 400.0\n'
|
||||
' │ │\n'
|
||||
' │ └─child: RenderCustomPaint#00000\n'
|
||||
' │ parentData: <none> (can use size)\n'
|
||||
' │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
|
||||
' │ size: Size(800.0, 400.0)\n'
|
||||
' │\n'
|
||||
' ├─child with index 6: RenderLimitedBox#00000\n'
|
||||
' ╎ │ parentData: index=6; layoutOffset=2400.0\n'
|
||||
' ╎ │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
|
||||
' ╎ │ size: Size(800.0, 400.0)\n'
|
||||
' ╎ │ maxWidth: 400.0\n'
|
||||
' ╎ │ maxHeight: 400.0\n'
|
||||
' ╎ │\n'
|
||||
' ╎ └─child: RenderCustomPaint#00000\n'
|
||||
' ╎ parentData: <none> (can use size)\n'
|
||||
' ╎ constraints: BoxConstraints(w=800.0, h=400.0)\n'
|
||||
' ╎ size: Size(800.0, 400.0)\n'
|
||||
' ╎\n'
|
||||
' ╎╌child with index 0 (kept alive offstage): RenderLimitedBox#00000\n' // <----- this one is index 0 and is marked as being offstage
|
||||
' ╎ │ parentData: index=0; keepAlive; layoutOffset=0.0\n'
|
||||
' ╎ │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
|
||||
' ╎ │ size: Size(800.0, 400.0)\n'
|
||||
' ╎ │ maxWidth: 400.0\n'
|
||||
' ╎ │ maxHeight: 400.0\n'
|
||||
' ╎ │\n'
|
||||
' ╎ └─child: RenderCustomPaint#00000\n'
|
||||
' ╎ parentData: <none> (can use size)\n'
|
||||
' ╎ constraints: BoxConstraints(w=800.0, h=400.0)\n'
|
||||
' ╎ size: Size(800.0, 400.0)\n'
|
||||
' ╎\n' // <----- dashed line ends here
|
||||
' └╌child with index 3 (kept alive offstage): RenderLimitedBox#00000\n'
|
||||
' │ parentData: index=3; keepAlive; layoutOffset=1200.0\n'
|
||||
' │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
|
||||
' │ size: Size(800.0, 400.0)\n'
|
||||
' │ maxWidth: 400.0\n'
|
||||
' │ maxHeight: 400.0\n'
|
||||
' │\n'
|
||||
' └─child: RenderCustomPaint#00000\n'
|
||||
' parentData: <none> (can use size)\n'
|
||||
' constraints: BoxConstraints(w=800.0, h=400.0)\n'
|
||||
' size: Size(800.0, 400.0)\n'
|
||||
));
|
||||
});
|
||||
|
||||
|
@ -110,7 +110,6 @@ void main() {
|
||||
final TestSemantics expectedSemantics = new TestSemantics.root(
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics.rootChild(
|
||||
id: 2,
|
||||
rect: TestSemantics.fullScreen,
|
||||
actions: SemanticsAction.tap.index,
|
||||
label: 'Dismiss',
|
||||
@ -118,7 +117,7 @@ void main() {
|
||||
),
|
||||
]
|
||||
);
|
||||
expect(semantics, hasSemantics(expectedSemantics));
|
||||
expect(semantics, hasSemantics(expectedSemantics, ignoreId: true));
|
||||
|
||||
semantics.dispose();
|
||||
debugDefaultTargetPlatformOverride = null;
|
||||
|
@ -2,6 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:ui' show SemanticsFlag;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
@ -10,6 +12,10 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
import 'semantics_tester.dart';
|
||||
|
||||
void main() {
|
||||
setUp(() {
|
||||
debugResetSemanticsIdCounter();
|
||||
});
|
||||
|
||||
testWidgets('MergeSemantics', (WidgetTester tester) async {
|
||||
final SemanticsTester semantics = new SemanticsTester(tester);
|
||||
|
||||
@ -19,8 +25,14 @@ void main() {
|
||||
textDirection: TextDirection.ltr,
|
||||
child: new Row(
|
||||
children: <Widget>[
|
||||
const Text('test1'),
|
||||
const Text('test2'),
|
||||
new Semantics(
|
||||
container: true,
|
||||
child: const Text('test1'),
|
||||
),
|
||||
new Semantics(
|
||||
container: true,
|
||||
child: const Text('test2'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -44,8 +56,14 @@ void main() {
|
||||
child: new MergeSemantics(
|
||||
child: new Row(
|
||||
children: <Widget>[
|
||||
const Text('test1'),
|
||||
const Text('test2'),
|
||||
new Semantics(
|
||||
container: true,
|
||||
child: const Text('test1'),
|
||||
),
|
||||
new Semantics(
|
||||
container: true,
|
||||
child: const Text('test2'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -71,8 +89,14 @@ void main() {
|
||||
textDirection: TextDirection.ltr,
|
||||
child: new Row(
|
||||
children: <Widget>[
|
||||
const Text('test1'),
|
||||
const Text('test2'),
|
||||
new Semantics(
|
||||
container: true,
|
||||
child: const Text('test1'),
|
||||
),
|
||||
new Semantics(
|
||||
container: true,
|
||||
child: const Text('test2'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -81,8 +105,8 @@ void main() {
|
||||
expect(semantics, hasSemantics(
|
||||
new TestSemantics.root(
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics.rootChild(id: 4, label: 'test1'),
|
||||
new TestSemantics.rootChild(id: 5, label: 'test2'),
|
||||
new TestSemantics.rootChild(id: 6, label: 'test1'),
|
||||
new TestSemantics.rootChild(id: 7, label: 'test2'),
|
||||
],
|
||||
),
|
||||
ignoreRect: true,
|
||||
@ -91,4 +115,49 @@ void main() {
|
||||
|
||||
semantics.dispose();
|
||||
});
|
||||
|
||||
testWidgets('MergeSemantics works if other nodes are implicitly merged into its node', (WidgetTester tester) async {
|
||||
final SemanticsTester semantics = new SemanticsTester(tester);
|
||||
|
||||
await tester.pumpWidget(
|
||||
new Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: new MergeSemantics(
|
||||
child: new Semantics(
|
||||
selected: true, // this is implicitly merged into the MergeSemantics node
|
||||
child: new Row(
|
||||
children: <Widget>[
|
||||
new Semantics(
|
||||
container: true,
|
||||
child: const Text('test1'),
|
||||
),
|
||||
new Semantics(
|
||||
container: true,
|
||||
child: const Text('test2'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(semantics, hasSemantics(
|
||||
new TestSemantics.root(
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics.rootChild(
|
||||
id: 1,
|
||||
flags: <SemanticsFlag>[
|
||||
SemanticsFlag.isSelected,
|
||||
],
|
||||
label: 'test1\ntest2',
|
||||
),
|
||||
]
|
||||
),
|
||||
ignoreRect: true,
|
||||
ignoreTransform: true,
|
||||
));
|
||||
|
||||
semantics.dispose();
|
||||
});
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ void main() {
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics.rootChild(
|
||||
id: 1,
|
||||
tags: <SemanticsTag>[RenderSemanticsGestureHandler.useTwoPaneSemantics],
|
||||
tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics],
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics(
|
||||
id: 5,
|
||||
@ -89,7 +89,7 @@ void main() {
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics.rootChild(
|
||||
id: 1,
|
||||
tags: <SemanticsTag>[RenderSemanticsGestureHandler.useTwoPaneSemantics],
|
||||
tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics],
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics(
|
||||
id: 5,
|
||||
@ -112,7 +112,7 @@ void main() {
|
||||
new TestSemantics(
|
||||
id: 4,
|
||||
label: 'Semantics Test with Slivers',
|
||||
tags: <SemanticsTag>[RenderSemanticsGestureHandler.excludeFromScrolling],
|
||||
tags: <SemanticsTag>[RenderViewport.excludeFromScrolling],
|
||||
),
|
||||
],
|
||||
)
|
||||
@ -132,7 +132,7 @@ void main() {
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics.rootChild(
|
||||
id: 1,
|
||||
tags: <SemanticsTag>[RenderSemanticsGestureHandler.useTwoPaneSemantics],
|
||||
tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics],
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics(
|
||||
id: 5,
|
||||
@ -203,7 +203,7 @@ void main() {
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics.rootChild(
|
||||
id: 7,
|
||||
tags: <SemanticsTag>[RenderSemanticsGestureHandler.useTwoPaneSemantics],
|
||||
tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics],
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics(
|
||||
id: 10,
|
||||
@ -257,7 +257,7 @@ void main() {
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics.rootChild(
|
||||
id: 11,
|
||||
tags: <SemanticsTag>[RenderSemanticsGestureHandler.useTwoPaneSemantics],
|
||||
tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics],
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics(
|
||||
id: 17,
|
||||
@ -339,7 +339,7 @@ void main() {
|
||||
new TestSemantics.rootChild(
|
||||
id: 18,
|
||||
rect: TestSemantics.fullScreen,
|
||||
tags: <SemanticsTag>[RenderSemanticsGestureHandler.useTwoPaneSemantics],
|
||||
tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics],
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics(
|
||||
id: 23,
|
||||
@ -371,7 +371,7 @@ void main() {
|
||||
new TestSemantics(
|
||||
id: 22,
|
||||
rect: new Rect.fromLTRB(0.0, 0.0, 120.0, 20.0),
|
||||
tags: <SemanticsTag>[RenderSemanticsGestureHandler.excludeFromScrolling],
|
||||
tags: <SemanticsTag>[RenderViewport.excludeFromScrolling],
|
||||
label: 'AppBar',
|
||||
),
|
||||
],
|
||||
@ -422,7 +422,7 @@ void main() {
|
||||
new TestSemantics.rootChild(
|
||||
id: 24,
|
||||
rect: TestSemantics.fullScreen,
|
||||
tags: <SemanticsTag>[RenderSemanticsGestureHandler.useTwoPaneSemantics],
|
||||
tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics],
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics(
|
||||
id: 29,
|
||||
@ -454,7 +454,7 @@ void main() {
|
||||
new TestSemantics(
|
||||
id: 28,
|
||||
rect: new Rect.fromLTRB(0.0, 0.0, 120.0, 20.0),
|
||||
tags: <SemanticsTag>[RenderSemanticsGestureHandler.excludeFromScrolling],
|
||||
tags: <SemanticsTag>[RenderViewport.excludeFromScrolling],
|
||||
label: 'AppBar'
|
||||
),
|
||||
],
|
||||
@ -507,7 +507,7 @@ void main() {
|
||||
new TestSemantics.rootChild(
|
||||
id: 30,
|
||||
rect: TestSemantics.fullScreen,
|
||||
tags: <SemanticsTag>[RenderSemanticsGestureHandler.useTwoPaneSemantics],
|
||||
tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics],
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics(
|
||||
id: 35,
|
||||
@ -540,7 +540,7 @@ void main() {
|
||||
id: 34,
|
||||
rect: new Rect.fromLTRB(0.0, 0.0, 120.0, 20.0),
|
||||
transform: new Matrix4.translation(new Vector3(0.0, 544.0, 0.0)),
|
||||
tags: <SemanticsTag>[RenderSemanticsGestureHandler.excludeFromScrolling],
|
||||
tags: <SemanticsTag>[RenderViewport.excludeFromScrolling],
|
||||
label: 'AppBar'
|
||||
),
|
||||
],
|
||||
@ -592,7 +592,7 @@ void main() {
|
||||
new TestSemantics.rootChild(
|
||||
id: 36,
|
||||
rect: TestSemantics.fullScreen,
|
||||
tags: <SemanticsTag>[RenderSemanticsGestureHandler.useTwoPaneSemantics],
|
||||
tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics],
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics(
|
||||
id: 41,
|
||||
@ -625,7 +625,7 @@ void main() {
|
||||
id: 40,
|
||||
rect: new Rect.fromLTRB(0.0, 0.0, 120.0, 20.0),
|
||||
transform: new Matrix4.translation(new Vector3(0.0, 544.0, 0.0)),
|
||||
tags: <SemanticsTag>[RenderSemanticsGestureHandler.excludeFromScrolling],
|
||||
tags: <SemanticsTag>[RenderViewport.excludeFromScrolling],
|
||||
label: 'AppBar'
|
||||
),
|
||||
],
|
||||
|
Loading…
x
Reference in New Issue
Block a user