Browse Source

Merge branch 'master' into rxui-updates

pull/4473/head
Artyom V. Gorchakov 6 years ago
committed by GitHub
parent
commit
724cc8b4e5
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      samples/ControlCatalog/App.xaml
  2. 5
      samples/ControlCatalog/Pages/ContextMenuPage.xaml
  3. 6
      samples/ControlCatalog/Pages/MenuPage.xaml
  4. 6
      src/Avalonia.Base/ApiCompatBaseline.txt
  5. 4
      src/Avalonia.Base/AvaloniaObject.cs
  6. 6
      src/Avalonia.Base/AvaloniaProperty.cs
  7. 25
      src/Avalonia.Base/DirectProperty.cs
  8. 39
      src/Avalonia.Base/DirectPropertyBase.cs
  9. 2
      src/Avalonia.Themes.Fluent/ContextMenu.xaml
  10. 14
      src/Avalonia.Themes.Fluent/MenuItem.xaml
  11. 23
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_DataValidation.cs
  12. 6
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs
  13. 105
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Metadata.cs
  14. 29
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_SetValue.cs
  15. 3
      tests/Avalonia.Base.UnitTests/DirectPropertyTests.cs

2
samples/ControlCatalog/App.xaml

@ -11,7 +11,7 @@
<Setter Property="FontSize" Value="14" />
</Style>
<Style Selector="TextBlock.h3">
<Setter Property="FontSize" Value="10" />
<Setter Property="FontSize" Value="12" />
</Style>
<StyleInclude Source="/SideBar.xaml"/>
</Application.Styles>

5
samples/ControlCatalog/Pages/ContextMenuPage.xaml

@ -14,13 +14,14 @@
Padding="48,48,48,48">
<Border.ContextMenu>
<ContextMenu>
<MenuItem Header="Standard _Menu Item"/>
<MenuItem Header="Standard _Menu Item" InputGesture="Ctrl+A" />
<MenuItem Header="_Disabled Menu Item" IsEnabled="False" InputGesture="Ctrl+D" />
<Separator/>
<MenuItem Header="Menu with _Submenu">
<MenuItem Header="Submenu _1"/>
<MenuItem Header="Submenu _2"/>
</MenuItem>
<MenuItem Header="Menu Item with _Icon">
<MenuItem Header="Menu Item with _Icon" InputGesture="Ctrl+Shift+B">
<MenuItem.Icon>
<Image Source="/Assets/github_icon.png"/>
</MenuItem.Icon>

6
samples/ControlCatalog/Pages/MenuPage.xaml

@ -16,13 +16,17 @@
<TextBlock Classes="h3" Margin="4 8">Defined in XAML</TextBlock>
<Menu>
<MenuItem Header="_First">
<MenuItem Header="Standard _Menu Item" InputGesture="Ctrl+A"/>
<MenuItem Header="Standard _Menu Item" InputGesture="Ctrl+A" />
<MenuItem Header="_Disabled Menu Item" IsEnabled="False" InputGesture="Ctrl+D" />
<Separator/>
<MenuItem Header="Menu with _Submenu">
<MenuItem Header="Submenu _1"/>
<MenuItem Header="Submenu _2 with Submenu">
<MenuItem Header="Submenu Level 2" />
</MenuItem>
<MenuItem Header="Submenu _3 with Submenu Disabled" IsEnabled="False">
<MenuItem Header="Submenu Level 2" />
</MenuItem>
</MenuItem>
<MenuItem Header="Menu Item with _Icon" InputGesture="Ctrl+Shift+B">
<MenuItem.Icon>

6
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

4
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);
}

6
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;
}

25
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;

39
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)
{

2
src/Avalonia.Themes.Fluent/ContextMenu.xaml

@ -9,6 +9,8 @@
<Border.ContextMenu>
<ContextMenu>
<MenuItem Header="Standard _Menu Item" />
<MenuItem Header="Disabled"
IsEnabled="False" />
<Separator />
<MenuItem Header="Menu with _Submenu">
<MenuItem Header="Submenu _1" />

14
src/Avalonia.Themes.Fluent/MenuItem.xaml

@ -8,6 +8,8 @@
Height="200">
<Menu VerticalAlignment="Top">
<MenuItem Header="File">
<MenuItem Header="Disabled"
IsEnabled="False" />
<MenuItem Header="New"
InputGesture="Ctrl+N">
<MenuItem Header="XML" />
@ -83,7 +85,6 @@
Content="{TemplateBinding Header}"
VerticalAlignment="Center"
HorizontalAlignment="Stretch"
TextBlock.Foreground="{TemplateBinding Foreground}"
Grid.Column="1">
<ContentPresenter.DataTemplates>
<DataTemplate DataType="sys:String">
@ -213,6 +214,9 @@
<Style Selector="MenuItem:selected /template/ Border#PART_LayoutRoot">
<Setter Property="Background" Value="{DynamicResource MenuFlyoutItemBackgroundPointerOver}" />
</Style>
<Style Selector="MenuItem:selected /template/ ContentPresenter#PART_HeaderPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource MenuFlyoutItemForegroundPointerOver}" />
</Style>
<Style Selector="MenuItem:selected /template/ TextBlock#PART_InputGestureText">
<Setter Property="Foreground" Value="{DynamicResource MenuFlyoutItemKeyboardAcceleratorTextForegroundPointerOver}" />
</Style>
@ -224,6 +228,9 @@
<Style Selector="MenuItem:pressed /template/ Border#PART_LayoutRoot:pointerover">
<Setter Property="Background" Value="{DynamicResource MenuFlyoutItemBackgroundPressed}" />
</Style>
<Style Selector="MenuItem:pressed /template/ Border#PART_LayoutRoot:pointerover ContentPresenter#PART_HeaderPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource MenuFlyoutItemForegroundPressed}" />
</Style>
<Style Selector="MenuItem:pressed /template/ Border#PART_LayoutRoot:pointerover TextBlock#PART_InputGestureText">
<Setter Property="Foreground" Value="{DynamicResource MenuFlyoutItemKeyboardAcceleratorTextForegroundPressed}" />
</Style>
@ -231,9 +238,12 @@
<Setter Property="Fill" Value="{DynamicResource MenuFlyoutSubItemChevronPressed}" />
</Style>
<Style Selector="MenuItem:disabled">
<Style Selector="MenuItem:disabled /template/ Border#PART_LayoutRoot">
<Setter Property="Background" Value="{DynamicResource MenuFlyoutItemBackgroundDisabled}" />
</Style>
<Style Selector="MenuItem:disabled /template/ ContentPresenter#PART_HeaderPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource MenuFlyoutItemForegroundDisabled}" />
</Style>
<Style Selector="MenuItem:disabled /template/ TextBlock#PART_InputGestureText">
<Setter Property="Foreground" Value="{DynamicResource MenuFlyoutItemKeyboardAcceleratorTextForegroundDisabled}" />
</Style>

23
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;

6
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);

105
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
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()
{

3
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);
}

Loading…
Cancel
Save