5#import "flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.h"
7#include "flutter/fml/platform/darwin/scoped_nsobject.h"
8#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h"
9#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterSemanticsScrollView.h"
14 UIAccessibilityScrollDirection direction) {
21 case UIAccessibilityScrollDirectionRight:
22 case UIAccessibilityScrollDirectionPrevious:
25 case UIAccessibilityScrollDirectionLeft:
26 case UIAccessibilityScrollDirectionNext:
29 case UIAccessibilityScrollDirectionUp:
31 case UIAccessibilityScrollDirectionDown:
39 SkM44 globalTransform = [reference node].transform;
41 globalTransform = parent.node.transform * globalTransform;
43 return globalTransform;
51CGPoint ConvertPointToGlobal(
SemanticsObject* reference, CGPoint local_point) {
52 SkM44 globalTransform = GetGlobalTransform(reference);
58 UIScreen* screen = [[[reference bridge]->view()
window] screen];
60 CGFloat
scale = screen == nil ? [UIScreen mainScreen].scale : screen.scale;
62 return [[reference bridge]->view() convertPoint:
result toView:nil];
65CGRect ConvertRectToGlobal(
SemanticsObject* reference, CGRect local_rect) {
66 SkM44 globalTransform = GetGlobalTransform(reference);
70 SkPoint::Make(local_rect.origin.x + local_rect.size.width, local_rect.origin.y),
72 local_rect.origin.y + local_rect.size.height),
74 local_rect.origin.y + local_rect.size.height)
76 for (
auto& point : quad) {
80 NSCAssert(
rect.setBoundsCheck(quad, 4),
@"Transformed points can't form a rect");
81 rect.setBounds(quad, 4);
86 UIScreen* screen = [[[reference bridge]->view()
window] screen];
88 CGFloat
scale = screen == nil ? [UIScreen mainScreen].scale : screen.scale;
91 return UIAccessibilityConvertFrameToScreenCoordinates(
result, [reference bridge]->view());
97@property(nonatomic, readonly) UISwitch* nativeSwitch;
102- (instancetype)initWithBridge:(
fml::WeakPtr<
flutter::AccessibilityBridgeIos>)bridge
106 _nativeSwitch = [[UISwitch alloc] init];
112 [_nativeSwitch release];
116- (NSMethodSignature*)methodSignatureForSelector:(
SEL)sel {
117 NSMethodSignature*
result = [
super methodSignatureForSelector:sel];
119 result = [_nativeSwitch methodSignatureForSelector:sel];
124- (void)forwardInvocation:(NSInvocation*)anInvocation {
125 [anInvocation setTarget:_nativeSwitch];
126 [anInvocation invoke];
132 _nativeSwitch.
on = YES;
134 _nativeSwitch.on = NO;
140 return _nativeSwitch.accessibilityValue;
146 _nativeSwitch.enabled = YES;
148 _nativeSwitch.enabled = NO;
151 return _nativeSwitch.accessibilityTraits;
162- (instancetype)initWithBridge:(
fml::WeakPtr<
flutter::AccessibilityBridgeIos>)bridge
167 [_scrollView setShowsHorizontalScrollIndicator:NO];
168 [_scrollView setShowsVerticalScrollIndicator:NO];
169 [
self.bridge->view() addSubview:_scrollView];
175 [_scrollView removeFromSuperview];
177 [_scrollView release];
190 [_scrollView setFrame:[
self accessibilityFrame]];
191 [_scrollView setContentSize:[
self contentSizeInternal]];
192 [_scrollView setContentOffset:[
self contentOffsetInternal] animated:NO];
237 return ConvertRectToGlobal(
self,
result).size;
251 return CGPointMake(
result.x - origin.x,
result.y - origin.y);
272#pragma mark - Override base class designated initializers
275- (instancetype)init {
277 [
super doesNotRecognizeSelector:_cmd];
281#pragma mark - Designated initializers
283- (instancetype)initWithBridge:(
fml::
WeakPtr<
flutter::AccessibilityBridgeIos>)bridge
291 self = [
super initWithAccessibilityContainer:bridge->view()];
296 _children = [[NSMutableArray alloc] init];
305 [child privateSetParent:nil];
307 [_children removeAllObjects];
308 [_childrenInHitTestOrder removeAllObjects];
310 [_childrenInHitTestOrder release];
313 _container.
get().semanticsObject = nil;
318#pragma mark - Semantic object property accesser
322 [child privateSetParent:nil];
325 _children = [[NSMutableArray alloc] initWithArray:children];
327 [child privateSetParent:self];
331- (void)setChildrenInHitTestOrder:(NSArray<
SemanticsObject*>*)childrenInHitTestOrder {
333 [child privateSetParent:nil];
335 [_childrenInHitTestOrder release];
338 [child privateSetParent:self];
343 return [
self.children count] != 0;
346#pragma mark - Semantic object method
348- (
BOOL)isAccessibilityBridgeAlive {
349 return [
self bridge].get() != nil;
352- (void)setSemanticsNode:(const
flutter::SemanticsNode*)node {
356- (void)accessibilityBridgeDidFinishUpdate {
362- (
BOOL)nodeWillCauseLayoutChange:(const
flutter::SemanticsNode*)node {
363 return [
self node].rect != node->rect || [
self node].transform != node->transform;
369- (
BOOL)nodeWillCauseScroll:(const
flutter::SemanticsNode*)node {
370 return !isnan([
self node].scrollPosition) && !isnan(node->scrollPosition) &&
371 [
self node].scrollPosition != node->scrollPosition;
378- (
BOOL)nodeShouldTriggerAnnouncement:(const
flutter::SemanticsNode*)node {
390 return [
self node].label != node->label;
393- (void)replaceChildAtIndex:(NSInteger)index withChild:(
SemanticsObject*)child {
397 [_children replaceObjectAtIndex:index withObject:child];
400- (NSString*)routeName {
404 NSString* newName = [
self accessibilityLabel];
405 if (newName != nil && [newName
length] > 0) {
409 if ([
self hasChildren]) {
411 NSString* newName = [child routeName];
412 if (newName != nil && [newName
length] > 0) {
420- (
id)nativeAccessibility {
424#pragma mark - Semantic object private method
430- (NSAttributedString*)createAttributedStringFromString:(NSString*)string
433 NSMutableAttributedString* attributedString =
434 [[[NSMutableAttributedString alloc] initWithString:string] autorelease];
435 for (
const auto& attribute : attributes) {
436 NSRange range = NSMakeRange(attribute->start, attribute->end - attribute->start);
437 switch (attribute->type) {
439 std::shared_ptr<flutter::LocaleStringAttribute> locale_attribute =
440 std::static_pointer_cast<flutter::LocaleStringAttribute>(attribute);
441 NSDictionary* attributeDict = @{
442 UIAccessibilitySpeechAttributeLanguage : @(locale_attribute->locale.data()),
444 [attributedString setAttributes:attributeDict range:range];
448 if (@available(iOS 13.0, *)) {
449 NSDictionary* attributeDict = @{
450 UIAccessibilitySpeechAttributeSpellOut : @YES,
452 [attributedString setAttributes:attributeDict range:range];
458 return attributedString;
461- (void)showOnScreen {
465#pragma mark - UIAccessibility overrides
467- (
BOOL)isAccessibilityElement {
468 if (![
self isAccessibilityBridgeAlive]) {
481 return [
self isFocusable];
495 ![
self node].label.empty() || ![
self node].value.empty() || ![
self node].hint.empty() ||
496 ([
self node].actions &
~flutter::kScrollableSemanticsActions) != 0;
501 [edges addObject:self];
503 if ([
self hasChildren]) {
505 [child collectRoutes:edges];
515 std::vector<uint8_t>
args;
517 args.push_back(action_id);
518 args.push_back(action_id >> 8);
519 args.push_back(action_id >> 16);
520 args.push_back(action_id >> 24);
521 [
self bridge]->DispatchSemanticsAction(
527- (NSString*)accessibilityIdentifier {
528 if (![
self isAccessibilityBridgeAlive]) {
535 return @([
self node].identifier.data());
538- (NSString*)accessibilityLabel {
539 if (![
self isAccessibilityBridgeAlive]) {
542 NSString* label = nil;
543 if (![
self node].label.empty()) {
544 label = @([
self node].label.data());
546 if (![
self node].tooltip.empty()) {
547 label = label ? [NSString stringWithFormat:@"%@\n%@", label, @([
self node].tooltip.data())]
548 : @([
self node].tooltip.data());
553- (bool)containsPoint:(CGPoint)point {
555 return CGRectContainsPoint([
self globalRect], point);
559- (
id)search:(CGPoint)point {
562 if ([child containsPoint:point]) {
563 id childSearchResult = [child search:point];
564 if (childSearchResult != nil) {
565 return childSearchResult;
570 if ([
self containsPoint:point] && [
self isFocusable]) {
571 return self.nativeAccessibility;
583- (
id)_accessibilityHitTest:(CGPoint)point withEvent:(
UIEvent*)event {
584 return [
self search:point];
588- (
BOOL)accessibilityScrollToVisible {
594- (
BOOL)accessibilityScrollToVisibleWithChild:(
id)child {
596 [child showOnScreen];
602- (NSAttributedString*)accessibilityAttributedLabel {
603 NSString* label = [
self accessibilityLabel];
604 if (label.length == 0) {
607 return [
self createAttributedStringFromString:label withAttributes:[
self node].labelAttributes];
610- (NSString*)accessibilityHint {
611 if (![
self isAccessibilityBridgeAlive]) {
615 if ([
self node].hint.empty()) {
618 return @([
self node].hint.data());
621- (NSAttributedString*)accessibilityAttributedHint {
622 NSString* hint = [
self accessibilityHint];
623 if (hint.length == 0) {
626 return [
self createAttributedStringFromString:hint withAttributes:[
self node].hintAttributes];
629- (NSString*)accessibilityValue {
630 if (![
self isAccessibilityBridgeAlive]) {
635 return @([
self node].value.data());
657- (NSAttributedString*)accessibilityAttributedValue {
658 NSString*
value = [
self accessibilityValue];
659 if (
value.length == 0) {
662 return [
self createAttributedStringFromString:value withAttributes:[
self node].valueAttributes];
665- (CGRect)accessibilityFrame {
666 if (![
self isAccessibilityBridgeAlive]) {
667 return CGRectMake(0, 0, 0, 0);
671 return [
super accessibilityFrame];
673 return [
self globalRect];
676- (CGRect)globalRect {
678 CGRect localRect = CGRectMake(
rect.x(),
rect.y(),
rect.width(),
rect.height());
679 return ConvertRectToGlobal(
self, localRect);
682#pragma mark - UIAccessibilityElement protocol
684- (void)setAccessibilityContainer:(
id)container {
689- (
id)accessibilityContainer {
698 if (![
self isAccessibilityBridgeAlive]) {
703 if (_container == nil) {
705 bridge:[
self bridge]]);
707 return _container.get();
709 if ([
self parent] == nil) {
715 return [[
self parent] accessibilityContainer];
718#pragma mark - UIAccessibilityAction overrides
720- (
BOOL)accessibilityActivate {
721 if (![
self isAccessibilityBridgeAlive]) {
731- (void)accessibilityIncrement {
732 if (![
self isAccessibilityBridgeAlive]) {
736 [
self node].value = [
self node].increasedValue;
741- (void)accessibilityDecrement {
742 if (![
self isAccessibilityBridgeAlive]) {
746 [
self node].value = [
self node].decreasedValue;
751- (
BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction {
752 if (![
self isAccessibilityBridgeAlive]) {
759 [
self bridge]->DispatchSemanticsAction([
self uid],
action);
763- (
BOOL)accessibilityPerformEscape {
764 if (![
self isAccessibilityBridgeAlive]) {
774#pragma mark UIAccessibilityFocus overrides
776- (void)accessibilityElementDidBecomeFocused {
777 if (![
self isAccessibilityBridgeAlive]) {
780 [
self bridge]->AccessibilityObjectDidBecomeFocused([
self uid]);
786 [
self bridge]->DispatchSemanticsAction([
self uid],
791- (void)accessibilityElementDidLoseFocus {
792 if (![
self isAccessibilityBridgeAlive]) {
795 [
self bridge]->AccessibilityObjectDidLoseFocus([
self uid]);
797 [
self bridge]->DispatchSemanticsAction([
self uid],
807#pragma mark - Override base class designated initializers
810- (instancetype)
init {
812 [
super doesNotRecognizeSelector:_cmd];
816#pragma mark - Designated initializers
818- (instancetype)initWithBridge:(
fml::
WeakPtr<
flutter::AccessibilityBridgeIos>)bridge
820 self = [
super initWithBridge:bridge uid:uid];
824#pragma mark - UIAccessibility overrides
826- (UIAccessibilityTraits)accessibilityTraits {
827 UIAccessibilityTraits traits = UIAccessibilityTraitNone;
830 traits |= UIAccessibilityTraitAdjustable;
835 traits |= UIAccessibilityTraitButton;
838 traits |= UIAccessibilityTraitSelected;
841 traits |= UIAccessibilityTraitButton;
845 traits |= UIAccessibilityTraitNotEnabled;
848 traits |= UIAccessibilityTraitHeader;
851 traits |= UIAccessibilityTraitImage;
854 traits |= UIAccessibilityTraitUpdatesFrequently;
857 traits |= UIAccessibilityTraitLink;
859 if (traits == UIAccessibilityTraitNone && ![
self hasChildren] &&
862 traits = UIAccessibilityTraitStaticText;
870@property(nonatomic, retain) UIView* platformView;
875- (instancetype)initWithBridge:(
fml::WeakPtr<
flutter::AccessibilityBridgeIos>)bridge
879 _platformView = [platformView retain];
886 [_platformView release];
892 return _platformView;
902#pragma mark - initializers
905- (instancetype)init {
907 [
super doesNotRecognizeSelector:_cmd];
911- (instancetype)initWithSemanticsObject:(
SemanticsObject*)semanticsObject
913 FML_DCHECK(semanticsObject) <<
"semanticsObject must be set";
918 self = [
super initWithAccessibilityContainer:bridge->view()];
921 _semanticsObject = semanticsObject;
928#pragma mark - UIAccessibilityContainer overrides
930- (NSInteger)accessibilityElementCount {
931 NSInteger
count = [[_semanticsObject children] count] + 1;
935- (nullable
id)accessibilityElementAtIndex:(NSInteger)index {
936 if (index < 0 || index >= [
self accessibilityElementCount]) {
940 return _semanticsObject.nativeAccessibility;
945 if ([child hasChildren]) {
951- (NSInteger)indexOfAccessibilityElement:(
id)element {
952 if (element == _semanticsObject.nativeAccessibility) {
956 NSArray<SemanticsObject*>* children = [_semanticsObject children];
957 for (
size_t i = 0; i < [children count]; i++) {
967#pragma mark - UIAccessibilityElement protocol
969- (
BOOL)isAccessibilityElement {
973- (CGRect)accessibilityFrame {
974 return [_semanticsObject accessibilityFrame];
977- (
id)accessibilityContainer {
983 : [[_semanticsObject parent] accessibilityContainer];
986#pragma mark - UIAccessibilityAction overrides
988- (
BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction {
989 return [_semanticsObject accessibilityScroll:direction];
constexpr int32_t kRootNodeId
constexpr float kScrollExtentMaxForInf
NSMutableArray< SemanticsObject * > * _children
NSMutableArray< SemanticsObject * > * _childrenInHitTestOrder
fml::WeakPtr< flutter::AccessibilityBridgeIos > _bridge
void ApplyTransform(SkPath &orig, SkScalar scaleX, SkScalar skewX, SkScalar transX, SkScalar skewY, SkScalar scaleY, SkScalar transY, SkScalar pers0, SkScalar pers1, SkScalar pers2)
void setFlutterAccessibilityContainer:(NSObject *flutterAccessibilityContainer)
virtual UIView * view() const =0
static MallocMapping Copy(const T *begin, const T *end)
static SkString identifier(const FontFamilyDesc &family, const FontDesc &font)
FlutterSemanticsFlag flags
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
#define FML_DCHECK(condition)
NSString * accessibilityValue()
UIAccessibilityTraits accessibilityTraits()
void privateSetParent:(SemanticsObject *parent)
BOOL isAccessibilityBridgeAlive()
id accessibilityContainer()
flutter::SemanticsNode node
NSArray< SemanticsObject * > * children
instancetype initWithBridge:uid:(fml::WeakPtr< flutter::AccessibilityBridgeIos > bridge,[uid] int32_t NS_DESIGNATED_INITIALIZER)
fml::WeakPtr< flutter::AccessibilityBridgeIos > bridge
fml::scoped_nsobject< UIScrollView > _scrollView
sk_sp< SkBlender > blender SkRect rect
auto WeakPtr(std::shared_ptr< T > pointer)
const int kScrollableSemanticsFlags
@ kIsInMutuallyExclusiveGroup
const int kHorizontalScrollSemanticsActions
const int kVerticalScrollSemanticsActions
std::vector< StringAttributePtr > StringAttributes
@ kDidLoseAccessibilityFocus
@ kDidGainAccessibilityFocus
SINT bool isfinite(const Vec< N, T > &v)
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
static constexpr SkPoint Make(float x, float y)
constexpr float y() const
constexpr float x() const