Browse Source

Merge branch 'master' into fixes/4330-itemsrepeater-no-scrollviewer

danwalmsley 5 years ago
parent
commit
675e13f8b1

+ 6 - 0
src/Avalonia.Base/ApiCompatBaseline.txt

@@ -0,0 +1,6 @@
+Compat issues with assembly Avalonia.Base:
+MembersMustExist : Member 'public void Avalonia.DirectProperty<TOwner, TValue>..ctor(System.String, System.Func<TOwner, TValue>, System.Action<TOwner, TValue>, Avalonia.DirectPropertyMetadata<TValue>, System.Boolean)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'protected void Avalonia.DirectPropertyBase<TValue>..ctor(Avalonia.AvaloniaProperty, System.Type, Avalonia.PropertyMetadata, System.Boolean)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'protected void Avalonia.DirectPropertyBase<TValue>..ctor(System.String, System.Type, Avalonia.PropertyMetadata, System.Boolean)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Boolean Avalonia.DirectPropertyBase<TValue>.IsDataValidationEnabled.get()' does not exist in the implementation but it does exist in the contract.
+Total Issues: 4

+ 3 - 1
src/Avalonia.Base/AvaloniaObject.cs

@@ -806,7 +806,9 @@ namespace Avalonia
                     break;
             }
 
-            if (p.IsDataValidationEnabled)
+            var metadata = p.GetMetadata(GetType());
+
+            if (metadata.EnableDataValidation == true)
             {
                 UpdateDataValidation(property, value);
             }

+ 3 - 3
src/Avalonia.Base/AvaloniaProperty.cs

@@ -369,14 +369,14 @@ namespace Avalonia
 
             var metadata = new DirectPropertyMetadata<TValue>(
                 unsetValue: unsetValue,
-                defaultBindingMode: defaultBindingMode);
+                defaultBindingMode: defaultBindingMode,
+                enableDataValidation: enableDataValidation);
 
             var result = new DirectProperty<TOwner, TValue>(
                 name,
                 getter,
                 setter,
-                metadata,
-                enableDataValidation);
+                metadata);
             AvaloniaPropertyRegistry.Instance.Register(typeof(TOwner), result);
             return result;
         }

+ 8 - 17
src/Avalonia.Base/DirectProperty.cs

@@ -23,16 +23,12 @@ namespace Avalonia
         /// <param name="getter">Gets the current value of the property.</param>
         /// <param name="setter">Sets the value of the property. May be null.</param>
         /// <param name="metadata">The property metadata.</param>
-        /// <param name="enableDataValidation">
-        /// Whether the property is interested in data validation.
-        /// </param>
         public DirectProperty(
             string name,
             Func<TOwner, TValue> getter,
             Action<TOwner, TValue> setter,
-            DirectPropertyMetadata<TValue> metadata,
-            bool enableDataValidation)
-            : base(name, typeof(TOwner), metadata, enableDataValidation)
+            DirectPropertyMetadata<TValue> metadata)
+            : base(name, typeof(TOwner), metadata)
         {
             Contract.Requires<ArgumentNullException>(getter != null);
 
@@ -47,16 +43,12 @@ namespace Avalonia
         /// <param name="getter">Gets the current value of the property.</param>
         /// <param name="setter">Sets the value of the property. May be null.</param>
         /// <param name="metadata">Optional overridden metadata.</param>
-        /// <param name="enableDataValidation">
-        /// Whether the property is interested in data validation.
-        /// </param>
         private DirectProperty(
             DirectPropertyBase<TValue> source,
             Func<TOwner, TValue> getter,
             Action<TOwner, TValue> setter,
-            DirectPropertyMetadata<TValue> metadata,
-            bool enableDataValidation)
-            : base(source, typeof(TOwner), metadata, enableDataValidation)
+            DirectPropertyMetadata<TValue> metadata)
+            : base(source, typeof(TOwner), metadata)
         {
             Contract.Requires<ArgumentNullException>(getter != null);
 
@@ -107,7 +99,8 @@ namespace Avalonia
         {
             var metadata = new DirectPropertyMetadata<TValue>(
                 unsetValue: unsetValue,
-                defaultBindingMode: defaultBindingMode);
+                defaultBindingMode: defaultBindingMode,
+                enableDataValidation: enableDataValidation);
 
             metadata.Merge(GetMetadata<TOwner>(), this);
 
@@ -115,8 +108,7 @@ namespace Avalonia
                 (DirectPropertyBase<TValue>)this,
                 getter,
                 setter,
-                metadata,
-                enableDataValidation);
+                metadata);
 
             AvaloniaPropertyRegistry.Instance.Register(typeof(TNewOwner), result);
             return result;
@@ -155,8 +147,7 @@ namespace Avalonia
                 this,
                 getter,
                 setter,
