diff --git a/src/SamplesApp/UITests.Shared/UITests.Shared.projitems b/src/SamplesApp/UITests.Shared/UITests.Shared.projitems
index 724f039ce073..1ae92eeb6bee 100644
--- a/src/SamplesApp/UITests.Shared/UITests.Shared.projitems
+++ b/src/SamplesApp/UITests.Shared/UITests.Shared.projitems
@@ -1821,6 +1821,10 @@
Designer
MSBuild:Compile
+
+ Designer
+ MSBuild:Compile
+
Designer
MSBuild:Compile
@@ -9821,6 +9825,9 @@
ContentPresenter_NativeEmbedding.xaml
+
+ ContentPresenter_NativeEmbedding_ZIndex.xaml
+
ContentPresenter_NativeEmbedding_Android_FillType.xaml
@@ -10172,4 +10179,4 @@
-
\ No newline at end of file
+
diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/ContentPresenter/ContentPresenter_NativeEmbedding_ZIndex.xaml b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/ContentPresenter/ContentPresenter_NativeEmbedding_ZIndex.xaml
new file mode 100644
index 000000000000..39a067c6158d
--- /dev/null
+++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/ContentPresenter/ContentPresenter_NativeEmbedding_ZIndex.xaml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/ContentPresenter/ContentPresenter_NativeEmbedding_ZIndex.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/ContentPresenter/ContentPresenter_NativeEmbedding_ZIndex.xaml.cs
new file mode 100644
index 000000000000..3bd82215c754
--- /dev/null
+++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/ContentPresenter/ContentPresenter_NativeEmbedding_ZIndex.xaml.cs
@@ -0,0 +1,72 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml;
+using Windows.UI.Core;
+using Microsoft.UI.Xaml.Data;
+using Uno.UI.Samples.Controls;
+
+namespace Uno.UI.Samples.Content.UITests.ContentPresenter
+{
+ [Sample("ContentPresenter", IsManualTest = true)]
+ public sealed partial class ContentPresenter_NativeEmbedding_ZIndex : UserControl
+ {
+ public ContentPresenter_NativeEmbedding_ZIndex()
+ {
+ this.InitializeComponent();
+ var state = new ToggleableState();
+ sv.DataContext = state;
+ var timer = new DispatcherTimer();
+ timer.Interval = TimeSpan.FromSeconds(1);
+ timer.Tick += (s, e) =>
+ {
+ state.State++;
+ };
+ timer.Start();
+ }
+ }
+
+ public class ToggleableState : INotifyPropertyChanged
+ {
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+ protected bool SetField(ref T field, T value, [CallerMemberName] string propertyName = null)
+ {
+ if (EqualityComparer.Default.Equals(field, value)) return false;
+ field = value;
+ OnPropertyChanged(propertyName);
+ return true;
+ }
+
+ private int _state;
+ public int State { get => _state; set => SetField(ref _state, value); }
+ }
+
+ public class StateToVisibilityConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, string language)
+ => ((int)value) % 2 == 0 ? Visibility.Visible : Visibility.Collapsed;
+
+ public object ConvertBack(object value, Type targetType, object parameter, string language) => throw new NotImplementedException();
+ }
+
+ public class StateToZIndexConverter : IValueConverter
+ {
+ private int _lastRet;
+ public int Modulus { get; set; }
+ public int Remainder { get; set; }
+ public object Convert(object value, Type targetType, object parameter, string language)
+ {
+ return ((int)value) % Modulus == Remainder ? _lastRet = ((int)value) : _lastRet;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string language) => throw new NotImplementedException();
+ }
+}
diff --git a/src/Uno.UI.Composition/Composition/Visual.skia.cs b/src/Uno.UI.Composition/Composition/Visual.skia.cs
index 9083d5578040..a936c6295780 100644
--- a/src/Uno.UI.Composition/Composition/Visual.skia.cs
+++ b/src/Uno.UI.Composition/Composition/Visual.skia.cs
@@ -9,6 +9,7 @@
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Text;
+using Windows.Foundation;
using Microsoft.CodeAnalysis.PooledObjects;
using SkiaSharp;
using Uno.Disposables;
@@ -519,7 +520,7 @@ static void RenderChildrenStep(Visual visual, PaintingSession session, bool appl
}
}
- internal void GetNativeViewPath(SKPath clipFromParent, SKPath clipPath)
+ internal void GetNativeViewPathAndZOrder(SKPath clipFromParent, SKPath clipPath, List nativeVisualsInZOrder)
{
if (this is { Opacity: 0 } or { IsVisible: false } || clipFromParent.IsEmpty)
{
@@ -546,6 +547,11 @@ internal void GetNativeViewPath(SKPath clipFromParent, SKPath clipPath)
clipPath.Op(localClipCombinedByClipFromParent, IsNativeHostVisual ? SKPathOp.Union : SKPathOp.Difference, clipPath);
}
+ if (IsNativeHostVisual && !localClipCombinedByClipFromParent.IsEmpty)
+ {
+ nativeVisualsInZOrder.Add(this);
+ }
+
if (GetPostPaintingClipping() is { } postClip)
{
postClip.Transform(TotalMatrix.ToSKMatrix(), postClip);
@@ -553,7 +559,7 @@ internal void GetNativeViewPath(SKPath clipFromParent, SKPath clipPath)
}
foreach (var child in GetChildrenInRenderOrder())
{
- child.GetNativeViewPath(localClipCombinedByClipFromParent, clipPath);
+ child.GetNativeViewPathAndZOrder(localClipCombinedByClipFromParent, clipPath, nativeVisualsInZOrder);
}
}
diff --git a/src/Uno.UI.Runtime.Skia.Android/Native/AndroidSkiaNativeElementHostingExtension.cs b/src/Uno.UI.Runtime.Skia.Android/Native/AndroidSkiaNativeElementHostingExtension.cs
index 388016cd9c7d..3c534da33f1a 100644
--- a/src/Uno.UI.Runtime.Skia.Android/Native/AndroidSkiaNativeElementHostingExtension.cs
+++ b/src/Uno.UI.Runtime.Skia.Android/Native/AndroidSkiaNativeElementHostingExtension.cs
@@ -18,7 +18,7 @@ public AndroidSkiaNativeElementHostingExtension(ContentPresenter owner)
_owner = owner;
}
- public void ArrangeNativeElement(object content, Rect arrangeRect, Rect clipRect)
+ public void ArrangeNativeElement(object content, Rect arrangeRect)
{
if (content is View view)
{
@@ -77,14 +77,6 @@ public void ChangeNativeElementOpacity(object content, double opacity)
}
}
- public void ChangeNativeElementVisibility(object content, bool visible)
- {
- if (content is View view)
- {
- view.Visibility = visible ? ViewStates.Visible : ViewStates.Invisible;
- }
- }
-
public object? CreateSampleComponent(string text)
{
if (ApplicationActivity.NativeLayerHost is not { } host)
diff --git a/src/Uno.UI.Runtime.Skia.AppleUIKit/Native/UIKitNativeElementHostingExtension.cs b/src/Uno.UI.Runtime.Skia.AppleUIKit/Native/UIKitNativeElementHostingExtension.cs
index bfc409462c1f..2b9e4b2ef3dc 100644
--- a/src/Uno.UI.Runtime.Skia.AppleUIKit/Native/UIKitNativeElementHostingExtension.cs
+++ b/src/Uno.UI.Runtime.Skia.AppleUIKit/Native/UIKitNativeElementHostingExtension.cs
@@ -20,7 +20,7 @@ public UIKitNativeElementHostingExtension(ContentPresenter presenter)
private UIView? OverlayLayer => _presenter.XamlRoot is { } xamlRoot ? (XamlRootMap.GetHostForRoot(xamlRoot) as IAppleUIKitXamlRootHost)?.NativeOverlayLayer : null;
- public void ArrangeNativeElement(object content, Rect arrangeRect, Rect clipRect)
+ public void ArrangeNativeElement(object content, Rect arrangeRect)
{
if (content is UIView view)
{
@@ -72,14 +72,6 @@ public void ChangeNativeElementOpacity(object content, double opacity)
}
}
- public void ChangeNativeElementVisibility(object content, bool visible)
- {
- if (content is UIView view)
- {
- view.Opaque = visible;
- }
- }
-
public object? CreateSampleComponent(string text)
{
if (OverlayLayer is null)
diff --git a/src/Uno.UI.Runtime.Skia.MacOS/Native/MacOSNativeElementHostingExtension.cs b/src/Uno.UI.Runtime.Skia.MacOS/Native/MacOSNativeElementHostingExtension.cs
index 08c2038fab74..3f81829e4a81 100644
--- a/src/Uno.UI.Runtime.Skia.MacOS/Native/MacOSNativeElementHostingExtension.cs
+++ b/src/Uno.UI.Runtime.Skia.MacOS/Native/MacOSNativeElementHostingExtension.cs
@@ -11,6 +11,14 @@ namespace Uno.UI.Runtime.Skia.MacOS;
internal class MacOSNativeElement : Microsoft.UI.Xaml.FrameworkElement
{
+ public MacOSNativeElement()
+ {
+ Unloaded += (s, e) =>
+ {
+ NativeUno.uno_native_dispose(NativeHandle);
+ };
+ }
+
public nint NativeHandle { get; internal set; }
internal bool Detached { get; set; }
@@ -29,7 +37,7 @@ private MacOSNativeElementHostingExtension(ContentPresenter contentPresenter)
public static void Register() => ApiExtensibility.Register(typeof(ContentPresenter.INativeElementHostingExtension), o => new MacOSNativeElementHostingExtension(o));
- public void ArrangeNativeElement(object content, Rect arrangeRect, Rect clipRect)
+ public void ArrangeNativeElement(object content, Rect arrangeRect)
{
if (content is MacOSNativeElement element)
{
@@ -39,7 +47,7 @@ public void ArrangeNativeElement(object content, Rect arrangeRect, Rect clipRect
}
else
{
- NativeUno.uno_native_arrange(element.NativeHandle, arrangeRect.Left, arrangeRect.Top, arrangeRect.Width, arrangeRect.Height, clipRect.Left, clipRect.Top, clipRect.Width, clipRect.Height);
+ NativeUno.uno_native_arrange(element.NativeHandle, arrangeRect.Left, arrangeRect.Top, arrangeRect.Width, arrangeRect.Height);
}
}
else if (this.Log().IsEnabled(LogLevel.Debug))
@@ -53,6 +61,7 @@ public void AttachNativeElement(object content)
if (content is MacOSNativeElement element)
{
NativeUno.uno_native_attach(element.NativeHandle);
+ element.Detached = false;
}
else if (this.Log().IsEnabled(LogLevel.Debug))
{
@@ -74,19 +83,6 @@ public void ChangeNativeElementOpacity(object content, double opacity)
}
}
- public void ChangeNativeElementVisibility(object content, bool visible)
- {
- if (content is MacOSNativeElement element)
- {
- // https://developer.apple.com/documentation/appkit/nsview/1483369-hidden?language=objc
- NativeUno.uno_native_set_visibility(element.NativeHandle, visible);
- }
- else if (this.Log().IsEnabled(LogLevel.Debug))
- {
- this.Log().Debug($"Object `{nameof(content)}` is a {content.GetType().FullName} and not a MacOSNativeElement subclass.");
- }
- }
-
public object? CreateSampleComponent(string text)
{
if (_window is null)
diff --git a/src/Uno.UI.Runtime.Skia.MacOS/Native/NativeUno.cs b/src/Uno.UI.Runtime.Skia.MacOS/Native/NativeUno.cs
index 516b7e59ac3e..6ced261d415c 100644
--- a/src/Uno.UI.Runtime.Skia.MacOS/Native/NativeUno.cs
+++ b/src/Uno.UI.Runtime.Skia.MacOS/Native/NativeUno.cs
@@ -342,7 +342,7 @@ internal static unsafe partial void uno_set_window_close_callbacks(
internal static partial nint uno_native_create_sample(nint window, string text);
[LibraryImport("libUnoNativeMac.dylib")]
- internal static partial void uno_native_arrange(nint element, double arrangeLeft, double arrangeTop, double arrangeWidth, double arrangeHeight, double clipLeft, double clipTop, double clipWidth, double clipHeight);
+ internal static partial void uno_native_arrange(nint element, double arrangeLeft, double arrangeTop, double arrangeWidth, double arrangeHeight);
[LibraryImport("libUnoNativeMac.dylib")]
internal static partial void uno_native_attach(nint element);
@@ -358,10 +358,10 @@ internal static unsafe partial void uno_set_window_close_callbacks(
internal static partial void uno_native_set_opacity(nint element, double opacity);
[LibraryImport("libUnoNativeMac.dylib")]
- internal static partial void uno_native_set_visibility(nint element, [MarshalAs(UnmanagedType.I1)] bool visible);
+ internal static partial void uno_native_measure(nint element, double childWidth, double childHeight, double availableWidth, double availableHeight, out double width, out double height);
[LibraryImport("libUnoNativeMac.dylib")]
- internal static partial void uno_native_measure(nint element, double childWidth, double childHeight, double availableWidth, double availableHeight, out double width, out double height);
+ internal static partial void uno_native_dispose(nint element);
[LibraryImport("libUnoNativeMac.dylib")]
internal static unsafe partial nint uno_set_execute_callback(delegate* unmanaged[Cdecl] callback);
@@ -384,9 +384,6 @@ internal static unsafe partial void uno_set_webview_navigation_callbacks(
[LibraryImport("libUnoNativeMac.dylib", StringMarshalling = StringMarshalling.Utf8)]
internal static partial nint uno_webview_create(nint window, string ok, string cancel);
- [LibraryImport("libUnoNativeMac.dylib")]
- internal static partial void uno_webview_dispose(nint webview);
-
[LibraryImport("libUnoNativeMac.dylib", StringMarshalling = StringMarshalling.Utf8)]
internal static partial string uno_webview_get_title(nint webview);
diff --git a/src/Uno.UI.Runtime.Skia.MacOS/UI/Xaml/Controls/WebView/MacOSNativeWebView.cs b/src/Uno.UI.Runtime.Skia.MacOS/UI/Xaml/Controls/WebView/MacOSNativeWebView.cs
index b275c2e7b3e1..f48d06f0958d 100644
--- a/src/Uno.UI.Runtime.Skia.MacOS/UI/Xaml/Controls/WebView/MacOSNativeWebView.cs
+++ b/src/Uno.UI.Runtime.Skia.MacOS/UI/Xaml/Controls/WebView/MacOSNativeWebView.cs
@@ -59,7 +59,6 @@ public MacOSNativeWebView(MacOSWindowNative window, CoreWebView2 owner)
Unloaded += (s, e) =>
{
- NativeUno.uno_webview_dispose(NativeHandle);
_webViews.Remove(NativeHandle);
};
@@ -383,6 +382,10 @@ internal static unsafe void DidReceiveScriptMessage(nint handle, sbyte* messageB
var message = messageBody == null ? "" : new string(messageBody);
webview._owner.RaiseWebMessageReceived(message);
}
+ else if (typeof(MacOSNativeWebView).Log().IsEnabled(LogLevel.Warning))
+ {
+ typeof(MacOSNativeWebView).Log().Warn($"MacOSNativeWebView.DidReceiveScriptMessage could not map 0x{handle:X} with an WKWebView");
+ }
}
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
diff --git a/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOMediaPlayer.m b/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOMediaPlayer.m
index 56416b21849e..63d81aa68ec0 100644
--- a/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOMediaPlayer.m
+++ b/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOMediaPlayer.m
@@ -294,7 +294,7 @@ void uno_mediaplayer_set_view(UNOMediaPlayer *media, UNOMediaPlayerView *view, N
media.videoLayer.frame = view.frame;
media.videoLayer.videoGravity = kCAGravityResizeAspect;
[view.layer addSublayer:media.videoLayer];
- [window.contentViewController.view addSubview:view positioned:NSWindowAbove relativeTo:nil];
+ view.originalSuperView = window.contentViewController.view;
}
static NSMutableSet *players;
@@ -511,15 +511,15 @@ - (BOOL)wantsUpdateLayer
return true;
}
-@synthesize visible;
-
// UNONativeElement
-- (void)detach {
+- (void)dispose {
#if DEBUG
- NSLog(@"detach mediaplayer %p", self);
+ NSLog(@"UNOMediaPlayer %p disposing with superview %p", self, self.superview);
#endif
-// [self removeFromSuperview];
+ if (self.superview) {
+ [self removeFromSuperview];
+ }
}
- (void)layout {
@@ -536,4 +536,6 @@ - (void)layout {
}
}
+@synthesize originalSuperView;
+
@end
diff --git a/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNONative.h b/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNONative.h
index e9cdec54ebe7..858856fdcaaf 100644
--- a/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNONative.h
+++ b/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNONative.h
@@ -11,9 +11,9 @@ NS_ASSUME_NONNULL_BEGIN
@protocol UNONativeElement
-@property (nonatomic) bool visible;
+@property (nonatomic) NSView* originalSuperView;
--(void) detach;
+-(void) dispose;
@end
@@ -23,18 +23,18 @@ NS_ASSUME_NONNULL_BEGIN
NSView* uno_native_create_sample(NSWindow *window, const char* _Nullable text);
-void uno_native_arrange(NSView *element, double arrangeLeft, double arrangeTop, double arrangeWidth, double arrangeHeight, double clipLeft, double clipTop, double clipWidth, double clipHeight);
+void uno_native_arrange(NSView* element, double arrangeLeft, double arrangeTop, double arrangeWidth, double arrangeHeight);
-void uno_native_attach(NSView* element);
+void uno_native_attach(NSView* element);
-void uno_native_detach(NSView* element);
+void uno_native_detach(NSView* element);
-bool uno_native_is_attached(NSView* element);
+bool uno_native_is_attached(NSView* element);
-void uno_native_measure(NSView* element, double childWidth, double childHeight, double availableWidth, double availableHeight, double* width, double* height);
+void uno_native_measure(NSView* element, double childWidth, double childHeight, double availableWidth, double availableHeight, double* width, double* height);
-void uno_native_set_opacity(NSView* element, double opacity);
+void uno_native_set_opacity(NSView* element, double opacity);
-void uno_native_set_visibility(NSView* element, bool visible);
+void uno_native_dispose(NSView *element);
NS_ASSUME_NONNULL_END
diff --git a/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNONative.m b/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNONative.m
index 85a97d1ce4f9..7a61f265087a 100644
--- a/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNONative.m
+++ b/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNONative.m
@@ -5,9 +5,12 @@
#import "UNONative.h"
static NSMutableSet *elements;
+static NSMutableSet *transients;
@implementation UNORedView : NSView
+@synthesize originalSuperView;
+
// make the background red for easier tracking
- (BOOL)wantsUpdateLayer
{
@@ -19,12 +22,19 @@ - (void)updateLayer
self.layer.backgroundColor = NSColor.redColor.CGColor;
}
-@synthesize visible;
-
- (void)detach {
// nothing needed
}
+- (void)dispose {
+#if DEBUG
+ NSLog(@"UNORedView %p disposing with superview %p", self, self.superview);
+#endif
+ if (self.superview) {
+ [self removeFromSuperview];
+ }
+}
+
@end
NSView* uno_native_create_sample(NSWindow *window, const char* _Nullable text)
@@ -38,54 +48,63 @@ - (void)detach {
label.stringValue = [NSString stringWithUTF8String:text];
label.frame = NSMakeRect(0, 0, label.fittingSize.width, label.fittingSize.height);
- NSView* sample = [[UNORedView alloc] initWithFrame:label.frame];
+ UNORedView* sample = [[UNORedView alloc] initWithFrame:label.frame];
[sample addSubview:label];
#if DEBUG
NSLog(@"uno_native_create_sample #%p label: %@", sample, label.stringValue);
#endif
- [window.contentViewController.view addSubview:sample];
+ sample.originalSuperView = window.contentViewController.view;
return sample;
}
-void uno_native_arrange(NSView *element, double arrangeLeft, double arrangeTop, double arrangeWidth, double arrangeHeight, double clipLeft, double clipTop, double clipWidth, double clipHeight)
+void uno_native_arrange(NSView *element, double arrangeLeft, double arrangeTop, double arrangeWidth, double arrangeHeight)
{
NSRect arrange = NSMakeRect(arrangeLeft, arrangeTop, arrangeWidth, arrangeHeight);
element.frame = arrange;
#if DEBUG
- NSLog(@"uno_native_arrange %p arrange(%g,%g,%g,%g) clip(%g,%g,%g,%g)", element,
- arrangeLeft, arrangeTop, arrangeWidth, arrangeHeight,
- clipLeft, clipTop, clipWidth, clipHeight);
+ NSLog(@"uno_native_arrange %p arrange(%g,%g,%g,%g)", element,
+ arrangeLeft, arrangeTop, arrangeWidth, arrangeHeight);
#endif
}
-void uno_native_attach(NSView* element)
+void uno_native_attach(NSView* element)
{
#if DEBUG
- NSLog(@"uno_native_attach %p -> %s attached", element, [elements containsObject:element] ? "already" : "not previously");
+ NSLog(@"!!uno_native_attach %p", element);
#endif
+ bool already_attached = NO;
if (!elements) {
elements = [[NSMutableSet alloc] initWithCapacity:10];
+ } else {
+ already_attached = [elements containsObject:element];
}
- // note: it's too early to add a mask since the layer has not been set yet
- [elements addObject:element];
+#if DEBUG
+ NSLog(@"uno_native_attach %p -> %s attached", element, already_attached ? "already" : "not previously");
+#endif
+ if (!already_attached) {
+ // note: it's too early to add a mask since the layer has not been set yet
+ [elements addObject:element];
+ }
+ [element.originalSuperView addSubview:element];
}
-void uno_native_detach(NSView *element)
+void uno_native_detach(NSView* element)
{
#if DEBUG
NSLog(@"uno_native_detach %p", element);
#endif
element.layer.mask = nil;
- if (elements) {
- if ([element conformsToProtocol:@protocol(UNONativeElement)]) {
- id native = (id) element;
- [native detach];
- }
- [elements removeObject:element];
+
+ if (!transients) {
+ transients = [[NSMutableSet alloc] initWithCapacity:10];
}
+ // once removed from superview the instance can be freed by the runtime unless we keep another reference to it
+ [transients addObject:element];
+ [elements removeObject:element];
+ [element removeFromSuperview];
}
-bool uno_native_is_attached(NSView* element)
+bool uno_native_is_attached(NSView* element)
{
bool attached = elements ? [elements containsObject:element] : NO;
#if DEBUG
@@ -94,7 +113,7 @@ bool uno_native_is_attached(NSView* element)
return attached;
}
-void uno_native_measure(NSView* element, double childWidth, double childHeight, double availableWidth, double availableHeight, double* width, double* height)
+void uno_native_measure(NSView* element, double childWidth, double childHeight, double availableWidth, double availableHeight, double* width, double* height)
{
CGSize size = element.subviews.firstObject.frame.size;
*width = size.width;
@@ -104,7 +123,7 @@ void uno_native_measure(NSView* element, double childWidth, double childHeight,
#endif
}
-void uno_native_set_opacity(NSView* element, double opacity)
+void uno_native_set_opacity(NSView* element, double opacity)
{
#if DEBUG
NSLog(@"uno_native_set_opacity #%p : %g -> %g", element, element.alphaValue, opacity);
@@ -112,13 +131,11 @@ void uno_native_set_opacity(NSView* element, double opacity)
element.alphaValue = opacity;
}
-void uno_native_set_visibility(NSView* element, bool visible)
+void uno_native_dispose(NSView* element)
{
#if DEBUG
- NSLog(@"uno_native_set_visibility #%p : hidden %s -> visible %s", element, element.hidden ? "TRUE" : "FALSE", visible ? "TRUE" : "FALSE");
+ NSLog(@"uno_native_dispose #%p", element);
#endif
- element.visible = visible;
- // hidden is controlled by both visible and clipping
- if (!visible)
- element.hidden = true;
+ [element dispose];
+ [transients removeObject:element];
}
diff --git a/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOWebView.h b/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOWebView.h
index 6b525670cacb..0be3a68c8cd3 100644
--- a/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOWebView.h
+++ b/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOWebView.h
@@ -37,7 +37,6 @@ uno_webview_unsupported_scheme_identified_fn_ptr uno_get_webview_unsupported_sch
void uno_set_webview_unsupported_scheme_identified_callback(uno_webview_unsupported_scheme_identified_fn_ptr fn_ptr);
NSView* uno_webview_create(NSWindow *window, const char *ok, const char *cancel);
-void uno_webview_dispose(WKWebView *webview);
const char* uno_webview_get_title(WKWebView *webview);
diff --git a/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOWebView.m b/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOWebView.m
index 8d152118901f..f1a85722b804 100644
--- a/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOWebView.m
+++ b/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOWebView.m
@@ -95,16 +95,10 @@ void uno_set_webview_unsupported_scheme_identified_callback(uno_webview_unsuppor
webview.okString = [NSString stringWithUTF8String:ok];
webview.cancelString = [NSString stringWithUTF8String:cancel];
- [window.contentViewController.view addSubview:webview positioned:NSWindowBelow relativeTo:nil];
+ webview.originalSuperView = window.contentViewController.view;
return webview;
}
-void uno_webview_dispose(WKWebView *webview)
-{
- [webview stopLoading];
- [webview removeFromSuperview];
-}
-
const char* uno_webview_get_title(WKWebView *webview)
{
return strdup(webview.title.UTF8String);
@@ -335,10 +329,14 @@ - (void)scrollWheel:(NSEvent *)event {
// UNONativeElement
-- (void) detach {
+- (void) dispose {
#if DEBUG
- NSLog(@"detach webview %p", self);
+ NSLog(@"UNOWebView %p disposing with superview %p", self, self.superview);
#endif
+ if (self.superview) {
+ [self stopLoading];
+ [self removeFromSuperview];
+ }
[self.configuration.userContentController removeScriptMessageHandlerForName:@"unoWebView"];
}
@@ -573,6 +571,6 @@ - (void)userContentController:(WKUserContentController *)userContentController d
}
}
-@synthesize visible;
+@synthesize originalSuperView;
@end
diff --git a/src/Uno.UI.Runtime.Skia.WebAssembly.Browser/Native/BrowserNativeElementHostingExtension.cs b/src/Uno.UI.Runtime.Skia.WebAssembly.Browser/Native/BrowserNativeElementHostingExtension.cs
index de9cc7c1131e..cbdf245c60af 100644
--- a/src/Uno.UI.Runtime.Skia.WebAssembly.Browser/Native/BrowserNativeElementHostingExtension.cs
+++ b/src/Uno.UI.Runtime.Skia.WebAssembly.Browser/Native/BrowserNativeElementHostingExtension.cs
@@ -33,17 +33,12 @@ public void DetachNativeElement(object content)
NativeMethods.DetachNativeElement(((BrowserHtmlElement)content).ElementId);
}
- public void ArrangeNativeElement(object content, Windows.Foundation.Rect arrangeRect, Windows.Foundation.Rect clipRect)
+ public void ArrangeNativeElement(object content, Windows.Foundation.Rect arrangeRect)
{
Debug.Assert(content is BrowserHtmlElement);
NativeMethods.ArrangeNativeElement(((BrowserHtmlElement)content).ElementId, arrangeRect.X, arrangeRect.Y, arrangeRect.Width, arrangeRect.Height);
}
- public void ChangeNativeElementVisibility(object content, bool visible)
- {
- // no need to do anything here, airspace clipping logic will take care of it automatically
- }
-
public void ChangeNativeElementOpacity(object content, double opacity)
{
Debug.Assert(content is BrowserHtmlElement);
diff --git a/src/Uno.UI.Runtime.Skia.Win32/Native/Win32NativeElementHostingExtension.cs b/src/Uno.UI.Runtime.Skia.Win32/Native/Win32NativeElementHostingExtension.cs
index f67fd3e1514b..91d7c2c7376a 100644
--- a/src/Uno.UI.Runtime.Skia.Win32/Native/Win32NativeElementHostingExtension.cs
+++ b/src/Uno.UI.Runtime.Skia.Win32/Native/Win32NativeElementHostingExtension.cs
@@ -286,7 +286,7 @@ public void DetachNativeElement(object content)
((Win32WindowWrapper)XamlRootMap.GetHostForRoot(_presenter.XamlRoot!)!).RenderingNegativePathReevaluated -= OnRenderingNegativePathReevaluated;
}
- public void ArrangeNativeElement(object content, Rect arrangeRect, Rect clipRect)
+ public void ArrangeNativeElement(object content, Rect arrangeRect)
{
if (content is not Win32NativeWindow window)
{
@@ -347,11 +347,6 @@ public unsafe object CreateSampleComponent(string text)
return new Win32NativeWindow(hwnd);
}
- public void ChangeNativeElementVisibility(object content, bool visible)
- {
- // no need to do anything here, airspace clipping logic will take care of it automatically
- }
-
public void ChangeNativeElementOpacity(object content, double opacity)
{
if (content is not Win32NativeWindow window)
diff --git a/src/Uno.UI.Runtime.Skia.Wpf/Extensions/WpfNativeElementHostingExtension.cs b/src/Uno.UI.Runtime.Skia.Wpf/Extensions/WpfNativeElementHostingExtension.cs
index 38b8b1b620d5..cda63fef005d 100644
--- a/src/Uno.UI.Runtime.Skia.Wpf/Extensions/WpfNativeElementHostingExtension.cs
+++ b/src/Uno.UI.Runtime.Skia.Wpf/Extensions/WpfNativeElementHostingExtension.cs
@@ -60,14 +60,6 @@ public void DetachNativeElement(object content)
}
}
- public void ChangeNativeElementVisibility(object content, bool visible)
- {
- if (content is System.Windows.UIElement contentAsUIElement)
- {
- contentAsUIElement.Visibility = visible ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed;
- }
- }
-
public void ChangeNativeElementOpacity(object content, double opacity)
{
if (content is System.Windows.UIElement contentAsUIElement)
@@ -76,7 +68,7 @@ public void ChangeNativeElementOpacity(object content, double opacity)
}
}
- public void ArrangeNativeElement(object content, Windows.Foundation.Rect arrangeRect, Windows.Foundation.Rect clipRect)
+ public void ArrangeNativeElement(object content, Windows.Foundation.Rect arrangeRect)
{
if (content is System.Windows.UIElement contentAsUIElement)
{
diff --git a/src/Uno.UI.Runtime.Skia.X11/Native/X11NativeElementHostingExtension.cs b/src/Uno.UI.Runtime.Skia.X11/Native/X11NativeElementHostingExtension.cs
index d16838560a38..7cda7eca2422 100644
--- a/src/Uno.UI.Runtime.Skia.X11/Native/X11NativeElementHostingExtension.cs
+++ b/src/Uno.UI.Runtime.Skia.X11/Native/X11NativeElementHostingExtension.cs
@@ -18,7 +18,6 @@ namespace Uno.WinUI.Runtime.Skia.X11;
internal partial class X11NativeElementHostingExtension : ContentPresenter.INativeElementHostingExtension
{
private Rect? _lastArrangeRect;
- private Rect? _lastClipRect;
private bool _layoutDirty = true;
private readonly ContentPresenter _presenter;
@@ -142,7 +141,6 @@ public void DetachNativeElement(object content)
_ = XLib.XUnmapWindow(Display, nativeWindow.WindowId);
_ = XLib.XSync(Display, false);
- _lastClipRect = null;
_lastArrangeRect = null;
Debug.Assert(_frameRenderedDisposable is not null);
@@ -155,10 +153,9 @@ public void DetachNativeElement(object content)
}
}
- public void ArrangeNativeElement(object content, Rect arrangeRect, Rect clipRect)
+ public void ArrangeNativeElement(object content, Rect arrangeRect)
{
_lastArrangeRect = arrangeRect;
- _lastClipRect = clipRect;
_layoutDirty = true;
_presenter.Visual.Compositor.InvalidateRender(_presenter.Visual);
// we don't update the layout right now. We wait for the next render to happen, as
@@ -175,7 +172,6 @@ private void UpdateLayout()
_layoutDirty = false;
if (_presenter.Content is X11NativeWindow nativeWindow &&
_lastArrangeRect is { } arrangeRect &&
- _lastClipRect is { } clipRect &&
XamlRoot is { } xamlRoot &&
XamlRootMap.GetHostForRoot(xamlRoot) is X11XamlRootHost host)
{
@@ -198,11 +194,6 @@ XamlRoot is { } xamlRoot &&
public Size MeasureNativeElement(object content, Size childMeasuredSize, Size availableSize) => availableSize;
- public void ChangeNativeElementVisibility(object content, bool visible)
- {
- // no need to do anything here, airspace clipping logic will take care of it automatically
- }
-
// This doesn't seem to work as most (all?) WMs won't change the opacity for subwindows, only top-level windows
public void ChangeNativeElementOpacity(object content, double opacity)
{
diff --git a/src/Uno.UI/Helpers/SkiaRenderHelper.skia.cs b/src/Uno.UI/Helpers/SkiaRenderHelper.skia.cs
index ff7acda4f978..da79a82eab88 100644
--- a/src/Uno.UI/Helpers/SkiaRenderHelper.skia.cs
+++ b/src/Uno.UI/Helpers/SkiaRenderHelper.skia.cs
@@ -1,6 +1,7 @@
#nullable enable
using System;
+using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
@@ -17,6 +18,8 @@ internal static class SkiaRenderHelper
{
private static readonly SKPictureRecorder _recorder = new();
+ private static readonly List _emptyList = new();
+
// This is used all the time, on all platforms but X11, when no native elements are present - DO NOT MODIFY
private static readonly SKPath _emptyClipPath = new();
@@ -28,7 +31,7 @@ internal static class SkiaRenderHelper
internal static bool CanRecordPicture([NotNullWhen(true)] UIElement? rootElement) =>
rootElement is { IsArrangeDirtyOrArrangeDirtyPath: false, IsMeasureDirtyOrMeasureDirtyPath: false };
- internal static (IntPtr, SKPath) RecordPictureAndReturnPath(float width, float height, ContainerVisual rootVisual, bool invertPath)
+ internal static (IntPtr picture, SKPath nativeClipPath, List nativeVisualsInZOrder) RecordPictureAndReturnPath(float width, float height, ContainerVisual rootVisual, bool invertPath)
{
var canvas = _recorder.BeginRecording(new SKRect(-999999, -999999, 999999, 999999));
using var _ = new SKAutoCanvasRestore(canvas, true);
@@ -36,15 +39,13 @@ internal static (IntPtr, SKPath) RecordPictureAndReturnPath(float width, float h
rootVisual.Compositor.RenderRootVisual(canvas, rootVisual);
- var path = !ContentPresenter.HasNativeElements() ?
- !invertPath ?
- _emptyClipPath :
- GetOrUpdateInvertedClippingPath(width, height) :
+ var (path, nativeVisualsInZOrder) = !ContentPresenter.HasNativeElements() ?
+ (!invertPath ? _emptyClipPath : GetOrUpdateInvertedClippingPath(width, height), _emptyList) :
CalculateClippingPath(width, height, rootVisual, invertPath);
var picture = UnoSkiaApi.sk_picture_recorder_end_recording(_recorder.Handle);
- return (picture, path);
+ return (picture, path, nativeVisualsInZOrder);
}
internal static void RenderPicture(SKCanvas canvas, IntPtr picture, SKColor background, FpsHelper fpsHelper)
@@ -74,7 +75,7 @@ internal static void RenderPicture(SKCanvas canvas, IntPtr picture, SKColor back
///
/// Does a rendering cycle and returns a path that represents the visible area of the native views.
///
- private static SKPath CalculateClippingPath(float width, float height, ContainerVisual rootVisual, bool invertPath)
+ private static (SKPath nativeClipPath, List nativeVisualsInZOrder) CalculateClippingPath(float width, float height, ContainerVisual rootVisual, bool invertPath)
{
var clipPath = new SKPath();
@@ -84,11 +85,12 @@ private static SKPath CalculateClippingPath(float width, float height, Container
parentClipPath.Rewind();
parentClipPath.AddRect(rect);
- rootVisual.GetNativeViewPath(parentClipPath, clipPath);
+ var nativeVisualsInZOrder = new List();
+ rootVisual.GetNativeViewPathAndZOrder(parentClipPath, clipPath, nativeVisualsInZOrder);
if (!invertPath)
{
- return clipPath;
+ return (clipPath, nativeVisualsInZOrder);
}
else
{
@@ -98,7 +100,7 @@ private static SKPath CalculateClippingPath(float width, float height, Container
clipPath.Dispose();
- return invertedPath;
+ return (invertedPath, nativeVisualsInZOrder);
}
}
diff --git a/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/ContentPresenter.skia.cs b/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/ContentPresenter.skia.cs
index 7208fc3f7fed..f80577a2543a 100644
--- a/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/ContentPresenter.skia.cs
+++ b/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/ContentPresenter.skia.cs
@@ -1,12 +1,15 @@
using System;
+using System.Buffers;
using System.Collections.Generic;
-using System.Collections.Immutable;
+using System.Diagnostics;
using System.Linq;
using Uno.Disposables;
using Uno.Foundation.Extensibility;
using Uno.Foundation.Logging;
using Uno.UI;
using Windows.Foundation;
+using Microsoft.UI.Composition;
+using Microsoft.UI.Xaml.Media;
namespace Microsoft.UI.Xaml.Controls;
@@ -15,10 +18,8 @@ partial class ContentPresenter
private Lazy _nativeElementHostingExtension;
private static readonly HashSet _nativeHosts = new();
- private Rect _lastFinalRect;
-#if DEBUG
+ private (Rect layoutRect, int zOrder)? _lastNativeArrangeArgs;
private bool _nativeElementAttached;
-#endif
internal static bool HasNativeElements() => _nativeHosts.Count > 0;
@@ -42,8 +43,6 @@ partial void InitializePlatform()
});
}
- private IDisposable _nativeElementDisposable;
-
partial void TryRegisterNativeElement(object oldValue, object newValue)
{
if (IsNativeHost && IsInLiveTree)
@@ -78,76 +77,28 @@ partial void TryRegisterNativeElement(object oldValue, object newValue)
}
}
- private void ArrangeNativeElement()
- {
- if (!IsNativeHost)
- {
- // the ArrangeNativeElement call is queued on the dispatcher, so by the time we get here, the ContentPresenter
- // might no longer be a NativeHost
- return;
- }
- var arrangeRect = this.GetAbsoluteBoundsRect();
- var ev = GetParentViewport().Effective;
-
- Rect clipRect;
- if (ev.IsEmpty)
- {
- clipRect = new Rect(0, 0, 0, 0);
- }
- else if (ev.IsInfinite)
- {
- clipRect = null;
- }
- else
- {
- var top = Math.Min(Math.Max(0, ev.Y), ActualHeight);
- var height = Math.Max(0, Math.Min(ev.Height + ev.Y, ActualHeight - top));
- var left = Math.Min(Math.Max(0, ev.X), ActualWidth);
- var width = Math.Max(0, Math.Min(ev.Width + ev.X, ActualWidth - left));
- clipRect = new Rect(left, top, width, height);
- }
-
- var clipInGlobalCoordinates = new Rect(
- arrangeRect.X + clipRect.X,
- arrangeRect.Y + clipRect.Y,
- clipRect.Width,
- clipRect.Height);
- _lastFinalRect = arrangeRect.IntersectWith(clipInGlobalCoordinates) ?? new Rect(arrangeRect.X, arrangeRect.Y, 0, 0);
-
- _nativeElementHostingExtension.Value!.ArrangeNativeElement(
- Content,
- arrangeRect,
- clipRect);
- }
-
partial void AttachNativeElement()
{
#if DEBUG
global::System.Diagnostics.Debug.Assert(IsNativeHost && XamlRoot is not null && !_nativeElementAttached);
- _nativeElementAttached = true;
#endif
+ _nativeElementAttached = true;
_nativeElementHostingExtension.Value!.AttachNativeElement(Content);
_nativeHosts.Add(this);
- EffectiveViewportChanged += OnEffectiveViewportChanged;
- LayoutUpdated += OnLayoutUpdated;
- var visiblityToken = RegisterPropertyChangedCallback(HitTestVisibilityProperty, OnHitTestVisiblityChanged);
- _nativeElementDisposable = Disposable.Create(() =>
- {
- UnregisterPropertyChangedCallback(HitTestVisibilityProperty, visiblityToken);
- });
}
partial void DetachNativeElement(object content)
{
#if DEBUG
- global::System.Diagnostics.Debug.Assert(IsNativeHost && _nativeElementAttached);
- _nativeElementAttached = false;
+ global::System.Diagnostics.Debug.Assert(IsNativeHost);
#endif
+ _lastNativeArrangeArgs = null;
_nativeHosts.Remove(this);
- EffectiveViewportChanged -= OnEffectiveViewportChanged;
- LayoutUpdated -= OnLayoutUpdated;
- _nativeElementHostingExtension.Value!.DetachNativeElement(content);
- _nativeElementDisposable?.Dispose();
+ if (_nativeElementAttached)
+ {
+ _nativeElementAttached = false;
+ _nativeElementHostingExtension.Value!.DetachNativeElement(content);
+ }
}
private Size MeasureNativeElement(Size childMeasuredSize, Size availableSize)
@@ -165,11 +116,6 @@ private Size MeasureNativeElement(Size childMeasuredSize, Size availableSize)
return ret;
}
- private void OnHitTestVisiblityChanged(DependencyObject sender, DependencyProperty dp)
- {
- _nativeElementHostingExtension.Value!.ChangeNativeElementVisibility(Content, HitTestVisibility != HitTestability.Collapsed);
- }
-
internal static void UpdateNativeHostContentPresentersOpacities()
{
foreach (var contentPresenter in _nativeHosts)
@@ -186,19 +132,52 @@ internal static void UpdateNativeHostContentPresentersOpacities()
}
}
- private void OnLayoutUpdated(object sender, object e)
+ ///
+ /// is read-only and won't be modified.
+ ///
+ internal static void OnNativeHostsRenderOrderChanged(List nativeVisualsInZOrder)
{
- // Not quite sure why we need to queue the arrange call, but the native element either explodes or doesn't
- // respect alignments correctly otherwise. This is particularly relevant for the initial load.
- DispatcherQueue.TryEnqueue(ArrangeNativeElement);
- }
+ var rentedArray = ArrayPool<(int, ContentPresenter)>.Shared.Rent(_nativeHosts.Count);
+ using var _ = new DisposableStruct<(int, ContentPresenter)[]>(static rentedArray => ArrayPool<(int, ContentPresenter)>.Shared.Return(rentedArray, clearArray: true), rentedArray);
- private void OnEffectiveViewportChanged(FrameworkElement sender, EffectiveViewportChangedEventArgs args)
- {
- global::System.Diagnostics.Debug.Assert(IsNativeHost);
- // The arrange call here is queued because EVPChanged is fired before the layout of the ContentPresenter is updated,
- // so calling ArrangeNativeElement synchronously would get outdated coordinates.
- DispatcherQueue.TryEnqueue(ArrangeNativeElement);
+ var count = 0;
+ foreach (var host in _nativeHosts)
+ {
+ rentedArray[count++] = (nativeVisualsInZOrder.IndexOf(host.Visual), host);
+ }
+ new Span<(int, ContentPresenter)>(rentedArray, 0, _nativeHosts.Count).Sort((one, two) => one.Item1 - two.Item1);
+
+ for (var index = 0; index < _nativeHosts.Count; index++)
+ {
+ var host = rentedArray[index].Item2;
+
+ if (index == -1 && host._nativeElementAttached)
+ {
+ // We're detaching the native element as it's no longer in view, but conceptually, it's still in the tree, so IsNativeHost is still true
+ Debug.Assert(host.IsNativeHost);
+ host._nativeElementAttached = false;
+ host._lastNativeArrangeArgs = null;
+ host._nativeElementHostingExtension.Value!.DetachNativeElement(host.Content);
+ }
+ else if (host._nativeElementAttached)
+ {
+ host.DetachNativeElement(host.Content);
+ host.AttachNativeElement();
+ ArrangeNativeElement(host, index);
+ }
+ }
+
+ static void ArrangeNativeElement(ContentPresenter host, int zOrder)
+ {
+ var arrangeRect = host.GetAbsoluteBoundsRect();
+
+ var nativeArrangeArgs = (arrangeRect, zOrder);
+ if (host._lastNativeArrangeArgs != nativeArrangeArgs)
+ {
+ host._lastNativeArrangeArgs = nativeArrangeArgs;
+ host._nativeElementHostingExtension.Value!.ArrangeNativeElement(host.Content, arrangeRect);
+ }
+ }
}
internal object CreateSampleComponent(string text)
diff --git a/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/INativeElementHostingExtension.cs b/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/INativeElementHostingExtension.cs
index 180036ac11e5..0aa5d63e9d2f 100644
--- a/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/INativeElementHostingExtension.cs
+++ b/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/INativeElementHostingExtension.cs
@@ -13,14 +13,12 @@ internal interface INativeElementHostingExtension
void DetachNativeElement(object content);
- void ArrangeNativeElement(object content, Rect arrangeRect, Rect clipRect);
+ void ArrangeNativeElement(object content, Rect arrangeRect);
Size MeasureNativeElement(object content, Size childMeasuredSize, Size availableSize);
object CreateSampleComponent(string text);
- void ChangeNativeElementVisibility(object content, bool visible);
-
void ChangeNativeElementOpacity(object content, double opacity);
#endif
}
diff --git a/src/Uno.UI/UI/Xaml/Media/CompositionTarget.Rendering.skia.cs b/src/Uno.UI/UI/Xaml/Media/CompositionTarget.Rendering.skia.cs
index a8719bc4a2ac..c4aac9223161 100644
--- a/src/Uno.UI/UI/Xaml/Media/CompositionTarget.Rendering.skia.cs
+++ b/src/Uno.UI/UI/Xaml/Media/CompositionTarget.Rendering.skia.cs
@@ -1,9 +1,12 @@
#nullable enable
using System;
+using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading;
using Windows.Foundation;
+using Microsoft.UI.Composition;
+using Microsoft.UI.Xaml.Controls;
using SkiaSharp;
using Uno.Foundation.Logging;
using Uno.UI.Composition;
@@ -38,6 +41,8 @@ public partial class CompositionTarget
private Size _xamlRootBounds;
// only set and read under _xamlRootBoundsGate
private float _xamlRootRasterizationScale;
+ // only set and read on the UI thread
+ private List _nativeVisualsInZOrder = new();
internal event Action? FrameRendered;
@@ -78,11 +83,12 @@ private void Render()
var rootElement = ContentRoot.VisualTree.RootElement;
var bounds = ContentRoot.VisualTree.Size;
- var renderedFrame = SkiaRenderHelper.RecordPictureAndReturnPath(
+ var (picture, path, nativeVisualsInZOrder) = SkiaRenderHelper.RecordPictureAndReturnPath(
(float)bounds.Width,
(float)bounds.Height,
rootElement.Visual,
invertPath: FrameRenderingOptions.invertNativeElementClipPath);
+ var renderedFrame = (picture, path);
var previousFrame = default((IntPtr frame, SKPath path)?);
lock (_frameGate)
{
@@ -108,6 +114,25 @@ private void Render()
XamlRootMap.GetHostForRoot(rootElement.XamlRoot)?.InvalidateRender();
}
+ var nativeVisualsZOrderChanged = _nativeVisualsInZOrder.Count != nativeVisualsInZOrder.Count;
+ if (!nativeVisualsZOrderChanged)
+ {
+ for (int i = 0; i < nativeVisualsInZOrder.Count; i++)
+ {
+ if (nativeVisualsInZOrder[i] != _nativeVisualsInZOrder[i])
+ {
+ nativeVisualsZOrderChanged = true;
+ break;
+ }
+ }
+ }
+
+ if (nativeVisualsZOrderChanged)
+ {
+ _nativeVisualsInZOrder = nativeVisualsInZOrder;
+ ContentPresenter.OnNativeHostsRenderOrderChanged(nativeVisualsInZOrder);
+ }
+
this.LogTrace()?.Trace($"CompositionTarget#{GetHashCode()}: {nameof(Render)} ends");
}