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: '' title: ''
labels: bug labels: bug
assignees: '' assignees: ''
--- ---
**Describe the bug** **Describe the bug**
@ -12,6 +11,7 @@ A clear and concise description of what the bug is.
**To Reproduce** **To Reproduce**
Steps to reproduce the behavior: Steps to reproduce the behavior:
1. Go to '...' 1. Go to '...'
2. Click on '....' 2. Click on '....'
3. Scroll down to '....' 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. If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):** **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** **Additional context**
Add any other context about the problem here. 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: '' title: ''
labels: enhancement labels: enhancement
assignees: '' assignees: ''
--- ---
**Is your feature request related to a problem? Please describe.** **Is your feature request related to a problem? Please describe.**

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

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

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

@ -39,17 +39,17 @@ namespace Avalonia.Data
/// <summary> /// <summary>
/// Gets or sets the name of the element to use as the binding source. /// Gets or sets the name of the element to use as the binding source.
/// </summary> /// </summary>
public string ElementName { get; set; } public string? ElementName { get; set; }
/// <summary> /// <summary>
/// Gets or sets the relative source for the binding. /// Gets or sets the relative source for the binding.
/// </summary> /// </summary>
public RelativeSource RelativeSource { get; set; } public RelativeSource? RelativeSource { get; set; }
/// <summary> /// <summary>
/// Gets or sets the source for the binding. /// Gets or sets the source for the binding.
/// </summary> /// </summary>
public object Source { get; set; } public object? Source { get; set; }
/// <summary> /// <summary>
/// Gets or sets the binding path. /// Gets or sets the binding path.
@ -59,24 +59,36 @@ namespace Avalonia.Data
/// <summary> /// <summary>
/// Gets or sets a function used to resolve types from names in the binding path. /// Gets or sets a function used to resolve types from names in the binding path.
/// </summary> /// </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); _ = target ?? throw new ArgumentNullException(nameof(target));
anchor = anchor ?? DefaultAnchor?.Target;
anchor ??= DefaultAnchor?.Target;
enableDataValidation = enableDataValidation && Priority == BindingPriority.LocalValue; enableDataValidation = enableDataValidation && Priority == BindingPriority.LocalValue;
INameScope nameScope = null; INameScope? nameScope = null;
NameScope?.TryGetTarget(out nameScope); NameScope?.TryGetTarget(out nameScope);
var (node, mode) = ExpressionObserverBuilder.Parse(Path, enableDataValidation, TypeResolver, 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) if (ElementName != null)
{ {
return CreateElementObserver( return CreateElementObserver(
(target as IStyledElement) ?? (anchor as IStyledElement), GetSource(),
ElementName, ElementName,
node); node);
} }
@ -96,9 +108,7 @@ namespace Avalonia.Data
} }
else else
{ {
return CreateSourceObserver( return CreateSourceObserver(GetSource(), node);
(target as IStyledElement) ?? (anchor as IStyledElement),
node);
} }
} }
else if (RelativeSource.Mode == RelativeSourceMode.DataContext) else if (RelativeSource.Mode == RelativeSourceMode.DataContext)
@ -111,15 +121,11 @@ namespace Avalonia.Data
} }
else if (RelativeSource.Mode == RelativeSourceMode.Self) else if (RelativeSource.Mode == RelativeSourceMode.Self)
{ {
return CreateSourceObserver( return CreateSourceObserver(GetSource(), node);
(target as IStyledElement) ?? (anchor as IStyledElement),
node);
} }
else if (RelativeSource.Mode == RelativeSourceMode.TemplatedParent) else if (RelativeSource.Mode == RelativeSourceMode.TemplatedParent)
{ {
return CreateTemplatedParentObserver( return CreateTemplatedParentObserver(GetSource(), node);
(target as IStyledElement) ?? (anchor as IStyledElement),
node);
} }
else if (RelativeSource.Mode == RelativeSourceMode.FindAncestor) 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."); throw new InvalidOperationException("AncestorType must be set for RelativeSourceMode.FindAncestor when searching the visual tree.");
} }
return CreateFindAncestorObserver( return CreateFindAncestorObserver(GetSource(), RelativeSource, node);
(target as IStyledElement) ?? (anchor as IStyledElement),
RelativeSource,
node);
} }
else else
{ {

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

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

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

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

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

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

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

@ -6,6 +6,8 @@ using Avalonia.Utilities;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
#nullable enable
namespace Avalonia.Markup.Parsers namespace Avalonia.Markup.Parsers
{ {
internal enum SourceMode internal enum SourceMode
@ -271,8 +273,8 @@ namespace Avalonia.Markup.Parsers
} }
else if (mode.SequenceEqual("parent".AsSpan())) else if (mode.SequenceEqual("parent".AsSpan()))
{ {
string ancestorNamespace = null; string? ancestorNamespace = null;
string ancestorType = null; string? ancestorType = null;
var ancestorLevel = 0; var ancestorLevel = 0;
if (PeekOpenBracket(ref r)) if (PeekOpenBracket(ref r))
{ {
@ -424,19 +426,19 @@ namespace Avalonia.Markup.Parsers
public class PropertyNameNode : INode public class PropertyNameNode : INode
{ {
public string PropertyName { get; set; } public string PropertyName { get; set; } = string.Empty;
} }
public class AttachedPropertyNameNode : INode public class AttachedPropertyNameNode : INode
{ {
public string Namespace { get; set; } public string Namespace { get; set; } = string.Empty;
public string TypeName { get; set; } public string TypeName { get; set; } = string.Empty;
public string PropertyName { get; set; } public string PropertyName { get; set; } = string.Empty;
} }
public class IndexerNode : INode public class IndexerNode : INode
{ {
public IList<string> Arguments { get; set; } public IList<string> Arguments { get; set; } = Array.Empty<string>();
} }
public class NotNode : INode, ITransformNode { } public class NotNode : INode, ITransformNode { }
@ -447,20 +449,20 @@ namespace Avalonia.Markup.Parsers
public class NameNode : INode public class NameNode : INode
{ {
public string Name { get; set; } public string Name { get; set; } = string.Empty;
} }
public class AncestorNode : INode public class AncestorNode : INode
{ {
public string Namespace { get; set; } public string? Namespace { get; set; }
public string TypeName { get; set; } public string? TypeName { get; set; }
public int Level { get; set; } public int Level { get; set; }
} }
public class TypeCastNode : INode public class TypeCastNode : INode
{ {
public string Namespace { get; set; } public string Namespace { get; set; } = string.Empty;
public string TypeName { get; set; } 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 public static class ExpressionObserverBuilder
{ {
internal static (ExpressionNode Node, SourceMode Mode) Parse(string expression, bool enableValidation = false, Func<string, string, Type> typeResolver = null, internal static (ExpressionNode? Node, SourceMode Mode) Parse(string expression, bool enableValidation = false, Func<string, string, Type>? typeResolver = null,
INameScope nameScope = null) INameScope? nameScope = null)
{ {
if (string.IsNullOrWhiteSpace(expression)) if (string.IsNullOrWhiteSpace(expression))
{ {
@ -32,8 +32,8 @@ namespace Avalonia.Markup.Parsers
object root, object root,
string expression, string expression,
bool enableDataValidation = false, bool enableDataValidation = false,
string description = null, string? description = null,
Func<string, string, Type> typeResolver = null) Func<string, string, Type>? typeResolver = null)
{ {
return new ExpressionObserver( return new ExpressionObserver(
root, root,
@ -45,10 +45,11 @@ namespace Avalonia.Markup.Parsers
IObservable<object> rootObservable, IObservable<object> rootObservable,
string expression, string expression,
bool enableDataValidation = false, bool enableDataValidation = false,
string description = null, string? description = null,
Func<string, string, Type> typeResolver = null) Func<string, string, Type>? typeResolver = null)
{ {
Contract.Requires<ArgumentNullException>(rootObservable != null); _ = rootObservable ?? throw new ArgumentNullException(nameof(rootObservable));
return new ExpressionObserver( return new ExpressionObserver(
rootObservable, rootObservable,
Parse(expression, enableDataValidation, typeResolver).Node, Parse(expression, enableDataValidation, typeResolver).Node,
@ -61,10 +62,10 @@ namespace Avalonia.Markup.Parsers
string expression, string expression,
IObservable<Unit> update, IObservable<Unit> update,
bool enableDataValidation = false, bool enableDataValidation = false,
string description = null, string? description = null,
Func<string, string, Type> typeResolver = null) Func<string, string, Type>? typeResolver = null)
{ {
Contract.Requires<ArgumentNullException>(rootGetter != null); _ = rootGetter ?? throw new ArgumentNullException(nameof(rootGetter));
return new ExpressionObserver( return new ExpressionObserver(
rootGetter, rootGetter,

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

@ -11,25 +11,25 @@ namespace Avalonia.Markup.Parsers
internal class ExpressionParser internal class ExpressionParser
{ {
private readonly bool _enableValidation; private readonly bool _enableValidation;
private readonly Func<string, string, Type> _typeResolver; private readonly Func<string, string, Type>? _typeResolver;
private readonly INameScope _nameScope; 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; _typeResolver = typeResolver;
_nameScope = nameScope; _nameScope = nameScope;
_enableValidation = enableValidation; _enableValidation = enableValidation;
} }
public (ExpressionNode Node, SourceMode Mode) Parse(ref CharacterReader r) public (ExpressionNode? Node, SourceMode Mode) Parse(ref CharacterReader r)
{ {
ExpressionNode rootNode = null; ExpressionNode? rootNode = null;
ExpressionNode node = null; ExpressionNode? node = null;
var (astNodes, mode) = BindingExpressionGrammar.Parse(ref r); var (astNodes, mode) = BindingExpressionGrammar.Parse(ref r);
foreach (var astNode in astNodes) foreach (var astNode in astNodes)
{ {
ExpressionNode nextNode = null; ExpressionNode? nextNode = null;
switch (astNode) switch (astNode)
{ {
case BindingExpressionGrammar.EmptyExpressionNode _: case BindingExpressionGrammar.EmptyExpressionNode _:
@ -57,13 +57,13 @@ namespace Avalonia.Markup.Parsers
nextNode = ParseFindAncestor(ancestor); nextNode = ParseFindAncestor(ancestor);
break; break;
case BindingExpressionGrammar.NameNode elementName: 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; break;
case BindingExpressionGrammar.TypeCastNode typeCast: case BindingExpressionGrammar.TypeCastNode typeCast:
nextNode = ParseTypeCastNode(typeCast); nextNode = ParseTypeCastNode(typeCast);
break; break;
} }
if (rootNode is null) if (node is null)
{ {
rootNode = node = nextNode; rootNode = node = nextNode;
} }
@ -79,7 +79,7 @@ namespace Avalonia.Markup.Parsers
private FindAncestorNode ParseFindAncestor(BindingExpressionGrammar.AncestorNode node) private FindAncestorNode ParseFindAncestor(BindingExpressionGrammar.AncestorNode node)
{ {
Type ancestorType = null; Type? ancestorType = null;
var ancestorLevel = node.Level; var ancestorLevel = node.Level;
if (!(node.Namespace is null) && !(node.TypeName is null)) if (!(node.Namespace is null) && !(node.TypeName is null))
@ -97,7 +97,7 @@ namespace Avalonia.Markup.Parsers
private TypeCastNode ParseTypeCastNode(BindingExpressionGrammar.TypeCastNode node) private TypeCastNode ParseTypeCastNode(BindingExpressionGrammar.TypeCastNode node)
{ {
Type castType = null; Type? castType = null;
if (!(node.Namespace is null) && !(node.TypeName is null)) if (!(node.Namespace is null) && !(node.TypeName is null))
{ {
if (_typeResolver == 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 WeakReference<INameScope> _nameScope;
private readonly string _name; private readonly string _name;
private IDisposable _subscription; private IDisposable? _subscription;
public ElementNameNode(INameScope nameScope, string name) 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 public class FindAncestorNode : ExpressionNode
{ {
private readonly int _level; private readonly int _level;
private readonly Type _ancestorType; private readonly Type? _ancestorType;
private IDisposable _subscription; private IDisposable? _subscription;
public FindAncestorNode(Type ancestorType, int level) public FindAncestorNode(Type? ancestorType, int level)
{ {
_level = level; _level = level;
_ancestorType = ancestorType; _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 list = target as IList;
var dictionary = target as IDictionary; var dictionary = target as IDictionary;
var indexerProperty = GetIndexer(typeInfo); 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]; var convertedObjectArray = new object[indexerParameters.Length];
for (int i = 0; i < Arguments.Count; i++) 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)) 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 IList<string> Arguments { get; }
public override Type PropertyType public override Type? PropertyType
{ {
get get
{ {
@ -144,15 +144,15 @@ namespace Avalonia.Markup.Parsers.Nodes
var list = target as IList; var list = target as IList;
var dictionary = target as IDictionary; var dictionary = target as IDictionary;
var indexerProperty = GetIndexer(typeInfo); 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]; var convertedObjectArray = new object[indexerParameters.Length];
for (int i = 0; i < Arguments.Count; i++) 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)) if (!TypeUtilities.TryConvert(indexerParameters[i].ParameterType, Arguments[i], CultureInfo.InvariantCulture, out temp))
{ {
@ -246,7 +246,7 @@ namespace Avalonia.Markup.Parsers.Nodes
return true; return true;
} }
private static PropertyInfo GetIndexer(TypeInfo typeInfo) private static PropertyInfo? GetIndexer(TypeInfo? typeInfo)
{ {
PropertyInfo indexer; 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.Data.Core;
using Avalonia.Utilities; using Avalonia.Utilities;
#nullable enable
namespace Avalonia.Markup.Parsers namespace Avalonia.Markup.Parsers
{ {
#if !BUILDTASK #if !BUILDTASK
@ -30,7 +32,7 @@ namespace Avalonia.Markup.Parsers
var parsed = new List<ISyntax>(); var parsed = new List<ISyntax>();
while (state != State.End) while (state != State.End)
{ {
ISyntax syntax = null; ISyntax? syntax = null;
if (state == State.Start) if (state == State.Start)
(state, syntax) = ParseStart(ref r); (state, syntax) = ParseStart(ref r);
else if (state == State.Next) else if (state == State.Next)
@ -53,7 +55,7 @@ namespace Avalonia.Markup.Parsers
return parsed; return parsed;
} }
private static (State, ISyntax) ParseNext(ref CharacterReader r) private static (State, ISyntax?) ParseNext(ref CharacterReader r)
{ {
r.SkipWhitespace(); r.SkipWhitespace();
if (r.End) 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(); var ident = r.ParseIdentifier();
if (ident.IsEmpty) if (ident.IsEmpty)
@ -147,7 +149,7 @@ namespace Avalonia.Markup.Parsers
return true; 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)) if (TryParseCasts(ref r, out var rv))
return rv; return rv;
@ -184,20 +186,20 @@ namespace Avalonia.Markup.Parsers
public class PropertySyntax : ISyntax 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 => obj is PropertySyntax other
&& other.Name == Name; && other.Name == Name;
} }
public class TypeQualifiedPropertySyntax : ISyntax public class TypeQualifiedPropertySyntax : ISyntax
{ {
public string Name { get; set; } public string Name { get; set; } = string.Empty;
public string TypeName { get; set; } public string TypeName { get; set; } = string.Empty;
public string TypeNamespace { get; set; } public string? TypeNamespace { get; set; }
public override bool Equals(object obj) public override bool Equals(object? obj)
=> obj is TypeQualifiedPropertySyntax other => obj is TypeQualifiedPropertySyntax other
&& other.Name == Name && other.Name == Name
&& other.TypeName == TypeName && other.TypeName == TypeName
@ -207,14 +209,14 @@ namespace Avalonia.Markup.Parsers
public class ChildTraversalSyntax : ISyntax public class ChildTraversalSyntax : ISyntax
{ {
public static ChildTraversalSyntax Instance { get; } = new ChildTraversalSyntax(); 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 class EnsureTypeSyntax : ISyntax
{ {
public string TypeName { get; set; } public string TypeName { get; set; } = string.Empty;
public string TypeNamespace { get; set; } public string? TypeNamespace { get; set; }
public override bool Equals(object obj) public override bool Equals(object? obj)
=> obj is EnsureTypeSyntax other => obj is EnsureTypeSyntax other
&& other.TypeName == TypeName && other.TypeName == TypeName
&& other.TypeNamespace == TypeNamespace; && other.TypeNamespace == TypeNamespace;
@ -222,9 +224,9 @@ namespace Avalonia.Markup.Parsers
public class CastTypeSyntax : ISyntax public class CastTypeSyntax : ISyntax
{ {
public string TypeName { get; set; } public string TypeName { get; set; } = string.Empty;
public string TypeNamespace { get; set; } public string? TypeNamespace { get; set; }
public override bool Equals(object obj) public override bool Equals(object? obj)
=> obj is CastTypeSyntax other => obj is CastTypeSyntax other
&& other.TypeName == TypeName && other.TypeName == TypeName
&& other.TypeNamespace == TypeNamespace; && 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.Data.Core;
using Avalonia.Utilities; using Avalonia.Utilities;
#nullable enable
// Don't need to override GetHashCode as the ISyntax objects will not be stored in a hash; the // 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. // only reason they have overridden Equals methods is for unit testing.
#pragma warning disable 659 #pragma warning disable 659
@ -39,7 +41,7 @@ namespace Avalonia.Markup.Parsers
var selector = new List<ISyntax>(); var selector = new List<ISyntax>();
while (!r.End && state != State.End) while (!r.End && state != State.End)
{ {
ISyntax syntax = null; ISyntax? syntax = null;
switch (state) switch (state)
{ {
case State.Start: case State.Start:
@ -110,7 +112,7 @@ namespace Avalonia.Markup.Parsers
return State.TypeName; 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(':')) 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(); r.SkipWhitespace();
if (r.TakeIf('>')) if (r.TakeIf('>'))
@ -325,11 +327,11 @@ namespace Avalonia.Markup.Parsers
public class OfTypeSyntax : ISyntax, ITypeSyntax 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 string Xmlns { get; set; } = string.Empty;
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
var other = obj as OfTypeSyntax; var other = obj as OfTypeSyntax;
return other != null && other.TypeName == TypeName && other.Xmlns == Xmlns; return other != null && other.TypeName == TypeName && other.Xmlns == Xmlns;
@ -338,11 +340,11 @@ namespace Avalonia.Markup.Parsers
public class IsSyntax : ISyntax, ITypeSyntax 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 string Xmlns { get; set; } = string.Empty;
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
var other = obj as IsSyntax; var other = obj as IsSyntax;
return other != null && other.TypeName == TypeName && other.Xmlns == Xmlns; return other != null && other.TypeName == TypeName && other.Xmlns == Xmlns;
@ -351,9 +353,9 @@ namespace Avalonia.Markup.Parsers
public class ClassSyntax : ISyntax 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; return obj is ClassSyntax && ((ClassSyntax)obj).Class == Class;
} }
@ -361,9 +363,9 @@ namespace Avalonia.Markup.Parsers
public class NameSyntax : ISyntax 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; return obj is NameSyntax && ((NameSyntax)obj).Name == Name;
} }
@ -371,11 +373,11 @@ namespace Avalonia.Markup.Parsers
public class PropertySyntax : ISyntax 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 && return obj is PropertySyntax &&
((PropertySyntax)obj).Property == Property && ((PropertySyntax)obj).Property == Property &&
@ -385,7 +387,7 @@ namespace Avalonia.Markup.Parsers
public class ChildSyntax : ISyntax public class ChildSyntax : ISyntax
{ {
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
return obj is ChildSyntax; return obj is ChildSyntax;
} }
@ -393,7 +395,7 @@ namespace Avalonia.Markup.Parsers
public class DescendantSyntax : ISyntax public class DescendantSyntax : ISyntax
{ {
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
return obj is DescendantSyntax; return obj is DescendantSyntax;
} }
@ -401,7 +403,7 @@ namespace Avalonia.Markup.Parsers
public class TemplateSyntax : ISyntax public class TemplateSyntax : ISyntax
{ {
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
return obj is TemplateSyntax; return obj is TemplateSyntax;
} }
@ -409,9 +411,9 @@ namespace Avalonia.Markup.Parsers
public class NotSyntax : ISyntax 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); return (obj is NotSyntax not) && Argument.SequenceEqual(not.Argument);
} }
@ -419,7 +421,7 @@ namespace Avalonia.Markup.Parsers
public class CommaSyntax : ISyntax public class CommaSyntax : ISyntax
{ {
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
return obj is CommaSyntax or; return obj is CommaSyntax or;
} }

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

@ -31,13 +31,13 @@ namespace Avalonia.Markup.Parsers
/// </summary> /// </summary>
/// <param name="s">The string.</param> /// <param name="s">The string.</param>
/// <returns>The parsed selector.</returns> /// <returns>The parsed selector.</returns>
public Selector Parse(string s) public Selector? Parse(string s)
{ {
var syntax = SelectorGrammar.Parse(s); var syntax = SelectorGrammar.Parse(s);
return Create(syntax); return Create(syntax);
} }
private Selector Create(IEnumerable<SelectorGrammar.ISyntax> syntax) private Selector? Create(IEnumerable<SelectorGrammar.ISyntax> syntax)
{ {
var result = default(Selector); var result = default(Selector);
var results = default(List<Selector>); var results = default(List<Selector>);
@ -110,7 +110,7 @@ namespace Avalonia.Markup.Parsers
results = new List<Selector>(); results = new List<Selector>();
} }
results.Add(result); results.Add(result ?? throw new NotSupportedException("Invalid selector!"));
result = null; result = null;
break; break;
default: 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 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 // only try 2 font weights in each direction
for (var j = 0; j < 200; j += 100) 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. //Nothing was found so we try to get a regular typeface.
return typefaces.Values.FirstOrDefault(); 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 ILayerFactory _layerFactory;
private readonly SharpDX.Direct2D1.RenderTarget _renderTarget; private readonly SharpDX.Direct2D1.RenderTarget _renderTarget;
private readonly DeviceContext _deviceContext; private readonly DeviceContext _deviceContext;
private readonly bool _ownsDeviceContext;
private readonly SharpDX.DXGI.SwapChain1 _swapChain; private readonly SharpDX.DXGI.SwapChain1 _swapChain;
private readonly Action _finishedCallback; private readonly Action _finishedCallback;
@ -51,10 +52,12 @@ namespace Avalonia.Direct2D1.Media
if (_renderTarget is DeviceContext deviceContext) if (_renderTarget is DeviceContext deviceContext)
{ {
_deviceContext = deviceContext; _deviceContext = deviceContext;
_ownsDeviceContext = false;
} }
else else
{ {
_deviceContext = _renderTarget.QueryInterface<DeviceContext>(); _deviceContext = _renderTarget.QueryInterface<DeviceContext>();
_ownsDeviceContext = true;
} }
_deviceContext.BeginDraw(); _deviceContext.BeginDraw();
@ -96,6 +99,13 @@ namespace Avalonia.Direct2D1.Media
{ {
throw new RenderTargetCorruptedException(ex); throw new RenderTargetCorruptedException(ex);
} }
finally
{
if (_ownsDeviceContext)
{
_deviceContext.Dispose();
}
}
} }
/// <summary> /// <summary>
@ -151,7 +161,7 @@ namespace Avalonia.Direct2D1.Media
using (var d2dSource = ((BitmapImpl)source.Item).GetDirect2DBitmap(_deviceContext)) using (var d2dSource = ((BitmapImpl)source.Item).GetDirect2DBitmap(_deviceContext))
using (var sourceBrush = new BitmapBrush(_deviceContext, d2dSource.Value)) using (var sourceBrush = new BitmapBrush(_deviceContext, d2dSource.Value))
using (var d2dOpacityMask = CreateBrush(opacityMask, opacityMaskRect.Size)) 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) if (d2dOpacityMask.PlatformBrush != null)
{ {

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

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

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

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

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

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

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

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

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

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