Pārlūkot izejas kodu

Limit InlineUIContainer to available width (#19651)

* 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 <[email protected]>
Co-authored-by: Benedikt Stebner <[email protected]>
Jan Kučera 1 mēnesi atpakaļ
vecāks
revīzija
912f916c10

+ 1 - 1
src/Avalonia.Controls/Documents/Inline.cs

@@ -66,7 +66,7 @@ namespace Avalonia.Controls.Documents
             control.SetValue(TextDecorationsProperty, value);
         }
         
-        internal abstract void BuildTextRun(IList<TextRun> textRuns);
+        internal abstract void BuildTextRun(IList<TextRun> textRuns, Size blockSize);
 
         internal abstract void AppendText(StringBuilder stringBuilder);
 

+ 0 - 19
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();

+ 17 - 3
src/Avalonia.Controls/Documents/InlineUIContainer.cs

@@ -18,6 +18,8 @@ namespace Avalonia.Controls.Documents
         public static readonly StyledProperty<Control> ChildProperty =
             AvaloniaProperty.Register<InlineUIContainer, Control>(nameof(Child));
 
+        private double _measuredWidth = double.NaN;
+
         /// <summary>
         /// Initializes a new instance of InlineUIContainer element.
         /// </summary>
@@ -51,11 +53,12 @@ namespace Avalonia.Controls.Documents
             set => SetValue(ChildProperty, value);
         }
 
-        internal override void BuildTextRun(IList<TextRun> textRuns)
+        internal override void BuildTextRun(IList<TextRun> 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);
+        }
     }
 }

+ 1 - 1
src/Avalonia.Controls/Documents/LineBreak.cs

@@ -19,7 +19,7 @@ namespace Avalonia.Controls.Documents
         {
         }
 
-        internal override void BuildTextRun(IList<TextRun> textRuns)
+        internal override void BuildTextRun(IList<TextRun> textRuns, Size blockSize)
         {
             var text = Environment.NewLine;
 

+ 2 - 2
src/Avalonia.Controls/Documents/Run.cs

@@ -50,7 +50,7 @@ namespace Avalonia.Controls.Documents
             set => SetValue(TextProperty, value);
         }
 
-        internal override void BuildTextRun(IList<TextRun> textRuns)
+        internal override void BuildTextRun(IList<TextRun> 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);
 

+ 2 - 2
src/Avalonia.Controls/Documents/Span.cs

@@ -38,11 +38,11 @@ namespace Avalonia.Controls.Documents
             set => SetValue(InlinesProperty, value);
         }
 
-        internal override void BuildTextRun(IList<TextRun> textRuns)
+        internal override void BuildTextRun(IList<TextRun> textRuns, Size blockSize)
         {
             foreach (var inline in Inlines)
             {
-                inline.BuildTextRun(textRuns);
+                inline.BuildTextRun(textRuns, blockSize);
             }
         }
 

+ 2 - 2
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;

+ 26 - 1
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()
         {