Skip to content

Commit

Permalink
ensure web_ui and ui have the same toString methods for StringAttribu…
Browse files Browse the repository at this point in the history
…tes (flutter#29323)
  • Loading branch information
Hixie authored Oct 26, 2021
1 parent 60cf753 commit 8189611
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 43 deletions.
24 changes: 13 additions & 11 deletions lib/ui/semantics.dart
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ class SemanticsAction {
case _kSetText:
return 'SemanticsAction.setText';
}
assert(false, 'Unhandled index: $index');
assert(false, 'Unhandled index: $index (0x${index.toRadixString(8).padLeft(4, "0")})');
return '';
}
}
Expand All @@ -289,6 +289,13 @@ class SemanticsAction {
// `lib/ui/semantics/semantics_node.h` and in each of the embedders *must* be
// updated.
class SemanticsFlag {
const SemanticsFlag._(this.index) : assert(index != null);

/// The numerical value for this flag.
///
/// Each flag has one bit set in this bit field.
final int index;

static const int _kHasCheckedStateIndex = 1 << 0;
static const int _kIsCheckedIndex = 1 << 1;
static const int _kIsSelectedIndex = 1 << 2;
Expand All @@ -300,7 +307,7 @@ class SemanticsFlag {
static const int _kIsInMutuallyExclusiveGroupIndex = 1 << 8;
static const int _kIsHeaderIndex = 1 << 9;
static const int _kIsObscuredIndex = 1 << 10;
static const int _kScopesRouteIndex= 1 << 11;
static const int _kScopesRouteIndex = 1 << 11;
static const int _kNamesRouteIndex = 1 << 12;
static const int _kIsHiddenIndex = 1 << 13;
static const int _kIsImageIndex = 1 << 14;
Expand All @@ -320,13 +327,6 @@ class SemanticsFlag {
// flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java,
// and the SemanticsFlag class in lib/web_ui/lib/src/ui/semantics.dart.

const SemanticsFlag._(this.index) : assert(index != null);

/// The numerical value for this flag.
///
/// Each flag has one bit set in this bit field.
final int index;

/// The semantics node has the quality of either being "checked" or "unchecked".
///
/// This flag is mutually exclusive with [hasToggledState].
Expand Down Expand Up @@ -580,7 +580,7 @@ class SemanticsFlag {
_kIsLinkIndex: isLink,
_kIsSliderIndex: isSlider,
_kIsKeyboardKeyIndex: isKeyboardKey,
};
};

@override
String toString() {
Expand Down Expand Up @@ -636,7 +636,7 @@ class SemanticsFlag {
case _kIsKeyboardKeyIndex:
return 'SemanticsFlag.isKeyboardKey';
}
assert(false, 'Unhandled index: $index');
assert(false, 'Unhandled index: $index (0x${index.toRadixString(8).padLeft(4, "0")})');
return '';
}
}
Expand All @@ -646,6 +646,8 @@ class SemanticsFlag {
// * engine/src/flutter/lib/web_ui/lib/src/ui/semantics.dart
// * engine/src/flutter/lib/ui/semantics/string_attribute.h
// * engine/src/flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java
// * engine/src/flutter/lib/web_ui/test/engine/semantics/semantics_api_test.dart
// * engine/src/flutter/testing/dart/semantics_test.dart

/// An abstract interface for string attributes that affects how assistive
/// technologies, e.g. VoiceOver or TalkBack, treat the text.
Expand Down
2 changes: 2 additions & 0 deletions lib/ui/semantics/string_attribute.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ using StringAttributes = std::vector<StringAttributePtr>;
// * engine/src/flutter/lib/ui/semantics.dart
// * engine/src/flutter/lib/web_ui/lib/src/ui/semantics.dart
// * engine/src/flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java
// * engine/src/flutter/lib/web_ui/test/engine/semantics/semantics_api_test.dart
// * engine/src/flutter/testing/dart/semantics_test.dart

enum class StringAttributeType : int32_t {
kSpellOut,
Expand Down
65 changes: 34 additions & 31 deletions lib/web_ui/lib/src/ui/semantics.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ class SemanticsAction {
static const int _kMoveCursorForwardByWordIndex = 1 << 19;
static const int _kMoveCursorBackwardByWordIndex = 1 << 20;
static const int _kSetText = 1 << 21;

final int index;

static const SemanticsAction tap = SemanticsAction._(_kTapIndex);
static const SemanticsAction longPress = SemanticsAction._(_kLongPressIndex);
static const SemanticsAction scrollLeft = SemanticsAction._(_kScrollLeftIndex);
Expand All @@ -39,25 +41,20 @@ class SemanticsAction {
static const SemanticsAction increase = SemanticsAction._(_kIncreaseIndex);
static const SemanticsAction decrease = SemanticsAction._(_kDecreaseIndex);
static const SemanticsAction showOnScreen = SemanticsAction._(_kShowOnScreenIndex);
static const SemanticsAction moveCursorForwardByCharacter =
SemanticsAction._(_kMoveCursorForwardByCharacterIndex);
static const SemanticsAction moveCursorBackwardByCharacter =
SemanticsAction._(_kMoveCursorBackwardByCharacterIndex);
static const SemanticsAction moveCursorForwardByCharacter = SemanticsAction._(_kMoveCursorForwardByCharacterIndex);
static const SemanticsAction moveCursorBackwardByCharacter = SemanticsAction._(_kMoveCursorBackwardByCharacterIndex);
static const SemanticsAction setText = SemanticsAction._(_kSetText);
static const SemanticsAction setSelection = SemanticsAction._(_kSetSelectionIndex);
static const SemanticsAction copy = SemanticsAction._(_kCopyIndex);
static const SemanticsAction cut = SemanticsAction._(_kCutIndex);
static const SemanticsAction paste = SemanticsAction._(_kPasteIndex);
static const SemanticsAction didGainAccessibilityFocus =
SemanticsAction._(_kDidGainAccessibilityFocusIndex);
static const SemanticsAction didLoseAccessibilityFocus =
SemanticsAction._(_kDidLoseAccessibilityFocusIndex);
static const SemanticsAction didGainAccessibilityFocus = SemanticsAction._(_kDidGainAccessibilityFocusIndex);
static const SemanticsAction didLoseAccessibilityFocus = SemanticsAction._(_kDidLoseAccessibilityFocusIndex);
static const SemanticsAction customAction = SemanticsAction._(_kCustomAction);
static const SemanticsAction dismiss = SemanticsAction._(_kDismissIndex);
static const SemanticsAction moveCursorForwardByWord =
SemanticsAction._(_kMoveCursorForwardByWordIndex);
static const SemanticsAction moveCursorBackwardByWord =
SemanticsAction._(_kMoveCursorBackwardByWordIndex);
static const SemanticsAction setText = SemanticsAction._(_kSetText);
static const SemanticsAction moveCursorForwardByWord = SemanticsAction._(_kMoveCursorForwardByWordIndex);
static const SemanticsAction moveCursorBackwardByWord = SemanticsAction._(_kMoveCursorBackwardByWordIndex);

static const Map<int, SemanticsAction> values = <int, SemanticsAction>{
_kTapIndex: tap,
_kLongPressIndex: longPress,
Expand Down Expand Up @@ -131,12 +128,16 @@ class SemanticsAction {
case _kSetText:
return 'SemanticsAction.setText';
}
assert(false, 'Unhandled index: $index');
assert(false, 'Unhandled index: $index (0x${index.toRadixString(8).padLeft(4, "0")})');
return '';
}
}

class SemanticsFlag {
const SemanticsFlag._(this.index) : assert(index != null); // ignore: unnecessary_null_comparison

final int index;

static const int _kHasCheckedStateIndex = 1 << 0;
static const int _kIsCheckedIndex = 1 << 1;
static const int _kIsSelectedIndex = 1 << 2;
Expand All @@ -163,22 +164,23 @@ class SemanticsFlag {
static const int _kIsSliderIndex = 1 << 23;
static const int _kIsKeyboardKeyIndex = 1 << 24;

const SemanticsFlag._(this.index) : assert(index != null); // ignore: unnecessary_null_comparison
final int index;
static const SemanticsFlag hasCheckedState = SemanticsFlag._(_kHasCheckedStateIndex);
static const SemanticsFlag isChecked = SemanticsFlag._(_kIsCheckedIndex);
static const SemanticsFlag isSelected = SemanticsFlag._(_kIsSelectedIndex);
static const SemanticsFlag isButton = SemanticsFlag._(_kIsButtonIndex);
static const SemanticsFlag isLink = SemanticsFlag._(_kIsLinkIndex);
static const SemanticsFlag isTextField = SemanticsFlag._(_kIsTextFieldIndex);
static const SemanticsFlag isSlider = SemanticsFlag._(_kIsSliderIndex);
static const SemanticsFlag isKeyboardKey = SemanticsFlag._(_kIsKeyboardKeyIndex);
static const SemanticsFlag isReadOnly = SemanticsFlag._(_kIsReadOnlyIndex);
static const SemanticsFlag isLink = SemanticsFlag._(_kIsLinkIndex);
static const SemanticsFlag isFocusable = SemanticsFlag._(_kIsFocusableIndex);
static const SemanticsFlag isFocused = SemanticsFlag._(_kIsFocusedIndex);
static const SemanticsFlag hasEnabledState = SemanticsFlag._(_kHasEnabledStateIndex);
static const SemanticsFlag isEnabled = SemanticsFlag._(_kIsEnabledIndex);
static const SemanticsFlag isInMutuallyExclusiveGroup = SemanticsFlag._(_kIsInMutuallyExclusiveGroupIndex);
static const SemanticsFlag isHeader = SemanticsFlag._(_kIsHeaderIndex);
static const SemanticsFlag isObscured = SemanticsFlag._(_kIsObscuredIndex);
static const SemanticsFlag isMultiline = SemanticsFlag._(_kIsMultilineIndex);
static const SemanticsFlag scopesRoute = SemanticsFlag._(_kScopesRouteIndex);
static const SemanticsFlag namesRoute = SemanticsFlag._(_kNamesRouteIndex);
static const SemanticsFlag isHidden = SemanticsFlag._(_kIsHiddenIndex);
Expand All @@ -187,18 +189,13 @@ class SemanticsFlag {
static const SemanticsFlag hasToggledState = SemanticsFlag._(_kHasToggledStateIndex);
static const SemanticsFlag isToggled = SemanticsFlag._(_kIsToggledIndex);
static const SemanticsFlag hasImplicitScrolling = SemanticsFlag._(_kHasImplicitScrollingIndex);
static const SemanticsFlag isMultiline = SemanticsFlag._(_kIsMultilineIndex);
static const SemanticsFlag isSlider = SemanticsFlag._(_kIsSliderIndex);
static const SemanticsFlag isKeyboardKey = SemanticsFlag._(_kIsKeyboardKeyIndex);

static const Map<int, SemanticsFlag> values = <int, SemanticsFlag>{
_kHasCheckedStateIndex: hasCheckedState,
_kIsCheckedIndex: isChecked,
_kIsSelectedIndex: isSelected,
_kIsButtonIndex: isButton,
_kIsLinkIndex: isLink,
_kIsSliderIndex: isSlider,
_kIsTextFieldIndex: isTextField,
_kIsFocusableIndex: isFocusable,
_kIsFocusedIndex: isFocused,
_kHasEnabledStateIndex: hasEnabledState,
_kIsEnabledIndex: isEnabled,
Expand All @@ -215,6 +212,9 @@ class SemanticsFlag {
_kHasImplicitScrollingIndex: hasImplicitScrolling,
_kIsMultilineIndex: isMultiline,
_kIsReadOnlyIndex: isReadOnly,
_kIsFocusableIndex: isFocusable,
_kIsLinkIndex: isLink,
_kIsSliderIndex: isSlider,
_kIsKeyboardKeyIndex: isKeyboardKey,
};

Expand All @@ -229,12 +229,8 @@ class SemanticsFlag {
return 'SemanticsFlag.isSelected';
case _kIsButtonIndex:
return 'SemanticsFlag.isButton';
case _kIsLinkIndex:
return 'SemanticsFlag.isLink';
case _kIsTextFieldIndex:
return 'SemanticsFlag.isTextField';
case _kIsFocusableIndex:
return 'SemanticsFlag.isFocusable';
case _kIsFocusedIndex:
return 'SemanticsFlag.isFocused';
case _kHasEnabledStateIndex:
Expand Down Expand Up @@ -267,20 +263,27 @@ class SemanticsFlag {
return 'SemanticsFlag.isMultiline';
case _kIsReadOnlyIndex:
return 'SemanticsFlag.isReadOnly';
case _kIsFocusableIndex:
return 'SemanticsFlag.isFocusable';
case _kIsLinkIndex:
return 'SemanticsFlag.isLink';
case _kIsSliderIndex:
return 'SemanticsFlag.isSlider';
case _kIsKeyboardKeyIndex:
return 'SemanticsFlag.isKeyboardKey';
}
assert(false, 'Unhandled index: $index');
assert(false, 'Unhandled index: $index (0x${index.toRadixString(8).padLeft(4, "0")})');
return '';
}
}


// When adding a new StringAttributeType, the classes in these file must be
// updated as well.
// * engine/src/flutter/lib/ui/semantics.dart
// * engine/src/flutter/lib/ui/semantics/string_attribute.h
// * engine/src/flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java
// * engine/src/flutter/lib/web_ui/test/engine/semantics/semantics_api_test.dart
// * engine/src/flutter/testing/dart/semantics_test.dart

abstract class StringAttribute {
StringAttribute._({
Expand All @@ -304,7 +307,7 @@ class SpellOutStringAttribute extends StringAttribute {

@override
String toString() {
return 'SpellOutStringAttribute(range: $range)';
return 'SpellOutStringAttribute($range)';
}
}

Expand All @@ -323,7 +326,7 @@ class LocaleStringAttribute extends StringAttribute {

@override
String toString() {
return 'LocaleStringAttribute(range: $range, locale: ${locale.toLanguageTag()})';
return 'LocaleStringAttribute($range, ${locale.toLanguageTag()})';
}
}

Expand Down
48 changes: 48 additions & 0 deletions lib/web_ui/test/engine/semantics/semantics_api_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

@TestOn('chrome || safari || firefox')

import 'package:test/bootstrap/browser.dart';
import 'package:test/test.dart';
import 'package:ui/ui.dart';

void main() {
internalBootstrapBrowserTest(() => testMain);
}

// The body of this file is the same as ../../../../../testing/dart/semantics_test.dart
// Please keep them in sync.

void testMain() {
// This must match the number of flags in lib/ui/semantics.dart
const int numSemanticsFlags = 25;
test('SemanticsFlag.values refers to all flags.', () async {
expect(SemanticsFlag.values.length, equals(numSemanticsFlags));
for (int index = 0; index < numSemanticsFlags; ++index) {
final int flag = 1 << index;
expect(SemanticsFlag.values[flag], isNotNull);
expect(SemanticsFlag.values[flag].toString(), startsWith('SemanticsFlag.'));
}
});

// This must match the number of actions in lib/ui/semantics.dart
const int numSemanticsActions = 22;
test('SemanticsAction.values refers to all actions.', () async {
expect(SemanticsAction.values.length, equals(numSemanticsActions));
for (int index = 0; index < numSemanticsActions; ++index) {
final int flag = 1 << index;
expect(SemanticsAction.values[flag], isNotNull);
expect(SemanticsAction.values[flag].toString(), startsWith('SemanticsAction.'));
}
});

test('SpellOutStringAttribute.toString', () async {
expect(SpellOutStringAttribute(range: const TextRange(start: 2, end: 5)).toString(), 'SpellOutStringAttribute(TextRange(start: 2, end: 5))');
});

test('LocaleStringAttribute.toString', () async {
expect(LocaleStringAttribute(range: const TextRange(start: 2, end: 5), locale: const Locale('test')).toString(), 'LocaleStringAttribute(TextRange(start: 2, end: 5), test)');
});
}
12 changes: 11 additions & 1 deletion testing/dart/semantics_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import 'dart:ui';

import 'package:litetest/litetest.dart';

/// Verifies Semantics flags and actions.
// The body of this file is the same as ../../lib/web_ui/test/engine/semantics/semantics_api_test.dart
// Please keep them in sync.

void main() {
// This must match the number of flags in lib/ui/semantics.dart
const int numSemanticsFlags = 25;
Expand All @@ -29,4 +31,12 @@ void main() {
expect(SemanticsAction.values[flag].toString(), startsWith('SemanticsAction.'));
}
});

test('SpellOutStringAttribute.toString', () async {
expect(SpellOutStringAttribute(range: const TextRange(start: 2, end: 5)).toString(), 'SpellOutStringAttribute(TextRange(start: 2, end: 5))');
});

test('LocaleStringAttribute.toString', () async {
expect(LocaleStringAttribute(range: const TextRange(start: 2, end: 5), locale: const Locale('test')).toString(), 'LocaleStringAttribute(TextRange(start: 2, end: 5), test)');
});
}

0 comments on commit 8189611

Please sign in to comment.