Follow-up to https://github.com/flutter/engine/pull/42493. The framework has been migrated to the new onSemanticsActionEvent callback.
1030 lines
42 KiB
Dart
1030 lines
42 KiB
Dart
// 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.
|
|
|
|
part of dart.ui;
|
|
|
|
/// The possible actions that can be conveyed from the operating system
|
|
/// accessibility APIs to a semantics node.
|
|
///
|
|
/// \warning When changes are made to this class, the equivalent APIs in
|
|
/// `lib/ui/semantics/semantics_node.h` and in each of the embedders
|
|
/// *must* be updated.
|
|
/// See also:
|
|
/// - file://./../../lib/ui/semantics/semantics_node.h
|
|
class SemanticsAction {
|
|
const SemanticsAction._(this.index, this.name);
|
|
|
|
/// The numerical value for this action.
|
|
///
|
|
/// Each action has one bit set in this bit field.
|
|
final int index;
|
|
|
|
/// A human-readable name for this flag, used for debugging purposes.
|
|
final String name;
|
|
|
|
static const int _kTapIndex = 1 << 0;
|
|
static const int _kLongPressIndex = 1 << 1;
|
|
static const int _kScrollLeftIndex = 1 << 2;
|
|
static const int _kScrollRightIndex = 1 << 3;
|
|
static const int _kScrollUpIndex = 1 << 4;
|
|
static const int _kScrollDownIndex = 1 << 5;
|
|
static const int _kIncreaseIndex = 1 << 6;
|
|
static const int _kDecreaseIndex = 1 << 7;
|
|
static const int _kShowOnScreenIndex = 1 << 8;
|
|
static const int _kMoveCursorForwardByCharacterIndex = 1 << 9;
|
|
static const int _kMoveCursorBackwardByCharacterIndex = 1 << 10;
|
|
static const int _kSetSelectionIndex = 1 << 11;
|
|
static const int _kCopyIndex = 1 << 12;
|
|
static const int _kCutIndex = 1 << 13;
|
|
static const int _kPasteIndex = 1 << 14;
|
|
static const int _kDidGainAccessibilityFocusIndex = 1 << 15;
|
|
static const int _kDidLoseAccessibilityFocusIndex = 1 << 16;
|
|
static const int _kCustomActionIndex = 1 << 17;
|
|
static const int _kDismissIndex = 1 << 18;
|
|
static const int _kMoveCursorForwardByWordIndex = 1 << 19;
|
|
static const int _kMoveCursorBackwardByWordIndex = 1 << 20;
|
|
static const int _kSetTextIndex = 1 << 21;
|
|
// READ THIS: if you add an action here, you MUST update the
|
|
// numSemanticsActions value in testing/dart/semantics_test.dart, or tests
|
|
// will fail.
|
|
|
|
/// The equivalent of a user briefly tapping the screen with the finger
|
|
/// without moving it.
|
|
static const SemanticsAction tap = SemanticsAction._(_kTapIndex, 'tap');
|
|
|
|
/// The equivalent of a user pressing and holding the screen with the finger
|
|
/// for a few seconds without moving it.
|
|
static const SemanticsAction longPress = SemanticsAction._(_kLongPressIndex, 'longPress');
|
|
|
|
/// The equivalent of a user moving their finger across the screen from right
|
|
/// to left.
|
|
///
|
|
/// This action should be recognized by controls that are horizontally
|
|
/// scrollable.
|
|
static const SemanticsAction scrollLeft = SemanticsAction._(_kScrollLeftIndex, 'scrollLeft');
|
|
|
|
/// The equivalent of a user moving their finger across the screen from left
|
|
/// to right.
|
|
///
|
|
/// This action should be recognized by controls that are horizontally
|
|
/// scrollable.
|
|
static const SemanticsAction scrollRight = SemanticsAction._(_kScrollRightIndex, 'scrollRight');
|
|
|
|
/// The equivalent of a user moving their finger across the screen from
|
|
/// bottom to top.
|
|
///
|
|
/// This action should be recognized by controls that are vertically
|
|
/// scrollable.
|
|
static const SemanticsAction scrollUp = SemanticsAction._(_kScrollUpIndex, 'scrollUp');
|
|
|
|
/// The equivalent of a user moving their finger across the screen from top
|
|
/// to bottom.
|
|
///
|
|
/// This action should be recognized by controls that are vertically
|
|
/// scrollable.
|
|
static const SemanticsAction scrollDown = SemanticsAction._(_kScrollDownIndex, 'scrollDown');
|
|
|
|
/// A request to increase the value represented by the semantics node.
|
|
///
|
|
/// For example, this action might be recognized by a slider control.
|
|
static const SemanticsAction increase = SemanticsAction._(_kIncreaseIndex, 'increase');
|
|
|
|
/// A request to decrease the value represented by the semantics node.
|
|
///
|
|
/// For example, this action might be recognized by a slider control.
|
|
static const SemanticsAction decrease = SemanticsAction._(_kDecreaseIndex, 'decrease');
|
|
|
|
/// A request to fully show the semantics node on screen.
|
|
///
|
|
/// For example, this action might be send to a node in a scrollable list that
|
|
/// is partially off screen to bring it on screen.
|
|
static const SemanticsAction showOnScreen = SemanticsAction._(_kShowOnScreenIndex, 'showOnScreen');
|
|
|
|
/// Move the cursor forward by one character.
|
|
///
|
|
/// This is for example used by the cursor control in text fields.
|
|
///
|
|
/// The action includes a boolean argument, which indicates whether the cursor
|
|
/// movement should extend (or start) a selection.
|
|
static const SemanticsAction moveCursorForwardByCharacter = SemanticsAction._(_kMoveCursorForwardByCharacterIndex, 'moveCursorForwardByCharacter');
|
|
|
|
/// Move the cursor backward by one character.
|
|
///
|
|
/// This is for example used by the cursor control in text fields.
|
|
///
|
|
/// The action includes a boolean argument, which indicates whether the cursor
|
|
/// movement should extend (or start) a selection.
|
|
static const SemanticsAction moveCursorBackwardByCharacter = SemanticsAction._(_kMoveCursorBackwardByCharacterIndex, 'moveCursorBackwardByCharacter');
|
|
|
|
/// Replaces the current text in the text field.
|
|
///
|
|
/// This is for example used by the text editing in voice access.
|
|
///
|
|
/// The action includes a string argument, which is the new text to
|
|
/// replace.
|
|
static const SemanticsAction setText = SemanticsAction._(_kSetTextIndex, 'setText');
|
|
|
|
/// Set the text selection to the given range.
|
|
///
|
|
/// The provided argument is a Map<String, int> which includes the keys `base`
|
|
/// and `extent` indicating where the selection within the `value` of the
|
|
/// semantics node should start and where it should end. Values for both
|
|
/// keys can range from 0 to length of `value` (inclusive).
|
|
///
|
|
/// Setting `base` and `extent` to the same value will move the cursor to
|
|
/// that position (without selecting anything).
|
|
static const SemanticsAction setSelection = SemanticsAction._(_kSetSelectionIndex, 'setSelection');
|
|
|
|
/// Copy the current selection to the clipboard.
|
|
static const SemanticsAction copy = SemanticsAction._(_kCopyIndex, 'copy');
|
|
|
|
/// Cut the current selection and place it in the clipboard.
|
|
static const SemanticsAction cut = SemanticsAction._(_kCutIndex, 'cut');
|
|
|
|
/// Paste the current content of the clipboard.
|
|
static const SemanticsAction paste = SemanticsAction._(_kPasteIndex, 'paste');
|
|
|
|
/// Indicates that the node has gained accessibility focus.
|
|
///
|
|
/// This handler is invoked when the node annotated with this handler gains
|
|
/// the accessibility focus. The accessibility focus is the
|
|
/// green (on Android with TalkBack) or black (on iOS with VoiceOver)
|
|
/// rectangle shown on screen to indicate what element an accessibility
|
|
/// user is currently interacting with.
|
|
///
|
|
/// The accessibility focus is different from the input focus. The input focus
|
|
/// is usually held by the element that currently responds to keyboard inputs.
|
|
/// Accessibility focus and input focus can be held by two different nodes!
|
|
static const SemanticsAction didGainAccessibilityFocus = SemanticsAction._(_kDidGainAccessibilityFocusIndex, 'didGainAccessibilityFocus');
|
|
|
|
/// Indicates that the node has lost accessibility focus.
|
|
///
|
|
/// This handler is invoked when the node annotated with this handler
|
|
/// loses the accessibility focus. The accessibility focus is
|
|
/// the green (on Android with TalkBack) or black (on iOS with VoiceOver)
|
|
/// rectangle shown on screen to indicate what element an accessibility
|
|
/// user is currently interacting with.
|
|
///
|
|
/// The accessibility focus is different from the input focus. The input focus
|
|
/// is usually held by the element that currently responds to keyboard inputs.
|
|
/// Accessibility focus and input focus can be held by two different nodes!
|
|
static const SemanticsAction didLoseAccessibilityFocus = SemanticsAction._(_kDidLoseAccessibilityFocusIndex, 'didLoseAccessibilityFocus');
|
|
|
|
/// Indicates that the user has invoked a custom accessibility action.
|
|
///
|
|
/// This handler is added automatically whenever a custom accessibility
|
|
/// action is added to a semantics node.
|
|
static const SemanticsAction customAction = SemanticsAction._(_kCustomActionIndex, 'customAction');
|
|
|
|
/// A request that the node should be dismissed.
|
|
///
|
|
/// A [SnackBar], for example, may have a dismiss action to indicate to the
|
|
/// user that it can be removed after it is no longer relevant. On Android,
|
|
/// (with TalkBack) special hint text is spoken when focusing the node and
|
|
/// a custom action is available in the local context menu. On iOS,
|
|
/// (with VoiceOver) users can perform a standard gesture to dismiss it.
|
|
static const SemanticsAction dismiss = SemanticsAction._(_kDismissIndex, 'dismiss');
|
|
|
|
/// Move the cursor forward by one word.
|
|
///
|
|
/// This is for example used by the cursor control in text fields.
|
|
///
|
|
/// The action includes a boolean argument, which indicates whether the cursor
|
|
/// movement should extend (or start) a selection.
|
|
static const SemanticsAction moveCursorForwardByWord = SemanticsAction._(_kMoveCursorForwardByWordIndex, 'moveCursorForwardByWord');
|
|
|
|
/// Move the cursor backward by one word.
|
|
///
|
|
/// This is for example used by the cursor control in text fields.
|
|
///
|
|
/// The action includes a boolean argument, which indicates whether the cursor
|
|
/// movement should extend (or start) a selection.
|
|
static const SemanticsAction moveCursorBackwardByWord = SemanticsAction._(_kMoveCursorBackwardByWordIndex, 'moveCursorBackwardByWord');
|
|
|
|
/// The possible semantics actions.
|
|
///
|
|
/// The map's key is the [index] of the action and the value is the action
|
|
/// itself.
|
|
static const Map<int, SemanticsAction> _kActionById = <int, SemanticsAction>{
|
|
_kTapIndex: tap,
|
|
_kLongPressIndex: longPress,
|
|
_kScrollLeftIndex: scrollLeft,
|
|
_kScrollRightIndex: scrollRight,
|
|
_kScrollUpIndex: scrollUp,
|
|
_kScrollDownIndex: scrollDown,
|
|
_kIncreaseIndex: increase,
|
|
_kDecreaseIndex: decrease,
|
|
_kShowOnScreenIndex: showOnScreen,
|
|
_kMoveCursorForwardByCharacterIndex: moveCursorForwardByCharacter,
|
|
_kMoveCursorBackwardByCharacterIndex: moveCursorBackwardByCharacter,
|
|
_kSetSelectionIndex: setSelection,
|
|
_kCopyIndex: copy,
|
|
_kCutIndex: cut,
|
|
_kPasteIndex: paste,
|
|
_kDidGainAccessibilityFocusIndex: didGainAccessibilityFocus,
|
|
_kDidLoseAccessibilityFocusIndex: didLoseAccessibilityFocus,
|
|
_kCustomActionIndex: customAction,
|
|
_kDismissIndex: dismiss,
|
|
_kMoveCursorForwardByWordIndex: moveCursorForwardByWord,
|
|
_kMoveCursorBackwardByWordIndex: moveCursorBackwardByWord,
|
|
_kSetTextIndex: setText,
|
|
};
|
|
|
|
static List<SemanticsAction> get values => _kActionById.values.toList(growable: false);
|
|
|
|
static SemanticsAction? fromIndex(int index) => _kActionById[index];
|
|
|
|
@override
|
|
String toString() => 'SemanticsAction.$name';
|
|
}
|
|
|
|
/// A Boolean value that can be associated with a semantics node.
|
|
//
|
|
// When changes are made to this class, the equivalent APIs in
|
|
// `lib/ui/semantics/semantics_node.h` and in each of the embedders *must* be
|
|
// updated. If the change affects the visibility of a [SemanticsNode] to
|
|
// accessibility services, `flutter_test/controller.dart#SemanticsController._importantFlags`
|
|
// must be updated as well.
|
|
class SemanticsFlag {
|
|
const SemanticsFlag._(this.index, this.name);
|
|
|
|
/// The numerical value for this flag.
|
|
///
|
|
/// Each flag has one bit set in this bit field.
|
|
final int index;
|
|
|
|
/// A human-readable name for this flag, used for debugging purposes.
|
|
final String name;
|
|
|
|
static const int _kHasCheckedStateIndex = 1 << 0;
|
|
static const int _kIsCheckedIndex = 1 << 1;
|
|
static const int _kIsSelectedIndex = 1 << 2;
|
|
static const int _kIsButtonIndex = 1 << 3;
|
|
static const int _kIsTextFieldIndex = 1 << 4;
|
|
static const int _kIsFocusedIndex = 1 << 5;
|
|
static const int _kHasEnabledStateIndex = 1 << 6;
|
|
static const int _kIsEnabledIndex = 1 << 7;
|
|
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 _kNamesRouteIndex = 1 << 12;
|
|
static const int _kIsHiddenIndex = 1 << 13;
|
|
static const int _kIsImageIndex = 1 << 14;
|
|
static const int _kIsLiveRegionIndex = 1 << 15;
|
|
static const int _kHasToggledStateIndex = 1 << 16;
|
|
static const int _kIsToggledIndex = 1 << 17;
|
|
static const int _kHasImplicitScrollingIndex = 1 << 18;
|
|
static const int _kIsMultilineIndex = 1 << 19;
|
|
static const int _kIsReadOnlyIndex = 1 << 20;
|
|
static const int _kIsFocusableIndex = 1 << 21;
|
|
static const int _kIsLinkIndex = 1 << 22;
|
|
static const int _kIsSliderIndex = 1 << 23;
|
|
static const int _kIsKeyboardKeyIndex = 1 << 24;
|
|
static const int _kIsCheckStateMixedIndex = 1 << 25;
|
|
// READ THIS: if you add a flag here, you MUST update the numSemanticsFlags
|
|
// value in testing/dart/semantics_test.dart, or tests will fail. Also,
|
|
// please update the Flag enum in
|
|
// flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java,
|
|
// and the SemanticsFlag class in lib/web_ui/lib/semantics.dart. If the new flag
|
|
// affects the visibility of a [SemanticsNode] to accessibility services,
|
|
// `flutter_test/controller.dart#SemanticsController._importantFlags`
|
|
// must be updated as well.
|
|
|
|
/// The semantics node has the quality of either being "checked" or "unchecked".
|
|
///
|
|
/// This flag is mutually exclusive with [hasToggledState].
|
|
///
|
|
/// For example, a checkbox or a radio button widget has checked state.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [SemanticsFlag.isChecked], which controls whether the node is "checked" or "unchecked".
|
|
static const SemanticsFlag hasCheckedState = SemanticsFlag._(_kHasCheckedStateIndex, 'hasCheckedState');
|
|
|
|
/// Whether a semantics node that [hasCheckedState] is checked.
|
|
///
|
|
/// If true, the semantics node is "checked". If false, the semantics node is
|
|
/// "unchecked".
|
|
///
|
|
/// For example, if a checkbox has a visible checkmark, [isChecked] is true.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [SemanticsFlag.hasCheckedState], which enables a checked state.
|
|
static const SemanticsFlag isChecked = SemanticsFlag._(_kIsCheckedIndex, 'isChecked');
|
|
|
|
/// Whether a tristate checkbox is in its mixed state.
|
|
///
|
|
/// If this is true, the check box this semantics node represents
|
|
/// is in a mixed state.
|
|
///
|
|
/// For example, a [Checkbox] with [Checkbox.tristate] set to true
|
|
/// can have checked, unchecked, or mixed state.
|
|
///
|
|
/// Must be false when the checkbox is either checked or unchecked.
|
|
static const SemanticsFlag isCheckStateMixed = SemanticsFlag._(_kIsCheckStateMixedIndex, 'isCheckStateMixed');
|
|
|
|
|
|
/// Whether a semantics node is selected.
|
|
///
|
|
/// If true, the semantics node is "selected". If false, the semantics node is
|
|
/// "unselected".
|
|
///
|
|
/// For example, the active tab in a tab bar has [isSelected] set to true.
|
|
static const SemanticsFlag isSelected = SemanticsFlag._(_kIsSelectedIndex, 'isSelected');
|
|
|
|
/// Whether the semantic node represents a button.
|
|
///
|
|
/// Platforms have special handling for buttons, for example Android's TalkBack
|
|
/// and iOS's VoiceOver provides an additional hint when the focused object is
|
|
/// a button.
|
|
static const SemanticsFlag isButton = SemanticsFlag._(_kIsButtonIndex, 'isButton');
|
|
|
|
/// Whether the semantic node represents a text field.
|
|
///
|
|
/// Text fields are announced as such and allow text input via accessibility
|
|
/// affordances.
|
|
static const SemanticsFlag isTextField = SemanticsFlag._(_kIsTextFieldIndex, 'isTextField');
|
|
|
|
/// Whether the semantic node represents a slider.
|
|
static const SemanticsFlag isSlider = SemanticsFlag._(_kIsSliderIndex, 'isSlider');
|
|
|
|
/// Whether the semantic node represents a keyboard key.
|
|
static const SemanticsFlag isKeyboardKey = SemanticsFlag._(_kIsKeyboardKeyIndex, 'isKeyboardKey');
|
|
|
|
/// Whether the semantic node is read only.
|
|
///
|
|
/// Only applicable when [isTextField] is true.
|
|
static const SemanticsFlag isReadOnly = SemanticsFlag._(_kIsReadOnlyIndex, 'isReadOnly');
|
|
|
|
/// Whether the semantic node is an interactive link.
|
|
///
|
|
/// Platforms have special handling for links, for example iOS's VoiceOver
|
|
/// provides an additional hint when the focused object is a link, as well as
|
|
/// the ability to parse the links through another navigation menu.
|
|
static const SemanticsFlag isLink = SemanticsFlag._(_kIsLinkIndex, 'isLink');
|
|
|
|
/// Whether the semantic node is able to hold the user's focus.
|
|
///
|
|
/// The focused element is usually the current receiver of keyboard inputs.
|
|
static const SemanticsFlag isFocusable = SemanticsFlag._(_kIsFocusableIndex, 'isFocusable');
|
|
|
|
/// Whether the semantic node currently holds the user's focus.
|
|
///
|
|
/// The focused element is usually the current receiver of keyboard inputs.
|
|
static const SemanticsFlag isFocused = SemanticsFlag._(_kIsFocusedIndex, 'isFocused');
|
|
|
|
/// The semantics node has the quality of either being "enabled" or
|
|
/// "disabled".
|
|
///
|
|
/// For example, a button can be enabled or disabled and therefore has an
|
|
/// "enabled" state. Static text is usually neither enabled nor disabled and
|
|
/// therefore does not have an "enabled" state.
|
|
static const SemanticsFlag hasEnabledState = SemanticsFlag._(_kHasEnabledStateIndex, 'hasEnabledState');
|
|
|
|
/// Whether a semantic node that [hasEnabledState] is currently enabled.
|
|
///
|
|
/// A disabled element does not respond to user interaction. For example, a
|
|
/// button that currently does not respond to user interaction should be
|
|
/// marked as disabled.
|
|
static const SemanticsFlag isEnabled = SemanticsFlag._(_kIsEnabledIndex, 'isEnabled');
|
|
|
|
/// 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 [isChecked].
|
|
static const SemanticsFlag isInMutuallyExclusiveGroup = SemanticsFlag._(_kIsInMutuallyExclusiveGroupIndex, 'isInMutuallyExclusiveGroup');
|
|
|
|
/// Whether a semantic node is a header that divides content into sections.
|
|
///
|
|
/// For example, headers can be used to divide a list of alphabetically
|
|
/// sorted words into the sections A, B, C, etc. as can be found in many
|
|
/// address book applications.
|
|
static const SemanticsFlag isHeader = SemanticsFlag._(_kIsHeaderIndex, 'isHeader');
|
|
|
|
/// Whether the value of the semantics node is obscured.
|
|
///
|
|
/// This is usually used for text fields to indicate that its content
|
|
/// is a password or contains other sensitive information.
|
|
static const SemanticsFlag isObscured = SemanticsFlag._(_kIsObscuredIndex, 'isObscured');
|
|
|
|
/// Whether the value of the semantics node is coming from a multi-line text
|
|
/// field.
|
|
///
|
|
/// This is used for text fields to distinguish single-line text fields from
|
|
/// multi-line ones.
|
|
static const SemanticsFlag isMultiline = SemanticsFlag._(_kIsMultilineIndex, 'isMultiline');
|
|
|
|
/// Whether the semantics node is the root of a subtree for which a route name
|
|
/// should be announced.
|
|
///
|
|
/// When a node with this flag is removed from the semantics tree, the
|
|
/// framework will select the last in depth-first, paint order node with this
|
|
/// flag. When a node with this flag is added to the semantics tree, it is
|
|
/// selected automatically, unless there were multiple nodes with this flag
|
|
/// added. In this case, the last added node in depth-first, paint order
|
|
/// will be selected.
|
|
///
|
|
/// From this selected node, the framework will search in depth-first, paint
|
|
/// order for the first node with a [namesRoute] flag and a non-null,
|
|
/// non-empty label. The [namesRoute] and [scopesRoute] flags may be on the
|
|
/// same node. The label of the found node will be announced as an edge
|
|
/// transition. If no non-empty, non-null label is found then:
|
|
///
|
|
/// * VoiceOver will make a chime announcement.
|
|
/// * TalkBack will make no announcement
|
|
///
|
|
/// Semantic nodes annotated with this flag are generally not a11y focusable.
|
|
///
|
|
/// This is used in widgets such as Routes, Drawers, and Dialogs to
|
|
/// communicate significant changes in the visible screen.
|
|
static const SemanticsFlag scopesRoute = SemanticsFlag._(_kScopesRouteIndex, 'scopesRoute');
|
|
|
|
/// Whether the semantics node label is the name of a visually distinct
|
|
/// route.
|
|
///
|
|
/// This is used by certain widgets like Drawers and Dialogs, to indicate
|
|
/// that the node's semantic label can be used to announce an edge triggered
|
|
/// semantics update.
|
|
///
|
|
/// Semantic nodes annotated with this flag will still receive a11y focus.
|
|
///
|
|
/// Updating this label within the same active route subtree will not cause
|
|
/// additional announcements.
|
|
static const SemanticsFlag namesRoute = SemanticsFlag._(_kNamesRouteIndex, 'namesRoute');
|
|
|
|
/// Whether the semantics node is considered hidden.
|
|
///
|
|
/// Hidden elements are currently not visible on screen. They may be covered
|
|
/// by other elements or positioned outside of the visible area of a viewport.
|
|
///
|
|
/// Hidden elements cannot gain accessibility focus though regular touch. The
|
|
/// only way they can be focused is by moving the focus to them via linear
|
|
/// navigation.
|
|
///
|
|
/// Platforms are free to completely ignore hidden elements and new platforms
|
|
/// are encouraged to do so.
|
|
///
|
|
/// Instead of marking an element as hidden it should usually be excluded from
|
|
/// the semantics tree altogether. Hidden elements are only included in the
|
|
/// semantics tree to work around platform limitations and they are mainly
|
|
/// used to implement accessibility scrolling on iOS.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [RenderObject.describeSemanticsClip]
|
|
static const SemanticsFlag isHidden = SemanticsFlag._(_kIsHiddenIndex, 'isHidden');
|
|
|
|
/// Whether the semantics node represents an image.
|
|
///
|
|
/// Both TalkBack and VoiceOver will inform the user the semantics node
|
|
/// represents an image.
|
|
static const SemanticsFlag isImage = SemanticsFlag._(_kIsImageIndex, 'isImage');
|
|
|
|
/// Whether the semantics node is a live region.
|
|
///
|
|
/// A live region indicates that updates to semantics node are important.
|
|
/// Platforms may use this information to make polite announcements to the
|
|
/// user to inform them of updates to this node.
|
|
///
|
|
/// An example of a live region is a [SnackBar] widget. On Android and iOS,
|
|
/// live region causes a polite announcement to be generated automatically,
|
|
/// even if the widget does not have accessibility focus. This announcement
|
|
/// may not be spoken if the OS accessibility services are already
|
|
/// announcing something else, such as reading the label of a focused
|
|
/// widget or providing a system announcement.
|
|
static const SemanticsFlag isLiveRegion = SemanticsFlag._(_kIsLiveRegionIndex, 'isLiveRegion');
|
|
|
|
/// The semantics node has the quality of either being "on" or "off".
|
|
///
|
|
/// This flag is mutually exclusive with [hasCheckedState].
|
|
///
|
|
/// For example, a switch has toggled state.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [SemanticsFlag.isToggled], which controls whether the node is "on" or "off".
|
|
static const SemanticsFlag hasToggledState = SemanticsFlag._(_kHasToggledStateIndex, 'hasToggledState');
|
|
|
|
/// If true, the semantics node is "on". If false, the semantics node is
|
|
/// "off".
|
|
///
|
|
/// For example, if a switch is in the on position, [isToggled] is true.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [SemanticsFlag.hasToggledState], which enables a toggled state.
|
|
static const SemanticsFlag isToggled = SemanticsFlag._(_kIsToggledIndex, 'isToggled');
|
|
|
|
/// Whether the platform can scroll the semantics node when the user attempts
|
|
/// to move focus to an offscreen child.
|
|
///
|
|
/// For example, a [ListView] widget has implicit scrolling so that users can
|
|
/// easily move the accessibility focus to the next set of children. A
|
|
/// [PageView] widget does not have implicit scrolling, so that users don't
|
|
/// navigate to the next page when reaching the end of the current one.
|
|
static const SemanticsFlag hasImplicitScrolling = SemanticsFlag._(_kHasImplicitScrollingIndex, 'hasImplicitScrolling');
|
|
|
|
/// The possible semantics flags.
|
|
///
|
|
/// The map's key is the [index] of the flag and the value is the flag itself.
|
|
static const Map<int, SemanticsFlag> _kFlagById = <int, SemanticsFlag>{
|
|
_kHasCheckedStateIndex: hasCheckedState,
|
|
_kIsCheckedIndex: isChecked,
|
|
_kIsSelectedIndex: isSelected,
|
|
_kIsButtonIndex: isButton,
|
|
_kIsTextFieldIndex: isTextField,
|
|
_kIsFocusedIndex: isFocused,
|
|
_kHasEnabledStateIndex: hasEnabledState,
|
|
_kIsEnabledIndex: isEnabled,
|
|
_kIsInMutuallyExclusiveGroupIndex: isInMutuallyExclusiveGroup,
|
|
_kIsHeaderIndex: isHeader,
|
|
_kIsObscuredIndex: isObscured,
|
|
_kScopesRouteIndex: scopesRoute,
|
|
_kNamesRouteIndex: namesRoute,
|
|
_kIsHiddenIndex: isHidden,
|
|
_kIsImageIndex: isImage,
|
|
_kIsLiveRegionIndex: isLiveRegion,
|
|
_kHasToggledStateIndex: hasToggledState,
|
|
_kIsToggledIndex: isToggled,
|
|
_kHasImplicitScrollingIndex: hasImplicitScrolling,
|
|
_kIsMultilineIndex: isMultiline,
|
|
_kIsReadOnlyIndex: isReadOnly,
|
|
_kIsFocusableIndex: isFocusable,
|
|
_kIsLinkIndex: isLink,
|
|
_kIsSliderIndex: isSlider,
|
|
_kIsKeyboardKeyIndex: isKeyboardKey,
|
|
_kIsCheckStateMixedIndex: isCheckStateMixed,
|
|
};
|
|
|
|
static List<SemanticsFlag> get values => _kFlagById.values.toList(growable: false);
|
|
|
|
static SemanticsFlag? fromIndex(int index) => _kFlagById[index];
|
|
|
|
@override
|
|
String toString() => 'SemanticsFlag.$name';
|
|
}
|
|
|
|
// When adding a new StringAttribute, the classes in these files must be
|
|
// updated as well.
|
|
// * engine/src/flutter/lib/web_ui/lib/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.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [AttributedString], where the string attributes are used.
|
|
/// * [SpellOutStringAttribute], which causes the assistive technologies to
|
|
/// spell out the string character by character when announcing the string.
|
|
/// * [LocaleStringAttribute], which causes the assistive technologies to
|
|
/// treat the string in the specific language.
|
|
abstract base class StringAttribute extends NativeFieldWrapperClass1 {
|
|
StringAttribute._({
|
|
required this.range,
|
|
});
|
|
|
|
/// The range of the text to which this attribute applies.
|
|
final TextRange range;
|
|
|
|
/// Creates a new attribute with all properties copied except for range, which
|
|
/// is updated to the specified value.
|
|
///
|
|
/// For example, the [LocaleStringAttribute] specifies a [Locale] for its
|
|
/// range of characters. Copying it will result in a new
|
|
/// [LocaleStringAttribute] that has the same locale but an updated
|
|
/// [TextRange].
|
|
StringAttribute copy({required TextRange range});
|
|
}
|
|
|
|
/// A string attribute that causes the assistive technologies, e.g. VoiceOver,
|
|
/// to spell out the string character by character.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [AttributedString], where the string attributes are used.
|
|
/// * [LocaleStringAttribute], which causes the assistive technologies to
|
|
/// treat the string in the specific language.
|
|
base class SpellOutStringAttribute extends StringAttribute {
|
|
/// Creates a string attribute that denotes the text in [range] must be
|
|
/// spell out when the assistive technologies announce the string.
|
|
SpellOutStringAttribute({
|
|
required TextRange range,
|
|
}) : super._(range: range) {
|
|
_initSpellOutStringAttribute(this, range.start, range.end);
|
|
}
|
|
|
|
@Native<Void Function(Handle, Int32, Int32)>(symbol: 'NativeStringAttribute::initSpellOutStringAttribute')
|
|
external static void _initSpellOutStringAttribute(SpellOutStringAttribute instance, int start, int end);
|
|
|
|
@override
|
|
StringAttribute copy({required TextRange range}) {
|
|
return SpellOutStringAttribute(range: range);
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
return 'SpellOutStringAttribute($range)';
|
|
}
|
|
}
|
|
|
|
/// A string attribute that causes the assistive technologies, e.g. VoiceOver,
|
|
/// to treat string as a certain language.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [AttributedString], where the string attributes are used.
|
|
/// * [SpellOutStringAttribute], which causes the assistive technologies to
|
|
/// spell out the string character by character when announcing the string.
|
|
base class LocaleStringAttribute extends StringAttribute {
|
|
/// Creates a string attribute that denotes the text in [range] must be
|
|
/// treated as the language specified by the [locale] when the assistive
|
|
/// technologies announce the string.
|
|
LocaleStringAttribute({
|
|
required TextRange range,
|
|
required this.locale,
|
|
}) : super._(range: range) {
|
|
_initLocaleStringAttribute(this, range.start, range.end, locale.toLanguageTag());
|
|
}
|
|
|
|
/// The lanuage of this attribute.
|
|
final Locale locale;
|
|
|
|
@Native<Void Function(Handle, Int32, Int32, Handle)>(symbol: 'NativeStringAttribute::initLocaleStringAttribute')
|
|
external static void _initLocaleStringAttribute(LocaleStringAttribute instance, int start, int end, String locale);
|
|
|
|
@override
|
|
StringAttribute copy({required TextRange range}) {
|
|
return LocaleStringAttribute(range: range, locale: locale);
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
return 'LocaleStringAttribute($range, ${locale.toLanguageTag()})';
|
|
}
|
|
}
|
|
|
|
/// An object that creates [SemanticsUpdate] objects.
|
|
///
|
|
/// Once created, the [SemanticsUpdate] objects can be passed to
|
|
/// [PlatformDispatcher.updateSemantics] to update the semantics conveyed to the
|
|
/// user.
|
|
abstract class SemanticsUpdateBuilder {
|
|
/// Creates an empty [SemanticsUpdateBuilder] object.
|
|
factory SemanticsUpdateBuilder() = _NativeSemanticsUpdateBuilder;
|
|
|
|
/// Update the information associated with the node with the given `id`.
|
|
///
|
|
/// The semantics nodes form a tree, with the root of the tree always having
|
|
/// an id of zero. The `childrenInTraversalOrder` and `childrenInHitTestOrder`
|
|
/// are the ids of the nodes that are immediate children of this node. The
|
|
/// former enumerates children in traversal order, and the latter enumerates
|
|
/// the same children in the hit test order. The two lists must have the same
|
|
/// length and contain the same ids. They may only differ in the order the
|
|
/// ids are listed in. For more information about different child orders, see
|
|
/// [DebugSemanticsDumpOrder].
|
|
///
|
|
/// The system retains the nodes that are currently reachable from the root.
|
|
/// A given update need not contain information for nodes that do not change
|
|
/// in the update. If a node is not reachable from the root after an update,
|
|
/// the node will be discarded from the tree.
|
|
///
|
|
/// The `flags` are a bit field of [SemanticsFlag]s that apply to this node.
|
|
///
|
|
/// The `actions` are a bit field of [SemanticsAction]s that can be undertaken
|
|
/// by this node. If the user wishes to undertake one of these actions on this
|
|
/// node, the [PlatformDispatcher.onSemanticsActionEvent] will be called with
|
|
/// a [SemanticsActionEvent] specifying the action to be performed. Because
|
|
/// the semantics tree is maintained asynchronously, the
|
|
/// [PlatformDispatcher.onSemanticsActionEvent] callback might be called with
|
|
/// an action that is no longer possible.
|
|
///
|
|
/// The `label` is a string that describes this node. The `value` property
|
|
/// describes the current value of the node as a string. The `increasedValue`
|
|
/// string will become the `value` string after a [SemanticsAction.increase]
|
|
/// action is performed. The `decreasedValue` string will become the `value`
|
|
/// string after a [SemanticsAction.decrease] action is performed. The `hint`
|
|
/// string describes what result an action performed on this node has. The
|
|
/// reading direction of all these strings is given by `textDirection`.
|
|
///
|
|
/// The `labelAttirbutes`, `valueAttirbutes`, `hintAttributes`,
|
|
/// `increasedValueAttirbutes`, and `decreasedValueAttributes` are the lists of
|
|
/// [StringAttribute] carried by the `label`, `value`, `hint`, `increasedValue`,
|
|
/// and `decreasedValue` respectively. Their contents must not be changed during
|
|
/// the semantics update.
|
|
///
|
|
/// The `tooltip` is a string that describe additional information when user
|
|
/// hover or long press on the backing widget of this semantics node.
|
|
///
|
|
/// The fields `textSelectionBase` and `textSelectionExtent` describe the
|
|
/// currently selected text within `value`. A value of -1 indicates no
|
|
/// current text selection base or extent.
|
|
///
|
|
/// The field `maxValueLength` is used to indicate that an editable text
|
|
/// field has a limit on the number of characters entered. If it is -1 there
|
|
/// is no limit on the number of characters entered. The field
|
|
/// `currentValueLength` indicates how much of that limit has already been
|
|
/// used up. When `maxValueLength` is >= 0, `currentValueLength` must also be
|
|
/// >= 0, otherwise it should be specified to be -1.
|
|
///
|
|
/// The field `platformViewId` references the platform view, whose semantics
|
|
/// nodes will be added as children to this node. If a platform view is
|
|
/// specified, `childrenInHitTestOrder` and `childrenInTraversalOrder` must
|
|
/// be empty. A value of -1 indicates that this node is not associated with a
|
|
/// platform view.
|
|
///
|
|
/// For scrollable nodes `scrollPosition` describes the current scroll
|
|
/// position in logical pixel. `scrollExtentMax` and `scrollExtentMin`
|
|
/// describe the maximum and minimum in-rage values that `scrollPosition` can
|
|
/// be. Both or either may be infinity to indicate unbound scrolling. The
|
|
/// value for `scrollPosition` can (temporarily) be outside this range, for
|
|
/// example during an overscroll. `scrollChildren` is the count of the
|
|
/// total number of child nodes that contribute semantics and `scrollIndex`
|
|
/// is the index of the first visible child node that contributes semantics.
|
|
///
|
|
/// The `rect` is the region occupied by this node in its own coordinate
|
|
/// system.
|
|
///
|
|
/// The `transform` is a matrix that maps this node's coordinate system into
|
|
/// its parent's coordinate system.
|
|
///
|
|
/// The `elevation` describes the distance in z-direction between this node
|
|
/// and the `elevation` of the parent.
|
|
///
|
|
/// The `thickness` describes how much space this node occupies in the
|
|
/// z-direction starting at `elevation`. Basically, in the z-direction the
|
|
/// node starts at `elevation` above the parent and ends at `elevation` +
|
|
/// `thickness` above the parent.
|
|
void updateNode({
|
|
required int id,
|
|
required int flags,
|
|
required int actions,
|
|
required int maxValueLength,
|
|
required int currentValueLength,
|
|
required int textSelectionBase,
|
|
required int textSelectionExtent,
|
|
required int platformViewId,
|
|
required int scrollChildren,
|
|
required int scrollIndex,
|
|
required double scrollPosition,
|
|
required double scrollExtentMax,
|
|
required double scrollExtentMin,
|
|
required double elevation,
|
|
required double thickness,
|
|
required Rect rect,
|
|
required String label,
|
|
required List<StringAttribute> labelAttributes,
|
|
required String value,
|
|
required List<StringAttribute> valueAttributes,
|
|
required String increasedValue,
|
|
required List<StringAttribute> increasedValueAttributes,
|
|
required String decreasedValue,
|
|
required List<StringAttribute> decreasedValueAttributes,
|
|
required String hint,
|
|
required List<StringAttribute> hintAttributes,
|
|
String? tooltip,
|
|
TextDirection? textDirection,
|
|
required Float64List transform,
|
|
required Int32List childrenInTraversalOrder,
|
|
required Int32List childrenInHitTestOrder,
|
|
required Int32List additionalActions,
|
|
});
|
|
|
|
/// Update the custom semantics action associated with the given `id`.
|
|
///
|
|
/// The name of the action exposed to the user is the `label`. For overridden
|
|
/// standard actions this value is ignored.
|
|
///
|
|
/// The `hint` should describe what happens when an action occurs, not the
|
|
/// manner in which a tap is accomplished. For example, use "delete" instead
|
|
/// of "double tap to delete".
|
|
///
|
|
/// The text direction of the `hint` and `label` is the same as the global
|
|
/// window.
|
|
///
|
|
/// For overridden standard actions, `overrideId` corresponds with a
|
|
/// [SemanticsAction.index] value. For custom actions this argument should not be
|
|
/// provided.
|
|
void updateCustomAction({required int id, String? label, String? hint, int overrideId = -1});
|
|
|
|
/// Creates a [SemanticsUpdate] object that encapsulates the updates recorded
|
|
/// by this object.
|
|
///
|
|
/// The returned object can be passed to [PlatformDispatcher.updateSemantics]
|
|
/// to actually update the semantics retained by the system.
|
|
///
|
|
/// This object is unusable after calling build.
|
|
SemanticsUpdate build();
|
|
}
|
|
|
|
@pragma('vm:entry-point')
|
|
base class _NativeSemanticsUpdateBuilder extends NativeFieldWrapperClass1 implements SemanticsUpdateBuilder {
|
|
@pragma('vm:entry-point')
|
|
_NativeSemanticsUpdateBuilder() { _constructor(); }
|
|
|
|
@Native<Void Function(Handle)>(symbol: 'SemanticsUpdateBuilder::Create')
|
|
external void _constructor();
|
|
|
|
@override
|
|
void updateNode({
|
|
required int id,
|
|
required int flags,
|
|
required int actions,
|
|
required int maxValueLength,
|
|
required int currentValueLength,
|
|
required int textSelectionBase,
|
|
required int textSelectionExtent,
|
|
required int platformViewId,
|
|
required int scrollChildren,
|
|
required int scrollIndex,
|
|
required double scrollPosition,
|
|
required double scrollExtentMax,
|
|
required double scrollExtentMin,
|
|
required double elevation,
|
|
required double thickness,
|
|
required Rect rect,
|
|
required String label,
|
|
required List<StringAttribute> labelAttributes,
|
|
required String value,
|
|
required List<StringAttribute> valueAttributes,
|
|
required String increasedValue,
|
|
required List<StringAttribute> increasedValueAttributes,
|
|
required String decreasedValue,
|
|
required List<StringAttribute> decreasedValueAttributes,
|
|
required String hint,
|
|
required List<StringAttribute> hintAttributes,
|
|
String? tooltip,
|
|
TextDirection? textDirection,
|
|
required Float64List transform,
|
|
required Int32List childrenInTraversalOrder,
|
|
required Int32List childrenInHitTestOrder,
|
|
required Int32List additionalActions,
|
|
}) {
|
|
assert(_matrix4IsValid(transform));
|
|
_updateNode(
|
|
id,
|
|
flags,
|
|
actions,
|
|
maxValueLength,
|
|
currentValueLength,
|
|
textSelectionBase,
|
|
textSelectionExtent,
|
|
platformViewId,
|
|
scrollChildren,
|
|
scrollIndex,
|
|
scrollPosition,
|
|
scrollExtentMax,
|
|
scrollExtentMin,
|
|
rect.left,
|
|
rect.top,
|
|
rect.right,
|
|
rect.bottom,
|
|
elevation,
|
|
thickness,
|
|
label,
|
|
labelAttributes,
|
|
value,
|
|
valueAttributes,
|
|
increasedValue,
|
|
increasedValueAttributes,
|
|
decreasedValue,
|
|
decreasedValueAttributes,
|
|
hint,
|
|
hintAttributes,
|
|
tooltip ?? '',
|
|
textDirection != null ? textDirection.index + 1 : 0,
|
|
transform,
|
|
childrenInTraversalOrder,
|
|
childrenInHitTestOrder,
|
|
additionalActions,
|
|
);
|
|
}
|
|
@Native<
|
|
Void Function(
|
|
Pointer<Void>,
|
|
Int32,
|
|
Int32,
|
|
Int32,
|
|
Int32,
|
|
Int32,
|
|
Int32,
|
|
Int32,
|
|
Int32,
|
|
Int32,
|
|
Int32,
|
|
Double,
|
|
Double,
|
|
Double,
|
|
Double,
|
|
Double,
|
|
Double,
|
|
Double,
|
|
Double,
|
|
Double,
|
|
Handle,
|
|
Handle,
|
|
Handle,
|
|
Handle,
|
|
Handle,
|
|
Handle,
|
|
Handle,
|
|
Handle,
|
|
Handle,
|
|
Handle,
|
|
Handle,
|
|
Int32,
|
|
Handle,
|
|
Handle,
|
|
Handle,
|
|
Handle)>(symbol: 'SemanticsUpdateBuilder::updateNode')
|
|
external void _updateNode(
|
|
int id,
|
|
int flags,
|
|
int actions,
|
|
int maxValueLength,
|
|
int currentValueLength,
|
|
int textSelectionBase,
|
|
int textSelectionExtent,
|
|
int platformViewId,
|
|
int scrollChildren,
|
|
int scrollIndex,
|
|
double scrollPosition,
|
|
double scrollExtentMax,
|
|
double scrollExtentMin,
|
|
double left,
|
|
double top,
|
|
double right,
|
|
double bottom,
|
|
double elevation,
|
|
double thickness,
|
|
String label,
|
|
List<StringAttribute> labelAttributes,
|
|
String value,
|
|
List<StringAttribute> valueAttributes,
|
|
String increasedValue,
|
|
List<StringAttribute> increasedValueAttributes,
|
|
String decreasedValue,
|
|
List<StringAttribute> decreasedValueAttributes,
|
|
String hint,
|
|
List<StringAttribute> hintAttributes,
|
|
String tooltip,
|
|
int textDirection,
|
|
Float64List transform,
|
|
Int32List childrenInTraversalOrder,
|
|
Int32List childrenInHitTestOrder,
|
|
Int32List additionalActions);
|
|
|
|
@override
|
|
void updateCustomAction({required int id, String? label, String? hint, int overrideId = -1}) {
|
|
_updateCustomAction(id, label ?? '', hint ?? '', overrideId);
|
|
}
|
|
@Native<Void Function(Pointer<Void>, Int32, Handle, Handle, Int32)>(symbol: 'SemanticsUpdateBuilder::updateCustomAction')
|
|
external void _updateCustomAction(int id, String label, String hint, int overrideId);
|
|
|
|
@override
|
|
SemanticsUpdate build() {
|
|
final _NativeSemanticsUpdate semanticsUpdate = _NativeSemanticsUpdate._();
|
|
_build(semanticsUpdate);
|
|
return semanticsUpdate;
|
|
}
|
|
@Native<Void Function(Pointer<Void>, Handle)>(symbol: 'SemanticsUpdateBuilder::build')
|
|
external void _build(_NativeSemanticsUpdate outSemanticsUpdate);
|
|
}
|
|
|
|
/// An opaque object representing a batch of semantics updates.
|
|
///
|
|
/// To create a SemanticsUpdate object, use a [SemanticsUpdateBuilder].
|
|
///
|
|
/// Semantics updates can be applied to the system's retained semantics tree
|
|
/// using the [PlatformDispatcher.updateSemantics] method.
|
|
abstract class SemanticsUpdate {
|
|
/// Releases the resources used by this semantics update.
|
|
///
|
|
/// After calling this function, the semantics update is cannot be used
|
|
/// further.
|
|
///
|
|
/// This can't be a leaf call because the native function calls Dart API
|
|
/// (Dart_SetNativeInstanceField).
|
|
void dispose();
|
|
}
|
|
|
|
@pragma('vm:entry-point')
|
|
base class _NativeSemanticsUpdate extends NativeFieldWrapperClass1 implements SemanticsUpdate {
|
|
/// This class is created by the engine, and should not be instantiated
|
|
/// or extended directly.
|
|
///
|
|
/// To create a SemanticsUpdate object, use a [SemanticsUpdateBuilder].
|
|
@pragma('vm:entry-point')
|
|
_NativeSemanticsUpdate._();
|
|
|
|
@override
|
|
@Native<Void Function(Pointer<Void>)>(symbol: 'SemanticsUpdate::dispose')
|
|
external void dispose();
|
|
}
|