5#import "flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.h"
7#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h"
8#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterSemanticsScrollView.h"
15 UIAccessibilityScrollDirection direction) {
22 case UIAccessibilityScrollDirectionRight:
23 case UIAccessibilityScrollDirectionPrevious:
26 case UIAccessibilityScrollDirectionLeft:
27 case UIAccessibilityScrollDirectionNext:
30 case UIAccessibilityScrollDirectionUp:
32 case UIAccessibilityScrollDirectionDown:
40 SkM44 globalTransform = [reference node].transform;
42 globalTransform = parent.node.transform * globalTransform;
44 return globalTransform;
59 UIScreen* screen = reference.
bridge->
view().window.screen;
61 CGFloat
scale = (screen ?: UIScreen.mainScreen).
scale;
71 SkPoint::Make(local_rect.origin.x + local_rect.size.width, local_rect.origin.y),
73 local_rect.origin.y + local_rect.size.height),
75 local_rect.origin.y + local_rect.size.height)
77 for (
auto& point : quad) {
81 NSCAssert(
rect.setBoundsCheck(quad, 4),
@"Transformed points can't form a rect");
82 rect.setBounds(quad, 4);
87 UIScreen* screen = reference.
bridge->
view().window.screen;
89 CGFloat
scale = (screen ?: UIScreen.mainScreen).
scale;
92 return UIAccessibilityConvertFrameToScreenCoordinates(
result, reference.
bridge->
view());
103- (instancetype)initWithBridge:(
fml::
WeakPtr<
flutter::AccessibilityBridgeIos>)bridge
107 _nativeSwitch = [[UISwitch alloc] init];
112- (NSMethodSignature*)methodSignatureForSelector:(
SEL)sel {
113 NSMethodSignature*
result = [
super methodSignatureForSelector:sel];
115 result = [
self.nativeSwitch methodSignatureForSelector:sel];
120- (void)forwardInvocation:(NSInvocation*)anInvocation {
121 anInvocation.target =
self.nativeSwitch;
122 [anInvocation invoke];
132 return self.nativeSwitch.accessibilityValue;
139 return self.nativeSwitch.accessibilityTraits;
150- (instancetype)initWithBridge:(
fml::
WeakPtr<
flutter::AccessibilityBridgeIos>)bridge
155 [_scrollView setShowsHorizontalScrollIndicator:NO];
156 [_scrollView setShowsVerticalScrollIndicator:NO];
157 [
self.bridge->view() addSubview:_scrollView];
163 [_scrollView removeFromSuperview];
175 self.scrollView.frame =
self.accessibilityFrame;
176 self.scrollView.contentSize = [
self contentSizeInternal];
177 [
self.scrollView setContentOffset:[
self contentOffsetInternal] animated:NO];
181 return self.scrollView;
227 CGPoint origin =
self.scrollView.frame.origin;
236 return CGPointMake(
result.x - origin.x,
result.y - origin.y);
254 NSMutableArray<SemanticsObject*>* _children;
258#pragma mark - Designated initializers
260- (instancetype)initWithBridge:(
fml::
WeakPtr<
flutter::AccessibilityBridgeIos>)bridge
268 self = [
super initWithAccessibilityContainer:bridge->view()];
273 _children = [[NSMutableArray alloc] init];
274 _childrenInHitTestOrder = [[NSArray alloc] init];
290 [_children removeAllObjects];
296#pragma mark - Semantic object property accesser
302 _children = [children mutableCopy];
308- (void)setChildrenInHitTestOrder:(NSArray<
SemanticsObject*>*)childrenInHitTestOrder {
312 _childrenInHitTestOrder = [childrenInHitTestOrder copy];
319 return [
self.children count] != 0;
322#pragma mark - Semantic object method
324- (
BOOL)isAccessibilityBridgeAlive {
325 return self.bridge.get() != nil;
328- (void)setSemanticsNode:(const
flutter::SemanticsNode*)node {
332- (void)accessibilityBridgeDidFinishUpdate {
338- (
BOOL)nodeWillCauseLayoutChange:(const
flutter::SemanticsNode*)node {
339 return self.node.rect != node->rect ||
self.node.transform != node->transform;
345- (
BOOL)nodeWillCauseScroll:(const
flutter::SemanticsNode*)node {
346 return !isnan(
self.node.scrollPosition) && !isnan(node->scrollPosition) &&
347 self.node.scrollPosition != node->scrollPosition;
354- (
BOOL)nodeShouldTriggerAnnouncement:(const
flutter::SemanticsNode*)node {
366 return self.node.label != node->label;
369- (void)replaceChildAtIndex:(NSInteger)index withChild:(
SemanticsObject*)child {
373 [_children replaceObjectAtIndex:index withObject:child];
376- (NSString*)routeName {
380 NSString* newName =
self.accessibilityLabel;
381 if (newName != nil && [newName
length] > 0) {
385 if ([
self hasChildren]) {
387 NSString* newName = [child routeName];
388 if (newName != nil && [newName
length] > 0) {
396- (
id)nativeAccessibility {
400- (NSAttributedString*)createAttributedStringFromString:(NSString*)string
403 NSMutableAttributedString* attributedString =
404 [[NSMutableAttributedString alloc] initWithString:string];
405 for (
const auto& attribute : attributes) {
406 NSRange range = NSMakeRange(attribute->start, attribute->end - attribute->start);
407 switch (attribute->type) {
409 std::shared_ptr<flutter::LocaleStringAttribute> locale_attribute =
410 std::static_pointer_cast<flutter::LocaleStringAttribute>(attribute);
411 NSDictionary* attributeDict = @{
412 UIAccessibilitySpeechAttributeLanguage : @(locale_attribute->locale.data()),
414 [attributedString setAttributes:attributeDict range:range];
418 if (@available(iOS 13.0, *)) {
419 NSDictionary* attributeDict = @{
420 UIAccessibilitySpeechAttributeSpellOut : @YES,
422 [attributedString setAttributes:attributeDict range:range];
428 return attributedString;
431- (void)showOnScreen {
435#pragma mark - UIAccessibility overrides
437- (
BOOL)isAccessibilityElement {
438 if (![
self isAccessibilityBridgeAlive]) {
451 return [
self isFocusable];
465 !
self.node.label.empty() || !
self.node.value.empty() || !
self.node.hint.empty() ||
471 [edges addObject:self];
473 if ([
self hasChildren]) {
475 [child collectRoutes:edges];
484 int32_t action_id =
action.uid;
485 std::vector<uint8_t>
args;
487 args.push_back(action_id);
488 args.push_back(action_id >> 8);
489 args.push_back(action_id >> 16);
490 args.push_back(action_id >> 24);
491 self.bridge->DispatchSemanticsAction(
497- (NSString*)accessibilityIdentifier {
498 if (![
self isAccessibilityBridgeAlive]) {
502 if (
self.node.identifier.empty()) {
505 return @(
self.node.identifier.data());
508- (NSString*)accessibilityLabel {
509 if (![
self isAccessibilityBridgeAlive]) {
512 NSString* label = nil;
513 if (!
self.node.label.empty()) {
514 label = @(
self.node.label.data());
516 if (!
self.node.tooltip.empty()) {
517 label = label ? [NSString stringWithFormat:@"%@\n%@", label, @(self.node.tooltip.data())]
518 : @(
self.node.tooltip.data());
523- (bool)containsPoint:(CGPoint)point {
525 return CGRectContainsPoint([
self globalRect], point);
529- (
id)search:(CGPoint)point {
532 if ([child containsPoint:point]) {
533 id childSearchResult = [child search:point];
534 if (childSearchResult != nil) {
535 return childSearchResult;
540 if ([
self containsPoint:point] && [
self isFocusable]) {
541 return self.nativeAccessibility;
553- (
id)_accessibilityHitTest:(CGPoint)point withEvent:(
UIEvent*)event {
554 return [
self search:point];
558- (
BOOL)accessibilityScrollToVisible {
564- (
BOOL)accessibilityScrollToVisibleWithChild:(
id)child {
566 [child showOnScreen];
572- (NSAttributedString*)accessibilityAttributedLabel {
573 NSString* label =
self.accessibilityLabel;
574 if (label.length == 0) {
577 return [
self createAttributedStringFromString:label withAttributes:self.node.labelAttributes];
580- (NSString*)accessibilityHint {
581 if (![
self isAccessibilityBridgeAlive]) {
585 if (
self.node.hint.empty()) {
588 return @(
self.node.hint.data());
591- (NSAttributedString*)accessibilityAttributedHint {
592 NSString* hint = [
self accessibilityHint];
593 if (hint.length == 0) {
596 return [
self createAttributedStringFromString:hint withAttributes:self.node.hintAttributes];
599- (NSString*)accessibilityValue {
600 if (![
self isAccessibilityBridgeAlive]) {
604 if (!
self.node.value.empty()) {
605 return @(
self.node.value.data());
627- (NSAttributedString*)accessibilityAttributedValue {
628 NSString*
value = [
self accessibilityValue];
629 if (
value.length == 0) {
632 return [
self createAttributedStringFromString:value withAttributes:self.node.valueAttributes];
635- (CGRect)accessibilityFrame {
636 if (![
self isAccessibilityBridgeAlive]) {
637 return CGRectMake(0, 0, 0, 0);
641 return [
super accessibilityFrame];
643 return [
self globalRect];
646- (CGRect)globalRect {
648 CGRect localRect = CGRectMake(
rect.x(),
rect.y(),
rect.width(),
rect.height());
652#pragma mark - UIAccessibilityElement protocol
654- (void)setAccessibilityContainer:(
id)container {
659- (
id)accessibilityContainer {
668 if (![
self isAccessibilityBridgeAlive]) {
673 if (
self.container == nil) {
677 return self.container;
679 if (
self.parent == nil) {
685 return self.parent.accessibilityContainer;
688#pragma mark - UIAccessibilityAction overrides
690- (
BOOL)accessibilityActivate {
691 if (![
self isAccessibilityBridgeAlive]) {
701- (void)accessibilityIncrement {
702 if (![
self isAccessibilityBridgeAlive]) {
706 self.node.value =
self.node.increasedValue;
711- (void)accessibilityDecrement {
712 if (![
self isAccessibilityBridgeAlive]) {
716 self.node.value =
self.node.decreasedValue;
721- (
BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction {
722 if (![
self isAccessibilityBridgeAlive]) {
733- (
BOOL)accessibilityPerformEscape {
734 if (![
self isAccessibilityBridgeAlive]) {
744#pragma mark UIAccessibilityFocus overrides
746- (void)accessibilityElementDidBecomeFocused {
747 if (![
self isAccessibilityBridgeAlive]) {
750 self.bridge->AccessibilityObjectDidBecomeFocused(
self.uid);
756 self.bridge->DispatchSemanticsAction(
self.uid,
761- (void)accessibilityElementDidLoseFocus {
762 if (![
self isAccessibilityBridgeAlive]) {
765 self.bridge->AccessibilityObjectDidLoseFocus(
self.uid);
767 self.bridge->DispatchSemanticsAction(
self.uid,
776#pragma mark - Designated initializers
778- (instancetype)initWithBridge:(
fml::
WeakPtr<
flutter::AccessibilityBridgeIos>)bridge
780 self = [
super initWithBridge:bridge uid:uid];
784#pragma mark - UIAccessibility overrides
787 UIAccessibilityTraits traits = UIAccessibilityTraitNone;
790 traits |= UIAccessibilityTraitAdjustable;
795 traits |= UIAccessibilityTraitButton;
798 traits |= UIAccessibilityTraitSelected;
801 traits |= UIAccessibilityTraitButton;
805 traits |= UIAccessibilityTraitNotEnabled;
808 traits |= UIAccessibilityTraitHeader;
811 traits |= UIAccessibilityTraitImage;
814 traits |= UIAccessibilityTraitUpdatesFrequently;
817 traits |= UIAccessibilityTraitLink;
820 self.accessibilityLabel.length != 0 &&
822 traits = UIAccessibilityTraitStaticText;
835- (instancetype)initWithBridge:(
fml::
WeakPtr<
flutter::AccessibilityBridgeIos>)bridge
840 [platformView setFlutterAccessibilityContainer:self];
846 return self.platformView;
855#pragma mark - initializers
857- (instancetype)initWithSemanticsObject:(
SemanticsObject*)semanticsObject
859 FML_DCHECK(semanticsObject) <<
"semanticsObject must be set";
864 self = [
super initWithAccessibilityContainer:bridge->view()];
867 _semanticsObject = semanticsObject;
874#pragma mark - UIAccessibilityContainer overrides
876- (NSInteger)accessibilityElementCount {
877 return self.semanticsObject.children.count + 1;
880- (nullable
id)accessibilityElementAtIndex:(NSInteger)index {
881 if (index < 0 || index >= [
self accessibilityElementCount]) {
885 return self.semanticsObject.nativeAccessibility;
890 if ([child hasChildren]) {
896- (NSInteger)indexOfAccessibilityElement:(
id)element {
897 if (element ==
self.semanticsObject.nativeAccessibility) {
901 NSArray<SemanticsObject*>* children =
self.semanticsObject.children;
902 for (
size_t i = 0;
i < [children count];
i++) {
912#pragma mark - UIAccessibilityElement protocol
914- (
BOOL)isAccessibilityElement {
918- (CGRect)accessibilityFrame {
919 return self.semanticsObject.accessibilityFrame;
922- (
id)accessibilityContainer {
928 :
self.semanticsObject.parent.accessibilityContainer;
931#pragma mark - UIAccessibilityAction overrides
933- (
BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction {
934 return [
self.semanticsObject accessibilityScroll:direction];
constexpr int32_t kRootNodeId
constexpr float kScrollExtentMaxForInf
virtual UIView * view() const =0
static MallocMapping Copy(const T *begin, const T *end)
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
#define FML_DCHECK(condition)
UIAccessibilityTraits accessibilityTraits()
NSString * accessibilityValue()
UIAccessibilityTraits accessibilityTraits()
BOOL isAccessibilityBridgeAlive()
id accessibilityContainer()
instancetype initWithBridge:uid:(fml::WeakPtr< flutter::AccessibilityBridgeIos > bridge,[uid] int32_t NS_DESIGNATED_INITIALIZER)
fml::WeakPtr< flutter::AccessibilityBridgeIos > bridge
SemanticsObjectContainer * container
fml::scoped_nsobject< UIScrollView > _scrollView
std::shared_ptr< flutter::AccessibilityBridgeMac > _bridge
CGRect ConvertRectToGlobal(SemanticsObject *reference, CGRect local_rect)
flutter::SemanticsAction GetSemanticsActionForScrollDirection(UIAccessibilityScrollDirection direction)
CGPoint ConvertPointToGlobal(SemanticsObject *reference, CGPoint local_point)
SkM44 GetGlobalTransform(SemanticsObject *reference)
SkPoint ApplyTransform(SkPoint &point, const SkM44 &transform)
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
const int kScrollableSemanticsActions
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