From 312f1250e27a08b01e2a1a594531e560fab5820c Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 30 Mar 2023 00:25:18 +0600 Subject: [PATCH 1/5] Don't use AvaloniaObject in IPlatformRenderInterface --- src/Avalonia.Base/CombinedGeometry.cs | 14 +++++--------- src/Avalonia.Base/Media/GeometryGroup.cs | 5 ++++- .../Platform/IPlatformRenderInterface.cs | 4 ++-- .../HeadlessPlatformRenderInterface.cs | 4 ++-- src/Skia/Avalonia.Skia/CombinedGeometryImpl.cs | 7 ++++--- src/Skia/Avalonia.Skia/GeometryGroupImpl.cs | 7 ++++--- src/Skia/Avalonia.Skia/PlatformRenderInterface.cs | 4 ++-- .../Avalonia.Direct2D1/Direct2D1Platform.cs | 4 ++-- .../Media/CombinedGeometryImpl.cs | 13 +++++++------ .../Avalonia.Direct2D1/Media/GeometryGroupImpl.cs | 7 ++++--- .../VisualTree/MockRenderInterface.cs | 4 ++-- tests/Avalonia.Benchmarks/NullRenderingPlatform.cs | 4 ++-- .../MockPlatformRenderInterface.cs | 4 ++-- 13 files changed, 42 insertions(+), 39 deletions(-) diff --git a/src/Avalonia.Base/CombinedGeometry.cs b/src/Avalonia.Base/CombinedGeometry.cs index 8f080d05c7..4b5866519b 100644 --- a/src/Avalonia.Base/CombinedGeometry.cs +++ b/src/Avalonia.Base/CombinedGeometry.cs @@ -152,19 +152,15 @@ namespace Avalonia.Media var g1 = Geometry1; var g2 = Geometry2; - if (g1 is object && g2 is object) + if (g1?.PlatformImpl != null && g2?.PlatformImpl != null) { var factory = AvaloniaLocator.Current.GetRequiredService(); - return factory.CreateCombinedGeometry(GeometryCombineMode, g1, g2); + return factory.CreateCombinedGeometry(GeometryCombineMode, g1.PlatformImpl, g2.PlatformImpl); } - else if (GeometryCombineMode == GeometryCombineMode.Intersect) - return null; - else if (g1 is object) - return g1.PlatformImpl; - else if (g2 is object) - return g2.PlatformImpl; - else + + if (GeometryCombineMode == GeometryCombineMode.Intersect) return null; + return g1?.PlatformImpl ?? g2?.PlatformImpl; } } } diff --git a/src/Avalonia.Base/Media/GeometryGroup.cs b/src/Avalonia.Base/Media/GeometryGroup.cs index 0326e606f4..3e61413919 100644 --- a/src/Avalonia.Base/Media/GeometryGroup.cs +++ b/src/Avalonia.Base/Media/GeometryGroup.cs @@ -78,7 +78,10 @@ namespace Avalonia.Media { var factory = AvaloniaLocator.Current.GetRequiredService(); - return factory.CreateGeometryGroup(FillRule, _children); + var children = new IGeometryImpl?[_children.Count]; + for (var c = 0; c < _children.Count; c++) + children[c] = _children[c].PlatformImpl; + return factory.CreateGeometryGroup(FillRule, children!); } return null; diff --git a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs index cfc7fac3ea..81fe2c046f 100644 --- a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs +++ b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs @@ -48,7 +48,7 @@ namespace Avalonia.Platform /// The fill rule. /// The geometries to group. /// A combined geometry. - IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList children); + IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList children); /// /// Creates a geometry group implementation. @@ -57,7 +57,7 @@ namespace Avalonia.Platform /// The first geometry. /// The second geometry. /// A combined geometry. - IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, Geometry g1, Geometry g2); + IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, IGeometryImpl g1, IGeometryImpl g2); /// /// Created a geometry implementation for the glyph run. diff --git a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs index f8100d3832..431989134a 100644 --- a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs +++ b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs @@ -47,8 +47,8 @@ namespace Avalonia.Headless } public IStreamGeometryImpl CreateStreamGeometry() => new HeadlessStreamingGeometryStub(); - public IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList children) => throw new NotImplementedException(); - public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, Geometry g1, Geometry g2) => throw new NotImplementedException(); + public IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList children) => throw new NotImplementedException(); + public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, IGeometryImpl g1, IGeometryImpl g2) => throw new NotImplementedException(); public IRenderTarget CreateRenderTarget(IEnumerable surfaces) => new HeadlessRenderTarget(); public bool IsLost => false; diff --git a/src/Skia/Avalonia.Skia/CombinedGeometryImpl.cs b/src/Skia/Avalonia.Skia/CombinedGeometryImpl.cs index 1a4f467f40..1e9240ec70 100644 --- a/src/Skia/Avalonia.Skia/CombinedGeometryImpl.cs +++ b/src/Skia/Avalonia.Skia/CombinedGeometryImpl.cs @@ -1,4 +1,5 @@ using Avalonia.Media; +using Avalonia.Platform; using SkiaSharp; namespace Avalonia.Skia @@ -15,10 +16,10 @@ namespace Avalonia.Skia Bounds = (stroke ?? fill)?.TightBounds.ToAvaloniaRect() ?? default; } - public static CombinedGeometryImpl ForceCreate(GeometryCombineMode combineMode, Geometry g1, Geometry g2) + public static CombinedGeometryImpl ForceCreate(GeometryCombineMode combineMode, IGeometryImpl g1, IGeometryImpl g2) { - if (g1.PlatformImpl is GeometryImpl i1 - && g2.PlatformImpl is GeometryImpl i2 + if (g1 is GeometryImpl i1 + && g2 is GeometryImpl i2 && TryCreate(combineMode, i1, i2) is { } result) return result; diff --git a/src/Skia/Avalonia.Skia/GeometryGroupImpl.cs b/src/Skia/Avalonia.Skia/GeometryGroupImpl.cs index 01be42bad0..200247095f 100644 --- a/src/Skia/Avalonia.Skia/GeometryGroupImpl.cs +++ b/src/Skia/Avalonia.Skia/GeometryGroupImpl.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Avalonia.Media; +using Avalonia.Platform; using SkiaSharp; namespace Avalonia.Skia @@ -9,7 +10,7 @@ namespace Avalonia.Skia /// internal class GeometryGroupImpl : GeometryImpl { - public GeometryGroupImpl(FillRule fillRule, IReadOnlyList children) + public GeometryGroupImpl(FillRule fillRule, IReadOnlyList children) { var fillType = fillRule == FillRule.NonZero ? SKPathFillType.Winding : SKPathFillType.EvenOdd; var count = children.Count; @@ -22,7 +23,7 @@ namespace Avalonia.Skia bool requiresFillPass = false; for (var i = 0; i < count; ++i) { - if (children[i].PlatformImpl is GeometryImpl geo) + if (children[i] is GeometryImpl geo) { if (geo.StrokePath != null) stroke.AddPath(geo.StrokePath); @@ -42,7 +43,7 @@ namespace Avalonia.Skia for (var i = 0; i < count; ++i) { - if (children[i].PlatformImpl is GeometryImpl { FillPath: { } fillPath }) + if (children[i] is GeometryImpl { FillPath: { } fillPath }) fill.AddPath(fillPath); } diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index 9c4b326f14..a9a79ff0c5 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -58,12 +58,12 @@ namespace Avalonia.Skia return new StreamGeometryImpl(); } - public IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList children) + public IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList children) { return new GeometryGroupImpl(fillRule, children); } - public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, Geometry g1, Geometry g2) + public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, IGeometryImpl g1, IGeometryImpl g2) { return CombinedGeometryImpl.ForceCreate(combineMode, g1, g2); } diff --git a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs index 287db92b4d..826296b055 100644 --- a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs +++ b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs @@ -158,8 +158,8 @@ namespace Avalonia.Direct2D1 public IGeometryImpl CreateLineGeometry(Point p1, Point p2) => new LineGeometryImpl(p1, p2); public IGeometryImpl CreateRectangleGeometry(Rect rect) => new RectangleGeometryImpl(rect); public IStreamGeometryImpl CreateStreamGeometry() => new StreamGeometryImpl(); - public IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList children) => new GeometryGroupImpl(fillRule, children); - public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, Geometry g1, Geometry g2) => new CombinedGeometryImpl(combineMode, g1, g2); + public IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList children) => new GeometryGroupImpl(fillRule, children); + public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, IGeometryImpl g1, IGeometryImpl g2) => new CombinedGeometryImpl(combineMode, g1, g2); public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos, Point baselineOrigin) diff --git a/src/Windows/Avalonia.Direct2D1/Media/CombinedGeometryImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/CombinedGeometryImpl.cs index 5a13c10bbc..b1ce160707 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/CombinedGeometryImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/CombinedGeometryImpl.cs @@ -1,3 +1,4 @@ +using Avalonia.Platform; using SharpDX.Direct2D1; using AM = Avalonia.Media; @@ -13,19 +14,19 @@ namespace Avalonia.Direct2D1.Media /// public CombinedGeometryImpl( AM.GeometryCombineMode combineMode, - AM.Geometry geometry1, - AM.Geometry geometry2) + IGeometryImpl geometry1, + IGeometryImpl geometry2) : base(CreateGeometry(combineMode, geometry1, geometry2)) { } private static Geometry CreateGeometry( AM.GeometryCombineMode combineMode, - AM.Geometry geometry1, - AM.Geometry geometry2) + IGeometryImpl geometry1, + IGeometryImpl geometry2) { - var g1 = ((GeometryImpl)geometry1.PlatformImpl).Geometry; - var g2 = ((GeometryImpl)geometry2.PlatformImpl).Geometry; + var g1 = ((GeometryImpl)geometry1).Geometry; + var g2 = ((GeometryImpl)geometry2).Geometry; var dest = new PathGeometry(Direct2D1Platform.Direct2D1Factory); using var sink = dest.Open(); g1.Combine(g2, (CombineMode)combineMode, sink); diff --git a/src/Windows/Avalonia.Direct2D1/Media/GeometryGroupImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/GeometryGroupImpl.cs index 352708bf03..5e49ef6d4e 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/GeometryGroupImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/GeometryGroupImpl.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Avalonia.Platform; using SharpDX.Direct2D1; using AM = Avalonia.Media; @@ -12,19 +13,19 @@ namespace Avalonia.Direct2D1.Media /// /// Initializes a new instance of the class. /// - public GeometryGroupImpl(AM.FillRule fillRule, IReadOnlyList geometry) + public GeometryGroupImpl(AM.FillRule fillRule, IReadOnlyList geometry) : base(CreateGeometry(fillRule, geometry)) { } - private static Geometry CreateGeometry(AM.FillRule fillRule, IReadOnlyList children) + private static Geometry CreateGeometry(AM.FillRule fillRule, IReadOnlyList children) { var count = children.Count; var c = new Geometry[count]; for (var i = 0; i < count; ++i) { - c[i] = ((GeometryImpl)children[i].PlatformImpl).Geometry; + c[i] = ((GeometryImpl)children[i]).Geometry; } return new GeometryGroup(Direct2D1Platform.Direct2D1Factory, (FillMode)fillRule, c); diff --git a/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs b/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs index 481b98a0b2..d494c47a55 100644 --- a/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs +++ b/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs @@ -30,12 +30,12 @@ namespace Avalonia.Base.UnitTests.VisualTree return new MockStreamGeometry(); } - public IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList children) + public IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList children) { throw new NotImplementedException(); } - public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, Geometry g1, Geometry g2) + public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, IGeometryImpl g1, IGeometryImpl g2) { throw new NotImplementedException(); } diff --git a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs index e5cbae4ae7..d40abd9f47 100644 --- a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs +++ b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs @@ -32,12 +32,12 @@ namespace Avalonia.Benchmarks return new MockStreamGeometryImpl(); } - public IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList children) + public IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList children) { throw new NotImplementedException(); } - public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, Geometry g1, Geometry g2) + public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, IGeometryImpl g1, IGeometryImpl g2) { throw new NotImplementedException(); } diff --git a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs index df128b8ae3..720755f2b0 100644 --- a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs +++ b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs @@ -72,12 +72,12 @@ namespace Avalonia.UnitTests return new MockStreamGeometryImpl(); } - public IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList children) + public IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList children) { return Mock.Of(); } - public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, Geometry g1, Geometry g2) + public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, IGeometryImpl g1, IGeometryImpl g2) { return Mock.Of(); } From cd5307aa8c4a4ae08e7e2e808617731180527e3c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 30 Mar 2023 18:36:23 +0200 Subject: [PATCH 2/5] Added failing test. --- .../AvaloniaObjectTests_Inheritance.cs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Inheritance.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Inheritance.cs index c5cbda5325..707e1d4470 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Inheritance.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Inheritance.cs @@ -1,5 +1,10 @@ using System.Collections.Generic; +using Avalonia.Controls; +using Avalonia.Controls.Documents; using Avalonia.Data; +using Avalonia.Media; +using Avalonia.Styling; +using Avalonia.UnitTests; using Xunit; namespace Avalonia.Base.UnitTests @@ -90,6 +95,31 @@ namespace Avalonia.Base.UnitTests Assert.Equal(1, raised); } + [Fact] + public void ClearValue_On_Parent_Raises_PropertyChanged_On_Child_With_Inherited_Grandparent_Value() + { + var grandparent = new Class1(); + var parent = new Class2 { Parent = grandparent }; + var child = new Class2 { Parent = parent }; + var raised = 0; + + grandparent.SetValue(Class1.BazProperty, "grandparent"); + parent.SetValue(Class1.BazProperty, "parent"); + + child.PropertyChanged += (s, e) => + { + Assert.Same(child, e.Sender); + Assert.Equal("parent", e.OldValue); + Assert.Equal("grandparent", e.NewValue); + Assert.Equal(BindingPriority.Inherited, e.Priority); + ++raised; + }; + + parent.ClearValue(Class1.BazProperty); + + Assert.Equal(1, raised); + } + [Fact] public void Setting_InheritanceParent_Raises_PropertyChanged_When_Parent_Has_Value_Set() { From 683afe6e9fdfa265e44289d3f76dc297f117584c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 30 Mar 2023 18:45:48 +0200 Subject: [PATCH 3/5] Use correct newValue on inherited property change. - `oldValue` was misnamed - it's the new value - We calculated the new value and then didn't pass it to children for their property changed events: they were using the default value which may be wrong if the value is set further up the tree --- src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs | 12 ++++++------ src/Avalonia.Base/PropertyStore/ValueStore.cs | 8 +++++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs b/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs index 330034f51d..6f0f6702e5 100644 --- a/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs +++ b/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs @@ -140,24 +140,24 @@ namespace Avalonia.PropertyStore var p = (StyledProperty)property; BindingPriority priority; - T oldValue; + T newValue; if (property.Inherits && owner.TryGetInheritedValue(property, out var i)) { - oldValue = ((EffectiveValue)i).Value; + newValue = ((EffectiveValue)i).Value; priority = BindingPriority.Inherited; } else { - oldValue = _metadata.DefaultValue; + newValue = _metadata.DefaultValue; priority = BindingPriority.Unset; } - if (!EqualityComparer.Default.Equals(oldValue, Value)) + if (!EqualityComparer.Default.Equals(newValue, Value)) { - owner.Owner.RaisePropertyChanged(p, Value, oldValue, priority, true); + owner.Owner.RaisePropertyChanged(p, Value, newValue, priority, true); if (property.Inherits) - owner.OnInheritedEffectiveValueDisposed(p, Value); + owner.OnInheritedEffectiveValueDisposed(p, Value, newValue); } if (ValueEntry?.GetDataValidationState(out _, out _) ?? diff --git a/src/Avalonia.Base/PropertyStore/ValueStore.cs b/src/Avalonia.Base/PropertyStore/ValueStore.cs index af31459a98..2b5a2f5022 100644 --- a/src/Avalonia.Base/PropertyStore/ValueStore.cs +++ b/src/Avalonia.Base/PropertyStore/ValueStore.cs @@ -419,7 +419,9 @@ namespace Avalonia.PropertyStore ReevaluateEffectiveValue(property, current); } else + { ReevaluateEffectiveValues(); + } } /// @@ -481,7 +483,8 @@ namespace Avalonia.PropertyStore /// /// The property whose value changed. /// The old value of the property. - public void OnInheritedEffectiveValueDisposed(StyledProperty property, T oldValue) + /// The new value of the property. + public void OnInheritedEffectiveValueDisposed(StyledProperty property, T oldValue, T newValue) { Debug.Assert(property.Inherits); @@ -489,12 +492,11 @@ namespace Avalonia.PropertyStore if (children is not null) { - var defaultValue = property.GetDefaultValue(Owner.GetType()); var count = children.Count; for (var i = 0; i < count; ++i) { - children[i].GetValueStore().OnAncestorInheritedValueChanged(property, oldValue, defaultValue); + children[i].GetValueStore().OnAncestorInheritedValueChanged(property, oldValue, newValue); } } } From 668fa942b70bd36279e980bd58c094c848a316d1 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 30 Mar 2023 22:28:55 +0200 Subject: [PATCH 4/5] Don't leak IPlatformRenderInterface. --- .../Media/GlyphRunTests.cs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs b/tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs index 43feb75c08..84ce341e98 100644 --- a/tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs @@ -1,22 +1,13 @@ using System; -using System.Linq; using Avalonia.Media; using Avalonia.Media.TextFormatting; -using Avalonia.Platform; using Avalonia.UnitTests; -using Avalonia.Utilities; using Xunit; namespace Avalonia.Base.UnitTests.Media { public class GlyphRunTests : TestWithServicesBase { - public GlyphRunTests() - { - AvaloniaLocator.CurrentMutable - .Bind().ToSingleton(); - } - [InlineData(new double[] { 30, 0, 0 }, new int[] { 0, 0, 0 }, 0, 0, 0)] [InlineData(new double[] { 30, 0, 0 }, new int[] { 0, 0, 0 }, 0, 3, 30)] [InlineData(new double[] { 10, 10, 10 }, new int[] { 0, 1, 2 }, 1, 0, 10)] @@ -25,7 +16,7 @@ namespace Avalonia.Base.UnitTests.Media [Theory] public void Should_Get_Distance_From_CharacterHit(double[] advances, int[] clusters, int start, int trailingLength, double expectedDistance) { - using(UnitTestApplication.Start(TestServices.StyledWindow)) + using (Start()) using (var glyphRun = CreateGlyphRun(advances, clusters)) { var characterHit = new CharacterHit(start, trailingLength); @@ -44,7 +35,7 @@ namespace Avalonia.Base.UnitTests.Media public void Should_Get_CharacterHit_FromDistance(double[] advances, int[] clusters, double distance, int start, int trailingLengthExpected, bool isInsideExpected) { - using(UnitTestApplication.Start(TestServices.StyledWindow)) + using (Start()) using (var glyphRun = CreateGlyphRun(advances, clusters)) { var textBounds = glyphRun.GetCharacterHitFromDistance(distance, out var isInside); @@ -190,5 +181,11 @@ namespace Avalonia.Base.UnitTests.Media return new GlyphRun(new MockGlyphTypeface(), 10, new string('a', count).AsMemory(), glyphInfos, biDiLevel: bidiLevel); } + + private static IDisposable Start() + { + return UnitTestApplication.Start(TestServices.StyledWindow.With( + renderInterface: new MockPlatformRenderInterface())); + } } } From a28483252b9039d5e9c921eb054c3aa034963d33 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 30 Mar 2023 22:29:48 +0200 Subject: [PATCH 5/5] Make MaskedTextBox tests pass again. They need a registered `IPlatformRenderInterface` and were only passing because `GlyphRunTests` was leaking one. --- tests/Avalonia.Controls.UnitTests/MaskedTextBoxTests.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/MaskedTextBoxTests.cs b/tests/Avalonia.Controls.UnitTests/MaskedTextBoxTests.cs index 19009416ef..a51abdb9af 100644 --- a/tests/Avalonia.Controls.UnitTests/MaskedTextBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/MaskedTextBoxTests.cs @@ -109,7 +109,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void CaretIndex_Can_Moved_To_Position_After_The_End_Of_Text_With_Arrow_Key() { - using (Start(TestServices.StyledWindow)) + using (Start()) { var target = new MaskedTextBox { @@ -184,7 +184,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Control_Backspace_Should_Remove_The_Word_Before_The_Caret_If_There_Is_No_Selection() { - using (Start(TestServices.StyledWindow)) + using (Start()) { MaskedTextBox textBox = new MaskedTextBox { @@ -226,7 +226,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Control_Delete_Should_Remove_The_Word_After_The_Caret_If_There_Is_No_Selection() { - using (Start(TestServices.StyledWindow)) + using (Start()) { var textBox = new MaskedTextBox { @@ -812,7 +812,7 @@ namespace Avalonia.Controls.UnitTests bool fromClipboard, string expected) { - using (Start(TestServices.StyledWindow)) + using (Start()) { var target = new MaskedTextBox { @@ -898,6 +898,7 @@ namespace Avalonia.Controls.UnitTests standardCursorFactory: Mock.Of()); private static TestServices Services => TestServices.MockThreadingInterface.With( + renderInterface: new MockPlatformRenderInterface(), standardCursorFactory: Mock.Of(), textShaperImpl: new MockTextShaperImpl(), fontManagerImpl: new MockFontManagerImpl());