Browse Source

Make sure inlines properly inherit text run properties from their parent (#19750)

* Make sure inlines properly inherit text run properties from their parent

* Adjust comment
Benedikt Stebner 1 month ago
parent
commit
62054dbcc1

+ 5 - 0
src/Avalonia.Controls/Documents/Bold.cs

@@ -13,5 +13,10 @@ namespace Avalonia.Controls.Documents
         {
             FontWeightProperty.OverrideDefaultValue<Bold>(FontWeight.Bold);
         }
+
+        public Bold()
+        {
+            SetCurrentValue(FontWeightProperty, FontWeight.Bold);
+        }
     }
 }

+ 30 - 31
src/Avalonia.Controls/Documents/Inline.cs

@@ -72,44 +72,43 @@ namespace Avalonia.Controls.Documents
 
         protected TextRunProperties CreateTextRunProperties()
         {
-            var textDecorations = TextDecorations;
-            var background = Background;
+            var parentOrSelfBackground = Background ?? FindParentBackground();
 
-            if(Parent is Inline inline)
-            {
-                if(textDecorations == null)
-                {
-                    textDecorations = inline.TextDecorations;
-                }
-
-                if(background == null)
-                {
-                    background = inline.Background;
-                }
-            }
-            
-            var fontStyle = FontStyle;
+            var typeface = new Typeface(
+                FontFamily, 
+                FontStyle, 
+                FontWeight, 
+                FontStretch);
 
-            if(Parent is Italic)
-            {
-                fontStyle = FontStyle.Italic;
-            }
+            return new GenericTextRunProperties(
+                typeface,
+                FontFeatures, 
+                FontSize,
+                TextDecorations, 
+                Foreground,
+                parentOrSelfBackground,
+                BaselineAlignment);
+        }
 
-            var fontWeight = FontWeight;
+        /// <summary>
+        /// Searches for the next parent inline element with a non-null Background and returns its Background brush.
+        /// </summary>
+        /// <returns>The first non-null Background brush found in parent inline elements, or null if none is found.</returns>
+        private IBrush? FindParentBackground()
+        {
+            var parent = Parent;
 
-            if(Parent is Bold)
+            while (parent is Inline inline)
             {
-                fontWeight = FontWeight.Bold;
+                if (inline.Background != null)
+                {
+                    return inline.Background;
+                }
+                  
+                parent = inline.Parent;
             }
 
-            return new GenericTextRunProperties(
-                new Typeface(FontFamily, fontStyle, fontWeight),
-                FontFeatures, 
-                FontSize,
-                textDecorations, 
-                Foreground, 
-                background,
-                BaselineAlignment);
+            return null;
         }
 
         /// <inheritdoc />

+ 5 - 0
src/Avalonia.Controls/Documents/Italic.cs

@@ -13,5 +13,10 @@ namespace Avalonia.Controls.Documents
         {
             FontStyleProperty.OverrideDefaultValue<Italic>(FontStyle.Italic);
         }
+
+        public Italic()
+        {
+            SetCurrentValue(FontStyleProperty, FontStyle.Italic);
+        }
     }
 }

+ 5 - 0
src/Avalonia.Controls/Documents/Underline.cs

@@ -11,5 +11,10 @@
         {
             TextDecorationsProperty.OverrideDefaultValue<Underline>(Media.TextDecorations.Underline);
         }
+
+        public Underline()
+        {
+            SetCurrentValue(TextDecorationsProperty, Media.TextDecorations.Underline);
+        }
     }
 }

+ 80 - 0
tests/Avalonia.Controls.UnitTests/InlineTests.cs

@@ -0,0 +1,80 @@
+using Avalonia.Media;
+using Avalonia.UnitTests;
+using Avalonia.Media.TextFormatting;
+using Avalonia.Controls.Documents;
+using Xunit;
+using System.Collections.Generic;
+
+namespace Avalonia.Controls.UnitTests
+{
+    public class InlineTests : ScopedTestBase
+    {
+        [Fact]
+        public void Should_Inherit_FontWeight_In_Nested_Inlines()
+        {
+            var bold = new Bold();
+            var span = new Span();
+            var run = new Run("Test");
+            span.Inlines.Add(run);
+            bold.Inlines.Add(span);
+
+            var textRuns = new List<TextRun>();
+            bold.BuildTextRun(textRuns, default);
+
+            var runProperties = textRuns[0].Properties;
+            Assert.Equal(FontWeight.Bold, runProperties.Typeface.Weight);
+        }
+
+        [Fact]
+        public void Should_Inherit_FontStyle_In_Nested_Inlines()
+        {
+            var italic = new Italic();
+            var span = new Span();
+            var run = new Run("Test");
+            span.Inlines.Add(run);
+            italic.Inlines.Add(span);
+
+            var textRuns = new List<TextRun>();
+            italic.BuildTextRun(textRuns, default);
+
+            var runProperties = textRuns[0].Properties;
+            Assert.Equal(FontStyle.Italic, runProperties.Typeface.Style);
+        }
+
+        [Fact]
+        public void Should_Inherit_FontStretch_In_Nested_Inlines()
+        {
+            var span = new Span();
+            var innerSpan = new Span();
+            var run = new Run("Test");
+            span.FontStretch = FontStretch.Condensed;
+            innerSpan.Inlines.Add(run);
+            span.Inlines.Add(innerSpan);
+
+            var textRuns = new List<TextRun>();
+            span.BuildTextRun(textRuns, default);
+
+            var runProperties = textRuns[0].Properties;
+            Assert.Equal(FontStretch.Condensed, runProperties.Typeface.Stretch);
+        }
+
+        [Fact]
+        public void Should_Inherit_Background_In_Nested_Inlines()
+        {
+            var backgroundBrush = Brushes.Red;
+            var span = new Span();
+            var innerSpan = new Span();
+            var run = new Run("Test");
+
+            span.Background = backgroundBrush;
+            innerSpan.Inlines.Add(run);
+            span.Inlines.Add(innerSpan);
+
+            var textRuns = new List<TextRun>();
+            span.BuildTextRun(textRuns, default);
+
+            var runProperties = textRuns[0].Properties;
+            Assert.Equal(backgroundBrush, runProperties.BackgroundBrush);
+        }
+    }
+}