From 912f916c1039ee2ae153e6e98c14bf318a62c295 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Ku=C4=8Dera?= <10546952+miloush@users.noreply.github.com> Date: Thu, 18 Sep 2025 13:36:39 +0100 Subject: [PATCH] Limit InlineUIContainer to available width (#19651) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Limit InlineUIContainer to available width * Unit test for InlineUIContainer maximum width * Invalidate InlineUIContainer's host * Better InlineUIContainer visual children management * OnInlineHostChanged also handled by InlineUIContainer --------- Co-authored-by: Jan Kučera Co-authored-by: Benedikt Stebner --- src/Avalonia.Controls/Documents/Inline.cs | 2 +- .../Documents/InlineCollection.cs | 19 ------------- .../Documents/InlineUIContainer.cs | 20 +++++++++++--- src/Avalonia.Controls/Documents/LineBreak.cs | 2 +- src/Avalonia.Controls/Documents/Run.cs | 4 +-- src/Avalonia.Controls/Documents/Span.cs | 4 +-- src/Avalonia.Controls/TextBlock.cs | 4 +-- .../TextBlockTests.cs | 27 ++++++++++++++++++- 8 files changed, 51 insertions(+), 31 deletions(-) diff --git a/src/Avalonia.Controls/Documents/Inline.cs b/src/Avalonia.Controls/Documents/Inline.cs index ffa584f055..4e14178805 100644 --- a/src/Avalonia.Controls/Documents/Inline.cs +++ b/src/Avalonia.Controls/Documents/Inline.cs @@ -66,7 +66,7 @@ namespace Avalonia.Controls.Documents control.SetValue(TextDecorationsProperty, value); } - internal abstract void BuildTextRun(IList textRuns); + internal abstract void BuildTextRun(IList textRuns, Size blockSize); internal abstract void AppendText(StringBuilder stringBuilder); diff --git a/src/Avalonia.Controls/Documents/InlineCollection.cs b/src/Avalonia.Controls/Documents/InlineCollection.cs index d98d15b3f1..61cabf5bf8 100644 --- a/src/Avalonia.Controls/Documents/InlineCollection.cs +++ b/src/Avalonia.Controls/Documents/InlineCollection.cs @@ -160,15 +160,6 @@ namespace Avalonia.Controls.Documents foreach (var child in this) { child.InlineHost = newValue; - - if (child is not InlineUIContainer container) - { - continue; - } - - oldValue?.VisualChildren.Remove(container.Child); - - newValue?.VisualChildren.Add(container.Child); } Invalidate(); @@ -180,11 +171,6 @@ namespace Avalonia.Controls.Documents LogicalChildren?.Add(inline); - if (inline is InlineUIContainer container) - { - InlineHost?.VisualChildren.Add(container.Child); - } - Invalidate(); } @@ -192,11 +178,6 @@ namespace Avalonia.Controls.Documents { LogicalChildren?.Remove(inline); - if (inline is InlineUIContainer container) - { - InlineHost?.VisualChildren.Remove(container.Child); - } - inline.InlineHost = null; Invalidate(); diff --git a/src/Avalonia.Controls/Documents/InlineUIContainer.cs b/src/Avalonia.Controls/Documents/InlineUIContainer.cs index 4d9e776bbf..35f04231bf 100644 --- a/src/Avalonia.Controls/Documents/InlineUIContainer.cs +++ b/src/Avalonia.Controls/Documents/InlineUIContainer.cs @@ -18,6 +18,8 @@ namespace Avalonia.Controls.Documents public static readonly StyledProperty ChildProperty = AvaloniaProperty.Register(nameof(Child)); + private double _measuredWidth = double.NaN; + /// /// Initializes a new instance of InlineUIContainer element. /// @@ -51,11 +53,12 @@ namespace Avalonia.Controls.Documents set => SetValue(ChildProperty, value); } - internal override void BuildTextRun(IList textRuns) + internal override void BuildTextRun(IList textRuns, Size blockSize) { - if(!Child.IsMeasureValid) + if (_measuredWidth != blockSize.Width || !Child.IsMeasureValid) { - Child.Measure(Size.Infinity); + Child.Measure(new Size(blockSize.Width, double.PositiveInfinity)); + _measuredWidth = blockSize.Width; } textRuns.Add(new EmbeddedControlRun(Child, CreateTextRunProperties())); @@ -74,13 +77,24 @@ namespace Avalonia.Controls.Documents if(change.OldValue is Control oldChild) { LogicalChildren.Remove(oldChild); + InlineHost?.VisualChildren.Remove(oldChild); } if(change.NewValue is Control newChild) { LogicalChildren.Add(newChild); + InlineHost?.VisualChildren.Add(newChild); } + + InlineHost?.Invalidate(); } } + + internal override void OnInlineHostChanged(IInlineHost? oldValue, IInlineHost? newValue) + { + var child = Child; + oldValue?.VisualChildren.Remove(child); + newValue?.VisualChildren.Add(child); + } } } diff --git a/src/Avalonia.Controls/Documents/LineBreak.cs b/src/Avalonia.Controls/Documents/LineBreak.cs index ee31b85be9..d279366166 100644 --- a/src/Avalonia.Controls/Documents/LineBreak.cs +++ b/src/Avalonia.Controls/Documents/LineBreak.cs @@ -19,7 +19,7 @@ namespace Avalonia.Controls.Documents { } - internal override void BuildTextRun(IList textRuns) + internal override void BuildTextRun(IList textRuns, Size blockSize) { var text = Environment.NewLine; diff --git a/src/Avalonia.Controls/Documents/Run.cs b/src/Avalonia.Controls/Documents/Run.cs index 472b90725e..bdb6920570 100644 --- a/src/Avalonia.Controls/Documents/Run.cs +++ b/src/Avalonia.Controls/Documents/Run.cs @@ -50,7 +50,7 @@ namespace Avalonia.Controls.Documents set => SetValue(TextProperty, value); } - internal override void BuildTextRun(IList textRuns) + internal override void BuildTextRun(IList textRuns, Size blockSize) { var text = Text ?? ""; @@ -59,7 +59,7 @@ namespace Avalonia.Controls.Documents return; } - var textRunProperties = CreateTextRunProperties(); + var textRunProperties = CreateTextRunProperties(); var textCharacters = new TextCharacters(text, textRunProperties); diff --git a/src/Avalonia.Controls/Documents/Span.cs b/src/Avalonia.Controls/Documents/Span.cs index e3f9a1825e..624806f67b 100644 --- a/src/Avalonia.Controls/Documents/Span.cs +++ b/src/Avalonia.Controls/Documents/Span.cs @@ -38,11 +38,11 @@ namespace Avalonia.Controls.Documents set => SetValue(InlinesProperty, value); } - internal override void BuildTextRun(IList textRuns) + internal override void BuildTextRun(IList textRuns, Size blockSize) { foreach (var inline in Inlines) { - inline.BuildTextRun(textRuns); + inline.BuildTextRun(textRuns, blockSize); } } diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index 34c66e0074..7387235a23 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -730,7 +730,7 @@ namespace Avalonia.Controls _textLayout = null; _constraint = deflatedSize; - //Force arrange so text will be properly alligned. + //Force arrange so text will be properly aligned. InvalidateArrange(); } @@ -742,7 +742,7 @@ namespace Avalonia.Controls foreach (var inline in inlines!) { - inline.BuildTextRun(textRuns); + inline.BuildTextRun(textRuns, deflatedSize); } _textRuns = textRuns; diff --git a/tests/Avalonia.Controls.UnitTests/TextBlockTests.cs b/tests/Avalonia.Controls.UnitTests/TextBlockTests.cs index ee075e1cda..97e5d85d92 100644 --- a/tests/Avalonia.Controls.UnitTests/TextBlockTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TextBlockTests.cs @@ -348,7 +348,7 @@ namespace Avalonia.Controls.UnitTests } [Fact] - public void InlineUIContainer_Child_Schould_Be_Arranged() + public void InlineUIContainer_Child_Should_Be_Arranged() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { @@ -382,6 +382,31 @@ namespace Avalonia.Controls.UnitTests } } + [Fact] + public void InlineUIContainer_Child_Should_Be_Constrained() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var target = new TextBlock(); + + GeometryDrawing drawing = new GeometryDrawing(); + drawing.Geometry = new RectangleGeometry(new Rect(0, 0, 500, 500)); + DrawingImage image = new DrawingImage(drawing); + + Image imageControl = new Image { Source = image }; + InlineUIContainer container = new InlineUIContainer(imageControl); + + target.Inlines.Add(new Run("The child should not be limited by position on line.")); + target.Inlines.Add(container); + + target.Measure(new Size(100, 100)); + target.Arrange(new Rect(target.DesiredSize)); + + Assert.True(imageControl.IsMeasureValid); + Assert.Equal(100, imageControl.Bounds.Width); + } + } + [Fact] public void Setting_Text_Should_Reset_Inlines() {