feat: working implementation of multiple widgets on macos

This commit is contained in:
Nick Fisher
2024-09-30 13:45:57 +08:00
parent 22020d8607
commit fbd54a2a09
21 changed files with 382 additions and 183 deletions

View File

@@ -1,7 +1,7 @@
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
typedef ResizeCallback = void Function(Size newSize);
typedef ResizeCallback = void Function(Size oldSize, Size newSize);
class ResizeObserver extends SingleChildRenderObjectWidget {
final ResizeCallback onResized;
@@ -34,7 +34,7 @@ class _RenderResizeObserver extends RenderProxyBox {
void performLayout() async {
super.performLayout();
if (size.width != _oldSize.width || size.height != _oldSize.height) {
onLayoutChangedCallback(size);
onLayoutChangedCallback(_oldSize, size);
_oldSize = Size(size.width, size.height);
}
}

View File

@@ -124,7 +124,6 @@ class _ThermionListenerWidgetState extends State<ThermionListenerWidget> {
return widget.child ?? Container();
}
return Stack(children: [
if (widget.child != null) Positioned.fill(child: widget.child!),
if (isDesktop) Positioned.fill(child: _desktop(pixelRatio)),
if (!isDesktop) Positioned.fill(child: _mobile(pixelRatio))
]);

View File

@@ -26,14 +26,21 @@ class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
ThermionFlutterTexture? _texture;
RenderTarget? _renderTarget;
static final _views = <t.View>[];
@override
void dispose() {
super.dispose();
_views.remove(widget.view);
_texture?.destroy();
}
@override
void initState() {
if (_views.contains(widget.view)) {
throw Exception("View already embedded in a widget");
}
_views.add(widget.view);
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
await widget.viewer.initialized;
@@ -51,9 +58,10 @@ class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
await widget.view.setRenderTarget(_renderTarget!);
await widget.view.updateViewport(width, height);
await widget.view.updateViewport(_texture!.width, _texture!.height);
var camera = await widget.view.getCamera();
await camera.setLensProjection(aspect: width / height);
await camera.setLensProjection(
aspect: _texture!.width / _texture!.height);
if (mounted) {
setState(() {});
@@ -73,18 +81,29 @@ class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
await _renderTarget!.destroy();
texture.destroy();
}
_views.clear();
});
});
_callbackId = _numCallbacks;
_numCallbacks++;
super.initState();
}
bool _rendering = false;
static int _numCallbacks = 0;
static int _primaryCallback = 0;
late int _callbackId;
int lastRender = 0;
void _requestFrame() {
WidgetsBinding.instance.scheduleFrameCallback((d) async {
if (!_rendering) {
if (widget.viewer.rendering && !_rendering) {
_rendering = true;
await widget.viewer.requestFrame();
if (_callbackId == _primaryCallback) {
await widget.viewer.requestFrame();
lastRender = d.inMilliseconds;
}
await _texture?.markFrameAvailable();
_rendering = false;
}
@@ -92,29 +111,37 @@ class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
});
}
bool _resizing = false;
final _resizing = <Future>[];
Timer? _resizeTimer;
Future _resize(Size newSize) async {
Future _resize(Size oldSize, Size newSize) async {
await Future.wait(_resizing);
_resizeTimer?.cancel();
_resizeTimer = Timer(const Duration(milliseconds: 10), () async {
if (_resizing || !mounted) {
return;
}
_resizeTimer!.cancel();
_resizing = true;
_resizeTimer = Timer(const Duration(milliseconds: 100), () async {
await Future.wait(_resizing);
if (!mounted) {
return;
}
if (newSize.width == _texture?.width &&
newSize.height == _texture?.height) {
return;
}
final completer = Completer();
_resizing.add(completer.future);
newSize *= MediaQuery.of(context).devicePixelRatio;
var newWidth = newSize.width.ceil();
var newHeight = newSize.height.ceil();
var lastTextureId = _texture?.hardwareId;
await _texture?.resize(
newWidth,
newHeight,
@@ -122,12 +149,23 @@ class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
0,
);
await widget.view.updateViewport(newWidth, newHeight);
var camera = await widget.view.getCamera();
await camera.setLensProjection(aspect: newWidth / newHeight);
if (_texture?.hardwareId != lastTextureId) {
await _renderTarget?.destroy();
_renderTarget = await widget.viewer.createRenderTarget(
_texture!.width, _texture!.height, _texture!.hardwareId);
await widget.view.setRenderTarget(_renderTarget!);
}
await widget.view.updateViewport(_texture!.width, _texture!.height);
var camera = await widget.view.getCamera();
await camera.setLensProjection(
aspect: _texture!.width.toDouble() / _texture!.height.toDouble());
if (!mounted) {
return;
}
setState(() {});
_resizing = false;
completer.complete();
_resizing.remove(completer.future);
});
}
@@ -137,30 +175,17 @@ class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
return widget.initial ?? Container(color: Colors.red);
}
return Stack(children: [
Positioned.fill(
child: ResizeObserver(
onResized: _resize,
child: Stack(children: [
Positioned.fill(
child: Texture(
key: ObjectKey("flutter_texture_${_texture!.flutterId}"),
textureId: _texture!.flutterId,
filterQuality: FilterQuality.none,
freeze: false,
))
]))),
Align(
alignment: Alignment.bottomLeft,
child: ElevatedButton(
onPressed: () async {
var img =
await widget.viewer.capture(renderTarget: _renderTarget!);
print(img);
},
child: Text("CAPTURE")),
)
]);
return ResizeObserver(
onResized: _resize,
child: Stack(children: [
Positioned.fill(
child: Texture(
key: ObjectKey("flutter_texture_${_texture!.flutterId}"),
textureId: _texture!.flutterId,
filterQuality: FilterQuality.none,
freeze: false,
))
]));
}
}

View File

@@ -5,8 +5,8 @@ public class SwiftThermionFlutterPlugin: NSObject, FlutterPlugin {
var registrar : FlutterPluginRegistrar
var registry: FlutterTextureRegistry
var texture: ThermionFlutterTexture?
var textures: [Int64: ThermionFlutterTexture] = [:]
var createdAt = Date()
var destroying = false
@@ -72,6 +72,13 @@ public class SwiftThermionFlutterPlugin: NSObject, FlutterPlugin {
self.registry = textureRegistry;
self.registrar = registrar
}
var markTextureFrameAvailable : @convention(c) (UnsafeMutableRawPointer?) -> () = { instancePtr in
let instance:SwiftThermionFlutterPlugin = Unmanaged<SwiftThermionFlutterPlugin>.fromOpaque(instancePtr!).takeUnretainedValue()
for (_, texture) in instance.textures {
instance.registry.textureFrameAvailable(texture.flutterTextureId)
}
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
let methodName = call.method;
@@ -86,33 +93,39 @@ public class SwiftThermionFlutterPlugin: NSObject, FlutterPlugin {
registry.textureFrameAvailable(flutterTextureId)
result(nil)
case "getRenderCallback":
result(nil)
if(renderCallbackHolder.isEmpty) {
renderCallbackHolder.append(unsafeBitCast(markTextureFrameAvailable, to:Int64.self))
renderCallbackHolder.append(unsafeBitCast(Unmanaged.passUnretained(self), to:UInt64.self))
}
result(renderCallbackHolder)
case "getDriverPlatform":
result(nil)
case "getSharedContext":
result(nil)
case "createTexture":
if(destroying) {
result(nil)
return
}
let args = call.arguments as! [Any]
let width = args[0] as! Int64
let height = args[1] as! Int64
self.texture = ThermionFlutterTexture(registry: registry, width: width, height: height)
let texture = ThermionFlutterTexture(registry: registry, width: width, height: height)
if(self.texture!.texture.metalTextureAddress == -1) {
if texture.texture.metalTextureAddress == -1 {
result(nil)
} else {
result([self.texture!.flutterTextureId as Any, self.texture!.texture.metalTextureAddress, nil])
textures[texture.flutterTextureId] = texture
result([texture.flutterTextureId, texture.texture.metalTextureAddress, nil])
}
case "destroyTexture":
self.destroying = true
self.texture?.destroy()
self.texture = nil
result(true)
self.destroying = false
let args = call.arguments as! [Any]
let flutterTextureId = args[0] as! Int64
if let texture = textures[flutterTextureId] {
texture.destroy()
textures.removeValue(forKey: flutterTextureId)
result(true)
} else {
result(false)
}
default:
result(FlutterMethodNotImplemented)
}

View File

@@ -23,7 +23,7 @@ public class ThermionFlutterTexture : NSObject, FlutterTexture {
}
public func onTextureUnregistered(_ texture:FlutterTexture) {
print("Texture unregistered")
}
public func destroy() {