7#import <WebKit/WebKit.h>
17 return CGRectMake(clipDlRect.
GetX(),
24 CATransform3D
transform = CATransform3DIdentity;
50 CGPathMoveToPoint(path_ref_, nil, p2.
x, p2.
y);
53 CGPathAddLineToPoint(path_ref_, nil, p2.
x, p2.
y);
56 CGPathAddQuadCurveToPoint(path_ref_, nil, cp.
x, cp.
y, p2.
x, p2.
y);
62 CGPathAddCurveToPoint(path_ref_, nil,
63 cp1.
x, cp1.
y, cp2.
x, cp2.
y, p2.
x, p2.
y);
65 void Close()
override { CGPathCloseSubpath(path_ref_); }
67 CGMutablePathRef
TakePath()
const {
return path_ref_; }
70 CGMutablePathRef path_ref_ = CGPathCreateMutable();
77@property(nonatomic)
BOOL backdropFilterViewConfigured;
82- (void)updateVisualEffectView:(UIVisualEffectView*)visualEffectView;
96 blurRadius:(CGFloat)blurRadius
97 visualEffectView:(UIVisualEffectView*)visualEffectView {
98 if (
self = [super init]) {
103 FML_DLOG(ERROR) <<
"Apple's API for UIVisualEffectView changed. Update the implementation to "
104 "access the gaussianBlur CAFilter.";
107 _backdropFilterView = visualEffectView;
108 _backdropFilterViewConfigured = NO;
120+ (void)prepareOnce:(UIVisualEffectView*)visualEffectView {
124 for (NSUInteger
i = 0;
i < visualEffectView.subviews.count;
i++) {
125 UIView*
view = visualEffectView.subviews[i];
126 if ([NSStringFromClass([
view class]) hasSuffix:
@"BackdropView"]) {
128 for (NSObject* filter in
view.layer.filters) {
129 if ([[filter valueForKey:
@"name"] isEqual:
@"gaussianBlur"] &&
130 [[filter valueForKey:
@"inputRadius"] isKindOfClass:[NSNumber class]]) {
135 }
else if ([NSStringFromClass([
view class]) hasSuffix:
@"VisualEffectSubview"]) {
142+ (
BOOL)isUIVisualEffectViewImplementationValid {
148 if (!
self.backdropFilterViewConfigured) {
149 [
self updateVisualEffectView:_backdropFilterView];
150 self.backdropFilterViewConfigured = YES;
152 return _backdropFilterView;
155- (void)updateVisualEffectView:(UIVisualEffectView*)visualEffectView {
156 NSObject* gaussianBlurFilter = [_gaussianBlurFilter copy];
158 UIView* backdropView = visualEffectView.subviews[_indexOfBackdropView];
159 [gaussianBlurFilter setValue:@(_blurRadius) forKey:@"inputRadius"];
160 backdropView.layer.filters = @[ gaussianBlurFilter ];
162 UIView* visualEffectSubview = visualEffectView.subviews[_indexOfVisualEffectSubview];
163 visualEffectSubview.layer.backgroundColor = UIColor.clearColor.CGColor;
164 visualEffectView.frame = _frame;
166 self.backdropFilterView = visualEffectView;
173@property(nonatomic, copy) NSArray<PlatformViewFilter*>* filters;
183- (
BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event {
184 for (UIView*
view in
self.subviews) {
185 if ([
view pointInside:[
self convertPoint:point toView:view] withEvent:event]) {
194 if (
self.filters.count == 0 && filters.count == 0) {
197 self.filters = filters;
198 NSUInteger index = 0;
199 for (index = 0; index <
self.filters.count; index++) {
200 UIVisualEffectView* backdropFilterView;
202 if (
self.backdropFilterSubviews.count <= index) {
203 backdropFilterView = filter.backdropFilterView;
204 [self addSubview:backdropFilterView];
205 [self.backdropFilterSubviews addObject:backdropFilterView];
207 [filter updateVisualEffectView:self.backdropFilterSubviews[index]];
210 for (NSUInteger
i =
self.backdropFilterSubviews.count;
i > index;
i--) {
211 [
self.backdropFilterSubviews[i - 1] removeFromSuperview];
212 [
self.backdropFilterSubviews removeLastObject];
217 if (!_backdropFilterSubviews) {
218 _backdropFilterSubviews = [[NSMutableArray alloc] init];
220 return _backdropFilterSubviews;
235@property(nonatomic) CATransform3D reverseScreenScale;
237- (
fml::CFRef<CGPathRef>)getTransformedPath:(CGPathRef)path matrix:(CATransform3D)matrix;
242 std::vector<fml::CFRef<CGPathRef>> paths_;
248 return [
self initWithFrame:frame screenScale:[UIScreen mainScreen].scale];
251- (instancetype)
initWithFrame:(CGRect)frame screenScale:(CGFloat)screenScale {
253 self.backgroundColor = UIColor.clearColor;
254 _reverseScreenScale = CATransform3DMakeScale(1 / screenScale, 1 / screenScale, 1);
262 return [CAShapeLayer class];
265- (CAShapeLayer*)shapeLayer {
266 return (CAShapeLayer*)
self.layer;
273 [
self shapeLayer].path = nil;
274 [
self setNeedsDisplay];
282- (
BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event {
286- (void)drawRect:(CGRect)rect {
290 CGContextRef context = UIGraphicsGetCurrentContext();
291 CGContextSaveGState(context);
294 CGContextSetAlpha(context, 1);
296 for (
size_t i = 0;
i < paths_.size();
i++) {
297 CGContextAddPath(context, paths_.at(
i));
298 CGContextClip(context);
300 CGContextFillRect(context, rect);
301 CGContextRestoreGState(context);
305 [
super drawRect:rect];
306 if (![
self shapeLayer].path) {
307 if (paths_.size() == 1) {
309 [
self shapeLayer].path = paths_.at(0);
312 CGPathRef pathSoFar = CGPathCreateWithRect(
rectSoFar_, nil);
313 [
self shapeLayer].path = pathSoFar;
314 CGPathRelease(pathSoFar);
322 CGPathRef
path = CGPathCreateWithRect(clipRect, nil);
324 CATransform3D matrixInPoints =
326 paths_.push_back([
self getTransformedPath:
path matrix:matrixInPoints]);
327 CGAffineTransform affine = [
self affineWithMatrix:matrixInPoints];
329 if (affine.b == 0 && affine.c == 0) {
337 if (clipDlRRect.IsEmpty()) {
339 }
else if (clipDlRRect.IsRect()) {
340 [
self clipRect:clipDlRRect.GetBounds() matrix:matrix];
343 CGPathRef pathRef =
nullptr;
346 if (clipDlRRect.GetRadii().AreAllCornersSame()) {
348 auto radii = clipDlRRect.GetRadii();
350 CGPathCreateWithRoundedRect(clipRect, radii.top_left.width, radii.top_left.height, nil);
352 CGMutablePathRef mutablePathRef = CGPathCreateMutable();
356 auto top = clipDlRect.
GetTop();
370 CGPathMoveToPoint(mutablePathRef, nil,
371 left + top_left.width, top);
373 CGPathAddLineToPoint(mutablePathRef, nil,
374 right - top_right.width, top);
375 CGPathAddCurveToPoint(mutablePathRef, nil,
377 right, top + top_right.height,
378 right, top + top_right.height);
380 CGPathAddLineToPoint(mutablePathRef, nil,
381 right, bottom - bottom_right.height);
382 CGPathAddCurveToPoint(mutablePathRef, nil,
384 right - bottom_right.width, bottom,
385 right - bottom_right.width, bottom);
387 CGPathAddLineToPoint(mutablePathRef, nil,
388 left + bottom_left.width, bottom);
389 CGPathAddCurveToPoint(mutablePathRef, nil,
391 left, bottom - bottom_left.height,
392 left, bottom - bottom_left.height);
394 CGPathAddLineToPoint(mutablePathRef, nil,
395 left, top + top_left.height);
396 CGPathAddCurveToPoint(mutablePathRef, nil,
398 left + top_left.width, top,
399 left + top_left.width, top);
400 CGPathCloseSubpath(mutablePathRef);
401 pathRef = mutablePathRef;
404 CATransform3D matrixInPoints =
409 paths_.push_back([
self getTransformedPath:pathRef matrix:matrixInPoints]);
416 CGPathReceiver receiver;
421 dlPath.Dispatch(receiver);
424 CATransform3D matrixInPoints =
426 paths_.push_back([
self getTransformedPath:receiver.TakePath() matrix:matrixInPoints]);
429- (CGAffineTransform)affineWithMatrix:(CATransform3D)matrix {
430 return CGAffineTransformMake(matrix.m11, matrix.m12, matrix.m21, matrix.m22, matrix.m41,
434- (
fml::CFRef<CGPathRef>)getTransformedPath:(CGPathRef)path matrix:(CATransform3D)matrix {
435 CGAffineTransform affine = [
self affineWithMatrix:matrix];
436 CGPathRef transformedPath = CGPathCreateCopyByTransformingPath(path, &affine);
448@property(nonatomic) NSUInteger capacity;
452@property(nonatomic) NSMutableSet<FlutterClippingMaskView*>* pool;
458- (instancetype)initWithCapacity:(NSInteger)capacity {
459 if (
self = [super init]) {
462 _pool = [[NSMutableSet alloc] initWithCapacity:1];
463 _capacity = capacity;
470 if (
self.pool.count == 0) {
473 screenScale:UIScreen.mainScreen.scale];
476 maskView.frame = frame;
478 [
self.pool removeObject:maskView];
485 if (
self.pool.count ==
self.capacity) {
488 [
self.pool addObject:maskView];
493@implementation UIView (FirstResponder)
495 if (
self.isFirstResponder) {
498 for (UIView* subview in
self.subviews) {
499 if (subview.flt_hasFirstResponderInViewHierarchySubtree) {
508@property(nonatomic, weak, readonly) UIView* embeddedView;
514- (instancetype)initWithEmbeddedView:(UIView*)embeddedView
516 gestureRecognizersBlockingPolicy:
518 self = [
super initWithFrame:embeddedView.frame];
520 self.multipleTouchEnabled = YES;
521 _embeddedView = embeddedView;
522 embeddedView.autoresizingMask =
523 (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
525 [
self addSubview:embeddedView];
529 platformViewsController:platformViewsController];
531 _delayingRecognizer =
534 forwardingRecognizer:forwardingRecognizer];
535 _blockingPolicy = blockingPolicy;
537 [
self addGestureRecognizer:_delayingRecognizer];
538 [
self addGestureRecognizer:forwardingRecognizer];
543- (void)forceResetForwardingGestureRecognizerState {
551 [oldForwardingRecognizer recreateRecognizerWithTarget:self];
552 self.delayingRecognizer.forwardingRecognizer = newForwardingRecognizer;
553 [
self removeGestureRecognizer:oldForwardingRecognizer];
554 [
self addGestureRecognizer:newForwardingRecognizer];
557- (void)releaseGesture {
558 self.delayingRecognizer.state = UIGestureRecognizerStateFailed;
561- (
BOOL)containsWebView:(UIView*)view remainingSubviewDepth:(
int)remainingSubviewDepth {
562 if (remainingSubviewDepth < 0) {
565 if ([
view isKindOfClass:[WKWebView class]]) {
568 for (UIView* subview in
view.subviews) {
569 if ([
self containsWebView:subview remainingSubviewDepth:remainingSubviewDepth - 1]) {
576- (void)blockGesture {
577 switch (_blockingPolicy) {
580 self.delayingRecognizer.state = UIGestureRecognizerStateEnded;
590 if (@available(iOS 26.0, *)) {
594 }
else if (@available(iOS 18.2, *)) {
601 if ([
self containsWebView:
self.embeddedView remainingSubviewDepth:1]) {
602 [
self removeGestureRecognizer:self.delayingRecognizer];
603 [
self addGestureRecognizer:self.delayingRecognizer];
609 if (
self.delayingRecognizer.touchedEndedWithoutBlocking) {
613 self.delayingRecognizer.state = UIGestureRecognizerStateEnded;
618 self.delayingRecognizer.shouldEndInNextTouchesEnded = YES;
629- (void)touchesBegan:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event {
632- (void)touchesMoved:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event {
635- (void)touchesCancelled:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event {
638- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
641- (
id)accessibilityContainer {
642 return self.flutterAccessibilityContainer;
649- (instancetype)initWithTarget:(
id)target
651 forwardingRecognizer:(UIGestureRecognizer*)forwardingRecognizer {
652 self = [
super initWithTarget:target action:action];
654 self.delaysTouchesBegan = YES;
655 self.delaysTouchesEnded = YES;
657 _shouldEndInNextTouchesEnded = NO;
658 _touchedEndedWithoutBlocking = NO;
659 _forwardingRecognizer = forwardingRecognizer;
664- (
BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer
665 shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer {
668 return otherGestureRecognizer != _forwardingRecognizer && otherGestureRecognizer !=
self;
671- (
BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer
672 shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer {
673 return otherGestureRecognizer ==
self;
676- (void)touchesBegan:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event {
678 [
super touchesBegan:touches withEvent:event];
681- (void)touchesEnded:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event {
682 if (
self.shouldEndInNextTouchesEnded) {
683 self.state = UIGestureRecognizerStateEnded;
684 self.shouldEndInNextTouchesEnded = NO;
686 self.touchedEndedWithoutBlocking = YES;
688 [
super touchesEnded:touches withEvent:event];
691- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event {
692 self.state = UIGestureRecognizerStateFailed;
714- (instancetype)initWithTarget:(
id)target
716 self = [
super initWithTarget:target action:nil];
720 _platformViewsController = platformViewsController;
728 platformViewsController:_platformViewsController];
731- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
739 [_flutterViewController touchesBegan:touches withEvent:event];
743- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
744 [_flutterViewController touchesMoved:touches withEvent:event];
747- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
748 [_flutterViewController touchesEnded:touches withEvent:event];
755 self.state = UIGestureRecognizerStateFailed;
757 [
self forceResetStateIfNeeded];
761- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event {
767 [_flutterViewController forceTouchesCancelled:touches];
770 self.state = UIGestureRecognizerStateFailed;
772 [
self forceResetStateIfNeeded];
776- (void)forceResetStateIfNeeded {
778 dispatch_async(dispatch_get_main_queue(), ^{
783 if (strongSelf.state != UIGestureRecognizerStatePossible) {
784 [(FlutterTouchInterceptingView*)strongSelf.view forceResetForwardingGestureRecognizerState];
789- (
BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer
790 shouldRecognizeSimultaneouslyWithGestureRecognizer:
791 (UIGestureRecognizer*)otherGestureRecognizer {
FlutterPlatformViewGestureRecognizersBlockingPolicy
@ FlutterPlatformViewGestureRecognizersBlockingPolicyEager
@ FlutterPlatformViewGestureRecognizersBlockingPolicyWaitUntilTouchesEnded
BOOL flt_hasFirstResponderInViewHierarchySubtree
NSMutableArray * backdropFilterSubviews()
void CubicTo(const flutter::DlPoint &cp1, const flutter::DlPoint &cp2, const flutter::DlPoint &p2) override
void MoveTo(const flutter::DlPoint &p2, bool will_be_closed) override
void LineTo(const flutter::DlPoint &p2) override
void QuadTo(const flutter::DlPoint &cp, const flutter::DlPoint &p2) override
CGMutablePathRef TakePath() const
BOOL touchedEndedWithoutBlocking
Collection of functions to receive path segments from the underlying path representation via the DlPa...
#define FML_DLOG(severity)
#define FML_DCHECK(condition)
instancetype initWithFrame
impeller::RoundRect DlRoundRect
impeller::Matrix DlMatrix
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
A 4x4 matrix using column-major storage.
constexpr auto GetBottom() const
constexpr Type GetY() const
Returns the Y coordinate of the upper left corner, equivalent to |GetOrigin().y|.
constexpr auto GetTop() const
constexpr Type GetHeight() const
Returns the height of the rectangle, equivalent to |GetSize().height|.
constexpr auto GetLeft() const
constexpr Type GetX() const
Returns the X coordinate of the upper left corner, equivalent to |GetOrigin().x|.
constexpr auto GetRight() const
constexpr Type GetWidth() const
Returns the width of the rectangle, equivalent to |GetSize().width|.