Support SemanticsFlag for Header (#15255)

This commit is contained in:
Michael Goderbauer 2018-03-09 13:32:01 -08:00 committed by GitHub
parent 96ce9d64ac
commit 3a40d0ee4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 207 additions and 1 deletions

View File

@ -152,7 +152,10 @@ class GalleryHomeState extends State<GalleryHome> with SingleTickerProviderState
child: new SafeArea( child: new SafeArea(
top: false, top: false,
bottom: false, bottom: false,
child: new Text(galleryItem.category, style: headerStyle), child: new Semantics(
header: true,
child: new Text(galleryItem.category, style: headerStyle),
),
), ),
), ),
) )

View File

@ -817,6 +817,21 @@ class RenderCustomPaint extends RenderProxyBox {
if (properties.button != null) { if (properties.button != null) {
config.isButton = properties.button; config.isButton = properties.button;
} }
if (properties.textField != null) {
config.isTextField = properties.textField;
}
if (properties.focused != null) {
config.isFocused = properties.focused;
}
if (properties.enabled != null) {
config.isEnabled = properties.enabled;
}
if (properties.inMutuallyExclusiveGroup != null) {
config.isInMutuallyExclusiveGroup = properties.inMutuallyExclusiveGroup;
}
if (properties.header != null) {
config.isHeader = properties.header;
}
if (properties.label != null) { if (properties.label != null) {
config.label = properties.label; config.label = properties.label;
} }

View File

@ -3015,6 +3015,10 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
bool checked, bool checked,
bool selected, bool selected,
bool button, bool button,
bool header,
bool textField,
bool focused,
bool inMutuallyExclusiveGroup,
String label, String label,
String value, String value,
String increasedValue, String increasedValue,
@ -3045,6 +3049,10 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
_checked = checked, _checked = checked,
_selected = selected, _selected = selected,
_button = button, _button = button,
_header = header,
_textField = textField,
_focused = focused,
_inMutuallyExclusiveGroup = inMutuallyExclusiveGroup,
_label = label, _label = label,
_value = value, _value = value,
_increasedValue = increasedValue, _increasedValue = increasedValue,
@ -3152,6 +3160,47 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
markNeedsSemanticsUpdate(); markNeedsSemanticsUpdate();
} }
/// If non-null, sets the [SemanticsNode.isHeader] semantic to the given value.
bool get header => _header;
bool _header;
set header(bool value) {
if (header == value)
return;
_header = value;
markNeedsSemanticsUpdate();
}
/// If non-null, sets the [SemanticsNode.isTextField] semantic to the given value.
bool get textField => _textField;
bool _textField;
set textField(bool value) {
if (textField == value)
return;
_textField = value;
markNeedsSemanticsUpdate();
}
/// If non-null, sets the [SemanticsNode.isFocused] semantic to the given value.
bool get focused => _focused;
bool _focused;
set focused(bool value) {
if (focused == value)
return;
_focused = value;
markNeedsSemanticsUpdate();
}
/// If non-null, sets the [SemanticsNode.isInMutuallyExclusiveGroup] semantic
/// to the given value.
bool get inMutuallyExclusiveGroup => _inMutuallyExclusiveGroup;
bool _inMutuallyExclusiveGroup;
set inMutuallyExclusiveGroup(bool value) {
if (inMutuallyExclusiveGroup == value)
return;
_inMutuallyExclusiveGroup = value;
markNeedsSemanticsUpdate();
}
/// If non-null, sets the [SemanticsNode.label] semantic to the given value. /// If non-null, sets the [SemanticsNode.label] semantic to the given value.
/// ///
/// The reading direction is given by [textDirection]. /// The reading direction is given by [textDirection].
@ -3581,6 +3630,14 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
config.isSelected = selected; config.isSelected = selected;
if (button != null) if (button != null)
config.isButton = button; config.isButton = button;
if (header != null)
config.isHeader = header;
if (textField != null)
config.isTextField = textField;
if (focused != null)
config.isFocused = focused;
if (inMutuallyExclusiveGroup != null)
config.isInMutuallyExclusiveGroup = inMutuallyExclusiveGroup;
if (label != null) if (label != null)
config.label = label; config.label = label;
if (value != null) if (value != null)

View File