-                metadata,
-                enableDataValidation);
+                metadata);
 
             AvaloniaPropertyRegistry.Instance.Register(typeof(TNewOwner), result);
             return result;

+ 22 - 17
src/Avalonia.Base/DirectPropertyBase.cs

@@ -23,17 +23,12 @@ namespace Avalonia
         /// <param name="name">The name of the property.</param>
         /// <param name="ownerType">The type of the class that registers the property.</param>
         /// <param name="metadata">The property metadata.</param>
-        /// <param name="enableDataValidation">
-        /// Whether the property is interested in data validation.
-        /// </param>
         protected DirectPropertyBase(
             string name,
             Type ownerType,
-            PropertyMetadata metadata,
-            bool enableDataValidation)
+            PropertyMetadata metadata)
             : base(name, ownerType, metadata)
         {
-            IsDataValidationEnabled = enableDataValidation;
         }
 
         /// <summary>
@@ -42,17 +37,12 @@ namespace Avalonia
         /// <param name="source">The property to copy.</param>
         /// <param name="ownerType">The new owner type.</param>
         /// <param name="metadata">Optional overridden metadata.</param>
-        /// <param name="enableDataValidation">
-        /// Whether the property is interested in data validation.
-        /// </param>
         protected DirectPropertyBase(
             AvaloniaProperty source,
             Type ownerType,
-            PropertyMetadata metadata,
-            bool enableDataValidation)
+            PropertyMetadata metadata)
             : base(source, ownerType, metadata)
         {
-            IsDataValidationEnabled = enableDataValidation;
         }
 
         /// <summary>
@@ -60,11 +50,6 @@ namespace Avalonia
         /// </summary>
         public abstract Type Owner { get; }
 
-        /// <summary>
-        /// Gets a value that indicates whether data validation is enabled for the property.
-        /// </summary>
-        public bool IsDataValidationEnabled { get; }
-
         /// <summary>
         /// Gets the value of the property on the instance.
         /// </summary>
@@ -102,6 +87,26 @@ namespace Avalonia
             return (DirectPropertyMetadata<TValue>)base.GetMetadata(type);
         }
 
+        /// <summary>
+        /// Overrides the metadata for the property on the specified type.
+        /// </summary>
+        /// <typeparam name="T">The type.</typeparam>
+        /// <param name="metadata">The metadata.</param>
+        public void OverrideMetadata<T>(DirectPropertyMetadata<TValue> metadata) where T : IAvaloniaObject
+        {
+            base.OverrideMetadata(typeof(T), metadata);
+        }
+
+        /// <summary>
+        /// Overrides the metadata for the property on the specified type.
+        /// </summary>
+        /// <param name="type">The type.</param>
+        /// <param name="metadata">The metadata.</param>
+        public void OverrideMetadata(Type type, DirectPropertyMetadata<TValue> metadata)
+        {
+            base.OverrideMetadata(type, metadata);
+        }
+
         /// <inheritdoc/>
         public override void Accept<TData>(IAvaloniaPropertyVisitor<TData> vistor, ref TData data)
         {

+ 1 - 1
src/Avalonia.Controls/Repeater/ItemsRepeaterElementIndexChangedEventArgs.cs

@@ -34,7 +34,7 @@ namespace Avalonia.Controls
         /// </summary>
         public int OldIndex { get; private set; }
 
-        internal void Update(IControl element, int newIndex, int oldIndex)
+        internal void Update(IControl element, int oldIndex, int newIndex)
         {
             Element = element;
             NewIndex = newIndex;

+ 1 - 1
src/Avalonia.Styling/Styling/PropertySetterBindingInstance.cs

@@ -164,7 +164,7 @@ namespace Avalonia.Styling
 
         private void ConvertAndPublishNext(object? value)
         {
-            _value = value is T v ? v : BindingValue<T>.FromUntyped(value);
+            _value = BindingValue<T>.FromUntyped(value);
 
             if (_isActive)
             {

+ 23 - 0
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_DataValidation.cs

@@ -62,6 +62,20 @@ namespace Avalonia.Base.UnitTests
             Assert.Equal(7, result[3].Value);
         }
 
+        [Fact]
+        public void Binding_Overridden_Validated_Direct_Property_Calls_UpdateDataValidation()
+        {
+            var target = new Class2();
+            var source = new Subject<BindingValue<int>>();
+
+            // Class2 overrides `NonValidatedDirectProperty`'s metadata to enable data validation.
+            target.Bind(Class1.NonValidatedDirectProperty, source);
+            source.OnNext(1);
+
+            var result = target.Notifications.Cast<BindingValue<int>>().ToList();
+            Assert.Equal(1, result.Count);
+        }
+
         [Fact]
         public void Bound_Validated_Direct_String_Property_Can_Be_Set_To_Null()
         {
@@ -150,6 +164,15 @@ namespace Avalonia.Base.UnitTests
             }
         }
 
+        private class Class2 : Class1
+        {
+            static Class2()
+            {
+                NonValidatedDirectProperty.OverrideMetadata<Class2>(
+                    new DirectPropertyMetadata<int>(enableDataValidation: true));
+            }
+        }
+
         public class ViewModel : NotifyingBase
         {
             private string _stringValue;

+ 2 - 4
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs

@@ -547,8 +547,7 @@ namespace Avalonia.Base.UnitTests
                 "foo",
                 o => "foo",
                 null,
-                new DirectPropertyMetadata<string>(defaultBindingMode: BindingMode.TwoWay),
-                false);
+                new DirectPropertyMetadata<string>(defaultBindingMode: BindingMode.TwoWay));
             var bar = foo.AddOwner<Class2>(o => "bar");
 
             Assert.Equal(BindingMode.TwoWay, bar.GetMetadata<Class1>().DefaultBindingMode);
@@ -562,8 +561,7 @@ namespace Avalonia.Base.UnitTests
                 "foo",
                 o => "foo",
                 null,
-                new DirectPropertyMetadata<string>(defaultBindingMode: BindingMode.TwoWay),
-                false);
+                new DirectPropertyMetadata<string>(defaultBindingMode: BindingMode.TwoWay));
             var bar = foo.AddOwner<Class2>(o => "bar", defaultBindingMode: BindingMode.OneWayToSource);
 
             Assert.Equal(BindingMode.TwoWay, bar.GetMetadata<Class1>().DefaultBindingMode);

