Unverified Commit b741d91a authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

Support disabled state for semantics (#13877)

Fixes https://github.com/flutter/flutter/issues/11993
parent e6e4406d
43327730a27344f1d45a1f9e4d53b14260778ce5
12e0e38a8b7a30a0d7e83b761c714a901b78f72e
......@@ -453,6 +453,7 @@ class ListTile extends StatelessWidget {
onLongPress: enabled ? onLongPress : null,
child: new Semantics(
selected: selected,
enabled: enabled,
child: new ConstrainedBox(
constraints: new BoxConstraints(minHeight: tileHeight),
child: new Padding(
......
......@@ -2818,6 +2818,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
RenderBox child,
bool container: false,
bool explicitChildNodes,
bool enabled,
bool checked,
bool selected,
bool button,
......@@ -2840,6 +2841,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
}) : assert(container != null),
_container = container,
_explicitChildNodes = explicitChildNodes,
_enabled = enabled,
_checked = checked,
_selected = selected,
_button = button,
......@@ -2911,6 +2913,17 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
markNeedsSemanticsUpdate();
}
/// If non-null, sets the [SemanticsNode.hasEnabledState] semantic to true and
/// the [SemanticsNode.isEnabled] semantic to the given value.
bool get enabled => _enabled;
bool _enabled;
set enabled(bool value) {
if (enabled == value)
return;
_enabled = value;
markNeedsSemanticsUpdate();
}
/// If non-null, sets the [SemanticsNode.isSelected] semantic to the given
/// value.
bool get selected => _selected;
......@@ -3213,6 +3226,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
config.isSemanticBoundary = container;
config.explicitChildNodes = explicitChildNodes;
if (enabled != null)
config.isEnabled = enabled;
if (checked != null)
config.isChecked = checked;
if (selected != null)
......
......@@ -243,6 +243,7 @@ class SemanticsProperties extends DiagnosticableTree {
///
/// The [container] argument must not be null.
const SemanticsProperties({
this.enabled,
this.checked,
this.selected,
this.button,
......@@ -264,6 +265,14 @@ class SemanticsProperties extends DiagnosticableTree {
this.onMoveCursorBackwardByCharacter,
});
/// If non-null, indicates that this subtree represents something that can be
/// in an enabled or disabled state.
///
/// For example, a button that a user can currently interact with would set
/// this field to true. A button that currently does not respond to user
/// interactions would set this field to false.
final bool enabled;
/// If non-null, indicates that this subtree represents a checkbox
/// or similar widget with a "checked" state, and what its current
/// state is.
......@@ -1080,6 +1089,8 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
}
final List<String> actions = _actions.keys.map((SemanticsAction action) => describeEnum(action)).toList()..sort();
properties.add(new IterableProperty<String>('actions', actions, ifEmpty: null));
if (_hasFlag(SemanticsFlags.hasEnabledState))
properties.add(new FlagProperty('isEnabled', value: _hasFlag(SemanticsFlags.isEnabled), ifFalse: 'disabled'));
if (_hasFlag(SemanticsFlags.hasCheckedState))
properties.add(new FlagProperty('isChecked', value: _hasFlag(SemanticsFlags.isChecked), ifTrue: 'checked', ifFalse: 'unchecked'));
properties.add(new FlagProperty('isSelected', value: _hasFlag(SemanticsFlags.isSelected), ifTrue: 'selected'));
......@@ -1741,12 +1752,32 @@ class SemanticsConfiguration {
_setFlag(SemanticsFlags.isSelected, value);
}
/// Whether the owning [RenderObject] is currently enabled.
///
/// A disabled object does not respond to user interactions. Only objects that
/// usually respond to user interactions, but which currently do not (like a
/// disabled button) should be marked as disabled.
///
/// The setter should not be called for objects (like static text) that never
/// respond to user interactions.
///
/// The getter will return null if the owning [RenderObject] doesn't support
/// the concept of being enabled/disabled.
bool get isEnabled => _hasFlag(SemanticsFlags.hasEnabledState) ? _hasFlag(SemanticsFlags.isEnabled) : null;
set isEnabled(bool value) {
_setFlag(SemanticsFlags.hasEnabledState, true);
_setFlag(SemanticsFlags.isEnabled, value);
}
/// If this node has Boolean state that can be controlled by the user, whether
/// that state is on or off, corresponding to true and false, respectively.
///
/// Do not set this to any value if the owning [RenderObject] doesn't have
/// Booleans state that can be controlled by the user.
bool get isChecked => _hasFlag(SemanticsFlags.hasCheckedState) && _hasFlag(SemanticsFlags.isChecked);
/// Do not call the setter for this field if the owning [RenderObject] doesn't
/// have checked/unchecked state that can be controlled by the user.
///
/// The getter returns null if the owning [RenderObject] does not have
/// checked/unchecked state.
bool get isChecked => _hasFlag(SemanticsFlags.hasCheckedState) ? _hasFlag(SemanticsFlags.isChecked) : null;
set isChecked(bool value) {
_setFlag(SemanticsFlags.hasCheckedState, true);
_setFlag(SemanticsFlags.isChecked, value);
......
......@@ -4764,6 +4764,7 @@ class Semantics extends SingleChildRenderObjectWidget {
Widget child,
bool container: false,
bool explicitChildNodes: false,
bool enabled,
bool checked,
bool selected,
bool button,
......@@ -4789,6 +4790,7 @@ class Semantics extends SingleChildRenderObjectWidget {
container: container,
explicitChildNodes: explicitChildNodes,
properties: new SemanticsProperties(
enabled: enabled,
checked: checked,
selected: selected,
button: button,
......@@ -4856,6 +4858,7 @@ class Semantics extends SingleChildRenderObjectWidget {
return new RenderSemanticsAnnotations(
container: container,
explicitChildNodes: explicitChildNodes,
enabled: properties.enabled,
checked: properties.checked,
selected: properties.selected,
button: properties.button,
......@@ -4895,6 +4898,7 @@ class Semantics extends SingleChildRenderObjectWidget {
renderObject
..container = container
..explicitChildNodes = explicitChildNodes
..enabled = properties.enabled
..checked = properties.checked
..selected = properties.selected
..label = properties.label
......
......@@ -101,7 +101,12 @@ void main() {
id: 1,
rect: new Rect.fromLTWH(0.0, 0.0, 800.0, 56.0),
transform: null,
flags: SemanticsFlags.hasCheckedState.index | SemanticsFlags.isChecked.index,
flags: <SemanticsFlags>[
SemanticsFlags.hasCheckedState,
SemanticsFlags.isChecked,
SemanticsFlags.hasEnabledState,
SemanticsFlags.isEnabled
],
actions: SemanticsAction.tap.index,
label: 'aaa\nAAA',
),
......@@ -109,7 +114,12 @@ void main() {
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: SemanticsFlags.hasCheckedState.index | SemanticsFlags.isChecked.index,
flags: <SemanticsFlags>[
SemanticsFlags.hasCheckedState,
SemanticsFlags.isChecked,
SemanticsFlags.hasEnabledState,
SemanticsFlags.isEnabled
],
actions: SemanticsAction.tap.index,
label: 'bbb\nBBB',
),
......@@ -117,7 +127,11 @@ void main() {
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: SemanticsFlags.hasCheckedState.index,
flags: <SemanticsFlags>[
SemanticsFlags.hasCheckedState,
SemanticsFlags.hasEnabledState,
SemanticsFlags.isEnabled
],
actions: SemanticsAction.tap.index,
label: 'CCC\nccc',
),
......
......@@ -347,6 +347,7 @@ void main() {
),
const ListTile(
title: const Text('three'),
enabled: false,
),
],
),
......@@ -360,13 +361,24 @@ void main() {
children: <TestSemantics>[
new TestSemantics.rootChild(
label: 'one',
flags: <SemanticsFlags>[
SemanticsFlags.hasEnabledState,
SemanticsFlags.isEnabled,
],
),
new TestSemantics.rootChild(
label: 'two',
flags: <SemanticsFlags>[SemanticsFlags.isSelected],
flags: <SemanticsFlags>[
SemanticsFlags.isSelected,
SemanticsFlags.hasEnabledState,
SemanticsFlags.isEnabled,
],
),
new TestSemantics.rootChild(
label: 'three',
flags: <SemanticsFlags>[
SemanticsFlags.hasEnabledState,
],
),
]
),
......
......@@ -246,7 +246,8 @@ void main() {
expect(config.isSemanticBoundary, isFalse);
expect(config.isButton, isFalse);
expect(config.isMergingSemanticsOfDescendants, isFalse);
expect(config.isChecked, isFalse);
expect(config.isEnabled, null);
expect(config.isChecked, null);
expect(config.isSelected, isFalse);
expect(config.isBlockingSemanticsOfPreviouslyPaintedNodes, isFalse);
expect(config.isFocused, isFalse);
......@@ -268,6 +269,7 @@ void main() {
config.isSemanticBoundary = true;
config.isButton = true;
config.isMergingSemanticsOfDescendants = true;
config.isEnabled = true;
config.isChecked = true;
config.isSelected = true;
config.isBlockingSemanticsOfPreviouslyPaintedNodes = true;
......@@ -302,6 +304,7 @@ void main() {
expect(config.isSemanticBoundary, isTrue);
expect(config.isButton, isTrue);
expect(config.isMergingSemanticsOfDescendants, isTrue);
expect(config.isEnabled, isTrue);
expect(config.isChecked, isTrue);
expect(config.isSelected, isTrue);
expect(config.isBlockingSemanticsOfPreviouslyPaintedNodes, isTrue);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment