Revert "Issues/80711 reland (#26813)" (flutter/engine#26859)

This reverts commit d03313b7b3f97d2083e3977d9e62974687760a33.
This commit is contained in:
chunhtai 2021-06-21 10:21:04 -07:00 committed by GitHub
parent 616fd2a662
commit 2a526b317d
5 changed files with 28 additions and 394 deletions

View File

@ -44,17 +44,12 @@ enum class SemanticsAction : int32_t {
kSetText = 1 << 21,
};
const int kVerticalScrollSemanticsActions =
const int kScrollableSemanticsActions =
static_cast<int32_t>(SemanticsAction::kScrollLeft) |
static_cast<int32_t>(SemanticsAction::kScrollRight) |
static_cast<int32_t>(SemanticsAction::kScrollUp) |
static_cast<int32_t>(SemanticsAction::kScrollDown);
const int kHorizontalScrollSemanticsActions =
static_cast<int32_t>(SemanticsAction::kScrollLeft) |
static_cast<int32_t>(SemanticsAction::kScrollRight);
const int kScrollableSemanticsActions =
kVerticalScrollSemanticsActions | kHorizontalScrollSemanticsActions;
/// C/C++ representation of `SemanticsFlags` defined in
/// `lib/ui/semantics.dart`.
///\warning This must match the `SemanticsFlags` enum in

View File

@ -13,8 +13,6 @@
#import "flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge_ios.h"
constexpr int32_t kRootNodeId = 0;
// This can be arbitrary number as long as it is bigger than 0.
constexpr float kScrollExtentMaxForInf = 1000;
@class FlutterCustomAccessibilityAction;
@class FlutterPlatformViewSemanticsContainer;
@ -33,7 +31,7 @@ constexpr float kScrollExtentMaxForInf = 1000;
* The parent of this node in the node tree. Will be nil for the root node and
* during transient state changes.
*/
@property(nonatomic, assign) SemanticsObject* parent;
@property(nonatomic, readonly) SemanticsObject* parent;
/**
* The accessibility bridge that this semantics object is attached to. This
@ -96,14 +94,6 @@ constexpr float kScrollExtentMaxForInf = 1000;
- (BOOL)onCustomAccessibilityAction:(FlutterCustomAccessibilityAction*)action;
/**
* Called after accessibility bridge finishes a semantics update.
*
* Subclasses can override this method if they contain states that can only be
* updated once every node in the accessibility tree has finished updating.
*/
- (void)accessibilityBridgeDidFinishUpdate;
#pragma mark - Designated initializers
- (instancetype)init __attribute__((unavailable("Use initWithBridge instead")));
@ -169,18 +159,6 @@ constexpr float kScrollExtentMaxForInf = 1000;
@end
/// The semantics object for scrollable. This class creates an UIScrollView to interact with the
/// iOS.
@interface FlutterScrollableSemanticsObject : UIScrollView
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
- (instancetype)initWithCoder:(NSCoder*)coder NS_UNAVAILABLE;
- (instancetype)initWithSemanticsObject:(SemanticsObject*)semanticsObject NS_DESIGNATED_INITIALIZER;
- (void)accessibilityBridgeDidFinishUpdate;
@end
/**
* Represents a semantics object that has children and hence has to be presented to the OS as a
* UIAccessibilityContainer.

View File

@ -34,58 +34,6 @@ flutter::SemanticsAction GetSemanticsActionForScrollDirection(
return flutter::SemanticsAction::kScrollUp;
}
SkM44 GetGlobalTransform(SemanticsObject* reference) {
SkM44 globalTransform = [reference node].transform;
for (SemanticsObject* parent = [reference parent]; parent; parent = parent.parent) {
globalTransform = parent.node.transform * globalTransform;
}
return globalTransform;
}
SkPoint ApplyTransform(SkPoint& point, const SkM44& transform) {
SkV4 vector = transform.map(point.x(), point.y(), 0, 1);
return SkPoint::Make(vector.x / vector.w, vector.y / vector.w);
}
CGPoint ConvertPointToGlobal(SemanticsObject* reference, CGPoint local_point) {
SkM44 globalTransform = GetGlobalTransform(reference);
SkPoint point = SkPoint::Make(local_point.x, local_point.y);
point = ApplyTransform(point, globalTransform);
// `rect` is in the physical pixel coordinate system. iOS expects the accessibility frame in
// the logical pixel coordinate system. Therefore, we divide by the `scale` (pixel ratio) to
// convert.
CGFloat scale = [[[reference bridge]->view() window] screen].scale;
auto result = CGPointMake(point.x() / scale, point.y() / scale);
return [[reference bridge]->view() convertPoint:result toView:nil];
}
CGRect ConvertRectToGlobal(SemanticsObject* reference, CGRect local_rect) {
SkM44 globalTransform = GetGlobalTransform(reference);
SkPoint quad[4] = {
SkPoint::Make(local_rect.origin.x, local_rect.origin.y), // top left
SkPoint::Make(local_rect.origin.x + local_rect.size.width, local_rect.origin.y), // top right
SkPoint::Make(local_rect.origin.x + local_rect.size.width,
local_rect.origin.y + local_rect.size.height), // bottom right
SkPoint::Make(local_rect.origin.x,
local_rect.origin.y + local_rect.size.height) // bottom left
};
for (auto& point : quad) {
point = ApplyTransform(point, globalTransform);
}
SkRect rect;
NSCAssert(rect.setBoundsCheck(quad, 4), @"Transformed points can't form a rect");
rect.setBounds(quad, 4);
// `rect` is in the physical pixel coordinate system. iOS expects the accessibility frame in
// the logical pixel coordinate system. Therefore, we divide by the `scale` (pixel ratio) to
// convert.
CGFloat scale = [[[reference bridge]->view() window] screen].scale;
auto result =
CGRectMake(rect.x() / scale, rect.y() / scale, rect.width() / scale, rect.height() / scale);
return UIAccessibilityConvertFrameToScreenCoordinates(result, [reference bridge]->view());
}
} // namespace
@implementation FlutterSwitchSemanticsObject {
@ -140,152 +88,6 @@ CGRect ConvertRectToGlobal(SemanticsObject* reference, CGRect local_rect) {
@end // FlutterSwitchSemanticsObject
@interface FlutterScrollableSemanticsObject ()
@property(nonatomic, strong) SemanticsObject* semanticsObject;
@end
@implementation FlutterScrollableSemanticsObject {
fml::scoped_nsobject<SemanticsObjectContainer> _container;
}
- (instancetype)initWithSemanticsObject:(SemanticsObject*)semanticsObject {
self = [super initWithFrame:CGRectZero];
if (self) {
_semanticsObject = [semanticsObject retain];
[semanticsObject.bridge->view() addSubview:self];
}
return self;
}
- (void)dealloc {
_container.get().semanticsObject = nil;
[_semanticsObject release];
[self removeFromSuperview];
[super dealloc];
}
- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event {
return nil;
}
- (NSMethodSignature*)methodSignatureForSelector:(SEL)sel {
NSMethodSignature* result = [super methodSignatureForSelector:sel];
if (!result) {
result = [_semanticsObject methodSignatureForSelector:sel];
}
return result;
}
- (void)forwardInvocation:(NSInvocation*)anInvocation {
[anInvocation setTarget:_semanticsObject];
[anInvocation invoke];
}
- (void)accessibilityBridgeDidFinishUpdate {
// In order to make iOS think this UIScrollView is scrollable, the following
// requirements must be true.
// 1. contentSize must be bigger than the frame size.
// 2. The scrollable isAccessibilityElement must return YES
//
// Once the requirements are met, the iOS uses contentOffset to determine
// what scroll actions are available. e.g. If the view scrolls vertically and
// contentOffset is 0.0, only the scroll down action is available.
[self setFrame:[_semanticsObject accessibilityFrame]];
[self setContentSize:[self contentSizeInternal]];
[self setContentOffset:[self contentOffsetInternal] animated:NO];
if (self.contentSize.width > self.frame.size.width ||
self.contentSize.height > self.frame.size.height) {
self.isAccessibilityElement = YES;
} else {
self.isAccessibilityElement = NO;
}
}
- (void)setChildren:(NSArray<SemanticsObject*>*)children {
[_semanticsObject setChildren:children];
// The children's parent is pointing to _semanticsObject, need to manually
// set it this object.
for (SemanticsObject* child in _semanticsObject.children) {
child.parent = (SemanticsObject*)self;
}
}
- (id)accessibilityContainer {
if (_container == nil) {
_container.reset([[SemanticsObjectContainer alloc]
initWithSemanticsObject:(SemanticsObject*)self
bridge:[_semanticsObject bridge]]);
}
return _container.get();
}
// private methods
- (CGSize)contentSizeInternal {
CGRect result;
const SkRect& rect = _semanticsObject.node.rect;
float scrollExtentMax = isfinite(_semanticsObject.node.scrollExtentMax)
? _semanticsObject.node.scrollExtentMax
: kScrollExtentMaxForInf + _semanticsObject.node.scrollPosition;
if (_semanticsObject.node.actions & flutter::kVerticalScrollSemanticsActions) {
result = CGRectMake(rect.x(), rect.y(), rect.width(), rect.height() + scrollExtentMax);
} else if (_semanticsObject.node.actions & flutter::kHorizontalScrollSemanticsActions) {
result = CGRectMake(rect.x(), rect.y(), rect.width() + scrollExtentMax, rect.height());
} else {
result = CGRectMake(rect.x(), rect.y(), rect.width(), rect.height());
}
return ConvertRectToGlobal(_semanticsObject, result).size;
}
- (CGPoint)contentOffsetInternal {
CGPoint result;
CGPoint origin = self.frame.origin;
const SkRect& rect = _semanticsObject.node.rect;
if (_semanticsObject.node.actions & flutter::kVerticalScrollSemanticsActions) {
result = ConvertPointToGlobal(
_semanticsObject, CGPointMake(rect.x(), rect.y() + _semanticsObject.node.scrollPosition));
} else if (_semanticsObject.node.actions & flutter::kHorizontalScrollSemanticsActions) {
result = ConvertPointToGlobal(
_semanticsObject, CGPointMake(rect.x() + _semanticsObject.node.scrollPosition, rect.y()));
} else {
result = origin;
}
return CGPointMake(result.x - origin.x, result.y - origin.y);
}
// The following methods are explicitly forwarded to the wrapped SemanticsObject because the
// forwarding logic above doesn't apply to them since they are also implemented in the
// UIScrollView class, the base class.
- (BOOL)accessibilityActivate {
return [_semanticsObject accessibilityActivate];
}
- (void)accessibilityIncrement {
[_semanticsObject accessibilityIncrement];
}
- (void)accessibilityDecrement {
[_semanticsObject accessibilityDecrement];
}
- (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction {
return [_semanticsObject accessibilityScroll:direction];
}
- (BOOL)accessibilityPerformEscape {
return [_semanticsObject accessibilityPerformEscape];
}
- (void)accessibilityElementDidBecomeFocused {
[_semanticsObject accessibilityElementDidBecomeFocused];
}
- (void)accessibilityElementDidLoseFocus {
[_semanticsObject accessibilityElementDidLoseFocus];
}
@end // FlutterScrollableSemanticsObject
@implementation FlutterCustomAccessibilityAction {
}
@end
@ -372,9 +174,6 @@ CGRect ConvertRectToGlobal(SemanticsObject* reference, CGRect local_rect) {
_node = *node;
}
- (void)accessibilityBridgeDidFinishUpdate { /* Do nothing by default */
}
/**
* Whether calling `setSemanticsNode:` with `node` would cause a layout change.
*/
@ -599,9 +398,27 @@ CGRect ConvertRectToGlobal(SemanticsObject* reference, CGRect local_rect) {
}
- (CGRect)globalRect {
const SkRect& rect = [self node].rect;
CGRect localRect = CGRectMake(rect.x(), rect.y(), rect.width(), rect.height());
return ConvertRectToGlobal(self, localRect);
SkM44 globalTransform = [self node].transform;
for (SemanticsObject* parent = [self parent]; parent; parent = parent.parent) {
globalTransform = parent.node.transform * globalTransform;
}
SkPoint quad[4];
[self node].rect.toQuad(quad);
for (auto& point : quad) {
SkV4 vector = globalTransform.map(point.x(), point.y(), 0, 1);
point.set(vector.x / vector.w, vector.y / vector.w);
}
SkRect rect;
rect.setBounds(quad, 4);
// `rect` is in the physical pixel coordinate system. iOS expects the accessibility frame in
// the logical pixel coordinate system. Therefore, we divide by the `scale` (pixel ratio) to
// convert.
CGFloat scale = [[[self bridge]->view() window] screen].scale;
auto result =
CGRectMake(rect.x() / scale, rect.y() / scale, rect.width() / scale, rect.height() / scale);
return UIAccessibilityConvertFrameToScreenCoordinates(result, [self bridge]->view());
}
#pragma mark - UIAccessibilityElement protocol

