Browse Source

Merge branch 'master' into feature/focus-visible

pull/4216/head
Steven Kirk 6 years ago
committed by GitHub
parent
commit
38bbe1c3f2
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 21
      samples/ControlCatalog/Pages/SliderPage.xaml
  2. 4
      src/Avalonia.Base/AvaloniaProperty.cs
  3. 4
      src/Avalonia.Base/AvaloniaPropertyRegistry.cs
  4. 2
      src/Avalonia.Base/Collections/Pooled/IReadOnlyPooledList.cs
  5. 3
      src/Avalonia.Base/Collections/Pooled/PooledList.cs
  6. 34
      src/Avalonia.Base/Data/BindingValue.cs
  7. 16
      src/Avalonia.Base/Data/Optional.cs
  8. 2
      src/Avalonia.Base/DirectPropertyBase.cs
  9. 2
      src/Avalonia.Base/IStyledPropertyMetadata.cs
  10. 140
      src/Avalonia.Base/Metadata/NullableAttributes.cs
  11. 7
      src/Avalonia.Base/PropertyStore/IPriorityValueEntry.cs
  12. 7
      src/Avalonia.Base/PropertyStore/LocalValueEntry.cs
  13. 3
      src/Avalonia.Base/StyledPropertyMetadata`1.cs
  14. 45
      src/Avalonia.Base/Utilities/StyleClassParser.cs
  15. 2
      src/Avalonia.Base/ValueStore.cs
  16. 5
      src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
  17. 66
      src/Avalonia.Controls/Slider.cs
  18. 12
      src/Avalonia.Controls/TickBar.cs
  19. 4
      src/Avalonia.Input/Navigation/TabNavigation.cs
  20. 5
      src/Avalonia.Styling/IStyledElement.cs
  21. 1
      src/Avalonia.Styling/StyledElement.cs
  22. 1
      src/Avalonia.Styling/Styling/Styles.cs
  23. 9
      src/Avalonia.Themes.Default/Slider.xaml
  24. 1
      src/Avalonia.Themes.Fluent/Slider.xaml
  25. 4
      src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs
  26. 4
      tests/Avalonia.Controls.UnitTests/DatePickerTests.cs
  27. 7
      tests/Avalonia.Controls.UnitTests/TimePickerTests.cs
  28. 54
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs

21
samples/ControlCatalog/Pages/SliderPage.xaml

@ -6,11 +6,22 @@
<TextBlock Classes="h2">A control that lets the user select from a range of values by moving a Thumb control along a Track.</TextBlock>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0 16 0 0" Spacing="16">
<Slider Value="0"
Minimum="0"
Maximum="100"
TickFrequency="10"
Width="300"/>
<StackPanel Orientation="Vertical"
HorizontalAlignment="Center">
<Slider Value="0"
Minimum="0"
Maximum="100"
TickFrequency="10"
Width="300" />
<Slider Name="CustomTickedSlider"
Value="0"
Minimum="0"
Maximum="100"
TickPlacement="BottomRight"
IsSnapToTickEnabled="True"
Ticks="0,20,25,40,75,100"
Width="300" />
</StackPanel>
<Slider Value="0"
Minimum="0"
Maximum="100"

4
src/Avalonia.Base/AvaloniaProperty.cs

@ -159,8 +159,6 @@ namespace Avalonia
/// </summary>
internal int Id { get; }
internal bool HasChangedSubscriptions => _changed?.HasObservers ?? false;
/// <summary>
/// Provides access to a property's binding via the <see cref="AvaloniaObject"/>
/// indexer.
@ -512,7 +510,7 @@ namespace Avalonia
/// <returns>
/// An <see cref="IDisposable"/> if setting the property can be undone, otherwise null.
/// </returns>
internal abstract IDisposable? RouteSetValue(
internal abstract IDisposable RouteSetValue(
IAvaloniaObject o,
object value,
BindingPriority priority);

4
src/Avalonia.Base/AvaloniaPropertyRegistry.cs

@ -362,7 +362,7 @@ namespace Avalonia
/// <param name="property">The property.</param>
/// <remarks>
/// You won't usually want to call this method directly, instead use the
/// <see cref="AvaloniaProperty.Register{TOwner, TValue}(string, TValue, bool, Data.BindingMode, Func{TOwner, TValue, TValue}, Action{IAvaloniaObject, bool})"/>
/// <see cref="AvaloniaProperty.Register{TOwner, TValue}(string, TValue, bool, Data.BindingMode, Func{TValue, bool}, Func{IAvaloniaObject, TValue, TValue}, Action{IAvaloniaObject, bool})"/>
/// method.
/// </remarks>
public void Register(Type type, AvaloniaProperty property)
@ -413,7 +413,7 @@ namespace Avalonia
/// <param name="property">The property.</param>
/// <remarks>
/// You won't usually want to call this method directly, instead use the
/// <see cref="AvaloniaProperty.RegisterAttached{THost, TValue}(string, Type, TValue, bool, Data.BindingMode, Func{THost, TValue, TValue})"/>
/// <see cref="AvaloniaProperty.RegisterAttached{THost, TValue}(string, Type, TValue, bool, Data.BindingMode, Func{TValue, bool}, Func{IAvaloniaObject, TValue, TValue})"/>
/// method.
/// </remarks>
public void RegisterAttached(Type type, AvaloniaProperty property)

2
src/Avalonia.Base/Collections/Pooled/IReadOnlyPooledList.cs

@ -13,9 +13,11 @@ namespace Avalonia.Collections.Pooled
public interface IReadOnlyPooledList<T> : IReadOnlyList<T>
{
#pragma warning disable CS0419
/// <summary>
/// Gets a <see cref="System.ReadOnlySpan{T}"/> for the items currently in the collection.
/// </summary>
#pragma warning restore CS0419
ReadOnlySpan<T> Span { get; }
}
}

3
src/Avalonia.Base/Collections/Pooled/PooledList.cs

@ -138,7 +138,6 @@ namespace Avalonia.Collections.Pooled
/// initially empty, but will have room for the given number of elements
/// before any reallocations are required.
/// </summary>
/// <param name="sizeToCapacity">If true, Count of list equals capacity. Depending on ClearMode, rented items may or may not hold dirty values.</param>
public PooledList(int capacity, ClearMode clearMode, ArrayPool<T> customPool, bool sizeToCapacity)
{
if (capacity < 0)
@ -499,11 +498,13 @@ namespace Avalonia.Collections.Pooled
public void AddRange(T[] array)
=> AddRange(array.AsSpan());
#pragma warning disable CS0419
/// <summary>
/// Adds the elements of the given <see cref="ReadOnlySpan{T}"/> to the end of this list. If
/// required, the capacity of the list is increased to twice the previous
/// capacity or the new size, whichever is larger.
/// </summary>
#pragma warning restore CS0419
public void AddRange(ReadOnlySpan<T> span)
{
var newSpan = InsertSpan(_size, span.Length, false);

34
src/Avalonia.Base/Data/BindingValue.cs

@ -1,4 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Utilities;
#nullable enable
@ -81,14 +82,14 @@ namespace Avalonia.Data
/// </remarks>
public readonly struct BindingValue<T>
{
private readonly T _value;
[AllowNull] private readonly T _value;
/// <summary>
/// Initializes a new instance of the <see cref="BindingValue{T}"/> struct with a type of
/// <see cref="BindingValueType.Value"/>
/// </summary>
/// <param name="value">The value.</param>
public BindingValue(T value)
public BindingValue([AllowNull] T value)
{
ValidateValue(value);
_value = value;
@ -96,7 +97,7 @@ namespace Avalonia.Data
Error = null;
}
private BindingValue(BindingValueType type, T value, Exception? error)
private BindingValue(BindingValueType type, [AllowNull] T value, Exception? error)
{
_value = value;
Type = type;
@ -154,7 +155,7 @@ namespace Avalonia.Data
BindingValueType.UnsetValue => AvaloniaProperty.UnsetValue,
BindingValueType.DoNothing => BindingOperations.DoNothing,
BindingValueType.Value => _value,
BindingValueType.BindingError =>
BindingValueType.BindingError =>
new BindingNotification(Error, BindingErrorType.Error),
BindingValueType.BindingErrorWithFallback =>
new BindingNotification(Error, BindingErrorType.Error, Value),
@ -175,7 +176,7 @@ namespace Avalonia.Data
/// The binding type is <see cref="BindingValueType.UnsetValue"/> or
/// <see cref="BindingValueType.DoNothing"/>.
/// </exception>
public BindingValue<T> WithValue(T value)
public BindingValue<T> WithValue([AllowNull] T value)
{
if (Type == BindingValueType.DoNothing)
{
@ -190,6 +191,7 @@ namespace Avalonia.Data
/// Gets the value of the binding value if present, otherwise the default value.
/// </summary>
/// <returns>The value.</returns>
[return: MaybeNull]
public T GetValueOrDefault() => HasValue ? _value : default;
/// <summary>
@ -206,6 +208,7 @@ namespace Avalonia.Data
/// The value if present and of the correct type, `default(TResult)` if the value is
/// not present or of an incorrect type.
/// </returns>
[return: MaybeNull]
public TResult GetValueOrDefault<TResult>()
{
return HasValue ?
@ -222,7 +225,8 @@ namespace Avalonia.Data
/// present but not of the correct type or null, or <paramref name="defaultValue"/> if the
/// value is not present.
/// </returns>
public TResult GetValueOrDefault<TResult>(TResult defaultValue)
[return: MaybeNull]
public TResult GetValueOrDefault<TResult>([AllowNull] TResult defaultValue)
{
return HasValue ?
_value is TResult result ? result : default
@ -242,7 +246,7 @@ namespace Avalonia.Data
UnsetValueType _ => Unset,
DoNothingType _ => DoNothing,
BindingNotification n => n.ToBindingValue().Cast<T>(),
_ => (T)value
_ => new BindingValue<T>((T)value)
};
}
@ -250,7 +254,7 @@ namespace Avalonia.Data
/// Creates a binding value from an instance of the underlying value type.
/// </summary>
/// <param name="value">The value.</param>
public static implicit operator BindingValue<T>(T value) => new BindingValue<T>(value);
public static implicit operator BindingValue<T>([AllowNull] T value) => new BindingValue<T>(value);
/// <summary>
/// Creates a binding value from an <see cref="Optional{T}"/>.
@ -278,7 +282,7 @@ namespace Avalonia.Data
/// <param name="e">The binding error.</param>
public static BindingValue<T> BindingError(Exception e)
{
e = e ?? throw new ArgumentNullException("e");
e = e ?? throw new ArgumentNullException(nameof(e));
return new BindingValue<T>(BindingValueType.BindingError, default, e);
}
@ -290,7 +294,7 @@ namespace Avalonia.Data
/// <param name="fallbackValue">The fallback value.</param>
public static BindingValue<T> BindingError(Exception e, T fallbackValue)
{
e = e ?? throw new ArgumentNullException("e");
e = e ?? throw new ArgumentNullException(nameof(e));
return new BindingValue<T>(BindingValueType.BindingErrorWithFallback, fallbackValue, e);
}
@ -303,7 +307,7 @@ namespace Avalonia.Data
/// <param name="fallbackValue">The fallback value.</param>
public static BindingValue<T> BindingError(Exception e, Optional<T> fallbackValue)
{
e = e ?? throw new ArgumentNullException("e");
e = e ?? throw new ArgumentNullException(nameof(e));
return new BindingValue<T>(
fallbackValue.HasValue ?
@ -319,7 +323,7 @@ namespace Avalonia.Data
/// <param name="e">The data validation error.</param>
public static BindingValue<T> DataValidationError(Exception e)
{
e = e ?? throw new ArgumentNullException("e");
e = e ?? throw new ArgumentNullException(nameof(e));
return new BindingValue<T>(BindingValueType.DataValidationError, default, e);
}
@ -331,7 +335,7 @@ namespace Avalonia.Data
/// <param name="fallbackValue">The fallback value.</param>
public static BindingValue<T> DataValidationError(Exception e, T fallbackValue)
{
e = e ?? throw new ArgumentNullException("e");
e = e ?? throw new ArgumentNullException(nameof(e));
return new BindingValue<T>(BindingValueType.DataValidationErrorWithFallback, fallbackValue, e);
}
@ -344,7 +348,7 @@ namespace Avalonia.Data
/// <param name="fallbackValue">The fallback value.</param>
public static BindingValue<T> DataValidationError(Exception e, Optional<T> fallbackValue)
{
e = e ?? throw new ArgumentNullException("e");
e = e ?? throw new ArgumentNullException(nameof(e));
return new BindingValue<T>(
fallbackValue.HasValue ?
@ -354,7 +358,7 @@ namespace Avalonia.Data
e);
}
private static void ValidateValue(T value)
private static void ValidateValue([AllowNull] T value)
{
if (value is UnsetValueType)
{

16
src/Avalonia.Base/Data/Optional.cs

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
#nullable enable
@ -22,13 +23,13 @@ namespace Avalonia.Data
/// </remarks>
public readonly struct Optional<T> : IEquatable<Optional<T>>
{
private readonly T _value;
[AllowNull] private readonly T _value;
/// <summary>
/// Initializes a new instance of the <see cref="Optional{T}"/> struct with value.
/// </summary>
/// <param name="value">The value.</param>
public Optional(T value)
public Optional([AllowNull] T value)
{
_value = value;
HasValue = true;
@ -48,7 +49,7 @@ namespace Avalonia.Data
public T Value => HasValue ? _value : throw new InvalidOperationException("Optional has no value.");
/// <inheritdoc/>
public override bool Equals(object obj) => obj is Optional<T> o && this == o;
public override bool Equals(object? obj) => obj is Optional<T> o && this == o;
/// <inheritdoc/>
public bool Equals(Optional<T> other) => this == other;
@ -69,6 +70,7 @@ namespace Avalonia.Data
/// Gets the value if present, otherwise the default value.
/// </summary>
/// <returns>The value.</returns>
[return: MaybeNull]
public T GetValueOrDefault() => HasValue ? _value : default;
/// <summary>
@ -85,6 +87,7 @@ namespace Avalonia.Data
/// The value if present and of the correct type, `default(TResult)` if the value is
/// not present or of an incorrect type.
/// </returns>
[return: MaybeNull]
public TResult GetValueOrDefault<TResult>()
{
return HasValue ?
@ -101,7 +104,8 @@ namespace Avalonia.Data
/// present but not of the correct type or null, or <paramref name="defaultValue"/> if the
/// value is not present.
/// </returns>
public TResult GetValueOrDefault<TResult>(TResult defaultValue)
[return: MaybeNull]
public TResult GetValueOrDefault<TResult>([AllowNull] TResult defaultValue)
{
return HasValue ?
_value is TResult result ? result : default
@ -112,7 +116,7 @@ namespace Avalonia.Data
/// Creates an <see cref="Optional{T}"/> from an instance of the underlying value type.
/// </summary>
/// <param name="value">The value.</param>
public static implicit operator Optional<T>(T value) => new Optional<T>(value);
public static implicit operator Optional<T>([AllowNull] T value) => new Optional<T>(value);
/// <summary>
/// Compares two <see cref="Optional{T}"/>s for inequality.
@ -128,7 +132,7 @@ namespace Avalonia.Data
/// <param name="x">The first value.</param>
/// <param name="y">The second value.</param>
/// <returns>True if the values are equal; otherwise false.</returns>
public static bool operator==(Optional<T> x, Optional<T> y)
public static bool operator ==(Optional<T> x, Optional<T> y)
{
if (!x.HasValue && !y.HasValue)
{

2
src/Avalonia.Base/DirectPropertyBase.cs

@ -120,7 +120,7 @@ namespace Avalonia
return o.GetValue<TValue>(this);
}
internal override object RouteGetBaseValue(IAvaloniaObject o, BindingPriority maxPriority)
internal override object? RouteGetBaseValue(IAvaloniaObject o, BindingPriority maxPriority)
{
return o.GetValue<TValue>(this);
}

2
src/Avalonia.Base/IStyledPropertyMetadata.cs

@ -1,5 +1,3 @@
using System;
namespace Avalonia
{
/// <summary>

140
src/Avalonia.Base/Metadata/NullableAttributes.cs

@ -0,0 +1,140 @@
#pragma warning disable MA0048 // File name must match type name
#define INTERNAL_NULLABLE_ATTRIBUTES
#if NETSTANDARD2_0 || NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 || NET45 || NET451 || NET452 || NET6 || NET461 || NET462 || NET47 || NET471 || NET472 || NET48
// https://github.com/dotnet/corefx/blob/48363ac826ccf66fbe31a5dcb1dc2aab9a7dd768/src/Common/src/CoreLib/System/Diagnostics/CodeAnalysis/NullableAttributes.cs
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace System.Diagnostics.CodeAnalysis
{
/// <summary>Specifies that null is allowed as an input even if the corresponding type disallows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
internal
#else
public
#endif
sealed class AllowNullAttribute : Attribute
{ }
/// <summary>Specifies that null is disallowed as an input even if the corresponding type allows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
internal
#else
public
#endif
sealed class DisallowNullAttribute : Attribute
{ }
/// <summary>Specifies that an output may be null even if the corresponding type disallows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
internal
#else
public
#endif
sealed class MaybeNullAttribute : Attribute
{ }
/// <summary>Specifies that an output will not be null even if the corresponding type allows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
internal
#else
public
#endif
sealed class NotNullAttribute : Attribute
{ }
/// <summary>Specifies that when a method returns <see cref="ReturnValue"/>, the parameter may be null even if the corresponding type disallows it.</summary>
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
internal
#else
public
#endif
sealed class MaybeNullWhenAttribute : Attribute
{
/// <summary>Initializes the attribute with the specified return value condition.</summary>
/// <param name="returnValue">
/// The return value condition. If the method returns this value, the associated parameter may be null.
/// </param>
public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
/// <summary>Gets the return value condition.</summary>
public bool ReturnValue { get; }
}
/// <summary>Specifies that when a method returns <see cref="ReturnValue"/>, the parameter will not be null even if the corresponding type allows it.</summary>
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
internal
#else
public
#endif
sealed class NotNullWhenAttribute : Attribute
{
/// <summary>Initializes the attribute with the specified return value condition.</summary>
/// <param name="returnValue">
/// The return value condition. If the method returns this value, the associated parameter will not be null.
/// </param>
public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
/// <summary>Gets the return value condition.</summary>
public bool ReturnValue { get; }
}
/// <summary>Specifies that the output will be non-null if the named parameter is non-null.</summary>
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
internal
#else
public
#endif
sealed class NotNullIfNotNullAttribute : Attribute
{
/// <summary>Initializes the attribute with the associated parameter name.</summary>
/// <param name="parameterName">
/// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null.
/// </param>
public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName;
/// <summary>Gets the associated parameter name.</summary>
public string ParameterName { get; }
}
/// <summary>Applied to a method that will never return under any circumstance.</summary>
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
internal
#else
public
#endif
sealed class DoesNotReturnAttribute : Attribute
{ }
/// <summary>Specifies that the method will not return if the associated Boolean parameter is passed the specified value.</summary>
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
internal
#else
public
#endif
sealed class DoesNotReturnIfAttribute : Attribute
{
/// <summary>Initializes the attribute with the specified parameter value.</summary>
/// <param name="parameterValue">
/// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to
/// the associated parameter matches this value.
/// </param>
public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue;
/// <summary>Gets the condition parameter value.</summary>
public bool ParameterValue { get; }
}
}
#endif

7
src/Avalonia.Base/PropertyStore/IPriorityValueEntry.cs

@ -1,7 +1,4 @@
using System;
using Avalonia.Data;
#nullable enable
#nullable enable
namespace Avalonia.PropertyStore
{
@ -10,8 +7,6 @@ namespace Avalonia.PropertyStore
/// </summary>
internal interface IPriorityValueEntry : IValue
{
BindingPriority Priority { get; }
void Reparent(IValueSink sink);
}

7
src/Avalonia.Base/PropertyStore/LocalValueEntry.cs

@ -1,4 +1,5 @@
using Avalonia.Data;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Data;
#nullable enable
@ -11,9 +12,9 @@ namespace Avalonia.PropertyStore
/// <typeparam name="T">The property type.</typeparam>
internal class LocalValueEntry<T> : IValue<T>
{
private T _value;
[AllowNull] private T _value;
public LocalValueEntry(T value) => _value = value;
public LocalValueEntry([AllowNull] T value) => _value = value;
public BindingPriority Priority => BindingPriority.LocalValue;
Optional<object> IValue.GetValue() => new Optional<object>(_value);

3
src/Avalonia.Base/StyledPropertyMetadata`1.cs

