Jelajahi Sumber

Merge branch 'master' into fixes/3466-animation-overrides-localvalue

Jumar Macato 5 tahun lalu
induk
melakukan
096f32cad8

+ 31 - 0
src/Avalonia.Controls/TextBox.cs

@@ -14,6 +14,7 @@ using Avalonia.Interactivity;
 using Avalonia.Media;
 using Avalonia.Metadata;
 using Avalonia.Data;
+using Avalonia.Layout;
 using Avalonia.Utilities;
 
 namespace Avalonia.Controls
@@ -72,6 +73,18 @@ namespace Avalonia.Controls
         public static readonly StyledProperty<TextAlignment> TextAlignmentProperty =
             TextBlock.TextAlignmentProperty.AddOwner<TextBox>();
 
+        /// <summary>
+        /// Defines the <see cref="HorizontalAlignment"/> property.
+        /// </summary>
+        public static readonly StyledProperty<HorizontalAlignment> HorizontalContentAlignmentProperty =
+            ContentControl.HorizontalContentAlignmentProperty.AddOwner<TextBox>();
+
+        /// <summary>
+        /// Defines the <see cref="VerticalAlignment"/> property.
+        /// </summary>
+        public static readonly StyledProperty<VerticalAlignment> VerticalContentAlignmentProperty =
+            ContentControl.VerticalContentAlignmentProperty.AddOwner<TextBox>();
+
         public static readonly StyledProperty<TextWrapping> TextWrappingProperty =
             TextBlock.TextWrappingProperty.AddOwner<TextBox>();
 
@@ -262,6 +275,24 @@ namespace Avalonia.Controls
             }
         }
 