@ -315,6 +315,10 @@ class SemanticsProperties extends DiagnosticableTree {
this.checked, this.checked,
this.selected, this.selected,
this.button, this.button,
this.header,
this.textField,
this.focused,
this.inMutuallyExclusiveGroup,
this.label, this.label,
this.value, this.value,
this.increasedValue, this.increasedValue,
@ -366,6 +370,35 @@ class SemanticsProperties extends DiagnosticableTree {
/// is focused. /// is focused.
final bool button; final bool button;
/// If non-null, indicates that this subtree represents a header.
///
/// A header divides into sections. For example, an address book application
/// might define headers A, B, C, etc. to divide the list of alphabetically
/// sorted contacts into sections.
final bool header;
/// If non-null, indicates that this subtree represents a text field.
///
/// TalkBack/VoiceOver provide special affordances to enter text into a
/// text field.
final bool textField;
/// If non-null, whether the node currently holds input focus.
///
/// At most one node in the tree should hold input focus at any point in time.
///
/// Input focus (indicates that the node will receive keyboard events) is not
/// to be confused with accessibility focus. Accessibility focus is the
/// green/black rectangular that TalkBack/VoiceOver on the screen and is
/// separate from input focus.
final bool focused;
/// If non-null, whether a semantic node is in a mutually exclusive group.
///
/// For example, a radio button is in a mutually exclusive group because only
/// one radio button in that group can be marked as [checked].
final bool inMutuallyExclusiveGroup;
/// Provides a textual description of the widget. /// Provides a textual description of the widget.
/// ///
/// If a label is provided, there must either by an ambient [Directionality] /// If a label is provided, there must either by an ambient [Directionality]
@ -2365,6 +2398,12 @@ class SemanticsConfiguration {
_setFlag(SemanticsFlag.isButton, value); _setFlag(SemanticsFlag.isButton, value);
} }
/// Whether the owning [RenderObject] is a header (true) or not (false).
bool get isHeader => _hasFlag(SemanticsFlag.isHeader);
set isHeader(bool value) {
_setFlag(SemanticsFlag.isHeader, value);
}
/// Whether the owning [RenderObject] is a text field. /// Whether the owning [RenderObject] is a text field.
bool get isTextField => _hasFlag(SemanticsFlag.isTextField); bool get isTextField => _hasFlag(SemanticsFlag.isTextField);
set isTextField(bool value) { set isTextField(bool value) {

View File

@ -4847,6 +4847,10 @@ class Semantics extends SingleChildRenderObjectWidget {
bool checked, bool checked,
bool selected, bool selected,
bool button, bool button,
bool header,
bool textField,
bool focused,
bool inMutuallyExclusiveGroup,
String label, String label,
String value, String value,
String increasedValue, String increasedValue,
@ -4881,6 +4885,10 @@ class Semantics extends SingleChildRenderObjectWidget {
checked: checked, checked: checked,
selected: selected, selected: selected,
button: button, button: button,
header: header,
textField: textField,
focused: focused,
inMutuallyExclusiveGroup: inMutuallyExclusiveGroup,
label: label, label: label,
value: value, value: value,
increasedValue: increasedValue, increasedValue: increasedValue,
@ -4960,6 +4968,10 @@ class Semantics extends SingleChildRenderObjectWidget {
checked: properties.checked, checked: properties.checked,
selected: properties.selected, selected: properties.selected,
button: properties.button, button: properties.button,
header: properties.header,
textField: properties.textField,
focused: properties.focused,
inMutuallyExclusiveGroup: properties.inMutuallyExclusiveGroup,
label: properties.label, label: properties.label,
value: properties.value, value: properties.value,
increasedValue: properties.increasedValue, increasedValue: properties.increasedValue,

View File

@ -3,6 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async'; import 'dart:async';
import 'dart:ui';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
@ -397,6 +398,52 @@ void _defineTests() {
semantics.dispose(); semantics.dispose();
}); });
testWidgets('Supports all flags', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
await tester.pumpWidget(new CustomPaint(
painter: new _PainterWithSemantics(
semantics: new CustomPainterSemantics(
key: const ValueKey<int>(1),
rect: new Rect.fromLTRB(1.0, 2.0, 3.0, 4.0),
properties: const SemanticsProperties(
enabled: true,
checked: true,
selected: true,
button: true,
textField: true,
focused: true,
inMutuallyExclusiveGroup: true,
header: true,
),
),
),
));
const int expectedId = 2;
final TestSemantics expectedSemantics = new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
id: 1,
previousNodeId: -1,
nextNodeId: expectedId,
children: <TestSemantics>[
new TestSemantics.rootChild(
id: expectedId,
rect: TestSemantics.fullScreen,
flags: SemanticsFlag.values.values.toList(),
previousNodeId: 1,
nextNodeId: -1,
),
]
),
],
);
expect(semantics, hasSemantics(expectedSemantics, ignoreRect: true, ignoreTransform: true));
semantics.dispose();
});
group('diffing', () { group('diffing', () {
testWidgets('complains about duplicate keys', (WidgetTester tester) async { testWidgets('complains about duplicate keys', (WidgetTester tester) async {
final SemanticsTester semanticsTester = new SemanticsTester(tester); final SemanticsTester semanticsTester = new SemanticsTester(tester);

View File

@ -449,6 +449,39 @@ void main() {
semantics.dispose(); semantics.dispose();
}); });
testWidgets('Semantics widget supports all flags', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
await tester.pumpWidget(
new Semantics(
container: true,
// flags
enabled: true,
checked: true,
selected: true,
button: true,
textField: true,
focused: true,
inMutuallyExclusiveGroup: true,
header: true,
)
);
final TestSemantics expectedSemantics = new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
rect: TestSemantics.fullScreen,
flags: SemanticsFlag.values.values.toList(),
previousNodeId: -1,
nextNodeId: -1,
),
],
);
expect(semantics, hasSemantics(expectedSemantics, ignoreId: true));
semantics.dispose();
});
testWidgets('Actions can be replaced without triggering semantics update', (WidgetTester tester) async { testWidgets('Actions can be replaced without triggering semantics update', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester); final SemanticsTester semantics = new SemanticsTester(tester);
int semanticsUpdateCount = 0; int semanticsUpdateCount = 0;