Revert "iOS: Migrate PlatformViewsController to Objective-C (#56790)" (flutter/engine#56817)
This is a combination of 3 reverts, required to get back to the revert that caused `ios_platform_view_tests` to start failing in the framework repo. In reverse chronological order, this reverts two trivial commits plus the non-trivial commit that likely caused the breakage: * Revert "iOS: Eliminate global in platformviews controller (#56805)" This reverts commit cea4600caa7098fa7ec109d18b869db46cda726a. * Revert "iOS: Delete FlutterPlatformViewsController.layerPoolSize (#56806)" This reverts commit 80fa8a590876e0d29055b9ddbfa8670c1f83759e. * Revert "iOS: Migrate PlatformViewsController to Objective-C (#56790)" This reverts commit afd05afc406deb79fbe9c16684aeeeb19322b288. [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
This commit is contained in:
parent
61d0019469
commit
52661e51c0
@ -44564,8 +44564,6 @@ ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatf
|
||||
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsController.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsController.mm + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate.mm + ../../../flutter/LICENSE
|
||||
@ -44627,6 +44625,8 @@ ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/overlay_laye
|
||||
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/overlay_layer_pool.mm + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.mm + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_views_controller.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_views_controller.mm + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.mm + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h + ../../../flutter/LICENSE
|
||||
@ -47500,8 +47500,6 @@ FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatfor
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsController.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsController.mm
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate.mm
|
||||
@ -47563,6 +47561,8 @@ FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/overlay_layer_
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/overlay_layer_pool.mm
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.mm
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_views_controller.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_views_controller.mm
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.mm
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h
|
||||
|
@ -80,8 +80,6 @@ source_set("flutter_framework_source") {
|
||||
"framework/Source/FlutterPlatformPlugin.h",
|
||||
"framework/Source/FlutterPlatformPlugin.mm",
|
||||
"framework/Source/FlutterPlatformViews.mm",
|
||||
"framework/Source/FlutterPlatformViewsController.h",
|
||||
"framework/Source/FlutterPlatformViewsController.mm",
|
||||
"framework/Source/FlutterPlatformViews_Internal.h",
|
||||
"framework/Source/FlutterPluginAppLifeCycleDelegate.mm",
|
||||
"framework/Source/FlutterRestorationPlugin.h",
|
||||
@ -122,6 +120,8 @@ source_set("flutter_framework_source") {
|
||||
"framework/Source/overlay_layer_pool.mm",
|
||||
"framework/Source/platform_message_response_darwin.h",
|
||||
"framework/Source/platform_message_response_darwin.mm",
|
||||
"framework/Source/platform_views_controller.h",
|
||||
"framework/Source/platform_views_controller.mm",
|
||||
"framework/Source/profiler_metrics_ios.h",
|
||||
"framework/Source/profiler_metrics_ios.mm",
|
||||
"framework/Source/vsync_waiter_ios.h",
|
||||
|
@ -105,8 +105,6 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
|
||||
@property(nonatomic, readonly, assign) BOOL allowHeadlessExecution;
|
||||
@property(nonatomic, readonly, assign) BOOL restorationEnabled;
|
||||
|
||||
@property(nonatomic, strong) FlutterPlatformViewsController* platformViewsController;
|
||||
|
||||
// Maintains a dictionary of plugin names that have registered with the engine. Used by
|
||||
// FlutterEngineRegistrar to implement a FlutterPluginRegistrar.
|
||||
@property(nonatomic, readonly) NSMutableDictionary* pluginPublications;
|
||||
@ -152,6 +150,7 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
|
||||
std::shared_ptr<flutter::ThreadHost> _threadHost;
|
||||
std::unique_ptr<flutter::Shell> _shell;
|
||||
|
||||
std::shared_ptr<flutter::PlatformViewsController> _platformViewsController;
|
||||
flutter::IOSRenderingAPI _renderingApi;
|
||||
std::shared_ptr<flutter::SamplingProfiler> _profiler;
|
||||
|
||||
@ -212,7 +211,7 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
|
||||
|
||||
_pluginPublications = [[NSMutableDictionary alloc] init];
|
||||
_registrars = [[NSMutableDictionary alloc] init];
|
||||
[self recreatePlatformViewsController];
|
||||
[self recreatePlatformViewController];
|
||||
_binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self];
|
||||
_textureRegistry = [[FlutterTextureRegistryRelay alloc] initWithParent:self];
|
||||
_connections.reset(new flutter::ConnectionCollection());
|
||||
@ -263,9 +262,9 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
|
||||
object:nil];
|
||||
}
|
||||
|
||||
- (void)recreatePlatformViewsController {
|
||||
- (void)recreatePlatformViewController {
|
||||
_renderingApi = flutter::GetRenderingAPIForProcess(FlutterView.forceSoftwareRendering);
|
||||
_platformViewsController = [[FlutterPlatformViewsController alloc] init];
|
||||
_platformViewsController.reset(new flutter::PlatformViewsController());
|
||||
}
|
||||
|
||||
- (flutter::IOSRenderingAPI)platformViewsRenderingAPI {
|
||||
@ -453,7 +452,11 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
|
||||
_shell.reset();
|
||||
_profiler.reset();
|
||||
_threadHost.reset();
|
||||
_platformViewsController = nil;
|
||||
_platformViewsController.reset();
|
||||
}
|
||||
|
||||
- (std::shared_ptr<flutter::PlatformViewsController>&)platformViewsController {
|
||||
return _platformViewsController;
|
||||
}
|
||||
|
||||
- (NSURL*)observatoryUrl {
|
||||
@ -632,7 +635,7 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
|
||||
[self.platformViewsChannel
|
||||
setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
|
||||
if (weakSelf) {
|
||||
[weakSelf.platformViewsController onMethodCall:call result:result];
|
||||
weakSelf.platformViewsController->OnMethodCall(call, result);
|
||||
}
|
||||
}];
|
||||
|
||||
@ -774,11 +777,11 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
|
||||
if (!strongSelf) {
|
||||
return std::unique_ptr<flutter::PlatformViewIOS>();
|
||||
}
|
||||
[strongSelf recreatePlatformViewsController];
|
||||
strongSelf.platformViewsController.taskRunner =
|
||||
shell.GetTaskRunners().GetPlatformTaskRunner();
|
||||
[strongSelf recreatePlatformViewController];
|
||||
strongSelf->_platformViewsController->SetTaskRunner(
|
||||
shell.GetTaskRunners().GetPlatformTaskRunner());
|
||||
return std::make_unique<flutter::PlatformViewIOS>(
|
||||
shell, strongSelf->_renderingApi, strongSelf.platformViewsController,
|
||||
shell, strongSelf->_renderingApi, strongSelf->_platformViewsController,
|
||||
shell.GetTaskRunners(), shell.GetConcurrentWorkerTaskRunner(),
|
||||
shell.GetIsGpuDisabledSyncSwitch());
|
||||
};
|
||||
@ -1100,7 +1103,7 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
|
||||
// Have to check in the next run loop, because iOS requests the previous first responder to
|
||||
// resign before requesting the next view to become first responder.
|
||||
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
||||
long platform_view_id = [self.platformViewsController firstResponderPlatformViewId];
|
||||
long platform_view_id = self.platformViewsController->FindFirstResponderPlatformViewId();
|
||||
if (platform_view_id == -1) {
|
||||
return;
|
||||
}
|
||||
@ -1397,10 +1400,11 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
|
||||
// create call is synchronous.
|
||||
flutter::Shell::CreateCallback<flutter::PlatformView> on_create_platform_view =
|
||||
[result, context](flutter::Shell& shell) {
|
||||
[result recreatePlatformViewsController];
|
||||
result.platformViewsController.taskRunner = shell.GetTaskRunners().GetPlatformTaskRunner();
|
||||
[result recreatePlatformViewController];
|
||||
result->_platformViewsController->SetTaskRunner(
|
||||
shell.GetTaskRunners().GetPlatformTaskRunner());
|
||||
return std::make_unique<flutter::PlatformViewIOS>(
|
||||
shell, context, result.platformViewsController, shell.GetTaskRunners());
|
||||
shell, context, result->_platformViewsController, shell.GetTaskRunners());
|
||||
};
|
||||
|
||||
flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
|
||||
@ -1495,9 +1499,8 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
|
||||
withId:(NSString*)factoryId
|
||||
gestureRecognizersBlockingPolicy:
|
||||
(FlutterPlatformViewGestureRecognizersBlockingPolicy)gestureRecognizersBlockingPolicy {
|
||||
[_flutterEngine.platformViewsController registerViewFactory:factory
|
||||
withId:factoryId
|
||||
gestureRecognizersBlockingPolicy:gestureRecognizersBlockingPolicy];
|
||||
[_flutterEngine platformViewsController]->RegisterViewFactory(factory, factoryId,
|
||||
gestureRecognizersBlockingPolicy);
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -46,6 +46,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
base64Encode:(bool)base64Encode;
|
||||
|
||||
- (FlutterPlatformPlugin*)platformPlugin;
|
||||
- (std::shared_ptr<flutter::PlatformViewsController>&)platformViewsController;
|
||||
- (FlutterTextInputPlugin*)textInputPlugin;
|
||||
- (FlutterRestorationPlugin*)restorationPlugin;
|
||||
- (void)launchEngine:(nullable NSString*)entrypoint
|
||||
@ -80,7 +81,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
userData:(nullable void*)userData;
|
||||
|
||||
@property(nonatomic, readonly) FlutterDartProject* project;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
@ -518,7 +518,8 @@ static BOOL _preparedOnce = NO;
|
||||
|
||||
@implementation FlutterTouchInterceptingView
|
||||
- (instancetype)initWithEmbeddedView:(UIView*)embeddedView
|
||||
platformViewsController:(FlutterPlatformViewsController*)platformViewsController
|
||||
platformViewsController:
|
||||
(fml::WeakPtr<flutter::PlatformViewsController>)platformViewsController
|
||||
gestureRecognizersBlockingPolicy:
|
||||
(FlutterPlatformViewGestureRecognizersBlockingPolicy)blockingPolicy {
|
||||
self = [super initWithFrame:embeddedView.frame];
|
||||
@ -666,7 +667,7 @@ static BOOL _preparedOnce = NO;
|
||||
// outlives the FlutterViewController. And ForwardingGestureRecognizer is owned by a subview of
|
||||
// FlutterView, so the ForwardingGestureRecognizer never out lives FlutterViewController.
|
||||
// Therefore, `_platformViewsController` should never be nullptr.
|
||||
__weak FlutterPlatformViewsController* _platformViewsController;
|
||||
fml::WeakPtr<flutter::PlatformViewsController> _platformViewsController;
|
||||
// Counting the pointers that has started in one touch sequence.
|
||||
NSInteger _currentTouchPointersCount;
|
||||
// We can't dispatch events to the framework without this back pointer.
|
||||
@ -677,12 +678,13 @@ static BOOL _preparedOnce = NO;
|
||||
}
|
||||
|
||||
- (instancetype)initWithTarget:(id)target
|
||||
platformViewsController:(FlutterPlatformViewsController*)platformViewsController {
|
||||
platformViewsController:
|
||||
(fml::WeakPtr<flutter::PlatformViewsController>)platformViewsController {
|
||||
self = [super initWithTarget:target action:nil];
|
||||
if (self) {
|
||||
self.delegate = self;
|
||||
FML_DCHECK(platformViewsController);
|
||||
_platformViewsController = platformViewsController;
|
||||
FML_DCHECK(platformViewsController.get() != nullptr);
|
||||
_platformViewsController = std::move(platformViewsController);
|
||||
_currentTouchPointersCount = 0;
|
||||
}
|
||||
return self;
|
||||
@ -690,7 +692,7 @@ static BOOL _preparedOnce = NO;
|
||||
|
||||
- (ForwardingGestureRecognizer*)recreateRecognizerWithTarget:(id)target {
|
||||
return [[ForwardingGestureRecognizer alloc] initWithTarget:target
|
||||
platformViewsController:_platformViewsController];
|
||||
platformViewsController:std::move(_platformViewsController)];
|
||||
}
|
||||
|
||||
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
|
||||
@ -699,7 +701,7 @@ static BOOL _preparedOnce = NO;
|
||||
// At the start of each gesture sequence, we reset the `_flutterViewController`,
|
||||
// so that all the touch events in the same sequence are forwarded to the same
|
||||
// `_flutterViewController`.
|
||||
_flutterViewController = _platformViewsController.flutterViewController;
|
||||
_flutterViewController = _platformViewsController->GetFlutterViewController();
|
||||
}
|
||||
[_flutterViewController touchesBegan:touches withEvent:event];
|
||||
_currentTouchPointersCount += touches.count;
|
||||
|
@ -1,149 +0,0 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMVIEWSCONTROLLER_H_
|
||||
#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMVIEWSCONTROLLER_H_
|
||||
|
||||
#include <Metal/Metal.h>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "flutter/flow/surface.h"
|
||||
#include "flutter/fml/task_runner.h"
|
||||
#include "flutter/fml/trace_event.h"
|
||||
#include "impeller/base/thread_safety.h"
|
||||
#include "third_party/skia/include/core/SkRect.h"
|
||||
|
||||
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewResponder.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/overlay_layer_pool.h"
|
||||
#import "flutter/shell/platform/darwin/ios/ios_context.h"
|
||||
|
||||
@class FlutterTouchInterceptingView;
|
||||
@class FlutterClippingMaskViewPool;
|
||||
|
||||
@interface FlutterPlatformViewsController : NSObject
|
||||
|
||||
- (id)init NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/// The task runner used to post rendering tasks to the platform thread.
|
||||
@property(nonatomic, assign) const fml::RefPtr<fml::TaskRunner>& taskRunner;
|
||||
|
||||
/// The flutter view.
|
||||
@property(nonatomic, weak) UIView* flutterView;
|
||||
|
||||
/// @brief The flutter view controller.
|
||||
@property(nonatomic, weak) UIViewController<FlutterViewResponder>* flutterViewController;
|
||||
|
||||
/// @brief Retrieve the view controller.
|
||||
- (UIViewController<FlutterViewResponder>*)flutterViewController;
|
||||
|
||||
/// @brief set the factory used to construct embedded UI Views.
|
||||
- (void)registerViewFactory:(NSObject<FlutterPlatformViewFactory>*)factory
|
||||
withId:(NSString*)factoryId
|
||||
gestureRecognizersBlockingPolicy:
|
||||
(FlutterPlatformViewGestureRecognizersBlockingPolicy)gestureRecognizerBlockingPolicy;
|
||||
|
||||
/// @brief Mark the beginning of a frame and record the size of the onscreen.
|
||||
- (void)beginFrameWithSize:(SkISize)frameSize;
|
||||
|
||||
/// @brief Cancel the current frame, indicating that no platform views are composited.
|
||||
///
|
||||
/// Additionally, reverts the composition order to its original state at the beginning of the
|
||||
/// frame.
|
||||
- (void)cancelFrame;
|
||||
|
||||
/// @brief Record a platform view in the layer tree to be rendered, along with the positioning and
|
||||
/// mutator parameters.
|
||||
///
|
||||
/// Called from the raster thread.
|
||||
- (void)prerollCompositeEmbeddedView:(int64_t)viewId
|
||||
withParams:(std::unique_ptr<flutter::EmbeddedViewParams>)params;
|
||||
|
||||
/// @brief Returns the`FlutterTouchInterceptingView` with the provided view_id.
|
||||
///
|
||||
/// Returns nil if there is no platform view with the provided id. Called
|
||||
/// from the platform thread.
|
||||
- (FlutterTouchInterceptingView*)flutterTouchInterceptingViewForId:(int64_t)viewId;
|
||||
|
||||
/// @brief Determine if thread merging is required after prerolling platform views.
|
||||
///
|
||||
/// Called from the raster thread.
|
||||
- (flutter::PostPrerollResult)postPrerollActionWithThreadMerger:
|
||||
(const fml::RefPtr<fml::RasterThreadMerger>&)rasterThreadMerger
|
||||
impellerEnabled:(BOOL)impellerEnabled;
|
||||
|
||||
/// @brief Mark the end of a compositor frame.
|
||||
///
|
||||
/// May determine changes are required to the thread merging state.
|
||||
/// Called from the raster thread.
|
||||
- (void)endFrameWithResubmit:(BOOL)shouldResubmitFrame
|
||||
threadMerger:(const fml::RefPtr<fml::RasterThreadMerger>&)rasterThreadMerger
|
||||
impellerEnabled:(BOOL)impellerEnabled;
|
||||
|
||||
/// @brief Returns the Canvas for the overlay slice for the given platform view.
|
||||
///
|
||||
/// Called from the raster thread.
|
||||
- (flutter::DlCanvas*)compositeEmbeddedViewWithId:(int64_t)viewId;
|
||||
|
||||
/// @brief Discards all platform views instances and auxiliary resources.
|
||||
///
|
||||
/// Called from the raster thread.
|
||||
- (void)reset;
|
||||
|
||||
/// @brief Encode rendering for the Flutter overlay views and queue up perform platform view
|
||||
/// mutations.
|
||||
///
|
||||
/// Called from the raster thread.
|
||||
- (BOOL)submitFrame:(std::unique_ptr<flutter::SurfaceFrame>)frame
|
||||
withIosContext:(const std::shared_ptr<flutter::IOSContext>&)iosContext
|
||||
grContext:(GrDirectContext*)grContext;
|
||||
|
||||
/// @brief Handler for platform view message channels.
|
||||
- (void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result;
|
||||
|
||||
/// @brief Returns the platform view id if the platform view (or any of its descendant view) is
|
||||
/// the first responder.
|
||||
///
|
||||
/// Returns -1 if no such platform view is found.
|
||||
- (long)firstResponderPlatformViewId;
|
||||
|
||||
/// @brief Pushes backdrop filter mutation to the mutator stack of each visited platform view.
|
||||
- (void)pushFilterToVisitedPlatformViews:(const std::shared_ptr<flutter::DlImageFilter>&)filter
|
||||
withRect:(const SkRect&)filterRect;
|
||||
|
||||
/// @brief Pushes the view id of a visted platform view to the list of visied platform views.
|
||||
- (void)pushVisitedPlatformViewId:(int64_t)viewId;
|
||||
|
||||
@end
|
||||
|
||||
@interface FlutterPlatformViewsController (Testing)
|
||||
|
||||
- (size_t)embeddedViewCount;
|
||||
|
||||
// Returns the `FlutterPlatformView`'s `view` object associated with the view_id.
|
||||
//
|
||||
// If the `PlatformViewsController` does not contain any `FlutterPlatformView` object or
|
||||
// a `FlutterPlatformView` object associated with the view_id cannot be found, the method
|
||||
// returns nil.
|
||||
- (UIView*)platformViewForId:(int64_t)viewId;
|
||||
|
||||
// Composite the PlatformView with `viewId`.
|
||||
//
|
||||
// Every frame, during the paint traversal of the layer tree, this method is called for all
|
||||
// the PlatformViews in `_viewsToRecomposite`.
|
||||
//
|
||||
// Note that `_viewsToRecomposite` does not represent all the views in the view hierarchy,
|
||||
// if a PlatformView does not change its composition parameter from last frame, it is not
|
||||
// included in the `views_to_recomposite_`.
|
||||
- (void)compositeView:(int64_t)viewId withParams:(const flutter::EmbeddedViewParams&)params;
|
||||
|
||||
- (const flutter::EmbeddedViewParams&)compositionParamsForView:(int64_t)viewId;
|
||||
|
||||
@end
|
||||
|
||||
#endif // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMVIEWSCONTROLLER_H_
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -6,20 +6,20 @@
|
||||
#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMVIEWS_INTERNAL_H_
|
||||
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h"
|
||||
#include "fml/task_runner.h"
|
||||
#include "impeller/base/thread_safety.h"
|
||||
#include "third_party/skia/include/core/SkRect.h"
|
||||
|
||||
#include <Metal/Metal.h>
|
||||
|
||||
#include "flutter/flow/surface.h"
|
||||
#include "flutter/fml/memory/weak_ptr.h"
|
||||
#include "flutter/fml/task_runner.h"
|
||||
#include "flutter/fml/trace_event.h"
|
||||
#include "flutter/impeller/base/thread_safety.h"
|
||||
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsController.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewResponder.h"
|
||||
#include "flutter/shell/platform/darwin/ios/ios_context.h"
|
||||
#include "third_party/skia/include/core/SkRect.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/platform_views_controller.h"
|
||||
#import "flutter/shell/platform/darwin/ios/ios_context.h"
|
||||
|
||||
// A UIView that acts as a clipping mask for the |ChildClippingView|.
|
||||
//
|
||||
@ -134,7 +134,8 @@
|
||||
// 2. Dispatching all events that are hittested to the embedded view to the FlutterView.
|
||||
@interface FlutterTouchInterceptingView : UIView
|
||||
- (instancetype)initWithEmbeddedView:(UIView*)embeddedView
|
||||
platformViewsController:(FlutterPlatformViewsController*)platformViewsController
|
||||
platformViewsController:
|
||||
(fml::WeakPtr<flutter::PlatformViewsController>)platformViewsController
|
||||
gestureRecognizersBlockingPolicy:
|
||||
(FlutterPlatformViewGestureRecognizersBlockingPolicy)blockingPolicy;
|
||||
|
||||
@ -191,7 +192,8 @@
|
||||
// directly to the FlutterView.
|
||||
@interface ForwardingGestureRecognizer : UIGestureRecognizer <UIGestureRecognizerDelegate>
|
||||
- (instancetype)initWithTarget:(id)target
|
||||
platformViewsController:(FlutterPlatformViewsController*)platformViewsController;
|
||||
platformViewsController:
|
||||
(fml::WeakPtr<flutter::PlatformViewsController>)platformViewsController;
|
||||
- (ForwardingGestureRecognizer*)recreateRecognizerWithTarget:(id)target;
|
||||
@end
|
||||
|
||||
|
@ -14,11 +14,12 @@
|
||||
@protocol FlutterViewEngineDelegate <NSObject>
|
||||
|
||||
@property(nonatomic, readonly) BOOL isUsingImpeller;
|
||||
@property(nonatomic, readonly) FlutterPlatformViewsController* platformViewsController;
|
||||
|
||||
- (flutter::Rasterizer::Screenshot)takeScreenshot:(flutter::Rasterizer::ScreenshotType)type
|
||||
asBase64Encoded:(BOOL)base64Encode;
|
||||
|
||||
- (std::shared_ptr<flutter::PlatformViewsController>&)platformViewsController;
|
||||
|
||||
/**
|
||||
* A callback that is called when iOS queries accessibility information of the Flutter view.
|
||||
*
|
||||
|
@ -745,14 +745,14 @@ static void SendFakeTouchEvent(UIScreen* screen,
|
||||
// thread.
|
||||
if (appeared) {
|
||||
[self installFirstFrameCallback];
|
||||
self.platformViewsController.flutterView = self.flutterView;
|
||||
self.platformViewsController.flutterViewController = self;
|
||||
[self.engine platformViewsController]->SetFlutterView(self.flutterView);
|
||||
[self.engine platformViewsController]->SetFlutterViewController(self);
|
||||
[self.engine iosPlatformView]->NotifyCreated();
|
||||
} else {
|
||||
self.displayingFlutterUI = NO;
|
||||
[self.engine iosPlatformView]->NotifyDestroyed();
|
||||
self.platformViewsController.flutterView = nil;
|
||||
self.platformViewsController.flutterViewController = nil;
|
||||
[self.engine platformViewsController]->SetFlutterView(nullptr);
|
||||
[self.engine platformViewsController]->SetFlutterViewController(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2321,7 +2321,7 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
|
||||
|
||||
#pragma mark - Platform views
|
||||
|
||||
- (FlutterPlatformViewsController*)platformViewsController {
|
||||
- (std::shared_ptr<flutter::PlatformViewsController>&)platformViewsController {
|
||||
return self.engine.platformViewsController;
|
||||
}
|
||||
|
||||
|
@ -5,12 +5,11 @@
|
||||
#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERVIEWCONTROLLER_INTERNAL_H_
|
||||
#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERVIEWCONTROLLER_INTERNAL_H_
|
||||
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h"
|
||||
|
||||
#include "flutter/fml/time/time_point.h"
|
||||
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterKeySecondaryResponder.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterKeyboardManager.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsController.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterRestorationPlugin.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterUIPressProxy.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewResponder.h"
|
||||
@ -57,8 +56,7 @@ typedef void (^FlutterKeyboardAnimationCallback)(fml::TimePoint);
|
||||
*/
|
||||
@property(nonatomic, assign, readwrite) BOOL prefersStatusBarHidden;
|
||||
|
||||
@property(nonatomic, readonly) FlutterPlatformViewsController* platformViewsController;
|
||||
|
||||
- (std::shared_ptr<flutter::PlatformViewsController>&)platformViewsController;
|
||||
- (FlutterRestorationPlugin*)restorationPlugin;
|
||||
|
||||
// Accepts keypress events, and then calls |nextAction| if the event was not
|
||||
|
@ -13,12 +13,13 @@ FLUTTER_ASSERT_ARC
|
||||
@property(nonatomic, assign) BOOL isUsingImpeller;
|
||||
@end
|
||||
|
||||
@implementation FakeDelegate
|
||||
|
||||
@synthesize platformViewsController = _platformViewsController;
|
||||
@implementation FakeDelegate {
|
||||
std::shared_ptr<flutter::PlatformViewsController> _platformViewsController;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
_callbackCalled = NO;
|
||||
_platformViewsController = std::shared_ptr<flutter::PlatformViewsController>(nullptr);
|
||||
return self;
|
||||
}
|
||||
|
||||
@ -27,6 +28,10 @@ FLUTTER_ASSERT_ARC
|
||||
return {};
|
||||
}
|
||||
|
||||
- (std::shared_ptr<flutter::PlatformViewsController>&)platformViewsController {
|
||||
return _platformViewsController;
|
||||
}
|
||||
|
||||
- (void)flutterViewAccessibilityDidCall {
|
||||
_callbackCalled = YES;
|
||||
}
|
||||
|
@ -43,7 +43,9 @@ class MockAccessibilityBridge : public AccessibilityBridgeIos {
|
||||
}
|
||||
void AccessibilityObjectDidBecomeFocused(int32_t id) override {}
|
||||
void AccessibilityObjectDidLoseFocus(int32_t id) override {}
|
||||
FlutterPlatformViewsController* GetPlatformViewsController() const override { return nil; }
|
||||
std::shared_ptr<PlatformViewsController> GetPlatformViewsController() const override {
|
||||
return nil;
|
||||
}
|
||||
std::vector<SemanticsActionObservation> observations;
|
||||
bool isVoiceOverRunningValue;
|
||||
|
||||
@ -72,7 +74,9 @@ class MockAccessibilityBridgeNoWindow : public AccessibilityBridgeIos {
|
||||
}
|
||||
void AccessibilityObjectDidBecomeFocused(int32_t id) override {}
|
||||
void AccessibilityObjectDidLoseFocus(int32_t id) override {}
|
||||
FlutterPlatformViewsController* GetPlatformViewsController() const override { return nil; }
|
||||
std::shared_ptr<PlatformViewsController> GetPlatformViewsController() const override {
|
||||
return nil;
|
||||
}
|
||||
std::vector<SemanticsActionObservation> observations;
|
||||
bool isVoiceOverRunningValue;
|
||||
|
||||
|
@ -50,7 +50,7 @@ class AccessibilityBridge final : public AccessibilityBridgeIos {
|
||||
|
||||
AccessibilityBridge(FlutterViewController* view_controller,
|
||||
PlatformViewIOS* platform_view,
|
||||
__weak FlutterPlatformViewsController* platform_views_controller,
|
||||
std::shared_ptr<PlatformViewsController> platform_views_controller,
|
||||
std::unique_ptr<IosDelegate> ios_delegate = nullptr);
|
||||
~AccessibilityBridge();
|
||||
|
||||
@ -72,7 +72,7 @@ class AccessibilityBridge final : public AccessibilityBridgeIos {
|
||||
|
||||
fml::WeakPtr<AccessibilityBridge> GetWeakPtr();
|
||||
|
||||
FlutterPlatformViewsController* GetPlatformViewsController() const override {
|
||||
std::shared_ptr<PlatformViewsController> GetPlatformViewsController() const override {
|
||||
return platform_views_controller_;
|
||||
};
|
||||
|
||||
@ -91,7 +91,7 @@ class AccessibilityBridge final : public AccessibilityBridgeIos {
|
||||
|
||||
FlutterViewController* view_controller_;
|
||||
PlatformViewIOS* platform_view_;
|
||||
__weak FlutterPlatformViewsController* platform_views_controller_;
|
||||
const std::shared_ptr<PlatformViewsController> platform_views_controller_;
|
||||
// If the this id is kSemanticObjectIdInvalid, it means either nothing has
|
||||
// been focused or the focus is currently outside of the flutter application
|
||||
// (i.e. the status bar or keyboard)
|
||||
|
@ -42,11 +42,11 @@ class DefaultIosDelegate : public AccessibilityBridge::IosDelegate {
|
||||
AccessibilityBridge::AccessibilityBridge(
|
||||
FlutterViewController* view_controller,
|
||||
PlatformViewIOS* platform_view,
|
||||
__weak FlutterPlatformViewsController* platform_views_controller,
|
||||
std::shared_ptr<PlatformViewsController> platform_views_controller,
|
||||
std::unique_ptr<IosDelegate> ios_delegate)
|
||||
: view_controller_(view_controller),
|
||||
platform_view_(platform_view),
|
||||
platform_views_controller_(platform_views_controller),
|
||||
platform_views_controller_(std::move(platform_views_controller)),
|
||||
last_focused_semantics_object_id_(kSemanticObjectIdInvalid),
|
||||
objects_([[NSMutableDictionary alloc] init]),
|
||||
previous_routes_({}),
|
||||
@ -271,13 +271,11 @@ static SemanticsObject* CreateObject(const flutter::SemanticsNode& node,
|
||||
} else if (node.HasFlag(flutter::SemanticsFlags::kHasImplicitScrolling)) {
|
||||
return [[FlutterScrollableSemanticsObject alloc] initWithBridge:weak_ptr uid:node.id];
|
||||
} else if (node.IsPlatformViewNode()) {
|
||||
FlutterPlatformViewsController* platformViewsController =
|
||||
weak_ptr->GetPlatformViewsController();
|
||||
FlutterTouchInterceptingView* touchInterceptingView =
|
||||
[platformViewsController flutterTouchInterceptingViewForId:node.platformViewId];
|
||||
return [[FlutterPlatformViewSemanticsContainer alloc] initWithBridge:weak_ptr
|
||||
return [[FlutterPlatformViewSemanticsContainer alloc]
|
||||
initWithBridge:weak_ptr
|
||||
uid:node.id
|
||||
platformView:touchInterceptingView];
|
||||
platformView:weak_ptr->GetPlatformViewsController()->GetFlutterTouchInterceptingViewByID(
|
||||
node.platformViewId)];
|
||||
} else {
|
||||
return [[FlutterSemanticsObject alloc] initWithBridge:weak_ptr uid:node.id];
|
||||
}
|
||||
|
@ -12,9 +12,9 @@
|
||||
#include "flutter/lib/ui/semantics/semantics_node.h"
|
||||
|
||||
@class UIView;
|
||||
@class FlutterPlatformViewsController;
|
||||
|
||||
namespace flutter {
|
||||
class PlatformViewsController;
|
||||
|
||||
/// Interface that represents an accessibility bridge for iOS.
|
||||
class AccessibilityBridgeIos {
|
||||
@ -39,7 +39,7 @@ class AccessibilityBridgeIos {
|
||||
* The input id is the uid of the newly focused SemanticObject.
|
||||
*/
|
||||
virtual void AccessibilityObjectDidLoseFocus(int32_t id) = 0;
|
||||
virtual FlutterPlatformViewsController* GetPlatformViewsController() const = 0;
|
||||
virtual std::shared_ptr<PlatformViewsController> GetPlatformViewsController() const = 0;
|
||||
};
|
||||
|
||||
} // namespace flutter
|
||||
|
@ -279,9 +279,8 @@ fml::RefPtr<fml::TaskRunner> CreateNewThread(const std::string& name) {
|
||||
/*ui=*/thread_task_runner,
|
||||
/*io=*/thread_task_runner);
|
||||
|
||||
FlutterPlatformViewsController* flutterPlatformViewsController =
|
||||
[[FlutterPlatformViewsController alloc] init];
|
||||
flutterPlatformViewsController.taskRunner = thread_task_runner;
|
||||
auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
|
||||
flutterPlatformViewsController->SetTaskRunner(thread_task_runner);
|
||||
auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
|
||||
/*delegate=*/mock_delegate,
|
||||
/*rendering_api=*/mock_delegate.settings_.enable_impeller
|
||||
@ -295,22 +294,19 @@ fml::RefPtr<fml::TaskRunner> CreateNewThread(const std::string& name) {
|
||||
id mockFlutterViewController = OCMClassMock([FlutterViewController class]);
|
||||
OCMStub([mockFlutterViewController view]).andReturn(mockFlutterView);
|
||||
std::string label = "some label";
|
||||
flutterPlatformViewsController.flutterView = mockFlutterView;
|
||||
flutterPlatformViewsController->SetFlutterView(mockFlutterView);
|
||||
|
||||
MockFlutterPlatformFactory* factory = [[MockFlutterPlatformFactory alloc] init];
|
||||
[flutterPlatformViewsController
|
||||
registerViewFactory:factory
|
||||
withId:@"MockFlutterPlatformView"
|
||||
gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
|
||||
flutterPlatformViewsController->RegisterViewFactory(
|
||||
factory, @"MockFlutterPlatformView",
|
||||
FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
|
||||
FlutterResult result = ^(id result) {
|
||||
};
|
||||
[flutterPlatformViewsController
|
||||
onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
|
||||
arguments:@{
|
||||
@"id" : @2,
|
||||
@"viewType" : @"MockFlutterPlatformView"
|
||||
}]
|
||||
result:result];
|
||||
flutterPlatformViewsController->OnMethodCall(
|
||||
[FlutterMethodCall
|
||||
methodCallWithMethodName:@"create"
|
||||
arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
|
||||
result);
|
||||
|
||||
auto bridge = std::make_unique<flutter::AccessibilityBridge>(
|
||||
/*view_controller=*/mockFlutterViewController,
|
||||
@ -326,7 +322,7 @@ fml::RefPtr<fml::TaskRunner> CreateNewThread(const std::string& name) {
|
||||
flutter::CustomAccessibilityActionUpdates actions;
|
||||
bridge->UpdateSemantics(/*nodes=*/nodes, /*actions=*/actions);
|
||||
XCTAssertNotNil(gMockPlatformView);
|
||||
[flutterPlatformViewsController reset];
|
||||
flutterPlatformViewsController->Reset();
|
||||
}
|
||||
XCTAssertNil(gMockPlatformView);
|
||||
}
|
||||
@ -344,9 +340,8 @@ fml::RefPtr<fml::TaskRunner> CreateNewThread(const std::string& name) {
|
||||
/*ui=*/thread_task_runner,
|
||||
/*io=*/thread_task_runner);
|
||||
|
||||
FlutterPlatformViewsController* flutterPlatformViewsController =
|
||||
[[FlutterPlatformViewsController alloc] init];
|
||||
flutterPlatformViewsController.taskRunner = thread_task_runner;
|
||||
auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
|
||||
flutterPlatformViewsController->SetTaskRunner(thread_task_runner);
|
||||
auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
|
||||
/*delegate=*/mock_delegate,
|
||||
/*rendering_api=*/mock_delegate.settings_.enable_impeller
|
||||
@ -358,19 +353,16 @@ fml::RefPtr<fml::TaskRunner> CreateNewThread(const std::string& name) {
|
||||
/*is_gpu_disabled_sync_switch=*/std::make_shared<fml::SyncSwitch>());
|
||||
|
||||
MockFlutterPlatformFactory* factory = [[MockFlutterPlatformFactory alloc] init];
|
||||
[flutterPlatformViewsController
|
||||
registerViewFactory:factory
|
||||
withId:@"MockFlutterPlatformView"
|
||||
gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
|
||||
flutterPlatformViewsController->RegisterViewFactory(
|
||||
factory, @"MockFlutterPlatformView",
|
||||
FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
|
||||
FlutterResult result = ^(id result) {
|
||||
};
|
||||
[flutterPlatformViewsController
|
||||
onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
|
||||
arguments:@{
|
||||
@"id" : @2,
|
||||
@"viewType" : @"MockFlutterPlatformView"
|
||||
}]
|
||||
result:result];
|
||||
flutterPlatformViewsController->OnMethodCall(
|
||||
[FlutterMethodCall
|
||||
methodCallWithMethodName:@"create"
|
||||
arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
|
||||
result);
|
||||
|
||||
auto bridge = std::make_unique<flutter::AccessibilityBridge>(
|
||||
/*view_controller=*/flutterViewController,
|
||||
@ -378,7 +370,7 @@ fml::RefPtr<fml::TaskRunner> CreateNewThread(const std::string& name) {
|
||||
/*platform_views_controller=*/flutterPlatformViewsController);
|
||||
|
||||
XCTAssertNotNil(gMockPlatformView);
|
||||
[flutterPlatformViewsController reset];
|
||||
flutterPlatformViewsController->Reset();
|
||||
platform_view->NotifyDestroyed();
|
||||
}
|
||||
XCTAssertNil(gMockPlatformView);
|
||||
@ -395,9 +387,8 @@ fml::RefPtr<fml::TaskRunner> CreateNewThread(const std::string& name) {
|
||||
/*ui=*/thread_task_runner,
|
||||
/*io=*/thread_task_runner);
|
||||
|
||||
FlutterPlatformViewsController* flutterPlatformViewsController =
|
||||
[[FlutterPlatformViewsController alloc] init];
|
||||
flutterPlatformViewsController.taskRunner = thread_task_runner;
|
||||
auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
|
||||
flutterPlatformViewsController->SetTaskRunner(thread_task_runner);
|
||||
auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
|
||||
/*delegate=*/mock_delegate,
|
||||
/*rendering_api=*/mock_delegate.settings_.enable_impeller
|
||||
@ -493,9 +484,8 @@ fml::RefPtr<fml::TaskRunner> CreateNewThread(const std::string& name) {
|
||||
/*ui=*/thread_task_runner,
|
||||
/*io=*/thread_task_runner);
|
||||
|
||||
FlutterPlatformViewsController* flutterPlatformViewsController =
|
||||
[[FlutterPlatformViewsController alloc] init];
|
||||
flutterPlatformViewsController.taskRunner = thread_task_runner;
|
||||
auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
|
||||
flutterPlatformViewsController->SetTaskRunner(thread_task_runner);
|
||||
auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
|
||||
/*delegate=*/mock_delegate,
|
||||
/*rendering_api=*/mock_delegate.settings_.enable_impeller
|
||||
@ -569,8 +559,8 @@ fml::RefPtr<fml::TaskRunner> CreateNewThread(const std::string& name) {
|
||||
/*ui=*/thread_task_runner,
|
||||
/*io=*/thread_task_runner);
|
||||
|
||||
FlutterPlatformViewsController* flutterPlatformViewsController =
|
||||
[[FlutterPlatformViewsController alloc] init];
|
||||
auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
|
||||
flutterPlatformViewsController->SetTaskRunner(thread_task_runner);
|
||||
auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
|
||||
/*delegate=*/mock_delegate,
|
||||
/*rendering_api=*/mock_delegate.settings_.enable_impeller
|
||||
@ -2185,12 +2175,11 @@ fml::RefPtr<fml::TaskRunner> CreateNewThread(const std::string& name) {
|
||||
/*is_gpu_disabled_sync_switch=*/std::make_shared<fml::SyncSwitch>());
|
||||
|
||||
id mockFlutterViewController = OCMClassMock([FlutterViewController class]);
|
||||
FlutterPlatformViewsController* flutterPlatformViewsController =
|
||||
[[FlutterPlatformViewsController alloc] init];
|
||||
flutterPlatformViewsController.taskRunner = thread_task_runner;
|
||||
auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
|
||||
flutterPlatformViewsController->SetTaskRunner(thread_task_runner);
|
||||
|
||||
OCMStub([mockFlutterViewController platformViewsController])
|
||||
.andReturn(flutterPlatformViewsController);
|
||||
.andReturn(flutterPlatformViewsController.get());
|
||||
platform_view->SetOwnerViewController(mockFlutterViewController);
|
||||
|
||||
platform_view->SetSemanticsEnabled(true);
|
||||
|
@ -0,0 +1,318 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_PLATFORM_VIEWS_CONTROLLER_H_
|
||||
#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_PLATFORM_VIEWS_CONTROLLER_H_
|
||||
|
||||
#include <Metal/Metal.h>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "flutter/flow/surface.h"
|
||||
#include "flutter/fml/memory/weak_ptr.h"
|
||||
#include "flutter/fml/task_runner.h"
|
||||
#include "flutter/fml/trace_event.h"
|
||||
#include "impeller/base/thread_safety.h"
|
||||
#include "third_party/skia/include/core/SkRect.h"
|
||||
|
||||
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewResponder.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/overlay_layer_pool.h"
|
||||
#import "flutter/shell/platform/darwin/ios/ios_context.h"
|
||||
|
||||
@class FlutterTouchInterceptingView;
|
||||
@class FlutterClippingMaskViewPool;
|
||||
|
||||
namespace flutter {
|
||||
|
||||
/// @brief Composites Flutter UI and overlay layers alongside embedded UIViews.
|
||||
class PlatformViewsController {
|
||||
public:
|
||||
PlatformViewsController();
|
||||
|
||||
~PlatformViewsController() = default;
|
||||
|
||||
/// @brief Retrieve a weak pointer to this controller.
|
||||
fml::WeakPtr<flutter::PlatformViewsController> GetWeakPtr();
|
||||
|
||||
/// @brief Set the platform task runner used to post rendering tasks.
|
||||
void SetTaskRunner(const fml::RefPtr<fml::TaskRunner>& platform_task_runner);
|
||||
|
||||
/// @brief Set the flutter view.
|
||||
void SetFlutterView(UIView* flutter_view) __attribute__((cf_audited_transfer));
|
||||
|
||||
/// @brief Set the flutter view controller.
|
||||
void SetFlutterViewController(UIViewController<FlutterViewResponder>* flutter_view_controller)
|
||||
__attribute__((cf_audited_transfer));
|
||||
|
||||
/// @brief Retrieve the view controller.
|
||||
UIViewController<FlutterViewResponder>* GetFlutterViewController()
|
||||
__attribute__((cf_audited_transfer));
|
||||
|
||||
/// @brief set the factory used to construct embedded UI Views.
|
||||
void RegisterViewFactory(
|
||||
NSObject<FlutterPlatformViewFactory>* factory,
|
||||
NSString* factoryId,
|
||||
FlutterPlatformViewGestureRecognizersBlockingPolicy gestureRecognizerBlockingPolicy)
|
||||
__attribute__((cf_audited_transfer));
|
||||
|
||||
/// @brief Mark the beginning of a frame and record the size of the onscreen.
|
||||
void BeginFrame(SkISize frame_size);
|
||||
|
||||
/// @brief Cancel the current frame, indicating that no platform views are composited.
|
||||
///
|
||||
/// Additionally, reverts the composition order to its original state at the beginning of the
|
||||
/// frame.
|
||||
void CancelFrame();
|
||||
|
||||
/// @brief Record a platform view in the layer tree to be rendered, along with the positioning and
|
||||
/// mutator parameters.
|
||||
///
|
||||
/// Called from the raster thread.
|
||||
void PrerollCompositeEmbeddedView(int64_t view_id,
|
||||
std::unique_ptr<flutter::EmbeddedViewParams> params);
|
||||
|
||||
/// @brief Returns the`FlutterTouchInterceptingView` with the provided view_id.
|
||||
///
|
||||
/// Returns nil if there is no platform view with the provided id. Called
|
||||
/// from the platform thread.
|
||||
FlutterTouchInterceptingView* GetFlutterTouchInterceptingViewByID(int64_t view_id);
|
||||
|
||||
/// @brief Determine if thread merging is required after prerolling platform views.
|
||||
///
|
||||
/// Called from the raster thread.
|
||||
PostPrerollResult PostPrerollAction(
|
||||
const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger,
|
||||
bool impeller_enabled);
|
||||
|
||||
/// @brief Mark the end of a compositor frame.
|
||||
///
|
||||
/// May determine changes are required to the thread merging state.
|
||||
/// Called from the raster thread.
|
||||
void EndFrame(bool should_resubmit_frame,
|
||||
const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger,
|
||||
bool impeller_enabled);
|
||||
|
||||
/// @brief Returns the Canvas for the overlay slice for the given platform view.
|
||||
///
|
||||
/// Called from the raster thread.
|
||||
DlCanvas* CompositeEmbeddedView(int64_t view_id);
|
||||
|
||||
/// @brief Discards all platform views instances and auxiliary resources.
|
||||
///
|
||||
/// Called from the raster thread.
|
||||
void Reset();
|
||||
|
||||
/// @brief Encode rendering for the Flutter overlay views and queue up perform platform view
|
||||
/// mutations.
|
||||
///
|
||||
/// Called from the raster thread.
|
||||
bool SubmitFrame(GrDirectContext* gr_context,
|
||||
const std::shared_ptr<IOSContext>& ios_context,
|
||||
std::unique_ptr<SurfaceFrame> frame);
|
||||
|
||||
/// @brief Handler for platform view message channels.
|
||||
void OnMethodCall(FlutterMethodCall* call, FlutterResult result)
|
||||
__attribute__((cf_audited_transfer));
|
||||
|
||||
/// @brief Returns the platform view id if the platform view (or any of its descendant view) is
|
||||
/// the first responder.
|
||||
///
|
||||
/// Returns -1 if no such platform view is found.
|
||||
long FindFirstResponderPlatformViewId();
|
||||
|
||||
/// @brief Pushes backdrop filter mutation to the mutator stack of each visited platform view.
|
||||
void PushFilterToVisitedPlatformViews(const std::shared_ptr<DlImageFilter>& filter,
|
||||
const SkRect& filter_rect);
|
||||
|
||||
/// @brief Pushes the view id of a visted platform view to the list of visied platform views.
|
||||
void PushVisitedPlatformView(int64_t view_id) { visited_platform_views_.push_back(view_id); }
|
||||
|
||||
// visible for testing.
|
||||
size_t EmbeddedViewCount() const;
|
||||
|
||||
// visible for testing.
|
||||
size_t LayerPoolSize() const;
|
||||
|
||||
// visible for testing.
|
||||
// Returns the `FlutterPlatformView`'s `view` object associated with the view_id.
|
||||
//
|
||||
// If the `PlatformViewsController` does not contain any `FlutterPlatformView` object or
|
||||
// a `FlutterPlatformView` object associated with the view_id cannot be found, the method
|
||||
// returns nil.
|
||||
UIView* GetPlatformViewByID(int64_t view_id);
|
||||
|
||||
// Visible for testing.
|
||||
void CompositeWithParams(int64_t view_id, const EmbeddedViewParams& params);
|
||||
|
||||
// Visible for testing.
|
||||
const EmbeddedViewParams& GetCompositionParams(int64_t view_id) const {
|
||||
return current_composition_params_.find(view_id)->second;
|
||||
}
|
||||
|
||||
private:
|
||||
PlatformViewsController(const PlatformViewsController&) = delete;
|
||||
PlatformViewsController& operator=(const PlatformViewsController&) = delete;
|
||||
|
||||
struct LayerData {
|
||||
SkRect rect;
|
||||
int64_t view_id;
|
||||
int64_t overlay_id;
|
||||
std::shared_ptr<OverlayLayer> layer;
|
||||
};
|
||||
|
||||
using LayersMap = std::unordered_map<int64_t, LayerData>;
|
||||
|
||||
// Update the buffers and mutate the platform views in CATransaction.
|
||||
//
|
||||
// Runs on the platform thread.
|
||||
void PerformSubmit(const LayersMap& platform_view_layers,
|
||||
std::unordered_map<int64_t, EmbeddedViewParams>& current_composition_params,
|
||||
const std::unordered_set<int64_t>& views_to_recomposite,
|
||||
const std::vector<int64_t>& composition_order,
|
||||
const std::vector<std::shared_ptr<OverlayLayer>>& unused_layers,
|
||||
const std::vector<std::unique_ptr<SurfaceFrame>>& surface_frames);
|
||||
|
||||
/// @brief Populate any missing overlay layers.
|
||||
///
|
||||
/// This requires posting a task to the platform thread and blocking on its completion.
|
||||
void CreateMissingOverlays(GrDirectContext* gr_context,
|
||||
const std::shared_ptr<IOSContext>& ios_context,
|
||||
size_t required_overlay_layers);
|
||||
|
||||
void OnCreate(FlutterMethodCall* call, FlutterResult result) __attribute__((cf_audited_transfer));
|
||||
|
||||
void OnDispose(FlutterMethodCall* call, FlutterResult result)
|
||||
__attribute__((cf_audited_transfer));
|
||||
|
||||
void OnAcceptGesture(FlutterMethodCall* call, FlutterResult result)
|
||||
__attribute__((cf_audited_transfer));
|
||||
|
||||
void OnRejectGesture(FlutterMethodCall* call, FlutterResult result)
|
||||
__attribute__((cf_audited_transfer));
|
||||
|
||||
/// @brief Return all views to be disposed on the platform thread.
|
||||
std::vector<UIView*> GetViewsToDispose();
|
||||
|
||||
void ClipViewSetMaskView(UIView* clipView) __attribute__((cf_audited_transfer));
|
||||
|
||||
// Applies the mutators in the mutators_stack to the UIView chain that was constructed by
|
||||
// `ReconstructClipViewsChain`
|
||||
//
|
||||
// Clips are applied to the `embedded_view`'s super view(|ChildClippingView|) using a
|
||||
// |FlutterClippingMaskView|. Transforms are applied to `embedded_view`
|
||||
//
|
||||
// The `bounding_rect` is the final bounding rect of the PlatformView
|
||||
// (EmbeddedViewParams::finalBoundingRect). If a clip mutator's rect contains the final bounding
|
||||
// rect of the PlatformView, the clip mutator is not applied for performance optimization.
|
||||
void ApplyMutators(const MutatorsStack& mutators_stack,
|
||||
UIView* embedded_view,
|
||||
const SkRect& bounding_rect) __attribute__((cf_audited_transfer));
|
||||
|
||||
std::shared_ptr<OverlayLayer> GetExistingLayer();
|
||||
|
||||
// Runs on the platform thread.
|
||||
void CreateLayer(GrDirectContext* gr_context,
|
||||
const std::shared_ptr<IOSContext>& ios_context,
|
||||
MTLPixelFormat pixel_format);
|
||||
|
||||
// Removes overlay views and platform views that aren't needed in the current frame.
|
||||
// Must run on the platform thread.
|
||||
void RemoveUnusedLayers(const std::vector<std::shared_ptr<OverlayLayer>>& unused_layers,
|
||||
const std::vector<int64_t>& composition_order);
|
||||
|
||||
// Appends the overlay views and platform view and sets their z index based on the composition
|
||||
// order.
|
||||
void BringLayersIntoView(const LayersMap& layer_map,
|
||||
const std::vector<int64_t>& composition_order);
|
||||
|
||||
// Resets the state of the frame.
|
||||
void ResetFrameState();
|
||||
|
||||
// The pool of reusable view layers. The pool allows to recycle layer in each frame.
|
||||
std::unique_ptr<OverlayLayerPool> layer_pool_;
|
||||
|
||||
// The platform view's |EmbedderViewSlice| keyed off the view id, which contains any subsequent
|
||||
// operation until the next platform view or the end of the last leaf node in the layer tree.
|
||||
//
|
||||
// The Slices are deleted by the PlatformViewsController.reset().
|
||||
std::unordered_map<int64_t, std::unique_ptr<EmbedderViewSlice>> slices_;
|
||||
|
||||
UIView* flutter_view_;
|
||||
UIViewController<FlutterViewResponder>* flutter_view_controller_;
|
||||
FlutterClippingMaskViewPool* mask_view_pool_;
|
||||
std::unordered_map<std::string, NSObject<FlutterPlatformViewFactory>*> factories_;
|
||||
|
||||
// The FlutterPlatformViewGestureRecognizersBlockingPolicy for each type of platform view.
|
||||
std::unordered_map<std::string, FlutterPlatformViewGestureRecognizersBlockingPolicy>
|
||||
gesture_recognizers_blocking_policies_;
|
||||
|
||||
/// The size of the current onscreen surface in physical pixels.
|
||||
SkISize frame_size_;
|
||||
|
||||
/// The task runner for posting tasks to the platform thread.
|
||||
fml::RefPtr<fml::TaskRunner> platform_task_runner_;
|
||||
|
||||
/// Each of the following structs stores part of the platform view hierarchy according to its
|
||||
/// ID.
|
||||
///
|
||||
/// This data must only be accessed on the platform thread.
|
||||
struct PlatformViewData {
|
||||
NSObject<FlutterPlatformView>* view;
|
||||
FlutterTouchInterceptingView* touch_interceptor;
|
||||
UIView* root_view;
|
||||
};
|
||||
|
||||
/// This data must only be accessed on the platform thread.
|
||||
std::unordered_map<int64_t, PlatformViewData> platform_views_;
|
||||
|
||||
/// The composition parameters for each platform view.
|
||||
///
|
||||
/// This state is only modified on the raster thread.
|
||||
std::unordered_map<int64_t, EmbeddedViewParams> current_composition_params_;
|
||||
|
||||
/// Method channel `OnDispose` calls adds the views to be disposed to this set to be disposed on
|
||||
/// the next frame.
|
||||
///
|
||||
/// This state is modified on both the platform and raster thread.
|
||||
std::unordered_set<int64_t> views_to_dispose_;
|
||||
|
||||
/// view IDs in composition order.
|
||||
///
|
||||
/// This state is only modified on the raster thread.
|
||||
std::vector<int64_t> composition_order_;
|
||||
|
||||
/// platform view IDs visited during layer tree composition.
|
||||
///
|
||||
/// This state is only modified on the raster thread.
|
||||
std::vector<int64_t> visited_platform_views_;
|
||||
|
||||
/// Only composite platform views in this set.
|
||||
///
|
||||
/// This state is only modified on the raster thread.
|
||||
std::unordered_set<int64_t> views_to_recomposite_;
|
||||
|
||||
/// @brief The composition order from the previous thread.
|
||||
///
|
||||
/// Only accessed from the platform thread.
|
||||
std::vector<int64_t> previous_composition_order_;
|
||||
|
||||
/// Whether the previous frame had any platform views in active composition order.
|
||||
///
|
||||
/// This state is tracked so that the first frame after removing the last platform view
|
||||
/// runs through the platform view rendering code path, giving us a chance to remove the
|
||||
/// platform view from the UIView hierarchy.
|
||||
///
|
||||
/// Only accessed from the raster thread.
|
||||
bool had_platform_views_ = false;
|
||||
|
||||
// WeakPtrFactory must be the last member.
|
||||
std::unique_ptr<fml::WeakPtrFactory<PlatformViewsController>> weak_factory_;
|
||||
};
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
#endif // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_PLATFORM_VIEWS_CONTROLLER_H_
|
@ -0,0 +1,874 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#import "shell/platform/darwin/ios/framework/Source/platform_views_controller.h"
|
||||
|
||||
#include "flutter/display_list/effects/image_filters/dl_blur_image_filter.h"
|
||||
#include "flutter/flow/surface_frame.h"
|
||||
#include "flutter/flow/view_slicer.h"
|
||||
#include "flutter/fml/make_copyable.h"
|
||||
#include "flutter/fml/synchronization/count_down_latch.h"
|
||||
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h"
|
||||
#import "flutter/shell/platform/darwin/ios/ios_surface.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// The number of frames the rasterizer task runner will continue
|
||||
// to run on the platform thread after no platform view is rendered.
|
||||
//
|
||||
// Note: this is an arbitrary number.
|
||||
static const int kDefaultMergedLeaseDuration = 10;
|
||||
|
||||
static constexpr NSUInteger kFlutterClippingMaskViewPoolCapacity = 5;
|
||||
|
||||
// Converts a SkMatrix to CATransform3D.
|
||||
//
|
||||
// Certain fields are ignored in CATransform3D since SkMatrix is 3x3 and CATransform3D is 4x4.
|
||||
CATransform3D GetCATransform3DFromSkMatrix(const SkMatrix& matrix) {
|
||||
// Skia only supports 2D transform so we don't map z.
|
||||
CATransform3D transform = CATransform3DIdentity;
|
||||
transform.m11 = matrix.getScaleX();
|
||||
transform.m21 = matrix.getSkewX();
|
||||
transform.m41 = matrix.getTranslateX();
|
||||
transform.m14 = matrix.getPerspX();
|
||||
|
||||
transform.m12 = matrix.getSkewY();
|
||||
transform.m22 = matrix.getScaleY();
|
||||
transform.m42 = matrix.getTranslateY();
|
||||
transform.m24 = matrix.getPerspY();
|
||||
return transform;
|
||||
}
|
||||
|
||||
// Reset the anchor of `layer` to match the transform operation from flow.
|
||||
//
|
||||
// The position of the `layer` should be unchanged after resetting the anchor.
|
||||
void ResetAnchor(CALayer* layer) {
|
||||
// Flow uses (0, 0) to apply transform matrix so we need to match that in Quartz.
|
||||
layer.anchorPoint = CGPointZero;
|
||||
layer.position = CGPointZero;
|
||||
}
|
||||
|
||||
CGRect GetCGRectFromSkRect(const SkRect& clipSkRect) {
|
||||
return CGRectMake(clipSkRect.fLeft, clipSkRect.fTop, clipSkRect.fRight - clipSkRect.fLeft,
|
||||
clipSkRect.fBottom - clipSkRect.fTop);
|
||||
}
|
||||
|
||||
// Determines if the `clip_rect` from a clipRect mutator contains the
|
||||
// `platformview_boundingrect`.
|
||||
//
|
||||
// `clip_rect` is in its own coordinate space. The rect needs to be transformed by
|
||||
// `transform_matrix` to be in the coordinate space where the PlatformView is displayed.
|
||||
//
|
||||
// `platformview_boundingrect` is the final bounding rect of the PlatformView in the coordinate
|
||||
// space where the PlatformView is displayed.
|
||||
bool ClipRectContainsPlatformViewBoundingRect(const SkRect& clip_rect,
|
||||
const SkRect& platformview_boundingrect,
|
||||
const SkMatrix& transform_matrix) {
|
||||
SkRect transformed_rect = transform_matrix.mapRect(clip_rect);
|
||||
return transformed_rect.contains(platformview_boundingrect);
|
||||
}
|
||||
|
||||
// Determines if the `clipRRect` from a clipRRect mutator contains the
|
||||
// `platformview_boundingrect`.
|
||||
//
|
||||
// `clip_rrect` is in its own coordinate space. The rrect needs to be transformed by
|
||||
// `transform_matrix` to be in the coordinate space where the PlatformView is displayed.
|
||||
//
|
||||
// `platformview_boundingrect` is the final bounding rect of the PlatformView in the coordinate
|
||||
// space where the PlatformView is displayed.
|
||||
bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect,
|
||||
const SkRect& platformview_boundingrect,
|
||||
const SkMatrix& transform_matrix) {
|
||||
SkVector upper_left = clip_rrect.radii(SkRRect::Corner::kUpperLeft_Corner);
|
||||
SkVector upper_right = clip_rrect.radii(SkRRect::Corner::kUpperRight_Corner);
|
||||
SkVector lower_right = clip_rrect.radii(SkRRect::Corner::kLowerRight_Corner);
|
||||
SkVector lower_left = clip_rrect.radii(SkRRect::Corner::kLowerLeft_Corner);
|
||||
SkScalar transformed_upper_left_x = transform_matrix.mapRadius(upper_left.x());
|
||||
SkScalar transformed_upper_left_y = transform_matrix.mapRadius(upper_left.y());
|
||||
SkScalar transformed_upper_right_x = transform_matrix.mapRadius(upper_right.x());
|
||||
SkScalar transformed_upper_right_y = transform_matrix.mapRadius(upper_right.y());
|
||||
SkScalar transformed_lower_right_x = transform_matrix.mapRadius(lower_right.x());
|
||||
SkScalar transformed_lower_right_y = transform_matrix.mapRadius(lower_right.y());
|
||||
SkScalar transformed_lower_left_x = transform_matrix.mapRadius(lower_left.x());
|
||||
SkScalar transformed_lower_left_y = transform_matrix.mapRadius(lower_left.y());
|
||||
SkRect transformed_clip_rect = transform_matrix.mapRect(clip_rrect.rect());
|
||||
SkRRect transformed_rrect;
|
||||
SkVector corners[] = {{transformed_upper_left_x, transformed_upper_left_y},
|
||||
{transformed_upper_right_x, transformed_upper_right_y},
|
||||
{transformed_lower_right_x, transformed_lower_right_y},
|
||||
{transformed_lower_left_x, transformed_lower_left_y}};
|
||||
transformed_rrect.setRectRadii(transformed_clip_rect, corners);
|
||||
return transformed_rrect.contains(platformview_boundingrect);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace flutter {
|
||||
|
||||
// Becomes NO if Apple's API changes and blurred backdrop filters cannot be applied.
|
||||
BOOL canApplyBlurBackdrop = YES;
|
||||
|
||||
PlatformViewsController::PlatformViewsController()
|
||||
: layer_pool_(std::make_unique<OverlayLayerPool>()),
|
||||
weak_factory_(std::make_unique<fml::WeakPtrFactory<PlatformViewsController>>(this)) {
|
||||
mask_view_pool_ =
|
||||
[[FlutterClippingMaskViewPool alloc] initWithCapacity:kFlutterClippingMaskViewPoolCapacity];
|
||||
};
|
||||
|
||||
void PlatformViewsController::SetTaskRunner(
|
||||
const fml::RefPtr<fml::TaskRunner>& platform_task_runner) {
|
||||
platform_task_runner_ = platform_task_runner;
|
||||
}
|
||||
|
||||
fml::WeakPtr<flutter::PlatformViewsController> PlatformViewsController::GetWeakPtr() {
|
||||
return weak_factory_->GetWeakPtr();
|
||||
}
|
||||
|
||||
void PlatformViewsController::SetFlutterView(UIView* flutter_view) {
|
||||
flutter_view_ = flutter_view;
|
||||
}
|
||||
|
||||
void PlatformViewsController::SetFlutterViewController(
|
||||
UIViewController<FlutterViewResponder>* flutter_view_controller) {
|
||||
flutter_view_controller_ = flutter_view_controller;
|
||||
}
|
||||
|
||||
UIViewController<FlutterViewResponder>* PlatformViewsController::GetFlutterViewController() {
|
||||
return flutter_view_controller_;
|
||||
}
|
||||
|
||||
void PlatformViewsController::OnMethodCall(FlutterMethodCall* call, FlutterResult result) {
|
||||
if ([[call method] isEqualToString:@"create"]) {
|
||||
OnCreate(call, result);
|
||||
} else if ([[call method] isEqualToString:@"dispose"]) {
|
||||
OnDispose(call, result);
|
||||
} else if ([[call method] isEqualToString:@"acceptGesture"]) {
|
||||
OnAcceptGesture(call, result);
|
||||
} else if ([[call method] isEqualToString:@"rejectGesture"]) {
|
||||
OnRejectGesture(call, result);
|
||||
} else {
|
||||
result(FlutterMethodNotImplemented);
|
||||
}
|
||||
}
|
||||
|
||||
void PlatformViewsController::OnCreate(FlutterMethodCall* call, FlutterResult result) {
|
||||
NSDictionary<NSString*, id>* args = [call arguments];
|
||||
|
||||
int64_t viewId = [args[@"id"] longLongValue];
|
||||
NSString* viewTypeString = args[@"viewType"];
|
||||
std::string viewType(viewTypeString.UTF8String);
|
||||
|
||||
if (platform_views_.count(viewId) != 0) {
|
||||
result([FlutterError errorWithCode:@"recreating_view"
|
||||
message:@"trying to create an already created view"
|
||||
details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]);
|
||||
return;
|
||||
}
|
||||
|
||||
NSObject<FlutterPlatformViewFactory>* factory = factories_[viewType];
|
||||
if (factory == nil) {
|
||||
result([FlutterError
|
||||
errorWithCode:@"unregistered_view_type"
|
||||
message:[NSString stringWithFormat:@"A UIKitView widget is trying to create a "
|
||||
@"PlatformView with an unregistered type: < %@ >",
|
||||
viewTypeString]
|
||||
details:@"If you are the author of the PlatformView, make sure `registerViewFactory` "
|
||||
@"is invoked.\n"
|
||||
@"See: "
|
||||
@"https://docs.flutter.dev/development/platform-integration/"
|
||||
@"platform-views#on-the-platform-side-1 for more details.\n"
|
||||
@"If you are not the author of the PlatformView, make sure to call "
|
||||
@"`GeneratedPluginRegistrant.register`."]);
|
||||
return;
|
||||
}
|
||||
|
||||
id params = nil;
|
||||
if ([factory respondsToSelector:@selector(createArgsCodec)]) {
|
||||
NSObject<FlutterMessageCodec>* codec = [factory createArgsCodec];
|
||||
if (codec != nil && args[@"params"] != nil) {
|
||||
FlutterStandardTypedData* paramsData = args[@"params"];
|
||||
params = [codec decode:paramsData.data];
|
||||
}
|
||||
}
|
||||
|
||||
NSObject<FlutterPlatformView>* embedded_view = [factory createWithFrame:CGRectZero
|
||||
viewIdentifier:viewId
|
||||
arguments:params];
|
||||
UIView* platform_view = [embedded_view view];
|
||||
// Set a unique view identifier, so the platform view can be identified in unit tests.
|
||||
platform_view.accessibilityIdentifier =
|
||||
[NSString stringWithFormat:@"platform_view[%lld]", viewId];
|
||||
|
||||
FlutterTouchInterceptingView* touch_interceptor = [[FlutterTouchInterceptingView alloc]
|
||||
initWithEmbeddedView:platform_view
|
||||
platformViewsController:GetWeakPtr()
|
||||
gestureRecognizersBlockingPolicy:gesture_recognizers_blocking_policies_[viewType]];
|
||||
|
||||
ChildClippingView* clipping_view = [[ChildClippingView alloc] initWithFrame:CGRectZero];
|
||||
[clipping_view addSubview:touch_interceptor];
|
||||
|
||||
platform_views_.emplace(viewId, PlatformViewData{
|
||||
.view = embedded_view, //
|
||||
.touch_interceptor = touch_interceptor, //
|
||||
.root_view = clipping_view //
|
||||
});
|
||||
|
||||
result(nil);
|
||||
}
|
||||
|
||||
void PlatformViewsController::OnDispose(FlutterMethodCall* call, FlutterResult result) {
|
||||
NSNumber* arg = [call arguments];
|
||||
int64_t viewId = [arg longLongValue];
|
||||
|
||||
if (platform_views_.count(viewId) == 0) {
|
||||
result([FlutterError errorWithCode:@"unknown_view"
|
||||
message:@"trying to dispose an unknown"
|
||||
details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]);
|
||||
return;
|
||||
}
|
||||
// We wait for next submitFrame to dispose views.
|
||||
views_to_dispose_.insert(viewId);
|
||||
result(nil);
|
||||
}
|
||||
|
||||
void PlatformViewsController::OnAcceptGesture(FlutterMethodCall* call, FlutterResult result) {
|
||||
NSDictionary<NSString*, id>* args = [call arguments];
|
||||
int64_t viewId = [args[@"id"] longLongValue];
|
||||
|
||||
if (platform_views_.count(viewId) == 0) {
|
||||
result([FlutterError errorWithCode:@"unknown_view"
|
||||
message:@"trying to set gesture state for an unknown view"
|
||||
details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]);
|
||||
return;
|
||||
}
|
||||
|
||||
FlutterTouchInterceptingView* view = platform_views_[viewId].touch_interceptor;
|
||||
[view releaseGesture];
|
||||
|
||||
result(nil);
|
||||
}
|
||||
|
||||
void PlatformViewsController::OnRejectGesture(FlutterMethodCall* call, FlutterResult result) {
|
||||
NSDictionary<NSString*, id>* args = [call arguments];
|
||||
int64_t viewId = [args[@"id"] longLongValue];
|
||||
|
||||
if (platform_views_.count(viewId) == 0) {
|
||||
result([FlutterError errorWithCode:@"unknown_view"
|
||||
message:@"trying to set gesture state for an unknown view"
|
||||
details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]);
|
||||
return;
|
||||
}
|
||||
|
||||
FlutterTouchInterceptingView* view = platform_views_[viewId].touch_interceptor;
|
||||
[view blockGesture];
|
||||
|
||||
result(nil);
|
||||
}
|
||||
|
||||
void PlatformViewsController::RegisterViewFactory(
|
||||
NSObject<FlutterPlatformViewFactory>* factory,
|
||||
NSString* factoryId,
|
||||
FlutterPlatformViewGestureRecognizersBlockingPolicy gestureRecognizerBlockingPolicy) {
|
||||
std::string idString([factoryId UTF8String]);
|
||||
FML_CHECK(factories_.count(idString) == 0);
|
||||
factories_[idString] = factory;
|
||||
gesture_recognizers_blocking_policies_[idString] = gestureRecognizerBlockingPolicy;
|
||||
}
|
||||
|
||||
void PlatformViewsController::BeginFrame(SkISize frame_size) {
|
||||
ResetFrameState();
|
||||
frame_size_ = frame_size;
|
||||
}
|
||||
|
||||
void PlatformViewsController::CancelFrame() {
|
||||
ResetFrameState();
|
||||
}
|
||||
|
||||
PostPrerollResult PlatformViewsController::PostPrerollAction(
|
||||
const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger,
|
||||
bool impeller_enabled) {
|
||||
// TODO(jonahwilliams): remove this once Software backend is removed for iOS Sim.
|
||||
#ifdef FML_OS_IOS_SIMULATOR
|
||||
const bool merge_threads = true;
|
||||
#else
|
||||
const bool merge_threads = !impeller_enabled;
|
||||
#endif // FML_OS_IOS_SIMULATOR
|
||||
|
||||
if (merge_threads) {
|
||||
if (composition_order_.empty()) {
|
||||
return PostPrerollResult::kSuccess;
|
||||
}
|
||||
if (!raster_thread_merger->IsMerged()) {
|
||||
// The raster thread merger may be disabled if the rasterizer is being
|
||||
// created or teared down.
|
||||
//
|
||||
// In such cases, the current frame is dropped, and a new frame is attempted
|
||||
// with the same layer tree.
|
||||
//
|
||||
// Eventually, the frame is submitted once this method returns `kSuccess`.
|
||||
// At that point, the raster tasks are handled on the platform thread.
|
||||
CancelFrame();
|
||||
return PostPrerollResult::kSkipAndRetryFrame;
|
||||
}
|
||||
// If the post preroll action is successful, we will display platform views in the current
|
||||
// frame. In order to sync the rendering of the platform views (quartz) with skia's rendering,
|
||||
// We need to begin an explicit CATransaction. This transaction needs to be submitted
|
||||
// after the current frame is submitted.
|
||||
raster_thread_merger->ExtendLeaseTo(kDefaultMergedLeaseDuration);
|
||||
}
|
||||
return PostPrerollResult::kSuccess;
|
||||
}
|
||||
|
||||
void PlatformViewsController::EndFrame(
|
||||
bool should_resubmit_frame,
|
||||
const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger,
|
||||
bool impeller_enabled) {
|
||||
#if FML_OS_IOS_SIMULATOR
|
||||
bool run_check = true;
|
||||
#else
|
||||
bool run_check = !impeller_enabled;
|
||||
#endif // FML_OS_IOS_SIMULATOR
|
||||
if (run_check && should_resubmit_frame) {
|
||||
raster_thread_merger->MergeWithLease(kDefaultMergedLeaseDuration);
|
||||
}
|
||||
}
|
||||
|
||||
void PlatformViewsController::PushFilterToVisitedPlatformViews(
|
||||
const std::shared_ptr<DlImageFilter>& filter,
|
||||
const SkRect& filter_rect) {
|
||||
for (int64_t id : visited_platform_views_) {
|
||||
EmbeddedViewParams params = current_composition_params_[id];
|
||||
params.PushImageFilter(filter, filter_rect);
|
||||
current_composition_params_[id] = params;
|
||||
}
|
||||
}
|
||||
|
||||
void PlatformViewsController::PrerollCompositeEmbeddedView(
|
||||
int64_t view_id,
|
||||
std::unique_ptr<EmbeddedViewParams> params) {
|
||||
SkRect view_bounds = SkRect::Make(frame_size_);
|
||||
std::unique_ptr<EmbedderViewSlice> view;
|
||||
view = std::make_unique<DisplayListEmbedderViewSlice>(view_bounds);
|
||||
slices_.insert_or_assign(view_id, std::move(view));
|
||||
|
||||
composition_order_.push_back(view_id);
|
||||
|
||||
if (current_composition_params_.count(view_id) == 1 &&
|
||||
current_composition_params_[view_id] == *params.get()) {
|
||||
// Do nothing if the params didn't change.
|
||||
return;
|
||||
}
|
||||
current_composition_params_[view_id] = EmbeddedViewParams(*params.get());
|
||||
views_to_recomposite_.insert(view_id);
|
||||
}
|
||||
|
||||
size_t PlatformViewsController::EmbeddedViewCount() const {
|
||||
return composition_order_.size();
|
||||
}
|
||||
|
||||
size_t PlatformViewsController::LayerPoolSize() const {
|
||||
return layer_pool_->size();
|
||||
}
|
||||
|
||||
UIView* PlatformViewsController::GetPlatformViewByID(int64_t view_id) {
|
||||
return [GetFlutterTouchInterceptingViewByID(view_id) embeddedView];
|
||||
}
|
||||
|
||||
FlutterTouchInterceptingView* PlatformViewsController::GetFlutterTouchInterceptingViewByID(
|
||||
int64_t view_id) {
|
||||
if (platform_views_.empty()) {
|
||||
return nil;
|
||||
}
|
||||
return platform_views_[view_id].touch_interceptor;
|
||||
}
|
||||
|
||||
long PlatformViewsController::FindFirstResponderPlatformViewId() {
|
||||
for (auto const& [id, platform_view_data] : platform_views_) {
|
||||
UIView* root_view = platform_view_data.root_view;
|
||||
if (root_view.flt_hasFirstResponderInViewHierarchySubtree) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void PlatformViewsController::ClipViewSetMaskView(UIView* clipView) {
|
||||
FML_DCHECK([[NSThread currentThread] isMainThread]);
|
||||
if (clipView.maskView) {
|
||||
return;
|
||||
}
|
||||
CGRect frame =
|
||||
CGRectMake(-clipView.frame.origin.x, -clipView.frame.origin.y,
|
||||
CGRectGetWidth(flutter_view_.bounds), CGRectGetHeight(flutter_view_.bounds));
|
||||
clipView.maskView = [mask_view_pool_ getMaskViewWithFrame:frame];
|
||||
}
|
||||
|
||||
// This method is only called when the `embedded_view` needs to be re-composited at the current
|
||||
// frame. See: `CompositeWithParams` for details.
|
||||
void PlatformViewsController::ApplyMutators(const MutatorsStack& mutators_stack,
|
||||
UIView* embedded_view,
|
||||
const SkRect& bounding_rect) {
|
||||
if (flutter_view_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
ResetAnchor(embedded_view.layer);
|
||||
ChildClippingView* clipView = (ChildClippingView*)embedded_view.superview;
|
||||
|
||||
SkMatrix transformMatrix;
|
||||
NSMutableArray* blurFilters = [[NSMutableArray alloc] init];
|
||||
FML_DCHECK(!clipView.maskView ||
|
||||
[clipView.maskView isKindOfClass:[FlutterClippingMaskView class]]);
|
||||
if (clipView.maskView) {
|
||||
[mask_view_pool_ insertViewToPoolIfNeeded:(FlutterClippingMaskView*)(clipView.maskView)];
|
||||
clipView.maskView = nil;
|
||||
}
|
||||
CGFloat screenScale = [UIScreen mainScreen].scale;
|
||||
auto iter = mutators_stack.Begin();
|
||||
while (iter != mutators_stack.End()) {
|
||||
switch ((*iter)->GetType()) {
|
||||
case kTransform: {
|
||||
transformMatrix.preConcat((*iter)->GetMatrix());
|
||||
break;
|
||||
}
|
||||
case kClipRect: {
|
||||
if (ClipRectContainsPlatformViewBoundingRect((*iter)->GetRect(), bounding_rect,
|
||||
transformMatrix)) {
|
||||
break;
|
||||
}
|
||||
ClipViewSetMaskView(clipView);
|
||||
[(FlutterClippingMaskView*)clipView.maskView clipRect:(*iter)->GetRect()
|
||||
matrix:transformMatrix];
|
||||
break;
|
||||
}
|
||||
case kClipRRect: {
|
||||
if (ClipRRectContainsPlatformViewBoundingRect((*iter)->GetRRect(), bounding_rect,
|
||||
transformMatrix)) {
|
||||
break;
|
||||
}
|
||||
ClipViewSetMaskView(clipView);
|
||||
[(FlutterClippingMaskView*)clipView.maskView clipRRect:(*iter)->GetRRect()
|
||||
matrix:transformMatrix];
|
||||
break;
|
||||
}
|
||||
case kClipPath: {
|
||||
// TODO(cyanglaz): Find a way to pre-determine if path contains the PlatformView boudning
|
||||
// rect. See `ClipRRectContainsPlatformViewBoundingRect`.
|
||||
// https://github.com/flutter/flutter/issues/118650
|
||||
ClipViewSetMaskView(clipView);
|
||||
[(FlutterClippingMaskView*)clipView.maskView clipPath:(*iter)->GetPath()
|
||||
matrix:transformMatrix];
|
||||
break;
|
||||
}
|
||||
case kOpacity:
|
||||
embedded_view.alpha = (*iter)->GetAlphaFloat() * embedded_view.alpha;
|
||||
break;
|
||||
case kBackdropFilter: {
|
||||
// Only support DlBlurImageFilter for BackdropFilter.
|
||||
if (!canApplyBlurBackdrop || !(*iter)->GetFilterMutation().GetFilter().asBlur()) {
|
||||
break;
|
||||
}
|
||||
CGRect filterRect = GetCGRectFromSkRect((*iter)->GetFilterMutation().GetFilterRect());
|
||||
// `filterRect` is in global coordinates. We need to convert to local space.
|
||||
filterRect = CGRectApplyAffineTransform(
|
||||
filterRect, CGAffineTransformMakeScale(1 / screenScale, 1 / screenScale));
|
||||
// `filterRect` reprents the rect that should be filtered inside the `flutter_view_`.
|
||||
// The `PlatformViewFilter` needs the frame inside the `clipView` that needs to be
|
||||
// filtered.
|
||||
if (CGRectIsNull(CGRectIntersection(filterRect, clipView.frame))) {
|
||||
break;
|
||||
}
|
||||
CGRect intersection = CGRectIntersection(filterRect, clipView.frame);
|
||||
CGRect frameInClipView = [flutter_view_ convertRect:intersection toView:clipView];
|
||||
// sigma_x is arbitrarily chosen as the radius value because Quartz sets
|
||||
// sigma_x and sigma_y equal to each other. DlBlurImageFilter's Tile Mode
|
||||
// is not supported in Quartz's gaussianBlur CAFilter, so it is not used
|
||||
// to blur the PlatformView.
|
||||
CGFloat blurRadius = (*iter)->GetFilterMutation().GetFilter().asBlur()->sigma_x();
|
||||
UIVisualEffectView* visualEffectView = [[UIVisualEffectView alloc]
|
||||
initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]];
|
||||
PlatformViewFilter* filter = [[PlatformViewFilter alloc] initWithFrame:frameInClipView
|
||||
blurRadius:blurRadius
|
||||
visualEffectView:visualEffectView];
|
||||
if (!filter) {
|
||||
canApplyBlurBackdrop = NO;
|
||||
} else {
|
||||
[blurFilters addObject:filter];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
|
||||
if (canApplyBlurBackdrop) {
|
||||
[clipView applyBlurBackdropFilters:blurFilters];
|
||||
}
|
||||
|
||||
// The UIKit frame is set based on the logical resolution (points) instead of physical.
|
||||
// (https://developer.apple.com/library/archive/documentation/DeviceInformation/Reference/iOSDeviceCompatibility/Displays/Displays.html).
|
||||
// However, flow is based on the physical resolution. For example, 1000 pixels in flow equals
|
||||
// 500 points in UIKit for devices that has screenScale of 2. We need to scale the transformMatrix
|
||||
// down to the logical resoltion before applying it to the layer of PlatformView.
|
||||
transformMatrix.postScale(1 / screenScale, 1 / screenScale);
|
||||
|
||||
// Reverse the offset of the clipView.
|
||||
// The clipView's frame includes the final translate of the final transform matrix.
|
||||
// Thus, this translate needs to be reversed so the platform view can layout at the correct
|
||||
// offset.
|
||||
//
|
||||
// Note that the transforms are not applied to the clipping paths because clipping paths happen on
|
||||
// the mask view, whose origin is always (0,0) to the flutter_view.
|
||||
transformMatrix.postTranslate(-clipView.frame.origin.x, -clipView.frame.origin.y);
|
||||
|
||||
embedded_view.layer.transform = GetCATransform3DFromSkMatrix(transformMatrix);
|
||||
}
|
||||
|
||||
// Composite the PlatformView with `view_id`.
|
||||
//
|
||||
// Every frame, during the paint traversal of the layer tree, this method is called for all
|
||||
// the PlatformViews in `views_to_recomposite_`.
|
||||
//
|
||||
// Note that `views_to_recomposite_` does not represent all the views in the view hierarchy,
|
||||
// if a PlatformView does not change its composition parameter from last frame, it is not
|
||||
// included in the `views_to_recomposite_`.
|
||||
void PlatformViewsController::CompositeWithParams(int64_t view_id,
|
||||
const EmbeddedViewParams& params) {
|
||||
/// TODO(https://github.com/flutter/flutter/issues/109700)
|
||||
CGRect frame = CGRectMake(0, 0, params.sizePoints().width(), params.sizePoints().height());
|
||||
FlutterTouchInterceptingView* touchInterceptor = platform_views_[view_id].touch_interceptor;
|
||||
touchInterceptor.layer.transform = CATransform3DIdentity;
|
||||
touchInterceptor.frame = frame;
|
||||
touchInterceptor.alpha = 1;
|
||||
|
||||
const MutatorsStack& mutatorStack = params.mutatorsStack();
|
||||
UIView* clippingView = platform_views_[view_id].root_view;
|
||||
// The frame of the clipping view should be the final bounding rect.
|
||||
// Because the translate matrix in the Mutator Stack also includes the offset,
|
||||
// when we apply the transforms matrix in |ApplyMutators|, we need
|
||||
// to remember to do a reverse translate.
|
||||
const SkRect& rect = params.finalBoundingRect();
|
||||
CGFloat screenScale = [UIScreen mainScreen].scale;
|
||||
clippingView.frame = CGRectMake(rect.x() / screenScale, rect.y() / screenScale,
|
||||
rect.width() / screenScale, rect.height() / screenScale);
|
||||
ApplyMutators(mutatorStack, touchInterceptor, rect);
|
||||
}
|
||||
|
||||
DlCanvas* PlatformViewsController::CompositeEmbeddedView(int64_t view_id) {
|
||||
return slices_[view_id]->canvas();
|
||||
}
|
||||
|
||||
void PlatformViewsController::Reset() {
|
||||
// Reset will only be called from the raster thread or a merged raster/platform thread.
|
||||
// platform_views_ must only be modified on the platform thread, and any operations that
|
||||
// read or modify platform views should occur there.
|
||||
fml::TaskRunner::RunNowOrPostTask(platform_task_runner_,
|
||||
[&, composition_order = composition_order_]() {
|
||||
for (int64_t view_id : composition_order_) {
|
||||
[platform_views_[view_id].root_view removeFromSuperview];
|
||||
}
|
||||
platform_views_.clear();
|
||||
});
|
||||
|
||||
composition_order_.clear();
|
||||
slices_.clear();
|
||||
current_composition_params_.clear();
|
||||
views_to_recomposite_.clear();
|
||||
layer_pool_->RecycleLayers();
|
||||
visited_platform_views_.clear();
|
||||
}
|
||||
|
||||
bool PlatformViewsController::SubmitFrame(GrDirectContext* gr_context,
|
||||
const std::shared_ptr<IOSContext>& ios_context,
|
||||
std::unique_ptr<SurfaceFrame> background_frame) {
|
||||
TRACE_EVENT0("flutter", "PlatformViewsController::SubmitFrame");
|
||||
|
||||
// No platform views to render; we're done.
|
||||
if (flutter_view_ == nullptr || (composition_order_.empty() && !had_platform_views_)) {
|
||||
had_platform_views_ = false;
|
||||
return background_frame->Submit();
|
||||
}
|
||||
had_platform_views_ = !composition_order_.empty();
|
||||
|
||||
bool did_encode = true;
|
||||
LayersMap platform_view_layers;
|
||||
std::vector<std::unique_ptr<SurfaceFrame>> surface_frames;
|
||||
surface_frames.reserve(composition_order_.size());
|
||||
std::unordered_map<int64_t, SkRect> view_rects;
|
||||
|
||||
for (int64_t view_id : composition_order_) {
|
||||
view_rects[view_id] = current_composition_params_[view_id].finalBoundingRect();
|
||||
}
|
||||
|
||||
std::unordered_map<int64_t, SkRect> overlay_layers =
|
||||
SliceViews(background_frame->Canvas(), composition_order_, slices_, view_rects);
|
||||
|
||||
size_t required_overlay_layers = 0;
|
||||
for (int64_t view_id : composition_order_) {
|
||||
std::unordered_map<int64_t, SkRect>::const_iterator overlay = overlay_layers.find(view_id);
|
||||
if (overlay == overlay_layers.end()) {
|
||||
continue;
|
||||
}
|
||||
required_overlay_layers++;
|
||||
}
|
||||
|
||||
// If there are not sufficient overlay layers, we must construct them on the platform
|
||||
// thread, at least until we've refactored iOS surface creation to use IOSurfaces
|
||||
// instead of CALayers.
|
||||
CreateMissingOverlays(gr_context, ios_context, required_overlay_layers);
|
||||
|
||||
int64_t overlay_id = 0;
|
||||
for (int64_t view_id : composition_order_) {
|
||||
std::unordered_map<int64_t, SkRect>::const_iterator overlay = overlay_layers.find(view_id);
|
||||
if (overlay == overlay_layers.end()) {
|
||||
continue;
|
||||
}
|
||||
std::shared_ptr<OverlayLayer> layer = GetExistingLayer();
|
||||
if (!layer) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::unique_ptr<SurfaceFrame> frame = layer->surface->AcquireFrame(frame_size_);
|
||||
// If frame is null, AcquireFrame already printed out an error message.
|
||||
if (!frame) {
|
||||
continue;
|
||||
}
|
||||
DlCanvas* overlay_canvas = frame->Canvas();
|
||||
int restore_count = overlay_canvas->GetSaveCount();
|
||||
overlay_canvas->Save();
|
||||
overlay_canvas->ClipRect(overlay->second);
|
||||
overlay_canvas->Clear(DlColor::kTransparent());
|
||||
slices_[view_id]->render_into(overlay_canvas);
|
||||
overlay_canvas->RestoreToCount(restore_count);
|
||||
|
||||
// This flutter view is never the last in a frame, since we always submit the
|
||||
// underlay view last.
|
||||
frame->set_submit_info({.frame_boundary = false, .present_with_transaction = true});
|
||||
layer->did_submit_last_frame = frame->Encode();
|
||||
|
||||
did_encode &= layer->did_submit_last_frame;
|
||||
platform_view_layers[view_id] = LayerData{
|
||||
.rect = overlay->second, //
|
||||
.view_id = view_id, //
|
||||
.overlay_id = overlay_id, //
|
||||
.layer = layer //
|
||||
};
|
||||
surface_frames.push_back(std::move(frame));
|
||||
overlay_id++;
|
||||
}
|
||||
|
||||
auto previous_submit_info = background_frame->submit_info();
|
||||
background_frame->set_submit_info({
|
||||
.frame_damage = previous_submit_info.frame_damage,
|
||||
.buffer_damage = previous_submit_info.buffer_damage,
|
||||
.present_with_transaction = true,
|
||||
});
|
||||
background_frame->Encode();
|
||||
surface_frames.push_back(std::move(background_frame));
|
||||
|
||||
// Mark all layers as available, so they can be used in the next frame.
|
||||
std::vector<std::shared_ptr<OverlayLayer>> unused_layers = layer_pool_->RemoveUnusedLayers();
|
||||
layer_pool_->RecycleLayers();
|
||||
|
||||
auto task = [&, //
|
||||
platform_view_layers = std::move(platform_view_layers), //
|
||||
current_composition_params = current_composition_params_, //
|
||||
views_to_recomposite = views_to_recomposite_, //
|
||||
composition_order = composition_order_, //
|
||||
unused_layers = std::move(unused_layers), //
|
||||
surface_frames = std::move(surface_frames) //
|
||||
]() mutable {
|
||||
PerformSubmit(platform_view_layers, //
|
||||
current_composition_params, //
|
||||
views_to_recomposite, //
|
||||
composition_order, //
|
||||
unused_layers, //
|
||||
surface_frames //
|
||||
);
|
||||
};
|
||||
|
||||
fml::TaskRunner::RunNowOrPostTask(platform_task_runner_, fml::MakeCopyable(std::move(task)));
|
||||
|
||||
return did_encode;
|
||||
}
|
||||
|
||||
void PlatformViewsController::CreateMissingOverlays(GrDirectContext* gr_context,
|
||||
const std::shared_ptr<IOSContext>& ios_context,
|
||||
size_t required_overlay_layers) {
|
||||
TRACE_EVENT0("flutter", "PlatformViewsController::CreateMissingLayers");
|
||||
|
||||
if (required_overlay_layers <= layer_pool_->size()) {
|
||||
return;
|
||||
}
|
||||
auto missing_layer_count = required_overlay_layers - layer_pool_->size();
|
||||
|
||||
// If the raster thread isn't merged, create layers on the platform thread and block until
|
||||
// complete.
|
||||
auto latch = std::make_shared<fml::CountDownLatch>(1u);
|
||||
fml::TaskRunner::RunNowOrPostTask(platform_task_runner_, [&]() {
|
||||
for (auto i = 0u; i < missing_layer_count; i++) {
|
||||
CreateLayer(gr_context, //
|
||||
ios_context, //
|
||||
((FlutterView*)flutter_view_).pixelFormat //
|
||||
);
|
||||
}
|
||||
latch->CountDown();
|
||||
});
|
||||
if (![[NSThread currentThread] isMainThread]) {
|
||||
latch->Wait();
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the buffers and mutate the platform views in CATransaction on the platform thread.
|
||||
void PlatformViewsController::PerformSubmit(
|
||||
const LayersMap& platform_view_layers,
|
||||
std::unordered_map<int64_t, EmbeddedViewParams>& current_composition_params,
|
||||
const std::unordered_set<int64_t>& views_to_recomposite,
|
||||
const std::vector<int64_t>& composition_order,
|
||||
const std::vector<std::shared_ptr<OverlayLayer>>& unused_layers,
|
||||
const std::vector<std::unique_ptr<SurfaceFrame>>& surface_frames) {
|
||||
TRACE_EVENT0("flutter", "PlatformViewsController::PerformSubmit");
|
||||
FML_DCHECK([[NSThread currentThread] isMainThread]);
|
||||
|
||||
[CATransaction begin];
|
||||
|
||||
// Configure Flutter overlay views.
|
||||
for (const auto& [view_id, layer_data] : platform_view_layers) {
|
||||
layer_data.layer->UpdateViewState(flutter_view_, //
|
||||
layer_data.rect, //
|
||||
layer_data.view_id, //
|
||||
layer_data.overlay_id //
|
||||
);
|
||||
}
|
||||
|
||||
// Dispose unused Flutter Views.
|
||||
for (auto& view : GetViewsToDispose()) {
|
||||
[view removeFromSuperview];
|
||||
}
|
||||
|
||||
// Composite Platform Views.
|
||||
for (int64_t view_id : views_to_recomposite) {
|
||||
CompositeWithParams(view_id, current_composition_params[view_id]);
|
||||
}
|
||||
|
||||
// Present callbacks.
|
||||
for (const auto& frame : surface_frames) {
|
||||
frame->Submit();
|
||||
}
|
||||
|
||||
// If a layer was allocated in the previous frame, but it's not used in the current frame,
|
||||
// then it can be removed from the scene.
|
||||
RemoveUnusedLayers(unused_layers, composition_order);
|
||||
|
||||
// Organize the layers by their z indexes.
|
||||
BringLayersIntoView(platform_view_layers, composition_order);
|
||||
|
||||
[CATransaction commit];
|
||||
}
|
||||
|
||||
void PlatformViewsController::BringLayersIntoView(const LayersMap& layer_map,
|
||||
const std::vector<int64_t>& composition_order) {
|
||||
FML_DCHECK(flutter_view_);
|
||||
UIView* flutter_view = flutter_view_;
|
||||
|
||||
previous_composition_order_.clear();
|
||||
NSMutableArray* desired_platform_subviews = [NSMutableArray array];
|
||||
for (int64_t platform_view_id : composition_order) {
|
||||
previous_composition_order_.push_back(platform_view_id);
|
||||
UIView* platform_view_root = platform_views_[platform_view_id].root_view;
|
||||
if (platform_view_root != nil) {
|
||||
[desired_platform_subviews addObject:platform_view_root];
|
||||
}
|
||||
|
||||
auto maybe_layer_data = layer_map.find(platform_view_id);
|
||||
if (maybe_layer_data != layer_map.end()) {
|
||||
auto view = maybe_layer_data->second.layer->overlay_view_wrapper;
|
||||
if (view != nil) {
|
||||
[desired_platform_subviews addObject:view];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NSSet* desired_platform_subviews_set = [NSSet setWithArray:desired_platform_subviews];
|
||||
NSArray* existing_platform_subviews = [flutter_view.subviews
|
||||
filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id object,
|
||||
NSDictionary* bindings) {
|
||||
return [desired_platform_subviews_set containsObject:object];
|
||||
}]];
|
||||
|
||||
// Manipulate view hierarchy only if needed, to address a performance issue where
|
||||
// `BringLayersIntoView` is called even when view hierarchy stays the same.
|
||||
// See: https://github.com/flutter/flutter/issues/121833
|
||||
// TODO(hellohuanlin): investigate if it is possible to skip unnecessary BringLayersIntoView.
|
||||
if (![desired_platform_subviews isEqualToArray:existing_platform_subviews]) {
|
||||
for (UIView* subview in desired_platform_subviews) {
|
||||
// `addSubview` will automatically reorder subview if it is already added.
|
||||
[flutter_view addSubview:subview];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<OverlayLayer> PlatformViewsController::GetExistingLayer() {
|
||||
return layer_pool_->GetNextLayer();
|
||||
}
|
||||
|
||||
void PlatformViewsController::CreateLayer(GrDirectContext* gr_context,
|
||||
const std::shared_ptr<IOSContext>& ios_context,
|
||||
MTLPixelFormat pixel_format) {
|
||||
layer_pool_->CreateLayer(gr_context, ios_context, pixel_format);
|
||||
}
|
||||
|
||||
void PlatformViewsController::RemoveUnusedLayers(
|
||||
const std::vector<std::shared_ptr<OverlayLayer>>& unused_layers,
|
||||
const std::vector<int64_t>& composition_order) {
|
||||
for (const std::shared_ptr<OverlayLayer>& layer : unused_layers) {
|
||||
[layer->overlay_view_wrapper removeFromSuperview];
|
||||
}
|
||||
|
||||
std::unordered_set<int64_t> composition_order_set;
|
||||
for (int64_t view_id : composition_order) {
|
||||
composition_order_set.insert(view_id);
|
||||
}
|
||||
// Remove unused platform views.
|
||||
for (int64_t view_id : previous_composition_order_) {
|
||||
if (composition_order_set.find(view_id) == composition_order_set.end()) {
|
||||
UIView* platform_view_root = platform_views_[view_id].root_view;
|
||||
[platform_view_root removeFromSuperview];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<UIView*> PlatformViewsController::GetViewsToDispose() {
|
||||
std::vector<UIView*> views;
|
||||
if (views_to_dispose_.empty()) {
|
||||
return views;
|
||||
}
|
||||
|
||||
std::unordered_set<int64_t> views_to_composite(composition_order_.begin(),
|
||||
composition_order_.end());
|
||||
std::unordered_set<int64_t> views_to_delay_dispose;
|
||||
for (int64_t viewId : views_to_dispose_) {
|
||||
if (views_to_composite.count(viewId)) {
|
||||
views_to_delay_dispose.insert(viewId);
|
||||
continue;
|
||||
}
|
||||
UIView* root_view = platform_views_[viewId].root_view;
|
||||
views.push_back(root_view);
|
||||
current_composition_params_.erase(viewId);
|
||||
views_to_recomposite_.erase(viewId);
|
||||
platform_views_.erase(viewId);
|
||||
}
|
||||
views_to_dispose_ = std::move(views_to_delay_dispose);
|
||||
return views;
|
||||
}
|
||||
|
||||
void PlatformViewsController::ResetFrameState() {
|
||||
slices_.clear();
|
||||
composition_order_.clear();
|
||||
visited_platform_views_.clear();
|
||||
}
|
||||
|
||||
} // namespace flutter
|
@ -13,14 +13,14 @@ namespace flutter {
|
||||
class IOSExternalViewEmbedder : public ExternalViewEmbedder {
|
||||
public:
|
||||
IOSExternalViewEmbedder(
|
||||
__weak FlutterPlatformViewsController* platform_views_controller,
|
||||
const std::shared_ptr<PlatformViewsController>& platform_views_controller,
|
||||
const std::shared_ptr<IOSContext>& context);
|
||||
|
||||
// |ExternalViewEmbedder|
|
||||
virtual ~IOSExternalViewEmbedder() override;
|
||||
|
||||
private:
|
||||
__weak FlutterPlatformViewsController* platform_views_controller_;
|
||||
const std::shared_ptr<PlatformViewsController>& platform_views_controller_;
|
||||
std::shared_ptr<IOSContext> ios_context_;
|
||||
|
||||
// |ExternalViewEmbedder|
|
||||
|
@ -12,7 +12,7 @@ FLUTTER_ASSERT_ARC
|
||||
namespace flutter {
|
||||
|
||||
IOSExternalViewEmbedder::IOSExternalViewEmbedder(
|
||||
__weak FlutterPlatformViewsController* platform_views_controller,
|
||||
const std::shared_ptr<PlatformViewsController>& platform_views_controller,
|
||||
const std::shared_ptr<IOSContext>& context)
|
||||
: platform_views_controller_(platform_views_controller), ios_context_(context) {
|
||||
FML_CHECK(ios_context_);
|
||||
@ -31,7 +31,7 @@ DlCanvas* IOSExternalViewEmbedder::GetRootCanvas() {
|
||||
void IOSExternalViewEmbedder::CancelFrame() {
|
||||
TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::CancelFrame");
|
||||
FML_CHECK(platform_views_controller_);
|
||||
[platform_views_controller_ cancelFrame];
|
||||
platform_views_controller_->CancelFrame();
|
||||
}
|
||||
|
||||
// |ExternalViewEmbedder|
|
||||
@ -42,7 +42,7 @@ void IOSExternalViewEmbedder::BeginFrame(
|
||||
// |ExternalViewEmbedder|
|
||||
void IOSExternalViewEmbedder::PrepareFlutterView(SkISize frame_size, double device_pixel_ratio) {
|
||||
FML_CHECK(platform_views_controller_);
|
||||
[platform_views_controller_ beginFrameWithSize:frame_size];
|
||||
platform_views_controller_->BeginFrame(frame_size);
|
||||
}
|
||||
|
||||
// |ExternalViewEmbedder|
|
||||
@ -51,7 +51,7 @@ void IOSExternalViewEmbedder::PrerollCompositeEmbeddedView(
|
||||
std::unique_ptr<EmbeddedViewParams> params) {
|
||||
TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::PrerollCompositeEmbeddedView");
|
||||
FML_CHECK(platform_views_controller_);
|
||||
[platform_views_controller_ prerollCompositeEmbeddedView:view_id withParams:std::move(params)];
|
||||
platform_views_controller_->PrerollCompositeEmbeddedView(view_id, std::move(params));
|
||||
}
|
||||
|
||||
// |ExternalViewEmbedder|
|
||||
@ -59,10 +59,8 @@ PostPrerollResult IOSExternalViewEmbedder::PostPrerollAction(
|
||||
const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger) {
|
||||
TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::PostPrerollAction");
|
||||
FML_CHECK(platform_views_controller_);
|
||||
BOOL impeller_enabled = ios_context_->GetBackend() != IOSRenderingBackend::kSkia;
|
||||
PostPrerollResult result =
|
||||
[platform_views_controller_ postPrerollActionWithThreadMerger:raster_thread_merger
|
||||
impellerEnabled:impeller_enabled];
|
||||
PostPrerollResult result = platform_views_controller_->PostPrerollAction(
|
||||
raster_thread_merger, ios_context_->GetBackend() != IOSRenderingBackend::kSkia);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -70,7 +68,7 @@ PostPrerollResult IOSExternalViewEmbedder::PostPrerollAction(
|
||||
DlCanvas* IOSExternalViewEmbedder::CompositeEmbeddedView(int64_t view_id) {
|
||||
TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::CompositeEmbeddedView");
|
||||
FML_CHECK(platform_views_controller_);
|
||||
return [platform_views_controller_ compositeEmbeddedViewWithId:view_id];
|
||||
return platform_views_controller_->CompositeEmbeddedView(view_id);
|
||||
}
|
||||
|
||||
// |ExternalViewEmbedder|
|
||||
@ -85,9 +83,7 @@ void IOSExternalViewEmbedder::SubmitFlutterView(
|
||||
// Properly support multi-view in the future.
|
||||
FML_DCHECK(flutter_view_id == kFlutterImplicitViewId);
|
||||
FML_CHECK(platform_views_controller_);
|
||||
[platform_views_controller_ submitFrame:std::move(frame)
|
||||
withIosContext:ios_context_
|
||||
grContext:context];
|
||||
platform_views_controller_->SubmitFrame(context, ios_context_, std::move(frame));
|
||||
TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::DidSubmitFrame");
|
||||
}
|
||||
|
||||
@ -96,10 +92,8 @@ void IOSExternalViewEmbedder::EndFrame(
|
||||
bool should_resubmit_frame,
|
||||
const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger) {
|
||||
TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::EndFrame");
|
||||
BOOL impeller_enabled = ios_context_->GetBackend() != IOSRenderingBackend::kSkia;
|
||||
[platform_views_controller_ endFrameWithResubmit:should_resubmit_frame
|
||||
threadMerger:raster_thread_merger
|
||||
impellerEnabled:impeller_enabled];
|
||||
platform_views_controller_->EndFrame(should_resubmit_frame, raster_thread_merger,
|
||||
ios_context_->GetBackend() != IOSRenderingBackend::kSkia);
|
||||
}
|
||||
|
||||
// |ExternalViewEmbedder|
|
||||
@ -116,12 +110,12 @@ bool IOSExternalViewEmbedder::SupportsDynamicThreadMerging() {
|
||||
void IOSExternalViewEmbedder::PushFilterToVisitedPlatformViews(
|
||||
const std::shared_ptr<DlImageFilter>& filter,
|
||||
const SkRect& filter_rect) {
|
||||
[platform_views_controller_ pushFilterToVisitedPlatformViews:filter withRect:filter_rect];
|
||||
platform_views_controller_->PushFilterToVisitedPlatformViews(filter, filter_rect);
|
||||
}
|
||||
|
||||
// |ExternalViewEmbedder|
|
||||
void IOSExternalViewEmbedder::PushVisitedPlatformView(int64_t view_id) {
|
||||
[platform_views_controller_ pushVisitedPlatformViewId:view_id];
|
||||
platform_views_controller_->PushVisitedPlatformView(view_id);
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
|
@ -40,13 +40,13 @@ class PlatformViewIOS final : public PlatformView {
|
||||
public:
|
||||
PlatformViewIOS(PlatformView::Delegate& delegate,
|
||||
const std::shared_ptr<IOSContext>& context,
|
||||
__weak FlutterPlatformViewsController* platform_views_controller,
|
||||
const std::shared_ptr<PlatformViewsController>& platform_views_controller,
|
||||
const flutter::TaskRunners& task_runners);
|
||||
|
||||
explicit PlatformViewIOS(
|
||||
PlatformView::Delegate& delegate,
|
||||
IOSRenderingAPI rendering_api,
|
||||
__weak FlutterPlatformViewsController* platform_views_controller,
|
||||
const std::shared_ptr<PlatformViewsController>& platform_views_controller,
|
||||
const flutter::TaskRunners& task_runners,
|
||||
const std::shared_ptr<fml::ConcurrentTaskRunner>& worker_task_runner,
|
||||
const std::shared_ptr<const fml::SyncSwitch>& is_gpu_disabled_sync_switch);
|
||||
@ -137,7 +137,7 @@ class PlatformViewIOS final : public PlatformView {
|
||||
std::mutex ios_surface_mutex_;
|
||||
std::unique_ptr<IOSSurface> ios_surface_;
|
||||
std::shared_ptr<IOSContext> ios_context_;
|
||||
__weak FlutterPlatformViewsController* platform_views_controller_;
|
||||
const std::shared_ptr<PlatformViewsController>& platform_views_controller_;
|
||||
AccessibilityBridgeManager accessibility_bridge_;
|
||||
ScopedObserver dealloc_view_controller_observer_;
|
||||
std::vector<std::string> platform_resolved_locale_;
|
||||
|
@ -41,9 +41,10 @@ void PlatformViewIOS::AccessibilityBridgeManager::Clear() {
|
||||
accessibility_bridge_.reset();
|
||||
}
|
||||
|
||||
PlatformViewIOS::PlatformViewIOS(PlatformView::Delegate& delegate,
|
||||
PlatformViewIOS::PlatformViewIOS(
|
||||
PlatformView::Delegate& delegate,
|
||||
const std::shared_ptr<IOSContext>& context,
|
||||
__weak FlutterPlatformViewsController* platform_views_controller,
|
||||
const std::shared_ptr<PlatformViewsController>& platform_views_controller,
|
||||
const flutter::TaskRunners& task_runners)
|
||||
: PlatformView(delegate, task_runners),
|
||||
ios_context_(context),
|
||||
@ -55,7 +56,7 @@ PlatformViewIOS::PlatformViewIOS(PlatformView::Delegate& delegate,
|
||||
PlatformViewIOS::PlatformViewIOS(
|
||||
PlatformView::Delegate& delegate,
|
||||
IOSRenderingAPI rendering_api,
|
||||
__weak FlutterPlatformViewsController* platform_views_controller,
|
||||
const std::shared_ptr<PlatformViewsController>& platform_views_controller,
|
||||
const flutter::TaskRunners& task_runners,
|
||||
const std::shared_ptr<fml::ConcurrentTaskRunner>& worker_task_runner,
|
||||
const std::shared_ptr<const fml::SyncSwitch>& is_gpu_disabled_sync_switch)
|
||||
@ -208,7 +209,7 @@ void PlatformViewIOS::OnPreEngineRestart() const {
|
||||
if (!owner_controller_) {
|
||||
return;
|
||||
}
|
||||
[owner_controller_.platformViewsController reset];
|
||||
owner_controller_.platformViewsController->Reset();
|
||||
[owner_controller_.restorationPlugin reset];
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user