Browse Source

Rework Inlines invalidation

Benedikt Stebner 3 years ago
parent
commit
2902e3d24a

+ 5 - 0
src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiAlgorithm.cs

@@ -238,6 +238,11 @@ namespace Avalonia.Media.TextFormatting.Unicode
             _levelRuns.Clear();
             _resolvedLevelsBuffer.Clear();
 
+            if (types.IsEmpty)
+            {
+                return;
+            }
+
             // Setup original types and working types
             _originalClasses = types;
             _workingClasses = _workingClassesBuffer.Add(types);

+ 11 - 0
src/Avalonia.Controls/Documents/IInlineHost.cs

@@ -0,0 +1,11 @@
+using Avalonia.LogicalTree;
+
+namespace Avalonia.Controls.Documents
+{
+    internal interface IInlineHost : ILogical
+    {
+        void AddVisualChild(IControl child);
+
+        void Invalidate();
+    }
+}

+ 3 - 9
src/Avalonia.Controls/Documents/Inline.cs

@@ -1,10 +1,9 @@
 using System.Collections.Generic;
 using System.Text;
-using Avalonia.LogicalTree;
 using Avalonia.Media;
 using Avalonia.Media.TextFormatting;
 
-namespace Avalonia.Controls.Documents 
+namespace Avalonia.Controls.Documents
 {
     /// <summary>
     /// Inline element.
@@ -45,7 +44,7 @@ namespace Avalonia.Controls.Documents
             set { SetValue(BaselineAlignmentProperty, value); }
         }
 
-        internal abstract void BuildTextRun(IList<TextRun> textRuns, IInlinesHost parent);
+        internal abstract void BuildTextRun(IList<TextRun> textRuns);
 
         internal abstract void AppendText(StringBuilder stringBuilder);
 
@@ -63,14 +62,9 @@ namespace Avalonia.Controls.Documents
             {
                 case nameof(TextDecorations):
                 case nameof(BaselineAlignment):
-                    Invalidate();
+                    InlineHost?.Invalidate();
                     break;
             }
         }
     }
-
-    public interface IInlinesHost : ILogical
-    {
-        void AddVisualChild(IControl child);
-    }
 }

+ 31 - 17
src/Avalonia.Controls/Documents/InlineCollection.cs

@@ -12,29 +12,37 @@ namespace Avalonia.Controls.Documents
     [WhitespaceSignificantCollection]
     public class InlineCollection : AvaloniaList<Inline>
     {
+        private readonly IInlineHost? _host;
         private string? _text = string.Empty;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="InlineCollection"/> class.
         /// </summary>
-        public InlineCollection(ILogical parent) : base(0)
+        public InlineCollection(ILogical parent) : this(parent, null) { }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="InlineCollection"/> class.
+        /// </summary>
+        internal InlineCollection(ILogical parent, IInlineHost? host = null) : base(0)
         {
+            _host = host;
+
             ResetBehavior = ResetBehavior.Remove;
             
             this.ForEachItem(
                 x =>
                 {
                     ((ISetLogicalParent)x).SetParent(parent);
-                    x.Invalidated += Invalidate;
-                    Invalidate();
+                    x.InlineHost = host;
+                    host?.Invalidate();
                 },
                 x =>
                 {
                     ((ISetLogicalParent)x).SetParent(null);
-                    x.Invalidated -= Invalidate;
-                    Invalidate();
+                    x.InlineHost = host;
+                    host?.Invalidate();
                 },
-                () => throw new NotSupportedException());
+                () => throw new NotSupportedException());         
         }
 
         public bool HasComplexContent => Count > 0;
