33// found in the LICENSE file.
44
55#import " SemanticsObject.h"
6- #include " flutter/lib/ui/semantics/semantics_node.h"
7- #import " flutter/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h"
86#import " flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h"
9- #import " flutter/shell/platform/darwin/ios/framework/Source/FlutterSemanticsScrollView.h"
107
118FLUTTER_ASSERT_ARC
129
3027// translated to calls such as -[NSObject accessibilityActivate]), while most
3128// other key events are dispatched to the framework.
3229@interface SemanticsObject (UIFocusSystem) <UIFocusItem, UIFocusItemContainer>
33- // / The `UIFocusItem` that represents this SemanticsObject.
34- // /
35- // / For regular `SemanticsObject`s, this method returns `self`,
36- // / for `FlutterScrollableSemanticsObject`s, this method returns its scroll view.
37- - (id <UIFocusItem>)focusItem ;
3830@end
3931
4032@implementation SemanticsObject (UIFocusSystem)
4133
42- - (id <UIFocusItem>)focusItem {
43- return self;
44- }
45-
4634#pragma mark - UIFocusEnvironment Conformance
4735
4836- (void )setNeedsFocusUpdate {
@@ -61,7 +49,7 @@ - (void)didUpdateFocusInContext:(UIFocusUpdateContext*)context
6149
6250- (id <UIFocusEnvironment>)parentFocusEnvironment {
6351 // The root SemanticsObject node's parent is the FlutterView.
64- return self.parent . focusItem ?: self.bridge ->view ();
52+ return self.parent ?: self.bridge ->view ();
6553}
6654
6755- (NSArray <id<UIFocusEnvironment>>*)preferredFocusEnvironments {
@@ -83,57 +71,8 @@ - (BOOL)canBecomeFocused {
8371 return self.node .HasAction (flutter::SemanticsAction::kTap );
8472}
8573
86- // The frame is described in the `coordinateSpace` of the
87- // `parentFocusEnvironment` (all `parentFocusEnvironment`s are `UIFocusItem`s).
88- //
89- // See also the `coordinateSpace` implementation.
90- // TODO(LongCatIsLooong): use CoreGraphics types.
9174- (CGRect)frame {
92- SkPoint quad[4 ] = {SkPoint::Make (self.node .rect .left (), self.node .rect .top ()),
93- SkPoint::Make (self.node .rect .left (), self.node .rect .bottom ()),
94- SkPoint::Make (self.node .rect .right (), self.node .rect .top ()),
95- SkPoint::Make (self.node .rect .right (), self.node .rect .bottom ())};
96-
97- SkM44 transform = self.node .transform ;
98- FlutterSemanticsScrollView* scrollView;
99- for (SemanticsObject* ancestor = self.parent ; ancestor; ancestor = ancestor.parent ) {
100- if ([ancestor isKindOfClass: [FlutterScrollableSemanticsObject class ]]) {
101- scrollView = ((FlutterScrollableSemanticsObject*)ancestor).scrollView ;
102- break ;
103- }
104- transform = ancestor.node .transform * transform;
105- }
106-
107- for (auto & vertex : quad) {
108- SkV4 vector = transform.map (vertex.x (), vertex.y (), 0 , 1 );
109- vertex = SkPoint::Make (vector.x / vector.w , vector.y / vector.w );
110- }
111-
112- SkRect rect;
113- rect.setBounds (quad, 4 );
114- // If this UIFocusItemContainer's coordinateSpace is a UIScrollView, offset
115- // the rect by `contentOffset` because the contentOffset translation is
116- // incorporated into the paint transform at different node depth in UIKit
117- // and Flutter. In Flutter, the translation is added to the cells
118- // while in UIKit the viewport's bounds is manipulated (IOW, each cell's frame
119- // in the UIScrollView coordinateSpace does not change when the UIScrollView
120- // scrolls).
121- CGRect unscaledRect =
122- CGRectMake (rect.x () + scrollView.bounds .origin .x , rect.y () + scrollView.bounds .origin .y ,
123- rect.width (), rect.height ());
124- if (scrollView) {
125- return unscaledRect;
126- }
127- // `rect` could be in physical pixels since the root RenderObject ("RenderView")
128- // applies a transform that turns logical pixels to physical pixels. Undo the
129- // transform by dividing the coordinates by the screen's scale factor, if this
130- // UIFocusItem's reported `coordinateSpace` is the root view (which means this
131- // UIFocusItem is not inside of a scroll view).
132- //
133- // Screen can be nil if the FlutterView is covered by another native view.
134- CGFloat scale = (self.bridge ->view ().window .screen ?: UIScreen.mainScreen ).scale ;
135- return CGRectMake (unscaledRect.origin .x / scale, unscaledRect.origin .y / scale,
136- unscaledRect.size .width / scale, unscaledRect.size .height / scale);
75+ return self.accessibilityFrame ;
13776}
13877
13978#pragma mark - UIFocusItemContainer Conformance
@@ -148,94 +87,16 @@ - (CGRect)frame {
14887 //
14988 // This method is only supposed to return items within the given
15089 // rect but returning everything in the subtree seems to work fine.
151- NSMutableArray <id <UIFocusItem> >* reversedItems =
90+ NSMutableArray <SemanticsObject* >* reversedItems =
15291 [[NSMutableArray alloc ] initWithCapacity: self .childrenInHitTestOrder.count];
15392 for (NSUInteger i = 0 ; i < self.childrenInHitTestOrder .count ; ++i) {
154- SemanticsObject* child = self. childrenInHitTestOrder [ self .childrenInHitTestOrder.count - 1 - i];
155- [reversedItems addObject: child.focusItem ];
93+ [reversedItems
94+ addObject: self .childrenInHitTestOrder[ self .childrenInHitTestOrder.count - 1 - i] ];
15695 }
15796 return reversedItems;
15897}
15998
16099- (id <UICoordinateSpace>)coordinateSpace {
161- // A regular SemanticsObject uses the same coordinate space as its parent.
162- return self.parent .coordinateSpace ?: self.bridge ->view ();
163- }
164-
165- @end
166-
167- // / Scrollable containers interact with the iOS focus engine using the
168- // / `UIFocusItemScrollableContainer` protocol. The said protocol (and other focus-related protocols)
169- // / does not provide means to inform the focus system of layout changes. In order for the focus
170- // / highlight to update properly as the scroll view scrolls, this implementation incorporates a
171- // / UIScrollView into the focus hierarchy to workaround the highlight update problem.
172- // /
173- // / As a result, in the current implementation only scrollable containers and the root node
174- // / establish their own `coordinateSpace`s. All other `UIFocusItemContainter`s use the same
175- // / `coordinateSpace` as the containing UIScrollView, or the root `FlutterView`, whichever is
176- // / closer.
177- // /
178- // / See also the `frame` method implementation.
179- #pragma mark - Scrolling
180-
181- @interface FlutterScrollableSemanticsObject (CoordinateSpace)
182- @end
183-
184- @implementation FlutterScrollableSemanticsObject (CoordinateSpace)
185- - (id <UICoordinateSpace>)coordinateSpace {
186- // A scrollable SemanticsObject uses the same coordinate space as the scroll view.
187- // This may not work very well in nested scroll views.
188- return self.scrollView ;
189- }
190-
191- - (id <UIFocusItem>)focusItem {
192- return self.scrollView ;
193- }
194-
195- @end
196-
197- @interface FlutterSemanticsScrollView (UIFocusItemScrollableContainer) <
198- UIFocusItemScrollableContainer>
199- @end
200-
201- @implementation FlutterSemanticsScrollView (UIFocusItemScrollableContainer)
202-
203- #pragma mark - FlutterSemanticsScrollView UIFocusItemScrollableContainer Conformance
204-
205- - (CGSize)visibleSize {
206- return self.frame .size ;
207- }
208-
209- - (void )setContentOffset : (CGPoint)contentOffset {
210- [super setContentOffset: contentOffset];
211- // Do no send flutter::SemanticsAction::kScrollToOffset if it's triggered
212- // by a framework update.
213- if (![self .semanticsObject isAccessibilityBridgeAlive ] || !self.isDoingSystemScrolling ) {
214- return ;
215- }
216-
217- double offset[2 ] = {contentOffset.x , contentOffset.y };
218- FlutterStandardTypedData* offsetData = [FlutterStandardTypedData
219- typedDataWithFloat64: [NSData dataWithBytes: &offset length: sizeof (offset)]];
220- NSData * encoded = [[FlutterStandardMessageCodec sharedInstance ] encode: offsetData];
221- self.semanticsObject .bridge ->DispatchSemanticsAction (
222- self.semanticsObject .uid , flutter::SemanticsAction::kScrollToOffset ,
223- fml::MallocMapping::Copy (encoded.bytes , encoded.length ));
224- }
225-
226- - (BOOL )canBecomeFocused {
227- return NO ;
228- }
229-
230- - (id <UIFocusEnvironment>)parentFocusEnvironment {
231- return self.semanticsObject .parentFocusEnvironment ;
232- }
233-
234- - (NSArray <id<UIFocusEnvironment>>*)preferredFocusEnvironments {
235- return nil ;
236- }
237-
238- - (NSArray <id<UIFocusItem>>*)focusItemsInRect : (CGRect)rect {
239- return [self .semanticsObject focusItemsInRect: rect];
100+ return self.bridge ->view ();
240101}
241102@end
0 commit comments