diff --git a/build/Base.props b/build/Base.props index a60373ebb3..100c9088cd 100644 --- a/build/Base.props +++ b/build/Base.props @@ -1,5 +1,6 @@  + diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs index 87c9ed0bae..558e96e132 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs @@ -148,6 +148,19 @@ namespace Avalonia.Rendering.SceneGraph return (VisualNode)node; } + private static object GetOrCreateChildNode(Scene scene, IVisual child, VisualNode parent) + { + var result = (VisualNode)scene.FindNode(child); + + if (result != null && result.Parent != parent) + { + Deindex(scene, result); + result = null; + } + + return result ?? CreateNode(scene, child, parent); + } + private static void Update(DrawingContext context, Scene scene, VisualNode node, Rect clip, bool forceRecurse) { var visual = node.Visual; @@ -231,7 +244,7 @@ namespace Avalonia.Rendering.SceneGraph { foreach (var child in visual.VisualChildren.OrderBy(x => x, ZIndexComparer.Instance)) { - var childNode = scene.FindNode(child) ?? CreateNode(scene, child, node); + var childNode = GetOrCreateChildNode(scene, child, node); Update(context, scene, (VisualNode)childNode, clip, forceRecurse); } diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs index d342f2eb2e..d2a9e0a673 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs @@ -119,6 +119,11 @@ namespace Avalonia.Rendering.SceneGraph throw new ObjectDisposedException("Visual node for {node.Visual}"); } + if (child.Parent != this) + { + throw new AvaloniaInternalException("VisualNode added to wrong parent."); + } + EnsureChildrenCreated(); _children.Add(child); } @@ -155,6 +160,11 @@ namespace Avalonia.Rendering.SceneGraph throw new ObjectDisposedException("Visual node for {node.Visual}"); } + if (node.Parent != this) + { + throw new AvaloniaInternalException("VisualNode added to wrong parent."); + } + EnsureChildrenCreated(); _children[index] = node; } diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index d37bec3334..2c6425e26c 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -1031,6 +1031,9 @@ namespace Avalonia.Win32.Interop [DllImport("shcore.dll")] public static extern long GetDpiForMonitor(IntPtr hmonitor, MONITOR_DPI_TYPE dpiType, out uint dpiX, out uint dpiY); + [DllImport("gdi32.dll")] + public static extern int GetDeviceCaps(IntPtr hdc, DEVICECAP nIndex); + [DllImport("shcore.dll")] public static extern void GetScaleFactorForMonitor(IntPtr hMon, out uint pScale); @@ -1147,6 +1150,12 @@ namespace Avalonia.Win32.Interop } } + public enum DEVICECAP + { + HORZRES = 8, + DESKTOPHORZRES = 118 + } + public enum PROCESS_DPI_AWARENESS { PROCESS_DPI_UNAWARE = 0, diff --git a/src/Windows/Avalonia.Win32/ScreenImpl.cs b/src/Windows/Avalonia.Win32/ScreenImpl.cs index e77aa07bcd..df4cf7fa19 100644 --- a/src/Windows/Avalonia.Win32/ScreenImpl.cs +++ b/src/Windows/Avalonia.Win32/ScreenImpl.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using Avalonia.Platform; +using Avalonia.Win32.Interop; using static Avalonia.Win32.Interop.UnmanagedMethods; namespace Avalonia.Win32 @@ -28,9 +29,28 @@ namespace Avalonia.Win32 (IntPtr monitor, IntPtr hdcMonitor, ref Rect lprcMonitor, IntPtr data) => { MONITORINFO monitorInfo = MONITORINFO.Create(); - if (GetMonitorInfo(monitor,ref monitorInfo)) + if (GetMonitorInfo(monitor, ref monitorInfo)) { - GetDpiForMonitor(monitor, MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI, out var x, out _); + var dpi = 1.0; + + var shcore = LoadLibrary("shcore.dll"); + var method = GetProcAddress(shcore, nameof(GetDpiForMonitor)); + if (method != IntPtr.Zero) + { + GetDpiForMonitor(monitor, MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI, out var x, out _); + dpi = (double)x; + } + else + { + var hdc = GetDC(IntPtr.Zero); + + double virtW = GetDeviceCaps(hdc, DEVICECAP.HORZRES); + double physW = GetDeviceCaps(hdc, DEVICECAP.DESKTOPHORZRES); + + dpi = (96d * physW / virtW); + + ReleaseDC(IntPtr.Zero, hdc); + } RECT bounds = monitorInfo.rcMonitor; RECT workingArea = monitorInfo.rcWork; @@ -40,7 +60,7 @@ namespace Avalonia.Win32 new PixelRect(workingArea.left, workingArea.top, workingArea.right - workingArea.left, workingArea.bottom - workingArea.top); screens[index] = - new WinScreen((double)x / 96.0d, avaloniaBounds, avaloniaWorkArea, monitorInfo.dwFlags == 1, + new WinScreen(dpi / 96.0d, avaloniaBounds, avaloniaWorkArea, monitorInfo.dwFlags == 1, monitor); index++; } diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs index 4d23c57eed..f57c73c45c 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs @@ -17,7 +17,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph public void Should_Add_VisualNode() { var parent = new VisualNode(new TestRoot(), null); - var child = new VisualNode(Mock.Of(), null); + var child = new VisualNode(Mock.Of(), parent); var layers = new SceneLayers(parent.Visual); var target = new DeferredDrawingContextImpl(null, layers); @@ -32,7 +32,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph public void Should_Not_Replace_Identical_VisualNode() { var parent = new VisualNode(new TestRoot(), null); - var child = new VisualNode(Mock.Of(), null); + var child = new VisualNode(Mock.Of(), parent); var layers = new SceneLayers(parent.Visual); parent.AddChild(child); @@ -50,8 +50,8 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph public void Should_Replace_Different_VisualNode() { var parent = new VisualNode(new TestRoot(), null); - var child1 = new VisualNode(Mock.Of(), null); - var child2 = new VisualNode(Mock.Of(), null); + var child1 = new VisualNode(Mock.Of(), parent); + var child2 = new VisualNode(Mock.Of(), parent); var layers = new SceneLayers(parent.Visual); parent.AddChild(child1); @@ -78,8 +78,8 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph var layers = new SceneLayers(root); var target = new DeferredDrawingContextImpl(null, layers); - var child1 = new VisualNode(Mock.Of(), null) { LayerRoot = root }; - var child2 = new VisualNode(Mock.Of(), null) { LayerRoot = root }; + var child1 = new VisualNode(Mock.Of(), node) { LayerRoot = root }; + var child2 = new VisualNode(Mock.Of(), node) { LayerRoot = root }; target.BeginUpdate(node); using (target.BeginUpdate(child1)) { } diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs index 5fd14e9ea9..327ef98c4d 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs @@ -620,10 +620,9 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph border1.IsVisible = false; scene = scene.CloneScene(); - - var panelNode = (VisualNode)scene.FindNode(panel); sceneBuilder.Update(scene, decorator); + var panelNode = (VisualNode)scene.FindNode(panel); Assert.Equal(2, panelNode.Children.Count); Assert.False(panelNode.Children[0].Disposed); Assert.False(panelNode.Children[1].Disposed); diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs index 3211c3397b..4ec3630053 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs @@ -24,7 +24,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph var node = new VisualNode(Mock.Of(), null); var collection = node.Children; - node.AddChild(Mock.Of()); + node.AddChild(Mock.Of(x => x.Parent == node)); Assert.NotSame(collection, node.Children); } @@ -106,9 +106,9 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph public void TrimChildren_Should_Work_Correctly() { var parent = new VisualNode(Mock.Of(), null); - var child1 = new VisualNode(Mock.Of(), null); - var child2 = new VisualNode(Mock.Of(), null); - var child3 = new VisualNode(Mock.Of(), null); + var child1 = new VisualNode(Mock.Of(), parent); + var child2 = new VisualNode(Mock.Of(), parent); + var child3 = new VisualNode(Mock.Of(), parent); parent.AddChild(child1); parent.AddChild(child2);