From 9ab6550b45cd4608f24e76a5d4e812d68a167491 Mon Sep 17 00:00:00 2001 From: chunhtai <47866232+chunhtai@users.noreply.github.com> Date: Wed, 19 Jun 2019 09:42:23 -0700 Subject: [PATCH] Introduce read only text field semantics (#9281) --- lib/stub_ui/lib/src/engine/semantics/semantics.dart | 5 +++++ lib/stub_ui/lib/src/ui/semantics.dart | 10 ++++++++++ lib/ui/semantics.dart | 9 +++++++++ lib/ui/semantics/semantics_node.h | 1 + .../android/io/flutter/view/AccessibilityBridge.java | 9 ++++++--- .../ios/framework/Source/accessibility_bridge.mm | 11 ++++++++--- shell/platform/embedder/embedder.h | 4 ++++ 7 files changed, 43 insertions(+), 6 deletions(-) diff --git a/lib/stub_ui/lib/src/engine/semantics/semantics.dart b/lib/stub_ui/lib/src/engine/semantics/semantics.dart index d2334ff80..83912ab0f 100644 --- a/lib/stub_ui/lib/src/engine/semantics/semantics.dart +++ b/lib/stub_ui/lib/src/engine/semantics/semantics.dart @@ -576,6 +576,11 @@ class SemanticsObject { /// Whether this object represents an editable text field. bool get isTextField => hasFlag(ui.SemanticsFlag.isTextField); + /// Whether this object is read only. + /// + /// Only applicable when [isTextField] is true. + bool get isReadOnly => hasFlag(ui.SemanticsFlag.isReadOnly); + /// Whether this object represents an image with no tappable functionality. bool get isVisualOnly => hasFlag(ui.SemanticsFlag.isImage) && diff --git a/lib/stub_ui/lib/src/ui/semantics.dart b/lib/stub_ui/lib/src/ui/semantics.dart index c7ae0cec1..3ab96af18 100644 --- a/lib/stub_ui/lib/src/ui/semantics.dart +++ b/lib/stub_ui/lib/src/ui/semantics.dart @@ -298,6 +298,7 @@ class SemanticsFlag { static const int _kIsToggledIndex = 1 << 17; static const int _kHasImplicitScrollingIndex = 1 << 18; static const int _kIsMultilineIndex = 1 << 19; + static const int _kIsReadOnlyIndex = 1 << 20; const SemanticsFlag._(this.index); @@ -354,6 +355,12 @@ class SemanticsFlag { static const SemanticsFlag isTextField = const SemanticsFlag._(_kIsTextFieldIndex); + /// Whether the semantic node is read only. + /// + /// Only applicable when [isTextField] is true. + static const SemanticsFlag isReadOnly = + const SemanticsFlag._(_kIsReadOnlyIndex); + /// Whether the semantic node currently holds the user's focus. /// /// The focused element is usually the current receiver of keyboard inputs. @@ -539,6 +546,7 @@ class SemanticsFlag { _kIsToggledIndex: isToggled, _kHasImplicitScrollingIndex: hasImplicitScrolling, _kIsMultilineIndex: isMultiline, + _kIsReadOnlyIndex: isReadOnly, }; @override @@ -584,6 +592,8 @@ class SemanticsFlag { return 'SemanticsFlag.hasImplicitScrolling'; case _kIsMultilineIndex: return 'SemanticsFlag.isMultiline'; + case _kIsReadOnlyIndex: + return 'SemanticsFlag.isReadOnly'; } return null; } diff --git a/lib/ui/semantics.dart b/lib/ui/semantics.dart index 6f1057f0d..f6f4e2afe 100644 --- a/lib/ui/semantics.dart +++ b/lib/ui/semantics.dart @@ -288,6 +288,7 @@ class SemanticsFlag { static const int _kHasToggledStateIndex = 1 << 16; static const int _kIsToggledIndex = 1 << 17; static const int _kHasImplicitScrollingIndex = 1 << 18; + static const int _kIsReadOnlyIndex = 1 << 20; const SemanticsFlag._(this.index); @@ -341,6 +342,11 @@ class SemanticsFlag { /// affordances. static const SemanticsFlag isTextField = SemanticsFlag._(_kIsTextFieldIndex); + /// Whether the semantic node is read only. + /// + /// Only applicable when [isTextField] is true. + static const SemanticsFlag isReadOnly = SemanticsFlag._(_kIsReadOnlyIndex); + /// Whether the semantic node currently holds the user's focus. /// /// The focused element is usually the current receiver of keyboard inputs. @@ -506,6 +512,7 @@ class SemanticsFlag { _kHasToggledStateIndex: hasToggledState, _kIsToggledIndex: isToggled, _kHasImplicitScrollingIndex: hasImplicitScrolling, + _kIsReadOnlyIndex: isReadOnly, }; @override @@ -549,6 +556,8 @@ class SemanticsFlag { return 'SemanticsFlag.isToggled'; case _kHasImplicitScrollingIndex: return 'SemanticsFlag.hasImplicitScrolling'; + case _kIsReadOnlyIndex: + return 'SemanticsFlag.isReadOnly'; } return null; } diff --git a/lib/ui/semantics/semantics_node.h b/lib/ui/semantics/semantics_node.h index 98755b158..62f1b8b32 100644 --- a/lib/ui/semantics/semantics_node.h +++ b/lib/ui/semantics/semantics_node.h @@ -69,6 +69,7 @@ enum class SemanticsFlags : int32_t { kHasToggledState = 1 << 16, kIsToggled = 1 << 17, kHasImplicitScrolling = 1 << 18, + kIsReadOnly = 1 << 20, }; const int kScrollableSemanticsFlags = diff --git a/shell/platform/android/io/flutter/view/AccessibilityBridge.java b/shell/platform/android/io/flutter/view/AccessibilityBridge.java index 20d366b2f..3030b7f36 100644 --- a/shell/platform/android/io/flutter/view/AccessibilityBridge.java +++ b/shell/platform/android/io/flutter/view/AccessibilityBridge.java @@ -529,9 +529,11 @@ public class AccessibilityBridge extends AccessibilityNodeProvider { if (semanticsNode.hasFlag(Flag.IS_TEXT_FIELD)) { result.setPassword(semanticsNode.hasFlag(Flag.IS_OBSCURED)); - result.setClassName("android.widget.EditText"); + if (!semanticsNode.hasFlag(Flag.IS_READ_ONLY)) { + result.setClassName("android.widget.EditText"); + } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - result.setEditable(true); + result.setEditable(!semanticsNode.hasFlag(Flag.IS_READ_ONLY)); if (semanticsNode.textSelectionBase != -1 && semanticsNode.textSelectionExtent != -1) { result.setTextSelection(semanticsNode.textSelectionBase, semanticsNode.textSelectionExtent); } @@ -1611,7 +1613,8 @@ public class AccessibilityBridge extends AccessibilityNodeProvider { IS_LIVE_REGION(1 << 15), HAS_TOGGLED_STATE(1 << 16), IS_TOGGLED(1 << 17), - HAS_IMPLICIT_SCROLLING(1 << 18); + HAS_IMPLICIT_SCROLLING(1 << 18), + IS_READ_ONLY(1 << 20); final int value; diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm index baa754bb8..25c53d2a9 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm @@ -713,7 +713,8 @@ SemanticsObject* AccessibilityBridge::GetOrCreateObject(int32_t uid, if (!object) { // New node case: simply create a new SemanticsObject. flutter::SemanticsNode node = updates[uid]; - if (node.HasFlag(flutter::SemanticsFlags::kIsTextField)) { + if (node.HasFlag(flutter::SemanticsFlags::kIsTextField) && + !node.HasFlag(flutter::SemanticsFlags::kIsReadOnly)) { // Text fields are backed by objects that implement UITextInput. object = [[[TextInputSemanticsObject alloc] initWithBridge:GetWeakPtr() uid:uid] autorelease]; } else { @@ -729,13 +730,16 @@ SemanticsObject* AccessibilityBridge::GetOrCreateObject(int32_t uid, flutter::SemanticsNode node = nodeEntry->second; BOOL isTextField = node.HasFlag(flutter::SemanticsFlags::kIsTextField); BOOL wasTextField = object.node.HasFlag(flutter::SemanticsFlags::kIsTextField); - if (wasTextField != isTextField) { + BOOL isReadOnly = node.HasFlag(flutter::SemanticsFlags::kIsReadOnly); + BOOL wasReadOnly = object.node.HasFlag(flutter::SemanticsFlags::kIsReadOnly); + if (wasTextField != isTextField || isReadOnly != wasReadOnly) { // The node changed its type from text field to something else, or vice versa. In this // case, we cannot reuse the existing SemanticsObject implementation. Instead, we replace // it with a new instance. NSUInteger positionInChildlist = [object.parent.children indexOfObject:object]; + SemanticsObject* parent = object.parent; [objects_ removeObjectForKey:@(node.id)]; - if (isTextField) { + if (isTextField && !isReadOnly) { // Text fields are backed by objects that implement UITextInput. object = [[[TextInputSemanticsObject alloc] initWithBridge:GetWeakPtr() uid:uid] autorelease]; @@ -743,6 +747,7 @@ SemanticsObject* AccessibilityBridge::GetOrCreateObject(int32_t uid, object = [[[FlutterSemanticsObject alloc] initWithBridge:GetWeakPtr() uid:uid] autorelease]; } + object.parent = parent; [object.parent.children replaceObjectAtIndex:positionInChildlist withObject:object]; objects_.get()[@(node.id)] = object; } diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index f409d26b0..e6afed179 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -153,6 +153,10 @@ typedef enum { // |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. kFlutterSemanticsFlagHasImplicitScrolling = 1 << 18, + // Whether the semantic node is read only. + // + // Only applicable when kFlutterSemanticsFlagIsTextField flag is on. + kFlutterSemanticsFlagIsReadOnly = 1 << 20, } FlutterSemanticsFlag; typedef enum { -- GitLab