View File

@ -10,8 +10,6 @@
FLUTTER_ASSERT_ARC
const CGRect kScreenSize = CGRectMake(0, 0, 600, 800);
namespace flutter {
namespace {
@ -26,11 +24,7 @@ class SemanticsActionObservation {
class MockAccessibilityBridge : public AccessibilityBridgeIos {
public:
MockAccessibilityBridge() : observations({}) {
view_ = [[UIView alloc] initWithFrame:kScreenSize];
window_ = [[UIWindow alloc] initWithFrame:kScreenSize];
[window_ addSubview:view_];
}
MockAccessibilityBridge() : observations({}) { view_ = [[UIView alloc] init]; }
UIView* view() const override { return view_; }
UIView<UITextInput>* textInputView() override { return nil; }
void DispatchSemanticsAction(int32_t id, SemanticsAction action) override {
@ -52,7 +46,6 @@ class MockAccessibilityBridge : public AccessibilityBridgeIos {
private:
UIView* view_;
UIWindow* window_;
};
} // namespace
} // namespace flutter
@ -167,145 +160,6 @@ class MockAccessibilityBridge : public AccessibilityBridgeIos {
XCTAssertEqual([object accessibilityTraits], UIAccessibilityTraitButton);
}
- (void)testVerticalFlutterScrollableSemanticsObject {
fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
new flutter::MockAccessibilityBridge());
fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
float transformScale = 0.5f;
float screenScale = [[bridge->view() window] screen].scale;
float effectivelyScale = transformScale / screenScale;
float x = 10;
float y = 10;
float w = 100;
float h = 200;
float scrollExtentMax = 500.0;
float scrollPosition = 150.0;
flutter::SemanticsNode node;
node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
node.actions = flutter::kVerticalScrollSemanticsActions;
node.rect = SkRect::MakeXYWH(x, y, w, h);
node.scrollExtentMax = scrollExtentMax;
node.scrollPosition = scrollPosition;
node.transform = {
transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, 1.0};
FlutterSemanticsObject* delegate = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
FlutterScrollableSemanticsObject* scrollable =
[[FlutterScrollableSemanticsObject alloc] initWithSemanticsObject:delegate];
SemanticsObject* scrollable_object = static_cast<SemanticsObject*>(scrollable);
[scrollable_object setSemanticsNode:&node];
[scrollable_object accessibilityBridgeDidFinishUpdate];
XCTAssertTrue(
CGRectEqualToRect(scrollable.frame, CGRectMake(x * effectivelyScale, y * effectivelyScale,
w * effectivelyScale, h * effectivelyScale)));
XCTAssertTrue(CGSizeEqualToSize(
scrollable.contentSize,
CGSizeMake(w * effectivelyScale, (h + scrollExtentMax) * effectivelyScale)));
XCTAssertTrue(CGPointEqualToPoint(scrollable.contentOffset,
CGPointMake(0, scrollPosition * effectivelyScale)));
}
- (void)testHorizontalFlutterScrollableSemanticsObject {
fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
new flutter::MockAccessibilityBridge());
fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
float transformScale = 0.5f;
float screenScale = [[bridge->view() window] screen].scale;
float effectivelyScale = transformScale / screenScale;
float x = 10;
float y = 10;
float w = 100;
float h = 200;
float scrollExtentMax = 500.0;
float scrollPosition = 150.0;
flutter::SemanticsNode node;
node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
node.actions = flutter::kHorizontalScrollSemanticsActions;
node.rect = SkRect::MakeXYWH(x, y, w, h);
node.scrollExtentMax = scrollExtentMax;
node.scrollPosition = scrollPosition;
node.transform = {
transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, 1.0};
FlutterSemanticsObject* delegate = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
FlutterScrollableSemanticsObject* scrollable =
[[FlutterScrollableSemanticsObject alloc] initWithSemanticsObject:delegate];
SemanticsObject* scrollable_object = static_cast<SemanticsObject*>(scrollable);
[scrollable_object setSemanticsNode:&node];
[scrollable_object accessibilityBridgeDidFinishUpdate];
XCTAssertTrue(
CGRectEqualToRect(scrollable.frame, CGRectMake(x * effectivelyScale, y * effectivelyScale,
w * effectivelyScale, h * effectivelyScale)));
XCTAssertTrue(CGSizeEqualToSize(
scrollable.contentSize,
CGSizeMake((w + scrollExtentMax) * effectivelyScale, h * effectivelyScale)));
XCTAssertTrue(CGPointEqualToPoint(scrollable.contentOffset,
CGPointMake(scrollPosition * effectivelyScale, 0)));
}
- (void)testCanHandleInfiniteScrollExtent {
fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
new flutter::MockAccessibilityBridge());
fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
float transformScale = 0.5f;
float screenScale = [[bridge->view() window] screen].scale;
float effectivelyScale = transformScale / screenScale;
float x = 10;
float y = 10;
float w = 100;
float h = 200;
float scrollExtentMax = INFINITY;
float scrollPosition = 150.0;
flutter::SemanticsNode node;
node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
node.actions = flutter::kVerticalScrollSemanticsActions;
node.rect = SkRect::MakeXYWH(x, y, w, h);
node.scrollExtentMax = scrollExtentMax;
node.scrollPosition = scrollPosition;
node.transform = {
transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, 1.0};
FlutterSemanticsObject* delegate = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
FlutterScrollableSemanticsObject* scrollable =
[[FlutterScrollableSemanticsObject alloc] initWithSemanticsObject:delegate];
SemanticsObject* scrollable_object = static_cast<SemanticsObject*>(scrollable);
[scrollable_object setSemanticsNode:&node];
[scrollable_object accessibilityBridgeDidFinishUpdate];
XCTAssertTrue(
CGRectEqualToRect(scrollable.frame, CGRectMake(x * effectivelyScale, y * effectivelyScale,
w * effectivelyScale, h * effectivelyScale)));
XCTAssertTrue(CGSizeEqualToSize(
scrollable.contentSize,
CGSizeMake(w * effectivelyScale,
(h + kScrollExtentMaxForInf + scrollPosition) * effectivelyScale)));
XCTAssertTrue(CGPointEqualToPoint(scrollable.contentOffset,
CGPointMake(0, scrollPosition * effectivelyScale)));
}
- (void)testFlutterScrollableSemanticsObjectIsNotHittestable {
fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
new flutter::MockAccessibilityBridge());
fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
flutter::SemanticsNode node;
node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
node.actions = flutter::kHorizontalScrollSemanticsActions;
node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
node.scrollExtentMax = 100.0;
node.scrollPosition = 0.0;
FlutterSemanticsObject* delegate = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
FlutterScrollableSemanticsObject* scrollable =
[[FlutterScrollableSemanticsObject alloc] initWithSemanticsObject:delegate];
SemanticsObject* scrollable_object = static_cast<SemanticsObject*>(scrollable);
[scrollable_object setSemanticsNode:&node];
[scrollable_object accessibilityBridgeDidFinishUpdate];
XCTAssertEqual([scrollable hitTest:CGPointMake(10, 10) withEvent:nil], nil);
}
- (void)testSemanticsObjectBuildsAttributedString {
fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
new flutter::MockAccessibilityBridge());

