Browse Source

Merge branch 'master' into allow-auto-hide-attached

pull/6054/head
Dariusz Komosiński 5 years ago
committed by GitHub
parent
commit
ca8491815c
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 21
      src/Avalonia.Animation/AnimationInstance`1.cs
  2. 23
      src/Avalonia.Input/KeyboardDevice.cs
  3. 2
      src/Avalonia.Styling/Styling/Activators/PropertyEqualsActivator.cs
  4. 32
      src/Avalonia.Styling/Styling/PropertyEqualsSelector.cs
  5. 46
      tests/Avalonia.Input.UnitTests/KeyboardDeviceTests.cs
  6. 23
      tests/Avalonia.Styling.UnitTests/SelectorTests_PropertyEquals.cs

21
src/Avalonia.Animation/AnimationInstance`1.cs

@ -5,6 +5,7 @@ using Avalonia.Animation.Animators;
using Avalonia.Animation.Utils; using Avalonia.Animation.Utils;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Reactive; using Avalonia.Reactive;
using JetBrains.Annotations;
namespace Avalonia.Animation namespace Avalonia.Animation
{ {
@ -45,8 +46,9 @@ namespace Avalonia.Animation
_onCompleteAction = OnComplete; _onCompleteAction = OnComplete;
_interpolator = Interpolator; _interpolator = Interpolator;
_baseClock = baseClock; _baseClock = baseClock;
_neutralValue = (T)_targetControl.GetValue(_animator.Property); control.PropertyChanged += ControlPropertyChanged;
UpdateNeutralValue();
FetchProperties(); FetchProperties();
} }
@ -216,5 +218,22 @@ namespace Avalonia.Animation
} }
} }
} }
private void UpdateNeutralValue()
{
var property = _animator.Property;
var baseValue = _targetControl.GetBaseValue(property, BindingPriority.LocalValue);
_neutralValue = baseValue != AvaloniaProperty.UnsetValue ?
(T)baseValue : (T)_targetControl.GetValue(property);
}
private void ControlPropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == _animator.Property && e.Priority > BindingPriority.Animation)
{
UpdateNeutralValue();
}
}
} }
} }

23
src/Avalonia.Input/KeyboardDevice.cs

@ -217,12 +217,31 @@ namespace Avalonia.Input
{ {
var bindings = (currentHandler as IInputElement)?.KeyBindings; var bindings = (currentHandler as IInputElement)?.KeyBindings;
if (bindings != null) if (bindings != null)
{
KeyBinding[]? bindingsCopy = null;
// Create a copy of the KeyBindings list if there's a binding which matches the event.
// If we don't do this the foreach loop will throw an InvalidOperationException when the KeyBindings list is changed.
// This can happen when a new view is loaded which adds its own KeyBindings to the handler.
foreach (var binding in bindings) foreach (var binding in bindings)
{ {
if (ev.Handled) if (binding.Gesture?.Matches(ev) == true)
{
bindingsCopy = bindings.ToArray();
break; break;
binding.TryHandle(ev); }
}
if (bindingsCopy is object)
{
foreach (var binding in bindingsCopy)
{
if (ev.Handled)
break;
binding.TryHandle(ev);
}
} }
}
currentHandler = currentHandler.VisualParent; currentHandler = currentHandler.VisualParent;
} }

2
src/Avalonia.Styling/Styling/Activators/PropertyEqualsActivator.cs

@ -33,6 +33,6 @@ namespace Avalonia.Styling.Activators
void IObserver<object>.OnCompleted() { } void IObserver<object>.OnCompleted() { }
void IObserver<object>.OnError(Exception error) { } void IObserver<object>.OnError(Exception error) { }
void IObserver<object>.OnNext(object value) => PublishNext(Equals(value, _value)); void IObserver<object>.OnNext(object value) => PublishNext(PropertyEqualsSelector.Compare(_property.PropertyType, value, _value));
} }
} }

32
src/Avalonia.Styling/Styling/PropertyEqualsSelector.cs

