Add isFocusable to SemanticsFlag (flutter/engine#12618)

This adds an isFocusable to SemanticsFlag so that the framework can tell the engine what semantics nodes are allowed to be focused, which will affect what platform flags are applied to the semantics information.

This flag is not yet in use by the frame
This commit is contained in:
Greg Spencer 2019-09-30 09:46:48 -07:00 committed by GitHub
parent bcaf7cfb46
commit b174801460
4 changed files with 53 additions and 9 deletions

View File

@ -290,6 +290,7 @@ class SemanticsFlag {
static const int _kHasImplicitScrollingIndex = 1 << 18;
static const int _kIsMultilineIndex = 1 << 19;
static const int _kIsReadOnlyIndex = 1 << 20;
static const int _kIsFocusableIndex = 1 << 21;
const SemanticsFlag._(this.index);
@ -348,6 +349,11 @@ class SemanticsFlag {
/// Only applicable when [isTextField] is true.
static const SemanticsFlag isReadOnly = SemanticsFlag._(_kIsReadOnlyIndex);
/// 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);
/// Whether the semantic node currently holds the user's focus.
///
/// The focused element is usually the current receiver of keyboard inputs.

View File

@ -72,6 +72,7 @@ enum class SemanticsFlags : int32_t {
// The Dart API defines the following flag but it isn't used in iOS.
// kIsMultiline = 1 << 19,
kIsReadOnly = 1 << 20,
kIsFocusable = 1 << 21,
};
const int kScrollableSemanticsFlags =

View File

@ -293,6 +293,7 @@ class SemanticsFlag {
static const int _kHasImplicitScrollingIndex = 1 << 18;
static const int _kIsMultilineIndex = 1 << 19;
static const int _kIsReadOnlyIndex = 1 << 20;
static const int _kIsFocusableIndex = 1 << 21;
const SemanticsFlag._(this.index);
@ -351,6 +352,11 @@ class SemanticsFlag {
/// Only applicable when [isTextField] is true.
static const SemanticsFlag isReadOnly = SemanticsFlag._(_kIsReadOnlyIndex);
/// 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);
/// Whether the semantic node currently holds the user's focus.
///
/// The focused element is usually the current receiver of keyboard inputs.
@ -517,6 +523,7 @@ class SemanticsFlag {
_kIsSelectedIndex: isSelected,
_kIsButtonIndex: isButton,
_kIsTextFieldIndex: isTextField,
_kIsFocusableIndex: isFocusable,
_kIsFocusedIndex: isFocused,
_kHasEnabledStateIndex: hasEnabledState,
_kIsEnabledIndex: isEnabled,
@ -548,6 +555,8 @@ class SemanticsFlag {
return 'SemanticsFlag.isButton';
case _kIsTextFieldIndex:
return 'SemanticsFlag.isTextField';
case _kIsFocusableIndex:
return 'SemanticsFlag.isFocusable';
case _kIsFocusedIndex:
return 'SemanticsFlag.isFocused';
case _kHasEnabledStateIndex:

View File

@ -1271,7 +1271,7 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
}
if (lastAdded != null && lastAdded.id != previousRouteId) {
previousRouteId = lastAdded.id;
createAndSendWindowChangeEvent(lastAdded);
sendWindowChangeEvent(lastAdded);
}
flutterNavigationStack.clear();
for (SemanticsNode semanticsNode : newRoutes) {
@ -1290,7 +1290,7 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
// TODO(goderbauer): Send this event only once (!) for changed subtrees,
// see https://github.com/flutter/flutter/issues/14534
sendAccessibilityEvent(0, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
sendWindowContentChangeEvent(0);
for (SemanticsNode object : updated) {
if (object.didScroll()) {
@ -1362,13 +1362,13 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
String label = object.label == null ? "" : object.label;
String previousLabel = object.previousLabel == null ? "" : object.label;
if (!label.equals(previousLabel) || !object.hadFlag(Flag.IS_LIVE_REGION)) {
sendAccessibilityEvent(object.id, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
sendWindowContentChangeEvent(object.id);
}
} else if (object.hasFlag(Flag.IS_TEXT_FIELD) && object.didChangeLabel()
&& inputFocusedSemanticsNode != null && inputFocusedSemanticsNode.id == object.id) {
// Text fields should announce when their label changes while focused. We use a live
// region tag to do so, and this event triggers that update.
sendAccessibilityEvent(object.id, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
sendWindowContentChangeEvent(object.id);
}
if (accessibilityFocusedSemanticsNode != null && accessibilityFocusedSemanticsNode.id == object.id
&& !object.hadFlag(Flag.IS_SELECTED) && object.hasFlag(Flag.IS_SELECTED)) {
@ -1472,13 +1472,13 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
}
/**
* Factory method that creates a {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED} and sends
* the event to Android's accessibility system.
* Creates a {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED} and sends the event to
* Android's accessibility system.
*
* The given {@code route} should be a {@link SemanticsNode} that represents a navigation route
* in the Flutter app.
*/
private void createAndSendWindowChangeEvent(@NonNull SemanticsNode route) {
private void sendWindowChangeEvent(@NonNull SemanticsNode route) {
AccessibilityEvent event = obtainAccessibilityEvent(
route.id,
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
@ -1488,6 +1488,27 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
sendAccessibilityEvent(event);
}
/**
* Creates a {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} and sends the event to
* Android's accessibility system.
*
* It sets the content change types to {@link AccessibilityEvent#CONTENT_CHANGE_TYPE_SUBTREE}
* when supported by the API level.
*
* The given {@code virtualViewId} should be a {@link SemanticsNode} below which the content has
* changed.
*/
private void sendWindowContentChangeEvent(int virtualViewId) {
AccessibilityEvent event = obtainAccessibilityEvent(
virtualViewId,
AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
}
sendAccessibilityEvent(event);
}
/**
* Factory method that creates a new {@link AccessibilityEvent} that is configured to represent
* the Flutter {@link SemanticsNode} represented by the given {@code virtualViewId}, categorized
@ -1559,7 +1580,7 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
}
accessibilityFocusedSemanticsNode = null;
hoveredObject = null;
sendAccessibilityEvent(0, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
sendWindowContentChangeEvent(0);
}
/**
@ -1626,7 +1647,8 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
HAS_IMPLICIT_SCROLLING(1 << 18),
// The Dart API defines the following flag but it isn't used in Android.
// IS_MULTILINE(1 << 19);
IS_READ_ONLY(1 << 20);
IS_READ_ONLY(1 << 20),
IS_FOCUSABLE(1 << 21);
final int value;
@ -2000,6 +2022,12 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
if (hasFlag(Flag.SCOPES_ROUTE)) {
return false;
}
if (hasFlag(Flag.IS_FOCUSABLE)) {
return true;
}
// If not explicitly set as focusable, then use our legacy
// algorithm. Once all focusable widgets have a Focus widget, then
// this won't be needed.
int scrollableActions = Action.SCROLL_RIGHT.value | Action.SCROLL_LEFT.value
| Action.SCROLL_UP.value | Action.SCROLL_DOWN.value;
return (actions & ~scrollableActions) != 0 || flags != 0