+        /// <summary>
+        /// Gets or sets the horizontal alignment of the content within the control.
+        /// </summary>
+        public HorizontalAlignment HorizontalContentAlignment
+        {
+            get { return GetValue(HorizontalContentAlignmentProperty); }
+            set { SetValue(HorizontalContentAlignmentProperty, value); }
+        }
+
+        /// <summary>
+        /// Gets or sets the vertical alignment of the content within the control.
+        /// </summary>
+        public VerticalAlignment VerticalContentAlignment
+        {
+            get { return GetValue(VerticalContentAlignmentProperty); }
+            set { SetValue(VerticalContentAlignmentProperty, value); }
+        }
+
         public TextAlignment TextAlignment
         {
             get { return GetValue(TextAlignmentProperty); }

+ 21 - 6
src/Avalonia.Styling/StyledElement.cs

@@ -573,9 +573,12 @@ namespace Avalonia
                     element._dataContextUpdating = true;
                     element.OnDataContextBeginUpdate();
 
-                    foreach (var child in element.LogicalChildren)
+                    var logicalChildren = element.LogicalChildren;
+                    var logicalChildrenCount = logicalChildren.Count;
+
+                    for (var i = 0; i < logicalChildrenCount; i++)
                     {
-                        if (child is StyledElement s &&
+                        if (element.LogicalChildren[i] is StyledElement s &&
                             s.InheritanceParent == element &&
                             !s.IsSet(DataContextProperty))
                         {
@@ -646,9 +649,15 @@ namespace Avalonia
                 AttachedToLogicalTree?.Invoke(this, e);
             }
 
-            foreach (var child in LogicalChildren.OfType<StyledElement>())
+            var logicalChildren = LogicalChildren;
+            var logicalChildrenCount = logicalChildren.Count;
+
+            for (var i = 0; i < logicalChildrenCount; i++)
             {
-                child.OnAttachedToLogicalTreeCore(e);
+                if (logicalChildren[i] is StyledElement child)
+                {
+                    child.OnAttachedToLogicalTreeCore(e);
+                }
             }
         }
 
@@ -661,9 +670,15 @@ namespace Avalonia
                 OnDetachedFromLogicalTree(e);
                 DetachedFromLogicalTree?.Invoke(this, e);
 
-                foreach (var child in LogicalChildren.OfType<StyledElement>())
+                var logicalChildren = LogicalChildren;
+                var logicalChildrenCount = logicalChildren.Count;
+
+                for (var i = 0; i < logicalChildrenCount; i++)
                 {
-                    child.OnDetachedFromLogicalTreeCore(e);
+                    if (logicalChildren[i] is StyledElement child)
+                    {
+                        child.OnDetachedFromLogicalTreeCore(e);
+                    }
                 }
 
 #if DEBUG

+ 2 - 2
src/Avalonia.Styling/Styling/Setter.cs

@@ -78,8 +78,6 @@ namespace Avalonia.Styling
         {
             Contract.Requires<ArgumentNullException>(control != null);
 
-            var description = style?.ToString();
-
             if (Property == null)
             {
                 throw new InvalidOperationException("Setter.Property must be set.");
@@ -107,6 +105,8 @@ namespace Avalonia.Styling
                 }
                 else
                 {
+                    var description = style?.ToString();
+
                     var activated = new ActivatedValue(activator, value, description);
                     return control.Bind(Property, activated, BindingPriority.StyleTrigger);
                 }

+ 25 - 10
src/Avalonia.Styling/Styling/Style.cs

@@ -116,13 +116,21 @@ namespace Avalonia.Styling
                 if (match.IsMatch)
                 {
                     var controlSubscriptions = GetSubscriptions(control);
-                    
-                    var subs = new CompositeDisposable(Setters.Count + Animations.Count);
 
-                    if (control is Animatable animatable)
+                    var animatable = control as Animatable;
+
+                    var setters = Setters;
+                    var settersCount = setters.Count;
+                    var animations = Animations;
+                    var animationsCount = animations.Count;
+
+                    var subs = new CompositeDisposable(settersCount + (animatable != null ? animationsCount : 0) + 1);
+
+                    if (animatable != null)
                     {
-                        foreach (var animation in Animations)
+                        for (var i = 0; i < animationsCount; i++)
                         {
+                            var animation = animations[i];
                             var obsMatch = match.Activator;
 
                             if (match.Result == SelectorMatchResult.AlwaysThisType ||
@@ -133,17 +141,19 @@ namespace Avalonia.Styling
 
                             var sub = animation.Apply(animatable, null, obsMatch);
                             subs.Add(sub);
-                        } 
+                        }
                     }
 
-                    foreach (var setter in Setters)
+                    for (var i = 0; i < settersCount; i++)
                     {
+                        var setter = setters[i];
                         var sub = setter.Apply(this, control, match.Activator);
                         subs.Add(sub);
                     }
 
+                    subs.Add(Disposable.Create((subs, Subscriptions) , state =>  state.Subscriptions.Remove(state.subs)));
+
                     controlSubscriptions.Add(subs);
-                    controlSubscriptions.Add(Disposable.Create(() => Subscriptions.Remove(subs)));
                     Subscriptions.Add(subs);
                 }
 
@@ -151,18 +161,23 @@ namespace Avalonia.Styling
             }
             else if (control == container)
             {
+                var setters = Setters;
+                var settersCount = setters.Count;
+
                 var controlSubscriptions = GetSubscriptions(control);
 
-                var subs = new CompositeDisposable(Setters.Count);
+                var subs = new CompositeDisposable(settersCount + 1);
 
-                foreach (var setter in Setters)
+                for (var i = 0; i < settersCount; i++)
                 {
+                    var setter = setters[i];
                     var sub = setter.Apply(this, control, null);
                     subs.Add(sub);
                 }
 
+                subs.Add(Disposable.Create((subs, Subscriptions), state => state.Subscriptions.Remove(state.subs)));
+
                 controlSubscriptions.Add(subs);
-                controlSubscriptions.Add(Disposable.Create(() => Subscriptions.Remove(subs)));
                 Subscriptions.Add(subs);
                 return true;
             }

+ 3 - 1
src/Avalonia.Styling/Styling/Styles.cs

@@ -239,8 +239,10 @@ namespace Avalonia.Styling
         /// <inheritdoc/>
         public bool Remove(IStyle item) => _styles.Remove(item);
 
+        public AvaloniaList<IStyle>.Enumerator GetEnumerator() => _styles.GetEnumerator();
+
         /// <inheritdoc/>
-        public IEnumerator<IStyle> GetEnumerator() => _styles.GetEnumerator();
+        IEnumerator<IStyle> IEnumerable<IStyle>.GetEnumerator() => _styles.GetEnumerator();
 
         /// <inheritdoc/>
         IEnumerator IEnumerable.GetEnumerator() => _styles.GetEnumerator();

+ 3 - 1
src/Avalonia.Themes.Default/TextBox.xaml

@@ -12,7 +12,9 @@
                 Background="{TemplateBinding Background}"
                 BorderBrush="{TemplateBinding BorderBrush}"
                 BorderThickness="{TemplateBinding BorderThickness}">
-          <DockPanel Margin="{TemplateBinding Padding}">
+          <DockPanel Margin="{TemplateBinding Padding}"
+                     HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
+                     VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
 
             <TextBlock Name="floatingWatermark"
                        Foreground="{DynamicResource ThemeAccentBrush}"

+ 20 - 6
src/Avalonia.Visuals/Visual.cs

@@ -386,11 +386,18 @@ namespace Avalonia
             AttachedToVisualTree?.Invoke(this, e);
             InvalidateVisual();
 
-            if (VisualChildren != null)
+            var visualChildren = VisualChildren;
+
+            if (visualChildren != null)
             {
-                foreach (Visual child in VisualChildren.OfType<Visual>())
+                var visualChildrenCount = visualChildren.Count;
+
+                for (var i = 0; i < visualChildrenCount; i++)
                 {
-                    child.OnAttachedToVisualTreeCore(e);
+                    if (visualChildren[i] is Visual child)
+                    {
+                        child.OnAttachedToVisualTreeCore(e);
+                    }
                 }
             }
         }
@@ -415,11 +422,18 @@ namespace Avalonia
             DetachedFromVisualTree?.Invoke(this, e);
             e.Root?.Renderer?.AddDirty(this);
 
-            if (VisualChildren != null)
+            var visualChildren = VisualChildren;
+
+            if (visualChildren != null)
             {
-                foreach (Visual child in VisualChildren.OfType<Visual>())
+                var visualChildrenCount = visualChildren.Count;
+
+                for (var i = 0; i < visualChildrenCount; i++)
                 {
-                    child.OnDetachedFromVisualTreeCore(e);
+                    if (visualChildren[i] is Visual child)
+                    {
+                        child.OnDetachedFromVisualTreeCore(e);
+                    }
                 }
             }
         }