+ 74 - 31
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Metadata.cs

@@ -1,5 +1,4 @@
-using System.Linq;
-using System.Reactive.Linq;
+using System.Runtime.CompilerServices;
 using Xunit;
 
 namespace Avalonia.Base.UnitTests
@@ -9,57 +8,101 @@ namespace Avalonia.Base.UnitTests
         public AvaloniaObjectTests_Metadata()
         {
             // Ensure properties are registered.
-            AvaloniaProperty p;
-            p = Class1.FooProperty;
-            p = Class2.BarProperty;
-            p = AttachedOwner.AttachedProperty;
+            RuntimeHelpers.RunClassConstructor(typeof(Class1).TypeHandle);
+            RuntimeHelpers.RunClassConstructor(typeof(Class2).TypeHandle);
+            RuntimeHelpers.RunClassConstructor(typeof(Class3).TypeHandle);
         }
 
-        [Fact]
-        public void IsSet_Returns_False_For_Unset_Property()
+        public class StyledProperty : AvaloniaObjectTests_Metadata
         {
-            var target = new Class1();
+            [Fact]
+            public void Default_Value_Can_Be_Overridden_In_Derived_Class()
+            {
+                var baseValue = Class1.StyledProperty.GetDefaultValue(typeof(Class1));
+                var derivedValue = Class1.StyledProperty.GetDefaultValue(typeof(Class2));
 
-            Assert.False(target.IsSet(Class1.FooProperty));
-        }
-
-        [Fact]
-        public void IsSet_Returns_False_For_Set_Property()
-        {
-            var target = new Class1();
+                Assert.Equal("foo", baseValue);
+                Assert.Equal("bar", derivedValue);
+            }
 
-            target.SetValue(Class1.FooProperty, "foo");
+            [Fact]
+            public void Default_Value_Can_Be_Overridden_In_AddOwnered_Property()
+            {
+                var baseValue = Class1.StyledProperty.GetDefaultValue(typeof(Class1));
+                var addOwneredValue = Class1.StyledProperty.GetDefaultValue(typeof(Class3));
 
-            Assert.True(target.IsSet(Class1.FooProperty));
+                Assert.Equal("foo", baseValue);
+                Assert.Equal("baz", addOwneredValue);
+            }
         }
 
