Browse Source

Merge branch 'master' into fix-menu-item-embed

pull/5451/head
Steven Kirk 5 years ago
committed by GitHub
parent
commit
3d4e248fd3
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      .github/ISSUE_TEMPLATE/bug_report.md
  2. 8
      .github/ISSUE_TEMPLATE/config.yml
  3. 1
      .github/ISSUE_TEMPLATE/feature_request.md
  4. 2
      src/Markup/Avalonia.Markup/Avalonia.Markup.csproj
  5. 49
      src/Markup/Avalonia.Markup/Data/Binding.cs
  6. 49
      src/Markup/Avalonia.Markup/Data/BindingBase.cs
  7. 14
      src/Markup/Avalonia.Markup/Data/MultiBinding.cs
  8. 2
      src/Markup/Avalonia.Markup/Data/RelativeSource.cs
  9. 17
      src/Markup/Avalonia.Markup/Data/TemplateBinding.cs
  10. 26
      src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs
  11. 21
      src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionObserverBuilder.cs
  12. 22
      src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs
  13. 2
      src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/ElementNameNode.cs
  14. 6
      src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/FindAncestorNode.cs
  15. 16
      src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/StringIndexerNode.cs
  16. 36
      src/Markup/Avalonia.Markup/Markup/Parsers/PropertyPathGrammar.cs
  17. 42
      src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs
  18. 6
      src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs
  19. 6
      src/Skia/Avalonia.Skia/SKTypefaceCollection.cs
  20. 12
      src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
  21. 6
      src/Windows/Avalonia.Direct2D1/Media/GeometryImpl.cs
  22. 6
      src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs
  23. 9
      src/Windows/Avalonia.Direct2D1/Media/StreamGeometryImpl.cs
  24. 2
      src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs
  25. 4
      tests/Avalonia.Skia.UnitTests/Media/CustomFontManagerImpl.cs
  26. 28
      tests/Avalonia.Skia.UnitTests/Media/SKTypefaceCollectionCacheTests.cs

7
.github/ISSUE_TEMPLATE/bug_report.md

@ -4,7 +4,6 @@ about: Create a report to help us improve Avalonia
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
@ -12,6 +11,7 @@ A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
@ -24,8 +24,9 @@ A clear and concise description of what you expected to happen.
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. Windows, Mac, Linux (State distribution)]
- Version [e.g. 0.10.0-rc1 or 0.9.12]
- OS: [e.g. Windows, Mac, Linux (State distribution)]
- Version [e.g. 0.10.0-rc1 or 0.9.12]
**Additional context**
Add any other context about the problem here.

8
.github/ISSUE_TEMPLATE/config.yml

@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Questions, Discussions, Ideas
url: https://github.com/AvaloniaUI/Avalonia/discussions/new
about: Please ask and answer questions here.
- name: Avalonia Community Support on Gitter
url: https://gitter.im/AvaloniaUI/Avalonia
about: Please ask and answer questions here.

1
.github/ISSUE_TEMPLATE/feature_request.md

@ -4,7 +4,6 @@ about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**

2
src/Markup/Avalonia.Markup/Avalonia.Markup.csproj

@ -2,6 +2,8 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>Avalonia</RootNamespace>
<Nullable>Enable</Nullable>
<WarningsAsErrors>CS8600;CS8602;CS8603</WarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<None Remove="Markup\Parsers\Nodes\ExpressionGrammer" />

49
src/Markup/Avalonia.Markup/Data/Binding.cs