@ -1,5 +1,4 @@
using System;
using System.Diagnostics;
using Avalonia.Data;
namespace Avalonia
@ -35,7 +34,7 @@ namespace Avalonia
/// <summary>
/// Gets the value coercion callback, if any.
/// </summary>
public Func<IAvaloniaObject, TValue, TValue>? CoerceValue { get; private set; }
public Func<IAvaloniaObject, TValue, TValue> CoerceValue { get; private set; }
object IStyledPropertyMetadata.DefaultValue => DefaultValue;

45
src/Avalonia.Base/Utilities/StyleClassParser.cs

@ -0,0 +1,45 @@
using System;
using System.Globalization;
namespace Avalonia.Utilities
{
#if !BUILDTASK
public
#endif
static class StyleClassParser
{
public static ReadOnlySpan<char> ParseStyleClass(this ref CharacterReader r)
{
if (IsValidIdentifierStart(r.Peek))
{
return r.TakeWhile(c => IsValidIdentifierChar(c));
}
else
{
return ReadOnlySpan<char>.Empty;
}
}
private static bool IsValidIdentifierStart(char c)
{
return char.IsLetter(c) || c == '_';
}
private static bool IsValidIdentifierChar(char c)
{
if (IsValidIdentifierStart(c) || c == '-')
{
return true;
}
else
{
var cat = CharUnicodeInfo.GetUnicodeCategory(c);
return cat == UnicodeCategory.NonSpacingMark ||
cat == UnicodeCategory.SpacingCombiningMark ||
cat == UnicodeCategory.ConnectorPunctuation ||
cat == UnicodeCategory.Format ||
cat == UnicodeCategory.DecimalDigitNumber;
}
}
}
}

