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 { ...@@ -453,6 +453,7 @@ class ListTile extends StatelessWidget {
onLongPress: enabled ? onLongPress : null, onLongPress: enabled ? onLongPress : null,
child: new Semantics( child: new Semantics(
selected: selected, selected: selected,
enabled: enabled,
child: new ConstrainedBox( child: new ConstrainedBox(
constraints: new BoxConstraints(minHeight: tileHeight), constraints: new BoxConstraints(minHeight: tileHeight),
child: new Padding( child: new Padding(
......
...@@ -2818,6 +2818,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -2818,6 +2818,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
RenderBox child, RenderBox child,
bool container: false, bool container: false,
bool explicitChildNodes, bool explicitChildNodes,
bool enabled,
bool checked, bool checked,
bool selected, bool selected,
bool button, bool button,
...@@ -2840,6 +2841,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -2840,6 +2841,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
}) : assert(container != null), }) : assert(container != null),
_container = container, _container = container,
_explicitChildNodes = explicitChildNodes, _explicitChildNodes = explicitChildNodes,
_enabled = enabled,
_checked = checked, _checked = checked,
_selected = selected, _selected = selected,
_button = button, _button = button,
...@@ -2911,6 +2913,17 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -2911,6 +2913,17 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
markNeedsSemanticsUpdate(); 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 /// If non-null, sets the [SemanticsNode.isSelected] semantic to the given
/// value. /// value.
bool get selected => _selected; bool get selected => _selected;
...@@ -3213,6 +3226,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -3213,6 +3226,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
config.isSemanticBoundary = container; config.isSemanticBoundary = container;
config.explicitChildNodes = explicitChildNodes; config.explicitChildNodes = explicitChildNodes;
if (enabled != null)
config.isEnabled = enabled;
if (checked != null) if (checked != null)
config.isChecked = checked; config.isChecked = checked;
if (selected != null) if (selected != null)
......
...@@ -243,6 +243,7 @@ class SemanticsProperties extends DiagnosticableTree { ...@@ -243,6 +243,7 @@ class SemanticsProperties extends DiagnosticableTree {
/// ///
/// The [container] argument must not be null. /// The [container] argument must not be null.
const SemanticsProperties({ const SemanticsProperties({
this.enabled,
this.checked, this.checked,
this.selected, this.selected,
this.button, this.button,
...@@ -264,6 +265,14 @@ class SemanticsProperties extends DiagnosticableTree { ...@@ -264,6 +265,14 @@ class SemanticsProperties extends DiagnosticableTree {
this.onMoveCursorBackwardByCharacter, 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 /// If non-null, indicates that this subtree represents a checkbox
/// or similar widget with a "checked" state, and what its current /// or similar widget with a "checked" state, and what its current
/// state is. /// state is.
...@@ -1080,6 +1089,8 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -1080,6 +1089,8 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
} }
final List<String> actions = _actions.keys.map((SemanticsAction action) => describeEnum(action)).toList()..sort(); final List<String> actions = _actions.keys.map((SemanticsAction action) => describeEnum(action)).toList()..sort();
properties.add(new IterableProperty<String>('actions', actions, ifEmpty: null)); 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)) if (_hasFlag(SemanticsFlags.hasCheckedState))
properties.add(new FlagProperty('isChecked', value: _hasFlag(SemanticsFlags.isChecked), ifTrue: 'checked', ifFalse: 'unchecked')); properties.add(new FlagProperty('isChecked', value: _hasFlag(SemanticsFlags.isChecked), ifTrue: 'checked', ifFalse: 'unchecked'));
properties.add(new FlagProperty('isSelected', value: _hasFlag(SemanticsFlags.isSelected), ifTrue: 'selected')); properties.add(new FlagProperty('isSelected', value: _hasFlag(SemanticsFlags.isSelected), ifTrue: 'selected'));
...@@ -1741,12 +1752,32 @@ class SemanticsConfiguration { ...@@ -1741,12 +1752,32 @@ class SemanticsConfiguration {
_setFlag(SemanticsFlags.isSelected, value); _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 /// 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. /// 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 /// Do not call the setter for this field if the owning [RenderObject] doesn't
/// Booleans state that can be controlled by the user. /// have checked/unchecked state that can be controlled by the user.
bool get isChecked => _hasFlag(SemanticsFlags.hasCheckedState) && _hasFlag(SemanticsFlags.isChecked); ///
/// 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) { set isChecked(bool value) {
_setFlag(SemanticsFlags.hasCheckedState, true); _setFlag(SemanticsFlags.hasCheckedState, true);
_setFlag(SemanticsFlags.isChecked, value); _setFlag(SemanticsFlags.isChecked, value);
......
...@@ -4764,6 +4764,7 @@ class Semantics extends SingleChildRenderObjectWidget { ...@@ -4764,6 +4764,7 @@ class Semantics extends SingleChildRenderObjectWidget {
Widget child, Widget child,
bool container: false, bool container: false,
bool explicitChildNodes: false, bool explicitChildNodes: false,
bool enabled,
bool checked, bool checked,
bool selected, bool selected,
bool button, bool button,
...@@ -4789,6 +4790,7 @@ class Semantics extends SingleChildRenderObjectWidget { ...@@ -4789,6 +4790,7 @@ class Semantics extends SingleChildRenderObjectWidget {
container: container, container: container,
explicitChildNodes: explicitChildNodes, explicitChildNodes: explicitChildNodes,
properties: new SemanticsProperties( properties: new SemanticsProperties(
enabled: enabled,
checked: checked, checked: checked,
selected: selected, selected: selected,
button: button, button: button,
...@@ -4856,6 +4858,7 @@ class Semantics extends SingleChildRenderObjectWidget { ...@@ -4856,6 +4858,7 @@ class Semantics extends SingleChildRenderObjectWidget {
return new RenderSemanticsAnnotations( return new RenderSemanticsAnnotations(
container: container, container: container,
explicitChildNodes: explicitChildNodes, explicitChildNodes: explicitChildNodes,
enabled: properties.enabled,
checked: properties.checked, checked: properties.checked,
selected: properties.selected, selected: properties.selected,
button: properties.button, button: properties.button,
...@@ -4895,6 +4898,7 @@ class Semantics extends SingleChildRenderObjectWidget { ...@@ -4895,6 +4898,7 @@ class Semantics extends SingleChildRenderObjectWidget {
renderObject renderObject
..container = container ..container = container
..explicitChildNodes = explicitChildNodes ..explicitChildNodes = explicitChildNodes
..enabled = properties.enabled
..checked = properties.checked ..checked = properties.checked
..selected = properties.selected ..selected = properties.selected
..label = properties.label ..label = properties.label
......
...@@ -101,7 +101,12 @@ void main() { ...@@ -101,7 +101,12 @@ void main() {
id: 1, id: 1,
rect: new Rect.fromLTWH(0.0, 0.0, 800.0, 56.0), rect: new Rect.fromLTWH(0.0, 0.0, 800.0, 56.0),
transform: null, transform: null,
flags: SemanticsFlags.hasCheckedState.index | SemanticsFlags.isChecked.index, flags: <SemanticsFlags>[
SemanticsFlags.hasCheckedState,
SemanticsFlags.isChecked,
SemanticsFlags.hasEnabledState,
SemanticsFlags.isEnabled
],
actions: SemanticsAction.tap.index, actions: SemanticsAction.tap.index,
label: 'aaa\nAAA', label: 'aaa\nAAA',
), ),
...@@ -109,7 +114,12 @@ void main() { ...@@ -109,7 +114,12 @@ void main() {
id: 4, id: 4,
rect: new Rect.fromLTWH(0.0, 0.0, 800.0, 56.0), rect: new Rect.fromLTWH(0.0, 0.0, 800.0, 56.0),
transform: new Matrix4.translationValues(0.0, 56.0, 0.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, actions: SemanticsAction.tap.index,
label: 'bbb\nBBB', label: 'bbb\nBBB',
), ),
...@@ -117,7 +127,11 @@ void main() { ...@@ -117,7 +127,11 @@ void main() {
id: 7, id: 7,
rect: new Rect.fromLTWH(0.0, 0.0, 800.0, 56.0), rect: new Rect.fromLTWH(0.0, 0.0, 800.0, 56.0),
transform: new Matrix4.translationValues(0.0, 112.0, 0.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, actions: SemanticsAction.tap.index,
label: 'CCC\nccc', label: 'CCC\nccc',
), ),
......
...@@ -347,6 +347,7 @@ void main() { ...@@ -347,6 +347,7 @@ void main() {
), ),
const ListTile( const ListTile(
title: const Text('three'), title: const Text('three'),
enabled: false,
), ),
], ],
), ),
...@@ -360,13 +361,24 @@ void main() { ...@@ -360,13 +361,24 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
label: 'one', label: 'one',
flags: <SemanticsFlags>[
SemanticsFlags.hasEnabledState,
SemanticsFlags.isEnabled,
],
), ),
new TestSemantics.rootChild( new TestSemantics.rootChild(
label: 'two', label: 'two',
flags: <SemanticsFlags>[SemanticsFlags.isSelected], flags: <SemanticsFlags>[
SemanticsFlags.isSelected,
SemanticsFlags.hasEnabledState,
SemanticsFlags.isEnabled,
],
), ),
new TestSemantics.rootChild( new TestSemantics.rootChild(
label: 'three', label: 'three',
flags: <SemanticsFlags>[
SemanticsFlags.hasEnabledState,
],
), ),
] ]
), ),
......
...@@ -246,7 +246,8 @@ void main() { ...@@ -246,7 +246,8 @@ void main() {
expect(config.isSemanticBoundary, isFalse); expect(config.isSemanticBoundary, isFalse);
expect(config.isButton, isFalse); expect(config.isButton, isFalse);
expect(config.isMergingSemanticsOfDescendants, isFalse); expect(config.isMergingSemanticsOfDescendants, isFalse);
expect(config.isChecked, isFalse); expect(config.isEnabled, null);
expect(config.isChecked, null);
expect(config.isSelected, isFalse); expect(config.isSelected, isFalse);
expect(config.isBlockingSemanticsOfPreviouslyPaintedNodes, isFalse); expect(config.isBlockingSemanticsOfPreviouslyPaintedNodes, isFalse);
expect(config.isFocused, isFalse); expect(config.isFocused, isFalse);
...@@ -268,6 +269,7 @@ void main() { ...@@ -268,6 +269,7 @@ void main() {
config.isSemanticBoundary = true; config.isSemanticBoundary = true;
config.isButton = true; config.isButton = true;
config.isMergingSemanticsOfDescendants = true; config.isMergingSemanticsOfDescendants = true;
config.isEnabled = true;
config.isChecked = true; config.isChecked = true;
config.isSelected = true; config.isSelected = true;
config.isBlockingSemanticsOfPreviouslyPaintedNodes = true; config.isBlockingSemanticsOfPreviouslyPaintedNodes = true;
...@@ -302,6 +304,7 @@ void main() { ...@@ -302,6 +304,7 @@ void main() {
expect(config.isSemanticBoundary, isTrue); expect(config.isSemanticBoundary, isTrue);
expect(config.isButton, isTrue); expect(config.isButton, isTrue);
expect(config.isMergingSemanticsOfDescendants, isTrue); expect(config.isMergingSemanticsOfDescendants, isTrue);
expect(config.isEnabled, isTrue);
expect(config.isChecked, isTrue); expect(config.isChecked, isTrue);
expect(config.isSelected, isTrue); expect(config.isSelected, isTrue);
expect(config.isBlockingSemanticsOfPreviouslyPaintedNodes, 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