@ -39,17 +39,17 @@ namespace Avalonia.Data
/// <summary>
/// Gets or sets the name of the element to use as the binding source.
/// </summary>
public string ElementName { get; set; }
public string? ElementName { get; set; }
/// <summary>
/// Gets or sets the relative source for the binding.
/// </summary>
public RelativeSource RelativeSource { get; set; }
public RelativeSource? RelativeSource { get; set; }
/// <summary>
/// Gets or sets the source for the binding.
/// </summary>
public object Source { get; set; }
public object? Source { get; set; }
/// <summary>
/// Gets or sets the binding path.
@ -59,24 +59,36 @@ namespace Avalonia.Data
/// <summary>
/// Gets or sets a function used to resolve types from names in the binding path.
/// </summary>
public Func<string, string, Type> TypeResolver { get; set; }
public Func<string, string, Type>? TypeResolver { get; set; }
protected override ExpressionObserver CreateExpressionObserver(IAvaloniaObject target, AvaloniaProperty targetProperty, object anchor, bool enableDataValidation)
protected override ExpressionObserver CreateExpressionObserver(IAvaloniaObject target, AvaloniaProperty targetProperty, object? anchor, bool enableDataValidation)
{
Contract.Requires<ArgumentNullException>(target != null);
anchor = anchor ?? DefaultAnchor?.Target;
_ = target ?? throw new ArgumentNullException(nameof(target));
anchor ??= DefaultAnchor?.Target;
enableDataValidation = enableDataValidation && Priority == BindingPriority.LocalValue;
INameScope nameScope = null;
INameScope? nameScope = null;
NameScope?.TryGetTarget(out nameScope);
var (node, mode) = ExpressionObserverBuilder.Parse(Path, enableDataValidation, TypeResolver, nameScope);
if (node is null)
{
throw new InvalidOperationException("Could not parse binding expression.");
}
IStyledElement GetSource()
{
return target as IStyledElement ??
anchor as IStyledElement ??
throw new ArgumentException("Could not find binding source: either target or anchor must be an IStyledElement.");
}
if (ElementName != null)
{
return CreateElementObserver(
(target as IStyledElement) ?? (anchor as IStyledElement),
GetSource(),
ElementName,
node);
}
@ -96,9 +108,7 @@ namespace Avalonia.Data
}
else
{
return CreateSourceObserver(
(target as IStyledElement) ?? (anchor as IStyledElement),
node);
return CreateSourceObserver(GetSource(), node);
}
}
else if (RelativeSource.Mode == RelativeSourceMode.DataContext)
@ -111,15 +121,11 @@ namespace Avalonia.Data
}
else if (RelativeSource.Mode == RelativeSourceMode.Self)
{
return CreateSourceObserver(
(target as IStyledElement) ?? (anchor as IStyledElement),
node);
return CreateSourceObserver(GetSource(), node);
}
else if (RelativeSource.Mode == RelativeSourceMode.TemplatedParent)
{
return CreateTemplatedParentObserver(
(target as IStyledElement) ?? (anchor as IStyledElement),
node);
return CreateTemplatedParentObserver(GetSource(), node);
}
else if (RelativeSource.Mode == RelativeSourceMode.FindAncestor)
{
@ -128,10 +134,7 @@ namespace Avalonia.Data
throw new InvalidOperationException("AncestorType must be set for RelativeSourceMode.FindAncestor when searching the visual tree.");
}
return CreateFindAncestorObserver(
(target as IStyledElement) ?? (anchor as IStyledElement),
RelativeSource,
node);
return CreateFindAncestorObserver(GetSource(), RelativeSource, node);
}
else
{

49
src/Markup/Avalonia.Markup/Data/BindingBase.cs

@ -38,22 +38,22 @@ namespace Avalonia.Data
/// <summary>
/// Gets or sets the <see cref="IValueConverter"/> to use.
/// </summary>
public IValueConverter Converter { get; set; }
public IValueConverter? Converter { get; set; }
/// <summary>
/// Gets or sets a parameter to pass to <see cref="Converter"/>.
/// </summary>
public object ConverterParameter { get; set; }
public object? ConverterParameter { get; set; }
/// <summary>
/// Gets or sets the value to use when the binding is unable to produce a value.
/// </summary>
public object FallbackValue { get; set; }
public object? FallbackValue { get; set; }
/// <summary>
/// Gets or sets the value to use when the binding result is null.
/// </summary>
public object TargetNullValue { get; set; }
public object? TargetNullValue { get; set; }
/// <summary>
/// Gets or sets the binding mode.
@ -68,26 +68,27 @@ namespace Avalonia.Data
/// <summary>
/// Gets or sets the string format.
/// </summary>
public string StringFormat { get; set; }
public string? StringFormat { get; set; }
public WeakReference DefaultAnchor { get; set; }
public WeakReference? DefaultAnchor { get; set; }
public WeakReference<INameScope> NameScope { get; set; }
public WeakReference<INameScope>? NameScope { get; set; }
protected abstract ExpressionObserver CreateExpressionObserver(
IAvaloniaObject target,
AvaloniaProperty targetProperty,
object anchor,
object? anchor,
bool enableDataValidation);
/// <inheritdoc/>
public InstancedBinding Initiate(
IAvaloniaObject target,
AvaloniaProperty targetProperty,
object anchor = null,
object? anchor = null,
bool enableDataValidation = false)
{
Contract.Requires<ArgumentNullException>(target != null);
_ = target ?? throw new ArgumentNullException(nameof(target));
anchor = anchor ?? DefaultAnchor?.Target;
enableDataValidation = enableDataValidation && Priority == BindingPriority.LocalValue;
@ -133,18 +134,13 @@ namespace Avalonia.Data
IAvaloniaObject target,
ExpressionNode node,
bool targetIsDataContext,
object anchor)
object? anchor)
{
Contract.Requires<ArgumentNullException>(target != null);
_ = target ?? throw new ArgumentNullException(nameof(target));
if (!(target is IDataContextProvider))
{
target = anchor as IDataContextProvider;
if (target == null)
{
throw new InvalidOperationException("Cannot find a DataContext to bind to.");
}
target = anchor as IDataContextProvider ?? throw new InvalidOperationException("Cannot find a DataContext to bind to.");
}
if (!targetIsDataContext)
@ -171,10 +167,9 @@ namespace Avalonia.Data
string elementName,
ExpressionNode node)
{
Contract.Requires<ArgumentNullException>(target != null);
_ = target ?? throw new ArgumentNullException(nameof(target));
NameScope.TryGetTarget(out var scope);
if (scope == null)
if (NameScope is null || !NameScope.TryGetTarget(out var scope) || scope is null)
throw new InvalidOperationException("Name scope is null or was already collected");
var result = new ExpressionObserver(
NameScopeLocator.Track(scope, elementName),
@ -188,9 +183,9 @@ namespace Avalonia.Data
RelativeSource relativeSource,
ExpressionNode node)
{
Contract.Requires<ArgumentNullException>(target != null);
_ = target ?? throw new ArgumentNullException(nameof(target));
IObservable<object> controlLocator;
IObservable<object?> controlLocator;
switch (relativeSource.Tree)
{
@ -220,7 +215,7 @@ namespace Avalonia.Data
object source,
ExpressionNode node)
{
Contract.Requires<ArgumentNullException>(source != null);
_ = source ?? throw new ArgumentNullException(nameof(source));
return new ExpressionObserver(source, node);
}
@ -229,7 +224,7 @@ namespace Avalonia.Data
IAvaloniaObject target,
ExpressionNode node)
{
Contract.Requires<ArgumentNullException>(target != null);
_ = target ?? throw new ArgumentNullException(nameof(target));
var result = new ExpressionObserver(
() => target.GetValue(StyledElement.TemplatedParentProperty),
@ -240,7 +235,7 @@ namespace Avalonia.Data
return result;
}
protected IObservable<object> GetParentDataContext(IAvaloniaObject target)
protected IObservable<object?> GetParentDataContext(IAvaloniaObject target)
{
// The DataContext is based on the visual parent and not the logical parent: this may
// seem counter intuitive considering the fact that property inheritance works on the logical
@ -252,7 +247,7 @@ namespace Avalonia.Data
.Select(x =>
{
return (x as IAvaloniaObject)?.GetObservable(StyledElement.DataContextProperty) ??
Observable.Return((object)null);
Observable.Return((object?)null);
}).Switch();
}

14
src/Markup/Avalonia.Markup/Data/MultiBinding.cs

@ -22,12 +22,12 @@ namespace Avalonia.Data
/// <summary>
/// Gets or sets the <see cref="IMultiValueConverter"/> to use.
/// </summary>
public IMultiValueConverter Converter { get; set; }
public IMultiValueConverter? Converter { get; set; }
/// <summary>
/// Gets or sets a parameter to pass to <see cref="Converter"/>.
/// </summary>
public object ConverterParameter { get; set; }
public object? ConverterParameter { get; set; }
/// <summary>
/// Gets or sets the value to use when the binding is unable to produce a value.
@ -52,12 +52,12 @@ namespace Avalonia.Data
/// <summary>
/// Gets or sets the relative source for the binding.
/// </summary>
public RelativeSource RelativeSource { get; set; }
public RelativeSource? RelativeSource { get; set; }
/// <summary>
/// Gets or sets the string format.
/// </summary>
public string StringFormat { get; set; }
public string? StringFormat { get; set; }
public MultiBinding()
{
@ -69,7 +69,7 @@ namespace Avalonia.Data
public InstancedBinding Initiate(
IAvaloniaObject target,
AvaloniaProperty targetProperty,
object anchor = null,
object? anchor = null,
bool enableDataValidation = false)
{
var targetType = targetProperty?.PropertyType ?? typeof(object);
@ -105,7 +105,7 @@ namespace Avalonia.Data
}
}
private object ConvertValue(IList<object> values, Type targetType, IMultiValueConverter converter)
private object ConvertValue(IList<object?> values, Type targetType, IMultiValueConverter? converter)
{
for (var i = 0; i < values.Count; ++i)
{
@ -116,7 +116,7 @@ namespace Avalonia.Data
}
var culture = CultureInfo.CurrentCulture;
values = new System.Collections.ObjectModel.ReadOnlyCollection<object>(values);
values = new System.Collections.ObjectModel.ReadOnlyCollection<object?>(values);
object converted;
if (converter != null)
{

2
src/Markup/Avalonia.Markup/Data/RelativeSource.cs

@ -94,7 +94,7 @@ namespace Avalonia.Data
/// <summary>
/// Gets the type of ancestor to look for when in <see cref="RelativeSourceMode.FindAncestor"/> mode.
/// </summary>
public Type AncestorType { get; set; }
public Type? AncestorType { get; set; }
/// <summary>
/// Gets or sets a value that describes the type of relative source lookup.

17
src/Markup/Avalonia.Markup/Data/TemplateBinding.cs

@ -17,8 +17,8 @@ namespace Avalonia.Data
ISetterValue
{
private bool _isSetterValue;
private IStyledElement _target;
private Type _targetType;
private IStyledElement _target = default!;
private Type? _targetType;
public TemplateBinding()
{
@ -33,7 +33,7 @@ namespace Avalonia.Data
public InstancedBinding Initiate(
IAvaloniaObject target,
AvaloniaProperty targetProperty,
object anchor = null,
object? anchor = null,
bool enableDataValidation = false)
{
// Usually each `TemplateBinding` will only be instantiated once; in this case we can
@ -68,12 +68,12 @@ namespace Avalonia.Data
/// <summary>
/// Gets or sets the <see cref="IValueConverter"/> to use.
/// </summary>
public IValueConverter Converter { get; set; }
public IValueConverter? Converter { get; set; }
/// <summary>
/// Gets or sets a parameter to pass to <see cref="Converter"/>.
/// </summary>
public object ConverterParameter { get; set; }
public object? ConverterParameter { get; set; }
/// <summary>
/// Gets or sets the binding mode.
@ -83,7 +83,7 @@ namespace Avalonia.Data
/// <summary>
/// Gets or sets the name of the source property on the templated parent.
/// </summary>
public AvaloniaProperty Property { get; set; }
public AvaloniaProperty? Property { get; set; }
/// <inheritdoc/>
public string Description => "TemplateBinding: " + Property;
@ -164,10 +164,7 @@ namespace Avalonia.Data
{
if (e.Property == StyledElement.TemplatedParentProperty)
{
var oldValue = (IAvaloniaObject)e.OldValue;
var newValue = (IAvaloniaObject)e.OldValue;
if (oldValue != null)
if (e.OldValue is IAvaloniaObject oldValue)
{
oldValue.PropertyChanged -= TemplatedParentPropertyChanged;
}

26
src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs

@ -6,6 +6,8 @@ using Avalonia.Utilities;
using System;
using System.Collections.Generic;
#nullable enable
namespace Avalonia.Markup.Parsers
{
internal enum SourceMode
@ -271,8 +273,8 @@ namespace Avalonia.Markup.Parsers
}
else if (mode.SequenceEqual("parent".AsSpan()))
{
string ancestorNamespace = null;
string ancestorType = null;
string? ancestorNamespace = null;
string? ancestorType = null;
var ancestorLevel = 0;
if (PeekOpenBracket(ref r))
{
@ -424,19 +426,19 @@ namespace Avalonia.Markup.Parsers
public class PropertyNameNode : INode
{
public string PropertyName { get; set; }
public string PropertyName { get; set; } = string.Empty;
}
public class AttachedPropertyNameNode : INode
{
public string Namespace { get; set; }
public string TypeName { get; set; }
public string PropertyName { get; set; }
public string Namespace { get; set; } = string.Empty;
public string TypeName { get; set; } = string.Empty;
public string PropertyName { get; set; } = string.Empty;
}
public class IndexerNode : INode
{
public IList<string> Arguments { get; set; }
public IList<string> Arguments { get; set; } = Array.Empty<string>();
}
public class NotNode : INode, ITransformNode { }
@ -447,20 +449,20 @@ namespace Avalonia.Markup.Parsers
public class NameNode : INode
{
public string Name { get; set; }
public string Name { get; set; } = string.Empty;
}
public class AncestorNode : INode
{
public string Namespace { get; set; }
public string TypeName { get; set; }
public string? Namespace { get; set; }
public string? TypeName { get; set; }
public int Level { get; set; }
}
public class TypeCastNode : INode
{
public string Namespace { get; set; }
public string TypeName { get; set; }
public string Namespace { get; set; } = string.Empty;
public string TypeName { get; set; } = string.Empty;
}
}
}

21
src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionObserverBuilder.cs

@ -8,8 +8,8 @@ namespace Avalonia.Markup.Parsers
{
public static class ExpressionObserverBuilder
{
internal static (ExpressionNode Node, SourceMode Mode) Parse(string expression, bool enableValidation = false, Func<string, string, Type> typeResolver = null,
INameScope nameScope = null)
internal static (ExpressionNode? Node, SourceMode Mode) Parse(string expression, bool enableValidation = false, Func<string, string, Type>? typeResolver = null,
INameScope? nameScope = null)
{
if (string.IsNullOrWhiteSpace(expression))
{
@ -32,8 +32,8 @@ namespace Avalonia.Markup.Parsers
object root,
string expression,
bool enableDataValidation = false,
string description = null,
Func<string, string, Type> typeResolver = null)
string? description = null,
Func<string, string, Type>? typeResolver = null)
{
return new ExpressionObserver(
root,
@ -45,10 +45,11 @@ namespace Avalonia.Markup.Parsers
IObservable<object> rootObservable,
string expression,
bool enableDataValidation = false,
string description = null,
Func<string, string, Type> typeResolver = null)
string? description = null,
Func<string, string, Type>? typeResolver = null)
{
Contract.Requires<ArgumentNullException>(rootObservable != null);
_ = rootObservable ?? throw new ArgumentNullException(nameof(rootObservable));
return new ExpressionObserver(
rootObservable,
Parse(expression, enableDataValidation, typeResolver).Node,
@ -61,10 +62,10 @@ namespace Avalonia.Markup.Parsers
string expression,
IObservable<Unit> update,
bool enableDataValidation = false,
string description = null,
Func<string, string, Type> typeResolver = null)
string? description = null,
Func<string, string, Type>? typeResolver = null)
{
Contract.Requires<ArgumentNullException>(rootGetter != null);
_ = rootGetter ?? throw new ArgumentNullException(nameof(rootGetter));
return new ExpressionObserver(
rootGetter,

22
src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs

@ -11,25 +11,25 @@ namespace Avalonia.Markup.Parsers
internal class ExpressionParser
{
private readonly bool _enableValidation;
private readonly Func<string, string, Type> _typeResolver;
private readonly INameScope _nameScope;
private readonly Func<string, string, Type>? _typeResolver;
private readonly INameScope? _nameScope;
public ExpressionParser(bool enableValidation, Func<string, string, Type> typeResolver, INameScope nameScope)
public ExpressionParser(bool enableValidation, Func<string, string, Type>? typeResolver, INameScope? nameScope)
{
_typeResolver = typeResolver;
_nameScope = nameScope;
_enableValidation = enableValidation;
}
public (ExpressionNode Node, SourceMode Mode) Parse(ref CharacterReader r)
public (ExpressionNode? Node, SourceMode Mode) Parse(ref CharacterReader r)
{
ExpressionNode rootNode = null;
ExpressionNode node = null;
ExpressionNode? rootNode = null;
ExpressionNode? node = null;
var (astNodes, mode) = BindingExpressionGrammar.Parse(ref r);
foreach (var astNode in astNodes)
{
ExpressionNode nextNode = null;
ExpressionNode? nextNode = null;
switch (astNode)
{
case BindingExpressionGrammar.EmptyExpressionNode _:
@ -57,13 +57,13 @@ namespace Avalonia.Markup.Parsers
nextNode = ParseFindAncestor(ancestor);
break;
case BindingExpressionGrammar.NameNode elementName:
nextNode = new ElementNameNode(_nameScope, elementName.Name);
nextNode = new ElementNameNode(_nameScope ?? throw new NotSupportedException("Invalid element name binding with null name scope!"), elementName.Name);
break;
case BindingExpressionGrammar.TypeCastNode typeCast:
nextNode = ParseTypeCastNode(typeCast);
break;
}
if (rootNode is null)
if (node is null)
{
rootNode = node = nextNode;
}
@ -79,7 +79,7 @@ namespace Avalonia.Markup.Parsers
private FindAncestorNode ParseFindAncestor(BindingExpressionGrammar.AncestorNode node)
{
Type ancestorType = null;
Type? ancestorType = null;
var ancestorLevel = node.Level;
if (!(node.Namespace is null) && !(node.TypeName is null))
@ -97,7 +97,7 @@ namespace Avalonia.Markup.Parsers
private TypeCastNode ParseTypeCastNode(BindingExpressionGrammar.TypeCastNode node)
{
Type castType = null;
Type? castType = null;
if (!(node.Namespace is null) && !(node.TypeName is null))
{
if (_typeResolver == null)

2
src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/ElementNameNode.cs

@ -9,7 +9,7 @@ namespace Avalonia.Markup.Parsers.Nodes
{
private readonly WeakReference<INameScope> _nameScope;
private readonly string _name;
private IDisposable _subscription;
private IDisposable? _subscription;
public ElementNameNode(INameScope nameScope, string name)
{

6
src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/FindAncestorNode.cs

@ -7,10 +7,10 @@ namespace Avalonia.Markup.Parsers.Nodes
public class FindAncestorNode : ExpressionNode
{
private readonly int _level;
private readonly Type _ancestorType;
private IDisposable _subscription;
private readonly Type? _ancestorType;
private IDisposable? _subscription;
public FindAncestorNode(Type ancestorType, int level)
public FindAncestorNode(Type? ancestorType, int level)
{
_level = level;
_ancestorType = ancestorType;

16
src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/StringIndexerNode.cs

@ -29,15 +29,15 @@ namespace Avalonia.Markup.Parsers.Nodes
var list = target as IList;
var dictionary = target as IDictionary;
var indexerProperty = GetIndexer(typeInfo);
var indexerParameters = indexerProperty?.GetIndexParameters();
ParameterInfo[] indexerParameters;
if (indexerProperty != null && indexerParameters.Length == Arguments.Count)
if (indexerProperty != null && (indexerParameters = indexerProperty.GetIndexParameters()).Length == Arguments.Count)
{
var convertedObjectArray = new object[indexerParameters.Length];
for (int i = 0; i < Arguments.Count; i++)
{
object temp = null;
object? temp = null;
if (!TypeUtilities.TryConvert(indexerParameters[i].ParameterType, Arguments[i], CultureInfo.InvariantCulture, out temp))
{
@ -125,7 +125,7 @@ namespace Avalonia.Markup.Parsers.Nodes
public IList<string> Arguments { get; }
public override Type PropertyType
public override Type? PropertyType
{
get
{
@ -144,15 +144,15 @@ namespace Avalonia.Markup.Parsers.Nodes
var list = target as IList;
var dictionary = target as IDictionary;
var indexerProperty = GetIndexer(typeInfo);
var indexerParameters = indexerProperty?.GetIndexParameters();
ParameterInfo[] indexerParameters;
if (indexerProperty != null && indexerParameters.Length == Arguments.Count)
if (indexerProperty != null && (indexerParameters = indexerProperty.GetIndexParameters()).Length == Arguments.Count)
{
var convertedObjectArray = new object[indexerParameters.Length];
for (int i = 0; i < Arguments.Count; i++)
{
object temp = null;
object? temp = null;
if (!TypeUtilities.TryConvert(indexerParameters[i].ParameterType, Arguments[i], CultureInfo.InvariantCulture, out temp))
{
@ -246,7 +246,7 @@ namespace Avalonia.Markup.Parsers.Nodes
return true;
}
private static PropertyInfo GetIndexer(TypeInfo typeInfo)
private static PropertyInfo? GetIndexer(TypeInfo? typeInfo)
{
PropertyInfo indexer;

36
src/Markup/Avalonia.Markup/Markup/Parsers/PropertyPathGrammar.cs

@ -3,6 +3,8 @@ using System.Collections.Generic;
using Avalonia.Data.Core;
using Avalonia.Utilities;
#nullable enable
namespace Avalonia.Markup.Parsers
{
#if !BUILDTASK
@ -30,7 +32,7 @@ namespace Avalonia.Markup.Parsers
var parsed = new List<ISyntax>();
while (state != State.End)
{
ISyntax syntax = null;
ISyntax? syntax = null;
if (state == State.Start)
(state, syntax) = ParseStart(ref r);
else if (state == State.Next)
@ -53,7 +55,7 @@ namespace Avalonia.Markup.Parsers
return parsed;
}
private static (State, ISyntax) ParseNext(ref CharacterReader r)
private static (State, ISyntax?) ParseNext(ref CharacterReader r)
{
r.SkipWhitespace();
if (r.End)
@ -106,7 +108,7 @@ namespace Avalonia.Markup.Parsers
});
}
static (string ns, string name) ParseXamlIdentifier(ref CharacterReader r)
static (string? ns, string name) ParseXamlIdentifier(ref CharacterReader r)
{
var ident = r.ParseIdentifier();
if (ident.IsEmpty)
@ -147,7 +149,7 @@ namespace Avalonia.Markup.Parsers
return true;
}
private static (State, ISyntax) ParseAfterProperty(ref CharacterReader r)
private static (State, ISyntax?) ParseAfterProperty(ref CharacterReader r)
{
if (TryParseCasts(ref r, out var rv))
return rv;
@ -184,20 +186,20 @@ namespace Avalonia.Markup.Parsers
public class PropertySyntax : ISyntax
{
public string Name { get; set; }
public string Name { get; set; } = string.Empty;
public override bool Equals(object obj)
public override bool Equals(object? obj)
=> obj is PropertySyntax other
&& other.Name == Name;
}
public class TypeQualifiedPropertySyntax : ISyntax
{
public string Name { get; set; }
public string TypeName { get; set; }
public string TypeNamespace { get; set; }
public string Name { get; set; } = string.Empty;
public string TypeName { get; set; } = string.Empty;
public string? TypeNamespace { get; set; }
public override bool Equals(object obj)
public override bool Equals(object? obj)
=> obj is TypeQualifiedPropertySyntax other
&& other.Name == Name
&& other.TypeName == TypeName
@ -207,14 +209,14 @@ namespace Avalonia.Markup.Parsers
public class ChildTraversalSyntax : ISyntax
{
public static ChildTraversalSyntax Instance { get; } = new ChildTraversalSyntax();
public override bool Equals(object obj) => obj is ChildTraversalSyntax;
public override bool Equals(object? obj) => obj is ChildTraversalSyntax;
}
public class EnsureTypeSyntax : ISyntax
{
public string TypeName { get; set; }
public string TypeNamespace { get; set; }
public override bool Equals(object obj)
public string TypeName { get; set; } = string.Empty;
public string? TypeNamespace { get; set; }
public override bool Equals(object? obj)
=> obj is EnsureTypeSyntax other
&& other.TypeName == TypeName
&& other.TypeNamespace == TypeNamespace;
@ -222,9 +224,9 @@ namespace Avalonia.Markup.Parsers
public class CastTypeSyntax : ISyntax
{
public string TypeName { get; set; }
public string TypeNamespace { get; set; }
public override bool Equals(object obj)
public string TypeName { get; set; } = string.Empty;
public string? TypeNamespace { get; set; }
public override bool Equals(object? obj)
=> obj is CastTypeSyntax other
&& other.TypeName == TypeName
&& other.TypeNamespace == TypeNamespace;

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

@ -4,6 +4,8 @@ using System.Linq;
using Avalonia.Data.Core;
using Avalonia.Utilities;
#nullable enable
// Don't need to override GetHashCode as the ISyntax objects will not be stored in a hash; the
// only reason they have overridden Equals methods is for unit testing.
#pragma warning disable 659
@ -39,7 +41,7 @@ namespace Avalonia.Markup.Parsers
var selector = new List<ISyntax>();
while (!r.End && state != State.End)
{
ISyntax syntax = null;
ISyntax? syntax = null;
switch (state)
{
case State.Start:
@ -110,7 +112,7 @@ namespace Avalonia.Markup.Parsers
return State.TypeName;
}
private static (State, ISyntax) ParseMiddle(ref CharacterReader r, char? end)
private static (State, ISyntax?) ParseMiddle(ref CharacterReader r, char? end)
{
if (r.TakeIf(':'))
{
@ -190,7 +192,7 @@ namespace Avalonia.Markup.Parsers
}
}
private static (State, ISyntax) ParseTraversal(ref CharacterReader r)
private static (State, ISyntax?) ParseTraversal(ref CharacterReader r)
{
r.SkipWhitespace();
if (r.TakeIf('>'))
@ -325,11 +327,11 @@ namespace Avalonia.Markup.Parsers
public class OfTypeSyntax : ISyntax, ITypeSyntax
{
public string TypeName { get; set; }
public string TypeName { get; set; } = string.Empty;
public string Xmlns { get; set; } = string.Empty;
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
var other = obj as OfTypeSyntax;
return other != null && other.TypeName == TypeName && other.Xmlns == Xmlns;
@ -338,11 +340,11 @@ namespace Avalonia.Markup.Parsers
public class IsSyntax : ISyntax, ITypeSyntax
{
public string TypeName { get; set; }
public string TypeName { get; set; } = string.Empty;
public string Xmlns { get; set; } = string.Empty;
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
var other = obj as IsSyntax;
return other != null && other.TypeName == TypeName && other.Xmlns == Xmlns;
@ -351,9 +353,9 @@ namespace Avalonia.Markup.Parsers
public class ClassSyntax : ISyntax
{
public string Class { get; set; }
public string Class { get; set; } = string.Empty;
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
return obj is ClassSyntax && ((ClassSyntax)obj).Class == Class;
}
@ -361,9 +363,9 @@ namespace Avalonia.Markup.Parsers
public class NameSyntax : ISyntax
{
public string Name { get; set; }
public string Name { get; set; } = string.Empty;
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
return obj is NameSyntax && ((NameSyntax)obj).Name == Name;
}
@ -371,11 +373,11 @@ namespace Avalonia.Markup.Parsers
public class PropertySyntax : ISyntax
{
public string Property { get; set; }
public string Property { get; set; } = string.Empty;
public string Value { get; set; }
public string Value { get; set; } = string.Empty;
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
return obj is PropertySyntax &&
((PropertySyntax)obj).Property == Property &&
@ -385,7 +387,7 @@ namespace Avalonia.Markup.Parsers
public class ChildSyntax : ISyntax
{
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
return obj is ChildSyntax;
}
@ -393,7 +395,7 @@ namespace Avalonia.Markup.Parsers
public class DescendantSyntax : ISyntax
{
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
return obj is DescendantSyntax;
}
@ -401,7 +403,7 @@ namespace Avalonia.Markup.Parsers
public class TemplateSyntax : ISyntax
{
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
return obj is TemplateSyntax;
}
@ -409,9 +411,9 @@ namespace Avalonia.Markup.Parsers
public class NotSyntax : ISyntax
{
public IEnumerable<ISyntax> Argument { get; set; }
public IEnumerable<ISyntax> Argument { get; set; } = Enumerable.Empty<ISyntax>();
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
return (obj is NotSyntax not) && Argument.SequenceEqual(not.Argument);
}
@ -419,7 +421,7 @@ namespace Avalonia.Markup.Parsers
public class CommaSyntax : ISyntax
{
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
return obj is CommaSyntax or;
}

6
src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs

@ -31,13 +31,13 @@ namespace Avalonia.Markup.Parsers
/// </summary>
/// <param name="s">The string.</param>
/// <returns>The parsed selector.</returns>
public Selector Parse(string s)
public Selector? Parse(string s)
{
var syntax = SelectorGrammar.Parse(s);
return Create(syntax);
}
private Selector Create(IEnumerable<SelectorGrammar.ISyntax> syntax)
private Selector? Create(IEnumerable<SelectorGrammar.ISyntax> syntax)
{
var result = default(Selector);
var results = default(List<Selector>);
@ -110,7 +110,7 @@ namespace Avalonia.Markup.Parsers
results = new List<Selector>();
}
results.Add(result);
results.Add(result ?? throw new NotSupportedException("Invalid selector!"));
result = null;
break;
default:

6
src/Skia/Avalonia.Skia/SKTypefaceCollection.cs

@ -32,7 +32,7 @@ namespace Avalonia.Skia
weight -= weight % 100; // make sure we start at a full weight
for (var i = (int)key.Style; i < 2; i++)
for (var i = 0; i < 2; i++)
{
// only try 2 font weights in each direction
for (var j = 0; j < 200; j += 100)
@ -57,8 +57,8 @@ namespace Avalonia.Skia
}
}
//Nothing was found so we use the first typeface we can get.
return typefaces.Values.FirstOrDefault();
//Nothing was found so we try to get a regular typeface.
return typefaces.TryGetValue(new Typeface(key.FontFamily), out typeface) ? typeface : null;
}
}
}

12
src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs

@ -21,6 +21,7 @@ namespace Avalonia.Direct2D1.Media
private readonly ILayerFactory _layerFactory;
private readonly SharpDX.Direct2D1.RenderTarget _renderTarget;
private readonly DeviceContext _deviceContext;
private readonly bool _ownsDeviceContext;
private readonly SharpDX.DXGI.SwapChain1 _swapChain;
private readonly Action _finishedCallback;
@ -51,10 +52,12 @@ namespace Avalonia.Direct2D1.Media
if (_renderTarget is DeviceContext deviceContext)
{
_deviceContext = deviceContext;
_ownsDeviceContext = false;
}
else
{
_deviceContext = _renderTarget.QueryInterface<DeviceContext>();
_ownsDeviceContext = true;
}
_deviceContext.BeginDraw();
@ -96,6 +99,13 @@ namespace Avalonia.Direct2D1.Media
{
throw new RenderTargetCorruptedException(ex);
}
finally
{
if (_ownsDeviceContext)
{
_deviceContext.Dispose();
}
}
}
/// <summary>
@ -151,7 +161,7 @@ namespace Avalonia.Direct2D1.Media
using (var d2dSource = ((BitmapImpl)source.Item).GetDirect2DBitmap(_deviceContext))
using (var sourceBrush = new BitmapBrush(_deviceContext, d2dSource.Value))
using (var d2dOpacityMask = CreateBrush(opacityMask, opacityMaskRect.Size))
using (var geometry = new SharpDX.Direct2D1.RectangleGeometry(_deviceContext.Factory, destRect.ToDirect2D()))
using (var geometry = new SharpDX.Direct2D1.RectangleGeometry(Direct2D1Platform.Direct2D1Factory, destRect.ToDirect2D()))
{
if (d2dOpacityMask.PlatformBrush != null)
{

6
src/Windows/Avalonia.Direct2D1/Media/GeometryImpl.cs

@ -33,13 +33,13 @@ namespace Avalonia.Direct2D1.Media
/// <inheritdoc/>
public IGeometryImpl Intersect(IGeometryImpl geometry)
{
var result = new PathGeometry(Geometry.Factory);
var result = new PathGeometry(Direct2D1Platform.Direct2D1Factory);
using (var sink = result.Open())
{
Geometry.Combine(((GeometryImpl)geometry).Geometry, CombineMode.Intersect, sink);
return new StreamGeometryImpl(result);
sink.Close();
}
return new StreamGeometryImpl(result);
}
/// <inheritdoc/>

6
src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs

@ -13,7 +13,7 @@ namespace Avalonia.Direct2D1.Media
/// </summary>
public class WicBitmapImpl : BitmapImpl
{
private BitmapDecoder _decoder;
private readonly BitmapDecoder _decoder;
private static BitmapInterpolationMode ConvertInterpolationMode(Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode interpolationMode)
{
@ -41,7 +41,7 @@ namespace Avalonia.Direct2D1.Media
/// <param name="fileName">The filename of the bitmap to load.</param>
public WicBitmapImpl(string fileName)
{
using (BitmapDecoder decoder = new BitmapDecoder(Direct2D1Platform.ImagingFactory, fileName, DecodeOptions.CacheOnDemand))
using (var decoder = new BitmapDecoder(Direct2D1Platform.ImagingFactory, fileName, DecodeOptions.CacheOnDemand))
{
WicImpl = new Bitmap(Direct2D1Platform.ImagingFactory, decoder.GetFrame(0), BitmapCreateCacheOption.CacheOnDemand);
Dpi = new Vector(96, 96);
@ -177,7 +177,7 @@ namespace Avalonia.Direct2D1.Media
/// <returns>The Direct2D bitmap.</returns>
public override OptionalDispose<D2DBitmap> GetDirect2DBitmap(SharpDX.Direct2D1.RenderTarget renderTarget)
{
FormatConverter converter = new FormatConverter(Direct2D1Platform.ImagingFactory);
using var converter = new FormatConverter(Direct2D1Platform.ImagingFactory);
converter.Initialize(WicImpl, SharpDX.WIC.PixelFormat.Format32bppPBGRA);
return new OptionalDispose<D2DBitmap>(D2DBitmap.FromWicBitmap(renderTarget, converter), true);
}

9
src/Windows/Avalonia.Direct2D1/Media/StreamGeometryImpl.cs

@ -29,9 +29,12 @@ namespace Avalonia.Direct2D1.Media
public IStreamGeometryImpl Clone()
{
var result = new PathGeometry(Direct2D1Platform.Direct2D1Factory);
var sink = result.Open();
((PathGeometry)Geometry).Stream(sink);
sink.Close();
using (var sink = result.Open())
{
((PathGeometry)Geometry).Stream(sink);
sink.Close();
}
return new StreamGeometryImpl(result);
}

2
src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs

@ -111,7 +111,7 @@ namespace Avalonia.Direct2D1
/// <returns>The Direct2D brush.</returns>
public static StrokeStyle ToDirect2DStrokeStyle(this Avalonia.Media.IPen pen, SharpDX.Direct2D1.RenderTarget renderTarget)
{
return pen.ToDirect2DStrokeStyle(renderTarget.Factory);
return pen.ToDirect2DStrokeStyle(Direct2D1Platform.Direct2D1Factory);
}
/// <summary>

4
tests/Avalonia.Skia.UnitTests/Media/CustomFontManagerImpl.cs

@ -48,7 +48,7 @@ namespace Avalonia.Skia.UnitTests.Media
continue;
}
typeface = new Typeface(customTypeface.FontFamily.Name, fontStyle, fontWeight);
typeface = new Typeface(customTypeface.FontFamily, fontStyle, fontWeight);
return true;
}
@ -83,7 +83,7 @@ namespace Avalonia.Skia.UnitTests.Media
case "Noto Mono":
{
var typefaceCollection = SKTypefaceCollectionCache.GetOrAddTypefaceCollection(_defaultTypeface.FontFamily);
skTypeface = typefaceCollection.Get(typeface);
skTypeface = typefaceCollection.Get(_defaultTypeface);
break;
}
default:

28
tests/Avalonia.Skia.UnitTests/Media/SKTypefaceCollectionCacheTests.cs

@ -7,25 +7,33 @@ namespace Avalonia.Skia.UnitTests.Media
public class SKTypefaceCollectionCacheTests
{
[Fact]
public void Should_Load_Typefaces_From_Invalid_Name()
public void Should_Get_Near_Matching_Typeface()
{
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
{
var notoMono =
new FontFamily("resm:Avalonia.Skia.UnitTests.Assets?assembly=Avalonia.Skia.UnitTests#Noto Mono");
var colorEmoji =
new FontFamily("resm:Avalonia.Skia.UnitTests.Assets?assembly=Avalonia.Skia.UnitTests#Twitter Color Emoji");
var notoMonoCollection = SKTypefaceCollectionCache.GetOrAddTypefaceCollection(notoMono);
var typeface = new Typeface("ABC", FontStyle.Italic, FontWeight.Bold);
Assert.Equal("Noto Mono", notoMonoCollection.Get(typeface).FamilyName);
var notoColorEmojiCollection = SKTypefaceCollectionCache.GetOrAddTypefaceCollection(colorEmoji);
Assert.Equal("Noto Mono",
notoMonoCollection.Get(new Typeface(notoMono, weight: FontWeight.Bold)).FamilyName);
}
}
[Fact]
public void Should_Get_Null_For_Invalid_FamilyName()
{
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
{
var notoMono =
new FontFamily("resm:Avalonia.Skia.UnitTests.Assets?assembly=Avalonia.Skia.UnitTests#Noto Mono");
var notoMonoCollection = SKTypefaceCollectionCache.GetOrAddTypefaceCollection(notoMono);
Assert.Equal("Twitter Color Emoji", notoColorEmojiCollection.Get(typeface).FamilyName);
var typeface = notoMonoCollection.Get(new Typeface("ABC"));
Assert.Null(typeface);
}
}
}

Loading…
Cancel
Save