Skip to content

Commit

Permalink
feat: Find associated VisualTree and XamlRoot for multiple roots
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinZikmund committed Jun 17, 2022
1 parent 4ac15a1 commit 6c09f4b
Show file tree
Hide file tree
Showing 13 changed files with 142 additions and 50 deletions.
12 changes: 8 additions & 4 deletions src/Uno.UI.Runtime.Skia.Wpf/Islands/UnoXamlHost.host.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,7 @@ private void InitializeHost()
}

protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);

{
if (!IsXamlContentLoaded())
{
return;
Expand Down Expand Up @@ -133,12 +131,18 @@ protected override void OnRender(DrawingContext drawingContext)
{
WinUI.Window.Current.Compositor.RenderVisual(surface, ChildInternal.Visual);
}
surface.Canvas.Flush();
}

// draw the bitmap to the screen
_bitmap.AddDirtyRect(new Int32Rect(0, 0, width, height));
_bitmap.AddDirtyRect(new Int32Rect(0, 0, _bitmap.PixelWidth, _bitmap.PixelHeight));
_bitmap.Unlock();


drawingContext.DrawImage(_bitmap, new Rect(0, 0, ActualWidth, ActualHeight));
drawingContext.DrawEllipse(Brushes.Blue, null, new Point(10, 10), 10, 10);
}

private Random _randomizer = new Random();
}
}
8 changes: 8 additions & 0 deletions src/Uno.UI.Runtime.Skia.Wpf/Islands/UnoXamlHostBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ protected WUX.UIElement ChildInternal
if (currentRoot != null)
{
currentRoot.SizeChanged -= XamlContentSizeChanged;
currentRoot.XamlRoot.InvalidateRender -= XamlRoot_InvalidateRender;
}

_childInternal = value;
Expand All @@ -187,6 +188,7 @@ protected WUX.UIElement ChildInternal
// If XAML content has changed, check XAML size
// to determine if UnoXamlHost needs to re-run layout.
frameworkElement.SizeChanged += XamlContentSizeChanged;
frameworkElement.XamlRoot.InvalidateRender += XamlRoot_InvalidateRender;
}

OnChildChanged();
Expand All @@ -196,6 +198,12 @@ protected WUX.UIElement ChildInternal
}
}

private void XamlRoot_InvalidateRender()
{
//InvalidateOverlays();
InvalidateVisual();
}

/// <summary>
/// Called when the property <seealso cref="ChildInternal"/> has changed.
/// </summary>
Expand Down
6 changes: 0 additions & 6 deletions src/Uno.UI.Runtime.Skia.Wpf/WpfIslandsHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,6 @@ void CreateApp(WinUI.ApplicationInitializationCallbackParams _)

WinUI.Application.Start(CreateApp, args);

WinUI.Window.InvalidateRender += () =>
{
InvalidateOverlays();
InvalidateVisual();
};