View File

@ -200,16 +200,11 @@ void AccessibilityBridge::UpdateSemantics(flutter::SemanticsNodeUpdates nodes,
view_controller_.view.accessibilityElements = nil;
}
NSMutableArray<NSNumber*>* doomed_uids = [NSMutableArray arrayWithArray:[objects_ allKeys]];
if (root) {
NSMutableArray<NSNumber*>* doomed_uids = [NSMutableArray arrayWithArray:[objects_.get() allKeys]];
if (root)
VisitObjectsRecursivelyAndRemove(root, doomed_uids);
}
[objects_ removeObjectsForKeys:doomed_uids];
for (SemanticsObject* object in [objects_ allValues]) {
[object accessibilityBridgeDidFinishUpdate];
}
if (!ios_delegate_->IsFlutterViewControllerPresentingModalViewController(view_controller_)) {
layoutChanged = layoutChanged || [doomed_uids count] > 0;
@ -263,11 +258,6 @@ static SemanticsObject* CreateObject(const flutter::SemanticsNode& node,
} else if (node.HasFlag(flutter::SemanticsFlags::kHasToggledState) ||
node.HasFlag(flutter::SemanticsFlags::kHasCheckedState)) {
return [[[FlutterSwitchSemanticsObject alloc] initWithBridge:weak_ptr uid:node.id] autorelease];
} else if (node.HasFlag(flutter::SemanticsFlags::kHasImplicitScrolling)) {
SemanticsObject* delegateObject =
[[[FlutterSemanticsObject alloc] initWithBridge:weak_ptr uid:node.id] autorelease];
return (SemanticsObject*)[[[FlutterScrollableSemanticsObject alloc]
initWithSemanticsObject:delegateObject] autorelease];
} else {
return [[[FlutterSemanticsObject alloc] initWithBridge:weak_ptr uid:node.id] autorelease];
}