-        [Fact]
-        public void IsSet_Returns_False_For_Cleared_Property()
+        public class DirectProperty : AvaloniaObjectTests_Metadata
         {
-            var target = new Class1();
+            [Fact]
+            public void Unset_Value_Can_Be_Overridden_In_Derived_Class()
+            {
+                var baseValue = Class1.DirectProperty.GetUnsetValue(typeof(Class1));
+                var derivedValue = Class1.DirectProperty.GetUnsetValue(typeof(Class2));
 
-            target.SetValue(Class1.FooProperty, "foo");
-            target.SetValue(Class1.FooProperty, AvaloniaProperty.UnsetValue);
+                Assert.Equal("foo", baseValue);
+                Assert.Equal("bar", derivedValue);
+            }
 
-            Assert.False(target.IsSet(Class1.FooProperty));
+            [Fact]
+            public void Unset_Value_Can_Be_Overridden_In_AddOwnered_Property()
+            {
+                var baseValue = Class1.DirectProperty.GetUnsetValue(typeof(Class1));
+                var addOwneredValue = Class3.DirectProperty.GetUnsetValue(typeof(Class3));
+
+                Assert.Equal("foo", baseValue);
+                Assert.Equal("baz", addOwneredValue);
+            }
         }
 
         private class Class1 : AvaloniaObject
         {
-            public static readonly StyledProperty<string> FooProperty =
-                AvaloniaProperty.Register<Class1, string>("Foo");
+            public static readonly StyledProperty<string> StyledProperty =
+                AvaloniaProperty.Register<Class1, string>("Styled", "foo");
+
+            public static readonly DirectProperty<Class1, string> DirectProperty =
+                AvaloniaProperty.RegisterDirect<Class1, string>("Styled", o => o.Direct, unsetValue: "foo");
+
+            private string _direct;
+
+            public string Direct
+            {
+                get => _direct;
+            }
         }
 
         private class Class2 : Class1
         {
-            public static readonly StyledProperty<string> BarProperty =
-                AvaloniaProperty.Register<Class2, string>("Bar");
+            static Class2()
+            {
+                StyledProperty.OverrideDefaultValue<Class2>("bar");
+                DirectProperty.OverrideMetadata<Class2>(new DirectPropertyMetadata<string>("bar"));
+            }
         }
 
-        private class AttachedOwner
+        private class Class3 : AvaloniaObject
         {
-            public static readonly AttachedProperty<string> AttachedProperty =
-                AvaloniaProperty.RegisterAttached<AttachedOwner, Class1, string>("Attached");
+            public static readonly StyledProperty<string> StyledProperty =
+                Class1.StyledProperty.AddOwner<Class3>();
+
+            public static readonly DirectProperty<Class3, string> DirectProperty =
+                Class1.DirectProperty.AddOwner<Class3>(o => o.Direct, unsetValue: "baz");
+
+            private string _direct;
+
+            static Class3()
+            {
+                StyledProperty.OverrideDefaultValue<Class3>("baz");
+            }
+
+            public string Direct
+            {
+                get => _direct;
+            }
         }
     }
 }

+ 29 - 0
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_SetValue.cs

@@ -39,6 +39,35 @@ namespace Avalonia.Base.UnitTests
             Assert.Equal(1, raised);
         }
 
+        [Fact]
+        public void IsSet_Returns_False_For_Unset_Property()
+        {
+            var target = new Class1();
+
+            Assert.False(target.IsSet(Class1.FooProperty));
+        }
+
+        [Fact]
+        public void IsSet_Returns_False_For_Set_Property()
+        {
+            var target = new Class1();
+
+            target.SetValue(Class1.FooProperty, "foo");
+
+            Assert.True(target.IsSet(Class1.FooProperty));
+        }
+
+        [Fact]
+        public void IsSet_Returns_False_For_Cleared_Property()
+        {
+            var target = new Class1();
+
+            target.SetValue(Class1.FooProperty, "foo");
+            target.SetValue(Class1.FooProperty, AvaloniaProperty.UnsetValue);
+
+            Assert.False(target.IsSet(Class1.FooProperty));
+        }
+
         [Fact]
         public void SetValue_Sets_Value()
         {

+ 1 - 2
tests/Avalonia.Base.UnitTests/DirectPropertyTests.cs

@@ -11,8 +11,7 @@ namespace Avalonia.Base.UnitTests
                 "test", 
                 o => null, 
                 null,
-                new DirectPropertyMetadata<string>(),
-                false);
+                new DirectPropertyMetadata<string>());
 
             Assert.True(target.IsDirect);
         }

+ 15 - 0
tests/Avalonia.Styling.UnitTests/SetterTests.cs

@@ -35,6 +35,21 @@ namespace Avalonia.Styling.UnitTests
             Assert.Equal("foo", control.Text);
         }
 
+        [Fact]
+        public void Setter_Should_Handle_Binding_Producing_UnsetValue()
+        {
+            var control = new TextBlock();
+            var subject = new BehaviorSubject<object>(AvaloniaProperty.UnsetValue);
+            var descriptor = InstancedBinding.OneWay(subject);
+            var binding = Mock.Of<IBinding>(x => x.Initiate(control, TextBlock.TagProperty, null, false) == descriptor);
+            var style = Mock.Of<IStyle>();
+            var setter = new Setter(TextBlock.TagProperty, binding);
+
+            setter.Instance(control).Start(false);
+
+            Assert.Equal("", control.Text);
+        }
+
         [Fact]
         public void Setter_Should_Materialize_Template_To_Property()
         {