@@ -98,22 +106,20 @@ namespace Avalonia.Controls.Documents
 
         public void Add(IControl child)
         {
-            if (!HasComplexContent && !string.IsNullOrEmpty(_text))
-            {
-                base.Add(new Run(_text));
-
-                _text = string.Empty;
-            }
+            var implicitRun = new InlineUIContainer(child);
 
-            base.Add(new InlineUIContainer(child));
+            Add(implicitRun);
         }
 
         public override void Add(Inline item)
         {
             if (!HasComplexContent)
             {
-                base.Add(new Run(_text));
-                
+                if (!string.IsNullOrEmpty(_text))
+                {
+                    base.Add(new Run(_text));
+                }
+                             
                 _text = string.Empty;
             }
             
@@ -124,11 +130,19 @@ namespace Avalonia.Controls.Documents
         /// Raised when an inline in the collection changes.
         /// </summary>
         public event EventHandler? Invalidated;
-        
+
         /// <summary>
         /// Raises the <see cref="Invalidated"/> event.
         /// </summary>
-        protected void Invalidate() => Invalidated?.Invoke(this, EventArgs.Empty);
+        protected void Invalidate()
+        {
+            if(_host != null)
+            {
+                _host.Invalidate();
+            }
+
+            Invalidated?.Invoke(this, EventArgs.Empty);
+        }
 
         private void Invalidate(object? sender, EventArgs e) => Invalidate();
     }

+ 8 - 4
src/Avalonia.Controls/Documents/InlineUIContainer.cs

@@ -1,6 +1,5 @@
 using System.Collections.Generic;
 using System.Text;
-using Avalonia.LogicalTree;
 using Avalonia.Media;
 using Avalonia.Media.TextFormatting;
 using Avalonia.Metadata;
@@ -58,11 +57,16 @@ namespace Avalonia.Controls.Documents
             set => SetValue(ChildProperty, value);
         }
 
-        internal override void BuildTextRun(IList<TextRun> textRuns, IInlinesHost parent)
+        internal override void BuildTextRun(IList<TextRun> textRuns)
         {
-            ((ISetLogicalParent)Child).SetParent(parent);
+            if(InlineHost == null)
+            {
+                return;
+            }
+
+            ((ISetLogicalParent)Child).SetParent(InlineHost);
 
-            parent.AddVisualChild(Child);
+            InlineHost.AddVisualChild(Child);
 
             textRuns.Add(new InlineRun(Child, CreateTextRunProperties()));
         }

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

@@ -20,7 +20,7 @@ namespace Avalonia.Controls.Documents
         {
         }
 
-        internal override void BuildTextRun(IList<TextRun> textRuns, IInlinesHost parent)
+        internal override void BuildTextRun(IList<TextRun> textRuns)
         {
             textRuns.Add(new TextEndOfLine());
         }

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

@@ -2,7 +2,6 @@ using System;
 using System.Collections.Generic;
 using System.Text;
 using Avalonia.Data;
-using Avalonia.LogicalTree;
 using Avalonia.Media.TextFormatting;
 using Avalonia.Metadata;
 
@@ -51,7 +50,7 @@ namespace Avalonia.Controls.Documents
             set { SetValue (TextProperty, value); }
         }
 
-        internal override void BuildTextRun(IList<TextRun> textRuns, IInlinesHost parent)
+        internal override void BuildTextRun(IList<TextRun> textRuns)
         {
             var text = (Text ?? "").AsMemory();
 
@@ -76,7 +75,7 @@ namespace Avalonia.Controls.Documents
             switch (change.Property.Name)
             {
                 case nameof(Text):
-                    Invalidate();
+                    InlineHost?.Invalidate();
                     break;
             }
         }

+ 3 - 6
src/Avalonia.Controls/Documents/Span.cs

@@ -1,10 +1,8 @@
 using System;
 using System.Collections.Generic;
 using System.Text;
-using Avalonia.LogicalTree;
 using Avalonia.Media.TextFormatting;
 using Avalonia.Metadata;
