[macos] Enable macOS platform views only for Metal (flutter/engine#30853)
This commit is contained in:
@@ -1227,6 +1227,7 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppD
|
||||
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterDartProject.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterMacOS.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterPlatformViews.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterPluginMacOS.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterPluginRegistrarMacOS.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h
|
||||
@@ -1288,6 +1289,9 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenG
|
||||
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMac.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMac.mm
|
||||
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMacTest.mm
|
||||
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.mm
|
||||
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewControllerTest.mm
|
||||
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderer.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderingBackend.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderingBackend.mm
|
||||
@@ -1316,6 +1320,8 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/KeyCodeMap.m
|
||||
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/KeyCodeMap_Internal.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/MacOSGLContextSwitch.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/MacOSGLContextSwitch.mm
|
||||
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/TestFlutterPlatformView.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/TestFlutterPlatformView.mm
|
||||
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/fixtures/flutter_desktop_test.dart
|
||||
FILE: ../../../flutter/shell/platform/darwin/macos/framework/module.modulemap
|
||||
FILE: ../../../flutter/shell/platform/embedder/assets/EmbedderInfo.plist
|
||||
|
||||
@@ -40,6 +40,7 @@ _flutter_framework_headers = [
|
||||
"framework/Headers/FlutterDartProject.h",
|
||||
"framework/Headers/FlutterEngine.h",
|
||||
"framework/Headers/FlutterMacOS.h",
|
||||
"framework/Headers/FlutterPlatformViews.h",
|
||||
"framework/Headers/FlutterPluginMacOS.h",
|
||||
"framework/Headers/FlutterPluginRegistrarMacOS.h",
|
||||
"framework/Headers/FlutterViewController.h",
|
||||
@@ -95,6 +96,8 @@ source_set("flutter_framework_source") {
|
||||
"framework/Source/FlutterOpenGLRenderer.mm",
|
||||
"framework/Source/FlutterPlatformNodeDelegateMac.h",
|
||||
"framework/Source/FlutterPlatformNodeDelegateMac.mm",
|
||||
"framework/Source/FlutterPlatformViewController.h",
|
||||
"framework/Source/FlutterPlatformViewController.mm",
|
||||
"framework/Source/FlutterRenderer.h",
|
||||
"framework/Source/FlutterRenderingBackend.h",
|
||||
"framework/Source/FlutterRenderingBackend.mm",
|
||||
@@ -186,11 +189,14 @@ executable("flutter_desktop_darwin_unittests") {
|
||||
"framework/Source/FlutterMetalSurfaceManagerTest.mm",
|
||||
"framework/Source/FlutterOpenGLRendererTest.mm",
|
||||
"framework/Source/FlutterPlatformNodeDelegateMacTest.mm",
|
||||
"framework/Source/FlutterPlatformViewControllerTest.mm",
|
||||
"framework/Source/FlutterTextInputPluginTest.mm",
|
||||
"framework/Source/FlutterTextInputSemanticsObjectTest.mm",
|
||||
"framework/Source/FlutterViewControllerTest.mm",
|
||||
"framework/Source/FlutterViewControllerTestUtils.h",
|
||||
"framework/Source/FlutterViewControllerTestUtils.mm",
|
||||
"framework/Source/TestFlutterPlatformView.h",
|
||||
"framework/Source/TestFlutterPlatformView.mm",
|
||||
]
|
||||
|
||||
cflags_objcc = flutter_cflags_objcc_arc
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
// 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_FLUTTERPLATFORMVIEWS_H_
|
||||
#define FLUTTER_FLUTTERPLATFORMVIEWS_H_
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
#import "FlutterCodecs.h"
|
||||
#import "FlutterMacros.h"
|
||||
|
||||
@protocol FlutterPlatformViewFactory <NSObject>
|
||||
|
||||
/**
|
||||
* Create a Platform View which is an `NSView`.
|
||||
*
|
||||
* A MacOS plugin should implement this method and return an `NSView`, which can be embedded in a
|
||||
* Flutter App.
|
||||
*
|
||||
* The implementation of this method should create a new `NSView`.
|
||||
*
|
||||
* @param viewId A unique identifier for this view.
|
||||
* @param args Parameters for creating the view sent from the Dart side of the
|
||||
* Flutter app. If `createArgsCodec` is not implemented, or if no creation arguments were sent from
|
||||
* the Dart code, this will be null. Otherwise this will be the value sent from the Dart code as
|
||||
* decoded by `createArgsCodec`.
|
||||
*/
|
||||
- (nonnull NSView*)createWithviewIdentifier:(int64_t)viewId arguments:(nullable id)args;
|
||||
|
||||
/**
|
||||
* Returns the `FlutterMessageCodec` for decoding the args parameter of `createWithFrame`.
|
||||
*
|
||||
* Only implement this if `createWithFrame` needs an arguments parameter.
|
||||
*/
|
||||
@optional
|
||||
- (nullable NSObject<FlutterMessageCodec>*)createArgsCodec;
|
||||
@end
|
||||
|
||||
#endif // FLUTTER_FLUTTERPLATFORMVIEWS_H_
|
||||
@@ -7,6 +7,7 @@
|
||||
#import "FlutterBinaryMessenger.h"
|
||||
#import "FlutterChannels.h"
|
||||
#import "FlutterMacros.h"
|
||||
#import "FlutterPlatformViews.h"
|
||||
#import "FlutterPluginMacOS.h"
|
||||
#import "FlutterTexture.h"
|
||||
|
||||
@@ -48,6 +49,18 @@ FLUTTER_DARWIN_EXPORT
|
||||
- (void)addMethodCallDelegate:(nonnull id<FlutterPlugin>)delegate
|
||||
channel:(nonnull FlutterMethodChannel*)channel;
|
||||
|
||||
/**
|
||||
* Registers a `FlutterPlatformViewFactory` for creation of platform views.
|
||||
*
|
||||
* Plugins expose `NSView` for embedding in Flutter apps by registering a view factory.
|
||||
*
|
||||
* @param factory The view factory that will be registered.
|
||||
* @param factoryId A unique identifier for the factory, the Dart code of the Flutter app can use
|
||||
* this identifier to request creation of a `NSView` by the registered factory.
|
||||
*/
|
||||
- (void)registerViewFactory:(nonnull NSObject<FlutterPlatformViewFactory>*)factory
|
||||
withId:(nonnull NSString*)factoryId;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#import "FlutterEngine.h"
|
||||
#import "FlutterMacros.h"
|
||||
#import "FlutterPlatformViews.h"
|
||||
#import "FlutterPluginRegistrarMacOS.h"
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositor.h"
|
||||
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalRenderer.h"
|
||||
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRenderer.h"
|
||||
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h"
|
||||
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderingBackend.h"
|
||||
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h"
|
||||
#include "flutter/shell/platform/embedder/embedder.h"
|
||||
#import "flutter/shell/platform/embedder/embedder.h"
|
||||
|
||||
/**
|
||||
* Constructs and returns a FlutterLocale struct corresponding to |locale|, which must outlive
|
||||
@@ -95,6 +96,11 @@ static FlutterLocale FlutterLocaleFromNSLocale(NSLocale* locale) {
|
||||
*/
|
||||
- (void)loadAOTData:(NSString*)assetsDir;
|
||||
|
||||
/**
|
||||
* Creates a platform view channel and sets up the method handler.
|
||||
*/
|
||||
- (void)setupPlatformViewChannel;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
@@ -145,6 +151,11 @@ static FlutterLocale FlutterLocaleFromNSLocale(NSLocale* locale) {
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)registerViewFactory:(nonnull NSObject<FlutterPlatformViewFactory>*)factory
|
||||
withId:(nonnull NSString*)factoryId {
|
||||
[[_flutterEngine platformViewController] registerViewFactory:factory withId:factoryId];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
// Callbacks provided to the engine. See the called methods for documentation.
|
||||
@@ -186,6 +197,14 @@ static void OnPlatformMessage(const FlutterPlatformMessage* message, FlutterEngi
|
||||
|
||||
// FlutterCompositor is copied and used in embedder.cc.
|
||||
FlutterCompositor _compositor;
|
||||
|
||||
// Method channel for platform view functions. These functions include creating, disposing and
|
||||
// mutating a platform view.
|
||||
FlutterMethodChannel* _platformViewsChannel;
|
||||
|
||||
// Used to support creation and deletion of platform views and registering platform view
|
||||
// factories. Lifecycle is tied to the engine.
|
||||
FlutterPlatformViewController* _platformViewController;
|
||||
}
|
||||
|
||||
- (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)project {
|
||||
@@ -219,6 +238,9 @@ static void OnPlatformMessage(const FlutterPlatformMessage* message, FlutterEngi
|
||||
name:NSCurrentLocaleDidChangeNotification
|
||||
object:nil];
|
||||
|
||||
_platformViewController = [[FlutterPlatformViewController alloc] init];
|
||||
[self setupPlatformViewChannel];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -383,8 +405,8 @@ static void OnPlatformMessage(const FlutterPlatformMessage* message, FlutterEngi
|
||||
|
||||
if ([FlutterRenderingBackend renderUsingMetal]) {
|
||||
FlutterMetalRenderer* metalRenderer = reinterpret_cast<FlutterMetalRenderer*>(_renderer);
|
||||
_macOSCompositor =
|
||||
std::make_unique<flutter::FlutterMetalCompositor>(_viewController, metalRenderer.device);
|
||||
_macOSCompositor = std::make_unique<flutter::FlutterMetalCompositor>(
|
||||
_viewController, _platformViewController, metalRenderer.device);
|
||||
_macOSCompositor->SetPresentCallback([weakSelf](bool has_flutter_content) {
|
||||
if (has_flutter_content) {
|
||||
FlutterMetalRenderer* metalRenderer =
|
||||
@@ -541,6 +563,10 @@ static void OnPlatformMessage(const FlutterPlatformMessage* message, FlutterEngi
|
||||
_embedderAPI.DispatchSemanticsAction(_engine, target, action, data.GetMapping(), data.GetSize());
|
||||
}
|
||||
|
||||
- (FlutterPlatformViewController*)platformViewController {
|
||||
return _platformViewController;
|
||||
}
|
||||
|
||||
#pragma mark - Private methods
|
||||
|
||||
- (void)sendUserLocales {
|
||||
@@ -630,6 +656,18 @@ static void OnPlatformMessage(const FlutterPlatformMessage* message, FlutterEngi
|
||||
_engine = nullptr;
|
||||
}
|
||||
|
||||
- (void)setupPlatformViewChannel {
|
||||
_platformViewsChannel =
|
||||
[FlutterMethodChannel methodChannelWithName:@"flutter/platform_views"
|
||||
binaryMessenger:self.binaryMessenger
|
||||
codec:[FlutterStandardMethodCodec sharedInstance]];
|
||||
|
||||
__weak FlutterEngine* weakSelf = self;
|
||||
[_platformViewsChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
|
||||
[[weakSelf platformViewController] handleMethodCall:call result:result];
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - FlutterBinaryMessenger
|
||||
|
||||
- (void)sendOnChannel:(nonnull NSString*)channel message:(nullable NSData*)message {
|
||||
@@ -781,20 +819,22 @@ static void OnPlatformMessage(const FlutterPlatformMessage* message, FlutterEngi
|
||||
|
||||
#pragma mark - Task runner integration
|
||||
|
||||
- (void)postMainThreadTask:(FlutterTask)task targetTimeInNanoseconds:(uint64_t)targetTime {
|
||||
const auto engine_time = _embedderAPI.GetCurrentTime();
|
||||
|
||||
__weak FlutterEngine* weak_self = self;
|
||||
auto worker = ^{
|
||||
FlutterEngine* strong_self = weak_self;
|
||||
if (strong_self && strong_self->_engine) {
|
||||
auto result = _embedderAPI.RunTask(strong_self->_engine, &task);
|
||||
if (result != kSuccess) {
|
||||
NSLog(@"Could not post a task to the Flutter engine.");
|
||||
}
|
||||
- (void)runTaskOnEmbedder:(FlutterTask)task {
|
||||
if (_engine) {
|
||||
auto result = _embedderAPI.RunTask(_engine, &task);
|
||||
if (result != kSuccess) {
|
||||
NSLog(@"Could not post a task to the Flutter engine.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)postMainThreadTask:(FlutterTask)task targetTimeInNanoseconds:(uint64_t)targetTime {
|
||||
__weak FlutterEngine* weakSelf = self;
|
||||
auto worker = ^{
|
||||
[weakSelf runTaskOnEmbedder:task];
|
||||
};
|
||||
|
||||
const auto engine_time = _embedderAPI.GetCurrentTime();
|
||||
if (targetTime <= engine_time) {
|
||||
dispatch_async(dispatch_get_main_queue(), worker);
|
||||
|
||||
|
||||
@@ -343,7 +343,8 @@ TEST_F(FlutterEngineTest, NativeCallbacks) {
|
||||
ASSERT_TRUE(latch_called);
|
||||
}
|
||||
|
||||
TEST(FlutterEngine, Compositor) {
|
||||
// TODO: Enable after https://github.com/flutter/flutter/issues/96668 is fixed.
|
||||
TEST(FlutterEngine, DISABLED_Compositor) {
|
||||
NSString* fixtures = @(flutter::testing::GetFixturesPath());
|
||||
FlutterDartProject* project = [[FlutterDartProject alloc]
|
||||
initWithAssetsPath:fixtures
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "flutter/shell/platform/common/accessibility_bridge.h"
|
||||
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h"
|
||||
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h"
|
||||
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderer.h"
|
||||
|
||||
@interface FlutterEngine ()
|
||||
@@ -70,6 +71,8 @@
|
||||
*/
|
||||
- (BOOL)unregisterTextureWithID:(int64_t)textureID;
|
||||
|
||||
- (nonnull FlutterPlatformViewController*)platformViewController;
|
||||
|
||||
// Accessibility API.
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,13 +7,16 @@
|
||||
|
||||
#include "flutter/fml/macros.h"
|
||||
#include "flutter/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h"
|
||||
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h"
|
||||
|
||||
namespace flutter {
|
||||
|
||||
class FlutterMetalCompositor : public FlutterCompositor {
|
||||
public:
|
||||
explicit FlutterMetalCompositor(FlutterViewController* view_controller,
|
||||
id<MTLDevice> mtl_device);
|
||||
explicit FlutterMetalCompositor(
|
||||
FlutterViewController* view_controller,
|
||||
FlutterPlatformViewController* platform_views_controller,
|
||||
id<MTLDevice> mtl_device);
|
||||
|
||||
virtual ~FlutterMetalCompositor() = default;
|
||||
|
||||
@@ -42,7 +45,12 @@ class FlutterMetalCompositor : public FlutterCompositor {
|
||||
bool Present(const FlutterLayer** layers, size_t layers_count) override;
|
||||
|
||||
private:
|
||||
// Presents the platform view layer represented by `layer`. `layer_index` is
|
||||
// used to position the layer in the z-axis.
|
||||
void PresentPlatformView(const FlutterLayer* layer, size_t layer_index);
|
||||
|
||||
const id<MTLDevice> mtl_device_;
|
||||
const FlutterPlatformViewController* platform_views_controller_;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(FlutterMetalCompositor);
|
||||
};
|
||||
|
||||
@@ -10,9 +10,13 @@
|
||||
|
||||
namespace flutter {
|
||||
|
||||
FlutterMetalCompositor::FlutterMetalCompositor(FlutterViewController* view_controller,
|
||||
id<MTLDevice> mtl_device)
|
||||
: FlutterCompositor(view_controller), mtl_device_(mtl_device) {}
|
||||
FlutterMetalCompositor::FlutterMetalCompositor(
|
||||
FlutterViewController* view_controller,
|
||||
FlutterPlatformViewController* platform_views_controller,
|
||||
id<MTLDevice> mtl_device)
|
||||
: FlutterCompositor(view_controller),
|
||||
mtl_device_(mtl_device),
|
||||
platform_views_controller_(platform_views_controller) {}
|
||||
|
||||
bool FlutterMetalCompositor::CreateBackingStore(const FlutterBackingStoreConfig* config,
|
||||
FlutterBackingStore* backing_store_out) {
|
||||
@@ -77,7 +81,6 @@ bool FlutterMetalCompositor::Present(const FlutterLayer** layers, size_t layers_
|
||||
SetFrameStatus(FrameStatus::kPresenting);
|
||||
|
||||
bool has_flutter_content = false;
|
||||
|
||||
for (size_t i = 0; i < layers_count; ++i) {
|
||||
const auto* layer = layers[i];
|
||||
FlutterBackingStore* backing_store = const_cast<FlutterBackingStore*>(layer->backing_store);
|
||||
@@ -94,8 +97,7 @@ bool FlutterMetalCompositor::Present(const FlutterLayer** layers, size_t layers_
|
||||
break;
|
||||
}
|
||||
case kFlutterLayerContentTypePlatformView:
|
||||
// Add functionality in follow up PR.
|
||||
FML_LOG(WARNING) << "Presenting PlatformViews not yet supported";
|
||||
PresentPlatformView(layer, i);
|
||||
break;
|
||||
};
|
||||
}
|
||||
@@ -103,4 +105,24 @@ bool FlutterMetalCompositor::Present(const FlutterLayer** layers, size_t layers_
|
||||
return EndFrame(has_flutter_content);
|
||||
}
|
||||
|
||||
void FlutterMetalCompositor::PresentPlatformView(const FlutterLayer* layer, size_t layer_position) {
|
||||
// TODO (https://github.com/flutter/flutter/issues/96668)
|
||||
// once the issue is fixed, this check will pass.
|
||||
FML_DCHECK([[NSThread currentThread] isMainThread])
|
||||
<< "Must be on the main thread to present platform views";
|
||||
|
||||
int64_t platform_view_id = layer->platform_view->identifier;
|
||||
NSView* platform_view = [platform_views_controller_ platformViewWithID:platform_view_id];
|
||||
|
||||
FML_DCHECK(platform_view) << "Platform view not found for id: " << platform_view_id;
|
||||
|
||||
CGFloat scale = [[NSScreen mainScreen] backingScaleFactor];
|
||||
platform_view.frame = CGRectMake(layer->offset.x / scale, layer->offset.y / scale,
|
||||
layer->size.width / scale, layer->size.height / scale);
|
||||
if (platform_view.superview == nil) {
|
||||
[view_controller_.flutterView addSubview:platform_view];
|
||||
}
|
||||
platform_view.layer.zPosition = layer_position;
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
@@ -14,7 +14,8 @@ TEST(FlutterMetalCompositorTest, TestPresent) {
|
||||
id mockViewController = CreateMockViewController(nil);
|
||||
|
||||
std::unique_ptr<flutter::FlutterMetalCompositor> macos_compositor =
|
||||
std::make_unique<FlutterMetalCompositor>(mockViewController, nullptr);
|
||||
std::make_unique<FlutterMetalCompositor>(
|
||||
mockViewController, /*platform_view_controller*/ nullptr, /*mtl_device*/ nullptr);
|
||||
|
||||
bool flag = false;
|
||||
macos_compositor->SetPresentCallback([f = &flag](bool has_flutter_content) {
|
||||
@@ -31,7 +32,8 @@ TEST(FlutterMetalCompositorTest, TestCreate) {
|
||||
[mockViewController loadView];
|
||||
|
||||
std::unique_ptr<flutter::FlutterMetalCompositor> macos_compositor =
|
||||
std::make_unique<FlutterMetalCompositor>(mockViewController, nullptr);
|
||||
std::make_unique<FlutterMetalCompositor>(
|
||||
mockViewController, /*platform_view_controller*/ nullptr, /*mtl_device*/ nullptr);
|
||||
|
||||
FlutterBackingStore backing_store;
|
||||
FlutterBackingStoreConfig config;
|
||||
@@ -52,7 +54,8 @@ TEST(FlutterMetalCompositorTest, TestCompositing) {
|
||||
[mockViewController loadView];
|
||||
|
||||
std::unique_ptr<flutter::FlutterMetalCompositor> macos_compositor =
|
||||
std::make_unique<FlutterMetalCompositor>(mockViewController, nullptr);
|
||||
std::make_unique<FlutterMetalCompositor>(
|
||||
mockViewController, /*platform_view_controller*/ nullptr, /*mtl_device*/ nullptr);
|
||||
|
||||
FlutterBackingStore backing_store;
|
||||
FlutterBackingStoreConfig config;
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// 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 <Cocoa/Cocoa.h>
|
||||
|
||||
#import "FlutterChannels.h"
|
||||
|
||||
#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterPlatformViews.h"
|
||||
|
||||
#include <map>
|
||||
#include <unordered_set>
|
||||
|
||||
@interface FlutterPlatformViewController : NSViewController
|
||||
@end
|
||||
|
||||
@interface FlutterPlatformViewController ()
|
||||
|
||||
/**
|
||||
* Creates a platform view of viewType with viewId.
|
||||
* FlutterResult is updated to contain nil for success or to contain
|
||||
* a FlutterError if there is an error.
|
||||
*/
|
||||
- (void)onCreateWithViewID:(int64_t)viewId
|
||||
viewType:(nonnull NSString*)viewType
|
||||
result:(nonnull FlutterResult)result;
|
||||
|
||||
/**
|
||||
* Disposes the platform view with `viewId`.
|
||||
* FlutterResult is updated to contain nil for success or a FlutterError if there is an error.
|
||||
*/
|
||||
- (void)onDisposeWithViewID:(int64_t)viewId result:(nonnull FlutterResult)result;
|
||||
|
||||
/**
|
||||
* Returns the platform view associated with the viewId.
|
||||
*/
|
||||
- (nullable NSView*)platformViewWithID:(int64_t)viewId;
|
||||
|
||||
/**
|
||||
* Register a view factory by adding an entry into the platformViewFactories map with key factoryId
|
||||
* and value factory.
|
||||
*/
|
||||
- (void)registerViewFactory:(nonnull NSObject<FlutterPlatformViewFactory>*)factory
|
||||
withId:(nonnull NSString*)factoryId;
|
||||
|
||||
/**
|
||||
* Handles platform view related method calls, for example create, dispose, etc.
|
||||
*/
|
||||
- (void)handleMethodCall:(nonnull FlutterMethodCall*)call result:(nonnull FlutterResult)result;
|
||||
|
||||
/**
|
||||
* Removes platform views slated to be disposed via method handler calls.
|
||||
*/
|
||||
- (void)disposePlatformViews;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,114 @@
|
||||
// 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.
|
||||
|
||||
#include "flutter/fml/logging.h"
|
||||
|
||||
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h"
|
||||
|
||||
@implementation FlutterPlatformViewController {
|
||||
// NSDictionary maps platform view type identifiers to FlutterPlatformViewFactories.
|
||||
NSMutableDictionary<NSString*, NSObject<FlutterPlatformViewFactory>*>* _platformViewFactories;
|
||||
|
||||
// Map from platform view id to the underlying NSView.
|
||||
std::map<int, NSView*> _platformViews;
|
||||
|
||||
// View ids that are going to be disposed on the next present call.
|
||||
std::unordered_set<int64_t> _platformViewsToDispose;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_platformViewFactories = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)onCreateWithViewID:(int64_t)viewId
|
||||
viewType:(nonnull NSString*)viewType
|
||||
result:(nonnull FlutterResult)result {
|
||||
if (_platformViews.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 = _platformViewFactories[viewType];
|
||||
if (!factory) {
|
||||
result([FlutterError
|
||||
errorWithCode:@"unregistered_view_type"
|
||||
message:@"trying to create a view with an unregistered type"
|
||||
details:[NSString stringWithFormat:@"unregistered view type: '%@'", viewType]]);
|
||||
return;
|
||||
}
|
||||
|
||||
NSView* platform_view = [factory createWithviewIdentifier:viewId arguments:nil];
|
||||
_platformViews[viewId] = platform_view;
|
||||
result(nil);
|
||||
}
|
||||
|
||||
- (void)onDisposeWithViewID:(int64_t)viewId result:(nonnull FlutterResult)result {
|
||||
if (_platformViews.count(viewId) == 0) {
|
||||
result([FlutterError errorWithCode:@"unknown_view"
|
||||
message:@"trying to dispose an unknown"
|
||||
details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]);
|
||||
return;
|
||||
}
|
||||
|
||||
// The following disposePlatformViews call will dispose the views.
|
||||
_platformViewsToDispose.insert(viewId);
|
||||
result(nil);
|
||||
}
|
||||
|
||||
- (void)registerViewFactory:(nonnull NSObject<FlutterPlatformViewFactory>*)factory
|
||||
withId:(nonnull NSString*)factoryId {
|
||||
_platformViewFactories[factoryId] = factory;
|
||||
}
|
||||
|
||||
- (nullable NSView*)platformViewWithID:(int64_t)viewId {
|
||||
if (_platformViews.count(viewId)) {
|
||||
return _platformViews[viewId];
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)handleMethodCall:(nonnull FlutterMethodCall*)call result:(nonnull FlutterResult)result {
|
||||
if ([[call method] isEqualToString:@"create"]) {
|
||||
NSMutableDictionary<NSString*, id>* args = [call arguments];
|
||||
if ([args objectForKey:@"id"]) {
|
||||
int64_t viewId = [args[@"id"] longValue];
|
||||
NSString* viewType = [NSString stringWithUTF8String:([args[@"viewType"] UTF8String])];
|
||||
[self onCreateWithViewID:viewId viewType:viewType result:result];
|
||||
} else {
|
||||
result([FlutterError errorWithCode:@"unknown_view"
|
||||
message:@"'id' argument must be passed to create a platform view."
|
||||
details:[NSString stringWithFormat:@"'id' not specified."]]);
|
||||
}
|
||||
} else if ([[call method] isEqualToString:@"dispose"]) {
|
||||
NSNumber* arg = [call arguments];
|
||||
int64_t viewId = [arg longValue];
|
||||
[self onDisposeWithViewID:viewId result:result];
|
||||
} else {
|
||||
result(FlutterMethodNotImplemented);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)disposePlatformViews {
|
||||
if (_platformViewsToDispose.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
FML_DCHECK([[NSThread currentThread] isMainThread])
|
||||
<< "Must be on the main thread to handle disposing platform views";
|
||||
for (int64_t viewId : _platformViewsToDispose) {
|
||||
NSView* view = _platformViews[viewId];
|
||||
[view removeFromSuperview];
|
||||
_platformViews.erase(viewId);
|
||||
}
|
||||
_platformViewsToDispose.clear();
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,128 @@
|
||||
// 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 "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h"
|
||||
|
||||
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h"
|
||||
#import "flutter/shell/platform/darwin/macos/framework/Source/TestFlutterPlatformView.h"
|
||||
|
||||
#include "flutter/testing/testing.h"
|
||||
|
||||
namespace flutter::testing {
|
||||
|
||||
TEST(FlutterPlatformViewController, TestCreatePlatformViewNoMatchingViewType) {
|
||||
// Use id so we can access handleMethodCall method.
|
||||
id platformViewController = [[FlutterPlatformViewController alloc] init];
|
||||
|
||||
FlutterMethodCall* methodCall =
|
||||
[FlutterMethodCall methodCallWithMethodName:@"create"
|
||||
arguments:@{
|
||||
@"id" : @2,
|
||||
@"viewType" : @"FlutterPlatformViewMock"
|
||||
}];
|
||||
|
||||
__block bool errored = false;
|
||||
FlutterResult result = ^(id result) {
|
||||
if ([result isKindOfClass:[FlutterError class]]) {
|
||||
errored = true;
|
||||
}
|
||||
};
|
||||
|
||||
[platformViewController handleMethodCall:methodCall result:result];
|
||||
|
||||
// We expect the call to error since no factories are registered.
|
||||
EXPECT_TRUE(errored);
|
||||
}
|
||||
|
||||
TEST(FlutterPlatformViewController, TestRegisterPlatformViewFactoryAndCreate) {
|
||||
// Use id so we can access handleMethodCall method.
|
||||
id platformViewController = [[FlutterPlatformViewController alloc] init];
|
||||
|
||||
TestFlutterPlatformViewFactory* factory = [TestFlutterPlatformViewFactory alloc];
|
||||
|
||||
[platformViewController registerViewFactory:factory withId:@"MockPlatformView"];
|
||||
|
||||
FlutterMethodCall* methodCall =
|
||||
[FlutterMethodCall methodCallWithMethodName:@"create"
|
||||
arguments:@{
|
||||
@"id" : @2,
|
||||
@"viewType" : @"MockPlatformView"
|
||||
}];
|
||||
|
||||
__block bool success = false;
|
||||
FlutterResult result = ^(id result) {
|
||||
// If a platform view is successfully created, the result is nil.
|
||||
if (result == nil) {
|
||||
success = true;
|
||||
}
|
||||
};
|
||||
[platformViewController handleMethodCall:methodCall result:result];
|
||||
|
||||
EXPECT_TRUE(success);
|
||||
}
|
||||
|
||||
TEST(FlutterPlatformViewController, TestCreateAndDispose) {
|
||||
// Use id so we can access handleMethodCall method.
|
||||
id platformViewController = [[FlutterPlatformViewController alloc] init];
|
||||
|
||||
TestFlutterPlatformViewFactory* factory = [TestFlutterPlatformViewFactory alloc];
|
||||
|
||||
[platformViewController registerViewFactory:factory withId:@"MockPlatformView"];
|
||||
|
||||
FlutterMethodCall* methodCallOnCreate =
|
||||
[FlutterMethodCall methodCallWithMethodName:@"create"
|
||||
arguments:@{
|
||||
@"id" : @2,
|
||||
@"viewType" : @"MockPlatformView"
|
||||
}];
|
||||
|
||||
__block bool created = false;
|
||||
FlutterResult resultOnCreate = ^(id result) {
|
||||
// If a platform view is successfully created, the result is nil.
|
||||
if (result == nil) {
|
||||
created = true;
|
||||
}
|
||||
};
|
||||
|
||||
[platformViewController handleMethodCall:methodCallOnCreate result:resultOnCreate];
|
||||
|
||||
FlutterMethodCall* methodCallOnDispose =
|
||||
[FlutterMethodCall methodCallWithMethodName:@"dispose"
|
||||
arguments:[NSNumber numberWithLongLong:2]];
|
||||
|
||||
__block bool disposed = false;
|
||||
FlutterResult resultOnDispose = ^(id result) {
|
||||
// If a platform view is successfully created, the result is nil.
|
||||
if (result == nil) {
|
||||
disposed = true;
|
||||
}
|
||||
};
|
||||
|
||||
[platformViewController handleMethodCall:methodCallOnDispose result:resultOnDispose];
|
||||
|
||||
EXPECT_TRUE(created);
|
||||
EXPECT_TRUE(disposed);
|
||||
}
|
||||
|
||||
TEST(FlutterPlatformViewController, TestDisposeOnMissingViewId) {
|
||||
// Use id so we can access handleMethodCall method.
|
||||
id platformViewController = [[FlutterPlatformViewController alloc] init];
|
||||
|
||||
FlutterMethodCall* methodCall =
|
||||
[FlutterMethodCall methodCallWithMethodName:@"dispose"
|
||||
arguments:[NSNumber numberWithLongLong:20]];
|
||||
|
||||
__block bool errored = false;
|
||||
FlutterResult result = ^(id result) {
|
||||
if ([result isKindOfClass:[FlutterError class]]) {
|
||||
errored = true;
|
||||
}
|
||||
};
|
||||
|
||||
[platformViewController handleMethodCall:methodCall result:result];
|
||||
|
||||
EXPECT_TRUE(errored);
|
||||
}
|
||||
|
||||
} // namespace flutter::testing
|
||||
@@ -0,0 +1,13 @@
|
||||
// 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 <Foundation/Foundation.h>
|
||||
|
||||
#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h"
|
||||
|
||||
@interface TestFlutterPlatformView : NSView
|
||||
@end
|
||||
|
||||
@interface TestFlutterPlatformViewFactory : NSObject <FlutterPlatformViewFactory>
|
||||
@end
|
||||
@@ -0,0 +1,28 @@
|
||||
// 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 <Cocoa/Cocoa.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "flutter/shell/platform/darwin/macos/framework/Source/TestFlutterPlatformView.h"
|
||||
|
||||
@implementation TestFlutterPlatformView
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation TestFlutterPlatformViewFactory
|
||||
- (NSView*)createWithviewIdentifier:(int64_t)viewId arguments:(nullable id)args {
|
||||
return [[TestFlutterPlatformView alloc] initWithFrame:CGRectZero];
|
||||
}
|
||||
|
||||
- (NSObject<FlutterMessageCodec>*)createArgsCodec {
|
||||
return [FlutterStandardMessageCodec sharedInstance];
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user