WpfApplication.Current.Activated += Current_Activated;
WpfApplication.Current.Deactivated += Current_Deactivated;
WpfApplication.Current.MainWindow.StateChanged += MainWindow_StateChanged;
Expand Down
4 changes: 2 additions & 2 deletions src/Uno.UI.RuntimeTests/MUX/Helpers/FlyoutHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ namespace Uno.UI.RuntimeTests.MUX.Helpers
{
internal static class FlyoutHelper
{
public static FrameworkElement GetOpenFlyoutPresenter()
public static FrameworkElement GetOpenFlyoutPresenter(XamlRoot xamlRoot)
{
#if NETFX_CORE
var popups = VisualTreeHelper.GetOpenPopups(Window.Current);
#else
var popups = VisualTreeHelper.GetOpenPopupsForXamlRoot(XamlRoot.Current);
var popups = VisualTreeHelper.GetOpenPopupsForXamlRoot(xamlRoot);
#endif
if (popups.Count != 1)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,7 @@ await RunOnUIThread.ExecuteAsync(() =>
#else
var flyoutPopup = VisualTreeHelper.GetOpenPopupsForXamlRoot(datePicker.XamlRoot)[0];
#endif
var datepickerFlyoutPresenter = GetDatePickerFlyoutPresenter();
var datepickerFlyoutPresenter = GetDatePickerFlyoutPresenter(datePicker.XamlRoot);

// The flyout popup, the flyout presenter and the button should have an RTL flow direction.
// The DatePicker itself should remain in LTR flow direction.
Expand Down Expand Up @@ -1096,9 +1096,9 @@ await RunOnUIThread.ExecuteAsync(() =>
});
}

private DatePickerFlyoutPresenter GetDatePickerFlyoutPresenter()
private DatePickerFlyoutPresenter GetDatePickerFlyoutPresenter(XamlRoot xamlRoot)
{
return FlyoutHelper.GetOpenFlyoutPresenter() as DatePickerFlyoutPresenter;
return FlyoutHelper.GetOpenFlyoutPresenter(xamlRoot) as DatePickerFlyoutPresenter;
}

private void VerifyDatesAreEqual(Calendar expected, DateTimeOffset actual)
Expand Down
31 changes: 16 additions & 15 deletions src/Uno.UI/UI/Xaml/Internal/VisualTree.cs
Original file line number Diff line number Diff line change
Expand Up @@ -675,22 +675,23 @@ private void RemoveRoot(UIElement root)
return rootVisual.AssociatedVisualTree;
}

DependencyObject? nextAncestor = null;
//TODO Uno: Uncomment and implement
//DependencyObject? nextAncestor = null;
//if (currentAncestor.DoesAllowMultipleAssociation() && currentAncestor.GetParentCount() > 1)
//{
// // We cannot travese up a tree through a multiply associated element. Our goal is to support DOs being
// // shared between XAML trees. We've seen cases where we traverse up the tree through CSetter objects,
// // so for now we allow the traversal if there's one unique parent. TODO: This could be fragile? Allowing
// // the traversal to happen when the parent count is 1 means that if this element gets another parent later,
// // we're now in an inconsistent state.
// // Bug 19548424: Investigate places where an element entering the tree doesn't have a unique VisualTree ptr
//}
//else
//{
// nextAncestor = currentAncestor.GetParentInternal(false /* public parent only */);
//}
if (false)//currentAncestor.DoesAllowMultipleAssociation() && currentAncestor.GetParentCount() > 1)
{
// We cannot travese up a tree through a multiply associated element. Our goal is to support DOs being
// shared between XAML trees. We've seen cases where we traverse up the tree through CSetter objects,
// so for now we allow the traversal if there's one unique parent. TODO: This could be fragile? Allowing
// the traversal to happen when the parent count is 1 means that if this element gets another parent later,
// we're now in an inconsistent state.
// Bug 19548424: Investigate places where an element entering the tree doesn't have a unique VisualTree ptr
}
else
{
nextAncestor = currentAncestor.GetParentInternal(false /* public parent only */);
}

//TODO Uno: Uncomment and implement
////
//// We have a few tricks to figure out which VisualTree an element may be associated with.
//// There is now a cached weak VisualTree pointer on each DO that we update when we do a live
Expand Down Expand Up @@ -743,7 +744,7 @@ private void RemoveRoot(UIElement root)
// }
//}

//currentAncestor = nextAncestor;
currentAncestor = nextAncestor;
}
if (result == null)
{
Expand Down
4 changes: 2 additions & 2 deletions src/Uno.UI/UI/Xaml/Media/VisualTreeHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -188,12 +188,12 @@ private static IReadOnlyList<Popup> GetOpenFlyoutPopups()

public static IReadOnlyList<Popup> GetOpenPopupsForXamlRoot(XamlRoot xamlRoot)
{
if (xamlRoot == XamlRoot.Current)
if (xamlRoot == Window.Current.RootElement.XamlRoot)
{
return GetOpenPopups(Window.Current);
}

return new Popup[0];
return Array.Empty<Popup>();
}


Expand Down
8 changes: 4 additions & 4 deletions src/Uno.UI/UI/Xaml/UIElement.Layout.netstd.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public void InvalidateMeasure()
(this.GetParent() as UIElement)?.InvalidateMeasure();
if (IsVisualTreeRoot)
{
Window.InvalidateMeasure(this);
XamlRoot.InvalidateMeasure();
}
}
}
Expand All @@ -70,7 +70,7 @@ internal void InvalidateParentMeasureDirtyPath()
}
else if (IsVisualTreeRoot)
{
Window.InvalidateMeasure(XamlRoot);
XamlRoot.InvalidateMeasure();
}
}

Expand All @@ -97,7 +97,7 @@ public void InvalidateArrange()
(this.GetParent() as UIElement)?.InvalidateArrange();
if (IsVisualTreeRoot)
{
Window.InvalidateArrange(this);
XamlRoot.InvalidateArrange();
}
}
}
Expand All @@ -124,7 +124,7 @@ private void InvalidateParentArrangeDirtyPath()
}
else if (IsVisualTreeRoot)
{
Window.InvalidateArrange(this);
XamlRoot.InvalidateArrange();
}
}

Expand Down
28 changes: 25 additions & 3 deletions src/Uno.UI/UI/Xaml/UIElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ private static readonly Dictionary<Type, RoutedEventFlag> ImplementedRoutedEvent
private static readonly Type[] _bringIntoViewRequestedArgs = new[] { typeof(BringIntoViewRequestedEventArgs) };

private readonly SerialDisposable _clipSubscription = new SerialDisposable();
private XamlRoot _xamlRoot = null;
private string _uid;
private WeakReference<VisualTree> _visualTreeWeakReference;