2
src/Avalonia.Base/ValueStore.cs

@ -162,7 +162,7 @@ namespace Avalonia
_sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
_owner,
property,
old,
new Optional<T>(old),
default,
BindingPriority.Unset));
}

5
src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj

@ -42,7 +42,10 @@
<Compile Include="../Avalonia.Base/Utilities/IdentifierParser.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Avalonia.Base/Utilities/StyleClassParser.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Remove="../Markup/Avalonia.Markup.Xaml/XamlIl\xamlil.github\**\obj\**\*.cs" />
<Compile Remove="../Markup/Avalonia.Markup.Xaml/XamlIl\xamlil.github\src\XamlIl\TypeSystem\SreTypeSystem.cs" />
<PackageReference Include="Avalonia.Unofficial.Cecil" Version="20190417.2.0" PrivateAssets="All" />

66
src/Avalonia.Controls/Slider.cs

@ -1,4 +1,5 @@
using System;
using Avalonia.Collections;
using Avalonia.Controls.Mixins;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
@ -64,6 +65,12 @@ namespace Avalonia.Controls
public static readonly StyledProperty<TickPlacement> TickPlacementProperty =
AvaloniaProperty.Register<TickBar, TickPlacement>(nameof(TickPlacement), 0d);
/// <summary>
/// Defines the <see cref="TicksProperty"/> property.
/// </summary>
public static readonly StyledProperty<AvaloniaList<double>> TicksProperty =
TickBar.TicksProperty.AddOwner<Slider>();
// Slider required parts
private bool _isDragging = false;
private Track _track;
@ -83,7 +90,8 @@ namespace Avalonia.Controls
PressedMixin.Attach<Slider>();
OrientationProperty.OverrideDefaultValue(typeof(Slider), Orientation.Horizontal);
Thumb.DragStartedEvent.AddClassHandler<Slider>((x, e) => x.OnThumbDragStarted(e), RoutingStrategies.Bubble);
Thumb.DragCompletedEvent.AddClassHandler<Slider>((x, e) => x.OnThumbDragCompleted(e), RoutingStrategies.Bubble);
Thumb.DragCompletedEvent.AddClassHandler<Slider>((x, e) => x.OnThumbDragCompleted(e),
RoutingStrategies.Bubble);
}
/// <summary>
@ -94,6 +102,15 @@ namespace Avalonia.Controls
UpdatePseudoClasses(Orientation);
}
/// <summary>
/// Defines the ticks to be drawn on the tick bar.
/// </summary>
public AvaloniaList<double> Ticks
{
get => GetValue(TicksProperty);
set => SetValue(TicksProperty, value);
}
/// <summary>
/// Gets or sets the orientation of a <see cref="Slider"/>.
/// </summary>
@ -240,19 +257,50 @@ namespace Avalonia.Controls
/// <param name="value">Value that want to snap to closest Tick.</param>
private double SnapToTick(double value)
{
var previous = Minimum;
var next = Maximum;
if (TickFrequency > 0.0)
if (IsSnapToTickEnabled)
{
previous = Minimum + (Math.Round((value - Minimum) / TickFrequency) * TickFrequency);
next = Math.Min(Maximum, previous + TickFrequency);
double previous = Minimum;
double next = Maximum;
// This property is rarely set so let's try to avoid the GetValue
var ticks = Ticks;
// If ticks collection is available, use it.
// Note that ticks may be unsorted.
if ((ticks != null) && (ticks.Count > 0))
{
for (int i = 0; i < ticks.Count; i++)
{
double tick = ticks[i];
if (MathUtilities.AreClose(tick, value))
{
return value;
}
if (MathUtilities.LessThan(tick, value) && MathUtilities.GreaterThan(tick, previous))
{
previous = tick;
}
else if (MathUtilities.GreaterThan(tick, value) && MathUtilities.LessThan(tick, next))
{
next = tick;
}
}
}
else if (MathUtilities.GreaterThan(TickFrequency, 0.0))
{
previous = Minimum + (Math.Round(((value - Minimum) / TickFrequency)) * TickFrequency);
next = Math.Min(Maximum, previous + TickFrequency);
}
// Choose the closest value between previous and next. If tie, snap to 'next'.
value = MathUtilities.GreaterThanOrClose(value, (previous + next) * 0.5) ? next : previous;
}
// Choose the closest value between previous and next. If tie, snap to 'next'.
return MathUtilities.GreaterThanOrClose(value, (previous + next) * 0.5) ? next : previous;
return value;
}
private void UpdatePseudoClasses(Orientation o)
{
PseudoClasses.Set(":vertical", o == Orientation.Vertical);

12
src/Avalonia.Controls/TickBar.cs

@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
using Avalonia.Data.Converters;
using Avalonia.Collections;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Utilities;
@ -135,15 +131,15 @@ namespace Avalonia.Controls
/// <summary>
/// Defines the <see cref="Ticks"/> property.
/// </summary>
public static readonly StyledProperty<List<double>> TicksProperty =
AvaloniaProperty.Register<TickBar, List<double>>(nameof(Ticks));
public static readonly StyledProperty<AvaloniaList<double>> TicksProperty =
AvaloniaProperty.Register<TickBar, AvaloniaList<double>>(nameof(Ticks));
/// <summary>
/// The Ticks property contains collection of value of type Double which
/// are the logical positions use to draw the ticks.
/// The property value is a <see cref="DoubleCollection" />.
/// </summary>
public List<double> Ticks
public AvaloniaList<double> Ticks
{
get { return GetValue(TicksProperty); }
set { SetValue(TicksProperty, value); }

4
src/Avalonia.Input/Navigation/TabNavigation.cs

@ -219,7 +219,9 @@ namespace Avalonia.Input.Navigation
if (parent != null)
{
if (direction == NavigationDirection.Previous && parent.CanFocus())
if (direction == NavigationDirection.Previous &&
parent.CanFocus() &&
KeyboardNavigation.GetIsTabStop((InputElement) parent))
{
return parent;
}

5
src/Avalonia.Styling/IStyledElement.cs

@ -17,11 +17,6 @@ namespace Avalonia
/// </summary>
event EventHandler Initialized;
/// <summary>
/// Raised when resources on the element are changed.
/// </summary>
event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
/// <summary>
/// Gets a value that indicates whether the element has finished initialization.
/// </summary>

1
src/Avalonia.Styling/StyledElement.cs

@ -67,7 +67,6 @@ namespace Avalonia
private List<IStyleInstance>? _appliedStyles;
private ITemplatedControl? _templatedParent;
private bool _dataContextUpdating;
private bool _notifyingResourcesChanged;
/// <summary>
/// Initializes static members of the <see cref="StyledElement"/> class.

1
src/Avalonia.Styling/Styling/Styles.cs

@ -21,7 +21,6 @@ namespace Avalonia.Styling
private IResourceHost? _owner;
private IResourceDictionary? _resources;
private Dictionary<Type, List<IStyle>?>? _cache;
private bool _notifyingResourcesChanged;
public Styles()
{

9
src/Avalonia.Themes.Default/Slider.xaml

@ -87,7 +87,10 @@
</ControlTemplate>
</Setter>
</Style>
<Style Selector="Slider:disabled /template/ Grid#grid">
<Setter Property="Opacity" Value="{DynamicResource ThemeDisabledOpacity}" />
</Style>
<Style Selector="Slider /template/ TickBar">
<Setter Property="Ticks" Value="{TemplateBinding Ticks}" />
</Style>
<Style Selector="Slider:disabled /template/ Grid#grid">
<Setter Property="Opacity" Value="{DynamicResource ThemeDisabledOpacity}" />
</Style>
</Styles>

1
src/Avalonia.Themes.Fluent/Slider.xaml

@ -182,6 +182,7 @@
<Style Selector="Slider /template/ TickBar">
<Setter Property="IsVisible" Value="False" />
<Setter Property="Ticks" Value="{TemplateBinding Ticks}" />
</Style>
<!-- TickBar Placement States -->

4
src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs

@ -154,7 +154,7 @@ namespace Avalonia.Markup.Parsers
private static (State, ISyntax) ParseColon(ref CharacterReader r)
{
var identifier = r.ParseIdentifier();
var identifier = r.ParseStyleClass();
if (identifier.IsEmpty)
{
@ -214,7 +214,7 @@ namespace Avalonia.Markup.Parsers
private static (State, ISyntax) ParseClass(ref CharacterReader r)
{
var @class = r.ParseIdentifier();
var @class = r.ParseStyleClass();
if (@class.IsEmpty)
{
throw new ExpressionParseException(r.Position, $"Expected a class name after '.'.");

4
tests/Avalonia.Controls.UnitTests/DatePickerTests.cs

@ -203,7 +203,9 @@ namespace Avalonia.Controls.UnitTests
}
private static TestServices Services => TestServices.MockThreadingInterface.With(
standardCursorFactory: Mock.Of<IStandardCursorFactory>());
fontManagerImpl: new MockFontManagerImpl(),
standardCursorFactory: Mock.Of<IStandardCursorFactory>(),
textShaperImpl: new MockTextShaperImpl());
private IControlTemplate CreateTemplate()
{

7
tests/Avalonia.Controls.UnitTests/TimePickerTests.cs

@ -12,7 +12,7 @@ namespace Avalonia.Controls.UnitTests
{
public class TimePickerTests
{
[Fact(Skip = "FIX ME ASAP")]
[Fact]
public void SelectedTimeChanged_Should_Fire_When_SelectedTime_Set()
{
using (UnitTestApplication.Start(Services))
@ -98,9 +98,10 @@ namespace Avalonia.Controls.UnitTests
}
}
private static TestServices Services => TestServices.MockThreadingInterface.With(
standardCursorFactory: Mock.Of<IStandardCursorFactory>());
fontManagerImpl: new MockFontManagerImpl(),
standardCursorFactory: Mock.Of<IStandardCursorFactory>(),
textShaperImpl: new MockTextShaperImpl());
private IControlTemplate CreateTemplate()
{

54
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs

@ -382,5 +382,59 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
Assert.Equal(Border.WidthProperty, border.Transitions[0].Property);
}
}
[Fact]
public void Style_Can_Use_Class_Selector_With_Dash()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Window.Styles>
<Style Selector='Border.foo-bar'>
<Setter Property='Background' Value='Red'/>
</Style>
</Window.Styles>
<StackPanel>
<Border Name='foo' Classes='foo-bar'/>
</StackPanel>
</Window>";
var loader = new AvaloniaXamlLoader();
var window = (Window)loader.Load(xaml);
var foo = window.FindControl<Border>("foo");
Assert.Equal(Colors.Red, ((ISolidColorBrush)foo.Background).Color);
}
}
[Fact]
public void Style_Can_Use_Pseudolass_Selector_With_Dash()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Window.Styles>
<Style Selector='Border:foo-bar'>
<Setter Property='Background' Value='Red'/>
</Style>
</Window.Styles>
<StackPanel>
<Border Name='foo'/>
</StackPanel>
</Window>";
var loader = new AvaloniaXamlLoader();
var window = (Window)loader.Load(xaml);
var foo = window.FindControl<Border>("foo");
Assert.Null(foo.Background);
((IPseudoClasses)foo.Classes).Add(":foo-bar");
Assert.Equal(Colors.Red, ((ISolidColorBrush)foo.Background).Color);
}
}
}
}

Loading…
Cancel
Save