Unverified Commit 2ebc7bee authored by chunhtai's avatar chunhtai Committed by GitHub

Adds tooltip to semantics node (#87684)

parent ec6481be
...@@ -698,7 +698,7 @@ class TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin { ...@@ -698,7 +698,7 @@ class TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin {
_enableFeedback = widget.enableFeedback ?? tooltipTheme.enableFeedback ?? _defaultEnableFeedback; _enableFeedback = widget.enableFeedback ?? tooltipTheme.enableFeedback ?? _defaultEnableFeedback;
Widget result = Semantics( Widget result = Semantics(
label: _excludeFromSemantics tooltip: _excludeFromSemantics
? null ? null
: _tooltipMessage, : _tooltipMessage,
child: widget.child, child: widget.child,
......
...@@ -3859,6 +3859,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -3859,6 +3859,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
AttributedString? attributedIncreasedValue, AttributedString? attributedIncreasedValue,
AttributedString? attributedDecreasedValue, AttributedString? attributedDecreasedValue,
AttributedString? attributedHint, AttributedString? attributedHint,
String? tooltip,
SemanticsHintOverrides? hintOverrides, SemanticsHintOverrides? hintOverrides,
TextDirection? textDirection, TextDirection? textDirection,
SemanticsSortKey? sortKey, SemanticsSortKey? sortKey,
...@@ -3917,6 +3918,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -3917,6 +3918,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
_attributedIncreasedValue = attributedIncreasedValue, _attributedIncreasedValue = attributedIncreasedValue,
_attributedDecreasedValue = attributedDecreasedValue, _attributedDecreasedValue = attributedDecreasedValue,
_attributedHint = attributedHint, _attributedHint = attributedHint,
_tooltip = tooltip,
_hintOverrides = hintOverrides, _hintOverrides = hintOverrides,
_textDirection = textDirection, _textDirection = textDirection,
_sortKey = sortKey, _sortKey = sortKey,
...@@ -4311,6 +4313,18 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -4311,6 +4313,18 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
markNeedsSemanticsUpdate(); markNeedsSemanticsUpdate();
} }
/// If non-null, sets the [SemanticsNode.tooltip] semantic to the given value.
///
/// The reading direction is given by [textDirection].
String? get tooltip => _tooltip;
String? _tooltip;
set tooltip(String? value) {
if (_tooltip == value)
return;
_tooltip = value;
markNeedsSemanticsUpdate();
}
/// If non-null, sets the [SemanticsConfiguration.hintOverrides] to the given value. /// If non-null, sets the [SemanticsConfiguration.hintOverrides] to the given value.
SemanticsHintOverrides? get hintOverrides => _hintOverrides; SemanticsHintOverrides? get hintOverrides => _hintOverrides;
SemanticsHintOverrides? _hintOverrides; SemanticsHintOverrides? _hintOverrides;
...@@ -4843,6 +4857,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -4843,6 +4857,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
config.attributedDecreasedValue = attributedDecreasedValue!; config.attributedDecreasedValue = attributedDecreasedValue!;
if (attributedHint != null) if (attributedHint != null)
config.attributedHint = attributedHint!; config.attributedHint = attributedHint!;
if (tooltip != null)
config.tooltip = tooltip!;
if (hintOverrides != null && hintOverrides!.isNotEmpty) if (hintOverrides != null && hintOverrides!.isNotEmpty)
config.hintOverrides = hintOverrides; config.hintOverrides = hintOverrides;
if (scopesRoute != null) if (scopesRoute != null)
......
...@@ -316,6 +316,7 @@ class SemanticsData with Diagnosticable { ...@@ -316,6 +316,7 @@ class SemanticsData with Diagnosticable {
required this.attributedIncreasedValue, required this.attributedIncreasedValue,
required this.attributedDecreasedValue, required this.attributedDecreasedValue,
required this.attributedHint, required this.attributedHint,
required this.tooltip,
required this.textDirection, required this.textDirection,
required this.rect, required this.rect,
required this.elevation, required this.elevation,
...@@ -339,6 +340,7 @@ class SemanticsData with Diagnosticable { ...@@ -339,6 +340,7 @@ class SemanticsData with Diagnosticable {
assert(attributedDecreasedValue != null), assert(attributedDecreasedValue != null),
assert(attributedIncreasedValue != null), assert(attributedIncreasedValue != null),
assert(attributedHint != null), assert(attributedHint != null),
assert(tooltip == '' || textDirection != null, 'A SemanticsData object with tooltip "$tooltip" had a null textDirection.'),
assert(attributedLabel.string == '' || textDirection != null, 'A SemanticsData object with label "${attributedLabel.string}" had a null textDirection.'), assert(attributedLabel.string == '' || textDirection != null, 'A SemanticsData object with label "${attributedLabel.string}" had a null textDirection.'),
assert(attributedValue.string == '' || textDirection != null, 'A SemanticsData object with value "${attributedValue.string}" had a null textDirection.'), assert(attributedValue.string == '' || textDirection != null, 'A SemanticsData object with value "${attributedValue.string}" had a null textDirection.'),
assert(attributedDecreasedValue.string == '' || textDirection != null, 'A SemanticsData object with decreasedValue "${attributedDecreasedValue.string}" had a null textDirection.'), assert(attributedDecreasedValue.string == '' || textDirection != null, 'A SemanticsData object with decreasedValue "${attributedDecreasedValue.string}" had a null textDirection.'),
...@@ -429,6 +431,11 @@ class SemanticsData with Diagnosticable { ...@@ -429,6 +431,11 @@ class SemanticsData with Diagnosticable {
/// See also [hint], which exposes just the raw text. /// See also [hint], which exposes just the raw text.
final AttributedString attributedHint; final AttributedString attributedHint;
/// A textual description of the widget's tooltip.
///
/// The reading direction is given by [textDirection].
final String tooltip;
/// The reading direction for the text in [label], [value], /// The reading direction for the text in [label], [value],
/// [increasedValue], [decreasedValue], and [hint]. /// [increasedValue], [decreasedValue], and [hint].
final TextDirection? textDirection; final TextDirection? textDirection;
...@@ -587,6 +594,7 @@ class SemanticsData with Diagnosticable { ...@@ -587,6 +594,7 @@ class SemanticsData with Diagnosticable {
properties.add(AttributedStringProperty('increasedValue', attributedIncreasedValue)); properties.add(AttributedStringProperty('increasedValue', attributedIncreasedValue));
properties.add(AttributedStringProperty('decreasedValue', attributedDecreasedValue)); properties.add(AttributedStringProperty('decreasedValue', attributedDecreasedValue));
properties.add(AttributedStringProperty('hint', attributedHint)); properties.add(AttributedStringProperty('hint', attributedHint));
properties.add(StringProperty('tooltip', tooltip, defaultValue: ''));
properties.add(EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null)); properties.add(EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
if (textSelection?.isValid ?? false) if (textSelection?.isValid ?? false)
properties.add(MessageProperty('textSelection', '[${textSelection!.start}, ${textSelection!.end}]')); properties.add(MessageProperty('textSelection', '[${textSelection!.start}, ${textSelection!.end}]'));
...@@ -610,6 +618,7 @@ class SemanticsData with Diagnosticable { ...@@ -610,6 +618,7 @@ class SemanticsData with Diagnosticable {
&& other.attributedIncreasedValue == attributedIncreasedValue && other.attributedIncreasedValue == attributedIncreasedValue
&& other.attributedDecreasedValue == attributedDecreasedValue && other.attributedDecreasedValue == attributedDecreasedValue
&& other.attributedHint == attributedHint && other.attributedHint == attributedHint
&& other.tooltip == tooltip
&& other.textDirection == textDirection && other.textDirection == textDirection
&& other.rect == rect && other.rect == rect
&& setEquals(other.tags, tags) && setEquals(other.tags, tags)
...@@ -637,6 +646,7 @@ class SemanticsData with Diagnosticable { ...@@ -637,6 +646,7 @@ class SemanticsData with Diagnosticable {
attributedIncreasedValue, attributedIncreasedValue,
attributedDecreasedValue, attributedDecreasedValue,
attributedHint, attributedHint,
tooltip,
textDirection, textDirection,
rect, rect,
tags, tags,
...@@ -648,8 +658,8 @@ class SemanticsData with Diagnosticable { ...@@ -648,8 +658,8 @@ class SemanticsData with Diagnosticable {
scrollExtentMin, scrollExtentMin,
platformViewId, platformViewId,
maxValueLength, maxValueLength,
currentValueLength,
Object.hash( Object.hash(
currentValueLength,
transform, transform,
elevation, elevation,
thickness, thickness,
...@@ -785,6 +795,7 @@ class SemanticsProperties extends DiagnosticableTree { ...@@ -785,6 +795,7 @@ class SemanticsProperties extends DiagnosticableTree {
this.decreasedValue, this.decreasedValue,
this.attributedDecreasedValue, this.attributedDecreasedValue,
this.hint, this.hint,
this.tooltip,
this.attributedHint, this.attributedHint,
this.hintOverrides, this.hintOverrides,
this.textDirection, this.textDirection,
...@@ -1178,6 +1189,16 @@ class SemanticsProperties extends DiagnosticableTree { ...@@ -1178,6 +1189,16 @@ class SemanticsProperties extends DiagnosticableTree {
/// * [hint] for a plain string version of this property. /// * [hint] for a plain string version of this property.
final AttributedString? attributedHint; final AttributedString? attributedHint;
/// Provides a textual description of the widget's tooltip.
///
/// In Android, this property sets the `AccessibilityNodeInfo.setTooltipText`.
/// In iOS, this property is appended to the end of the
/// `UIAccessibilityElement.accessibilityLabel`.
///
/// If a [tooltip] is provided, there must either by an ambient
/// [Directionality] or an explicit [textDirection] should be provided.
final String? tooltip;
/// Provides hint values which override the default hints on supported /// Provides hint values which override the default hints on supported
/// platforms. /// platforms.
/// ///
...@@ -1469,6 +1490,7 @@ class SemanticsProperties extends DiagnosticableTree { ...@@ -1469,6 +1490,7 @@ class SemanticsProperties extends DiagnosticableTree {
properties.add(AttributedStringProperty('attributedDecreasedValue', attributedDecreasedValue, defaultValue: null)); properties.add(AttributedStringProperty('attributedDecreasedValue', attributedDecreasedValue, defaultValue: null));
properties.add(StringProperty('hint', hint, defaultValue: null)); properties.add(StringProperty('hint', hint, defaultValue: null));
properties.add(AttributedStringProperty('attributedHint', attributedHint, defaultValue: null)); properties.add(AttributedStringProperty('attributedHint', attributedHint, defaultValue: null));
properties.add(StringProperty('tooltip', tooltip));
properties.add(EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null)); properties.add(EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
properties.add(DiagnosticsProperty<SemanticsSortKey>('sortKey', sortKey, defaultValue: null)); properties.add(DiagnosticsProperty<SemanticsSortKey>('sortKey', sortKey, defaultValue: null));
properties.add(DiagnosticsProperty<SemanticsHintOverrides>('hintOverrides', hintOverrides, defaultValue: null)); properties.add(DiagnosticsProperty<SemanticsHintOverrides>('hintOverrides', hintOverrides, defaultValue: null));
...@@ -1898,6 +1920,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -1898,6 +1920,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
|| _attributedValue != config.attributedValue || _attributedValue != config.attributedValue
|| _attributedIncreasedValue != config.attributedIncreasedValue || _attributedIncreasedValue != config.attributedIncreasedValue
|| _attributedDecreasedValue != config.attributedDecreasedValue || _attributedDecreasedValue != config.attributedDecreasedValue
|| _tooltip != config.tooltip
|| _flags != config._flags || _flags != config._flags
|| _textDirection != config.textDirection || _textDirection != config.textDirection
|| _sortKey != config._sortKey || _sortKey != config._sortKey
...@@ -2027,6 +2050,12 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -2027,6 +2050,12 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
AttributedString get attributedHint => _attributedHint; AttributedString get attributedHint => _attributedHint;
AttributedString _attributedHint = _kEmptyConfig.attributedHint; AttributedString _attributedHint = _kEmptyConfig.attributedHint;
/// A textual description of the widget's tooltip.
///
/// The reading direction is given by [textDirection].
String get tooltip => _tooltip;
String _tooltip = _kEmptyConfig.tooltip;
/// The elevation along the z-axis at which the [rect] of this [SemanticsNode] /// The elevation along the z-axis at which the [rect] of this [SemanticsNode]
/// is located above its parent. /// is located above its parent.
/// ///
...@@ -2235,6 +2264,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -2235,6 +2264,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
_attributedIncreasedValue = config.attributedIncreasedValue; _attributedIncreasedValue = config.attributedIncreasedValue;
_attributedDecreasedValue = config.attributedDecreasedValue; _attributedDecreasedValue = config.attributedDecreasedValue;
_attributedHint = config.attributedHint; _attributedHint = config.attributedHint;
_tooltip = config.tooltip;
_hintOverrides = config.hintOverrides; _hintOverrides = config.hintOverrides;
_elevation = config.elevation; _elevation = config.elevation;
_thickness = config.thickness; _thickness = config.thickness;
...@@ -2282,6 +2312,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -2282,6 +2312,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
AttributedString attributedIncreasedValue = _attributedIncreasedValue; AttributedString attributedIncreasedValue = _attributedIncreasedValue;
AttributedString attributedDecreasedValue = _attributedDecreasedValue; AttributedString attributedDecreasedValue = _attributedDecreasedValue;
AttributedString attributedHint = _attributedHint; AttributedString attributedHint = _attributedHint;
String tooltip = _tooltip;
TextDirection? textDirection = _textDirection; TextDirection? textDirection = _textDirection;
Set<SemanticsTag>? mergedTags = tags == null ? null : Set<SemanticsTag>.of(tags!); Set<SemanticsTag>? mergedTags = tags == null ? null : Set<SemanticsTag>.of(tags!);
TextSelection? textSelection = _textSelection; TextSelection? textSelection = _textSelection;
...@@ -2336,6 +2367,8 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -2336,6 +2367,8 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
attributedIncreasedValue = node._attributedIncreasedValue; attributedIncreasedValue = node._attributedIncreasedValue;
if (attributedDecreasedValue == null || attributedDecreasedValue.string == '') if (attributedDecreasedValue == null || attributedDecreasedValue.string == '')
attributedDecreasedValue = node._attributedDecreasedValue; attributedDecreasedValue = node._attributedDecreasedValue;
if (tooltip == '')
tooltip = node._tooltip;
if (node.tags != null) { if (node.tags != null) {
mergedTags ??= <SemanticsTag>{}; mergedTags ??= <SemanticsTag>{};
mergedTags!.addAll(node.tags!); mergedTags!.addAll(node.tags!);
...@@ -2385,6 +2418,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -2385,6 +2418,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
attributedIncreasedValue: attributedIncreasedValue, attributedIncreasedValue: attributedIncreasedValue,
attributedDecreasedValue: attributedDecreasedValue, attributedDecreasedValue: attributedDecreasedValue,
attributedHint: attributedHint, attributedHint: attributedHint,
tooltip: tooltip,
textDirection: textDirection, textDirection: textDirection,
rect: rect, rect: rect,
transform: transform, transform: transform,
...@@ -2457,6 +2491,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -2457,6 +2491,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
decreasedValueAttributes: data.attributedDecreasedValue.attributes, decreasedValueAttributes: data.attributedDecreasedValue.attributes,
hint: data.attributedHint.string, hint: data.attributedHint.string,
hintAttributes: data.attributedHint.attributes, hintAttributes: data.attributedHint.attributes,
tooltip: data.tooltip,
textDirection: data.textDirection, textDirection: data.textDirection,
textSelectionBase: data.textSelection != null ? data.textSelection!.baseOffset : -1, textSelectionBase: data.textSelection != null ? data.textSelection!.baseOffset : -1,
textSelectionExtent: data.textSelection != null ? data.textSelection!.extentOffset : -1, textSelectionExtent: data.textSelection != null ? data.textSelection!.extentOffset : -1,
...@@ -2595,6 +2630,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -2595,6 +2630,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
properties.add(AttributedStringProperty('increasedValue', _attributedIncreasedValue)); properties.add(AttributedStringProperty('increasedValue', _attributedIncreasedValue));
properties.add(AttributedStringProperty('decreasedValue', _attributedDecreasedValue)); properties.add(AttributedStringProperty('decreasedValue', _attributedDecreasedValue));
properties.add(AttributedStringProperty('hint', _attributedHint)); properties.add(AttributedStringProperty('hint', _attributedHint));
properties.add(StringProperty('tooltip', _tooltip, defaultValue: ''));
properties.add(EnumProperty<TextDirection>('textDirection', _textDirection, defaultValue: null)); properties.add(EnumProperty<TextDirection>('textDirection', _textDirection, defaultValue: null));
properties.add(DiagnosticsProperty<SemanticsSortKey>('sortKey', sortKey, defaultValue: null)); properties.add(DiagnosticsProperty<SemanticsSortKey>('sortKey', sortKey, defaultValue: null));
if (_textSelection?.isValid ?? false) if (_textSelection?.isValid ?? false)
...@@ -3955,6 +3991,16 @@ class SemanticsConfiguration { ...@@ -3955,6 +3991,16 @@ class SemanticsConfiguration {
_hasBeenAnnotated = true; _hasBeenAnnotated = true;
} }
/// A textual description of the widget's tooltip.
///
/// The reading direction is given by [textDirection].
String get tooltip => _tooltip;
String _tooltip = '';
set tooltip(String tooltip) {
_tooltip = tooltip;
_hasBeenAnnotated = true;
}
/// Provides hint values which override the default hints on supported /// Provides hint values which override the default hints on supported
/// platforms. /// platforms.
SemanticsHintOverrides? get hintOverrides => _hintOverrides; SemanticsHintOverrides? get hintOverrides => _hintOverrides;
...@@ -4420,6 +4466,8 @@ class SemanticsConfiguration { ...@@ -4420,6 +4466,8 @@ class SemanticsConfiguration {
otherAttributedString: child._attributedHint, otherAttributedString: child._attributedHint,
otherTextDirection: child.textDirection, otherTextDirection: child.textDirection,
); );
if (_tooltip == '')
_tooltip = child._tooltip;
_thickness = math.max(_thickness, child._thickness + child._elevation); _thickness = math.max(_thickness, child._thickness + child._elevation);
...@@ -4442,6 +4490,7 @@ class SemanticsConfiguration { ...@@ -4442,6 +4490,7 @@ class SemanticsConfiguration {
.._attributedDecreasedValue = _attributedDecreasedValue .._attributedDecreasedValue = _attributedDecreasedValue
.._attributedHint = _attributedHint .._attributedHint = _attributedHint
.._hintOverrides = _hintOverrides .._hintOverrides = _hintOverrides
.._tooltip = _tooltip
.._elevation = _elevation .._elevation = _elevation
.._thickness = _thickness .._thickness = _thickness
.._flags = _flags .._flags = _flags
......
...@@ -6695,6 +6695,7 @@ class Semantics extends SingleChildRenderObjectWidget { ...@@ -6695,6 +6695,7 @@ class Semantics extends SingleChildRenderObjectWidget {
AttributedString? attributedDecreasedValue, AttributedString? attributedDecreasedValue,
String? hint, String? hint,
AttributedString? attributedHint, AttributedString? attributedHint,
String? tooltip,
String? onTapHint, String? onTapHint,
String? onLongPressHint, String? onLongPressHint,
TextDirection? textDirection, TextDirection? textDirection,
...@@ -6759,6 +6760,7 @@ class Semantics extends SingleChildRenderObjectWidget { ...@@ -6759,6 +6760,7 @@ class Semantics extends SingleChildRenderObjectWidget {
attributedDecreasedValue: attributedDecreasedValue, attributedDecreasedValue: attributedDecreasedValue,
hint: hint, hint: hint,
attributedHint: attributedHint, attributedHint: attributedHint,
tooltip: tooltip,
textDirection: textDirection, textDirection: textDirection,
sortKey: sortKey, sortKey: sortKey,
tagForChildren: tagForChildren, tagForChildren: tagForChildren,
...@@ -6901,6 +6903,7 @@ class Semantics extends SingleChildRenderObjectWidget { ...@@ -6901,6 +6903,7 @@ class Semantics extends SingleChildRenderObjectWidget {
attributedIncreasedValue: _effectiveAttributedIncreasedValue, attributedIncreasedValue: _effectiveAttributedIncreasedValue,
attributedDecreasedValue: _effectiveAttributedDecreasedValue, attributedDecreasedValue: _effectiveAttributedDecreasedValue,
attributedHint: _effectiveAttributedHint, attributedHint: _effectiveAttributedHint,
tooltip: properties.tooltip,
hintOverrides: properties.hintOverrides, hintOverrides: properties.hintOverrides,
textDirection: _getTextDirection(context), textDirection: _getTextDirection(context),
sortKey: properties.sortKey, sortKey: properties.sortKey,
...@@ -6936,7 +6939,8 @@ class Semantics extends SingleChildRenderObjectWidget { ...@@ -6936,7 +6939,8 @@ class Semantics extends SingleChildRenderObjectWidget {
final bool containsText = properties.attributedLabel != null || final bool containsText = properties.attributedLabel != null ||
properties.label != null || properties.label != null ||
properties.value != null || properties.value != null ||
properties.hint != null; properties.hint != null ||
properties.tooltip != null;
if (!containsText) if (!containsText)
return null; return null;
...@@ -6977,6 +6981,7 @@ class Semantics extends SingleChildRenderObjectWidget { ...@@ -6977,6 +6981,7 @@ class Semantics extends SingleChildRenderObjectWidget {
..attributedIncreasedValue = _effectiveAttributedIncreasedValue ..attributedIncreasedValue = _effectiveAttributedIncreasedValue
..attributedDecreasedValue = _effectiveAttributedDecreasedValue ..attributedDecreasedValue = _effectiveAttributedDecreasedValue
..attributedHint = _effectiveAttributedHint ..attributedHint = _effectiveAttributedHint
..tooltip = properties.tooltip
..hintOverrides = properties.hintOverrides ..hintOverrides = properties.hintOverrides
..namesRoute = properties.namesRoute ..namesRoute = properties.namesRoute
..textDirection = _getTextDirection(context) ..textDirection = _getTextDirection(context)
......
...@@ -281,27 +281,33 @@ class _SemanticsDebuggerPainter extends CustomPainter { ...@@ -281,27 +281,33 @@ class _SemanticsDebuggerPainter extends CustomPainter {
assert(data.attributedLabel != null); assert(data.attributedLabel != null);
final String message; final String message;
if (data.attributedLabel.string.isEmpty) { final String tooltipAndLabel = <String>[
if (data.tooltip.isNotEmpty)
data.tooltip,
if (data.attributedLabel.string.isNotEmpty)
data.attributedLabel.string,
].join('\n');
if (tooltipAndLabel.isEmpty) {
message = annotations.join('; '); message = annotations.join('; ');
} else { } else {
final String label; final String effectivelabel;
if (data.textDirection == null) { if (data.textDirection == null) {
label = '${Unicode.FSI}${data.attributedLabel.string}${Unicode.PDI}'; effectivelabel = '${Unicode.FSI}$tooltipAndLabel${Unicode.PDI}';
annotations.insert(0, 'MISSING TEXT DIRECTION'); annotations.insert(0, 'MISSING TEXT DIRECTION');
} else { } else {
switch (data.textDirection!) { switch (data.textDirection!) {
case TextDirection.rtl: case TextDirection.rtl:
label = '${Unicode.RLI}${data.attributedLabel.string}${Unicode.PDF}'; effectivelabel = '${Unicode.RLI}$tooltipAndLabel${Unicode.PDF}';
break; break;
case TextDirection.ltr: case TextDirection.ltr:
label = data.attributedLabel.string; effectivelabel = tooltipAndLabel;
break; break;
} }
} }
if (annotations.isEmpty) { if (annotations.isEmpty) {
message = label; message = effectivelabel;
} else { } else {
message = '$label (${annotations.join('; ')})'; message = '$effectivelabel (${annotations.join('; ')})';
} }
} }
......
...@@ -154,7 +154,7 @@ void main() { ...@@ -154,7 +154,7 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(tester.getSemantics(find.byType(BackButton)), matchesSemantics( expect(tester.getSemantics(find.byType(BackButton)), matchesSemantics(
label: 'Back', tooltip: 'Back',
isButton: true, isButton: true,
hasEnabledState: true, hasEnabledState: true,
isEnabled: true, isEnabled: true,
......
...@@ -649,7 +649,7 @@ void main() { ...@@ -649,7 +649,7 @@ void main() {
// Prev/Next month buttons. // Prev/Next month buttons.
expect(tester.getSemantics(previousMonthIcon), matchesSemantics( expect(tester.getSemantics(previousMonthIcon), matchesSemantics(
label: 'Previous month', tooltip: 'Previous month',
isButton: true, isButton: true,
hasTapAction: true, hasTapAction: true,
isEnabled: true, isEnabled: true,
...@@ -657,7 +657,7 @@ void main() { ...@@ -657,7 +657,7 @@ void main() {
isFocusable: true, isFocusable: true,
)); ));
expect(tester.getSemantics(nextMonthIcon), matchesSemantics( expect(tester.getSemantics(nextMonthIcon), matchesSemantics(
label: 'Next month', tooltip: 'Next month',
isButton: true, isButton: true,
hasTapAction: true, hasTapAction: true,
isEnabled: true, isEnabled: true,
......
...@@ -1964,7 +1964,7 @@ void main() { ...@@ -1964,7 +1964,7 @@ void main() {
], ],
children: <TestSemantics>[ children: <TestSemantics>[
TestSemantics( TestSemantics(
label: 'Delete', tooltip: 'Delete',
actions: <SemanticsAction>[SemanticsAction.tap], actions: <SemanticsAction>[SemanticsAction.tap],
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
flags: <SemanticsFlag>[ flags: <SemanticsFlag>[
......
...@@ -817,7 +817,7 @@ void main() { ...@@ -817,7 +817,7 @@ void main() {
// Input mode toggle button // Input mode toggle button
expect(tester.getSemantics(switchToInputIcon), matchesSemantics( expect(tester.getSemantics(switchToInputIcon), matchesSemantics(
label: 'Switch to input', tooltip: 'Switch to input',
isButton: true, isButton: true,
hasTapAction: true, hasTapAction: true,
isEnabled: true, isEnabled: true,
...@@ -860,7 +860,7 @@ void main() { ...@@ -860,7 +860,7 @@ void main() {
// Input mode toggle button // Input mode toggle button
expect(tester.getSemantics(switchToCalendarIcon), matchesSemantics( expect(tester.getSemantics(switchToCalendarIcon), matchesSemantics(
label: 'Switch to calendar', tooltip: 'Switch to calendar',
isButton: true, isButton: true,
hasTapAction: true, hasTapAction: true,
isEnabled: true, isEnabled: true,
......
...@@ -670,7 +670,7 @@ void main() { ...@@ -670,7 +670,7 @@ void main() {
semantics.dispose(); semantics.dispose();
}); });
testWidgets('Tooltip is used as semantics label', (WidgetTester tester) async { testWidgets('Tooltip is used as semantics tooltip', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester); final SemanticsTester semantics = SemanticsTester(tester);
await tester.pumpWidget( await tester.pumpWidget(
...@@ -697,7 +697,7 @@ void main() { ...@@ -697,7 +697,7 @@ void main() {
], ],
children: <TestSemantics>[ children: <TestSemantics>[
TestSemantics( TestSemantics(
label: 'Add Photo', tooltip: 'Add Photo',
actions: <SemanticsAction>[ actions: <SemanticsAction>[
SemanticsAction.tap, SemanticsAction.tap,
], ],
......
...@@ -616,7 +616,7 @@ void main() { ...@@ -616,7 +616,7 @@ void main() {
SemanticsFlag.isFocusable, SemanticsFlag.isFocusable,
], ],
actions: <SemanticsAction>[SemanticsAction.tap], actions: <SemanticsAction>[SemanticsAction.tap],
label: 'Back', tooltip: 'Back',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
), ),
TestSemantics( TestSemantics(
......
...@@ -1263,7 +1263,7 @@ void main() { ...@@ -1263,7 +1263,7 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
TestSemantics.rootChild( TestSemantics.rootChild(
id: 1, id: 1,
label: 'TIP', tooltip: 'TIP',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
), ),
], ],
...@@ -1462,7 +1462,8 @@ void main() { ...@@ -1462,7 +1462,8 @@ void main() {
flags: <SemanticsFlag>[SemanticsFlag.scopesRoute], flags: <SemanticsFlag>[SemanticsFlag.scopesRoute],
children: <TestSemantics>[ children: <TestSemantics>[
TestSemantics( TestSemantics(
label: 'Foo\nBar', tooltip: 'Foo',
label: 'Bar',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
), ),
], ],
......
...@@ -1066,7 +1066,8 @@ void main() { ...@@ -1066,7 +1066,8 @@ void main() {
flags: <SemanticsFlag>[SemanticsFlag.scopesRoute], flags: <SemanticsFlag>[SemanticsFlag.scopesRoute],
children: <TestSemantics>[ children: <TestSemantics>[
TestSemantics( TestSemantics(
label: 'Foo\nBar', tooltip: 'Foo',
label: 'Bar',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
), ),
], ],
...@@ -1108,7 +1109,8 @@ void main() { ...@@ -1108,7 +1109,8 @@ void main() {
flags: <SemanticsFlag>[SemanticsFlag.scopesRoute], flags: <SemanticsFlag>[SemanticsFlag.scopesRoute],
children: <TestSemantics>[ children: <TestSemantics>[
TestSemantics( TestSemantics(
label: 'Foo\nBar', tooltip: 'Foo',
label: 'Bar',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
), ),
], ],
......
...@@ -561,6 +561,7 @@ void main() { ...@@ -561,6 +561,7 @@ void main() {
' increasedValue: ""\n' ' increasedValue: ""\n'
' decreasedValue: ""\n' ' decreasedValue: ""\n'
' hint: ""\n' ' hint: ""\n'
' tooltip: ""\n'
' textDirection: null\n' ' textDirection: null\n'
' sortKey: null\n' ' sortKey: null\n'
' platformViewId: null\n' ' platformViewId: null\n'
...@@ -659,6 +660,7 @@ void main() { ...@@ -659,6 +660,7 @@ void main() {
' increasedValue: ""\n' ' increasedValue: ""\n'
' decreasedValue: ""\n' ' decreasedValue: ""\n'
' hint: ""\n' ' hint: ""\n'
' tooltip: ""\n'
' textDirection: null\n' ' textDirection: null\n'
' sortKey: null\n' ' sortKey: null\n'
' platformViewId: null\n' ' platformViewId: null\n'
......
...@@ -158,7 +158,8 @@ void main() { ...@@ -158,7 +158,8 @@ void main() {
'properties: SemanticsProperties, ' 'properties: SemanticsProperties, '
'attributedLabel: "label" [SpellOutStringAttribute(TextRange(start: 0, end: 5))], ' 'attributedLabel: "label" [SpellOutStringAttribute(TextRange(start: 0, end: 5))], '
'attributedValue: "value" [LocaleStringAttribute(TextRange(start: 0, end: 5), en-MX)], ' 'attributedValue: "value" [LocaleStringAttribute(TextRange(start: 0, end: 5), en-MX)], '
'attributedHint: "hint" [SpellOutStringAttribute(TextRange(start: 1, end: 2))]' // ignore: missing_whitespace_between_adjacent_strings 'attributedHint: "hint" [SpellOutStringAttribute(TextRange(start: 1, end: 2))], '
'tooltip: null'// ignore: missing_whitespace_between_adjacent_strings
')', ')',
); );
......
...@@ -61,6 +61,34 @@ void main() { ...@@ -61,6 +61,34 @@ void main() {
semantics.dispose(); semantics.dispose();
}, semanticsEnabled: false); }, semanticsEnabled: false);
testWidgets('Semantics tooltip', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
final TestSemantics expectedSemantics = TestSemantics.root(
children: <TestSemantics>[
TestSemantics.rootChild(
tooltip: 'test1',
textDirection: TextDirection.ltr,
),
],
);
await tester.pumpWidget(
Semantics(
tooltip: 'test1',
textDirection: TextDirection.ltr,
),
);
expect(semantics, hasSemantics(
expectedSemantics,
ignoreTransform: true,
ignoreRect: true,
ignoreId: true,
));
semantics.dispose();
});
testWidgets('Detach and reattach assert', (WidgetTester tester) async { testWidgets('Detach and reattach assert', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester); final SemanticsTester semantics = SemanticsTester(tester);
final GlobalKey key = GlobalKey(); final GlobalKey key = GlobalKey();
......
...@@ -39,6 +39,7 @@ class TestSemantics { ...@@ -39,6 +39,7 @@ class TestSemantics {
this.actions = 0, this.actions = 0,
this.label = '', this.label = '',
this.value = '', this.value = '',
this.tooltip = '',
this.increasedValue = '', this.increasedValue = '',
this.decreasedValue = '', this.decreasedValue = '',
this.hint = '', this.hint = '',
...@@ -72,6 +73,7 @@ class TestSemantics { ...@@ -72,6 +73,7 @@ class TestSemantics {
this.increasedValue = '', this.increasedValue = '',
this.decreasedValue = '', this.decreasedValue = '',
this.hint = '', this.hint = '',
this.tooltip = '',
this.textDirection, this.textDirection,
this.transform, this.transform,
this.textSelection, this.textSelection,
...@@ -110,6 +112,7 @@ class TestSemantics { ...@@ -110,6 +112,7 @@ class TestSemantics {
this.label = '', this.label = '',
this.hint = '', this.hint = '',
this.value = '', this.value = '',
this.tooltip = '',
this.increasedValue = '', this.increasedValue = '',
this.decreasedValue = '', this.decreasedValue = '',
this.textDirection, this.textDirection,
...@@ -176,6 +179,9 @@ class TestSemantics { ...@@ -176,6 +179,9 @@ class TestSemantics {
/// performed on this node. /// performed on this node.
final String hint; final String hint;
/// A textual tooltip of this node.
final String tooltip;
/// The reading direction of the [label]. /// The reading direction of the [label].
/// ///
/// Even if this is not set, the [hasSemantics] matcher will verify that if a /// Even if this is not set, the [hasSemantics] matcher will verify that if a
...@@ -292,6 +298,8 @@ class TestSemantics { ...@@ -292,6 +298,8 @@ class TestSemantics {
return fail('expected node id $id to have decreasedValue "$decreasedValue" but found value "${nodeData.decreasedValue}".'); return fail('expected node id $id to have decreasedValue "$decreasedValue" but found value "${nodeData.decreasedValue}".');
if (hint != nodeData.hint) if (hint != nodeData.hint)
return fail('expected node id $id to have hint "$hint" but found hint "${nodeData.hint}".'); return fail('expected node id $id to have hint "$hint" but found hint "${nodeData.hint}".');
if (tooltip != nodeData.tooltip)
return fail('expected node id $id to have tooltip "$tooltip" but found hint "${nodeData.tooltip}".');
if (textDirection != null && textDirection != nodeData.textDirection) if (textDirection != null && textDirection != nodeData.textDirection)
return fail('expected node id $id to have textDirection "$textDirection" but found "${nodeData.textDirection}".'); return fail('expected node id $id to have textDirection "$textDirection" but found "${nodeData.textDirection}".');
if ((nodeData.label != '' || nodeData.value != '' || nodeData.hint != '' || node.increasedValue != '' || node.decreasedValue != '') && nodeData.textDirection == null) if ((nodeData.label != '' || nodeData.value != '' || nodeData.hint != '' || node.increasedValue != '' || node.decreasedValue != '') && nodeData.textDirection == null)
...@@ -365,6 +373,8 @@ class TestSemantics { ...@@ -365,6 +373,8 @@ class TestSemantics {
buf.writeln("$indent decreasedValue: '$decreasedValue',"); buf.writeln("$indent decreasedValue: '$decreasedValue',");
if (hint != null && hint != '') if (hint != null && hint != '')
buf.writeln("$indent hint: '$hint',"); buf.writeln("$indent hint: '$hint',");
if (tooltip != null && tooltip != '')
buf.writeln("$indent tooltip: '$tooltip',");
if (textDirection != null) if (textDirection != null)
buf.writeln('$indent textDirection: $textDirection,'); buf.writeln('$indent textDirection: $textDirection,');
if (textSelection?.isValid ?? false) if (textSelection?.isValid ?? false)
......
...@@ -228,7 +228,7 @@ class LabeledTapTargetGuideline extends AccessibilityGuideline { ...@@ -228,7 +228,7 @@ class LabeledTapTargetGuideline extends AccessibilityGuideline {
!data.hasAction(ui.SemanticsAction.tap)) { !data.hasAction(ui.SemanticsAction.tap)) {
return result; return result;
} }
if (data.label == null || data.label.isEmpty) { if ((data.label == null || data.label.isEmpty) && (data.tooltip == null || data.tooltip.isEmpty)) {
result += Evaluation.fail( result += Evaluation.fail(
'$node: expected tappable node to have semantic label, ' '$node: expected tappable node to have semantic label, '
'but none was found.\n', 'but none was found.\n',
......
...@@ -494,6 +494,7 @@ Matcher matchesSemantics({ ...@@ -494,6 +494,7 @@ Matcher matchesSemantics({
String? increasedValue, String? increasedValue,
AttributedString? attributedIncreasedValue, AttributedString? attributedIncreasedValue,
String? decreasedValue, String? decreasedValue,
String? tooltip,
AttributedString? attributedDecreasedValue, AttributedString? attributedDecreasedValue,
TextDirection? textDirection, TextDirection? textDirection,
Rect? rect, Rect? rect,
...@@ -625,6 +626,7 @@ Matcher matchesSemantics({ ...@@ -625,6 +626,7 @@ Matcher matchesSemantics({
value: value, value: value,
attributedValue: attributedValue, attributedValue: attributedValue,
increasedValue: increasedValue, increasedValue: increasedValue,
tooltip: tooltip,
attributedIncreasedValue: attributedIncreasedValue, attributedIncreasedValue: attributedIncreasedValue,
decreasedValue: decreasedValue, decreasedValue: decreasedValue,
attributedDecreasedValue: attributedDecreasedValue, attributedDecreasedValue: attributedDecreasedValue,
...@@ -1783,6 +1785,7 @@ class _MatchesSemanticsData extends Matcher { ...@@ -1783,6 +1785,7 @@ class _MatchesSemanticsData extends Matcher {
this.attributedIncreasedValue, this.attributedIncreasedValue,
this.decreasedValue, this.decreasedValue,
this.attributedDecreasedValue, this.attributedDecreasedValue,
this.tooltip,
this.flags, this.flags,
this.actions, this.actions,
this.textDirection, this.textDirection,
...@@ -1808,6 +1811,7 @@ class _MatchesSemanticsData extends Matcher { ...@@ -1808,6 +1811,7 @@ class _MatchesSemanticsData extends Matcher {
final AttributedString? attributedIncreasedValue; final AttributedString? attributedIncreasedValue;
final String? decreasedValue; final String? decreasedValue;
final AttributedString? attributedDecreasedValue; final AttributedString? attributedDecreasedValue;
final String? tooltip;
final SemanticsHintOverrides? hintOverrides; final SemanticsHintOverrides? hintOverrides;
final List<SemanticsAction>? actions; final List<SemanticsAction>? actions;
final List<CustomSemanticsAction>? customActions; final List<CustomSemanticsAction>? customActions;
...@@ -1845,6 +1849,8 @@ class _MatchesSemanticsData extends Matcher { ...@@ -1845,6 +1849,8 @@ class _MatchesSemanticsData extends Matcher {
description.add(' with decreasedValue: $decreasedValue '); description.add(' with decreasedValue: $decreasedValue ');
if (attributedDecreasedValue != null) if (attributedDecreasedValue != null)
description.add(' with attributedDecreasedValue: $attributedDecreasedValue'); description.add(' with attributedDecreasedValue: $attributedDecreasedValue');
if (tooltip != null)
description.add(' with tooltip: $tooltip');
if (actions != null) if (actions != null)
description.add(' with actions: ').addDescriptionOf(actions); description.add(' with actions: ').addDescriptionOf(actions);
if (flags != null) if (flags != null)
...@@ -1942,6 +1948,8 @@ class _MatchesSemanticsData extends Matcher { ...@@ -1942,6 +1948,8 @@ class _MatchesSemanticsData extends Matcher {
return failWithDescription( return failWithDescription(
matchState, 'attributedDecreasedValue was: ${data.attributedDecreasedValue}'); matchState, 'attributedDecreasedValue was: ${data.attributedDecreasedValue}');
} }
if (tooltip != null && tooltip != data.tooltip)
return failWithDescription(matchState, 'tooltip was: ${data.tooltip}');
if (textDirection != null && textDirection != data.textDirection) if (textDirection != null && textDirection != data.textDirection)
return failWithDescription(matchState, 'textDirection was: $textDirection'); return failWithDescription(matchState, 'textDirection was: $textDirection');
if (rect != null && rect != data.rect) if (rect != null && rect != data.rect)
......
...@@ -566,6 +566,7 @@ void main() { ...@@ -566,6 +566,7 @@ void main() {
attributedValue: AttributedString('c'), attributedValue: AttributedString('c'),
attributedDecreasedValue: AttributedString('d'), attributedDecreasedValue: AttributedString('d'),
attributedHint: AttributedString('e'), attributedHint: AttributedString('e'),
tooltip: 'f',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
rect: const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0), rect: const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0),
elevation: 3.0, elevation: 3.0,
......
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