@ -1,4 +1,6 @@
using System; using System;
using System.ComponentModel;
using System.Globalization;
using System.Text; using System.Text;
using Avalonia.Styling.Activators; using Avalonia.Styling.Activators;
@ -75,11 +77,37 @@ namespace Avalonia.Styling
} }
else else
{ {
var result = (control.GetValue(_property) ?? string.Empty).Equals(_value); return Compare(_property.PropertyType, control.GetValue(_property), _value)
return result ? SelectorMatch.AlwaysThisInstance : SelectorMatch.NeverThisInstance; ? SelectorMatch.AlwaysThisInstance
: SelectorMatch.NeverThisInstance;
} }
} }
protected override Selector? MovePrevious() => _previous; protected override Selector? MovePrevious() => _previous;
internal static bool Compare(Type propertyType, object propertyValue, object? value)
{
if (propertyType == typeof(object) &&
propertyValue?.GetType() is Type inferredType)
{
propertyType = inferredType;
}
var valueType = value?.GetType();
if (valueType is null || propertyType.IsAssignableFrom(valueType))
{
return Equals(propertyValue, value);
}
var converter = TypeDescriptor.GetConverter(propertyType);
if (converter?.CanConvertFrom(valueType) == true)
{
return Equals(propertyValue, converter.ConvertFrom(null, CultureInfo.InvariantCulture, value));
}
return false;
}
} }
} }

46
tests/Avalonia.Input.UnitTests/KeyboardDeviceTests.cs

@ -1,5 +1,9 @@
using Avalonia.Input.Raw; using System;
using System.Windows.Input;
using Avalonia.Controls;
using Avalonia.Input.Raw;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.UnitTests;
using Moq; using Moq;
using Xunit; using Xunit;
@ -86,5 +90,45 @@ namespace Avalonia.Input.UnitTests
focused.Verify(x => x.RaiseEvent(It.IsAny<TextInputEventArgs>())); focused.Verify(x => x.RaiseEvent(It.IsAny<TextInputEventArgs>()));
} }
[Fact]
public void Can_Change_KeyBindings_In_Keybinding_Event_Handler()
{
var target = new KeyboardDevice();
var button = new Button();
var root = new TestRoot(button);
var raised = 0;
button.KeyBindings.Add(new KeyBinding
{
Gesture = new KeyGesture(Key.O, KeyModifiers.Control),
Command = new DelegateCommand(() =>
{
button.KeyBindings.Clear();
++raised;
}),
});
target.SetFocusedElement(button, NavigationMethod.Pointer, 0);
target.ProcessRawEvent(
new RawKeyEventArgs(
target,
0,
root,
RawKeyEventType.KeyDown,
Key.O,
RawInputModifiers.Control));
Assert.Equal(1, raised);
}
private class DelegateCommand : ICommand
{
private readonly Action _action;
public DelegateCommand(Action action) => _action = action;
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter) => true;
public void Execute(object parameter) => _action();
}
} }
} }

23
tests/Avalonia.Styling.UnitTests/SelectorTests_PropertyEquals.cs

@ -22,6 +22,23 @@ namespace Avalonia.Styling.UnitTests
Assert.False(await activator.Take(1)); Assert.False(await activator.Take(1));
} }
[Theory]
[InlineData("Bar", FooBar.Bar)]
[InlineData("352", 352)]
[InlineData("0.1", 0.1)]
public async Task PropertyEquals_Matches_When_Property_Has_Matching_Value_And_Different_Type(string literal, object value)
{
var control = new TextBlock();
var target = default(Selector).PropertyEquals(TextBlock.TagProperty, literal);
var activator = target.Match(control).Activator.ToObservable();
Assert.False(await activator.Take(1));
control.Tag = value;
Assert.True(await activator.Take(1));
control.Tag = null;
Assert.False(await activator.Take(1));
}
[Fact] [Fact]
public void OfType_PropertyEquals_Doesnt_Match_Control_Of_Wrong_Type() public void OfType_PropertyEquals_Doesnt_Match_Control_Of_Wrong_Type()
{ {
@ -40,5 +57,11 @@ namespace Avalonia.Styling.UnitTests
Assert.Equal("TextBlock[Text=foo]", target.ToString()); Assert.Equal("TextBlock[Text=foo]", target.ToString());
} }
private enum FooBar
{
Foo,
Bar
}
} }
} }

Loading…
Cancel
Save