+ 47 - 0
tests/Avalonia.Benchmarks/Styling/StyleAttachBenchmark.cs

@@ -0,0 +1,47 @@
+using System;
+using System.Runtime.CompilerServices;
+using Avalonia.Controls;
+using Avalonia.UnitTests;
+using BenchmarkDotNet.Attributes;
+
+namespace Avalonia.Benchmarks.Styling
+{
+    [MemoryDiagnoser]
+    public class StyleAttachBenchmark : IDisposable
+    {
+        private readonly IDisposable _app;
+        private readonly TestRoot _root;
+        private readonly TextBox _control;
+
+        public StyleAttachBenchmark()
+        {
+            _app = UnitTestApplication.Start(
+                TestServices.StyledWindow.With(
+                    renderInterface: new NullRenderingPlatform(),
+                    threadingInterface: new NullThreadingPlatform()));
+
+            _root = new TestRoot(true, null)
+            {
+                Renderer = new NullRenderer(),
+            };
+
+            _control = new TextBox();
+        }
+
+        [Benchmark]
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        public void AttachTextBoxStyles()
+        {
+            var styles = UnitTestApplication.Current.Styles;
+
+            styles.Attach(_control, UnitTestApplication.Current);
+
+            styles.Detach();
+        }
+
+        public void Dispose()
+        {
+            _app.Dispose();
+        }
+    }
+}