Expand Down Expand Up @@ -212,8 +211,31 @@ string IXUidProvider.Uid

public XamlRoot XamlRoot
{
get => _xamlRoot ?? XamlRoot.Current;
set => _xamlRoot = value;
get
{
var visualTree = VisualTree.GetForElement(this);
if (visualTree is not null)
{
var xamlRoot = visualTree.GetOrCreateXamlRoot();
return xamlRoot;
}

return null;
}
set
{
if (XamlRoot == value)
{
return;
}

if (XamlRoot is not null)
{
throw new InvalidOperationException("Cannot change XamlRoot for existing element");
}

// TODO:MZ: It should be possible to set XamlRoot when still null.
}
}

#region VirtualizationInformation
Expand Down
11 changes: 5 additions & 6 deletions src/Uno.UI/UI/Xaml/Window.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ public UIElement Content
oldRoot.SizeChanged -= RootSizeChanged;
}
}

if (value != null)
{
value.IsWindowRoot = true;
Expand All @@ -100,9 +101,10 @@ public UIElement Content

if (value is FrameworkElement newRoot)
{
newRoot.SizeChanged += RootSizeChanged;
newRoot.SizeChanged += RootSizeChanged;
}
XamlRoot.Current.NotifyChanged();

(value?.XamlRoot ?? oldContent?.XamlRoot)?.NotifyChanged();
}
}

Expand Down Expand Up @@ -221,10 +223,7 @@ internal void OnVisibilityChanged(bool newVisibility)
}
}

private void RootSizeChanged(object sender, SizeChangedEventArgs args)
{
XamlRoot.Current.NotifyChanged();
}
private void RootSizeChanged(object sender, SizeChangedEventArgs args) => _rootVisual.XamlRoot.NotifyChanged();

private void RaiseSizeChanged(Windows.UI.Core.WindowSizeChangedEventArgs windowSizeChangedEventArgs)
{
Expand Down
4 changes: 4 additions & 0 deletions src/Uno.UI/UI/Xaml/Window.skia.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ public void Init()
{
Dispatcher = CoreDispatcher.Main;
CoreWindow = CoreWindow.GetOrCreateForCurrentThread();
InitDragAndDrop();
}
CoreWindow.SetInvalidateRender(QueueInvalidateRender);

InitializeCommon();
Expand Down Expand Up @@ -95,6 +97,8 @@ private void InternalSetContent(UIElement content)
throw new InvalidOperationException("The root visual could not be created.");
}

CoreWindow.SetInvalidateRender(_rootVisual.XamlRoot.QueueInvalidateRender);

UIElement.LoadingRootElement(_rootVisual);

Compositor.RootVisual = _rootVisual.Visual;
Expand Down
46 changes: 44 additions & 2 deletions src/Uno.UI/UI/Xaml/XamlRoot.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#nullable enable

using Uno.UI.Xaml.Core;
using Uno.UI.Xaml.Islands;
using Windows.Foundation;

namespace Windows.UI.Xaml;
Expand All @@ -18,11 +19,52 @@ internal XamlRoot(VisualTree visualTree)
VisualTree = visualTree;
}

/// <summary>
/// Occurs when a property of XamlRoot has changed.
/// </summary>
public event TypedEventHandler<XamlRoot, XamlRootChangedEventArgs>? Changed;

public UIElement? Content => Window.Current?.Content;
public UIElement? Content => VisualTree.PublicRootVisual;

public Size Size => Content?.RenderSize ?? Size.Empty;
//TODO Uno specific: This is most likely not implemented here in MUX:
public Size Size
{
get
{
var rootElement = VisualTree.RootElement;
if (rootElement is RootVisual rootVisual)
{
//TODO:MZ: Support multiple windows!
return Window.Current.Bounds.Size;
}
else if (rootElement is XamlIslandRoot xamlIslandRoot)
{
return new Size(xamlIslandRoot.ActualSize.X, xamlIslandRoot.ActualSize.Y);
}

return default;
}
}

//TODO Uno specific: This is most likely not implemented here in MUX:
internal Rect Bounds
{
get
{
var rootElement = VisualTree.RootElement;
if (rootElement is RootVisual rootVisual)
{
//TODO:MZ: Support multiple windows!
return Window.Current.Bounds;
}
else if (rootElement is XamlIslandRoot xamlIslandRoot)
{
return new Rect(0, 0, xamlIslandRoot.ActualSize.X, xamlIslandRoot.ActualSize.Y);
}

return default;
}
}

public double RasterizationScale
=> global::Windows.Graphics.Display.DisplayInformation.GetForCurrentView().RawPixelsPerViewPixel;
Expand Down
Loading

0 comments on commit 6c09f4b

Please sign in to comment.