-using Avalonia.Utilities;
 
 namespace Avalonia.Controls.Documents
 {
@@ -27,8 +25,7 @@ namespace Avalonia.Controls.Documents
         public Span()
         {
             Inlines = new InlineCollection(this);
-
-            Inlines.Invalidated += (s, e) => Invalidate();
+            Inlines.Invalidated += (s, e) => InlineHost?.Invalidate();
         }
 
         /// <summary>
@@ -37,13 +34,13 @@ namespace Avalonia.Controls.Documents
         [Content]
         public InlineCollection Inlines { get; }
 
-        internal override void BuildTextRun(IList<TextRun> textRuns, IInlinesHost parent)
+        internal override void BuildTextRun(IList<TextRun> textRuns)
         {
             if (Inlines.HasComplexContent)
             {
                 foreach (var inline in Inlines)
                 {
-                    inline.BuildTextRun(textRuns, parent);
+                    inline.BuildTextRun(textRuns);
                 }
             }
             else

+ 3 - 12
src/Avalonia.Controls/Documents/TextElement.cs

@@ -1,5 +1,4 @@
-using System;
-using Avalonia.Media;
+using Avalonia.Media;
 
 namespace Avalonia.Controls.Documents
 {
@@ -251,10 +250,7 @@ namespace Avalonia.Controls.Documents
             control.SetValue(ForegroundProperty, value);
         }
 
-        /// <summary>
-        /// Raised when the visual representation of the text element changes.
-        /// </summary>
-        public event EventHandler? Invalidated;
+        internal IInlineHost? InlineHost { get; set; }
 
         protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
         {
@@ -269,14 +265,9 @@ namespace Avalonia.Controls.Documents
                 case nameof(FontWeight):
                 case nameof(FontStretch):
                 case nameof(Foreground):
-                    Invalidate();
+                    InlineHost?.Invalidate();
                     break;
             }
         }
-
-        /// <summary>
-        /// Raises the <see cref="Invalidate"/> event.
-        /// </summary>
-        protected void Invalidate() => Invalidated?.Invoke(this, EventArgs.Empty);
     }
 }

+ 10 - 9
src/Avalonia.Controls/TextBlock.cs

@@ -14,7 +14,7 @@ namespace Avalonia.Controls
     /// <summary>
     /// A control that displays a block of text.
     /// </summary>
-    public class TextBlock : Control, IInlinesHost
+    public class TextBlock : Control, IInlineHost
     {
         /// <summary>
         /// Defines the <see cref="Background"/> property.
@@ -155,9 +155,7 @@ namespace Avalonia.Controls
         /// </summary>
         public TextBlock()
         {
-            Inlines = new InlineCollection(this);
-
-            Inlines.Invalidated += InlinesChanged;
+            Inlines = new InlineCollection(this, this);
         }
 
         /// <summary>
@@ -211,7 +209,7 @@ namespace Avalonia.Controls
         }
 
         /// <summary>
-        /// Gets or sets the inlines.
+        /// Gets the inlines.
         /// </summary>
         [Content]
         public InlineCollection Inlines { get; }
@@ -569,7 +567,7 @@ namespace Avalonia.Controls
 
                 foreach (var inline in Inlines)
                 {
-                    inline.BuildTextRun(textRuns, this);
+                    inline.BuildTextRun(textRuns);
                 }
 
                 textSource = new InlinesTextSource(textRuns);
@@ -667,8 +665,6 @@ namespace Avalonia.Controls
                 case nameof (Padding):
                 case nameof (LineHeight):
                 case nameof (MaxLines):
-                    
-                case nameof (InlinesProperty):
 
                 case nameof (Text):
                 case nameof (TextDecorations):
@@ -685,7 +681,7 @@ namespace Avalonia.Controls
             InvalidateTextLayout();
         }
 
-        void IInlinesHost.AddVisualChild(IControl child)
+        void IInlineHost.AddVisualChild(IControl child)
         {
             if (child.VisualParent == null)
             {
@@ -693,6 +689,11 @@ namespace Avalonia.Controls
             }            
         }
 
+        void IInlineHost.Invalidate()
+        {
+            InvalidateTextLayout();
+        }
+
         private readonly struct InlinesTextSource : ITextSource
         {
             private readonly IReadOnlyList<TextRun> _textRuns;