From 599cf1f2971fbbd998b412599e4d2081697da96c Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Wed, 21 Feb 2024 12:09:18 +0100 Subject: [PATCH] Fix InlinesCollection Logical/VisualParent update (#14679) * Add failing test * Update Visual/LogicalTree when parents change --- .../Documents/InlineCollection.cs | 90 +++++++++++-------- .../TextBlockTests.cs | 19 ++++ 2 files changed, 74 insertions(+), 35 deletions(-) diff --git a/src/Avalonia.Controls/Documents/InlineCollection.cs b/src/Avalonia.Controls/Documents/InlineCollection.cs index dc6f828234..f3d32d92d3 100644 --- a/src/Avalonia.Controls/Documents/InlineCollection.cs +++ b/src/Avalonia.Controls/Documents/InlineCollection.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Avalonia.Collections; using Avalonia.LogicalTree; using Avalonia.Metadata; @@ -23,32 +24,8 @@ namespace Avalonia.Controls.Documents ResetBehavior = ResetBehavior.Remove; this.ForEachItem( - x => - { - x.InlineHost = InlineHost; - - LogicalChildren?.Add(x); - - if (x is InlineUIContainer container) - { - InlineHost?.VisualChildren.Add(container.Child); - } - - Invalidate(); - }, - x => - { - LogicalChildren?.Remove(x); - - if(x is InlineUIContainer container) - { - InlineHost?.VisualChildren.Remove(container.Child); - } - - x.InlineHost = null; - - Invalidate(); - }, + OnAdd, + OnRemove, () => throw new NotSupportedException()); } @@ -70,9 +47,11 @@ namespace Avalonia.Controls.Documents get => _inlineHost; set { + var oldValue = _inlineHost; + _inlineHost = value; - OnInlineHostChanged(value); + OnInlineHostChanged(oldValue, value); } } @@ -118,7 +97,7 @@ namespace Avalonia.Controls.Documents /// /// Adds a text segment to the collection. /// - /// For non complex content this appends the text to the end of currently held text. + /// For non-complex content this appends the text to the end of currently held text. /// For complex content this adds a to the collection. /// /// @@ -159,25 +138,66 @@ namespace Avalonia.Controls.Documents Invalidated?.Invoke(this, EventArgs.Empty); } - private void OnParentChanged(IAvaloniaList? oldParent, IAvaloniaList? newParent) + private void OnParentChanged(ICollection? oldValue, ICollection? newValue) { foreach (var child in this) { - if (oldParent != newParent) + if (Equals(oldValue, newValue)) { - oldParent?.Remove(child); - - newParent?.Add(child); + continue; } + + oldValue?.Remove(child); + + newValue?.Add(child); } + + Invalidate(); } - private void OnInlineHostChanged(IInlineHost? inlineHost) + private void OnInlineHostChanged(IInlineHost? oldValue, IInlineHost? newValue) { foreach (var child in this) { - child.InlineHost = inlineHost; + if (child is not InlineUIContainer container) + { + continue; + } + + oldValue?.VisualChildren.Remove(container.Child); + + newValue?.VisualChildren.Add(container.Child); + } + + Invalidate(); + } + + private void OnAdd(TextElement inline) + { + inline.InlineHost = InlineHost; + + LogicalChildren?.Add(inline); + + if (inline is InlineUIContainer container) + { + InlineHost?.VisualChildren.Add(container.Child); } + + Invalidate(); + } + + private void OnRemove(TextElement inline) + { + LogicalChildren?.Remove(inline); + + if (inline is InlineUIContainer container) + { + InlineHost?.VisualChildren.Remove(container.Child); + } + + inline.InlineHost = null; + + Invalidate(); } } } diff --git a/tests/Avalonia.Controls.UnitTests/TextBlockTests.cs b/tests/Avalonia.Controls.UnitTests/TextBlockTests.cs index 79a6706983..4e1379c5c2 100644 --- a/tests/Avalonia.Controls.UnitTests/TextBlockTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TextBlockTests.cs @@ -51,6 +51,25 @@ namespace Avalonia.Controls.UnitTests } } + [Fact] + public void Changing_Inlines_Should_Attach_Embedded_Controls_To_Parents() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + var target = new TextBlock(); + + var control = new Border(); + + var inlineUIContainer = new InlineUIContainer { Child = control }; + + target.Inlines = new InlineCollection { inlineUIContainer }; + + Assert.Equal(inlineUIContainer, control.Parent); + + Assert.Equal(target, control.VisualParent); + } + } + [Fact] public void Can_Call_Measure_Without_InvalidateTextLayout() {