From 4870fc0d4d86c8e9c3c2aaa58453042f91eae7b2 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Mon, 22 Nov 2021 12:59:33 +0100 Subject: [PATCH 01/90] feat(Style): Allow to using attached properties in attribute selector --- .../Markup/Parsers/SelectorGrammar.cs | 68 ++++++++++++++++++- .../Markup/Parsers/SelectorParser.cs | 55 +++++++++++++-- 2 files changed, 116 insertions(+), 7 deletions(-) diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs index 953a7e9a15..15197d39c5 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs +++ b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs @@ -25,6 +25,7 @@ namespace Avalonia.Markup.Parsers Traversal, TypeName, Property, + AttachedProperty, Template, End, } @@ -74,6 +75,9 @@ namespace Avalonia.Markup.Parsers case State.Name: (state, syntax) = ParseName(ref r); break; + case State.AttachedProperty: + (state, syntax) = ParseAttachedProperty(ref r); + break; } if (syntax != null) { @@ -270,11 +274,15 @@ namespace Avalonia.Markup.Parsers return (State.CanHaveType, ParseType(ref r, new OfTypeSyntax())); } - private static (State, ISyntax) ParseProperty(ref CharacterReader r) + private static (State, ISyntax?) ParseProperty(ref CharacterReader r) { var property = r.ParseIdentifier(); - if (!r.TakeIf('=')) + if (r.TakeIf('(')) + { + return (State.AttachedProperty, default); + } + else if (!r.TakeIf('=')) { throw new ExpressionParseException(r.Position, $"Expected '=', got '{r.Peek}'"); } @@ -286,6 +294,42 @@ namespace Avalonia.Markup.Parsers return (State.CanHaveType, new PropertySyntax { Property = property.ToString(), Value = value.ToString() }); } + private static (State, ISyntax) ParseAttachedProperty(ref CharacterReader r) + { + var syntax = ParseType(ref r, new AttachedPropertySyntax()); + if (!r.TakeIf('.')) + { + throw new ExpressionParseException(r.Position, $"Expected '.', got '{r.Peek}'"); + } + var property = r.ParseIdentifier(); + if (property.IsEmpty) + { + throw new ExpressionParseException(r.Position, $"Expected Attached Property Name, got '{r.Peek}'"); + } + syntax.Property = property.ToString(); + + if (!r.TakeIf(')')) + { + throw new ExpressionParseException(r.Position, $"Expected ')', got '{r.Peek}'"); + } + + if (!r.TakeIf('=')) + { + throw new ExpressionParseException(r.Position, $"Expected '=', got '{r.Peek}'"); + } + + var value = r.TakeUntil(']'); + + syntax.Value = value.ToString(); + + r.Take(); + + var state = r.End + ? State.End + : State.Middle; + return (state, syntax); + } + private static TSyntax ParseType(ref CharacterReader r, TSyntax syntax) where TSyntax : ITypeSyntax { @@ -461,6 +505,26 @@ namespace Avalonia.Markup.Parsers } } + public class AttachedPropertySyntax : ISyntax, ITypeSyntax + { + public string Xmlns { get; set; } = string.Empty; + + public string TypeName { get; set; } = string.Empty; + + public string Property { get; set; } = string.Empty; + + public string Value { get; set; } = string.Empty; + + public override bool Equals(object obj) + { + return obj is AttachedPropertySyntax syntax + && syntax.Xmlns == Xmlns + && syntax.TypeName == syntax.TypeName + && syntax.Property == syntax.Property + && syntax.Value == syntax.Value; + } + } + public class IsSyntax : ISyntax, ITypeSyntax { public string TypeName { get; set; } = string.Empty; diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs index 11fb287d46..23bbdb762d 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs +++ b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using Avalonia.Styling; using Avalonia.Utilities; +using System.Linq; namespace Avalonia.Markup.Parsers { @@ -75,23 +76,67 @@ namespace Avalonia.Markup.Parsers throw new InvalidOperationException($"Cannot find '{property.Property}' on '{type}"); } + { + object typedValue; + + if (TypeUtilities.TryConvert( + targetProperty.PropertyType, + property.Value, + CultureInfo.InvariantCulture, + out typedValue)) + { + result = result.PropertyEquals(targetProperty, typedValue); + } + else + { + throw new InvalidOperationException( + $"Could not convert '{property.Value}' to '{targetProperty.PropertyType}"); + } + } + break; + } + case SelectorGrammar.AttachedPropertySyntax attachedProperty: + var targetType = result?.TargetType; + + if (targetType == null) + { + throw new InvalidOperationException("Attached Property selectors must be applied to a type."); + } + + var attachedPropertyOwnerType = Resolve(attachedProperty.Xmlns, attachedProperty.TypeName); + + if (attachedPropertyOwnerType is null) + { + throw new InvalidOperationException($"Cannot find '{attachedProperty.Xmlns}:{attachedProperty.TypeName}"); + } + + var targetAttachedProperty = AvaloniaPropertyRegistry.Instance.GetRegisteredAttached(targetType) + .FirstOrDefault(ap => ap.OwnerType == attachedPropertyOwnerType && ap.Name == attachedProperty.Property); + + if (targetAttachedProperty == null) + { + throw new InvalidOperationException($"Cannot find '{attachedProperty.Property}' on '{attachedPropertyOwnerType}"); + } + + { object typedValue; if (TypeUtilities.TryConvert( - targetProperty.PropertyType, - property.Value, + targetAttachedProperty.PropertyType, + attachedProperty.Value, CultureInfo.InvariantCulture, out typedValue)) { - result = result.PropertyEquals(targetProperty, typedValue); + result = result.PropertyEquals(targetAttachedProperty, typedValue); } else { throw new InvalidOperationException( - $"Could not convert '{property.Value}' to '{targetProperty.PropertyType}"); + $"Could not convert '{attachedProperty.Value}' to '{targetAttachedProperty.PropertyType}"); } - break; } + + break; case SelectorGrammar.ChildSyntax child: result = result.Child(); break; From 3e9c86054b71efafe67c146d59334e099b2792e7 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Mon, 22 Nov 2021 13:00:15 +0100 Subject: [PATCH 02/90] feat(Style): Add test to check using attached properties in attribute selector --- .../Parsers/SelectorGrammarTests.cs | 36 ++++++++++ .../Parsers/SelectorParserTests.cs | 49 ++++++++++++++ .../SelectorTests_PropertyEquals.cs | 67 +++++++++++++++++++ 3 files changed, 152 insertions(+) diff --git a/tests/Avalonia.Markup.UnitTests/Parsers/SelectorGrammarTests.cs b/tests/Avalonia.Markup.UnitTests/Parsers/SelectorGrammarTests.cs index 568f6deaf2..6fbf024ff1 100644 --- a/tests/Avalonia.Markup.UnitTests/Parsers/SelectorGrammarTests.cs +++ b/tests/Avalonia.Markup.UnitTests/Parsers/SelectorGrammarTests.cs @@ -197,6 +197,42 @@ namespace Avalonia.Markup.UnitTests.Parsers result); } + [Fact] + public void OfType_AttachedProperty() + { + var result = SelectorGrammar.Parse("Button[(Grid.Column)=1]"); + + Assert.Equal( + new SelectorGrammar.ISyntax[] + { + new SelectorGrammar.OfTypeSyntax { TypeName = "Button" }, + new SelectorGrammar.AttachedPropertySyntax { + Xmlns = string.Empty, + TypeName="Grid", + Property = "Column", + Value = "1" }, + }, + result); + } + + [Fact] + public void OfType_AttachedProperty_WithNamespace() + { + var result = SelectorGrammar.Parse("Button[(x|Grid.Column)=1]"); + + Assert.Equal( + new SelectorGrammar.ISyntax[] + { + new SelectorGrammar.OfTypeSyntax { TypeName = "Button" }, + new SelectorGrammar.AttachedPropertySyntax { + Xmlns = "x", + TypeName="Grid", + Property = "Column", + Value = "1" }, + }, + result); + } + [Fact] public void Not_OfType() { diff --git a/tests/Avalonia.Markup.UnitTests/Parsers/SelectorParserTests.cs b/tests/Avalonia.Markup.UnitTests/Parsers/SelectorParserTests.cs index 1c0cba56c9..338bdb3ae1 100644 --- a/tests/Avalonia.Markup.UnitTests/Parsers/SelectorParserTests.cs +++ b/tests/Avalonia.Markup.UnitTests/Parsers/SelectorParserTests.cs @@ -7,6 +7,25 @@ namespace Avalonia.Markup.UnitTests.Parsers { public class SelectorParserTests { + static SelectorParserTests() + { + //Ensure the attached properties are registered before run tests + System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(Grid).TypeHandle); + System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(Auth).TypeHandle); + } + + class Auth + { + public readonly static AttachedProperty NameProperty = + AvaloniaProperty.RegisterAttached("Name"); + + public static string GetName(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(NameProperty); + + public static void SetName(AvaloniaObject avaloniaObject, string value) => + avaloniaObject.SetValue(NameProperty, value); + } + [Fact] public void Parses_Boolean_Property_Selector() { @@ -14,6 +33,36 @@ namespace Avalonia.Markup.UnitTests.Parsers var result = target.Parse("TextBlock[IsPointerOver=True]"); } + [Fact] + public void Parses_AttacchedProperty_Selector_With_Namespace() + { + var target = new SelectorParser((ns, type) => + { + return (ns, type) switch + { + ("", nameof(TextBlock)) => typeof(TextBlock), + ("l",nameof(Auth)) => typeof(Auth), + _ => null + }; + }); + var result = target.Parse("TextBlock[(l|Auth.Name)=Admin]"); + } + + [Fact] + public void Parses_AttacchedProperty_Selector() + { + var target = new SelectorParser((ns, type) => + { + return (ns, type) switch + { + ("", nameof(TextBlock)) => typeof(TextBlock), + ("", nameof(Grid)) => typeof(Grid), + _ => null + }; + }); + var result = target.Parse("TextBlock[(Grid.Column)=1]"); + } + [Fact] public void Parses_Comma_Separated_Selectors() { diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_PropertyEquals.cs b/tests/Avalonia.Styling.UnitTests/SelectorTests_PropertyEquals.cs index 7689a458ae..729b42196b 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_PropertyEquals.cs +++ b/tests/Avalonia.Styling.UnitTests/SelectorTests_PropertyEquals.cs @@ -8,6 +8,73 @@ namespace Avalonia.Styling.UnitTests { public class SelectorTests_PropertyEquals { + static SelectorTests_PropertyEquals() + { + //Ensure the attached properties are registered before run tests + System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(Grid).TypeHandle); + System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(Auth).TypeHandle); + } + + class Auth + { + public readonly static AttachedProperty NameProperty = + AvaloniaProperty.RegisterAttached("Name"); + + public static string GetName(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(NameProperty); + + public static void SetName(AvaloniaObject avaloniaObject, string value) => + avaloniaObject.SetValue(NameProperty, value); + } + + [Fact] + public async Task PropertyEquals_Attached_Property_Matching_Value() + { + var target = new Markup.Parsers.SelectorParser((ns, type) => + { + return (ns, type) switch + { + ("", nameof(TextBlock)) => typeof(TextBlock), + ("", nameof(Grid)) => typeof(Grid), + _ => null + }; + }).Parse("TextBlock[(Grid.Column)=1]"); + + + var control = new TextBlock(); + var activator = target.Match(control).Activator.ToObservable(); + + Assert.False(await activator.Take(1)); + Grid.SetColumn(control, 1); + Assert.True(await activator.Take(1)); + Grid.SetColumn(control, 0); + Assert.False(await activator.Take(1)); + } + + [Fact] + public async Task PropertyEquals_Attached_Property_With_Namespace_Matching_Value() + { + var target = new Markup.Parsers.SelectorParser((ns, type) => + { + return (ns, type) switch + { + ("", nameof(TextBlock)) => typeof(TextBlock), + ("l", nameof(Auth)) => typeof(Auth), + _ => null + }; + }).Parse("TextBlock[(l|Auth.Name)=Admin]"); + + + var control = new TextBlock(); + var activator = target.Match(control).Activator.ToObservable(); + + Assert.False(await activator.Take(1)); + Auth.SetName(control, "Admin"); + Assert.True(await activator.Take(1)); + Auth.SetName(control, null); + Assert.False(await activator.Take(1)); + } + [Fact] public async Task PropertyEquals_Matches_When_Property_Has_Matching_Value() { From f59486ebc2a06bf586e84034efca4579c23999ca Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Mon, 22 Nov 2021 16:46:57 +0100 Subject: [PATCH 03/90] fixes(SelectorGrammar): AttachedPropertySyntax method Equal --- .../Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs index 15197d39c5..620477b6cd 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs +++ b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs @@ -519,9 +519,9 @@ namespace Avalonia.Markup.Parsers { return obj is AttachedPropertySyntax syntax && syntax.Xmlns == Xmlns - && syntax.TypeName == syntax.TypeName - && syntax.Property == syntax.Property - && syntax.Value == syntax.Value; + && syntax.TypeName == TypeName + && syntax.Property == Property + && syntax.Value == Value; } } From 0f18e319b0f60473e273c854ff9afae4604b9718 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Tue, 23 Nov 2021 14:50:22 +0100 Subject: [PATCH 04/90] fixes(Style): Missing XamlIl Compilation for SelectorGrammar.AttachedPropertySyntax --- .../AvaloniaXamlIlSelectorTransformer.cs | 73 +++++++++++++++++++ .../AvaloniaXamlIlWellKnownTypes.cs | 2 + 2 files changed, 75 insertions(+) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs index dfabd66d17..bc89513b22 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs @@ -40,6 +40,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers var selectorType = pn.Property.GetClrProperty().Getter.ReturnType; var initialNode = new XamlIlSelectorInitialNode(node, selectorType); + var avaloniaAttachedPropertyT = context.GetAvaloniaTypes().AvaloniaAttachedPropertyT; XamlIlSelectorNode Create(IEnumerable syntax, Func typeResolver) { @@ -85,6 +86,50 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers result = new XamlIlPropertyEqualsSelector(result, targetProperty, typedValue); break; } + case SelectorGrammar.AttachedPropertySyntax: + { + var targetType = result?.TargetType; + if (targetType == null) + { + throw new XamlParseException("Attached Property selectors must be applied to a type.",node); + } + + var attachedProperty= (SelectorGrammar.AttachedPropertySyntax)i; + var attachedPropertyOwnerType = typeResolver(attachedProperty.Xmlns, attachedProperty.TypeName).Type; + + if (attachedPropertyOwnerType is null) + { + throw new XamlParseException($"Cannot find '{attachedProperty.Xmlns}:{attachedProperty.TypeName}",node); + } + + var attachedPropertyName = attachedProperty.Property + "Property"; + + var targetPropertyField = attachedPropertyOwnerType.GetAllFields() + .FirstOrDefault(f => f.IsStatic + && f.IsPublic + && f.Name == attachedPropertyName + && f.FieldType!.GenericTypeDefinition == avaloniaAttachedPropertyT + ); + + if (targetPropertyField is null) + { + throw new XamlParseException($"Cannot find '{attachedProperty.Property}' on '{attachedPropertyOwnerType}", node); + } + + var targetPropertyType = XamlIlAvaloniaPropertyHelper + .GetAvaloniaPropertyType(targetPropertyField, context.GetAvaloniaTypes(), node); + + if (!XamlTransformHelpers.TryGetCorrectlyTypedValue(context, + new XamlAstTextNode(node, attachedProperty.Value, context.Configuration.WellKnownTypes.String), + targetPropertyType, out var typedValue)) + throw new XamlParseException( + $"Cannot convert '{attachedProperty.Value}' to '{targetPropertyType.GetFqn()}", + node); + + result = new XamlIlAttacchedPropertyEqualsSelector(result, targetPropertyField, typedValue); + + break; + } case SelectorGrammar.ChildSyntax child: result = new XamlIlCombinatorSelector(result, XamlIlCombinatorSelector.SelectorType.Child); break; @@ -338,6 +383,34 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers } } + + class XamlIlAttacchedPropertyEqualsSelector : XamlIlSelectorNode + { + public XamlIlAttacchedPropertyEqualsSelector(XamlIlSelectorNode previous, + IXamlField propertyFiled, + IXamlAstValueNode value) + : base(previous) + { + PropertyFiled = propertyFiled; + Value = value; + } + + public IXamlField PropertyFiled { get; set; } + public IXamlAstValueNode Value { get; set; } + + public override IXamlType TargetType => Previous?.TargetType; + protected override void DoEmit(XamlEmitContext context, IXamlILEmitter codeGen) + { + codeGen.Ldsfld(PropertyFiled); + context.Emit(Value, codeGen, context.Configuration.WellKnownTypes.Object); + EmitCall(context, codeGen, + m => m.Name == "PropertyEquals" + && m.Parameters.Count == 3 + && m.Parameters[1].FullName == "Avalonia.AvaloniaProperty" + && m.Parameters[2].Equals(context.Configuration.WellKnownTypes.Object)); + } + } + class XamlIlOrSelectorNode : XamlIlSelectorNode { List _selectors = new List(); diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs index 6dd3521183..89b88790dc 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs @@ -15,6 +15,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IXamlType AvaloniaObjectExtensions { get; } public IXamlType AvaloniaProperty { get; } public IXamlType AvaloniaPropertyT { get; } + public IXamlType AvaloniaAttachedPropertyT { get; } public IXamlType IBinding { get; } public IXamlMethod AvaloniaObjectBindMethod { get; } public IXamlMethod AvaloniaObjectSetValueMethod { get; } @@ -93,6 +94,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers AvaloniaObjectExtensions = cfg.TypeSystem.GetType("Avalonia.AvaloniaObjectExtensions"); AvaloniaProperty = cfg.TypeSystem.GetType("Avalonia.AvaloniaProperty"); AvaloniaPropertyT = cfg.TypeSystem.GetType("Avalonia.AvaloniaProperty`1"); + AvaloniaAttachedPropertyT = cfg.TypeSystem.GetType("Avalonia.AttachedProperty`1"); BindingPriority = cfg.TypeSystem.GetType("Avalonia.Data.BindingPriority"); IBinding = cfg.TypeSystem.GetType("Avalonia.Data.IBinding"); IDisposable = cfg.TypeSystem.GetType("System.IDisposable"); From cadd264be0bd1e5ed710f5bd81b8ea602a5eac61 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Tue, 23 Nov 2021 14:51:01 +0100 Subject: [PATCH 05/90] fixes: PropertyEqualsSelector.ToString --- src/Avalonia.Styling/Styling/PropertyEqualsSelector.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Avalonia.Styling/Styling/PropertyEqualsSelector.cs b/src/Avalonia.Styling/Styling/PropertyEqualsSelector.cs index 5d9c3fe56b..7681673ab6 100644 --- a/src/Avalonia.Styling/Styling/PropertyEqualsSelector.cs +++ b/src/Avalonia.Styling/Styling/PropertyEqualsSelector.cs @@ -53,11 +53,16 @@ namespace Avalonia.Styling if (_property.IsAttached) { + builder.Append('('); builder.Append(_property.OwnerType.Name); builder.Append('.'); } builder.Append(_property.Name); + if (_property.IsAttached) + { + builder.Append(')'); + } builder.Append('='); builder.Append(_value ?? string.Empty); builder.Append(']'); From 9f2390ae3c13eb4d00e3242d6ed00a6504299bbe Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Tue, 23 Nov 2021 18:23:21 +0100 Subject: [PATCH 06/90] fixes: minor fixes --- .../Transformers/AvaloniaXamlIlSelectorTransformer.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs index bc89513b22..79589a5a4f 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs @@ -86,15 +86,13 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers result = new XamlIlPropertyEqualsSelector(result, targetProperty, typedValue); break; } - case SelectorGrammar.AttachedPropertySyntax: + case SelectorGrammar.AttachedPropertySyntax attachedProperty: { var targetType = result?.TargetType; if (targetType == null) { throw new XamlParseException("Attached Property selectors must be applied to a type.",node); } - - var attachedProperty= (SelectorGrammar.AttachedPropertySyntax)i; var attachedPropertyOwnerType = typeResolver(attachedProperty.Xmlns, attachedProperty.TypeName).Type; if (attachedPropertyOwnerType is null) @@ -108,12 +106,12 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers .FirstOrDefault(f => f.IsStatic && f.IsPublic && f.Name == attachedPropertyName - && f.FieldType!.GenericTypeDefinition == avaloniaAttachedPropertyT + && f.FieldType.GenericTypeDefinition == avaloniaAttachedPropertyT ); if (targetPropertyField is null) { - throw new XamlParseException($"Cannot find '{attachedProperty.Property}' on '{attachedPropertyOwnerType}", node); + throw new XamlParseException($"Cannot find '{attachedProperty.Property}' on '{attachedPropertyOwnerType.GetFqn()}", node); } var targetPropertyType = XamlIlAvaloniaPropertyHelper @@ -127,7 +125,6 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers node); result = new XamlIlAttacchedPropertyEqualsSelector(result, targetPropertyField, typedValue); - break; } case SelectorGrammar.ChildSyntax child: From 19e4132c6bc9efa810156d5b119c1c1fce163af3 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 3 Dec 2021 13:12:50 +0100 Subject: [PATCH 07/90] feat(Build): Add 3 retry when the LeakTests fails --- build/JetBrains.dotMemoryUnit.props | 2 +- nukebuild/Build.cs | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/build/JetBrains.dotMemoryUnit.props b/build/JetBrains.dotMemoryUnit.props index 1713dead68..eb4e2b6f15 100644 --- a/build/JetBrains.dotMemoryUnit.props +++ b/build/JetBrains.dotMemoryUnit.props @@ -1,5 +1,5 @@ - + diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index 6f4afd9191..f0b894b596 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -253,10 +253,14 @@ partial class Build : NukeBuild .DependsOn(Compile) .Executes(() => { - var testAssembly = "tests\\Avalonia.LeakTests\\bin\\Release\\net461\\Avalonia.LeakTests.dll"; - DotMemoryUnit( - $"{XunitPath.DoubleQuoteIfNeeded()} --propagate-exit-code -- {testAssembly}", - timeout: 120_000); + void DoMemoryTest() + { + var testAssembly = "tests\\Avalonia.LeakTests\\bin\\Release\\net461\\Avalonia.LeakTests.dll"; + DotMemoryUnit( + $"{XunitPath.DoubleQuoteIfNeeded()} --propagate-exit-code -- {testAssembly}", + timeout: 120_000); + } + ControlFlow.ExecuteWithRetry(DoMemoryTest, waitInSeconds: 3); }); Target ZipFiles => _ => _ From 691f93a1ed0073475b2fa396eb9d9f72e6288038 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 10 Dec 2021 12:57:59 +0100 Subject: [PATCH 08/90] Multi-target net6.0. --- packages/Avalonia/Avalonia.csproj | 2 +- src/Avalonia.Animation/Avalonia.Animation.csproj | 2 +- src/Avalonia.Base/Avalonia.Base.csproj | 2 +- .../Avalonia.Controls.DataGrid.csproj | 4 ++-- src/Avalonia.Controls/Avalonia.Controls.csproj | 2 +- src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj | 4 ++-- src/Avalonia.Desktop/Avalonia.Desktop.csproj | 2 +- src/Avalonia.DesktopRuntime/Avalonia.DesktopRuntime.csproj | 2 +- src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj | 2 +- src/Avalonia.Dialogs/Avalonia.Dialogs.csproj | 2 +- src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj | 2 +- src/Avalonia.Headless.Vnc/Avalonia.Headless.Vnc.csproj | 2 +- src/Avalonia.Headless/Avalonia.Headless.csproj | 2 +- src/Avalonia.Input/Avalonia.Input.csproj | 2 +- src/Avalonia.Interactivity/Avalonia.Interactivity.csproj | 2 +- src/Avalonia.Layout/Avalonia.Layout.csproj | 2 +- src/Avalonia.Native/Avalonia.Native.csproj | 2 +- src/Avalonia.OpenGL/Avalonia.OpenGL.csproj | 4 ++-- src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj | 2 +- src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj | 2 +- src/Avalonia.Styling/Avalonia.Styling.csproj | 2 +- src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj | 2 +- src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj | 2 +- src/Avalonia.Visuals/Avalonia.Visuals.csproj | 2 +- src/Avalonia.X11/Avalonia.X11.csproj | 2 +- .../Avalonia.LinuxFramebuffer.csproj | 2 +- .../Avalonia.Markup.Xaml.Loader.csproj | 2 +- src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj | 2 +- src/Markup/Avalonia.Markup/Avalonia.Markup.csproj | 2 +- src/Skia/Avalonia.Skia/Avalonia.Skia.csproj | 2 +- src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj | 2 +- src/Windows/Avalonia.Win32/Avalonia.Win32.csproj | 2 +- 32 files changed, 35 insertions(+), 35 deletions(-) diff --git a/packages/Avalonia/Avalonia.csproj b/packages/Avalonia/Avalonia.csproj index 44e2290a0d..af9ed71c5f 100644 --- a/packages/Avalonia/Avalonia.csproj +++ b/packages/Avalonia/Avalonia.csproj @@ -1,6 +1,6 @@  - netstandard2.0;net461;netcoreapp2.0 + netstandard2.0;net461;netcoreapp2.0;net6.0 Avalonia diff --git a/src/Avalonia.Animation/Avalonia.Animation.csproj b/src/Avalonia.Animation/Avalonia.Animation.csproj index ee91acf45e..85938ad958 100644 --- a/src/Avalonia.Animation/Avalonia.Animation.csproj +++ b/src/Avalonia.Animation/Avalonia.Animation.csproj @@ -1,6 +1,6 @@  - netstandard2.0 + netstandard2.0;net6.0 diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj index 0470dbfe89..e872756a58 100644 --- a/src/Avalonia.Base/Avalonia.Base.csproj +++ b/src/Avalonia.Base/Avalonia.Base.csproj @@ -1,6 +1,6 @@  - netstandard2.0 + netstandard2.0;net6.0 Avalonia.Base Avalonia True diff --git a/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj b/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj index 6975642db2..d50e8b1115 100644 --- a/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj +++ b/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj @@ -1,6 +1,6 @@  - netstandard2.0 + netstandard2.0;net6.0 Avalonia.Controls.DataGrid @@ -16,7 +16,7 @@ - + diff --git a/src/Avalonia.Controls/Avalonia.Controls.csproj b/src/Avalonia.Controls/Avalonia.Controls.csproj index 7f1f4bc8f3..479bbb49b6 100644 --- a/src/Avalonia.Controls/Avalonia.Controls.csproj +++ b/src/Avalonia.Controls/Avalonia.Controls.csproj @@ -1,6 +1,6 @@  - netstandard2.0 + netstandard2.0;net6.0 diff --git a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj index 35bd0f97e1..9d8842f613 100644 --- a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj +++ b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj @@ -1,6 +1,6 @@  - netstandard2.0 + netstandard2.0;net6.0 fTRXyBf4D^g(51oL;6N) zhC)MxO)K74Mi>C$T2Z5s*t<3k;swriOEeQCQ%)d3Dhl>SBzyElThqu4s629EdiC!2 zS=bjXUvIp5_nK0vwXUJe+^nvUkD9~gh0=qh5dHe>gf(l6t?l4@O!|7B7l$3-dvjj+ z`DQ;D><#HnseW)o7%dLf0^ke)xgglA5M{pLxs+k)I!bI5$rm21V~9W8(-5k8vFD@Fh#6r=G2 z$!nOPL<7+TfB`-~0RB@?Pe`y;vI#J#N*)`;zpaA(qR_b@DaetFmP$Br8WsV41t+jy zSq2^HO^w^P<9u6G?jl2xuSgX|HR??-1-1;xRLu>Cf%hml(fw z>8~z)zz__FOwRl?dvN@b!R{d>{hC#(Jp(!r~^y^LsVWmE~-!0&ZAh;_5Xk$%Xc z(|DnVmnsWa%{rtg&lo9Iip*SGEw(MuJQqJSTDpir^L&OV-}rsjQu=;ofo8^FFPMHu zcN%j7Uckf%FSw-YBY<~>>S#r%h)luPNS+UxxS~#)i?FvJZ=b(%Hb zY52|qYj?Bm3#$+Oec}sWfNkS0*f!d~K=3a)RL(CULtEnm?4+gioAl_?egd(XwNDRr z@1oRSnfdI(iB=p|ro3Mj+oQaH+&)OS4fprs>m%Z6%*}+O0(_AMSx_thtT>m8TZrut zq$@)JM$WKkOWDaBmKy8{O0h_{LP2M13(=j}(UEJ}yK~6OA{Tad?o5=Xk~cHU`5P>~ zQwvWrecyXO@sWv37fR1Ri7#S9$6Ma@#_D;IMGZXCcLDdOFfU%U-fW=Cpx}?lmjLs@ zw5TYvv|Z#kgW8~0D;<$s9aO<`yg+Eg##-6bCl7pleAhV3ohm)~*~6ch+BH@BCcA+x zmOjpQ@c-UfdN=eS{69W6tftU`{6OHkMX+QlFQlg^cnjhz0q2za5~&@5@X0NZ2c!VM z6Fwp35y$TcL~&Y7;4xHOx5)8G8I5pcL1~U;r@6~=U#kE1++6gE!r)SJaq5_9e|EL2 zr?q9dA)32lWY?j?JA3Aex9Ji)9d(Vv$-a(>x?&-l33`j+@bdWdaf?_pJKA%E3>o~c zd=-0W6Tq8WG&o7AsBA5FFP9%y4~Rdc-15pqT6Z;i)0#rB77u=2!W>`=osDq#S$|-+FmYug9Epr0=Xj zS&eNc1*b!+wK6IBFsTYSTe!0HzMmcY$s{K65ZhgPSLt2sQ0cumHoy#Mk@yC@yADOJ zI8`A^1YHQHM4SjA<2jsPxL$;)jtj>pQyYGKNK+#7DZZ*-D(nf#dYnCh-bwNcmxHIa zBNJuna2ZuN{EoG|KUXFPo9O`TqZJB(JF(TayvcTt-#6KA9rMX^ak4yq@y$^FO<8M*5DB*9;@9zUVT#mn0g;1FqD%igyOA*+m zS@VUp3rk&3J_!tdLA;Uh62THO4hZ7id@rDT#lodJArP~L6Vf=OyXG{P^6%Q>&WKv$ zZVK6;0OLY`Mb05{45PBQ_l1viPc5ibZL4{uI+gewfJIFHu|Y@F^~XQhL;6_4 zV`O_!wHZ1-;J>f&_2HhNHXtEA8d$Xt#7Y%a0T+{yp{H0E|d=!Q1!~R57xI0j;l=juDS3&%ZtSUiMblv0m`=kc7eF43hBf4 zao(v8W7ooy(Jizi>I~{X6;mGrR1?)DR+D>Vi9m%0c!{{3C^T<2KtqQ<`Va^lFE+n1 z0osFV=BX$VU$*ms)dxzoOPI(!5R*I)@^|257+XMdVZY;Zu?tk|o;2r7c?_qBi2w`k zB^4aU$aX6)=8oY}7l89`m(hOW+cvMhGN?2lVqh-p(4a6&DP*; zu$p;rP@Ld6mg1n>NEY!DcbJ&19A^S}X}IsCxt% zs-X32&!LDkRLu-YA7IdibeZ^5vVHQ#i7&r>t^dkrm}9JSxph80$VQ!$#ib)HAo0o4 zPuR7r#@FB84z(NThLocy3_3yvWs!25Pux%;Xi=LiwTf|-$)>a4 zu%89H_V4VyZePblc4lX!rz4tm`r6$?OTZblHbEr4c|G`cWbQSA z@Wz@#K?cWyAd)Xauy=qfpz7TctnfXT3L{Cip{i4{*yLQ7I0mLTeRSy7wO#wByPG;2 z4*ajHnrG6D*|7tM2YdU5ZW+Q4K%KlzSI$q$K5k9 zi(|h%&?kKo^XD2-=+ecw1w=f7gMeNUUnvu2GhrObw$Dj`Y^7Fq&>{KnA^eoxdlOA8t0e(hZ(^ zr(x9K=^RS;H<>K;$zU*!%!C#S9+G@qP&pFtlNQbuO$0v(R2Ab?=$h&q5kjb!Iqpi0 z=h)^GAdfkTC#O90D!Ir7OpY>34hf$CuQ3@;N797d;OGM2qeI%{=#rN;zLLoh@TcHD zF5Hetx8>){Yr+1#z1`%WIXLX;ays3!(bLDA>G30l*6F;#h#X=wsTSQ8E1lOK?8=3; zCe=;nYjp#)*7v@>_l}zmcTG;;IGPz4y>2`|D&Hl_Erh!?PUp9N5xDTA7)E627bKmV zGiinSv%!Lh@d84s^c}=8D`CT;A5>d@s8yDAagb>d;i6|sJu3T<{ZvD~LCrvQU}pB$ z>Wm7R(@kwO1SW~WYlgQ5#s!$v))I209y+7M8m|^3D4;PiJTy4a)76%1X^KZF1j^52 zmq9Hi*`!I@Q{)oJN$GM4h7di(?=#YEP?j0eb5+}HHk8(FdEiz`m}yir8$$+z%hNQJ zay92oKBF;e+qH|a#q2`sTur=pxzj=cG=4)9tWAx1i^pi72I5ldd~QzH(7&uxD#tag z3kTC((@Mp-LVeZ28k_dnXOla}Ta>CXg#tgNx@MHh2~mCJA)Q_Spa0piG7cu2&H*wY z`vA_NQJ5@_Rm*^q)^j@<5c@?tb(stZbkN8eB^p}RCB3Q)_+ZnL-?P-#Kh@APX~~9) zO%08aV#3?7l%Lu$(A$(bM$KbYD?#1+8)bfMKYbdL&#Hz6c$~qv?0+Mkq-^m(UC+A3POMd5+~hQAS{K!sW#ezsRpmBv2C_1T=bq0hgfQxisp|p6d{Qb<>&o0l=X6(nzmDY1H`XDGAn0S zTVKkF?w|ea(z6#YVmX|4^V-3H3E0W;UCL_`!}kUHUaseh?}+T??O)(|Vb#JY{|;E8 zI?(uip0}54M&kP(3_$eLES`gJf(@P|QDLyyPc=+nlY`$1GBi^v5;(wu=jWz5+!*lS z2LJ<2+?<*q^;exAW@xy(xh%*SgQVPBJYK;CXcT3SSF+C~zL4F0Xyl$FsfE<)v8j9a zXU)4tR~!59YM;rtin)vbQG5Q-jyLaC{^TdMXO67Bb(KBKvh3}OsjGX(=2eQ+#IB}^ zDHY$Fa$QZVWj8kzHI$v zB~eQk?7muyL#ZfXV`l)XR?Ig*5(Xv-UoueFip&a#t;noon2S3TTxJEKF91d_ms|Nf z*=SH>(_pfxIgetEk ztC+=HGQ!@^HAiXdaP5gvQ!ya^QtSK~$8n zbAvMF!7CH*c9O8m#ye?ut9F`biHOxD@bP%xutqtu3SX!YFB6_$8AqA$&}7jnrF*HW zD6Zqh!r5r)kxH@f(jzU7P>eFaNTq0bO>w{hU+*YiZx3Z+L!xG&1^^3;qcu#c&@OV} z8Y~NU&^>@(1aMyH*dz4B?7RwkQm65AkfCYHvz3qJl@>>T&1>!KXbzO1!o7>5|e?aQrN^HQzgfc zrpzLc=cvqv@k3>!^ZV4Q%)@Y0ZFLv?ON%Xg2YWkmIZxINbAfJZ&YpMvZ0b3cW@;$A zlp8N5o5CtpUEJ6}AsCofALjcc=4*xiJtmxa46-3udI=m6X91mns6Ni`Fqj=kQ#?#d zW{*Ofe@EhCC9y=*iekWVimOE=9kRhS53GO89kV%Y5L;04NF!L8l>|o4UlNFJERf(e zwt11irZx7j(gGJ2TlReE?8>>)M|wN*Sx*)@>zbrhvPP_OVz_ljt4?%ZdbY2>p*e)5 z#!O9V%oFPbuBLT{NKQo5fh4)WM$d!)KsvzI2?u07rX6zzCmgbe%CnVu6bV~x%y!Fh z{AMSgY0ldd+&!B;G%(nm>a6d;Q8ehL=j=HrpX7WW9YtSbw1FQ*9QpbM9p?8=$R_oq zlR&J!$_|}GnV-5`C$W{Eg4Lc2s-)PTk zuU#{^)EB`jTyxE}y9aJM)HN}B%g&DB?EJxO7X%X#dH?>0JrCHb!=Ail>X9UtYJ{U9 z1T9I!PHFm4by<2q8e>VzPVIuQSi+A}1sGJTW!%6yWd%89;0D1hV7n$2)~|BWJoNzv zy-XlpA9v%D*(_iIk-TifOiP#$)QoF7a-0={=wTFX$TP!hI64dtjn3o^xlE1WOv|ZL ze}8tlWgy+`vc+;gT8UlT*3|_>J*tSLf~^MQVAI6GOV55Ee(D`{Ec&Tjb4vu^A@f9+ z<+2BSlkc$JZt&;iX>LpgwNgm4 zv>qr+MasS$vSyWSRA9QGHR4%9r93wkNK;e#4W^FxeDyt!MU|X9t!3r5Njc}{U+cA| zT9to!fO2jwB8?hqSR7`zI)k;Glzv~zz1bS+MXCCun_Fn=l;3n8OFML6W#TGIQtywz4;Wb1F9X*Elk!r4egUQ z{^jqN)hz%YPWe;}ydAhY+`k`yhJ78fR0?sN z_foYJR7GVf7GoL}R7V<0K`h}Zm7r0m;75T5`@v@eO(9Vrv^^EQX|WlhGF#ekeUQ{* z3t9u9WYSDP>aX5RZ$kn~QJ2jEuN8PsZ6KclZX7NcT{YrdxPwkv6nj}R!hTB>FRij@ z>l>4G{pCj$5ruMC%gEF=MIa9>xO{?i%&{pQV%V@(HD$yU-r9iUXQ>rD_hkC- zgI_V~gCu=PDDS0|bfhqbZA`jMq)#KI1|eu@KVFW{5rgwk)BLD)^_u1Mk1{K*t`7g= zQp>)Ll4n&IX@BQ z86~+*Ek?hB5752>a2UxvJ|Tz*M;8|&q`e9OY!Ke4zD44cuo18Zatscg5kkQzV@hmG z8)BAtLyp4B?GSe6_XAx9{ZJhd9yb}lE%}&l3q!w^hu4r%?Tft=8s%t}kR)Yk99~RL z*jsd$2}ly7{>#s~;ZQ)hgZxKE6?=e9U{}L;2%}htq(7(7Qc13s!&oKn3k=HRtgZDs zv!&l5jP=d``_ct^7v1IWh>zl3qmq)6VpSuO2MGov8Hc|Ry1GpxResnMGb>r(F<3<5 zKliopYUUeloWVB{3pbub@5R&|~%%x8-~Km`sdp9_aWVJM8g z*e$Q{3-)E!2JSsjL`({M*_yHBFz>apw^)9$upsqQyzkHNr?`gtVXhdD`oZ1|{~_Sp zcddun{Q563zD6kUOb^YFq#@hzBIJx8s+lsf`;Dbl z`gg5Zyt7!uJbTy=Apn0HiI_Y^Cy9zY9Q|r86lw*nU4eWUwgh+?IHhX(74GrKkhk5& zeyDG|rO6>x6W^mscg2bAvdDElG$(`)D*>)%_yRd_p6tZD^U z5!!er+=0pP)Hj#~e*@5!^oA@Za6su9kKA5)0dkJ89|}LLnuFxhbTxO@udD-}$dnMq z^?(T>j61>d|FHemHhuT)GQUmhc!IyfLY^u-LkQkO+Ah2Z`TK+#OASgdVUk)qB?_r* zOsKp=e^9z7BZJ<8we;*|d$qA;z;84bZ2frED~)BSrLk;$tn%_B=bH%6p=Me#-2SBubCMC}d^!nCA~6e#7DRgDI-m$ccH(Jo+7IJb%LpSEs)c`yzO zC-LHc0q?0t4u@K#Dum1jmrFSO{9hzaF1Ub&khWQIxXk+DEtQeR)vnU$#|Hsf!o_YyZa|I2C=Kt{VH8cs$%>(bX zy+hlIe+(xF-e-GIoyR|hzcDSF-gq~r^!VfW=k7P+>)#Og9u$7V`q^{X2h^6u_ib_s zp>n+fNgNARSyG-#7mJ8H82C-s;817h&>(OfaDsjPx8hrb*FfU2V2=}s>cIPe4=i+y z_?%yVLD>Zxy9K*Ixe_O#H@s4KOd^-9$ovWej8daNffDT~D63N@q$^u3cJJLYJ=N8jOt_p@ z?EkSNd#>Dj<<6z4-P5~=20ACZCbQ{8N3tUraK>G6tHTPys{?p4ivzo1qut=jbFz7H z|G8E<{DHztc~+;&&!J6?W7Q2q)nC3^=+Swa22-6qdT-0Xt)g}I(tqi_39m`x^?2j> zVDF?B%>${fo?377AoCS^@s&E@>puq-`fv%rQW9{r#9eBX>lmYsyXzyI%do!HXMoHO4k?>Xl^@B6&Z z%a@kDEw&$wKa@ARl+mxznBa^M2Jqb$inzh^U{Y2RiwX7`;3tSQhTZUw;ToaQ9M_PI zlV!4IKqU)H*+8}$;;vzgsdSqh^UN4$j`Djf+5c#K#qy`(HyYmPy|qWsOk^jH^xTSX zK%aoh5_*Jxz&b-dax3n7HcBcs41<;$Ix3Ku#Qch(qc%{6F)u`3#R`k41cU(se zq2RGdFkrFRqHvd4Kx^m&LkMH{olcpyuPa2;zR3!LS%dmd1J2RF!HNEV%^cjoW8xG)AW5x+*38H&& z5BYnHoerZ|!+j^MtwMJ9Brj-lTn^YacpY_cvlhUyuGK16^)Ptes3e?xDa8nat(t7{ zr3j*`an{(aAO!#xEgCd{ybi7}hIT+8sEA*sC=IrKz{s29%FS7=akch5a_HK9T^IJd z+sDk~bD;}vzQ3{B5F64=)it`-4eMn}F>VeREEU4@dv*+;N@st;ucyZ1*Il})F;UX` zWFoe6f96w8pEj;hWYk)c72*aERv;@C+^+OaE3Z=8cp&cJ3@_t3oq~`vpoS0>B52J8 z5M=5=7!1o40}erEz=VnZvXo535g_%m#7$hJ!~#s`8)144*fc3{6LKR-&=%p5`uR}J4*773p;ePk#T?(%sCwP!E%*s6?~ zGY`7$118t%u8gJ3-m|j9q#x7cAcE^`;}zjq;p@;@{ahv2!Y4*Pu7wG6AD-J(phuooln(WsISxx#spohJ}OI`LQ>ZsFxz_Clc4%gud>nV#Y}{LWz* z3q9Sgh{=@vAA)QA(y#dSmhQ-KYg>1Gv?f$;_4`BRHvSFip4wgQT5KRi<-M^9ow8o7 z%icnJt)i!`es{+}N9)dp`no<}th}Z_Ko=%~YlN-nIp`CFI!LEG!o^VJlqwO{3Q!6` zUIm&Das(eh;T(e?jQNZ~FcAxs`aB?P4e_wV$S^eugD2ir*dJv0g-R{ms_9-KqJLx; z<&$Oo70#OClBCnuRo~iIJJ%R557#SGijfD;w8x6I!O`v$8l$QWS)zWM=uM3EcORK> z-C5UG)jGY%-Wa;l8v68AI_fK$W=zyk>@=t_DCl7w(P4V?av3MFL#L_`kEQxF+o zQ#2}c1#C~6Sx%!7Rt!a2^iv~@RaOQ{5of?vR##RhtH9Gy5vVA3n*A0(i#>2yj4GBj zm^|z_e<@yCEk>C>x7GYjMk(|cNrly(hxdL+ZP(6BKD_o3m0h<(xUIE2Hqz9RF~_>A zg5{2mK)J(ztLax}%+XV2*ChEj0*yUec_Nj)`Hue1`qlQK4sU;Vd22-`G*;0QjwZec zx=aD;(_!Ir=rhIVWYTF9=y?PsKdjXh!5Jnfg^NTA0B_MO{p6G@Fxqj-F(P;(awp07 zipn5(vRZVi;tJl9ve{CWFtZq;XB0T>2;z0n5w`GsyLPSZOk<7iYXA~k$o_sGuieid z=63^gVamSetD0&({bo+6zR8z2HfF!W&dxtTSU$;k@g|>&@)SiOg|rvrbYanH9V~hi zAt>aYWaL^7sJQfs6W8*E6wonAk-;&Vu(;xga7MgEQ;Ll3%ytS(4(5T*4GZYEaT?~2M_iJGG}K$=8CpA+M}IOzVl9dEJS1}(5GfM z{!{o9WFs!vctYpJxtllsW#c?R*X8_y>@LpA^7ovFZuNv9ek!{w$8ihXju^uE73Z$s z__vL7to~^nY*g?5>-+J44g~ns2-X}k9k5NKNzKijh~`T{|>%AB{Bk~pTg;rmn^z3 zHIN~U-$m9!#=2*uQvv78I(;Sb5|h=K@XsL&RXOgT7H(AXtrXLj3;fZ|tr05(FTl0wV$X%4 zFSlvRpD>Pb1nFp1O1cIS_)$_+QnYE1JYbXLcg_we#>99s*{Pi|?iO}s8f`|i-W!fK z_RpXzXv-_Y0{<|(dpAB!+UVxhH*eTEL0&FsCpr7dBE2A`$-yoWd8*S_bev>kwcr^Z9NPLn`x(Deg({u3I*(f;>|$Tf9)!^u;ormG zEl%M6VdvIhC&xEqr!e3lrHBIIFTfU#ZT=XL>KMpb0<3eGUQ`?>rm0XCf5R@jE9v}qdal^VNvJ>0}0Fzcih1s+P zU{hec!UQ9;S`0dP5&gVhts@0N4${i`lF1yqX|I2-tr8xIy2fPC9I$xWzpa0JX_3p& zln#yYkGeBvYpOgHQmR~E>pUbVI-8sizMxSi5!mUHq>73(RsZP`&Op_5rutC(~6(W&Vq{U^>& z(0^(kI9lu*>1}OoNctnQ9#36qaWLYwMJvLtXe!D-+ETYrW-SZXcDE)Ht!7(Iv}uS} zXhRWK36e6GevdCJb(iT4-!MkR9H(l@s70W5PZ>=w?1%y`5 z3C9zBEEJ8#j%$%oUcoo$G$~b`f9LutFEdjQ$*wO z=&bZxG`_31CX?)`>@H6i#iQ|YS$n?s&uN{*E?9C6vutme84Y7CYIUMb%KuiI$ZP%o z>WHNu$q$zPJe$in(?*sQfaQ}UG-E-GHcfaxCofq%C}bNjy!?&?Ws)xV(fnSC5ACX`MS5E;MLC4?@Fwge2$`a*Mwq^kzF2yV zkonyA;sg03sQ?wCzv4ry4`n}kd-k)pU;52MyB@tL6gRxGyYVYMot-`S%O5W`sm)qz zaYtkJt1}HvE{D_Kkj%am3Isy<%c}z}ONB4%ZxkCEvOSSvtIZwa-$>sed}oz@?BbuH z?*<{PFtw}kpA>s}DW2+93Ur_V2Qdz2fJgD~N2j>rQtv zUN^vHpe!M{nPO(7gdGAUf>wyw_F)2=MP;&Saf*pxK$_ZU8MJLz1uO?jAfNIqoSTs| zELsNH=%u3!v%vpHwvm6H|6iA0IA9M}R4h-HFN6}oPEV=7Bp3@cd&3Lm|fQ+$-@}?`Hh8bf(8izw{ zH(^#lCDhuAT*!T`ecfI4NTX3%9*YM2cB?@L14xU0lUgWufT<;G9wbwW8bU0BxaGXV z$!=8S0WKJyuk9EHWxc_q(|WZ^OT?8(n6+9{q`a!PF62<&dh6Q6&IO+DH>K6y+QB^^ z6Zo3S%8H%Z$(C?esV!_Vn2MCJKvjjjjU^>1PoOL{wYn4(mS-n+-*&CuTa>9eacsGn z|C!on+Oeax648is@{^Brzwiy%a*n0-M(746avDPxB<428zPt)G0qQh;K$kP=^5#T! zNyR39k&gVQ@HImN+I)|h4;w_X-9h2Tb4sFdD{&*IYzXH>n0KK)1ON`T_}13z7H2(O ztqy-v!syf4)Ow9M;52tlRW<@?^ZD%C>W=O9CgMtQfLA#E&b@bzWnW?QT#tK8C&O8! zD4UK8bjZ+40Fj0X?z{#{N{*figvSJ+R1nO9*Z={aVAp&<#x3Qrv6Oxid}4E6C1y0n z#3K;ML`5jkT$HUGXc7-HW#ylHN1N+zQ|}huXFjD?l{77M*22rsAFfOptg|Lj*%4@n z+LemzhbucH+J(9PcMO2n%HQs9x%qH+ZAE3x&`9&#kTGhUsF~W=;BEJ0-a1iN54mRX z*etgR6g!^smaJ^^qkExzrC4qgn0#R^JI$}rn%MYC_9ey%5d$h7?+Y33Uba?h@P14P z!T;SWzyB5*%Z=}6U*ewG_%f~t|9|AM7^-b+tF7+tt}ZSuEk<6L>`RpBg%ZBh`bsF- zO9|m(aW51z*sDrlkJ|IJ4VFa)oGF7&K-oG^Q0y*~v>PN~Dd3KBCVPC$3k37O(#PPz z^TJpc33}j0X@4+cd zOtP?7ZRx?8tsRuZL*pfJBjsyEU??@89gbuP*=)TOzsRm1{zoeLaU8s-v1! zg?Vhq9LntNte&WL)FhC8u|rP3*l!!H)Ah`I;`JS8_I3*$ zxCe6+;AWm8s>ht8mnaQRut;&1=v4yy-blAke8MX(VC&_#e!^%pfnY}1F;sy7ACPmw z(xHO;S?(&#;LFmah(>|0K5D#%J|NGsVo?hL=x+(kI_{k1xKgA~f7~>v6d_cf~9g8|Z-V7L}}iVfNtfj>ag_50A}PzG-g8$EmYF(^gdb-A1elzd6;_RBaH(7c{u6 z>S3e?!Equ{t*fVV7DiWzr;2ta>_-{y;QS^!{Wub%(ZwQeOTZ4llXnui=td29gVn*Fm=M@Av3dp+9rphLauh3+lvDj z>~FD{0BZ`^v&maRc3YTM*eff36V&0P5EuVCRH1aP*hP;;X-K#p6|_YwCZ(~&teCsI zVpsJOXC@sXm*Tt1)nD_%ZD)S=_dnQt1K;xbuVo*2u>STpwq-A{ zcBZixR|;+}OzjLg3)@N2Tlsc|xv;HRYG*20(9EWo)J)c9)=B_b)OVFci)`~o-f0qz zaob^DQ?y_bY!=1f$>@CSt`pO?fa=4UzxwjQ)sNg?SJL2lOYPy>sSiHdKKGlu-<*B< zmih~AFJVeJSTzM3#?Xp;FK+QzmatY~|J)B{T%%TMh&c>UGPPQT>;Z^~QDIIfe+w#D zh)*fU?nG`&IsYWP#)rX?z+@^seDbvUv~$VzS@UPSnNxe-^Vag_ohN%U1A=>W-@egv zYm+B$8XKN$WHt(c<@bC6c8LzF_-h%UHu$tH8z};y?UrWBC)|NNM5UJ9*^<9_Pr+xe`usSHx(1&YHaV3l$+sL^o{v@3 zPB&KE{lrO)PU)(mkfRszhi^zuMylu}W4tC}0Q(s6sw3vejdKp(FR;|%Zbt+sFs7mF zIuMB-)OHIbeC{l}+nprM!^Z2iKnXFt); z-+DXjJiGy#1IRP@D&*zKjR_lu4VVw^^=B zXbcpcuI5v5@5zPgM{Ya1JRdEun{Ft-XXkxqQ+(}lS1#g~jaAcoXq+~_i+{lM0b6yD zm`YUu%#ceRdM0*#*ds-_)^nU|aEoHgWn?hH0BZQ*|8rUGMWE0N!?#AcI@D^v;BtjA zL}4a21iq)7KZ%1S4P%`*8wHi=r)_Nx(Xgaf#OjNN76(<)nwaw--~5&A*T3I1S*`eO zZ|`rFHIwx}_+dwD_QU*~t0DQ%{A1stwmy%4`Y>i5Je##u#c7oSk!3@iRTMmT9xIbw zEVoFN--jw5z-wGCCe_>93JFI*4jLM6T(wp9G#=6NU(r2PRpV5-W}WJIb>mcHO{05( z*S`7JPlQ`z%8#T{A5kaT6CZf2wtNG-b)xPAeEWlF9_c%1JENP(_0;&#g2G9$N()T-5uoSZ*;5z*C$qZ4ogKsf{@ft{gN69iQswfBIbZ z>mq)epWOZn0yrv$-L8MjoDGNKu4?^@zwBhOXL^t%4m1qgD`6Q<$L5 zZm3gRxf?A_3COp4OC+mzERePM1S+6*Jd~Z1~KZ zn+S>E;?J9Fqlx*tp23FQ>DX|4L}K(?YLOWI%IE8h73L0W*k0NaH-qlcsI^CYb^S#0 zH*jHbqRA62AFmBm*uJ6CD>|y{+x=>#skEu8fcDQDRC+tydV6_Ev`Hy83qmZW9qpEh z0Ec$fYBa@OOR-9;#5ROE_|`@m`5YdV=AfY*bI{&c9gkoR!kB}e%jTd8OR0<#RRXAm zz^ANWW1E`=a5G^fXK6@tb5H}H8vs%O4#{<(&8kGD>Z(;>l-eQ9L2_DCI0p<=AOQ*f z+mnE)V{G*bHZe&Nw7sq zDr>tt5~-e}Jx%Q`6~ocu3g;I!I)%T=9jo@5K<*AEnfS0FAu(?lZAy2e+hDIKjx;NU z7G5lk7}~3di$mL7)!84>YFr+q=h0&daIf0U)-dt(ono(LUTwU_)&|S4%}xe5;4hT(p;L zC@RFun&27>U#Y#9u_ckc4_ULpo4&QZc>@ZNB-Gv<%NUv~Csz66$`LZ2>^TtX-S=Lk zRBdW*pP6i0vSoiI@HxxL@%hfP2fFxlkxAQDLmsTIl!1@4gg0xg{xvdVKGY}!&QC6-E4PDNq#za5vjlHXY-`i!olCnMr`GMxpG?TzVOgay9V2+BUB;4aFmtJ|zy3SxiAzMv)B zfv}E-P#FvvWPEVLtoSy=9M#eL+_LaZ_CEMQ6pfG-iNBoQCp$RV8ow+&CjzO*)sdc- z2w7k+t|r9@Dw6ElhN$ImJ-940Tu)t1ECRYbTpS6#0RSH~B7!4fCTQuijhS|2X(xeR zOQvIHMRcJ6-{tf33wtVeTtBRhc)g|k{GO5f_i3XY>0PPTls@8wo2P46eN(fmD%BAw zaXfHr?%^B9biU%C$9eDdr@nZz-sir{eDKkQ=~cVk288JTM;1p9*kb5U&BpVBQy4>k zYN9t3Jy|Vi zHQjuXc9mBZ;qV5H2as>)IY8hv+M@GR8yG;~FHB%i0XzoSKaEDcYQSM#KBD7`id4Ie zWkt*P3oitt)wt<;C-TEoFm$O2mu&*s*df&(X%6rggc zT|^`;4Hag+AX-7X9kcvcI1I>JKsc{_S_d|Y6z@P%0jSR>^aJ&Y-u`mEp(x&3o%A5h zMTxpar`Fl3y}|5#NB4N@N7vhD>&uHPODg&rBG%4KS+llcXPK@(Q8nC}us0-2k|mLz ziN$4K)70T~$B@lFA>QBF;`Me}oEES5?NO&oqY)Jbb0p{;d*vs09PPd5{GOJ|ezUD% z;hLk>6FcL^zx$dY(zJ5?`qa>{!!%fN!!>!=MMrj_pFb_Nb?0KlXY#yTUQ7q0+O$-lax;r~so9k+-Dk3Y<70d&`{8yWY!f7D! zguRBrsn|ZGj)Bk%2Ts8>FVr!_b*RGK+3Sdlh6x`jV> zU=BJl2hHz@+TEHWQK>gs>>+Qo`O+JY?aSP})?5mf%-V_Ns;;48-D4j#*e2dET9+v@ z^qCEZc4RIb?(&+=UhHek_9>pmeuubhi10VLXSdm0)0n7AAhcrw;d)i-Z$dqyJso=@ z%NgJ$E>g-F;4!fCXY+Vrm_?MWU{pPEV%&zt?~O&`j9N;{{z+jtvS8=f_(YzoJ9@St zH^9iXgW;7aTg2Y?;?GoXx~coZ4J(ag+Q}Tdvv=nKl_vXHvFa8_9XLr{v zvKeLU4db0{25t6fHa^W8mxOxZH16uEPiQIb#TCb0*Tw$2)Gl?L{(h)y;qK7AYxjij zS?pZAH*)tneiI{an{1!_&}7Htqtb6v?URVSv%}Z^cfzNDFY@5N*aYwM4(wgWxbLxe z&aY!P24Egw+Vde@5h68}D!odnKOeDK!X~A`Xg3<{=aKy_>QXwK;H2`NkCzHQzwaX2 z3af^202~flcs3QP17==th==4m4{%<;-#gEFz5Z1&!}urG*Joz>`T!JTZNB@qgiLue)!REf^zD##lXM1aTQ#lYA zwKd5~Z<)6&9u0&4!ss!25RhO8+S|Yxki$KJ;9-_MT{`x{(zV33= zueN@s+*0z@KI1P4b#*ljVqA>6{Q>b0TV7pye9OzTTV4rY+Vb+!$d;Faam&l>7q`3= z7Axu-%DpC|@1Dw(^h@sN3%Or%KYy3|CHM2L+%K3$CL_J#*V~%V@ybYHO{nsZ7KX^it{vb)-q)n;NikMHp|c782am#c^E&T?3gOd^Mt0)pDl! zT)?Ul2Yb8;9uQ@mv_=;!opiOt8xHmheMDKPo1nwEqEySJ2HpcdnO~Ium{J?GRBLU@ zY*u5Bn}V)-C;xB_ADWGaB9oW?HMS5d+Z7Rhfq&Ha{Ugo>CsO|;OqXuL$380eYt-$f zFA=J!1PZ=EuI=-%qCscFhWrealM83xELV>g(Iac{><8p$(0B^qm+lq5B)$vhL>YJY z(_yzDIT9HA39O$`q>pm2Z>cm2M41#RiY`KZq@--zK{ebQ;T%PoCtXgZ`3tY7Frtzu z;L!>IVj>=6q$Y3z=5f9HE8==ZBy(US_h4m!LzB_ogQolQ4DB1MuI|vz7}k!So+JW% zV!b~IE_{{MjQ!%-gLf>Q6BUIx;8)V0%`xFatW^e-rgh){n(ipw2^5k2s zmdCURDshqe{1xlrI=}gtG)9EY!e$NSwwA%;3?Bv^zx2bEof@%>*cNRMS|0TF zoZ9p5hs&Cmk7s)Og~<5A!uVZlTm5hyhynU4!(1egfi>rcqv)(C$NX@xal!=$8!Y@y zl1J_`FI)uW#0#%c>@|IGboG#O#L50eAcG~H?lK?Ti*w7oS~)Oe94c8Txy^W6>s5Sk z{AA{a8#38{zk(Nz_6;bvLNn&U$oU9|fg{ugT$mQdJ`s7{@Gk+niC8)x5=nc7Y$f-3 zSjH4%0%X$p2t-mARIrXy5F)7%lDZi#fB3!Et)Gp6C9fylHqvR2Spw;BAe`wP6`nt^ zbJi(qVgVZCeAa;T$g}G>Ig4L)wAhN54~4DU=qYASlcxb{a^gXCH<8 z$X9gu{S*Q*ndbQ|J?S9653VhW1P3ZWGL3941EaGKcHTtHoAf2gq+T9D{u~$OTG^ys z!mWq;hiRe+Rpex(K2qRKSY{x9|DawGUTE%~40R`K+v-cajXf46La}Q9R+*!R{!9_x6&`MK~#_UNjDaNwE?b%uya63x&>GMd`S{5T>5W{>}W}B8`3|Np9-CkjFr)X%lzUb1V`g$T= z;%yDJFShyS=PhAf_u|9+VRQC{26NaleY9!n!T-|hru4?T*=t&74+ui`eYRJ1jl{HE%KlYGehe9-K(*X^h+P4E4^K|gJn7UO@Do(m*xC%5R1Ikq(^!f3e1EX6_$OKdW}Ig!iZIa-9*an0VE9)|KJbk zyeUTo7>SYcC(Oefhf|&M#-v2yTs$cy{W-EF70^99TR!{QT;89{@&WxCCpU3%4=^t7 zA?`C0Ho(~u7L=;ikf2r$Is^q+-UWli#f=X%TR(vFGlvl<$Jvb=140Y-8+n7mFomc{ zrD|2Hfz_jO1f+R@$>E$O*RUr|yb)Up0}Ts5j#LA(k4qU!vRW*4as|m+3C3@T8|v=` z0KB2L#sYj%Bx13+OmMDBm&QD)HtiTqxQ6G&ZxeaMc6y0GSxq;HJlVS_JP;in-6Ym- z{tqURf-GTprWzh?c>3aEIZ`X_^7LrlbtL<};?M0r zUexPpWGR-`&c)uB{m#kkw?0*IZb8no#C+;L#A3c?8@6y&U)Baj=7nac;l*Zc@E%`i zYz?I?=MG%bJ+^njo{of@!H?biuE`sJ-T3>vcYUadsnh5v*_y#p}|UHPcg4hhLcboz2LJZT9NdzFtD(`Jb7RT~N=MxtB4 zb;LAmo44I+ytO}1yS|)RT+C!YT|m8#F#;2p@J|>cJqIpmod(bk!lOWfARIgo-Dwm4 zrswoDJQ8;~#)j>iSO_f34n+T2%DDBtVal*mm~so`2zbVB$o-X{u?1px*)xJ9eZ(Js zbnQdn<7<5RK=uz$@BOQ0{%a#7>)96>gS@nsdxejnA0|SzkcdOtq4~-O0*-Dv4{~ey z@{~n5p>Q6^2@pm8*YUaj>~jng#kZyx?wFwi0C*Adyz@zLnNG#J z#ZU*tPKQK}t?56S-+Si*by4ck#HHJ*ds3e;S3gCcm|nH*IZ&&B*OBvCUH%+EUvSVD z@XGUo`NL~DYWb0DigC;1JE2>NZ$V$8+?lk|!waBQ+^So#?5|*bB^_S10t^Z1@OkBm z^6xO^DhPj(z=|Q+CBAphWyR^lLx$K=Uyizz=~q!cO8kN31A-Z);D96jiv7*0Dqx|V zAr-4F$W*L3Ye?6<^N#GdkuHn>(SSK(-f^_CP|2Fx&y=j2k>c_B>?fI`b=`+#n=W)M z0TYTokEVx8w)8pQ3%RqzzkYdV;RyBS65TR&>+)U{s#}-?s9d>T0EGR^VA5f)@;6OF z<1z<~5U^jZLF;;3ojD-sTzd+1E~WBc_^)M=4&axYY8Uz4{z>>K{B%u-KbTAJumFn$ zb{(-k3C$RLm8t;~Ku#U-#Sp6~79Lc{J*4u}wFUr28RK8UBdcv!M_o-K=FjBvv2BIy^o{ zRmtF)k)0D-lgjS!Hcd6AMpE^&Yn@%|zU_EisZ^_`RqFeL`>tVuir)EM)zd{I|p5lBOs6C&pE|FXZ??MQm&j+gfAA8^!Jt%iuRuD{&Y(W*15 zP4@U?M?)slWp(NNCT;v+b-mT;i&~66i&djJcC796dYf7`s8rWXtR~YF0`E#ETCd6U zqz7(K0OB_Q=!gGklgD2-7zm^!KX9CU9>9>}v>@G$2cQaml$wn=`5y2-jW_9Zf6ex( zCD;$gu!rafN584F;bf)E_PC4X_WB%tGMAW@rIy8x`<3|I9nV_$ADf>&J=u)JtjO+s z=^trlXZyN1h&k=z4yE;-WkFF5RT}wjaDbC_%*&}kTBX*k(SagoU5~O*3Z|F`avx)E zr2{~TTrWyxWv&PUDbTtuuB##zwSd&WBFE@Y)-0ClRzio_WXwOItmsBB`A?)DSOb83 z=e;PxGl(%!h?PaX!ND#|YGDA$3IfAv+qPr{jO)J)Yu9LWYMVa&33Vc&_ZpwCa`x5i zpL~g*%;hZ*iYZ=!vHgQBUv<*jXJb3wU0KqiFjd7*SE}tBZ>gKLV7<@cmFN`|Ac50$41tj?_BdvMc+wx))y%O0s`+ zws)1pR-(N2(!afcIjP5DTf}^ux$1N!IJ}^#=Pe7!(??UjdE>{!DAlW+O_<&I*-ejU zTwQOC++n^;f!MWQ?#q5%dB5e}@Z*zqidWbB=e76!1{ zqByRX7KIaA6?2`sD-)4f3_qFuEpN$wB>TVFJ@)s|X8=llm1h=SP%e8Wlyd|My3lpHq0E$pc79;r z&rU5)zjOZ&7AK(2eB&ED(DKhcC*M$Loh>1@-^luiY{$^l6zVA)C0N0dW)8TbL~15o zz%EzXf{gwk%%7K8#??x@4?eT9FR~3U1(Be!;CIt9g5E8^r zE38^+SII3%W{J+dXcGBvm4T36VXNz@=onHd8sB2#FPI-{Q7D4Z@OUp4>6|E7upo6< zkjUTSt_}8P90}JeBO}@Wat5^_7D<#nZnUb+c3OfocBgT7rM#m)4moEqYEotm%CK>e zL*8%jyRX5tTeuPWIG=^`k1`>J3B)F!wT&!#E4P(EU@(2pCgwULYfY2W3PsBz;DU){ z{bKDP#uN!q`LNM9m^PF;c}@0@wvwW7*kMI7QkD=E3>6q($}JP3b^%93LW?WKfCF zV_k^igA5RS2v;3|M}b5Ew;o5>M2s`WQ4680zMm- z*A*hi#xckpecFSDv4~1C??<*a^LSw^^Hxt%AYP?6`ErbIg$r^)hXu@1Zq^}r{Bxb6 zQCCAcu(GLBbnWZM@M{!~)}W`;-G#(ho<)CAS81oOV#rC!eqw2lw^n!SO??@Ane*cP z<<36wo{Q#vYb{9lgFljOq7Aa^qB|#LfM8|`hzig@1f#>9Q#RP6h${+0D>+|Q&bL6O z&|E+qY!XWyk=>A0RqIl#vaG6pv1SPR_dgr`gBV-d;M~?w#9_@oM*0NZ%XN(RoFouD zzre}74867zysWpSwPl$4a!fv*^-@ri0K1TITn!dUXt}hyfCVSkNT{@=vB~7_o^*mk zNsS<$f32cmHAh%uLv>X=8Z1S$p4Fg7JY^+cxtTOn%1l9_Z-j)&o%q;o!ntKiOmv%9 z7yctVPPB?bLr?L9#$a*i+}AufyYFzt-VdKoiR+^Tf{4hPlq5xA@Sy$2M&%=HKp2w1lgw&eD7dTm)m(27Ra;TJA`C zFb?||0-nIC31AB$wayb~nL-)FKL)C#NE(Rre zDBsZ*xi>EnpLyE!;I-RDqH(?>Ad7^>B|IsNZVCm-1lc|qx6prUyqJXi8RNC3JR7g( zMtQuV(Xhk8#!JE(ZILMF)kKO7SWc*Z7ftu_d|fvas$$nRsS0;T!&5oA`t&C)`$r*J ze3dL&F5~5*Ww8lQ@V}6Si`k%mjg9Xd)0r#T>#qa*by)l`Y|pD^2QfIE;jmAK$o#0j>=IYnXj3aC*EMWI)UBA}V!#LZ4PGMH>F zMg|ikdvdd+Imn)-;sShNO9G|5Hvl^@PJ$FKA!mOUCo@T)eW}eiw`i^_=|DPTW((i<;g;ETRZ;c_*qzBh_}4}EU3=p3WpoLBBKrv0#sd_sQj9?FfC$V!G^;c+Z?u=mfC!Ak?;NXnH7Xl4VN76sLHqhh>TauC;{e_B=>OfkygWPl7 zz^F>~12U-pR-Ihv>$!2c5jy#gWPd@ByCC5zbhz&0@G|3$KjcZ%D_D8iD2vg{z@6B7ZYmJSWP0p$pX24W`)%3)04N|en66$0i3yl8Y#TZ*Fz>7GF< z9I%r4Tq<;nr*M>}AZc)CPtV=?v>^}*nw<5ct&UQyw#4YKDsz~lv8b8fbLZmCq#f5@ zf77vwuH{;_VqBqecef{Ryz|a>Sy`sMoc+Kd``{lunAQc*2PEIcRSP>JWw0vfo0LH2 zvc3g3`y_qN`b32!FLtg|6jdF^o|n`AHZyWU1%!!C?iLw@sACoDa|KyB+qNa`+&yy= z?vQ+P&MjFxvz>5`Nb84e#V+n}TAN!s)U8@{s}x2W0w(V#{l9Sa&7$TMu9UpL_BEPSV31a+ei%x*98LbNK4)d#EPc#(?Dq!U zjM1`u@Y~Eded#~%xrvQu&h`#`D7jW3^yhJ*;?x&&mUx=>Kf?n5uG8~dEbz-`w_D%~ z?eDMp+&{w#zjuAMzzRQeVyhLN#y)rN@{nImx_3d&l~}v-Z6SZ_Qm#NFh_Gy*6zymu zJ(d(~5Jfb}*PHx>0rod+rwA7HH&0cuix;~t;M%oNJGAuhY}sdc-9H$8o0o+!6k56| ze1_VzhILB0c!FE+Ty%HAu|Yc!y9+`lat)%51nEbmJC_R$+IHuXbT6jwht$d9VPYfqM%`3)24dGZ_A7$mbcpp+hM&oM>4VQlYX)JlPFM)eL z`7TNbKWx_`(2H)XN|44XIokQKLsB3O$w%H}5B?$K$#psNy8^Y44~w{SllBkiBu_V0 zq4V_IlCeZ1WFH?GRR~&xdgsi;=lc|V#~Uv$VT^$cbf0k2o*4^fcYw3^8DT)$ z-~hYW7!W9DubbLsyK1{ge)8=?2o2ih+}bV??ZP!sYL|3_er|nmO9tODDX;I^o z*_y>y%I%v?!HW&!+U0t}SD=8;ke+}8L(&t`p385e^hw8=z%E{AtUL zbf1t3dMULyPQT0I*Mb3}JP4Er%PEu}@oPx$C~vJ$kQ*CQuqh(G1Be8boJSlm8KXe_ zPk4*26E6=fSIqv+U#0{;I?~#HpsHf;(CmqpmIHTBuiPJMy!F1825r?y^~k|StKAIV z?GpR6)!lz~YS#yE8Js+SeZ0RC`6&gC^38o7x~~`76xxpnF%j2~E42TzcsAr+kR8;5J<8q$K>8q&wZfU+ZWl7doQCHGeQ3f1UsEZuwi08cp?*o^?o~z&CY5$Mm^nWR;XFJAjpja$`AZ zSzXNKsmteE=KZ0)L*o58OxOfGm&K*BeFrgpY`)KJlQW71Rj_?$8kI2L|5;F1_(#Nv zuCnivV@C0McfofTdk$ zaygW9&ypPeM5>GH8P(-b_ibz}pOMG%-pj{Q&Xq@FNtYTGKZ*rHM?yN z$56(~*4_81&D`Oa@B{0v9R3HwKNfM|M8ZK$?2Bu-h=IzEU?C_YS>=n6a|?eMcM~oo z04U;Ao=?n5NW|->;cEcuQ1YzN_X0D)aGO{BUaXRGrCv1u@wct7hWWFmh?O1YhBUjGHpKtIuO8YBHiJLKef{JoLh7ncM0fwjG{ z(azfG<)+gUxpj?wr%0qssnA*|HwXx=_96cYy9B`U!!$nhff+C98ZE&iEi!YFwBZx$ zOn$YKz5gEH1EIy6iWh~bj=eM9!!=t6f_GeYfA%(IfEg@Ck_vTD9^A)uV zxWMDUXdn z`=FQQeMx8?f7I-!<|Pbal~4nakLPwRE+^7b{rxzkrB+YpD_p3 zf}BVxiwHADP9f;0%=1SYAWRMzx_Vx`-E#35Io^JCo8_*XckTZo_p>m z-M{^5TlRye7K`nXYcny?sSd}Tqb0p+XlqOix z9mMef{ea_CHlxSoI~J*hXrN?FX1)v+D2E}~ths`-xyKTiX&zf2yJw{*y>wDh7M*d` z?X0z}W^e24(uA6JU6;~UMiYs-xyfVHjbB(S(fHxtzA(RMB9nOM#LfySE}hzM2i`Bv zHFD!=;BXb{NFhI@8o77jS^}qygyxsB?e12}ohgGsy|y~hSk?#y(d%|GdWJY3r_Nxv zkTYjU@%rdY&NNQ08S<17wtoxx<(D!Ok<9Lnri@vs)T*QNr$t2~QD1Ki%mo~43u)w; ziR_L9dt1Y`^@*Npw{~S|==MV$P0>JS;-i`QPZB9 zJJNgvV#?iszPiALR>wUc0b7jZL?Z5t1!k0(;XoiF(q{Fl1sQ0*I$2SMXdy8BB3VZrUng%|(4?<&Ge+iSXrvxkX)*D?$HkJu#}h7HVpaFO|>-v zcRCoF9~?Pg6^)%u)~KVpq9y&K7Y^)zEc4tDWc#DIAJh`oYwJi{t$(t^L>>sDxh+rR zJ6FrszRu187E;(Z{&SO)e6o7n)EMcE#j8R+5l_WT#pqJHYmrCS8VrH?fMd6|uiIcR zsw^Eg$HTsu&6Ke_(wz;1Mul-f>8?j12y+o`OZGh%OmOZ* zFXzgA(P%K1yV1ku@R}2a+E-@EXDAN6>P5W!-8=8};`SNJ((ah57{^_o?O0XSq05NqcLn=Vt`Ga8`&7(pTNA7k-na5e<>5$tF<5V zl52k%SLS`a?U&_@H6QdZZc&*`PM8|PUGYR^xLcluTPkYQf@Y-YnP;xUf{Se(|D%3E z-B4zY=%a*gC)@My#VPEMHC#`+3nvnGYIY9-S{+DN;@OgcPq0bRMS~?=rkh409z^~% zB2dB3UBlOqB<1ywoWYnj6as<+kJTgKFhes@|AfsB`X}8Q7&LntgB1 z(5%~HNY1*$5#4BO*N=XEV0LiS6tCbP$Qhhzov>WxzlD9djys<=M$jkNf_P*pS5b@& znV17mqD6RJp}XX=l*>kDxqF^;#r7hQJ($>~jMK~wDg92=uZXpwm6gv&olOeZGdF?N;c16&N!OfrYTXYKL}c&=^F79xujs733n%tak^B zG}%x1>=Rw-M?TQJ15_@gcmF}W3-j#fki~+b&7f*ZXlBWV%Dm2-N!kOKe+;#53zm>U zU_B)u9ogj1L>7|*|0^5@Iq>rqIS}p~SXC(9n^x(iYemI0c8%m>%DGoNIBE`++>p0T zZ<*M=a-wf`Cw`SuucueA1{L-S^m~Mw zgGN@+D@5U?MrM&vE^pKx5*1SoHOA7#us+GADqxQ@<97CgMwf0f-6c;A`b78Iw`2bc zlPv;S4|$|JB&&}SdXtjrO)~orlR((P=};pIBU*oEBCv*rNSYJ$>)eUX0%+KIE;%{~ zNMscGiL>`K@4tU8y|Xou zk=C8{4SRbhIy2YJ)vz;&^s6ZLy3$u~EA}^9E%~-mrZLK3C>htT+E&^tpl_0%Z8&sY z+4K$LjWgk(uO~USKb7epn=m|T`kl^qL3{c@?@cQWc3Z#2F>q!$y<---#Klu2A4_N- z?2EVyK-Y#B3~+YEdD^?=Z43yDf@Zm#Hp?dWYra{uNiocM!3?8GYXiHL$|Zn1e`5LD z6%tu&R`a#aeGpkwu!R5M#6)(yP+H}6C)nBDRJxj?* zu**}LN!O)^eBI3r-5%?N$xV7ba%}R?3qf#}-NN?#AK|`zKjyuhd$;5w1=@u1jiMtI zT?)?X!Y1)geFtTmTVoiNi)Ss#0vCQ<(D+H-)RZSxNq%iCA4F1BBVoe z%lHr=h0tDG#wT1jE>a0LE>vvGz*LmSqkb>#UCcU=|CH9!mDBmfkV8;pJ zeez|{`$!y5<#EAOirL?Sw6|{wr%YGRepYqA44FXEV0yo@QqI)Zz0Jz{A=u! z9`=6C#uWdLOvcHlMl}!=P30lunY^#B|jm*rM-UuZt{mHLv>aB6tvY`-M?fq&Yla|m+KbTUP%`@%w(KB7iCa6-)34{ zQr(#=*F~FYZAo=!3hLg>)`wJg_R4jI3|k*k-PwY=b8L>Kx^q{l>t}N;)t$@LrFmiN z19h3qV_w+$I4sHbmNA>+|2JD7^c}Q5D1aU718x!YpZR+4XLD9b-ysb0m$uEB7oUvN zoN3~yL}g&hr@D{>!jr|IB``VQzrf!A6W%upKf?Q?^84>%V?Bs=KMB7|47V*wZr;Y; zhdjX#Q;PS=4@2^V8!zTY^5M{)iSlgE{L&TnO!#n^Jh8fvC+q`q-Itk6u)3J<&s|~N zzbu?&^UCU?j3md>b4ezox-(a)t0<^DQ&9JtOeUndvsbA*!el~v?rcHbCz(u0b?2^9 zH^yW_symmji?L-gfx2v-F}6%5zR3E0`JSm@GC|)#GC>vw$V4lCNMk2v7?&o-lBc{t z?kmU@<)Xp9Dv+ZOWw;Mtv2I4@oZGtZ6xKx!kJ-ZKV!mk(UuC}EM)NH_w{X7EX6zqQ zUF;tpyh@v~f3UjPKj0CN`a2~ecaT(<$=~hleUeM$+sQo(!(p<3TqCJmavCThbbHSdAGIIrW zX)ah@%*A`I@?4q=s*Aax?TI~iN<^+8sV*B^=!eTo_uN}OAdd0Cq5At*}R}Go0oyh`zU=6n-`R( zy-(1y_DFT_KdKLYZEIu zEszFX>2_wRhQ95F{Ru8KmU7A?>(znw4|&LuwAQZH zJ1Rs0B*#-L~dQ<7Y>7rk#s% zN76>}i5Br93?3LP8A9*Hpo$9QQ&l5WKye$406?fg)eyxKM4{|sA0q<;BSD?s4T@TVwNey1We_4qj)Y#?J}>usX6*>pC^g!P z4RyX9_K3xd-%E6TqOB%rf*9EqA$K*z$SsIW@q8~#m7B73Kl<^s zI0~69=B{~~h8{tiQZ7Ls=7qbU0;HVWfw0#h#6uzMA@d`fs1W-cFL{wlDL}r5fE~p= zOe6waIVd}^n06wGGv{=G1ktD~))%w2q1m!F)aWgQQ0ltskG^u#oFQ57^t28MjjHlk zIAv@$jSJt`mDD@6My!cjdOHF>U!Zx2t=C-piwMR06l|-n(fqxytNFWG^C!`K9TpA| zXCf6mWS%V@)_g1-wxAtboBwaldb2Q~3mIzK2DdbyEtxi(cC+T4mKBlc0e>co}$6VULZv zqxqGnf0dO;u98hD&ED>vvxO{{fWhH1q)MmlF{{~YaJY3l+em6FWU0jxG!)585TodR zyr-iSwBb#3&cep?R~vB_SZd|Q8U9?xv`O|b3xI7%i3lgoZtRX6i`PJ^PWB!uvkYMiljx8yLU+ zsYvhcUh>yVjC|Ur=(MoXv$)t39&HlA&9^jk$DxiP=BKZnJ<v0tvg!x~8_M?}T=j&JWd zu`pEIGjMjI{`$({%J%+!H=Lhc?i@ZYoC`-myXq=RPmIqE?KCP4gY83m>iD;LTaa=2 z(%+Bv*FaSzxhqGv$eyC&KbV&*DQ9UJ_dduKrJ$AMDg=dujQ~VSrDFEbGb0+9Mj=w< zRNNFP|kttE!lZ5e}_5yEvMQ7(zvS$&dbAxLp|lf6~#v2`mpYbAu|?uA_*IWqXeqiel+{o z)*PocFzUKpR=>@!qZHv5-jYwz3ik|}fNP9sK+N8%`;D)_u{V4+lZez}* zG_yR<(##6PHX~R9vQfn;+Vlh2JmqHkrww|Ns6!@c79FLJmqar~!*=NUj+@;&}cT!Kdt z5xB@aD*!Ps0%gP}N(7~%OmYfA%Am**YZ7w`-5^Ebl5-Ek03NOgMc|r^z{^zd75Qmm zJn_(?NfnjTfNZ7pBZpA`4RF) zrO^=Vu4PC?ci@cC12Xx{&O>IyxY3Nx5}*7ZgdyblF6XXGLwuyZ#Hk-ZK`9lOQy+Gw z0Ie>X&dvP6XSQ_6llIfPD(vyr0xXzO3Oi;VO@tli=T#z8xk9g*FP4;2^!h&+kH&`% zrj351%g~eSbg{b3+Zbd-^+@RL?bGhvJNSn67Ew6bQ=?V@omm|GywNmv>u5(t&iY6D zPY?opmi8YO!zr*m8GM|`HW%P_O0njCc`Gj5f)xsJg`jwB3x;Qh_G$$(ENyPm1tl0c zT)^*k;aeTpcGXwgc2!}@{<&?NefAA+Ui-|Q1EUA;A3iicTd=p@`{3Cx9UI&A?!8CX zXdU7x7dp{)J@iD%I#dv~v4TlE27eR0rD!(?r@^QZN(!*<4x@v4OqHCT*K4q&N$|lG zz^?!xY~lx1C-=Vl;dFKOn->1ghu<>V)VX|oV144$jX#_q1feiI)mY2g02u;)knIKU zf7+=7-{$59j_?s`2;0EfO2}>Cipw|fE0o0QIWF(_1sk{sZAe7C0hR*!TOnxMiqQs6 zJ4rZ5I|+!1tqjG7-Gnx9Xi7PsKT+SXUIYAl8E)@yx+f!jYaNZ<47caZe#^{XeD9!6 z5t%C?v|e>XqPwO<3smJDhuWK>q0Y#@;Y*r5Rp}v#d{>6(!3OiE4L2@g0p7%)2Lq2& z+E0W}2~K%GQD6|2m_r>0ei_UukN)MODRA~9-5@3q$&*nW@__V9Yr%j2d9T-kzMi9&4)2_Ip zPy6!i3-sWDQXjC9Ca#LFlD9AB%x;o61ZW4#v4-Wn+AuOsUO9Aju)pq5b#kA!cDk-_rm{$(A85|(Od9=e(1`W+*0hZb&eXSi zozqS``m^y~=&{fzumYsG6oj-%cr3y)C}|{fS`@*tXenGrTQEd|!Em5KJ9r26fpm7} zO~q+RhdcfQU?pk+Hppr4#*iLl|4w%yA7` z;;K1d)pPqPoed>W^c2wJYb_S3#0Mrbgavnyw~#6(W<@L`)^tQ z&@BU6O}x8CJ}u5UTztZA`cr~ZfW?EB0PTI&NC>x*o!1g0{b^y^iWpcILuNg1Jg zJqO=E8{lGi^If6h5WsF~&dEC|MLAn|jl?elsmt>Kl_&A-gc`)t5k-(FLqYfo*ocZvk#O_MZ(i@{?w(n?->|c9}m^JY~{Ap zSaF!-oA6Ha*pF(tXP!I#Niva6{d&{1!Y)Py=plk;Fl3`r9RrPjCzi+rWiDaP{O22hlI>WMtC2Y zhKyO+WK0~Pt5rgLehGdgr6Ldt4tIgm=)DUSDyv3mt?e#r>sKp$s@2xCrEjsX^U|dtckEs4X*4_g?va>oL zzVDr?8BOm+ni)-3AGJ}}sE)d{UE1Dv-L`q%aW}?>5E~4bI6xrS#3V78VpEI>Nel`6 zX#qQdV2lIA)Q|-dk@;49G4rc#&ZoVTnj06x@}*J#eJoKtDsb+)k6)M@A%wZvVr`{IvGVY4@^ z(~fIvvA2JY2}NuTytUws1=^pAM@ZR zvdB2;sXRKTVMlhGZENSe;kSmJo!f2Odln2C2*Je*j*bq;g~dX_nL%GEhWkgTJLi?Q z=S;wtNp43i<`LZ$RW2QPt6+@BvL`1rw+cLvuMpS@5a_zn6?g}5#VdK8@wiP}>*dvR zX+|sUJR~XTh!u2p_*==IfZ3-v+6=aay1)J4;WuqVYCz$UvG1n*T2&qyn)S~V&amA> z1}m*02^{^E z--`w#SK984M@56H8%wJXkUk-GpGGd6eCfVJXQxy0*~xqj z3^8pW^w=^Au&Z7weD!yIe}{L9b~EDTHA>TBOSuVU1pOl>^=xrcsiYtIKgw5?*+xB3 z{7{ubr0J%U4(2*ApEKvCS7ye{V{`2A7~5X>>oL$Fv$cQ3I{vI&fc^dL<}wcPluNi4I}l1 zugRv7@L+?}qmrjon&!69P`F_71y!msjh5zY0d{-r-YT9wj zXxX5+_|e$u4tDRw9(ma{xg0BmrwBp-TP}z;8i6^{vfKG*9-}YjXhJ`e{qarYbH~8a zc;d-=*y8+PPEI;MDLn?l6UMGnjA{_}Dzj8yc@dgzo!Jd@IuQq8yE)#K=nDGXE{h3f zNsPtTnI(K7l0pKE87iF?XN6Y{i&ePQmTtYLWB;97b4S97qdm*pRO-tyZ!0acL0`+t zZCm>GICl~F|LY3-x-7`at-1h z1Sc+e6Q@88*ZYaX#1q3&RnPSkIs~G*Di2{)6lUQb!Si?9iXOqI&L4j7LsXaL;9083 zQg#EL*mlEhRF-AyDivfY)?>MdsAM;?3@k-W6|v*MSX5N-ftC~%EE(ZKqUqv7wlrOV z9Og^WTd#2&Y^qdJlEClQo$AJW6Nz*@N)xMd2$5Ozv5-1b=2TQPgkYfpEfv0C_mh^v zvX(O-xgRIN-m&Eva=UsaP^LwPGA)WjxG$EupgNSF9uzOFiW*`JQPpHIUV#xIJ+}7v6*O5>-jjQ5E)yj-NGdiGzOB$C67y# z(TNb_DXmyAmvF3o2kk-!gErvv#qK_SZF=RwRoBGxTMk`Y_~zo#JrffPM_Kaku8ELAHwY=iYlR@&?JJVmaV`a9ypht*wH*RAMfu-LR(Gnqu`%Pa&fDDwXU2B+d40)Dy0yn;U9`~|kmrj3754pWwHfC5|3RCd z+P1$TI~wX6_BEyI#~M6|ZRz=)9V>m&p?F)SX*Lp`H7rb~`;)#JYarsQk2=khHfJ7g zgrv>rO@ta!KI?+b4q3kTOH6zx_k*9}d)$M#i%BI*5uD7g${Fer)eT|L$>j7vej^|W zj}siNROY7WjVophKo=owkA(zdRCA(yXT^1B+h_wU+hjvav?b{05?0Lidp&Mgb z2FnyvX6XTneua}JvAr3q+o)OO%B$%j2TvakRltkO;FC=WlKEUCY{ zVJi@Cxu{(PSqr{B&=2F7Au?aMy(i3dZtB@AN}dT$wk2;yDbIwm_Q+BFUtKxvI|ZBh zgX(s-+mO1@*W>cZUj?aokHgw#nAhsWc+Vn_(1G_Sh$||9mr|BBy_;-kyu7$~xqyX@ z92990Qi3I_JZ?NsiKHqS$|UZSl!!y29%Q_%?7n`m_uv3Zc7)9nR}3eYN0zSY&9<4D z*ZQ%S%1<}}=nLKx9=n6mpu>T3l^=1)iO9VLDV9=> zI6UD|WQqyPpZXN7#1yH}ujVsXX|;P-_i40~6RSI&qp~U!irCvcZ4Y1 zQ^!7BnD=y8dvkkNS)0+J@3AlHjlDxguc`3N;<+KwH?pm+g3WA~j)}N`Emkr!lO%zo zA&{ewa`B^*`2yi36>VuE%;Lv?98+oD=nDFM9j`o(3!?Ho_NOxz)$llg3ihIck@7g; z1XKNG|GYkAvl@cVSes$sC6J+Kk_puvG5~yurN~i4bCWp=c|LBOG!s_HV|3M zA-fS)Jx@C!ktwEwqHAR7a+gzS1qX6;9L0BtQxK&S^dT8rG#yyqd^)0$pr4}{l+^=p=CD20?~iU-iNaV@=>p-}Q}u0etKVQ!n?aUsDf}$G zazTr71DcNZVL4p*O=N!3Wz9{vzt)@9X-1jaA1ugoXwurtn2#as3mf@-u&U5N*tTT% z@K6Fq7-j^b;X2(J9UY4}^8*n-*lTLR{2*LDaEc4T5z!gU-?-KC=*J45x3LeHp1Q{J zo-eT_>l8EOf3TeYd43IY7##DMv`--C>=N(f67IF$NiJ7%#q)PUU|DM4e!z0?tuL9` zr1@t2>n&@h!B0%Rdo2IXsgI1|9H($8!6*L(S})uu--ABaNysS%T2J=F7mo`!ck zFFs~!7`|$JVW#z1Tk8qK$*qGsy8ZGm^UME{^G}TQ_6FVC+zyP-^U%}(it*u<9*b=w z-(PjxFiyp`agn*PZEyjLIKifk``FaM%+l=(?=`XWmcmcpXUYyA8(0|ZS!{@`T5{?d}C z_>f?Kl9cr>9MnC+*P4jJf5_*Qhs}AkoJ%A~`Kz|e_Ox;^-r^h zZoO})rKzQDp)R<5VYoZh6RkCM=i({L(EDd|AJlG{*|*PGvUxVzm^wVryOd5g9wtOQ!qQLNzroxIT|1xp14kqo~v;4BIO7d9}-9mQNglt9Giz#0Nm zEWiSaC7_sM{P-erOLhFvt^I7r!Fb|O>;4lXd>yy^ji4G|&Vp6z49 z%(%S#x0mm_?oGU1;EQ5UV|@gvUC{3ETJiXhXcvqWrCnGYmuMH*Oo$(jTgpna#IcGm z4{!wuV8#2c>I!b@i09kpM<&uE7Pe~nqTf2Y6usUMU9$SUy=_hDr)OucY0f8B&tkl& zT@T1_g}rsW8hb`|FM<^i)2E2=p0lbcjZ=0#l_sOG!X+;AXglQ(dp%Bvuy08&V0D)V zl^ls(Sl^0;OT6-Kz+bTz-r2m(=bvw$9SSuE<61|_zICdt)=c+#gWa}d4P+zUq|cU) zJN{!XdbAFDy1J6^MC?Jm# zdPrsj=HSNS1#a2GGl3!^F`EV$$jWl6@Tln=!zx-I6clk&@l^kgp}ConGw;}y$&H@g z-kNM{n(Z;6TI#Xpu>%V;cbpurd5LxEv)hhzw+;#FILre~9M%dyv`T6herPfe&w^=^ zpRu9;X>s4Fd|b@|jfz`UHWeVWT%eo@S))UL-L=Z7dkcv*DmFxRp!#rz1PC0Uc7f7-3!9gIr+o?=vWTA1EmiYaE zBk=oEYh$YK^4|e8fqqB+d*yqp>g)V>#PgY=Xl~7{%o^&cJ1Q&oCwGwifiFnJ&moF%{|GV zRdT(+F(-1a@ZM)OdG9OG8T8(*;#soMF@_?)hMw(J+Wesn?-d+_^enb1y_X=KrDxF} z@vQC%cB6P!RpxCIxj7TcvpU=pEkCRFm7cxrlFyzfJ$qZ#v*3MG-uv-OJeyU$vGnZ6 ztDY^t_jY!}CE8qm@9nIC&ykp4>Mx&P^d%?8xuU;>jY_DfzY`cF>M!8<%G&5tDbM2j zPa-dj#&G@ppHS|Hg{L}5e1+0Id|u`uRbn5%*xfM>J@#(xFlh2oe~(6a!V#beQi2!&Wm%ohl;7@-XiO)8=Zge6ZJfZI2U zF)PFez#0mUV3Zgzc9fw5mb)!?TLB-4j95m3B?tkh!6*U;CiD5lx94vc*a08*Y!YXe zwA3fPD`yGY}%>HvH|3Qpj;cbpLy`fRHY~RNFas-cW}x1@AeRp3G5|c za*HK-xl{AgnQe9c)uzm#{XWaPjcSWt9o^YGx+OMFAR?POGJeabc|LqatYN&jp{1K# z#myHSmMvDlCy;Nm=HMb;**$aDiNW6Zwu_(6kM^^^wyh`o+Xp+gUycF`SPocY6z_Q_ zmn}fia=G+nvF0n}k{D;&tleaX(-G9cWJyYx{Z1#TL(P$i=4LpnX8H zEy3n@(r)0t#D9yQn^fJMCJyUR z@?{PlSjIXX?4S}64|k%V%+SVLIM9UdQ!qo^t%;NoXNg)!D-pL)aias}R;CuhMyD__ zj2Dpb>{wXR)fav$YOK0*E2p&4SZh1sLu{Ev!@{_){{9lP(EkKn>G;GVK|+=eHNnpN zZ|v#o;5)HFqm}JE+R0@*PXvWab{;yZ*m>k3k!4&c+Icw1$IEse$AywXN}-riD8cyu zrp+2h$82xcQtNF$d@wU{%}0+8WtXq;T~IxBdAK{2S?Ry)%MW(H#D!<>27E`n2y_6;d=JOs$Ue1MIJXDjwA*BkN=OV2*QU>%5cSPkqx!A|wi!ddBuiz7|d@r^6G5+2i%6lP0 zm2vLl?-kEJz$Q1mm!ADQ{$BCy1EkyW>^gf5efgXitBU!pw%4e?msLDV^UG~LtSiO# zspzkGmikM!9<4ptKJb@(d2Js4wP%5UiRQv6fCUUk)N87((Qy7x;k%UP>rfI_hV(%G zBb~7T&eJ7}r>#oI0rxYjy6k)2f6H`RGO?Ui4WHVT>F*gh-W3|VeXMI|*Y@dn-_RBJ zJ@WCL?(Fp8vBs?1q3K0@pqRpU$(~RzMrQ zyp0W>>Nege%@b45x;A!~1cit>a_5Hdg-_U~@40d$QWwrAX3mbaH8pQZ`#O(gT9?|V zdK((Iyy-(1mMo3K+p@_PTdlKZ%-%efNhDm3oX=f9(%!pTZ-*o5%pwS#F?W>1^4EOL zG@-e4K6p&bUB&og-esfe1&lv@!gSAiUCQ-=6+C9ofqQHF6R4^Uu!3+w6Ov5$aJjeS z46v1IHE;^Thl}rEQ<6^z<{n{(l&c~VgnA7zl<}iT5C9C^Q9%b}691(rH;@Ytzg?}) zj*K3gv@ESYIBV53Ts^brNX^?W$WJ?>*6h&S>+%Iu=DVhUYTw>i)5USTQNjs%?Liq; z+6Y}C%}ejid5I%1s+BeRIi}OJtBE9*3XbN*DYqSuppdzq&}H0xrJV0;5YZP%(d%*E z)#;>zgc{}AuAO7OO3#%*-j{r2HRl~4>*}1zkIj$I5B7JCc8$gwJqhqY`ID1Qr zNLz1qWh&TW*4g3OF5$!3vzcTdkm$1dFRKlVdBV%-u|x%A{KFUj+0|k(yL5HghQ7w( z4z1cTbYBI2+!FFQy&Wk%tp>zOUzXnp+aU&ID=KEz>v0MM8ITh5rLdI(_dlq3i3qaO z41&%?Mi>SJ*dZsDrs#teE+Ak@6@!4&$f2Gy>0BjANcfJKb@aqw$IhOHwAQ}ct{uwG zt(xtt4)a{DWn}ryI}csGI5x09J7vfm7|$IXvA9M%yn&S!zo)xrWnyLj)FHQL_sFhm z2NzH59GYv}HP8>kRF;zH59JNSu$qoMS%*c$y`Pjsgg^z4WS^v7ofSz8MUOYO06FoH z>a5&@Xg5%GN}pi|QMXa!t|QUm^ST_Uai?_K!=u|GvdeqAq1U`XMRCL6>i-m#a z-@41@TpsMZYUAo8W73rsqKUCljy*yM=w z6QJh|k%K!OgiYk(YLqcj-2A0*btKCu;=v;DQnN%U^V3{-tC>A5A|Hiuw$xzGeL;bD zk4?#4%lV66d3dZHHqI~C`YHZ_Dtp(FeB=HPrlK5BZu783>4gn?P1r5OZ^LaK-18Kd zjcM3~6?{p^EBy-p9@wl(z9fAAcl>*y_g}{EaNUh__Wj~rRcCSdJos6h>u%g**!0;a zi_cc*P^v*-t9bUws%NQ>{Jm_EwQkZ!vKgq4{JrSoqZ{ALeE>o)^I8fO@A`!DuDw|B ztNaW!$aTTIbZ))AP>)p9kr$C+8}>6YyW+|HL7H5~@l7AJ+h&Md+^18}&tF4PGr`lO7V7 z_#ci2J;K{A1Y@=&3!QGmfTy<(+3MG?TFQwYcq!$v1x;-)BXnN_BLridXG2-x!p`HH2^}@$F#Z@RQDt$ z!t!toufIUyKur;SONS>)fIUI-QHgl!5szdDYAVj#ROuET`c%{=4}B8G5tRbSUvRyK z;-8uJY&HACx2JLuIqQ{0RO>e}GmkF(W|Pii&dDBm8GKc*;FObkKMb9tYJma2IoP1NQ)0{#y6IWz^T(2taslxOb{=s-r!f zZOkGw*vC`K5ICfya;{rMA|t|Ulz?BA8BCj~m|Ko@MDC(UCAjeaO>c{pbIsx_3B9$H zE{@PWpU(I?Gn975o-U=H_f)2!efZ;*dBmfG_5Jag%(lz>+ebOxkpI!P z?TUe}A=t2)wXkZt+yM3fjp{Dh`#GNHs`{kt<&GW|xE-H>zwQ-q5%_SrKcA}!xU?#@ zj@zg-ybVChsP7`e70498n<*n1MjoX=fKs2UEkdBKzlo{mgxV2B_=nUtJa0NrQrE}s+OO*#yJB)BaQmN*zpM59pS9co ziD+%~oH32cU&yUyYCum)NxyT-QgbYJ*(#{AK6%o24J zewgm(Nxr>Pptg=M6hEQN-?^J-{)p%N%887@Hmf&lF8(0EbA1DQ+4lJi`yZtLivH`? zPO~-nqi9fC`h3o5H6y78-M0ZfSnj>Ks`uO;;k}3SRI(nfNAKqzx6*x^48i*Qs0?R$ zG%xjgzyGHwS;`_w$@Sx@__NCcs#-Uo#CxFC9SYPXXHJYdoeN{zY*t^5+w{dig69vX zyHm@#+9zaW6u;$@zPV0HCYIm&9*u5BU85@8X5~r5ws*4Scz~@g+=ecPpj#h?O>UA} za*dEN<@6QaJ?%b%$DGm5W7JKONd)QiSlFOoC?=}tBD(a{vSoQ}b^P6ycXuINP>;^PZ$0+KK#R*q84v$$C(`7rEKMQQziJ|GC zPV4GJE4$l1!j4!n5%wU{EUm08eXnrzdux*XuIXuvjRyAKyP)TLrG?yV5F(<^0LMP! z6|5WJmLA+dfL?{EcyNG2228VwrIWa%W=QOg%i+aJn+0)2vMsX>R6Dum=8jlb*ZAq< zwKcLP61IiS#>9oWt+A*@r_-UVS8Jj!Sr?w@oY?L$Pu058VSl0??8-|!Yg4Y4q+Tu28y%}@kL3iQ5AS_B5gF;7k#Cj`$C@u$aY6E8gjtK=@h&};;0=x#y z`pRDXZf;1Yj<>{X*#d_b6+Y40sMCa__Ih(|^1{+$Q)-YsG?p)%HcyN;r)o4aylI#Z z9O20AN`b0EyXZ%i=);q#dz$QaHV_yVbF@st_?_s&v;UGe#FcdTlBBDF2w z+}{yfjz-dlvwM$CUOm;v#>~;oL|bMk;GXu^pWK>1p7#`fiC@FJ=@jt}tOam&ZT9XP zE8b0Y=Eb{(=LvhZ{BE6Yq&~GYnjCCR<)YE_j^16zZoA5Hzd6=1(UQqIe3L=X@vSHC zi0*@J02R;iolbb$o1~fCR4uF{9GGF?puWK|aJC`lj&*jdc+SG6A)XZjq(5MLpp0)! zgE}P=BB}QbaBj(e^5{?#J;r%N)X z>_QlZ^cCf7e-3iSi;)D11-y514YDEiGCNKN{po7b96!8z$f=6?V?6V{6cUiU%&Sn~ z5Xw^YLluFsPkUUHXT);jU}u4_w9Hs%hE|7IerR=Y-n!rcvT`fnmhQr*tUz80kY#?6 z`3o=6{})dKRJpWw^`rn@D(6U*{6*v|k&UiG{No9n4G9O~0Z!;Had>IPk;Vkt7fROF zYxP^Wsx_ip(mrC7I*s@r#P&XO4GPyn;m@H|^n{l$SEQ**>+Eb>dVU1r^0XRm5Mpu=>lwllY31b4(lsQrLdci3 zs!FR}?(nNDF=z9HX>{H;Ke0M7XPX;yU-jAA1&t;<$})>=tnk^z!sm~XdmMW94#;j3 zupkC0EZv?nAX^oyQlOk$Fjon1veWQu)W;z=iZT-tg9Df30|qpn2f1*L5`qPJ7{w^A05!0~5s>gr*b7R<4lIy=R?)P_QLd_?Nh^ zw%$7A8lSXGw(e@hpOe-Z`K-_FPqc)hul|`~R;@eChVtM3cE0e`oaig;Un?pNK8IC0 zAl;U;c%k*Qaj2>US5?9X-GD}mQ?3eho_1FCC*J)bXbc;SOUe_ONkdSiz(Tzln2j4?s7gesdcMsH0scy zscWup-}n0M6MF;d*vzA{D&VMf84QWSFNXIFn*3Vr>g>?L@rLaex&~an6ZW_*J@nA6 ztM9*UHrICa)gKw1I=VF+PY_ z$bHzyR-6@)H71A@IU^+qFQ8$Ovm;Uy^EUJT^31y_b?xx+Ylat*y*4>M&yw?Omy)}dEPT6^zh=bdq?%uT=_cV_GFqW3 zq4_OLo(|-U_)EpVk&Aw$AlyYT1hA-yzpm^_Qq&EwdZo+N0D1+4E}*u&PIfE~`!hJ+~HrPF7nQDbE$kp1PFo#TzfHygsv7WJ9+x<%Z+eZQ&VU>DGn;K8$ zU*T&HkwxP6MU34^nIa6~Yh;Qb-CD0jxCLz%`69%{$N}+Ahk(Q@mOPr`NWx-4MZ%(@ zQ#rEHcl3|!wPho#^qp_Y3ozo6BI zOat1(6c4QdCY5|;*W}C)&e|a4IVwTJIu|P?q~ysShd>kQ9q3U}-f@3Xv~&k!mHW8z zE}q|nJiD;p>#!MX1ofam4+C=R=yY}VaO=?w2xBqy;(_H^&X@e*W7^pv_2i-Pm5!~) zI=ARF9f`zrG!S3Ca-w}#wms3eYBl_kub%Cn-#I(gJDs%2s<6-281-1k96lCM$w)?u zCFfu~!p|f8^bNpu!cULSg2xelA8A4!EBbxFMr_F^N6ZtPn2$0>EAPZ2S(ji=-bW>A3`4K{;KtjC*GUQQbS66M zgHW0@yyZmnB&EMrB#*(!G0Pdah{Tq0C9%eO=)>wL~^ zxXgN%=G15gbL~uxwaj%UsL-(P5pVh$`wHYC#W_#~(SjXng{K$6TD2w)BS$?;EIBhE zvCCr)G?YNfcQJN9@$)I!LU8q@lC90jjuci_9F%o%VUc&7PToZkCtRB@Wwc3^C$;6s zf!p2OV@*2rdLnXDdeJo18H{gf8yh~pt$8j`_e4i$I2fJ^*S2Ot;X%K(-A^tU-m~V} zeVI@+G1E3Z?f0jK(mro6+TaSJ#gy|;al|I6U%E@NWm1s$us8U|CPSN+Azy*7t#-LR zBXhc**S-u zBKw%o(w5P>mRw~}`PI)=D(Z@GGMDFE9z24a6XHmCEN4LkA6^Qk3X|)6^9`;4`g*R(>u{I?^x4782 zm%VDEb54-z(yNseZh8=LS0wP?eMu&_$WXLV+2(=zi}$Q&bdx0$_V;J7p90W_>FZ3}6wOhqS^8%`8KdiNF(usvjGi z3`Hked-6>t$G$6D_v{?qo(_*6Kh4;1Q&Y>f1%uNzYj?LSWE%6$lPmp)2F7kW*vId)?WNy<*RuhZMxzAoeL?IAj|q;fm^g(s<3YAvPjoFrgrR0peCEC<5o$Lt5W?wgR zFBUeV^PBW4d~YuDy={7yEEy=u@uf@x$9-?e>s5}EqNWzvqr(4*J)7I?m~~;};Y!9B zTY+G`2<3a^f&j;)LW09HtZCCfSgf6!{HIQD^x9*!d^w1u+Id68(+%)qB^8@+9e zblZ$CX7yxTw$=`}9ln3Gw*~tiWb!;Fj|^0VotWH{j(W7%^1SRiXg|n3Ok2O`o`*=w zw1aOF5pyD#wpk1{#8fB@T^iIZxv=5A?I!CBy!J|8Dh3-asA9JS#t?Gusg};c_zoS) zxSKRyzbjhXuD57Zwe^<0-X^cvU1QY%W99lwn>E-I^|}l?Tf$b;)7Y~;@hL0P2}Wz( z{*#Wh-I20i9&qx+V$Ww&W5MWfawhF|#2k)nJ)b0o^MDl?ajJ`&%*7~Z0(%}}?H~r^ z`XZtZ{|-qZXWX6Ua5`A%nIf31hd>gF`OX1ltg&5fszro{Xf)O&KQ94W~6T#sI{zz`XkfZ=_ zG7eU_7m|XGsAwsSGvzQHAO|P>W<=w*;i6zkL&@j3yD=UmTD;D67BZZx;*?5qS}-?> z)2c35Z5nH9^?+UzfC1fG$=5{U4NK#Wj9g(B2f6~jjtHj@EPR?Rx3~ncAnzl_oBG%- z-I;R-5dx}1*eVI@15y#E3y|9=QZZg36=hjVWfA#CR#S9tP&AJusRP?GTIu>Vz^=F6bk-?czYY&+~sW#}^S?v7Ex*;;_6pV*Xz0*2_ zb1B?!N7&nn15q_rJ^muqV&TeJc;;mI9>F}oeUNU-5Fo!}L^wl7tHQV4OHI8na2@($<&BYZMOw-MW|=t32~M$!irzD78Hi%L)ToC_Yt*4S6O^Ok(; zO53buwyWe;lny;% z!P_VRN_Krc#1k&bD9?fN+Z5k9$t``wF~gIjVb5>8tIBf|l!6WRsJ+47z&$q(F14a0 zr##r>!nNExakm1xoCL&4@;>p#64;lgm(N1Y2C`0j3UlqD*%Ja0nVHjY>L+^?a6-lvPAKvi*?d z&`2tT8M4{!b_Re!+eodR*$X#J14D7PMy=P_aL5UvJJb~T%89$tg}9ob%mpeY#Rgdg zF-0aKp@_Jj7~gToX8AjSm2nXLy-cBZ5ZlmSuRK;_86tL}v`dwE^*K{Ri7AxYL^+{* z(WVIGi<$UIAXnh35?n$&+9KgQh#Rn>Y0y5UW-a&dtQRdQ2o?1_vB1Cvrjy z9!8N`y$KH^cUf)9!`PUjJV7ApNlNOdJbmfJB{W^7s;m4rs2CN_G_(Av;R&63auqWc ziAJVJY+WnUHTBWRY|frpA@rL)lS^j@wQ6T}!Z4BZHoDnV;c<7wQXgwSzWl4g<3%ZX z@yACX8FSgXY*ef_=xW~IF5(0sj})7w7${Mhk}-7<$`MMNHuZVV3DPBXIpfZRI{`My zKoixVQuKDRT76e`r1IbLR<6q{+MC|IYcn|z?3UC!Z}Fu%)^X*^_qn>q{DH?&)d_oR zK)Nw!X^F#DEGog`2|PqzUF`vI-cp2#da0~Y3O?~I6@^|D6RkrQ7*$)@co&Y-lBQp5; z7= zO{5W&y-hZt3g-ruid(AaYta-}`pPe0fDT|J&{v>2s&3(ZO(oz{feBzTR#3N-Te{q@ zbr@H4liUxY4g>p4drr5r*gG-R0@JEf?HXODjnzyQ_QjxKS3A8NdeKCFTWik>LipvOSk1NI6sZ~!yaW78r?S}uD; z)xet+bG7PL>|xzLoaZ-`J?bppjbchguNsg86h-4ge6y$EJquCYnpgwXtx=Q`50~S> z3uMtcq$;2u;4b0+k7P?{OK$pO!e7~U1<{vw&v-0s#`2rckF7tB{l-%GgdsRq-?7-@ zYEOa4bVx6#OH;AQnFnV6a;Dv1KQndnws3z1cWDdaE>A($xpBILzs!MC4WV$fDy>>| zOW7Hy(IYY_9W3&(6MuYD8iZ5kz%D5bHMQF znYsC{qxbLGdEKzDXV=gvi`qM#Zs|7#cLcPFaI!mWI_GAu0_MmhGm_I`sympY92&r8WrIE ziojJGT%_vW*pt`p3T1`kt#F{AoyrdIND;w+ zLJNwFfEGR^0OcMA5tC@s-1?e0NTDE0G)qRwQkD1IV#n80v;>8 zIk5kVygB@WV4MRn45HB_GC6HdF-QZC3{Lr;N2&tQw0KT{D5n^GMxzTL?~}AjJ7d!8 zL_4h!n;u4S15TUrEHW0+Sa3!$x%_nnH+oSbVrf$lRhaLKPtfhYfu>=ENcaqX={$D0$!o zLtd6}yi{To0aB006nvyi&Modb)T!Ld=1jiT-DGhZ!@itjncXxxQ)kf5sttP!cheaq zh4HN6^Nx2QOb(<_B?eQx11KUa5(X}{)N_`RtTAN1_%7%-=vDYgy_{1RRx{ot(2u}EfL#No!#=#_+?*E6u9{5P=27Yu zp=Web(RqNsCw!PV$pp`ResLu|(Rg^@%BhyYVCujA;>eNwokjhz0W=OZ?z*mEBj~qI;PMk$mMTgoC$l}PuOEr z4wn6ZfT&s!70kl%)cXfwkGngY(gglA)=2%VUvU?6zl20gt2I4LzZ=2YihVK)d}ebI@Q+U1-bM>dYV740xQZbEdlETefS}d1J69=+bI{ z$DuwGe(e_c*Zk5-&cgXyp^CU;jSNU~BDx_Kx$967>Lj(f>FC6C5vW51ewDAe$R>hT zn8h$2Nc)t`Vlo%2BLpLkaIML29r4~{o1S+x(7ziRYu{j75H7HD^6krqP?Jle?eD#K z>$d$B?QI~wStq6ib%@Bm=b`c<-z@S4P;rz1CbVRWu@c&nQp7iBG$1p_kAAx+(#969gb^ z3I)vNsrm$iJQQM2m*pLOE=-^vgaYUSPGCq`KS8wg8jgRIffe8^ue5j~m>R;&F!)+?-$+-C0>9Lu*>85UXLyB@+zq58X z3&@0j*~Rij{0r*)3H*!jZBb5Lka+nB8i5FnhFG`-FCVN_`4sxcQ~;UNNv!G+20(&J zzVi~7`2o!1Qt+!B3sn*-oc0(3kFUD&dFu_}IzC;>l{>>JritMgUa)XduHo5n7hhJ0 zrHR>Cmh`Z<$^RMv;^X`i9;le2b0|bU69Xh+jVy5EXnjo$=FeC(M5CMBq1VI6quXGV z2&)uR7>kDMD}2eE8JWl%OGgY?Pl_j5giv+dz)SLjB8fHVDg4P`&@LHDRsK2wm@V-Q zll6f977p!GtA|&H)#{Fcu|-D+^vQvsIc3V`WmU*`o8jOVTg<`Ig@1K4Sh@yxtlUTB z$u)CoEhkTY`*k;AAAKFd?focwZ;@8nH`nhYab3QjU*C-k042Y$xX+a98L)j>q`e!T z$NVBIo5xV~65l?VmorCN+PT4gBg?g}?b1 z(v8KuoH_{{vEZ*|#H|b4uAGwt7Ug{~^{PE4O!B&|o3ZO+btC1T=jN1`57B?K71HyT z!he<0b3U~eB(-j;E2;HKk)U(--b(!~qm7TUf5UqA0hxoCF;rL{nhlir$Zw`)%}@T& zaY&1x1%i5xv4&XGZnFbz5fosoV!lwxVHJ&P%Rr0w59lrWca$48A0HTQ?C3kRV~2mT zzp2@23uIQpR=-(kUe|PdAT>IYz9Ql^c|dnVvh%ah5#ZW|59J^5g}nB-_zaEFF73&! zxUtY7@L3vN)<#2wCYudYKd}B`W>SrGt%hkyfheq|0#l@f^pXi8tVpyc+XXEzGy^nX z>CDe94q=1R@GG8l*oS0(Cxs7yycdw3`{oWGS?F4g)e7JG@2nu`CBJ&Y6gG|2x{M8n z2NE;EvBi#5=0a|Cd?FSN#mUh=%bo1QOGDN+SIQYOhZ?56bz^aNFpTb^^*Q|U3~Vfq zG?_Eh!hWaPV5A#JLsBT6AH>cU)0qOuQ^Zuk*gc`F+0`82$#~ooPZrM)!rNNTal+{= zw=i;~FPjceM zb;IRzI_Vv7-^W`tzdAA1J9wEv zY0$vFI(B*CX*6=?SU1MXxn^VEg=`L!Er#q4R%eEcsTa0b=@`vbN68io_y}f7YNKR} z4XaCsKC`Nr@w$Ls^$(SE-__n+6DZM){?^ZT+^18|ys zhi|%jbmy$oG-sw-Ae`6lqp0Jga8aOkb0cd)-C>!1k~N8ZYc#sRwQh zMxr=tYJf4N-LCj2s&HedJq=G~Bm{qS%9TPo2wYqh;F0d)(Tb*6;9kVu?u3HooBVq# zSItc{W=`)rcP@A9YJPkC{+^-1fuPAVyK~qupC2H!SVv}d`>mJlee2G-{JGKhG$)!O z(`_BeMhqp#x}C?qX_XK!v|-D@g`}c7oyFr)3M7G|OlQg7rJ)tB7H;+_)AhM!ibpR>$?4->PUOJ`h;p!t7 z*f8oH7B0TTI#BPBqJA9X!%pBm8YzUZ6?cC?t?w@zGa5-_vk9$0Jq(^(Ir2jbf*F|^ z+)eQcfh$ZRukhxaIfNA(#)`$snZtHT18oY%6)F}#QB^*xxL#qhhy}gzHpI5m7@|yD zswdq8b2b@ICy=lOS}`6^)dCPv0)KasyR>rM>Qq&jK>?0Z7*UnnBh5Oj;HUn)A_0j2 zQ|08NDv02#@2pNg!q@_K$MteZLVXgT+f_KZ@?xDdDghkFU@;-$$BRe^E`232pXhN^ zP|&EtSiVcS##sgc*a+F6t*O2i^7i?mrWjwS)Y&|cl8bJ$ZVhbE-L|9FqYV$w+_FJ| zFV8%Tc|rTZV$F4UJs5s!EohC;iZaHfvv*|=O0uY;2jx?@($A_M)Jyd!0Z`S0&Gb9( z!Tr&r)+4s{dfvCqx(yxJvR>I=FWN77SZ;*A4+4eXD*g6JpHWupipS0{*Y*^mW>D=E zATwxX?(!|tun21Srp@o;b)U`@pCQRZ_JS}Zx+@;RwxuK#gaXCEoWErAhjVz2vm#KF z{_y9Bc%qIR7_BXdSY1HD2J)afQArYsynZ)#Yy5}r;N|8};+ zT=>+Qsjy>Rm2o&N=U=Ykd3iBkI6Jlunl7_HDar}eZJ3L0X@cz)Cs0Q#Fd`}!&iz>A z9228PbgJrGUNu`?j8bJ_vDc{Nnp+T>BuaV!VBl~g9G>7})*vlJ3H^{)fc$V`4o9jU z1c!6+IT!(@f6}w`JA!*j*n#DrAp>z}YM2#snq>4{tklqc{e;dH93i0(h& z0aCN#alRd|XT|+JvdP2P@{k}z3BFI*6s7lW@-w*+@U^z& zCr3wyhkCkN23iN`(CtaYkii@j3}J9>ZB&Ld>8$b~UQI5^K*w1mRg;g?$@~S{^&97& zWCBj=BWZC`FB~i{0WKFFt30}|Uj+~o#bf-*3QRpjMd3o0N! zRD6U@q2=dC@_0gelgAUOp17c(PqS;@c3eB@tMYm1yOO6X;FdRDN(q#Ft^an~!CyQpO- zAIEv33FG*qoIApF+6EAfz$n&3>hkk1A)IMU0q)QtnsElwl?$e`fby2=78;pe&)2Au z|Dj~lVmpyB(?yT!)F|otzj-#-QF=@ZidHS)roZ=vn1c`{xnD5QeJNliQyF$yUH$pM3h*oUd!%PiY$`7Onj-o#=0H^zx8)=-WZl|95CrtX4Fsl zWQ|ch+YhsFM$8D-CarQwFBvs@;|U!?XISmzZ96w-G9blRI#5%qc*%vlO!M{r2R(pE zgU9U7s(TfMBwi$Tz`FNf{G-D|sJT5hG(9}s+XFHH}6+j(s!BRAOYkAbnJM;6ey9gWQRkFm$Nk ztkS?_Q7&mWTvD`CBQyd}qdGfhtO0mTRFHtRU@%ZZA~~FNG>yVwq5ago?g?dH#M4T} z(M=x47FH34NqvIf`G-A4I||;=smY1lFsLS5v8Q;3BS#+2vRN`JN+DL-EhBQp{K0Ww z1VE4%A0iDKZc_j!?Xq?!G8O#RSgQ8aKHX8HrF|;n>#}*w4US~1srDJcL^(9efthna zbDDxSWHqR@1Y4X0T=(crXYBK0*9~N4Lp;(xxezw$$2B$0!<`C{vB^y^&0>l}3GD}@ z&d+)9lQ7vY%X?sJG)f;9b8F$n;{f#{dlU4f@Q_w~gY8zdKoLkI7oigKRpO(lBUWks zi-SeQJ=}i}KUgvDL)3hvb6(>X%n9m#f&>*LH9DVL1}P$yc279ldC-Q$7sNh>5&sa^sc~8a;roaXVCzm&6NYk z2iXExz4@U6&VRH6u55V;oD;Q(Mu0iXs8wN9y$-7i{2j9f)OO|EOqdK%PJ_5vqu+;Z zfIy*s-h@;Klh5QM^r78mG2%q`MZokw$Yd?_glYS_F8^5zqd5^sQcjo02 zxMxS@Jyq`tGark~hw!fPOWz~ir`$7n$$O~HkMZBB>Q_~JcC(NiqkE*?m-?NxSMaX8 z_&s|sb&oX2o`VMs_3UI7&vmEIPw~&X(r1}}h8)9qo?*{P&+*S1zPAs_6VeY=9w5@% zKcYQ4k&8p#F!+tYWQUzgv3`CWl2lZJj@Ugx9t$z@5%8=y6xNF@2hbfd)WtmxW2|Fd zp6H0yJAIwqjeP_B9oNbu(r;CEJl@FP5u!Ihc;Ix6pWmhIf0eZ4v^lM6h*19P!Vj+F zL6FX`#5(7DjlPuEE{}AldxttgAiX5nbdY_WZ9~mHr_{rLyQz#f#2X|Tl(x8S7bFsi zA?}03z0P=?T+pHyMe`6u9|8-g(_QblD_rMpb9=ml4Z|Zd^G5ddmEeLWY&CV59mCCC z(@x6Q+lzM}!@FHlFMoH70dH{vK2LE;yq)3g^kR;ffCo-uG>9(A6;G7kUJL*UbR~~L z>ON%5G;hr}^mv1gW_PNosm;#5-tEZ62d3=SjLF&<4!4-L{4n}*wY(o#rULTgujPGzD?az|&*SuY?U}VM zsb}q52yWfCw)52&g)Dhhl=PzOUthaQ`uW<&87j}M9lU4}*DqR%vS#fiJpabEXPHhK zU)%obKP%7wbLsjQaQz{E{o1u{>#qYRa1s3pBldwCEAHUa5N z(tFsu(f2p3eHt)aE=LP@7uycq{dwu*?47uN&!*R%sD1D&b_*&4{h9O~_95{+`%vlm zM(G8N8(n|zrq@Gw|3&szTz}uj>*z~_S)nH9F}-0vGOmibr=782v&zLCiO^^BzZ(CQ z%@3k)xOi3`V7pX0Sc}?^sU_Clgk;)e)|2(1XhlS9(e8+i*`L7y;?&qh z;#DsH7LpzLxI?}}KH*oe)09xrJ|tVoD*8ELCKly8{a$BxC;j3xW9*A#kMN)TKln{s z@i(W&SnB_vo!w|>@i+fsjQvBgo#+yDL&Z3v?UcR^Ynh5v@u;LEW3;Qsk^0;7 z(1^!ykBa6)+GIZPd#+Ablzx$Z@@->pgG~F;S|1CpeH(joZf(b_-%(`NcS`&AjJ!*Z zs{$Y_W72b~Zn`diQq^6$US%)9Pw_LviF?<6QPu~v_IRqA426I$EBe6BW4N^JJmA~| zoN(^oT>rIOKXc)QyYGGhzeBaHYEUKi57#Ro-X^^=@pSvaxxVWzJpcS%(A{g_T(e5Q z02Tx)=wpakw*vx}jq7xtCx2jIHtUDqe&LGD71M}>U~K7~(!VJ8ayg;PlAO>@;6C#Y z3>R?U*cBPv3c0Yr0@5$pQ(V?RRd`yFmrs|*WdZyB-Rx7mQd)oEDKTz^r%Kmbas7RC zoo!uvYU6d(6&PooYtP{NmbG24{<|_S|6Y1NCp`n~!7F%w9emT@K!r$2e*7DBnMt?7 zI7-ML#Clt#Jv;Y!EnGMr3_jM{X>_!xOK>Y&Kz@&->YIn{D#0EZ)@Vn(RLNiw~Ub#;efo7QE}58{b78I<-Lp(7QGj4V%9U zZ)-W*{fq9i)Ge&Lm)6?Z!)wn$d~PYvmlqojGlh-;7(_#*+K+wBREvR}hc1x-S$9Qb zG*t^>9bOi}|NXseq3`RD969nx^*gKA$p67u@)y$U?#A0lXtA{)U@w0VXS5nA!*xgk z%MCr4MdqqBT?2)J)ZXevX>%2oxhL73b?G9mi06gh{q8QdRQSkU7ha!v{lLWFB<;h( zzsrkjZ&7vO{4&k=@vQ{18Z3NdOaSZ~;hD03f@I~p3g%fBgW1lRXpq5rV zlb~nlp0)37xaVtYw@Uv8b{-Tl#W<@_tK?<;>P0Dx)jN#*mK{yV1$V&L<0d@PEQ$)? z*reviCY@FVz^p=5HiAH5F+-t8nr~}uh=4qyyLGs2IGv2NG_?4=;4RWiVFur(;!5uk zp$meYk%JZaPs-^Bd~C=HRN$I!cM0F36#(-8IIyqA(EQ=kqubiHbf)Hl&ZIGNsCRa* zZs=gn(^40U#U@ZgaCfRdZcn#%?W|L&dkyEWjBaoG>_3e3c8nmp5<8rYcJ5v1KDnb) zo~^C-xr2=ftLx%gYIH8z(wk{;Haq8m1%ixj$DpX?uS-dJNO}(+#cgYmwhgFf&DjD#36+W%3^zFIFJ&><%K#kO0 z$$A_7-9~uVcqofpI&?v^@dCU$^jJ(nYMDwa--1)D8r~hXI!EyK9ue;+$ZtOn5@&F4 zs&ubL(@W5O9w6N8enb(AKTGBsdmW~_>IrN&PEU;=hi@0$bcmaz8o+GfAO^H7%jz-F z<0FJS^`64hEB=l~U&IIUXjfg! z$V9!HP3`n|HToldqfeJM=PvMCYu?B)-Q-B;nfAg5lgF+)(U?kVR1>Dz>DFtS*{;UOK*kPOvF2ZLz+-aR2g&nf3vJxEMZn)0iy z$M&EPw4T&5G0{Y%j$9i$K`2$l*_q>wW-#QqG*XCbBF+dA*QB(NLMV(=j9XE9H){Wi zhSAiJ58$q@+%zSo@4q}WymzQ0(zev52&CLgP*I*N(4V+0^z9^^I2?d1%GyoV7Z} z&rfgL_9Db{JL>TfXKN$nw($H#>^ta0BD0Z@#fV#|P#|{`=TzXur!++@Zv-ziLXIiq zGZeTe&9rc&@c9KB8wA`(<6RC{XQZbm+#R0GMDv$T#mB%d+uP8VZ}VjP`iD<`!Jwbk z8Ct^LnBCAc+dp{CAwkF5I=7PU%sQRp9W&d}7+{mB<{xy=-6EbEhJ4bfRcEjspbOQ< zNDJa@CUtXXcMqK1Vp&JTeF$e?^2VIJZXl#%Y_&BUIU^ec5E4ks6&@&mjGJA zoz_R%`mEle!t)1f?M{os>2@^Ovi(+fD~n!L>xi3udPBm{(d6mqxb^e?l+$W8A`-6% zJr4=Y02-ynOUSDEbTY^#WC9zePjK|Z@VS=*9P5JlV5Z9#oM>kB^r;6-gsAj zXyz|g=0=ZuoEg`3qt|=9_3o*<@Snc%`e%>QxHn?lAHujJ%Pm)D9b=G#?&>;9I>@J-KG6E{s<`|!@yg$on!dU(t1_;M)k zU!GW+-{aWozxT_x-CekN4c=*htws93S^B6@vgy}YM7j<*4*Rf^_qK9gLnd@ zk{5c@ocaCgx2e_U+P(iDY2N`K*-_;E`n{QXqlp@gMx!tzX|i$-t31jptya6*tj)o@YkTeW zt`mzBju;F!V8Gx6HtFt2Ha5l_a3A&^=?uq_d>HV-k!_4QA7IV~ujcz#^_!VD3Jc%w zv0(V#imec580iWjKJA{wQ2Xf7DSuQvKgAmXBAT{V`CU`2_In28~uhvY1Uz z*CCe?P9&Y~WE3PIv{)4!)pk46Xd1c`MRj1W@(#rT7)IPeA{J^0H=u_qpD&P%`jl z(TE1yph1H~GfAQu=sG?zDJX^tx~GhxGPOxk8-l?-;xZV}@(z_^v202g_e=6DYQNy$ z+xXunK#LDC?e_D(`+!*F*}(hVpaV7YP4xT`wGuWDpVE}BN7gbBD^7XJyKxK<$mRyU zMW);+vKy9V^3FiDOR97UWTqej6CHMlp3Pf!m49;Y&DWKHV_IESwesw*9(?dOw>WFu z&M@ccooacCx$f%H1cJW_-lNbn=gRfxeL z?RE@OrnXd+BqtV^_1Ao*T!9V39Y6<1KFw2d=(K3{K3)4Y@7Ll_BcO`umT)REf519F zf_1JVZdd`EQaMiTD8wDasQ|0wbV%$Or#PwI9IpUZG;FA>hZ#zERgr+}rW{>H^1xuW z9vRsakY6C@W!as(MwhXdA3ASicVIHwG2B}}=#6bU+|`I$7>%174(!~rv9azVy|>R_ z(O92M&i1s%lO@HQ-JUJ|{j+Y1bD(~PE~V7W|7QA?UjjcG_AxRFDH;p`ntBB9C^IB8 z8Mw45&LWG~>IIYrH!zL>)r?#lyh5SVyH3wjIk8|_jN;?IQXZ^GRg@yKlW>&5+1-P0 zc$CI;9Xe(6568}90SFa1J18UvnNBE|hevp_vAW%5q=(?Rvf0M`aaTWEvhi(BAgRHBWl{?_b!E#6TB!|=?<&Mzl%X+n6TzPu;G|JsuzB}7vfk=gz#idKd z9XQR`J4;hfx6E6AOQwa`uSPSs^Q(iRu4z;^As zY{!VjZMQ6a6rcM}S!bEJ9bZfYdaIT$2b##P^Qu~_)*{xfMnSb(@E;ZvDD5b|3z46x z0Of0yilDEiqz1gOSQO{7Qm}jge!!_*q~6Lr@wLMj)=r(;G_t2XK67%SZ)bb|x{jH) z@)C$G-hkmOsM|td(T#lFXl3kv2A} zF)pD5!J=Ry$@6C!OP&kn%kEB(dgF4=9Gz%nmaQ-6%#vRNJvjfml&HE#{7KxZY*7Xw zg(y!bX}lpjJ*q1ub2ix)rCZ%Cz5}=^WgReQf#FSPA*)J%oSr|T>FnbZRN9g$ZV0H+0MO9Q77D)@k zW=_N@{bIZ|(OOZC98hirqOmDBn_Y8yjnpZ$$@x!6ct)@`aXxlW`rekqQ>gE)s@|Pm zRb6QBad>xm9bJvJso{B|04#y529J7>lh%Y3c1cE4l&dPj*qJ=n5-eaB3oc(`$N zZ~OG@aDB_vU}Fd9y&GBw*%kvzBwY=|66SI<&SQn_p;vL*H1Z(|#E3^^B~qInBXVQ= zq2BErJ2|)iLQUOcgVnHQ8fnqo)7Q>lav8R3Skf}Esu7T|K+ zMC1rK^@8JbsgvAc-_zU4hCW~ec4MT^)YRR!q29rLo;~*ZvE9g&>5G@dlfx4y4s{>d z)PfPq9Bp65+Bk8hgS&!56MJP7uRPmnZ!z!i`w$_I11=G_^*qSo;1$&%zuJmkgbdIv8_vYV^WCG$D*@fN{724PLNne z(kXm*m6Bplz#Bjl)UHn2Nk)~%%}!3(2xUYDV8{L?)OP0l2Tt#5UjNZA4r0wMn2wryWOvhUEsrsiFzZy8JuL;_{^x?a`;w|3y9v0tPzkUg~FVqk8j|Siq zHF=pvC|(>iaxlkmN|DbF!$#3v6OHg`Xc)Qhw?}<>@vsHOB4#wTuWfkTHM48RweEmp z+mtI@CT5mC<__2!+h_X3;?f8DcXbe-sa5>y7=)8sm0jw(%(Du(%o7+8@fq>i%(FTE zfsBUgFI6kx{*U^j%umd4+GjIA$-&*3xk~-C`VPQtR_;_kv5X&mLc(Fba5+NFc%mQR z4e?>QqXwoi<9q|1RLBT+tTjtOqi3IE zTte7WpHzbitS9Mss2J|%=pDP8j%To_?7~Eph_9joG7Qd7d;w7q6n74lIQJJk%ach8 z$J4c4#Rnh@X^slVt()xFR*p*Nne$&fL5VT6qJZ;CHMsO_crqLw4~nLx@A)TdU=0(o zv(Nvry)zW$)BP>x{8h}kNP%2JBc&Wcyw2>4V$e&GQiL=-#B*{9c=S@2*zlrP z9Cj@|de%E2-ZeA0bny)JfO>3y5|5$JO5(=I^PH14>F6@@uz&?ym=v%`V?$m{ zQ<-qX%+Kd31)zdjB^SQyqOR~Jl!9s)>mPN0>W3b&#r>%b6IVLo3%*c!SA8mxe)q)a zo%V&^-H@k|7v)v)AoyyEBz_-QL%bM6NfcOiO*!gV+KbjwazG^|QhD;GyHm;gO~y~N z>hrmci*4JwhlWzMm4mevvH9ferrlfG2SSYv&C}89d3|QEXSBW~(A-#A8*r~LE}87z zFdz0+M#I&KG9-EP1ApHI{{9|vNQ2Uo?o8EGI4oe2wIp$|P)a#8Avqp|vC_aAd4iV| z%i9q{9Fa%AtX{`LIUiiW9Gzvb4&vfBnZkXqFYffZ$~@&`jp2CbVsCRg+B#EL-caJL z_PCQi)PO5*80sQv)~a_lH=ox!u(P#pqONV*T&$x`w@&N2ueGJQ71b+%<1l_afpwSp zA23A8;q$vE=a52|7cnh-T6B1yIjok?EWtBJke)B@Kzk?cKgv&Ph_8??4Qql_fJk&@ zQ-X$58vCGEG;W<(PcI(+B}RtMzO~c_o&>v)z&WPch*Z(<2=?>&=r=5N2H5>j;aWML4Ih_skUp0x z#rtrnA9kcNqh=~yRk17q4jBY{lrlcdpZYfER?WFQX2Lrkrd{^$isc6 z&E~19stA-fR95xZivCl(58vrc#@vx9yK~anIx_ybQ-syt>rU1#Ot4&848O=5)vv>q zByRSLEb!Cx!e{og^JOt`tbtbtStv1C{wbUImc6hz#GmK$VrKE<{YwDu>PDg=WK2nEE;M!lb&rJ zE}AL!AKe(8YaC8Bk8Rx2J6PAgUer9*^qJPqp6VuNEI2m)r(`NzjfxTt;X2SK&HXz( z_mp|=1vn&_`x&Or471GFwMN`RFxtgy!kh>Hw_mE?#(FiZ2}9rdJ%6ilO_(~sG&4-o zS}@4L_X*{<3{&94TQFw&*0T%)&0~#N5`vjxnASC6Hi5==Gt8RtCJMn=7^Z#oe!v?H z80f6SpD|QcNoOAHHsw7s>;Jvv)<6&7TM2HYs(2uszzl8H0|6hpIS%?DS?_6wrNpMg z-JQB0Cw$l7p`I??r;DX;VrC!zRb&#EmIs!$Jy$at5KAB6eMRf^C-pm6rw;R23us~s zU>JAWOKZXq?mohE?mQ>V-!V-0nlPu(kKCWVW|)GtJRjrvBlojzO_-~(2KO*b|C%t{ z(9b<8#r#Z|VG8EH2$(mi$Ka10UK1wQkEMD|m`PEtHgP{Mm|^mK2m5mx-@1eS%nM?N z3dTd<`bUPDHutjv<`~aw{hBb7=w~OvSdwePtOv{i9&-V|C|HYTB$3>t9z{R@WA3K_ zW)k-c_wsmV%`gRHZdLwXc}k_&s^6~(Lvd5L@!aRlFa_TNj5@>L+PpRl#`9)^5w101 zwjtr|%``9AF--iTV9ag6&k*A6NbKZ1K_4%bT)mfpq~`RdTM-YfYG0cr$!L-HP#0mY3Dz?$8fnnrXfI)`Z!H z^}3nAwYVlsImR+JQ!w{UfVq*rC9XBY6wG}N-+CYSbDbHc zpr38%=idp>;%#feKp%RWc#nD*FmE@*n7@_zapnQ#8nIhUDRbX=ARD=;em3&}X_Bxe z)BB$*&*A;{{CjAw+4nDH?hzfz&+vZNs_$ROJOB@MdcU~p`+vgS^q@E)ro=V*_onav z3hyhZy|_02-t>LSaT-y6jrX_Z-<#S~9KbPt|F%`%--P!q%FogM?fLiF{&Aj-qIOps z{43g_QQw&MA}f<%m-A8JPPGAZa`S zsF7Bs5D=+Ugfh_A)zREYNjy*hRoW%mMY}VbKEkll=j;+Xd9SetEL3UZsZ@(_$UpGFtarCy6&>;th5|eewna;tj!SK!}^uc4pu|(VA zved#mc8cai>)1f|_RgYG`_Octbc3&~rK!1D^aa}MDD;c&noC7;HF zGEE^p(O+!{CH$1p+9g%-Dzvj&tjH^rZM@nCSGI%OO02DwT##Fb+J@?qHQ}mCS`Z(S z3_I*LtAcEhl+%O!Q*nK6u0#k6E2|Q+5FuIdS#gyHbuwaehL6%R;qDnPwnoO@Xtx=K z3f)DnT|v9ks8i^1+flbSyk%%?AwF?%qHe6UVtZt=Ki1*sO>9xGQfn)R$1MFPEFqLH zv?it_jRSjk&%E)1p_$Gd7p=RfefvcNZLkDkP2lUTK7p9UHfVdlN_%UB%|cZNnw?md zEFH^b5vwnYaeW|{#gttozbxl&h($fuvT);qWidXuvK=i;M>|Y<>)J;;Mw%L79*IX$ z(Hhl)1AbJZrE++=R4Xzi2N|Av$)*hJGHn(L^f9fEOaxYt@y(p>&2D$u?Z)z0-B#Dm z{Q7uZ*Q{BaJlY@c<^@t!V})#IA77RRj&>4Z9+WB_K@dE_(JghM-SPAo*39b~#S(u7g4Q3m53qUM2O( zW$w%BOq-!UpXEdhNaBA>VX~9)FUY;H^dcoS(a66;djR~q6Lh~36~2SLC8$wkpPuM% zYi_6(cEyr{|5|njq!f5i+AVNqQ>WD7*GTsWM$;MaS0oeuQaAiKI{jb}F}{|ODaeAkOk_G;oOZ$; zhfMh?`4j*0gOXvf?Ip$@p%Ov-5BgWAuP&~4mE7Eb?EHlV5pCAuRJ;hUQj4jUO9onY zbqtt?U$>tJ^H6q-4bL8g0S1Ef;zS7?Yy)kyH@3W6v zN}WG^!C7kYm;PEjGdyD{an@2f$kdj!tJJccO9t{;komXFS+J=DY?WQgwP`Pc%5ANU z@mfTJzzxhe4q}RiusDsdZm}trYbbZ2tj>meE+i$koFdn&)G{c>IDxX$6KY)>kc;V3 z^3$v9s_(*AQ>7L0($XUM7z8Dj8O?_K6EMl>W&F0NDFd_BDliLVmiH&?v@XZip2m5n zziYfHJ*!)Msd`J7WBa<){<^xUiG)S#_)}_MG&MdNr+^FGpteV*=WSm*p<>`KO{#xX4@lym)4Gt|$~L+8V6!q;b#lX7;6vFkikeU0hQPejJAvW-D&7 zm@KPs?6tyH1LK&g9H%2pAo1V%I4HCE-H0U{$r&k6eBffc;BM`uJ%#~HjeeUjW z0t3Bzy~`5YMalCf4h{B<`K-cj@#`)+@_c=bUbpwl`E%~2YYY~uu0M}5%+B6&!C-Hq zxUXbU<1=qow92Dy+eL$X)WL@MJ>ZJ!bu}vQmhy(T9M_rFYM@n}4GIl~9lZEu$vw^G zF^aE(HY!I+w@ujo!0su+Zrv(iMs*Pby9VfDnykrLfVkc19X+kBPM8>Aq z3Bk0L$p$F}2R+o06AWBM9R&rm+BbGPtq!XpD7d|;=Ojb?ZMJAu`bd|mGLk++dr@Tm z4L_)6R!W&nkCh4wa*V2$D%|X8vvflw3CxOW$95~QSgzmJm4L& z5Dwpsw6-)gqIMx-j@ah_QAb9OZ&O%CmiT+gC7vV*o+wG%@bC@F1;f%o3ct5~Cd8Ga z5?w8g;Yxd#qbK1$vNe~#FxulwIb(IsZdX^#s-JfFy(k^rk_Awj;423SAy2DciOW$$D@t=5#6tauRY2*HHeO8*py4ww6>P2FZ(E%E}0P zE+>2>yJf;vndz>8)FB#h(VRjWH|s2v*sjFJG6BfN;Rxrd>C{<(-9~kGP*Vg%^f>Mn;tDck1yH*S{>|X9>S~mqzt2O? zYj`*Et_S(YmAD`D@57Hc?PFZw$^;%j+o*xx0PC&Hk9@=8SvrYbwvdVZUc=`f)4mTDSh-80}l+86)3W&5rMPDpylw zT}}01_kyT7mpExuwE!b`I~));gT`fwa0JZr#adN5z7m90o_tXcYzCxHP2wW4xiQh1 z?5qw^Ed}T~DC|c&BCF>}5jr5}u6xw>wUirYE58NaIh>VxfD!G;NjJqZ576X2-h3R<-x^LD~+LgHy>sb8XY;K2~OCnZtK%zo;J^p6!n{xKkt> z%6C_{Yv5e{CC*hV);p*ql+pAsiV5M;2zPK4#Y080;Giq>IMR-gjx@+zRe^G{kaZIk z35qm<%SccJ^NutyT6fD*ChXO3@{M8={_J^|j9;~7*H4%J>=rk;&tA~cQZssNd}L!R z-95UH7=P))J=@*$vDnc*YrhDL@DcaMzHT8qr{na8v#=xGyT zciLYQLKw?XuU~U0MjqhBbU6?@p-haW2l{%t6J@bLxzFb> zGh~^3LRwtj(((m`yr95OLsiFZEIjw+1vyEGektFuWhJEtkChb-<+u~Fp2G8XVOOJ8 zWO;4;hs9C6&Th^Uk@N4c@url(WnC z==3{Fy1Gi)>I*)pSaJT4?`K#+Y*{6i2IeXhU=(qvS>%xiKVIvY2MQLuTYp-udxwTXzg`w2Me5G+X z?0cc)!S_{8)u1k#b1qT{neCy`b%NNCco(QE#w4`vI6h&XBlDmLWQ` zabWubrTF2-nGKQ$f-zXSjErD&;{gXHWS0YsAt(!|!*|cy?rKj_Rk^MyuL#SUay5}| zZ=-Ykl8r|%5W9wk1`l|b{!{$-RP%hz%w);JfX)TxYBo1cO`aNi{efa{ba;O2o?<%3 zLz#Yxn^JI-r93Tpa)}orEMznANfOk+YvJXPeTdTA*@y6UAQm21kP{iZ@CbYxU^mru z!^xe}ajOT3p3mSaR`RUIN>Tbone zb=}n=S(_dp%?Dn`=PFzMh1j+ zrQNe%-EQFgXNERd~fHv5FFZ8>c)!G&=M+iaz_+q24@u9DS(&<$@-1Kn*< z&B@A!vT3M#v9b|xjj|YEFw8~HP(kRntW~x3ZBsiXsPSyQl#a2coyz4Ets~9GDzMy5lH?#3)5}a;;@_ zo#yKlE)QaaF=Q)HK-JMey_yA<(89JP2(8irC`6VC7&hxL|46MVr!({x;%8>N=4!S~ z#vl>5LFjaHojZRsjF>auZtx6tWf5KVhVr~2b9uVv@uiP^b!0$N`Rgy2b!kvVb5wbn7s5jsg1{Q5(B_<}(0jD*)YPBB&Mqv519fxQpO&GEu2;0lf zNPnmt8hchW;^el}5?o--hOiXpOK;?{iUbyvzay^+8k%(PDz%7^V3=rf8AfvfvqYkw zTU~n?lIq3cw;SAp-Fe)WlwUo-Dn&PG7cZ@%P(amKI+927w$le?jiRZVeo|zPPO&sW!k1{y&e-cey}oYo!NpNug_WvWUEG5Wo86@-Xr;cg z^c~c<656kR^RAHr?8&E*uOfiCl_>kuv=8~u_o-Zg;&li~89-^a@_B1VWUn3uz!y-{Vgz^o$VjCm#tK22M_E;sMi z^{}qTBI`$dolp;|W3{tsfBUXWSFzXk)YrI&vI{9TvsJzA*yi8owF0%g&^&(@JQ!>j z7$I512^==U>h3|>ycH;nqFAY_>4ZY#*hel;XpcmiBup{>k}H7dH7LSR!VEyJlvkIE z)KJX{18t3>F>5o(kw5|3vZ#W|j3Tx`(K~ZEYJ>P6MQ(d!w`R;BiF4yHDiHTJnh1t4 z;;D(ZUbySm5)TvUU~37ZKs932Bhdc4lv~q&cpTgGD(IsqTW+BY31lfFwIG4x1JPS5 zU8Lc<86xkcEFAGse+JI4MzZhv92^IsL13rM09UO73)jh1Qc+r(l3l4Td_NIO)Wwmr zyaFl!=A2V5F=G?Y#Q3#*8TC^Ua8E)ZFYQGt0TD`Er+2xe)k1v`fnVa>{7 zNd&>`Hj91~GK3XNNF}6rx;PB5O;BLWxL(fN6_+rs93m%g^@6RE zW!*3=t|k?XWJ}S!$y|Hp;Bw*0x6!CTqu!9KIiy*Ku4<%9)|}Nf&iCzq z?aJ!h0wO4M=|3u`hCCH%(5{4R9G z0VXb~(-<$aZv;`2vkJnUf$U0AnJtu&WyiQq_H$VWO$rpA+(_+vK78!C%NMuaf9xl_ zJ52lMTi+76Hu}m}mR|X)+&omPyPxgqWrV}*;1Jr8-7R@rx)G0#b44JT0T~i_o5T5j z84e?+THRmGj>)t1Qoa^;CX`2=QUCf5KKN8#(uKhhl5qMk~-Bf9ile0^WBZtH4FjdfHteWkUv$nP9ILWcDXw;mrP zIk^3Huf2v-M+s}sp8ilrpmmV^hI)F~@0#`q@QGvL4CD&JudHj@i+I?((!m%wNF29_ zK&Vac0t-RjE^F3Z1wIm4^$n*hGY@%S^4SRugi-|M_z0b_xWOPr?ytbY2+_ArD5E3m zruwEj+Uk;#8bT?h3p9)n6qiC~9zJ5Tb(L`RF0XoOwmBAP9OALI5t~~pDOy(L&0GZ4 zAJ*^6RpfXvuOPqFK2%vV;vEg=i~N}?qE^(L9r2VqD$8`sxDIE+0)?6BS3C{+ZBhD^ zh4eh(7nV#*BT(A})FJ~J94FvufTWqgoE74E4qL#sM6AFLTi4Uxilmt}mF1AfipW#V zkj9wS%y?PmI7kIZ=mraBZVKiwgj;caOTVVNU7_NAM-x*=hO=&isGu`Z7dNQwFIl%i zNoRwqcJaRSwa16e4uc=hQQOOg%OKL>frmV$M&K>DFTJ}8=TZo>F2*_BA#jSfC6SJ#B8b@!iJ?_y-_t9rRu^99cES*d zYk0$r(u56hl2G6&jt3SOcMR1u_3vr#+)!H^NGCd`TH1%aef1OdZR=XLMq>s#Bo>A> z%lzQZzj=JSpF%f<$K*1J{by4KmFx^>Eo2jouy-|>JZIzSZ% z(t8ODA;yADZ^d3{V>Sc4;0Q!0lMSIvxea0DBQ>)cG1rCb@pV0&LtR77jdE8Y@k)p2 z$T5tbUO$`Tu|Ik`!7AjvPp&WzG;b}xGXyq>K_%kfi3_hQo!CDxJD0xgl4AJMjwB{h ziS-?O&l6&PCd-QdqkVVKHh95kT+)Sb^dIaS?eE$SPeez0eVKpSS9bAUZ$MvqTNV>S z-Mux?-dGuxwqxA8{FmZ@w0>>c0sl>{7G`78+Ns)i6089^95ozEh@Q2n=h5X0?+2si zH>5X&OVcfI7Lbe--Z1T`jNKSAu#m6s{jc&Yg5;Awfnb~XCFuVy=6}fXLh-hx zbhiaID8K?O!~!9TTOsTa>ofAwaqbAIuvtLx#P%wJbahQ^&k!z{?J==0Us-txV;cQN zTF_{DUv1xxw&8WLk($tC{o+X~t>+0lTm>|JzpHrT^>ahxe$T9@@E zpZ8!sBy%??C)55?Y}WwhqrvCUQinN^7D{#P!nrA#h3xE=8z>vD zw8cThwNhYf704UK`!pLWg-Kp#K(Vj|EM#voSj%Xri^H&sL_Yzq8&X!QXf^YGNLkr! zP7=Do=gBFsfP%#@i3mJ#s=PPRI0Vzf)Z+T0+9b;Dq2Sh$mMlHXy*$b#_NZc%m|n^i z+%gk3*<_IOyI(vBdA?cMn%-Pt@O0>#2xGBX8zFguv|)=wI4`y_cBhTcH=}Sa<|>{1 ztRV5aWNkIqY$GCX7R}}x)jW|GgqV}9!L;UGwC0J>LPz~%<4}6U^TZ!K;+W?P8^wr=XvOY;273$eJ)+fSI6g*<)rH^S3xQW;?~$C^KuqP(7|6ke`XA zek~X!7c*ulhz%_m^CIGKIF`w>cD&}GsFAh5EFI==74)+MkuX2z@f5@^n&$;qof6HIMm% zwPBRE$#^--z?v|?vk~jHW-Mjq4_FJ1sk02O+0S$iW@v2~jQJrR^YEH56fgHim0}J@ z%rFHwq&Ti`sJ{lx*qSg^fEguvw#=Df%s8alafm;rdOQWS7x3cqaNO7|#K~a9pcp?OJwa!1E-JXYG1Ljeyxg zFi427*0&llcZRX-J_pQiP}7L^PC?zHf<5R!Kf4)b?OJve`}$Un-@DQLt%7y0hcdlU zyHicVLQMBZ^UCkR(d?hn#KVtp5*P4QBaR9%lQ=&J240MKUY}V|HzKDcw^elKht-St zc?i!bFRKBM=g48g@E7Od@vXVcZ@FriCO#{xhVi#9&bMWlXBb8`!K{bwe@1&4{lmTu zKbxxryqkrtTq$BacGWa?*a?tFdkL^p5*83tnCVL8ZaF5*{~DfuO3q*SSLx$Vba#(F zP?&?~|983hU&Hf9Tb@6%d}0o5n7;`g^9SCH`RCwyZm+JGTU+Mhd|QV3Wg!eCuzJiN z{lH#WryzG{m7gx5D8DxBT_AJH#(@Yn%C<cvlr~#u0HkF zcU=6)6(wmsl4u^X!sWZWtkkk7Htv7_c_$BHNYZ9QJ`hdHfb!2s2JM8crXgCPS(OMB z%zW$%*YGX4*+4!GJNb{2sfOjv%Mpno?+F_*J9a_gDRR0Q=~iMmRu?o$16rszL4*Gr zpvlRk1;SxZSMxy204ee0c!anvWQ`_ujU`)CTo-4zB8k>eRn3U(Wml7Aj6EI9F%MZ8gS*+5qyq*joy$^pXRcBZ!2lj9mhqYA)n6Dm08di>889}n5lL-wxX^CFdte1)nnxd8 zQ$^%zsz@Id?FqnuUoy<&%74l4$fAoFH|4l~ueK8~4Gc4<{6emWX)NTUky1Yl7)n)% zDuGAk7|=rfIP z`8Yq9-+e2_!{1ddRF18nA^F|U0fxVenltyy@2U@H=Z>5_#isAtl&hL~UYV!xJpWxh zuQBGE8PCH5pBpp(&6UH54!Cj{kGVVFmSMj3+V>4w`M#us%mbPA;#!_>6VK}rI!{X=<>PHM;1q#b(| z`{#P)h8)ejh0i(U{iE|ud3D8lQ7%un_G9!vZ@_F?v0ijezlFz-et2B~i@8_fIebfG zTYe5#M?9~`8I*&^yyh~0;WG$$%bn9zfXTIGnD4KE!FR=n_`9vfcj3GXy!{2w;rD^> zQvFuQ4dQ*qcLC3|P4Fq4n}Dajkv)`T64SZPeca0XiTVI6^&!W~^9O8}ZfHLFaWKrc zmyb2`F~H=;#4rQPVU)7Wx_mzv_d~+Q^LZ1V=h`YZ=RkB9}}XX&4dfU9F%P{3zWMa&na&p$yQ(F=h$Q9lahtU^JlW(QoqLEqO@Ps4=OP% z<5A^^ft2*RAit&jOZHpp*Z5m#%inqfKQ9M;c+>>G#qjvnr~!X*9-eTeL|5Rd9Q2WE z%lQ1^3Ya?Zc=LB361$A=uFJP&JO}wX*9+jYsDtLHe+u~|m@W#=qF1mV=q%boa{-LQ z)YgLMvhBm!wx)fE{GF;-+mdVha6wx-17+JsvTc`r*JAqaBWtvM^lxbUsL__r&tBgD z*q6NjpIxyJs0KnA%ePx;zTgr6&hwRRNv`L%IB$NmYTFW~J#Gt_Cvt83n3hodk7&s_ zV@&&xbGLnh+oC!O%fxS#Z9y}AOiO6Xv;?}wo?pVAhmQxw!|>Rz59Z-{&l5cDdB8JG z-O1xM=^@<56MU{wAGGJu2c2&e6JYSI+_#zLB;PXW4w=tmpXbKGFdJ6Tr5|Uk&hy9J zBhUY{JQnkLPw@x$@%(r280IgMWAJ1CI@c3GTb_RE98_1SY{ULXJ5<7%z#0DM2s2y zjA1sefSJv#&-a7=9}&DylmwpV+VXSAOu)}3j5GIL8jm>4&l7lVhJQ!|4ER5(4>Pai zae@zCwSqQ!K|e|6Sr{kt%9yDw@k-hDp=?_duas?xSIV{zLHDL{?gcH}ti1!zQQ-nO zU%->)=MYXb_`CcBFqEjpCKjytMQwq%n;Exgi#dK+g&ctAq))>?#(<~iQolHXv-2UQ z;eP(syg0q$Tj&XOHi&jXrOc-?+~*0Lqg0P0Ntu25Ti@g7efSpDs-DTM%y0(hT7Vc3%`wD20j=0 zyMxAezqI1JAHW%)@pplJ;B)>;ZkzcN#yrHlWr*9(Du-6I{U5yfEb|sJ8`#8bvC$TI z7-IaOE#n8mHsIu?f75#?E-+=MqnwNX%sVqJ0nqJaT6$ zy5&|;%kbo<_HpfQIOn7d+~2AEqvTruNaB6A?SN(j%+=J^_Plc6YR~nU^1)S}+ib7= z1;c+74qpHrt7?#86u`jHEA)#tWX$W(y2FNe1R@!99UHTD1MkMqxen18O@o{`V78{z0lc&?l0 z_4*i%7@Apry^!$^Hcbh$1u%N-Q!8PppFaa81uf-UMBP}i1)e=lvSod}X25-i;IL{t zR)JgD->Yl(_eZ0@r?@}cs{MsXr|0>D|MI?iKjUlJbL^{+tnwWDYDwa&5FY#LmHhK? zu78{DRd4=z{<}8aX6zs8XA|!q8V~lBu@C8aNB+64|F`Ui@bN0ILF2}AtDb5JdIh4K7M z{yF%|g}LX)^3TCpz)}qOkMjD`cyw@+iZMxsb0tH-bBF)=x@z+FN9lT-AVU`X9dn+*4uH8vg`!eui!cM`m0cqg9{VUj6;5&vlzT7nLE%#;>C0Cv^Vl{5nFDH1kHl{6)f`yiAtQ7Q&z(?FPW4 zU^^(>zqyl<=d%@XHyLn+`?nB|`n%kK`{#mnEQF)}E;iskU$BmaaB0k;is}9iAcW+X zcky_ZJqO_|3Zsdl)d zWk0$Ns%8E;$%Zr@;FiojIr!XjBqNj0!Gmz^hCQH$>3p(1zly9e0~m*d*_z)2h4KjX z^LC;|HLU!AX?9In1DcM6!?r!=8Fsw^$7j>pyiB{sfctX69wXg#;mdSZN#-p|F$ zBwTjgCHxBdpmiKj&q^5_d?EOI#1@nFT1Z=md=I+~>kKwM>X!Is8IjvT)}(e{qIR%4 z8SUhIYKQmm%!>X*wqqHG+ub1By{jwu70?h%Cx|k!gd@ z(X>i!`TWzPnG*)hfG<;9+Fdl(XO_>A-lHA!b9~dxC-|G#-_-6mrZ0jPnUC=Ic>n8B z!#2QsgWG|2soh{t=lyN4hSo>3 zTVo8^%dFF3yi)u7n_#i4q5rKh22X)LLUi<&6Q%>`Geln> zlb9m$YK8s@*q=+-1;DbDODjO^YB?(Dq6AEQK+iAlF#77=%WmOU={e4{j=PA&i?lph~WF z833|J0ew9t;S1K2=muvE}NdbMG`CH&k^tBqilttjTB4rV=@en#?+8`MQJ|xETxl`HaIP>@%z8Vjb zB-=nTq|-Df;TY`+4)-52olq@d?U8@5fR z*hdHF9byXO__HKrOEYu-wfmv$G*+J>fK*B^b}FuiF74mRJj(HP;R2|g3AGgs@h1e7-;;IO_}gh z++a398=-M`+H0z{l1fCvgf^(#64pwuR#sXTid6LsGmbufkx%)M@(r>!BT$E%p9D(A`|C?a0W!~c~(wNvYec4PxvG` zx{c|O+5wB(B%bNJ`RAZdJU6!+hmLwN{54^-9S4nqw%pQ!nx`$grN?SK1%41+`UB1n zn^K2`HCcu5ed_0T+)v}!x%zp&pr1ylUxocNfJX0>{m}eDqa-v|$xT}0FjK+*wF4d{6%5sx^4=x`1H-Xxn){zKGb z!Zm|Xx3eoMzgO`-9pYE$Q=^Kkh&dtvZ69K9RBa>tFH4G{suWis@l7f34%-^LgOZZ> zb|L|;{#(;Nxz``DtJ_79Rb94Y?6HMfM|5kLY`AjXs>hi3?YVhBUgqOz%*tuVf70Cf zFZ1l(G<)SHo|d@Wn6)oEYc=;GjjLJ4gobCBvUU?@E-wFnnJ3B(_jV#AwqTx%p0Imw zX@6+>EL}D82}g8OjK2C)bygkcy&hAx^G=^F7YOb_ree*8jE>it_B{~B`yMfTcH2&{ zT03&^*=_bsrP%SMu~KBEDGTIwyYpPTeHj<~=_T_!W zUrDg4yt$qBAL*hOkdLLb4U3Im#PS)>ck@|w6CS`HRj8`!Ge+1Z_5n(ZVlSLh4;GbbKxP`XA7RcJ#Y85y_h*d{cz3Chxr_8GR`3zpD`w% z5r%n?&mjpjrv#S6&>j~*;Jw+j_Z)j%yjZZun{Yat_Bhu2EcWuxLDR*^+HfdG)1N8! z;O-nW%zp@MV7Y(&*t~eLlT5klK1BGzp`(gQ?}4n5xmgw4vAy|)P&Y27oya9w>8q?x zSnOrY>4oW}qTsGzFt;Ja=2TNnqy%{zMe`xkhP1RL5?z7Pib_?pMXXi%9m!({7m#D$ zl^c7y2yulT3lBg0cYX#=nt^f?CA_*EY1OkMhXhGa{-z=4_Lh0bWx2hzY{0qAW!_r} zWt#Wapn~KKH!s^#aK=9$))$?(lzsq5guY2p67kxas*2oha-3^7EyHqIaJBt(dt-gL z##b7y5aH5-{bZ@H4cC_z`^%9h!R5tD)83Kx7TQy6ZxI`DeUg7p@}GP@cfRR4?tY0cxC z2HbN7+}ju~n$cH*qyFB^x^SoIF4lBM06Fa2>JhaVxs{8+4GZDnAd~6GaT3j;3YWV5pwJ|=BOzrX(rh@9?Z>gGY(TiM2NcMGjlbVbc|mE2bebU zN*h>VWKds(jLkS@|0*_e9AHgs40s}*LuEtJ3s&I58Qvaq)h?jK%qqALqDS2??pI#} z_LGcqjSg>#`O;!QOFO4|uueGZ?SdQ7BSk;IC1H3TH>uC!w}4H1KjJ@3>wz?#ScO@<%PV9B>yaZ4f%tp(zS4n^ zx<7XaP@l>NaOUFf8fR`fudy4aEr*OSX7ihReef6wP?cRxY7pspb9wm~lHgaAgr5eOmx3V7ehLTJQ zUV2mnGNI&Y3OTrgy+I`4PWb}Em0Pw{Zrw^hhi_5u8?M^2P_=by)xwsl;j?h=K@E(n zmG7Z%htid9N7`esbBG8?zOAXsdTccol4p|cc1gl{zB8>RVgAR(~^0Y#m~rv!ZiUWVvcVZnr4(S$-9$n=dE zeIcj=DX$-`y7=O%(GT3Bes-wx#EGh*vl0G=G9X4#z~HvOj$1zpqz`z-m%L(h#yE%W z%6P@+5X+WS4qzi7%lJv^Q&mogr^Ssp7wo818&5~1PM~0mBPLJG6%~QbeJDQS27tZP zhIPyB^H`P_iwSSJ)xI6YXkOqyROCf_y5|ku&Vd{Hj9)x{V;y!Q1qHseCf;)e!h!by zQ2yK(f$UTk&rOWyd>^ah)zn9<8z}wab3kUwz+>jE=;LzrQ}8}drejbH70tXhw5@n? zbVY~UNCoe8A9}%90tcQuaEp5Qd*3^YLto_jaVp!TSXzlRkcyQ$Bffsr0wlsHjI@0U zaxv5tzQigj+IGBDkb@ilfSBq5nw$y=t3DrPn>RiiFh2d{ll<}2tx5X)xxTMKc}0R7 z`o7qQJy`I4eeMxkn5P~^ ztc;=!s5a#^o?DbXncpkl2TyY5x3SEJOq$f-i+ z8z-!FXKX+p(p=%4l`>AQ(##BtGIxnM<(d;pV|~0e(Tb#`v5J`A=L))m$j9OoPD)Jb zH%SoY^K-4g@2-r;D=QL-iZy<#8*Af;yW9%gLO}B!U--11P1@@$s&J9At^0N&at3;j~sZQM&IQW}` zx2QKg`smqfG4EXaPlL0C)0GfH>CDLLR{*dnwi1Jx$9jX<$p^2v0v#Ow{`Yw=smAvo z0iZ((r7JLaK78!?!v|f+!-v?kPwP$T+(}?~7>CYfiziMjp1oatcW-Ymk0*;$w_>QQ zRXSt~8`fCvP2>Q^5|cfJQ5q?Hi|4e$^jZNO$Q7nd=2qNth3&3NiBn!+$srCLcyo4j zKPh>HsvH&X6?bWiI6A{hJC!m)`8c5+w^G=u3K1QL6smLqRhx>TVg&=~Eaze0`vKx+&aHGmw!h6-ovCTu~9p8SU9oQVvX`xKut_tS!}Hq%ER^H9zYf?>w7UN-rtLw z#J|G(Evvr&G-?)?D?h{gJ^A;r8E5-{7XAN@`nM$W@3Y?rb|6ZrFSwdg6XH>f>WJXK zZ?x^+9Zl~aj1NKE>I~Hl)(1L!8invY6#k&+gVBdPKijc5dim}yuWQ2XZJyoJ+}HhY zV{aLI|2WpVFY^_3tNOq2NNz{;$m`RAGQX;e&bDw6$p=$$WT8W`DI?KXwH=k!EG!%7 zn$XWjbIMA3oH*-gVyX!uA?2nOVCZ8fjzh{KfCRiq7mgIkjre4pFHr6uEiVEe2y$^X z%EhGjbWkQ({6V(mWJ&`)QQaM*3}&yNE;V$mEQOqH*bsd~gHtyyCejg`!{!b-qv<+# zxieZCd@?>6jc@9Sr4uC)Z_DjpvHKl?lFwC+-rX~ku=c-amFkz9upNv1x`6O9Rw^CvgdBM%tr;1+uvc1nC@q>%^ZAeEzJDvYmO`9_1$ zz&Iu|K8oncE34LRzif8jRJ>xSe)8y8Z8AAC=&+QfTPw=^&QeF+x~LXj9Z&Bm6MHvCJ8iS7);8aCW3hYQLx(x#BEJ){v-hBO!AH|B4@Aq9 z6NF$TO9U0OsslSx)RVFf8CWMJ*mj^eH0pkm?1UT?{47_;Dg}7;roRC^jnu@*Izf;q z;fgcps)Bx@y(pLJb94=Wzq*y~WJzhVEap$hEtgL=P4q}tYG7hq5-Q7zk(9qziwUm6 zD$`MivT59#`&>6o?e#a74^P;vlZ&R5@3DucQIbBJ_gy9~Z>syo+nlb6ba?~Ue99$y z|IpI!#N&Fl1QZ5og=nOx6OdJ_aCaEP`O~N@rgv0>y2PwU$N5uIj3QY$jE?!dMVj!t z92(?A1w4nUYnLM1qVtq<4kalYQk3RC8mSG3(3h;>hg^U1P{z*y)v4r?MO@e?n61(T zZllrv`8!j{b)1Uf1+xG3#v(oIJ?nk=uj^RklCJqK<6k{TqZjuaN)=n$b~*>{81gMf zErB{A(p%F1i~r%*Qbq{;_ubboy}V`1mZd+F#$5^9K?HMjDab0U+?OtE7De_JVJXsa zN+FlMT8aC`Y_$s{nN^^vsYgvISc;0c3J{W#AmPY9r;Q5;a3ZkP1E&5ZD+5_CjV{>*QPrswgo(su9EuN%KDnpWM^WI?8=Z|A%v(;0H-xd6eoTI z^2@d(GyeJMqfw?Eq*noxDJv~;ph^~~2gRP97EvDbxGahr^aFNI1XSTw-R@Iv5D;oZ zx3y4l==wUQq)08guBoQ<#;5^6X(Mx_R9Q$$I+GIilsveAI3N!sq=AZvn&>~#8?*Mt zW?T!NDNCQH)jjV$d|+GAksX_MH+y=+(Q?F&cn-n8j`8@i7jn(aRJBlL(pSDq=>K7uo?3a8qj@(6B#Vc>=&t@d-B9*dn)RZ{5< zgl(d*qJs0ts-S#$5tmXVMHh)ik(BbF%|Qj5QA&-<7#aU?!bo^I_;(sQ#km^qhaPRT zwH<(Q^9(AHaZ8jNZEp*~8r|F7HrPHGY7Mn=KBX$9GLej+uva;l71l!TKgb3)BZV#T zY+KAKGt2Ye*qKI|g=`{F$QUhG?>JF(^exl<7tJ&tj914`_Mi9eh2xK2E*7`=_AP9l z!%nPhcUvrWN6kB%$K%nuF1OQJr;aT-cHet=@_1i~Z`v1}yKVpVpFjGNc=P5XTZUWq z43&DyyPNM2O%nsLy3Tc76*y(o%=dBcl7bB(1@kGGvHcJ7_-9*%Skh5W&VV0m}EsixEZ60%Vi*N-LI4v&86)~ki~)^~0= z(sJ#kW8F==7q`at-P3mPQgtr6b$3(WwnMQrXvl)|2zNoC<&YA={F;?*=>-(r(>!FQ zgf+m8%%{ajCk3iHigT>U;RW%yT%6zXk_|qT;XCC3XK8L~087Eq1a5+^LYObA7DjmU z6><_dxdgHEa4XM0V=)xiKq6yOn|kq+a`eVGZFX(;Z+333eqd-WWu0*jst$e7H5t3% zP+$A&zwEj&-TW=PQ~geJYxi#dzlhiMEcV=yOxN`UI(lz+D-L3?C< z=3(`{kdrIHL+g|q(q1p5yAZ_ygYa!x3AqmP1_{U-%(EKGCqORFWdg$)H@!(kS3|yZaDr>oN^PKk>h%3L=$7rn`SZn0d zKMIS=B^$g>)hMG2nWLtH3bPiT62(|sBxmgf_b0vz=?fqhOPJ>{sN8$=2;lurf|r}D{A(@~NPy6{nG^$vlu%7O$7=0;dN zQ^{ni!bWEw$xxxnfS+m~71>cTHed9Lat>oMpvFjVB+G45hA1g%7C$4oWaH3qesSPZ zr?b_wJG#FYepTZ9+WwB@bgU}1zkTsoX`5$fcuUU{bR@+orvyn?A{t`^`mI7 zKV93~&>kqBD=9lT(~(SzBRNG}< z++GIB0zgcj(n;6C#!EEG3GG0TbOeElA+;aGYBKFs8z5P82ZQDTq!kvDWx!YFD<>;( z0kx9ik=5#SB3BKZNvOpKFKpU%>uwP0rh#~Qf8wg-`dDH^2MBd!?@)Mj^9Ju*PtV_X z%MM#2q1Vp1{p#5#MX_t6yI4%$tM)k8pSWgt?g*_j=R+VlIiw7y2T;P}6TBc^=ZgG$M|CGkWNzbvOuJab&C8pZeb)>s`(rH(3DqT0zW?6#gDeM;) ziu({Fc|z!lJEJM5aBHZDhfh)1w?%>9spbWJ`XQyYBS*rn^mdqzvvtPEvZ|pf9^4!( zsE(&YBh;-&VZ6ce?!+(Hb+F@L1WTn**iY$S*VU0~PeD>^sH+K=CId;N3-&`Ruu(m$ z7FC)QsP+ui>!c&1MU`pItJRcGYCN%6pc@F_6!GGL&2K!?({tpFo9Avh+DE_Br*^cp zY&|`(`}WN(TQ41c*Os{*!&|0OP4$hF4tk%3r{CQz+b^9Qy>v(W!X10)*8%a^OW!4pt2~XnEU^x{XRi;VjM+H6{_7)j*_lKp`)?n2-w#2BIbdP1S&rdW*I_Smp!T`Y?K`p1`T;lo(#bu}i5@za_pnMs!41U$ zx9-$XCJR^p{~zClq8Tp>z`RVHBNeU+^q2L!T^6T>Ow17aFmKK0u>~3-PYOuHeqw#S zouod1_mN7G%htb|O2OF=cd5bovoGBG#dOP{>txTuJXn~u2z75Oy8^1OJD6-JkEW`} zhbnqYN`1{Gp-6d6GOTHj=RP0*%`@@It+HZqRvdnt+E{9{l;SL5pWxkS@%C-TI zz1!{#loba(B@MCO4&tTH;l~|nIS}2FZiGD+h2h`~A@>!gNtf&vSbZ%Da?e^p;H(fy zUWIQNa?Ya&YXq%en!^v|PlL!mnC=75pet(VMBo{?;K`k6N>PzxpEE19kbYp&d0Zr^ z-POraPJfq z=2dEyT7R@8T3S*PbGSkf-N~G1(_?%z!Z@WF&datKh+YTI)>|FcK$+g<=xfy7dU|n# z!&O%1=yrEEI*MT#DDjEyLW`CUkHN~*y7U2CZRPNoMf*NHGU(i^$B$mDrw1n=Mhe9R z)+ex0n@y$Sno`E8&nV^T9!7EfX_>}^>K)w4puS;3WYti5%f?495XhPURjMb$AZr4t1eH0Us*$+oFlt6h zFK;Ba$3=%>lrT#dx$)lGK55bZ{{Ckd4qbWp*cF>pwaV`bI2_6T`SbQnj?cZ+KHvy> zw93aeURt&EWf&XY(Erqm@Y&C6=?1=mRE5KmOK@^Rsi$}?DfpS?ci>9DTxvY``h02h!VL=7Mg2sWL zT&pQn8uWv1Ju1X(S(z!Nl|AWjoHunzO)Q0KOtJ7~lefByP?71z`7$@|NtKllP$&pwCpOpmoyi7PaeVfs5+lBprIbUUU7{qf;U zoA?oM9#s;`6KQw6rUEq_YvBN*XU(aYx)mDaDTywsT#RaZaN@|;5g>~ym>A zFyf<%7_G&*n?Px?YSx>ug~09!e$C#2*l;X{dm>ao3|Ey0saSNe*X6WYl&FYO=_`;c zDXh>|d;KW23zLvE3u!WX0rIe4@BPP$Cf~`)+bu;yTkX4tyQ)S{O`Z~-z~fp)c}djo z>L{w6b2wbjit=KAZA!%Il9ycdU;j0}^e%t7ZX36`#82M)Uh-MWOi)fz+GnJos2CN-zk>6;%GTT0zAjo7HwmlB7{)2g>O{-x ze5DB=U2npYbeNau@R=+S^6iNJF*4_g^uB*BmfA_s9yE8j&r|;}e zuiN`<-`U=aR?-%gT}i7h*^*`1vW0t-O*731n`*$25C~2j4A|JF8L$c9fPs)WCh#R- zN6_uO+&{hjd402*FCxLBntEz;VX<(`+V zvu%$(me=z0)u;V^L2qMtXwZ>#y_S6qufuv$SReVataKNfilK-jWuAmEc6L7;=#X*{ z(6j^$w+hx$QgUUrJR*@-8+tC(4*U;$S2Fmf?OjV3m&SXz6YIiA+ZVpTc6Dd}kN9l% zp?-2$XGbo5 zEBkxU9{5_rya(PE@8BJEwtk13-a!Th3+ca~%Kmn}y&bX^AA3ycYxf%N3c}y~2l$Mf zxXIq~XlaQELk8^X1iN~1TIKF^=O*M3GqM2HvrX>03VES43hUl9C==T7IY@F^`6bHU zfG{Qb=teqmIOqaWHr+P}E;*S~2`=U0lIwDj#32e0cn2jOZ*sAQ(WC9vi+AlB-{sXr z$1aW(X(B7_Lwj0{Zf)`6*x+ifKRRp}c*ETMLwC*$R3E(5x?0yW-FL9%gtyrOyX5w?)JfaW%f!x9j(&3?LlQN!rC5#H#Z_^b@^pGcl<}cC|KHecOY- zt%;XpG_Bb^G(S}7OaCA~2UM7aj0wHWghaVj2XRa@Ok#!(&U5hfk-ZGNjw>7Jt4xOz z*SymH=Unb1`8-q$)%IwWmmgS~rwbpROYzFd+N%!~{(fAoY zxTN!|yB$bLG6n4KcC^(QB1P5`vpHzZzP9YbV`G`?dpO-wsl{aCP&NB#cDe3wEV-+S zEmSvZH6vPc?9yMdzYUFs{k;v@G}hUUbq28yHCwEcb|P<`Xvkc+PJSqqyIcK8UT2L^ zbGdcOD+XIpU|3!!!cg%z_4Cn7Gp>2=wA%w76;QklErh;*eCJ!2c=X=O)w3M#4>bg`lGDY8m%PSrVbKHOUu_7&SD6IQv zb$Yj7bjvM686w^M+>odC#+bPdo=>wzJJgqMoA1(UignnkPzdcqvo9_MYpm9QU3yg% z2bk!tb&O1PSNH99`+fagHgBfK#gdm^V%tn@ajUx}WU7$mHFRP2TgXGb5Xl6wk0S1d z!}usaz#wNk@c;wvgOENXr7kXISeY&RvtcmFcW1JX;2XF@@yEwu|84v&Y9Or!D++## z)MS16nZ^3wSYK%pJwG0QnbVhQVb6&n!3&+HI+NmAt!7LODHFLv3)eUaBg#6l+XOH3 zYM?)8dFor*&2?Ot+s>JTJ2QGDnTimuH=o(Qt!=O`(CF{8%}$SwMQ3ks>e^QCjQAq4 z62mq7kG_Ahx$pMb)}C?=Yn)wPneV^ziv9^**WzkTcYV4I`)?J-SwH(Fd31%M&!D@w zAl6r<6EQc#zs*r*j%NRz{(PyO_(y3t}BRLgG#fiMUKGrn$S1!Nbrt5i*FJ^hYxE-~T{{<^(Sa#L8`8uw|T1DK%6Goj%D4x z=>yaqfwPcmXR~Q^t}1&=6Y^fK|9*?Oz2z#xhhW0H#ZmT(bT@oHuajqoud)`(vvabF z3!nPb1#$GYC!f5HfB#(wjQwBfJic$wSotJKLp%$+TGF|zSb|>I#QHvUVe*35@#Jmz zk|^wBqbv=pPJ&6vc>>OV@Pvc~Y{e6XB8^#V&hdmDXHg%{(ec5$BfCeNV@a1UDhAka z_OTx}hZ=olfhJ6kziSTfItAHfrfhLMLBgIyB^uIgoSJer7f+d;F(O64Xg0!wt@R75nSmops_%%A2ys>AiRz^y^;Xv&=NT z7SMa`IdQx28uqkUc%SURb7+whg#&Sl5;M4lbQLv2m}b#nbN*w|}hVs;ejm zn635U&ntj2QY`c<>!2S)9|78a%`!Ah@*emycK(jr3!ZLz8+Rsfqvt8Ss@XH(K^7$5 zL@WwF@zZOy!XG%M#rWFXYtPBm9Irhm)0Au);6ndYq;5Pm*FmO7=KC!Blwc!o@HW8 z+1t^>Vf{NY3m{b=-&DUt9Dly%$O4A8XTMJGlGdIUmxOR-gcZWS;G*fhyxbPx_ zqnL;w*P%e!(C10B{F1FmS*z=yxv{!#7d4sn8A$onm|RzenGG@|gg;7v?JNn``2Gp8+gy`PB_>7J1l zGW1yXhtSXV0E@vyb}~2xC03Z*_+@8^MCDjmogvpexr&|rVd&Ig)6SH+a=s-!P%5tU z_1cXCCH80;W-}@7V}B?9Gv0xllFk{F7rcWfqv2!qMy989Bl9-sCT7yece3K;2X=$* zQuEHFxoT%~Tfe!m+QZ?{`{%SJb&^s&!149Q(gHEK7ZWcH2+8C->2ty<~{$!XV#x@&wKtC zdC$-0J^y6h^TQI&Q~v!gescYH?#X-pRNnLa@lWPGzcz3D6UuX5!^_|MR(by9Zw8&| zc_8okCx|_j=f1q>e{q@TpDcJz^Ot}Bi^J=`6XElbpFfrN+?V(K$;&){LU~SfK0!F; z3Vptix8D5ckLNw-{oj=Fk6q^Z=L_gVzc0`83kCC8fByJooQG=U6qlu7-9i>Ld6{w= zZ{MLI%goZnajQ0b_1Ny!;{DDmpU`WkG`fav_APtFn(m&sVI+G&eD<1-;k|pJRoSLP z%2`fgjnu0}$4H}P!Zz^MJF(^(;fBuyne1(&rNTR|!TQCUus$u4^tDOrJn5Q;i*%-SQ&Ln&*Zz#_yrryF8cLX>})K0|^}5^h6L?gymfuy(wNJv_!MeE^Jt-Bd6`=r4pAg8#);nnO#e zz_m)K8?f6M0#s;eN7X*sWuAaD4c!w!E13{!&j@771I4WdQr-^6ke-1JfZH7D1h_z; z*>C(!D41nsWdx>DR#ArB$UI0DDh1yNHUy`DQWj3?)(5wQRLTCz!b}$Na|KXDIbfBO zvQ8$`=(!w?zQSqrrz5KbNC6RUmuZ00zHwa&tq`^6v_g{g<%ebx$|!hziB1v`PP4_2 z{!fXOD-70ySbv#EEESjKU$&xDNnj>Q;4GX{>%2sG9e%h830CKFXdOPV7@6!q+(!^V z!>b-O%Sf%3G%!VxO$w3;h$WCuW%CG#6ray#^Z9)^O)`v!9xxuz!E!d;1_%#gg8y2t zQ(i~$$i6DCtgvo5hvgwNfxNb-zytEyZs2QE5j^OusQ9iAg44}yF8Z;mrPn^>bv0-kAeTsEMjwJ(cT2)~!+;5Tt2GQ2F)#{#S2ju)jEnOM;AFZE>y@?#Hu~W zt&B&*p`gDUX@u)_q^8%KAivlkqXhE~6O-9jS}E;pYozj7$~LXv|^q+^hSRB`*^Mo}GQa1y{HKaFGz13b$H zlL0UR3Jp@7-PS$-L|_pYX2_hhkve^2pB>Gp^~eK55%=EVopEjv@>H)IZOC| zHz!V7?hE21rBeAk(-(8fQSjY-Qa?5Z$hK{2vYk=x9^C^)ml>aG-R!Or!A4>c9P>nQ;a~42HSRnXB{HZ|7>n^RZ zSKwVX7xtgaQ`x3Pe5A?wI_7$VsnOozNZJEEAaYxa1p(ti)Vp8CXDdwBdY`Y(X0o)j zyWYO>IMP}CI&AmYq4ZMVoKKrD%ZtA%t>Mav-(i}@xpvw5SN8l`Fzae z!{+`eA|S3JxsDz_9K}(dbBb=D2vT06M1%(&<$?0rD81!_9=A796M&vVI*3NHvY_;P z%-t-gJ}`R6mfly8KL17J15-(l7iscdBz?NzMH=Dl;x6{L(!)5@GETsIG6m>6@V2;5w+~i$%gRSv&Z%IL>i^PyY^Ku#83X7+oXEqaM5juS2r^V+H$hT@ zPPAJZnNUYI5vu-m2LEpO4$~QT$Y~KrIe$k+~8~Q7*BU+E=wt*(-M1% z^OFe{PfWODUDlJd0e9n;QEp4I7@1_EbeI>t!Ti%hgJ~g$GzI=Pjl!oefKZ%H?Y;R| zv{aiL_LU*Z5B>=S%3{74s?@y}Z{9^!Qph`bM`IJm7!{8B03DeBxGbw28X38L3purU z?YFGoy|i{Hq^jUjR(@#H${|cPj``oLdz}+rZ{6zte|771s~dlrbxYXu8T7xpo9pgL z;ZM=s3+`1xi*UGBI!IS1-+t9Pfkq|YycQF7id9blWkmc1!s96RN-^7$O=FIAFViNm_sz?uef-9#p~wT?XSOP6-sQ=n*RxF zCZ9$^_|pH-UMJ56PIs|XB*Ff?T|i!;MyEN*4?irILeB=Q^~(*+#MW(f2ohXQI60et z5SF^%$K4vSXgKJvC`G!Aj zgnXc!Obzl0ZAmvA9q#3jhml8U!>~-8j{vD~H~%-iH3~CboJhfW>jJ&QKRbX}`Bt|4D!lZ~XY$W`9k}X^q8W)=T@JKal^KGIwRmbca1^ zn|uG^AHQK~_d|zY+Nm=YRW?>uRJ4rgwc@0%a?ixTfxe(d`s&xRuV!C)?m6aog0aN% z&h2M9K?@P_WI>t%FWBG<>&T=Hu)B4Xm@cXb>69`r?8RmakqM}(q8Jq?;o9^#O3D5T zmpVg5dR~c3SP3Xyq21F(sJdIpldrt^;MoJx=MH3Fdi(Ot+uoPUlNX;ovfbhMDc`1D z18Sx`=0y0Vz#|O7lB^a!oH2N8W}}3nC)y!OGYb$v1mK#GP8UP!zD_5>MF0;!Qu?*p zL{9WY@`q6bfRE+Xl~uW+nFM43egaYfX;I?T z@N}}j&bu=-)DdZXMJhU2-M2XJd4l%r>)p$(>6o>>^yjRuY5ru}$%Y*#yXw0@rypVO zwqx&HLXavl0Z55NT7-(_<>0_x+G1@~J4qM^aHRx2LHvMA zE)GeQ=zUd4&L+oQYf}hi7^qdG|HGy-6eocJv5drjCtJtQkFn=$*_Z3qENem<)fm}# z_n!P`<{W8XXq>I` z-~@_s$n4Rw=_{z(QNMv|PW3-Gd^yv!)%SRS2uy*m5ro*rSAr`d1N}@G9_$|<81L)t zXd~1Jx*r{-2%Ts_nY39Boy1#OsNO|p4SvQEK2b6Pfi)s0O5xz-qASRRSBu+`lZwKk zFwtK0c5km5s;hR}V@(N@Q*W@8h1{0PaDC13<6ky*j7?7Uz41yLV_n^`=|Ft!E&8s* zJ(1Y;-&>5GZRzMRwCrwo)Yc~3Dm3D-6sZk1n#}!GWBZ%B`#W29HWk~7G9zB=gw4g; zoein7(Bq$Os;=R-2IPeJ7VuiBP?MWV}}pEUZt5HvW4B(q?+LMG8Q%1bj6oPis_^EF$1fe+Q`I6GDqehFbf^#u$y9t@Gs z^%oU9rL8KPqE;i#X?qrunn5sg#%a?FE_$^HGEbp2QA!QXD zOO~Hb>`V2QRdj^9mO3jI=21Y~xBJ4Q>b2al;r3>$!`85EJIWN_{)ZCdwi0vW&J!K; zhea{_4e`8sOBY_{pVdf1=O)koIf*d3yN%*AxVyWBFUa?GSpyP$9gv`n8}>&BJ!Y#9 z`4;I2jES!Mg53d5xSn6#TkcNb#wcS82wbo+oYbw&bv5y5(C={(<^xFG&AJOntwxk7 zf||SGWoqZC0eY3(2&{f3RO@uq_}gZxy3)3wsebX0-eFYk{7zXlsHv1ygD?nS4cVLK z8UmTs-cQg zh=d9tWr#sjEM>eLgNAq^3s#xWlLwNSvhvP+o*d48Z5Z_}8P<_m9JEHQTk;^A%05Eb z&a5_@&vqn-z<2)&Ig}Lcm(@VCpImo}EReu!cz_(b+yn3zRn>>#gn}W8f~}*pQc?#u z6DPr`IFF$qh_O+snK3}k~0UF&6R6I;$MP4jNE&x7WRMwgc}>- zroN17wbU{#u7)eMTGi?RxmGJE1YI#3(hiFBO08Bd zu|p;ohux!kN&ZtKEW;c<0h-%8GRXj3)K zOZ8*BF7E1%?n}?kiJBBqlG(ha;i$7T`w;l~1+#y2pu}hSQa)X&p0G93Bp1SMS>To< z8n!T;t8P;%re?x$fU#S3drpbnCw5p(yCd|kxVpn++R=!A zIA0f|4{`bUd*u_!HAlZ9g^tv}^nIGxkOM z-VYYxni>fAg=g^ZqZdc}MrX$|W7(hAo!GVdj#=4m6LD1F1t}I#?oHF=PtR{FHPdNLlhh0rR5STP$Y(Y-Ca+8ch1#J1*Wdq+#9nyvudPlO?#V(tYfMeaC~qs6FEt zn22Oy^j!M9IC81=5W@dV2*RGAzU@ijZ8ERe!XcYg1C$8NniMh;&xOxmFp>r!o0AxW zlp#5b3h7WB)C$P5YEd1W?VCpsW);`n)QUMZc?_s9bk9R1%S6cOO>|x&326Le27n9- zvZt&>v7`0dLZR)cy6uglqe1o=Q@VNQfzktu2dtylE$lvQ`{>BRz;yH$)nz_;&GGW)Y&|qWJ6=k?BrZ`Qo!Y0Yb*H zJdRqmxX_fTF9UtpcyW;^je?6g02S)ANF&YhGw`QfsE0=`9Pu-XZ@6iwmfw(yqDUvX zN(&kx?w8ux6D_;%-`nxv-Twa8f#t@IMn|f|VypDFZmUjhyVlTm_0+9jxyfBW@Z7ta zw$+bZJPAud+`DAAZ2``KFDv<vp`~lJ&I!wm#v{l$N*F~I|0tL zGy`l#H3Z=c+wkP3Vsq#0Ouu<7(^Iu~@_cbg`^-d*xv{4`zBs`?SlQh(FwospH{Y7w zU)h%F9q4MP*#T1GeC&W-@D=eH*if-d7?K{bH5dsv&m3irPOr^kEC$n-vQi?UT)NZR zgvu>$bK*Z(^5E(2YvzyG=2v!Y8?*yk;?SOntNUgT?&upLToCAr8D4AcChQ$m3*t^B z5zeH;S_-e8s-|ORvL;mZ*9LW5bv(=ihDjVIJ%>`tK2o`Qm~yY$mRV5)>jJc0+QN;8Lvw` zXc+|Kj6gSGanPC0P1ph8Do%3j_d1>&kLXZ+GEo~4hd4TLM(F?58#Gyi)?mV5$bR3# z&K){kI{7!>^_5f@93Sh=0 zw1wv(&dZEL7qudYXc`R?VC+)H6AB4J2t5=NaX7!=^ojv-xzq@psx+;d3Ke;_lgfYT zM2-sfG$7Y_5kCOn&K+8~*>-bL>Ey^dZN8i%NkTGsUHS#l-dg?)sFIzUjxWrsWSUmU zbLkg`(sYNrIJP5mV3v?ZK;2JU|5n`zihxxhBAd`oGBiwL{L-3J*P6DT#XS zt0sSZ2m>VmYaAvY?F#4$`T(C%OKpkZi@6T^k$lII^(7DFBn11(jJ#nmtl_Q472&c! zt^72O0e%>s1o*DuI*nHN91&*(6SSAcBOEa$!kQp>xgb+Kq52qigW~Gre$ZG`1Kio% zLE6slSa#Rkf8_eRYnJXbS?Ksc1?Hf8;tJB-t&YNJrg8r+$}!)xrLJ# zZ`=FP8%J+A`QlL5vAZW%j&&ZnXL{w>FYb%YMPifHAQ}9kM6=&1e0T6O<=ibQ)4>Pv zj3^m9Oja1@CAbgua3692k-&ye3-DK{DxTc-9(SqD%u(Nvnu0q=m06@&!bK_vMj^3q z4RRPKIz!^AE&QUWBvsw?hvnTzp(J=>Mq5sl8&v9DZe zeep$BdtcpDBtBC^wC1>gd|pN%6$FHIrnwXoap7o-<2bqa3B->M%i3uXaui7J18>cT zrYx#aNT`N1JyHvyBT9Ba{>d}5BcU8vhHjUOxFr7R2WfJ#hyUW&Ww*ey&2-lImuSwx zTZ@2q^(;VZI)Q05lzp6iv7tFO7miJ4&tuv>y{R24Us$CZLBpU>N%%W)rbAi@gu7z1 zL(=P@>FGpp2kEwSOUaUxf><1XCmcc=FVGN7CAhGHEU0e2>>5j_6(;bDWDSw#L6TIZ zGb{20%%-q2-0G>c|AsR|Jv;`YR8&@B{P}8jdur*f9NAydX{IGzs%H8h!4=tq!Kopq zqqEz^zVzA)ljEX)eGeczfMrGahQPA=7c3Mt5;z2e3*sOhuhfDbvhdMDd&0#JZ56BI z_(w>1)t%OndF~J#fCiy|ic5U35R?){h(it!LRcZGvpZ}%N|#DkY%2*FA>_{w05C+1 z&+Obe^H~`m4P7dk7n0qC^DNqwA}0E*Lq zI8#~=%k9?1{l>0cEi1-pLcw}}q={AhA<~q68)1l1$`9d~naEEs?>X-M!w}l|ae!I^ z@k-tHf_@x%yN)-cu9>h-+LmlrSg$-p_F>i3+}u<)#_dB1ewp8kKf#^k7DBk!hcf+o zn83vb*ylw81)g%(iwp>iI~eGZ5kM`$7Z?v$MXFF)67q)deRMN%M4)-YK2NLu0|E@- z*9H)R7)3lej3zgmLlgliw^TWn-?HcWyIH*@WnFsf{#)*m*uI|CK^uFYxa*dM?H zJRnD%3`PKidbkktW#}(@`7h8-C@2M=9#9~38tDinTc}Vh2CQ3H$)QnI=LY4$weT<_ z*)ZpT;FdQWSjRQhp&+^Dt2=8t;hcw-La^h2`3kTA;U+<=RJ%bacdM+cxExNi#N=2j z*Vb9VRm-;Ba!X`R}8Rd+*hyQ z55iw7#eLPDX(hb^xu-`UYr&3?LWhv5t{6xZ!sUX9Iv(N+fZLX_*UDr#Jw%cz&_E!+ z5J|bXa>_erox4)TUMoFlJ?K^N*RGim4TQb6Fma6xzQ*VCADGX1%%==`R9mJ6Cq)Y{ z9JEV_f8;zMhaRpO@>>!z9I(Z-5KWN~uoeKm8x6&xA-bk`1k~wpxFd3a6jzoqwe@iK z$xd5!H5(HnGkw-cM85No;h4KURbaKDhSTXIB0`*v{PuRtHI=}qoK-JCCu8gD#VR>;j~;T zMvf1p1j#f*gDjlprmaZG#7^DxtWFE?mW07kRN{BE)=L@y)=rr>QM7l+UR^e1uocxv ztWQ@o*klP>Q5a|&*hF9vHJbC{CveuwF-f32x}69#mwP=ZUx%v)niT#3V5~;bjY&lm zR=kDTdM;^JwS6$PboNs=-;Fu96RF`xP^G^zG zR5t--RGW-p_E1DIdq8diXGB2V1VW8D5lj+FT}5T8u|CyS+2*!Y#A8L^=dITflYKfZ za`THV)|B+zTwAy%G^6OLiNxCa2PS_vc_z9uZ*5;q%nTK*E&1ViTf97!O4m0}*57}q z_45U*r2G3090onks+}^nOL07LIYI-&K?QUHT9)%uN;(?HY$VGg1c5EJAexMO&{x2UdSbz(KiCa?;rTG9CIEcFm28BiLAO&p?4hW#3dLkZ3TkaoRtodqgBCpS zl|l7_p1p1$=p8pSFNPm(`6mfCoM?%AUV`$yVx24g9eXkRNwzmT?OV4B-t!>QMmT}I zolj%m>d9{&FSnKyxM2CrFcof9!AL;;hoV(iSA|2=KLR>+J*(f~Hch8(su;RO_*~72Bm>jCY^1u$HSG%x22hx8|Pdpq1*^_xH7BW@z$W)P4t>R){T8pkW zNa=^?4^4gYhN6uEY!=*sd0$ZmE&)ahO@!S}Qp@Qc#6QLjYPovrAl*tyvP#`DYo0MI z8CJ~)jH6c{{O|+4-em8}nSsG6ad%&BZC~@@9oOG-_KS0iUCn$gYd^l2f&XnA5XcssE7^KR+&iJ3968+L|OySC{tnviuT6@-jm6V>o)kddN6&qZV@N*g^ zYt&JhHaFaAz13)&o#~stda1e5b>d4+bZq!|t~mChDg5 zuFQICC#us6jcu7CZJ(j!_)PQaOx;bV+wZ*NWIvrP!aMwH+-)}46x-y`xd#MSo#>!A zE@Q=mNf1!nY8Tm&f)C)JvE#n*c}qj~5Iov|$6;8BqCAsBGaVu>Rguqgw=|UG$VmEL zK+1DJ)Q&=Zc>HbGr>14c(@f!I1qrs+0893H$mFXP{fq~b#*v9L3fqAaa_9l{Gx=j` zvnqFfK$s4R6R@0Q#~sBoReXI^b@C6nMW}k6;d9HFu|p)e?M+d>LryjIkbKOfUXh9{ zeKn)k2A9VxeAbefdE&I&^&RWCSHC0Of4HezyG;}ioj5$6r5FbJzT@IIg&;hO@F`Ul z6H&=gpi&*mP>$%t)1WS_c_GMxirbi*BytiP(N=MB<07HNASLDk213;3<<8}n%Hf5Y z^0Erg9aeUi^}70&P_(VL|J;-PJ+(uL%2?yxEqOcUDzOvd_d$~D#tEKT2cm8w<1NDt0z7y-0wft-fx;wKw+ zk)7P1_;C(BEeFq&7gzpK(m0Gec3$Z1T&NoPCrLC~iss-q{_E(eMt0AoTeH7@Mx1pH z&P1~}@HKfr-|N8VG4j*MT}uTZj%zg)Ks?rf&S)zEY*|4=7rm<9D$P&OyBNO_6JoKb z%^r<%?;!bUa*YLXN*l{b|1>m(yk?p@J)l2n=5p8zw=vEnTe$gch9*<1@9@0~ zeFv(;@tsZE?~JmSP1#~=VlET!b=AJv_OFvQ2Muq%xif9-DcO13n9DJ3^&XtRqWe_O zP=>;Y#^={1(h#F4300#_e`4XQSi=L3aIcRHLh`X*^K@Pm>fgLDxj z%tE(G_3k$9o>&?&*AP1M3%@h{;ynlME*<+i7uF8ib*foHN^&h- zmIxN7n(vw}lm{~>GHpBK;j-ye&vY^c z*89uqmU|6W>!P)+yrVYR9+Sj|o`5$~)4#Xg<(M>kGaaegvaX(FV5IZJl~~IN7)A&B zdnqRje}`o$AtOdWc9rfBR;zZ$BV$mj1%q~<6#4GKd$@6xL~9_zdJ63xkoHI zlR3}RFE1`qGNyQQdu5Rs-muD_}$RMrC-vUg&Node*@hxF0=}7 z$e4rZ;H2u}D(X`ZPCLZAwa~~Zx0>of6q+ls8(${6K7>biXdL#Za2S$ylL>0`lxR)1 zMv$lBw8I69>=#viZ}Eev5S8i?8xYxBSB2#s0r<@V@9S^7jzpY&W0CDAT}G|NsI59T zK6|Ks@rH@IzACdeI`MA*#fjQIQ+1iC*2UgP_bYDucB9iYw_ID+mgt^2b86Q|Z|aq z7!@Lv9JC>{5mwuyIvsNZ_V%`p25*J2dTD6IveHDQBF_YvWGgK#W!)XAy55qa=dzEF z{@X0OQ)e=n?GAGQJn${(cXi@3%p%kxkLj5&s`}eAl(&J;zl*g$&Goyq7}%uWiEG!c zb+IWF+ z!XuQft!gQ=i(k0Zmkibq95#D0ZlL;NNo)76*~E{D-+?a*wRx04Y$p>U>@Bq*;E6V= z@cB^E1U(fuJcWfpUqF-veyUue6mT{OiTfx}x%O;IP7Hw<$E&-B51qSd=boPN)2~R6P0x?*Hfv3zU1O__uZV9f zZ?CRvy7ZmN;d)rD6!#cG{OCjC_Yn6$$_x1N>uU*k;+_RUE6?L#v%xPALeLxkoIwZ2 z_#|jYm~qrWOn#1N0{H1a@yFYcw{GO<7f?PRB`GIJ?F<6ze=n>VMWm}SdD?w)P{{v= zq+-jrV_&v>Iq+=@OI|hl$&vd;scjRz7xg*9&qNOjToUy6wcFOL0`lbXciiasJc2v2 zTD%0G%1sz4a{S6+-| zm47Ix#P7)~!#Vss2PKCIyWUlXe!Xh*I8a0#Mma<3fAg9gdix*$>HpEM5(+UI(6pzi zTnx~&PAfI^fjXdEGfJe7tmp`?8P%rY$||V{O`N511R;pli^O#QzV}dUc(q5Z0qg9E z4<;pmdIRFCo3X~;NWu{8zaW={)h`*Y*qgcWNRM3kE9VK4xtvqk%Z|W{ti=Np zo=BoskNo{r+snP%TY4u_UE{WfP^KzX8ObEdQrnv|v&Ms3jmzx_+RMw@g0*e>fa6eA z{TT!LDw`|PHIeFZb0XnKdf0&7RbS)ns*IKxdu-13Vg0O2G`7`^9|CWCL60Yi9^zA+ zw<|!8uZkhWXPSkn%(xjQY)z!x=`cZmW))m=g9PN-;pw4rBO^ILqgXGMP~B3^mCeB5 z@BoXrn9_(T#{^Z0~WSxQiDxt^z3Ju*J|j*r>9dSio$*l6Rz0qr%) zdOc;$wB81N<-o4Ss^JEMJ$-aAYUt~jzAB>W9(CDAY_0>_{d~n4o4tLwq~qj5({Sg3 z4vjJ1SydLsQXxf@bC2}^{~1rtAkx5u_O3r?9%7N(Mz9$ zY{t6TC&f#+DfVX22~!dSemDHZ8r-|G`7rTt1!Rv4!iifE=%_>I1$rG(1Emk-NI()8 zaZ~Ow3S7to>L|qtLP9Fp7jjj(i}Y4IHIFVZgQZHO9$|??jcQZluB6 zipGE8(qyeExUK9BOQn+tnt-lt9qeehx$WCSQ$b_VfVQ~0=l-hZzUVlsEZZ#CU$a(lw)k`aa|4hQo&1C_h&_Ez4Ya>~}*XBn&69+|04qkPrDl9O7i%)de z@Y6~aCp%V4@*;n=bbl4Dmi(jntG%|@(km9OR=mY>%XRCk73)Wf*4J7zdg*JlQsVM@ z)QM=s0Ps7)xOFp`?w}nqkfEV0dSz=wB!OceNz;O$qdP@8~^SHO) zz;>}=Vd`%`lzJeJl0J^+{~^5L7Mg`!CQXYcUi9HQEpeW}`ED9gVxx6&g zjg!o6EtP>0(xLuKxY;Rm>C5{#S5`r;et8JlGhR?zj<;+Y0Hu~PI5YxTtMVA)6XpAK z(c0iq@&huCI%tDO$ZH5NMii%zHkjsqSE8}fJscng)anhu6K22J?(;NGHye9IsXXk6 zm!h88J2F&!Eh@mAwH=aXM%RD1g~g4|VqZY7g$uT?D^z*u`Bh(IP&o_ef8c=)0{_6e zTti|3;r5}sB)9_QrZio$Qc)+#ko>~a3*q9#Y^mcCM}<%&9q}_@hh-jCB;EjeHP$ZF z;?E&f^0DrbAN^<~`#7W@MBmzstdoIHJ(T-`ndp$;#580Y#(26*ZUV%cPP^4(1F9uY z+S3wu7V}APJC9^K`TTUaYvhvjmafe1?ZK-S&P-Y-T*F;ueuF#E@=AAoW`N}W=^~Q(T2Z@&iV(dC`2Ye4)fusN1)3k|ojoluop``c<_BemJTs9bF9Z?2BZv3e#n3iq>X z#01R2F*#Ci-&pv9R`%4Cjtcr1+(zULpbkC!B9#OnqTwDbtcyu=Evz-aF6KY>9mt#> z8@IMEE{%1Yi|=3FKD^pIxiHz0t_8X9v3tbP=B~(B+(8FLHfwF;QeG(Uvdt^^WQ_^CVa%jH~SJu3$+hhDz6e6<_^Aq&J=# z7^aSd*;8E4s>WJ>Aie`$y7y&V76_RpC|S*LF!{ja@LXuMSTmgs1RtT&TnQBr7Jku9 z)P$91^+4T2Z*I9$B{{0NX23lcwfF{_2mYyF;I;P+btwSfl0iYT3^?Yktkt02vr6+x z)PzziY}s&F_rb!*1dg4MXd!4(VfgZ3NI{lCZj)ZY6+& zz&9>BVgZOY65A+z%atNUlfMm4%BlT2 zK2N$U2rs8fctQ3np!|rJO~FK?Lka9F^#Mg8i#YZbc>ofyVNvnV%S6%L4p9gZpuV|e zCbS)g1fk=q_E){!a4JkXE8qHX;>jnxR{e2&h-nm&GK2n@o8F>)0ZL6pD8zELOgtGD@p!V;-lr$K4fqmcO0lLkk7CrX+Y$f*o37yCMBT`VB>MRp2u!48qCL!p~9TgOwI z>>PC~%VQXnkJQ#FU^rUB?aFJaAgmL&qQphNu65g0G#v?wb z#i%PPRuK$>gkr>25COz>aDKvUFi8-6`+%rZqEU*Gl4O^jnUDvP;&2|$PWb6^gLMRZ z#H*4L-Cb3c2;mHMO?6MTG*xz1b;hD_A)p|eGLT?lAP{O%C7)~$ksitK05|?iR(QGZ zMDB7-im~El;ogMM#s`1ZF_IYC+gjgY)rQ*4-oCop8k1J;d2DLv^u-$;=FSpLa;_{o z)MnKK3o@2z@I~3ag1;J;7bhTJt*9gDGpXxjGA?jFh9UF-~>d#ohVmQgd!N7uurQ2Vmc8G>3FzL zsq;4<#mNgzLOz7=y9ZV0_)kIrjEo62olto=T0jYBpasy?_ihCWJ z#z0eWAvljnyn&#jHG_LXefxSEy4+h5@?6bioh=!P_0)I^sCeWA8G6yJNGJ~6vOI!}lmDQ}eWE1Ujw=UdWf1EM%$u@{)nUSb( z9Q~9R2&BWBYKz7@O17fkI?b}ryBbZ2hI)W^fc?;7c}wD0M`D=&z6@GDxG?w z%P8e~FK4$iib-0PQrTJwoA&}Xz3Nv3GDMmdJ5|O=dM7kEwH0p|gV1-p=rB3pRo0OUT~MK?9K1?(kitJXUp+s6@@Tr} zI8lHq0bRZUecCI_D%Emv2s97#DA$}?6vGW;zz=Rsk{A&TSU5Ue0dcAipWl)yh(!>| zT+_uy>pF#=d1foZ7>Q=FR5Rtu(3!GIN##s^KH~`c0PL*?cy%sfO+GNH-6eQJ=tPvR zO=3uai@Z+0zEn4u>}pAXGTmmQu-P@a-96dJ^3M?!({uTR|ICtSoG43@vbnd+i(%^xCH7S*}KUPulXwWK=$`i ze{#35@>yr8S<>;=+0^O192Y6fZ}5_*2}-qGk% zPXBDIl_a9TxFWPpz3-MW%cyN^N_Y8e@A!MjL(3inOM%`y1pVf!C2G<0?$FpF_ zIWD(Qm+Mn_Ri)nd;8LG5vFf80S4`}0+F8CUfeF5v{jNN_<$;Fb4_%dR=$OYO*J&CM zj%KVJfQqcmQnT^@C2<2n0P}tw;^x<2Zbjt6wd51KU^!UNG0?67JYocL0uqIl8cbPt zM?@9G2JtDm$d{qNGT@E!zb<7A_$$0JPU>)}lx0e3eS>s%$BJ%xabU%;o4q;YXQ#4X z4b^A2EBnhv`MXBt7r63RjMz{K!1BE20tQq**x(96a5leO(|7BTX&9hE!={m2#pTSd zU71TC#vG-!-@!lK1^+bFX;xA$onG9kP7~@8OcHKu=jt@$v2bOivfQ_+P7}HIii?^Q zb+t}22KOm1(~LtLq`?#$t3+r zg89UCg#7-3>&UITS4jUu3W8a*7gT7fnjiNtD4zU-P4=!tOUG!&;>A9h`cJ+bi2L0Y zaSu?RX6j_`ez)S!@9{^w+S+DylA+u7_K-ZHjO6Oji4YTkKRV;xr=Rmv(dr*T?%fl4Qg7e~$= z?%TKTJB8S}UjrSEf)1#U5Z;+Fm%vMvW_rPgXcc+ph#ob#l>t$r1M&;9BqRax9Ll1S zsX`SoDpi#1nug8eIc=z_61NSp(}wRLidBu;qNawb_Ud*_CDrJPM@!?BSfy`PjnhpO zA_6I3kuX`EDwXIlAti*zyGqUT`J`$X_32=pE%X#yi=s)~DV$cZ$u4t+y}w^;E2^5P z6WRSanw43?eXe)27-LOUI;-|pPPwk518*{WiHC$v(C#;&T`MfFH_0%XRW z5_BMx^FalolUJe ztz9d|QF|&GvvnuP=5j z_B(34OAcpIwT2C7^sNn!xObu^RNaO0JodGNbv=7_PhB|NA-=?Z@ma>U-M#%}hRO1t|Ol0#}b)*oAMjE;X$sMNV=fH^}Xhgm^EgjCNkgFw=Ft!pu43HE`k2r$IPS7 z1;I9n~4mVL3W{j_c2z!VMV< zB08wO*GbjL0T$?BQamw=hz^b=;@<0`8jfUC-{U7h4y~5+Q8G7-8(%p)W7ANC2y1Iv zn`=62I}!K-6^d~H-3ipe7Upf4f6WxxNotA%8O{?d*!bSM044$AAI z!-qUMoGS=sO5|6(>0}MaHL^(B%+<&y5JgnV22mr+g8;}ynmTHp8|-bUogRH~ZmD}{ z@$ZK^8Y=yxql29b_Ma1vGT^db!~Ow#6Bdx44T;HM|54>J{y@C#Cgck1?x2CPJAlkP z^x0g`7seM!wM)bjq&maHPLwP-q#&nL32=hXT&DY~hZC8K=8DSrSbAV0KGnARq<^O$ zwX*&l{+>6b27AN3}bz3ENzTz|aw5kw4bhOZa>n4M~^EJ#FM z4B_y-12GtKO!Hz{e1&Q+d~!Xk9`&#g6A}Ugs+pb8hik{}$&OrUyLWZXJnk0B*YsbI+2V^X}8OfoQ@QVz>q0Z7bLZ8CTnN@W|Yw!!5o zg=%Z^dER`~HIKA(QKNTz4R2YoVRJO5r01Hw+3%Gv%BELqT_bBsj@QL{o3hU%!uiwm zQiZd<-O1{*KZ)&+$B*EGy!3|dVJwbg^h1vz4ADmPCaN$idIT9+RU5Qh2rVUWMalv1 zJ$oI$3(rznEWnKlRNmJc4WSi{0P8&!X^b}F)lja8qhwt|&BEc1?gAMvBTeO?_(40m z3tW#(nkJz(zsq!=)?hEvMCaT3XQKoA;O7G(zg|V;kBoO&j18|y)4jWQ_j;?w%d7kL z?i#!ESa&*R>BwR7r&qdaYXHj6Y4d5WW2v-p7v5)-dD{=+PdU-V>VLGFrYM~ zyprFPEo!ut+vD{mC$6+yX|V2?8yq}*sOd`E;?ElDo60=?x)Msi(&`%9*nf@GjUO28 z99g{d-_uLtZ<~^?P(vF;TN<=Az}`zzE`%MdovbHanzb$+>`k4qyWck zES{_RmC@hHD=jBNz=%=ZKhV-OyF1Sv?Cxr;?5pwYaz!mQ$=Zq9aQ}hK?lb0S&&pk| zjMiTH#J$s_l_Lu^R~q#Lp2~&6mXk}Zr_VKAcf*-oEqEcJ1u{py&lXRU_i0z{rztmXcyX4N1askV_$-GG_=3F6ROV0^<@UGiorq(_e4K^Y4&~Z8-4%#S8Fn3a(@w3B?Dji)>hd8?3GE_ zCDSR04WzV>n~>(Fr7Qm`9AD`D%X+j&AdQ3C0bvYs79iXCpu*2(6z&ZGRQU_@eR%Pm zjeGibpR{1+d&F0j=@IWxPQ|M9G|qU3aMzMP}9o1+BwNlQ|Zd`ony1Eki{RZKRo;IY_`V9vovJAP4KL(iua2# zAue418RQfcsWFR$AmMJxX>OEy0l5b{$f_Gs&;b4cBqX||3r2FyNvSBJ9D7pqa--!W z*|-poH4u?pQJ_RrRFU1aC23&FsTyqCxeD9Bb+}No*F1^ zltllkrzt#=ao3k#;wzWf4tKzusXMy(3xz+xduH+T@O!;H@vt9Kz68puXg5j7MCJ7a z1Tad^q}((9SEl}z3JQ3hDkY&J<$|DJQ6xm+8W>-+dId!da)W-K&I9;d9lYf@dS_J5 zVo}GDgP#AWOm+1)X&43N4Ekq7Eke&*@v#nfQsz!Z)>ZGQir{fk%DbRFuQs%tzZdPe&ihpUYD z^L_vR_nE)3Effb4M4=kF_b=j}Pa*gIALKj45Jxga0*)Q*c}DpQ#j2D9Jt&?A`2MLD#_r+ zLM-=A%!Pa(Yy4oie&6)uNU$Z8s!kspFArB3afK$5HKU_Fl^wo|J#P)qw9f7s@2(o? z^e1+-b?s|IwPw4|km!rMD%<$H>);#Li$6dSYl1Aq62w|YakU^dPXhQ2{N!p1iLZ^K;lRL&sfK~_K@>|?jC))av5~{= zwF6a-#)iS2iK5f{+K=yStlifbEo+U3CWHRjnx3n7wT#t{ZI87LbRX`$o7>!0_9@(T zZsB^FtBoZvBXJ(VG5{Dc;(r)_l0z7wB0p7-!pp8T1 zU;~L2M>NKDLXKS_UvGNa2)f%~WucoH&x4Me112R=I`)X=5z@;k16oOset3wW0DFw)TZR_qOUkB_I)Ui?Cd47EhPg=t>$wtv1vQIn@ zEk5*$RQ%e1%6lbJen2&0inyVAYXBPw1zg@ZY}^+rb3|-gRoHbrUMqv{$r~o|%~IQzBt)^&SZXvU94+;_O0Ck`WR*4! z84$dE$)m+KeVrus>vhdF?((nk4kz`;7qTg=^H;E`h$rpB>YvDyrFlF_wb^G0x?3(A zTIWRN1383=LVj=`g)ldBpXB_=CeAxl$aPsC4s#vi!r#LldI(#@g}^V|Tu=b$s^ZX- z*au$fC))tPS1T=n@Is;}vI~@Bo<`M`j4ZD^AX$|)U z2MwopFTXW9I$AfquQAhavrGht@8fIlWv8J#N0BpfX9fjzMPdkg_d$iIYd-IQ9Mv~644qvt21syFS)Ch;6lZre-Pa@Wj0)}!VECT>3M;RJYrHa~7*|dOqRC!BTJ%T?4 z+EMW~Nja(!Y9d?(CFSTm6_h}4ucmrt+UF8o7gage3orlUdHUx1pK#8e1wDi*##*Ye z0?e}>ujt8?#6Y7SeaF)GM8-Re zg_p$-^%`bxn?*3&P_b$f1DtIKp14t4J9LKSX` zqq6lsg19A&$z)z{x1e=8)pq&c<24*ksq6}ssgc}Xq{Z_BFfo%@T?b#~;pIM)Q>lOP z@VCEe_{^Er%N)7*bCzBU`&r>@gL8J-OW&^VO%(q48+qM7!9QDAUlYebUv8Yr?Rk9- zs@7w8DD_h^{_F%UE)f@!9gCO}GIAy0anadQ`UqxSHH5e)Tph+s-R`hM1?G(MDm2~= z(~dSTWJBE=QWJ8>RBdUlsv9;8ca#h}nXJ!{&+qE^b>qu51HF!4Az1hoW3Q2!B&*H@ z@KCRz@EvB${E&SsJl)?Ql5xk^{ADaW$sLbysU&wyC=7&IQ5~3ZWNSblLU#s%$^}VR z%R15d@(0Sr#pMq^kmL`Q{f$&^Ny)%3YCve_y=^4XwnXg!TtxQwW zf*NPi>E8Mu@j3h!yy7#MLrOqSm>cRrG#v7njfM+r#(+nF=1OWPs+1y49GZ2Cr7lD= zV-rg1bCGF)Ur^~Au1CMSam?$UNp|)`8lE;>TzTvvs=l(`cv?YR-Vs)mgo03!tO#W82Ex| z{%^=#@x5XdGqJCshEvs(rMeweQXZkug!%sgIj>ynM&}Rg9KsT#*KCA&bfgD8-tTwgs}w|d{@*7>U@lNLz*p>Qf`^XW`Rueo_F z)_Yug^3t*U-u}9DE_L+e@jZe3c=w^6)*dw+tE%j_mTj-!0eDwUb~Q-{WuLJIgVRn>m| zeY*;eM7rBrj7EK+F*3aTVpD4Hpq>4q@Vmg|ki(oGbh7B$n^Ea^x6$ev9yO!ti-hTj zQLI>uBL0qA5Bj$WpDA5u-{99r={jmmyqm70o{pJ0#8+@X_DE3b9zG$xrwa3g494|Y zx*xS9Q00a4^iY$+C_azt&G z{x$A{|C9-R`R>kKOH*T}0h)10$_VD&Z3fw(5%A(o9(XFzODc~lN_sK*=+65$Ppt$j zpqMSG%H+}pokw2A;`T$B5#1TebXWSI=*X;>4u%1Z!DZ)Bo|zeq<hN#MV~?0Fq|8ifm$~G#ZTb?B!;m4L!(2ZBg3z?@s|ISjgS1_?6!E*UoXAyf3?jo zB9@^Bk0qWtiM@>q#MD)gc0HW;()Ia@>(g`{JQj5o#M^MaO}Gc$Md3YBOKE{HFkmqf zqJWYs$!VQ}N09^0jzb@(ygNuNh^iYd0_a zR?-_?owcH<8E~Sm;9wdR3>5W}ltS?j{t0{q;sEToiVqc~0=^MM?78m-HA9G>N*xo< zz)M3COts-KroG5qa5LN;00Pp3#R^R4J(sVCDx^U(;wf_NLMqEACYrAO1{fSyoKPIwA)lhtkpMObaEk8H>)s5%=Rs3=J zdf(4Bu0LGyJpo$O+{^Q46|LFvD;~wW@q8bWYeE7$@R^3W7>*43EM2Ts$s8KNT#1{edwC zR2tqpcXLHKD!oNc=Z$i5_S5%o-7(x5Z|*qQ7uuQ~NjHxHHZqS2s%vlP znvS!n`b;QXSZr&GP#^{Vy7gCBK^y>11-Y$Sm_=?KY!JjA;D;%>NT)W8k1b>#xg6ZD z2k^NbPt)T!CUe9+w%Zrhrx9_bC`gDfdV2aDmo>Ie9c`QL?-4tXpFHrcrHSFwQ!{iDP)ifonNDCF zs%Zy!?>Y%h2^LR{T>0UZ!Cb+H$Z&Rf3wG3P!xMURY0!YBNcHlHA*_n;rsG`G(r@j{ zZXQGUO6$o#&i<-mAP>&cTG5;hGT3+O_i{QM(U>I~H*-2HjTJTa?&oKR&i7|A_EL?% zO}W-ZqN6wir#Z@9;q<1`P#Vbwwb@UzCi8cK&lsPLf7e{3Eosl`{a@#2;eEIOy87+L z^?&0$57&w3eV=$9&Vt1AINkk}@458($bW6#b9(PTbDk)@cle7N?|p{zMCp3pe{Nj= zEWD0Uwe-IJqdMvPbke%;b*LEUz;heVeUbA_>A8s?oi`5fOst>uUf9!<}!_uEynLBA04{YpN<%6zkb&`cCXi<}Wp2H6NdRfxnaJ$^|<2Q=eSq1u{K& z5inZk6}2vsI#qVyq)y6GJ{+hSObi@s z9~$*GSb~?EpX!NhQ=hnG@|FYbHuJdAw&kYjzInCco#TZc7%U|k_warFkhHJC&HKvd z!1q=9d>B0eHuHcG%@ON~&*5))s94idnmf@rT_+lUh_4B}4(MM<=a(oPF`81($T6yp zUZl74&Mj~blpYNG+;MuT>4m1nYnE~^Yt;uRDm_N_u}U?r!d8-Ai~FT6fJ7z+4Z(Ygb(bXE75`Qnh~2R~W*oCF zSzoX&T851yOEiL!7pyO^2M6ZyXP~fyM#5$mK+}AVe9iujujz21V$Go6{}5L@_!dY)X#+8 zc_pWZkJCQr2k*g;;`M9z{^B~(?TwrEmp^xekCi@G>4(>kK}wf*xcrWzoJOU0_uaJd z?ha0)(!2W&n`l&;XL(F2{lNMa()-r;is<_#MAh^@mHJ7BT$AEhk8s+R-Zybe#YpIL z&@R>uXG$8!B)PBVslf|`M*}Rq#pFyt{8b>4_)N8PM#TxNrQeNjcZ+Qb)tkq#rketDH zb0nZt9o~L=sYB;AK6~WlTURgbZ4f&PzcB>NUs)^s6Vs-?Px53vMDNF1{1XNRdL$=* zk-u%j_bPDzFHrw@SmYXNKS?1%e9^+Wg&Z&@h*Tg87V_Ah%GDg|T54MH-x>X` z`Afd`R_6OzV{YQ3Q&<-p_{L*+zk~RZSzoyy3Y1?itqzLzJAu1Wa;}dR!WvOPF^7^6 zvJj#lGTZ4hjk1?+x_aBN#;y?yuZ*|KWG3|>7O;ao1vWL3m<7NB@xtT9?;XMS zT7_^v08qz8j{)^ZL~gHi3~X7Bjh5jHjVQTlJH6+FqTHnJn>)SwK^XNqWiNf1Zz;-B z_`YXMZFz42h6L81<~YLl?ip$CveNoX*Ga#_b<*!5o9Ioj;VfZ{(&u!RTJRi=eSM6s zLw`kuIlC6%1r4@^Qxp_fYWfI@q6j|@8DBsqc-*A_gSMc+D!D`}(UvqDl&UJtPs!{_ zuV@HbG8yeBrHRd*-t&kkH)5_g1Rf{4YiVN7NfX4Jd%bf}mR|eY`!?oEyg-`MxVn-P zP<{DVP@{Mdbm_4nk9|!+!k2(R5W-X%ugYomS$v44qbQy<GAmFTq}*tM7$?Cjp&IIOv10;H}Vcs%Gi1|8Nh9;@~kA%k5A z^pFw_{N*T)0#DkS0*JJ@>RZ@kFc3%t6VYUxPPq%nFsb$cC4WOa^Q^s`-Nnd*uXOP> zk1^+#`nD7;_iRf;T}uK>K04p3F)*{g1*?8DmS0!+4GM-1w(p?jkGI$Mbu7~sL_U-9 zvx(a2b3ec~#A^zVgA-zH_#F6NeEWjE$Z@$M&3#-+X9;V~rHXWx3OGwu@a+A=$EoNq z1zaheG(><-rCb4fn5GJ+1e>C|Dg@n7QMDADu_B%{Rk{`KqvBD-p)8sq=RX3!Yw^Vw zyBo+5)e4dv#(BaZLM)O-3AYjI12II{+)+;yt16X{0@x6T8__KNW({-0N(d-<|t9z6JHNAEW2JJajea2{ia z2Lc=t8GASwm{Uc|(JB6=oE~r#iX9h%&LBosX2RY0Idpq zX$J_3Bkcr*+a-sZ1LX#3Z7dG>N4Q?nL`XDrL9KxF+E3r|#K7qO)%mTB`L@yFrYv^k zp1vi=@BjP1S$$7S3iw^n8`)al=JfVF#sc2=d;BO~{}ty&xK8ru=M}t$K3@+pj2XO$ zKUdam6!53K4|di@tuTxl|EJ75q9w}6hLJ@+TWH#nkwvwHA&#h47>>WI6a*~0O}XDx ziX2jSoyXSins4)r)HRK!8tXfnmvZ%EC+NrAo`~Db zqk4NoED&@1``Q<_)BI?hpWyTUku-1esTHq3%jb>jH1B`eEQb~FvtWLhH-Anzb86$c z4^})k{OI}5g({!ZJ@kD%2MTaN1{$zlQQv5q8yFrJIK--m~r3(jk$<@|E zJ$w+l<+>JpEiiv2urM3;_IO*n>RP&*HRsgnhTgnoa%tLm>_GQIop?v{RQ-Zlqh0c} zH#9WNt=Pf?lZAJ&lSdBKjipx96Zz(aCcX}xbPhoSNWaO~;JBdj2M!4o@e)5Mx{xIW zj6(xummPVxnZ4Qc?j!71lamDte-G3t{vH!?n2m0lWN-3_%ow0lMVqt21FC@sRPrM6 zH*-JeW!cM2m&K%Sh@CK<$o_id@yW@@M}E!E2_G-t@1NrvaZaAyyx$R=6U+za1fK&@ zc;*a#;C$7CZYr+p9)hum7YJFL1G2>(q=UjH!qiDf?(!{&KQ^B|o^dT$@KPAtEd{F>7B0m*;Q-?y8u zqx8NZGwD36tn*0kBb`V3e8O`6_r+-4itjD@kjmdTSAK5nS9PYCZy`}hSlZ1zuW&x z`deu1_G@M;y!)FNJAW6g1=UfbwfHw_PSSO(1zo4L`1Izrkj8+uC_dLmTYr)F4bYln zEs#P*@)Ryj_#)_v#QwkoWbn3^P6QmDR*SS0IYz7sQ5%<@?ReSD_Ua1{GPNnsJ~G)? zn4iLTN$=!iN45x!op??0`nQSZpmTEG^^HwqFFprKd>jErI3$<`1v9i`lJa6(`a$55cEi(KnUAb#DqW@Rw~~biZ-{^ zM4k0a77Nx7MhM! zef+!5@f!eXKJRl0)?aqY0}Klu>I#r^GZui$qFKg%8f(Unf-Cki6Sr=pT^F*h*ja`lH-=~*fHG6Se?{ImQYEPrS%V9WO3%NnNFrqQob6c)j6qIjns z5&(HIP=j$nCXgb|j{?1xLq&}+7A+=&!{{IlFd9%HohLxuz#tG1SkB6DoFS*lLTKq6 zn%rhF1?_qmvlDo zm;E)?ezT7hMbOBaLa{C^3-^&CIiu1?>e9{p&8Y6zy6L|e(KXv-R(o@^{ZGHNHRHU0 z0jt)7`KHL1gr4OUJdk>G!reoeD0pv5NV$m%^43U*9YfERoG&(~z4#m3;DR9;B@U1i zsGap6fE<K2x@ZwrPQ^LH#P|o z@FOf~SyWNyFX%BKbu^{MeM)6hvB$uJ#nz_OxNpHcF7;QDnaXYZE}hzeIPfFuLyAF+ zrv_T&bbiuiHo(cu5e~vc$MZE5 z@32sdI17>+O)4K{f%M35YI(O+^dSq$pa2F`pb~H&^kEGdWn0^_>6L{6D+}eBHorX1 zW{(!`|Ng;mjV})uUT)dZz7Q)+#IIwPQ`cC!CuTp*bUp9=&_iQ12XL$%g?AU;&Gr`F z%cj-}FMS%Hiix(jzWYwX;JtsnPdrteS6VZW635AG2_Bhl*t)S8#LSR8I$mgg+ z6fYGQnt=ZcbK6DhPcuuQIV2jB^4TG-;Bi~@DwKKR9xkF8Npm8*0xrD(Z~6+hO71-z zBcx*}k)N^BzUa$&!#QQwQimwos~YC2nBlgu_QgbDF2OQ&6Pb>^uKrK1HC(#Q)ACtn zh;DDv^fgpb^UW7f%%P@Mwfm{1wVMmS{p@E&EwfmdarmzapZso}EVBewvi>~g`DV;B zAUrA6sHAcU99zV*X32@6qbt-STEWJUeFlk8`WNNQQe8_QalKi%9k{z9#1q5Oj)Q8U zrnrNHUK2R)CVogs+mz~|(v6TlGDL#ps~fM{e3Wq$yclTrfHKEL2L?bd0T!T^8K}ow z1iiH8__?8jf@qOZhFWYjj#{nOW2{Lh9Og(gVrKhZKZ7-#zwHwSe=uNjHc#qQ@-1?u zyRWPMn%i#cT02+xf1ef4G+uT3@>K86M&i9#tC-l0wF(Qj70X&-@JP)Sl$5i>u|1sj z_$q{vA~EqOWdMiptP)I?;a2u)w;~9J5D1!P0S=9l_pfprCa()bg2);Pgw!f8im7ot zk7OqSlaOQJ^616b$^{x2Gr7zBCjCHXEh?)eO!ajy8rk0$K5t>XACMN24xzOf4Fjr;LFEv*M0Vz2g4p5Tkc_hSG$t)QcodDRE7GFDJq#9w0p z|KJOxCl+%40`agOEfU#Tve#UwuiapJ=%dnFed?s?zGq%#xtMn3hchEDj1&sxC4*eE z0V8kV{Y=l%`UyLLfk)3j_z2-`Mu!*hLMIZ`!AyuJa&rq2C?Xg5)h${LIppLUeM*X< zzlwCfe;;j^$@fia|DfG=NR)M3gBEi}JDM|~9r!nRJg^$n->FgztFVJi2tcI24d3r4 zER2b%pq4^Ag^>zR&q0h(I(lHR6X?LJREBwql3(ygToe(t*t9rplzfaFGQe{Xi3M7R zY;csND>eR9so>sbv`4I5Mi(7_#w^#VhP^V3Ts zjicIIZj&bmQ+=>7u)f8fkf^FXm!GbO@0+Fds2fG~O}gOslf$P5Y!4=Z`ZwsgP3!!yEfRfyo}36oEiD zBREIkEti4vV}Jux9otvkJh``Pp*dMq=SdBZWH+xCGd(38^*4}z-@Q&;Ewbu=sZ7oZe9&4E15i|bFvxabP<%+AaW8)6fXzJ=q zhweIes4L-g5J*Tw&3gg7@m~cWyl;rR!{yA76-SlAN{B105|wgE5+F=U_YISh6+z=} zJmrl>(CV1{iKPsW7-wzdTO{YJ#L$54p{a&W9#_^zoN>FVD(juhD;3tht-E5QH_VT0 zeXPH8R;;Ra)CSdx5w&Kl7sdIuWo8;@4>wJ|qZ8UQB+PdFbrNUI2*0z3GLtbzE(8ayAI4)7ZN{T<_JiUjz7*eaEp4wa)$6n!o zP{m&AXNg-~6x6rXigM+`4HO@AF??L=w4}P<7w7sH_4s0KY5rko~q=;T$}*g(am(mIy+b?vJ2 zHudP+**oK#cDB5$pN5KoKWzPD>(`5;;xn+H&Pdv()`-nD5U_TEw z5_&UY^8)utgT6=}5vURyU!8~c2<>yz=R~3WChQFEDQ8*REw)I^qJ~A|B6~#zEt}*{ z5i3JbYwjSFed895rQX%+Z5yn&hqY#{LlhLvr@`z<)1$RE^jvu;*Rf?7I_ z(ALOgC0m5dI;R5(?G}OWtOH_zn{;GxkVaWDPSH-VY@Lca^G~nZFh&oVHd&+7Yu}Kp zPxhlB$^7K{_Diz+Fs54JstvmgBud6UH^LZViNMV9UPWYsRgp5mb#81a&gpJB#bG!< z^;UkwcauIZDo;EBQ(;V@{e`4Wu!G}{!) zr@M~4escGb&O^6v*?r{2DyKVIWv>mEev%&!YaxFO^NeXyj980OlPb}Mcue*Vt|`HC z!0}_}iUdMAM`Ytc8$X{u?7SyC5s0&bK1du$uke)kk!aM<@6yd$9kb^azenN;yy5Hl?Hb~9jjAp zKH<) zJ}D_iRn^e*FbR2mDlUT5}y~1nnN*rKC ztI@-gi|%r$dcScS#UOD5vN9-Wn?zMsji%@=1U!d025=(OfF;wAu1h9j(FpIShjIsc zIL?v#W#Effag~jV(GO7a+l3?*`NBU4i zd+E@&y$z{n`!>1oo^t#0H)>-G4qtLG-qSYb%jX(1eosCaTpVqlt!6EooOqM=?nKxA zF5>A-xEk{9o8U1Gq)A(`K30@7>;0 zv(T|O8oT6`k$y+m_50u11G-4eYf`B<{^d5ZosfVblef!>mR0bv+EG*ZX>h3yVKu+Y z#*``_Qz+51PD_VfKpQ$W>SCi7vr2i$z|gHpyC9&}T~U<+UlKGcuo|$-#r}#hl9$-9 zEZ&KY4hF*EWDs6gV};iho^I&PWPhPfg&nga?^-D$j}4dVIHL^H`JUKYTWVYVnOr>5 zHa+0WS+coc%;n7)A`=N`#Hm_)=|X?(+N~x-Dq5S;FiUf`)2Ph{eQu|{tudg})>e3B zXsUPF@sXF15wiJ7|z zptN9SD4s-EA`)~Y%Zr#F6@21sn>S{X`(vptLyP9XEtAW)-EU!kZ~1)k-1Y@*uyqge zLbqtLJ!`SxY~h{awbRp|-!f@QRu$G4SEci~9kOPM%RFcp)Zr1{pUS#KoFb(b#-Kp* z0r6J6Qe7fxkmN7s1H*ud4gfT)Wj*banw_7supgK|w<1%7YfV0rIjJ2S^e5Rzr>6_! zrW$9BAF1dn6|J9oJbY>DkQ6JPeuNAdzJjph zDEt*D-HV(r_%?*q^PWL1P)TR`%U=2yypIYWqL3305xAicz&C>Z3$qaIAN;t0DMyrbpFW#3ALB8@FpgT`!* zg#!_&4WuGo<{zI1nu`hM^&Y-|ZmF&mz+=eYfC{A$_K_}HQoOh-N>!yyRKpKAWhx(6 z)TfAT6gQ9u$>_v^tDJ0T-qv}Yw1KzPwYAjitF;DQ%|Epyy2sSw>@@VJwqQyj>yj%l zj%DyQSbOl80Idb-CZV;EbSE5=#zCPv@{g5#cs{{ri$V^=U+h7!j@XpMz@#%?K1x_9 zv8pF}tdICt_|q2loaKoyF|pjyI`A#xMx0}PaLbm5InR=~6s=7HN_0EqS%iFx@N5ZC zoly(~4lnZlkckP`Ei_J0LQ=}^kaW9<5Q#b?k&vX@NwU`=A$?S3=0pC)l4y}7j=J_Q zRt0pXpuII~v<3HfR&d*<)%YkY96PL(Psx|hq7}g*sVRerUw}-%y+f6JRoC#{YWc3I-AsE_MYEp zZ)w}I-}Hjk@!;fM%kQeI*X2W3;B508{7%$`d5w50+PT9p{GY{JrA($=2&_C`jJMvn zSpTQY((&b{KMj_8x{E8{|Nh!fUM&1GHFX!)KfFFHpFnLpFQgx0trny1wPUT33uCQx zZty%WG+K6OO{>rdbV_J)&B!5@Tq zz!&ZY4-3(L=f|vwtwv$4y+&*`a(-+z5vMa!OTJ?t>GQRWp&0I@GP!eNU~iva zqb_u;3s11;fQH$%Fn(>5PA%T&8^{9VRwFK2>uw6v z1U4Yb6e3zzqr)TADAG{{)gm0a+)!2t(_|;};?~CR9Q?2e83B#Qr8z6RM$$-Gv^;{ zk3KX%svhX-ZOdq{zoF;OJ5CG^X5s^Ji=po=-B#CBPhz%J{7Qe;XiP_|Lxn_SaI7;L zPJ;#?g#PIUKQMr=0n<&U8oL>}<$zf#lm}EmkwM*Fpr5FJ3+X2!FoF*4lrn7K4kL}y zox*wIzFlOyV7pwD$CDM^%UdQEItzDnZk`Pl(lQB?lusFkJ8zA-d$?b5(Wf6%?!v9P~x??TTWzs|c$eaorNqYF*7)^TH? z>p;)q4!3beSA{P{JcoTBvL2dZ9*%Tu29Q{|Mo2BfwW9$J#w{hhDJkI%abb4tFA8gB zb_ogvp4|d;6y4>eVPnsAgcnl6F-?1pWyVJ1b6)0{?{nGRtHK`RZDdIB|GX!}f4Opb z|MUBn_Wk=os`SI2EoA9GJdVBGkH30QFO=|Ch#AlrAw_}eD%L6hAW6PZikit~7<3VY zuS3K~rKF%4l({06@Emk*lwbFj51LWhdf%a)mt1-Fj;@~Ofg|FF#|wkgedBva8d6a> zctimX2~_ub1^%6C%G#ko!8sgipvvGPMgjm*W)*5gcHrM#0O2ZE(C|#9#1N>-HlDIb zK_{)!!$(mObRwG(@j9-4gckQ*Wxg`AlzG_vaBby)*mGmgKR@=?iXmZ4sNpK!g)uq6 zMKLCd1;f%qya2;GNBEF)@Mh4S;0MkU28y%ffJ{ze% zTzNPzeL_3Ary+M>@T%n_&m8$lOFl8Nzq4a&da$*9ThpMnYfoJCw zhvph4y7%U~#@qLH_Ktw(|9m~c9>@6z2sOl%TN`7;7jw3xBCUqk0O~rf-bzP6@+0=g<*^sgDsoCvYW=GQbYJ2A2 zhBbPnCsGsXV(;o7DI6ck^-n0ZbjHhbNC>B-IlLCW1H-Jqcl-rVjZF^#sS%G-=M)L4 zf$3d<)WGyO&VZ)Kp$4x$&A06~z5Yf%)9ZGdZom6tlYM;nJ^dr^7=ElgT~L??{PO`Z z2%R2w6yiE!81?_)TL)_pN%q!l)ZO4JY0&Sm8TA5{B`lkqts9m}tZc88YjA*MJ~p!> zz0^P0nW~KqM$Czsy5X(c=6eP`bQA(FwWSyyOsN`JVA9ljHD{ z=Bd8<`Mw$5!muS^8{e;UXqy_4@9q*4T8 z%@eJ*9k#BWL#gP#-3@K|k(LJS#8}T2-MQUadpuMdHYBFfb<0<6U3$}1+006G;j;18 zmZ9O6iD(pI2__V4TMF%j<47Q zI$0YvhU|grj=;!R?ZBbY6uJrbX)ix8aP4Ya?SPXtPVY?jj#cR%{2PO9`gM~{0~*7S z*>Gfj;M^s>Ub7jZO%z(NUv4o3ZreoI8=?jX4(4GHb>v?J_6B`0%Y=czCyK}x!$*-Y z^|A>!MVe~7oI;A~D6lu&O?ttsR2%x61jT_b1f+tsV8b)Jl!_Wr<_+4TR)8zqBSRW( zRn9vHs_3OV+{xb8j*x#c^&#`9WoD^ z3Qd^&>|A>1KOA2=QFy4kt+~nFsU5F1VlAH&SGyyCVVGfzOjU;wa#X%Mi47lK14dl(w984(Xq?&kQC6dJ!u zVTL$8fZjfT{{DR3tKUk!;KIc(fZH9fhd;yZ2L>GV<6>VxT_dacdMfAXen_Q&Jh=LZDK4?21>34QtLVOML}UiU&Wro=+1nqVd=zT)8)p2 zBLf$-8NPgvUiZDVpZvP-`0!X|Utu~=I8+7L?SyUF^>GQ>A}I^*%?KA5>m~d~b-qSm zYRZmK6Uaj?=OCtv8=ws!si3kXSP=z7^WfFmII@GIVBmy6`=rI}&)%{B;6muh>vrAz zY(2Z?P88UJbsIH{Rlj>>?Z@B3A67l&9P+t(eV_x5e?bpR>g!-Y*#4sU7c4*2`V#)d zh{Q3Y#b_~`E{cD_FeCuX*#3ispT5n&zdmN#Hpl8+qI>Pf%rxs5_PK`K_ug>_egD%~ zw;}K#3I8&j9{|Ea(4q}9`~p}Ol~RosJ4ivITHOag7d+<{OGK6iAua@A69&eGEqA{v z0wz>Kz_jkR)%CX?)v_<>KAz4vm97QzjqX|Z835(q^1>X8sAF}r>6XVr9Z|(2+3X{# zcvt)b*WeH9y1VeBWQ6d@Sh3QcOBk5LAD197MJx%BFlvM+i$EBDgg1aNgyyEc1`C3c zZc>(Z9`vwr6N z;otA2d9d{_A}8`k+~;(oM9n5(WaJ2VLgyp90?2kFT|tsFU6zSs#WYuH;lUMmOb;v* z4VfSqcz=!1F{F8XEW9<=|Bl*?h z+)kBnGJ;}GV(H+>uZ@!-w{So;mmA!>wD%W#myQ>%jKk^0o+~u5FR%UN3HUw!Zhcs0 z#Jf%CntBn8OyqnFKxLUf;F-&54;2_0P`6Rsl1nXLgEAkc-_CEzEyN3V#E-nR^=eyw z?g=K}{oAucg{upHf}2gwZXdiG);s)bzZai{e=R87{fR2L*05ocYt2oeKdDi2ai=Qs z57OYuzD+l5^4LNNt%haKU%>Td`jx|@@T&9g1TTh|_jk07YH(?>FNE;-7)1wJ#dQQLOpYRQWQ7*Z%hD3lf51L;3gco-FzN)Cb9`TgOdHg z&SW>RGqx4loPea`@lv(K!U4=#VFr#r~ zBUAoDC;OatE>d_7d;vY?p%&NTJk&_OeL(>#uY|j8*^=yf`CQizgIViTT6v3dmy4<8yjbR~<7UOLdc*ut2nt-fQSf%T)kiklEU z*Ax*w>eEL$PVR2M@<7+A)y}c}>23a=`0$lOqg(FtINjk&Ko4w2?2qh9%rmB0VF+XZ z&jW{-M8{&a_(S*&-4w067@p?>TBdW32h60F*(+$d63+u-b~JKwUb@@@OIv0zTHQ9<(p|)t5Qazgarh5fF2L}l zlQ9|6LSa(S3^rkSDnI&j7#@jRyYY6K`j1xyV{9R9atf^I-}Hol&BS>(0bI|heT3eR%2QV|OVhP$l#(yyI1{nCkhL;db}^MShd;Q?Ew>e`z; zAyd3Ix_hZ@fWH8*V}{4n!?a2_RaM`F*U?IN9T-(*yiR0->Ly(j zuagB*2k&gCPjkFZ0I0|!UI(*=`}Z$}>r}{H=GmLcbzB^)6Z6Omxwu5W(~>kUG zfNVe$D+#B=0Z1IBLpBZkRYjmq(Ke)ZXzW0!y1KT@kyb_{>B(SvdEfme_IA^IW8ulh z?paluLaP(HeP8q0W1(8dH`&*Rhd<>G42;(Jzl9(xd>QbyeFb~xA}z-b^#ZcK5~std zt^%iHH5YL@u*Ar5#r+;7m`5pm;ljAsV2`HvJ{rsW^2%DT(PLo7xOSk~9b=K<;leko zt9)*`d|08B=s*LUP9tB#*TCsuFlfzGfl?^0U=gJdzKS$=^oEeLx<+ME zS=0(!^FXR+OeG&igJYz#P7k2L@$r!%t#VAE_WJv4`uu1~I5cRFI}1;2YHZ8|3-CK` ztnF*#cSuBRri{M?zf<{CnKm}#cPej@sH61a{KfG*l@G|_?)o$M9qu9iOYl2i;qaXy zUE!-7zT=nRJ1?GC+V@378K2s>bR>8od`BV|A{nzc50WYBiKn0^Muj=-y-Jv4C-75I zq2EjRoj9FkFX4A6?SQa5f`r|vgDuQmr3}^nYHOU15@@H4dMhjT9JJ%gHq+UN$w*K; z>;%sU{eIbDu&CKzvDkC=P%q2BTQ}dW zF=^|%^@TGJ>YTdW0qqhY%@7ku{CN#FQc^%AlZX*{?4x7>Q%7(<#@ZSL36r%+!N+_R z*|kzIJg4F=d8x)1RlMWlwQ6Fx zpk$L@#t)3&4Y?VEP8k-m!VP(24KThXHL~284-8)hEm$V2!x5)W6tGuQo3KaJm`^U} zTwJyr!u3^egzJ^MM%LFwve9e+h3|O>iK4K@K`bJ?b4aynEU8m`FR78U4C833=t2L) zMSAarc~g37(08Wl+Ror$dobM+@6UK%Jg?*4*sVvp(T{KKUyko#4NVJ2J2G8TCwi<^ z4tpA(49YIkq z=vRB-b1OOcJ;e{OvfULM;_nrg8w3 ziuZ)&iDAmQo2H6XOaQV)d96Af>Ey?FL{6e#)H^J_QrH6(oPKZ{Qz z{`+A*cRl#T9T-!Ua5xWstE(_KW#dkYS&m6#b`*E{Kd|aB`xjcqjt1_S??USA zcn`B4Ej;~j-FsgQ>ZYuo@g=ssyYNFS>4W{uU3jW+#p$bIJ?~%dXC9gpVpFuv#1prH zZZEo@ZLhVTZ5Q27jv9ckxc+|g{aLZ<`(rQSYw$fl>;P-?$JgA?+@Igi+^g;<5DT=Q z_q=sqXtIet-ZUB7`&RK|5B&V+53Ef}^A!6qPm9p=33RNaJX~q7jMxGJ69r&OY^C<9 zM*0STq>qZ29Pk`2m?xFE2a{@Ytse4UGo& zkANI?nVYkQLLPEv4m=h89O21rr0f{PdP!5psYXRqXnsJF$z=!$;@eBkyDEV1zN@h?ipXCD92GM-l$R^*}C2SzzyFa(T0{d39!C&m+xaxkJP0 zd?3HYZw@VOY0PHY{hF*o(W^av$ewh*)O)yhdFFjl-SouJWxb=BiDcLK_|iaKXL>yO zYgfuPM6ffAyBog9Gr%BE35czD;CHEqCsG^_x~&Gif?B)-aezrtFJ-w101^Cvix83g z#LEeVHaO7LLH-e8icJwbP7&nEV3cF*LSAR41|fU2poBLMd14tp5psnnRj~{gfuyK6 zN|}4${t%ZgvP5raJF&aH+Fjk<>2zDV2HOk1%3T@=!xq=TzJDp^R1Q^?18_YE!*Y zMU~nStxfhfIn7RE{mw0W-!N)(+Z>lsRX=#vZWbRAzbqUU9?5&=ESP)}TOF(d7=wG) zoZ=qTcM@l+ z^DTH#aqfTm92c$2iz7q5-SKFR*F?PLFgvV~Fk3N+*T9np#RI3x<%A)Tk=&_`wCL~$ zyI?1^G}A`)$Qt<;L6wB19m3|p6G=|L9(lG2`QbGpwgbu?`=%{nZ#GsrLv13f?QX~o z`;912D2jJls(pHMq}o(l-(Yq{ohp?z+7wOp_9Sc54HjF#qcL~SE3Db3fX-+4y0nR= zOi-apcJv@q11a+^Q^!zHf7WOovbbLN#$5;FtUFiJ>DKsFAF@_iflLmjgMPbO9r1Z4 zwrx)ahI$&kR;Sfgg_3zKAZJ}H>vIm*Yf%5XN~U+&y<6w12F}!Fnq3Ndwi?HLe>%rEc&L~hS1(*%_S#q=71QL-^jW>bR zK|wYh3T7LF)1m25Dil>It5P;BD8Pes$gzS>XJdv6h)9cVqs2r;I`XhcH$ZpCaVN?k zI*{@sfiBM4%)V5s-_zCAYO^MS8aaAsG(^;zWNWJ<+?9B{DHd`ynhzsmO{Q~3YMPp@ zT3tAs(d#`)wMOmiXbsobvfrpv`FzUhbGke#g~1sJIL!`ox_j-HSfrHstU@2G4`$~Z z(krtbT{Ia|TWm>(rfYiI8y)PAxSdL@Rn_|MWE0>8*9-Rur}L+_4WZv#J;IbnF9EN# z!iy*$zf=R=Z$wat8d0IS9>oVljq)sll<2L4-d9Xi(}_nBGu2s0BT*%s#nRnGRWpHI z1z+L3Lqt(+(9`sr?zrXVD^DEUzhnE>xrwpN+3nSPLuRu&kVu$}ey+3Z+MbG6rFuvO_i0j;5~ zJDM14*6Bix`9vsck?Rb)pia}Wy~$D+K>Z4%%cgf*jBg&))MVU|E~VV;wOQNRqrVQe z`fjiGYMowtebNI*pIl>cS^Q>`HkR;J)o2WMm9Oi_{KTQWw<_*-*EyYjm#sckZFL4M z?pl)*W{15Yt~JE`so}V(u_@?|M2$6Ow@zMFm5wsAT_aER4s}J1M$46F+Oq>)W+QV? z&iJiOI54-E9m=7qpvmP;Ro7)*wLMUkP>gE*PSFkL`)=Wu{7vmlsg7c=96B65NCLue zb#{oFQM88BoCE$xt5j*PM>t-oRh$LcAo=_(wi%x0v*3D4kq!e2p2^OFc9b&Vtd4U* zwNj~GL;^JRb5|=NLgr?M2Kw`@%}ovQSkO=9d%)0kv)y_yG_s|dTlt2?_#P8)sw~{2 zY>e>(5{-g;5eLIw=3JSgF~MVBC}KvB(n;W({+z4X#q@TQ+oU(u<$Ts^tbDCKGmvnp zw+(wz9({u|*W~vlA}W0>W(Wt=6Ep|5ExxuD6s&}e&234{t)|ZXgDO6M2 zkhPfx7Zw76um$23lIO{FBm1rlaYEr--s*)f&WB9ck~K@cfvILe6tG1Z5Lmz$W337N zP|Qy~E~%>|ad>OsFoZVM(LZ#-1K@=`NQc|FNI}WHsMJE5ixjffQ0H^QnkCZV=u#e$ zBbT{L#c7Ezc2=d}@IvLK$z7r7&idi4k%{~k<4Y?G%gsBxf=#X}SJYMA9IR{lhN-Z& zaCz_Req*<9b$aVZ;~g!-KVBT}-jmbm2G#1C_GDdh{%1j8ISO zfvQ$=?|JJHu2>IBl%VxM28BYt8>-6Xe`Y;49yotJ5c~-PlIF6SdFZE})o)&pS6dF^ zK3SDYDr~gUIO`A1w#^(InaUf!nOfeHnr#cDY#vLEv!SKA?XUDd?Aw}r$BS0Wtj#?! z)Y04Xh4D#*GWFVSje5ATF_rsD!&Looq+~O}2>v{cT*3Q^HfK@d?Vg3X=`i+oC)4RK zKL*>y#~Cr4ESTqnPQv_gE6DIPF|#Y*VjGH|l*6U$w!LtG2rI zW`)rYt1h56yA<}ukjCEF6%RNSCUwn@8#L}%GN6j*Sk&bALdDRTUaEy_SqOGBccNZcdGL#C3ss9Wdq=Q7NWE3W?u zwGukS{}%2R-Y;CAzjhJjj(g&wO3@8S(d|27jLU17s6A&}W{+0eI506T(39fr^b*cY8w~{&lJxKQqI01^rQ3de12vWT($w)OEnG#A{-(#Wu*dsRdg*V zNtEK5s9+u_n#*}dt|g94)Z!uNXZ>o4UtIhU6m>W-1cyY*akn=CXa6j9+0@1@Z}nRiO{`wfOd&J6kg_v)b+MaCKj? zl&y|lY~fp0I*na@&bFib6E2rY)^*9<%cht~n;L0xd036b+1}A;>^R*2tInHlJdsiu z)sCrsM>63k5SX9`=uI!nqTtP2gq^|>;gb9zRG5$@d{B7@fM(b;*{PK)wOB#42mwIj zdW7W(@+0GPrByO5H4I1iQn^S!slyE#BP)gJT`Nr3zjx)xt|QBfB?)7kbo`%qGO0U|%~C}iSaD>`Noz^O!W*)+5_g5I!iyb}{>(|lC^BT$ zbIuJZ=6qAIGtwQ2ZcUi|Hn~RLKbJB!@3<iVmh#fyTyw+gfE~ z{oa=9#RD0qckuX|3a|J2oIz)`(@V}m?8zz)^+VjxsRPyuaZUIw(L55-7piputl|RM zcx(mzyzx(CZ3AF2gx^7jfJ(Or^X2+H0od8r98JFqYpA-KCr9(OFbL(z{9n zZmSbi8Xbf&oLOoSu|u_>m8-SnNmA%ZXVWVM{US~RP6nLpfKYS!j}}O4l!t;KVfl7ZO~u zwj|wK;?&{|wGmTQpbEx-MW^;SRk|vjzCIJoNMh5hs@ZYl)(w`d^H|l+bhu}xFX>qLJeOoS&+OtEA6;j(h9nT;3*i%8Z zZ+y^cw0q)-T7^d2(p`t60lzIeV*yM3G&2bah}@^21XSfQfqhbZn(AfYb6#su_MhMb zx8VU%Xo9bC82s2Hl)ImLt6lJSp`#E0?UZ10Ap?fnoHJlbbA1 zv#fo&)}ZUus?V?=&A)e=%=-;GBP!z^t>N*~Fh~Ma1qA{GM0NlnWtmL5K#^tH3gFvl zx&(UT6(>P+WzW8jl0SjEr2lQAx)1&0LltCKc#3tP$j^?#gBzrW^&)zI81$|IRy~?W zE~8GXmZ4e^;ga$3Iqo065<^B=b>z<}IC*CoeHTMdO6FLdWM62RLbt*16@qZOJK4|h zEk7?fTK2NSK0F7ir0>{??~vxoD;v7)fNsJGj0hXbjL0eRlc4}F`eeh;w190ASKttq zQ8shsgd+hpCC*v`9NDN0Nj%1e!2n_k8eL_w8?Wg<+Px3l=9)Y1_{VU=y!iD0C+7MTCG(CBGa-LDvj3rr?6&)oD*_Lb}XACBoxP%3-A8BDpHU`_v zl9%-^ENNMCG8wZhV4}T%y5D!|cF(kejd}0=e}Dh1>ArpIhB{Ss>V)r{bIq=iy}gb9 zK(`xTV8zDwEcKzQVE$LYj=Mu9V6K=Eiy8$Qus~T3;;|@7a@%c)0nM2p)bns+STN;> zksTVYX9X2107q18hqA=qQXwt@uLzt}whNFqpVdd$#RNDZ71JW)6&KpMJmyN2Y8Kil zFqIOLErwYnLv=ZYU50({s=s~H!mF;i>Xi$-=Dysxd#UChOD^}-n5sM9oo~D}-`&j) zoPMDpZTg4%)&KnVYu>hd_uH<$<1NWgebFb*8ccz+AM%f8?%2Nl_Dn-yjm+&^x82cr z>I@L`7zYI(hkwF2P*j2g9*~IeZOIm^^E*W)@U-xD%>&D$g=*OT3Dzpg|JTuglCYBj zH;J7Al8t~otV)1aky`^4;yVOButR929inx=oFKwRCjuI}evyD(TXXSip6ks4Z*U7j zBpeL1tIMHt28%O7MzQuZp$ewwO_XE`9wnuUPnU_|$N%|b+4Tw2m6@D>Uoa$oX8*47 zJ+8(-u;Io()71G6i#2{}85*kf8_vE9o+yCkh~Iz;;!11%08NSs0?|dBgTD^T2NBJ1 z|6($4CoeEhLM+l6rd37@3iVBRICYgpsu2f^I1*(ROQLYm`$bLT^=wZgFvqT(!PBeN zv-el=NzVj64bS}d)-xTLyNJs40V#nvGmJ8XPC-VG#e49_@@n%UZPgB&nQzv3v^t_v zZWNoRz-bT(P6r!|71C0QzxM2HuiW=fcYkW)wh7I)T{G7e7p{D;K7m9 zxUi2s+W0-jLi|jX%Lv>b8R0zr%h2)QjEDc0{FCrUg6HS?82EwFKPY}3;p4}J>+8ou zK^+3v)l8=tWm?^i?MT4J4c2@SgY}BASYrtz$GbvD>B}&Ewa66#qNp@HuUR{11fn&N zGvdf0CLF!?(D5V3FWtRm)BNo4K($inCcPeo*$=XVlBy=Hb4Y=Z0VK0oTkWp7N|FZ$ zdk5r2+DNc@v;e{+OF|FhMu3($iF%W&POf1TZ9Pu9GL@v96F(SU>zTT4!Vs3p1}?An z3`Gs@osGKp(C!kW#Yo;>SjZ}URiwaUwth!!tQ_JextJJD zCt|wv)auP<(S;@+E~r*(ne8&ZZNo-b*TSYuuV1Psh5A-75nT_EHG#YCX&>P8}aB zJ7m2h;x~|s@NVH{^%vW15bldTm=p(?R(1o}>ki0&r`JTaYLQ`*%pbvmnuEDTA=ARk z0Lf3#A$W-}l9ccRQ(Y^{FqjxBodS$mh2(T9459{w1a?y5>b(Z0)F@X5`k8S1ZO4yc z0Ouyg`tKgNJDm&#DESARi^wrZgGjyya_+P|58RR>`6akTg{#Um3~*VHiY1GZ)^HIy zkhCtYpFYZQt_tO@856|FWe2lFN72^3zdmxKIe!W&;bG7KY4@;qifZ zeBs3A9j~o+4<{?O{GONY^-oWf)b2~Jx+2wk5iLN#c85BPNu=*FUO>Vs4ug5UuZ zv75pgQwi)C7&ffp7#0|}AcjIRgKb!)k|QsNTz(9w#d=z5se*p~>`f8J1OPn>4v@th z4-G`(v4~dhCR|Ds>C#;>KBj=?a_J6fAW3FY(!Rt+C1gsJSZFMEXvQ_kPz?GKd;K5x zU$;6}9G|&)Q|0>Zx!ln7!5hzP-#s#ajIC@|nPhs?tR%FLQkk$I&5_gKPii+Gv>cdWSDG8Y?0uKzAGTPhpVTN9 z6)=CXTYw2zX)HCKzike~BhcRW5Om{!P=h7kh+LeI(~$24=EyciF9PLOC09ZrM!gCk z4;i}=(L{tdkP)uzB;ju|>up3414s=asN6`9(qpa02u+My=%8;1X>OTU|@CQI7Lz|U=4t)8cBepO4~PfgomM<6x}S% zM0decK>vWJSEGND34pq>-algY>luqUtAVEiyEtipn(YVkQoly78b9^I?Akq-*AGo6 z)|L|WRI4{LoxPddm4EH+m%Qbc`T5~1?^(M1%D`~G-4RB$4#Y2F9_EBA>RMpM~9v_=OFsQp^jRw>`u!Xui zu_X&>f2rd2yykCRmRkS9+-h+EgU8-24J4Kw#z)h9O1z!t0&)tJnQfsyn=tESP*(_D z3w8q40$d2DbTA)yL*Whw*OJS4%rbf5xKSq@l9H2=AqvX7m01bY!8ZsJ#hfnw`bs55 zcQXk>%9VMUjc@C3sBeyr@2^)Tb>r`O^Lw1BEpMK9$#~u;w#S{8M*vuDLj}&uD=ELcIWm}i+N-%qkjE?{SLTV7qrSW z<|M3WU1`7F1Qi}VgRKEfg=81(bb!jW^JX!may{>EP$>rJ0?!7M+%6DJdt;ns-`75pjy5P#%Z;odQjUO!bk2$s1 z?5RKh@W{}}_yOZbwO;3_&CV*KM)4q$CERQ74yCM(tJ%@a-Lt77^d*dqdk6TT78)aE z&|9jn_W(*C#sHbe$nu7GYgjI@|0@xHtwgyssD_Fqr&ACAH5FQuN)%X3I)~nYZ5(x# zDBqf`wT+{PJUNFn>0}r&Qb?YTc9#l^gCUbAYJ}dA}Wf7;+j0Cz4Yc#;mBj70uBir9`Qjt zx%*R#rgEfrqBrl(MG*?`Ou16e?adj)d5DKtHH6ePaslN0el-5Wp0>lQVe#d)FMRJQ zRbq5cC0H|DF@8bE{`@kPrm9rO3gRtizYw0E=vuOm4YA*2?O=6a&w@n@qY|NfY+k1p z5fqqSq;GNzA6!k_85Te7*#aq@ejge;JPrjEE*E(UQ85AN5Bf&$DmrXse3ivqLD_#A zT&70zkuxfbh+NN?_y3#8sW-_*i|&6-9@Th#_9y+@_GCnB<7s13D|&n?raAlj%&whL zmeW^)Z0QMGZLo-^Qf`~Gc*g+Vu_ElMuX<7IfjF~HtpEfNU~|kE0YPb6s9`8d1c@Df zXdu20;!q>(g;1{%R^m}$#-VRTEAa~DAtiq*;tHi~x7d|hhFfe#2!jGR5viOg=vZRZ zCCYf4;42?2om+IN6+bBkhBj;EgD*VvoP`lP)JcorH$#O=uei-ffsON^{f9sGh! z3uMl&l#&U`6D4F>R?4!+Ez`A@_5o>0(o{eKnv%#%3GW3ULX=6{DEmmV=-25qIg=yc zm8-K8tK~#Q>l!$;qSfkLf#oeRr@vI6jSVhlEnas|Hfq=UGC8w9?x-3(v7kbeFDC%w zaSaTVO!oe4cCnDC)e?Va8>(0IzQXi$D<=%~?_20!S{Nn&7;H=m$R=XW4?hlUv9JPS zmxgnU3T_ebPay&mifUV!xI5R2ZoxR)m0`kQf4P`Jg1`v%InB}?{;~7=3=Isje3=|! zFxGPK3|AL8$K>2!sr;uzPe=c|Mi$c9ye&|Pm?5{@oL%)U-%tSfJ*7HoHK@W_uQ9Mx zODiuVLLg@kZo4EQcjc2}s+f{_LRP0urq|kivf|Nf!g}eIC|rsEWX*RqZWcGQmtrl< z2s?zs^#f5C%w`F<9-Uu;?HBZbR~dF*&_hY^6Ts_$OAwWCAj3Wir}bei<0&oiv7Xa% zZnn3|gxMW)J4T1AGrcoi8I*twLZYd#N{h!rQiHOC0%TYKB(;HBm<`-4=|B(xk&5f3 zz;@9zp=|{-M2=lW#w9wV0$(+eE?a43b)^)Qg?r=iq`_erGsFf1u}cRssr(I}PmSlb zc13wx@3OZvvwAWzKHxNmqmG_pcg$gZTeMpAtf&g()5FP7~q+2yM+1r%z7?JOd1&yh6+*ebU2V;X;#ShW6$N%?@c_QibIYe zAwfJsF~3%R$S&5Ul7wB!FL(}U2h&Ma#JnQg4QKYXn)0c&Yg2Pa>anSS$6gEf@8}un zOOA(?e&h525)YUm^yl;jyY|3=+1oDfH|pm!=A~N~hbL@C|6OB~YWbW@SsBHg2QIxi zK>Pwb*G+XJYPZ3YER|{?X+Oiwt}xZ2`V~2pv4qIP4)hY>za-G#BxPVbMxg<)OfumP z6kR%}^#u*r>So-cTj)+$6ESDnqS5o3Q0QMrfF1OgbNYKuyxsp=%_^-ML3XuFz%W^} z_%&vg)6k_iRc6*>?wNOY|cfohrc- zgh}FXjPgHVK=-59=)X$EBa<+skdS!1(n&;PNEAywQ&T9gQhVqK)SQ%F7$Mf1Z`|~} zmM}X5piWuWViAZo$hn3LkYGqHs8dTpDyw?ACd-#yib5}1dlH9P*eGzPW%8py;`e2j zdN#e{^3rNPk-FwkbZqxP?}S+q8Q;C{##-KzvKcKse^;y~MzZeFxpXq4P`@mL>U1)e>9S2XY^I!Q`A$H4W>-2E zYmTS&Q+tfN{d-ggbf%u^qNieU8KbsT-k^V;;d%b+jK^lIi>Q<^FINrL*mV8QM5JeG zDXddZC^Wf|^2D9>#sgcaZadg)5VA@{jKFVL5gx1?EufAP)Nwtlo_*8#diKHtRCzYB zu57mza?WbbQ?$=j%0bwcQhu?v6vjz)=cqyoY`7JFg-j171Ft*RW>mek>blO_*2$&a zk89*a#C6qVugXuyrJ#Rzhl5WU1UhMY`@L{F=0O*Q68+0u} z2IM*|wm32KOgb$n@GLql&R$ffr3n=YAz2ybQ(LE%jDu}*v=eg^z7Qlp&$%pweGf{l zQu(Fn&8d1K-7AXDRi`K`r1QMbJNxDfrs7VbiF2oRej+~{QOGpaBC3fUIjnbTGEwUC z%H*-JA8~4sPgjq=4x;bKLMhaRH`GlgCTcP<0Kwx>W83DM6kNHY9)?dqz7H88=t}(R zrUjD@wqc-4fmV=|`fO)Q5a;-U)Ew;Ug4S#GOkXYE&4i)BTD`Ac?CI{!_fiNAYT3E% zHiHfh7X=Z>+@3C!@Kjo0B=?g50Q!It5q-$V9g9{$9?E%*^RVb}Q6NSW=M(kKii{y` zo!U3hUq^6|*EFm1`80Eed8b?!GFjAd%O$etmg(A7#~($pd{1<|*YZbYPP=0+=Bs z)hyyRlzh7(1cc*!WurBWEm)8$)v@&}lzM4=*6OMHC{cWRa&)o2*w@=Vk{?0+?P$>N zbt6>gDMz-uP6XS*6S-6ZN7m&R!qtUO9k} zuSiXM>FVO$M+Oo9I*~{_t&3*wrE{B?CRWq2iT%Z)x!&Ca{-UEWRNQv;V6B&>6S<P#0o|;r=5Uk)GJxYx?SBt1t z0!{0SG;IM8_6i{Cl_#nEFw%&h~@6hsO{qAcJ=EJt>8y^q{hmt^a&6W2Yyk6C}Zy@Xs)GHsU4P3pW zG+67I&+2ux!3l?b!Q#An*K6#Vu-zF(HV3Ic;$7%Z6`bVtbz2J9gA!7SmV*$FLg*ZA z*EGAkVHsn}oydm>WILIaR7emYSHZG@jL8(NjoI5u+jEknLdx7uR}gASrc(c<3wZi~ z(c!*cvfBeD>7|^ju-_o1j}8;N)^@|V`0Q?xi6$xtBQX&OL+jW$iLrDL?aI>N!c4H+ zVVOQuUtAiy=USxu8ysms3rG62Zu==dRU_s_uefG3A4hN>XwjM zR4D6OIEu3w(6`O0v!In)#o$hC3zat^^HD1Z{@4QKQm|`HOB7mofRery)-NG;&9CF! z`WY{)cjFY?E>yOl{j_s0J?AkH1R^8Dg*;*i$A@P}W^2{_P+=$<3HTf~cp$n_1-i{| zBre14@x)iS?PHw>$spd7WR(JkZ%d)XZJ__HkJcL|)^c;X;T4xnCs$|`^4N~PvCYwm z%j>;k6k~r}7jPS7!!l)Xt7RkB-kTk8m?txZT<*!9O1$WfIeV%$Z`zp~+qHG<)+9<1C^_rg2Q45a@Pt!5it}S{yYjjpa>H z6VF7g@k~roh}!T@&doS2A>5N`nPxgMG!pvTygVhlWRKRWNe1O|I9Tn0rD2P+Z*|0B z4Ta?jgI#YAPLJB0tL!0B*5lVW)Gp|TrOf7Opwb(UEAj!YLj|aYttYd%sAy24D)>(E z=VhQpukhx&JqTMs#t8FJmqFBmDp>I?s+0hY1ff*&BhZ0y37I&xdd3V|;3SnwXi74f z0;Y3?f`kc!;g|umDZy@wAk1gE@`8)j5Ydf_ZUO?3P_{3dPFkZjP%CPUne+j2=wbI# zs$w(;UA0ul0i|*vlGTu8I~GkzX6@GZ*#TzIJ3Od&GLW^IT%8B3w2C@yPPe{l9>`iO z0E4f8FEW$D?;jagUdyD<|8D$D=?#vItFBeihx*4PM*yJ0ZUOylknkg@<)#Y--7X#C zt+0Vf6UriKk^&0blK=S_#YE#b0SaNqZ%C4lLmTR)x#ZaB$}Kk$37L|+Ql*k1+6ou| z7bXR^`pZh`QrAp2zgkN5WTv_b3qEHyRejv+$fn9acP#hhX1m3&{g-30ke$uTzRAu6 zil)z~hBN+x`O|9J3NWpaR~b1bqOhRN3HQ_~{vIX)m#CEoeWC^>`J|nIVwSWTIVD$C zC@2F7#{W1^ek?=vZa@iT$4JM`wVUDA$Key(P*Hda=3~+ol*j`rF;aGn)dgW{vOYI9 zhv$zbko~NXOjr#1R?+VkM`^Qv_$j!;2EHOKk_~qtWPnby0mK>v5zq#njE{hX#t*Te4 zk*&?AYr_-bsr9@T8+L|DGH=g$jKpZ9ywwTNH;di&*18!{dvdD@S;U++7#-T=jbIW< z$-Bw3iX{i?p*Dq5+^dE>FP^07l38yxHB;hT?@QSAV)eAfk^RHmN8it3JfI+5^zy3@}$#vLH`499Yn>r70N0av@#}W3?zp zeh0phk%t+q2hou&n*ppWtycUzpp;}}XsNv&EwR*M-3g056_2Yi-4wK0ZE)Uoxr?S1 zlJ~BY47fh0DFmT{^zUL6gVXZD%gp_B{B#Ay!YARB)GvauKFw3)gYh+=p;pqs+T zp-?fzyi@$40qO+!32=eJG4jgmHjYoA%r!hTf-mFG;3i%9|4G$#p^UgrrX>EMfX~h)IHNV6@Cfeb>%!0)))V)zcTTzE9||G>TMkSM?`chG2hz_b*{`_0-G%N#n;?^5id9m4 zE9{-^D*F^`O!+56(wnKfZ0_FRrgE{X8j4OFm{=;alH%z=8R=&c2Av)3Q6W>klO>H7 zt8u4Vo9R!CmgXn}ZHf&#tc`E`C(cNEGsUGxffX%5gIKHYuz*Nbl$MYo3$O;B2Dqt} zat*m!fR7*<;wDsY(?KponhGtp4yeSHGUaO;=quMN_3mt#MAlG4A`MHMkeZA0NEuKr~wk}Mp)k=l_x%pc*^-V6^JUc$ckl??k zrMt>8uHTof1nT&fNK@Rt)La1dfj`Yc)4;5kfq?r8#lL3(?N&i|U z_f0+mZk3)7QhqoslJKvz7GNF;T7FF@az( zLecs>z)Y7Ap|+%}JtGuo-qNDj3$xt@P5i*%*p|^3UW+97L&J&5bb2W_y5`n>UOq!%kT+t6GE(0q8~n2liVGtY zA&vw2jRe$)GPN-Z;?eba`bgIXDqjA71xrbwSsc%~Qf023H5IT;M1vv{cc;dy!QN z1R2Z^W>S$*%clifR13g$ai10xnART3xsy@}3601>dnnDtmW#_7HG4E}19x$S`hAut z66sxfC2^`fy^&2!vympd>uIH>F2N)#D%qR2lrh zt2rE}#7>MIqhby??@7At3Ozz1Y7n|xnhNA5?&+mosZ=g+=A+rw9F<+NuX;o(yR>Nb z-1Kqt7rrEwT-tpDFS#^6cLVN7GND;i!>*r#b`E=+6S-j$P&|`_Rv-L>OBzRFSpE8KXQeT8SOo7i}hq2}iLaZL<;u#hiW+Msov`b19r63cT zo(E8nbsU)-VY=sfOmM1V$KT1m{Z1%F(5sl6cno)D|Kwqy)Rq#h-vaJZw&>0K+7k`O|h|O=qe)}uXJq9h~ zeyM8?D~!PMfERh_B0(7FWT28b>#IBPAv^D!oc0-_M;Nq5#5L@j?dlB2dE!c`peeWW zQ}$u03EBD4PDa`;T<2VGUI$Ei67bq&((&#@H^l{V|49rLz}rGkFJMzGwF8DUGS+t} zzl1rFwA)FjuPc+YI)x)YkTix2HlI5&T15SZbp?08CaJMnuC21M{k~FICSOse>MN<3 zOxIFnN7@=Ir2VhnP$oyF@LA+)Lc z1pBm1W-v^94TiJdA-aQLB1^MAFfoh9+$$y5*&Ja|`6WvhEFecwR z_P=J6CSS|T2ffN<3+oMD^Qe2(GP`Jt()VbWey3$g%Ml94+23y3r;^VplmmT@gMcOQ z+_LO{u*dp@y&t5-OO{IFaX~&m@Tzzd1*vMZy$Y8}-^s4vYd4Tn)B-kMI^EJ)c)CqU z#GIuGKIT()u9*uH%6?>cP9>jSo7IGq(alq4`OYcuGFy34J2~b`yVy+QeOY%nk-uV> zv+<8)hNFprIffjVm}6<-p$~dcp1J+z2>~-rR2-wa3h>4O^vMpPAK=1-_f#W)LyL*f zS^&1>85*nA=o4K*I26v)yQnRB11`Mt`HhK#FoBSh5^fwx|D_BDDw?Ak?h~mZXjz49 zJ5+cCzHuqWfoli7wUkQ9Nh6yyPFB~dzobNM>T%mv!WVs@l2 zChTqVe`IAb%1oNC%B~XKALqFQ#79Lp>*;eHTNX!ic$Qx;W?!uE|Ys~HosbzarM+y2@bY{v3Dm~)nVV1rr$dZ zUO@d-@(GouI2;P`HQTdJAoxh{g*+dIl>ZWng<}VKbmzGQkcv zG(`NiHJoM+$&HdVTu#<-x%`A2RaONdlZtl7x^a(4JO;-BnYUH2rf~+JlK7*`R%R|T zi(ux^gh*{vSwwHIV_gBc*QvIs&4#3+30DUEHO-t>;fV*|N}KhW(D1!;S2-YWGKn#;dQwb^VW zQD%K^vj(o%SQJDcq1)N{YZL+9F#rm8eF(()a^!z6gCIN>51Z&=@1Ou7XS9ygJlVj6M0G6@NPW)OjTS zO)4c9#}&%>Nb11{OGB_JqzbopFTFrW$2JjF{6igC2!nn}Um`zr8VX<(N z8_lqF`2ur`y&*s3F1_v06DQWTzUk28Ia4j18=NkAgI=dM6wq|{o%kX{L6*lJYdn2< zATUrEUKmXWJl>2u;Kdw@0!!IIA9;;-1PMU*XHC zKB7|N&7!+SnDsYp5hY(c(WtZJI%zsbc7QGP=P-M#Kd}eh8B`m>Jpq*tHa8*()GE&L0yEi#D18rSd<18z)wbLghA+^ zL@T8TP31&^?Pp?};GZrGL8hX52wdLE3KR8u8y5=%5rS2KG&>^+Dzx)rDMRK$H@HB| ztr_VQ&@mDS1JM{QMt)9318#yO@ICE}9h_XLJhzqs30K+kg|0>S&7D;YAVFLxe!by& zmtwYIpM%{HH4MPUgh_s^Y~Igi*%3A^Ed1{Nc1C>mX!Cw4hmJt!=lJ>Mi=J0C?-$<5 z&#(OcV%Oums1csP{Wo3gJaqf}SXB5K&TszgMXqmt6m*XZKgId28|TIK_iO&7`3B)v z{Qf(Bc(Ln$hV%c(vbcWd#`PWjTNCbOFBA{58TDnI`Tp6XEW~$yqS+=E5(DDD1IhM8 zXPa~C25e&l$-e!p*g zBo!jBa^?0-Wma5+Ev}Q_y+yI|T{g_)$#M&weoddcZfLinl@pAy?*uR2^ za^z-uK*os*nc`R2Z2yedWu@TwZ^B^_M(=n0*!fesR*B8R&GUGGS>f1(dy9yFMHPVm zqT@V{Exf(wS9Cmt_5pt0`*S)Di|@CsWA-QB-v4VfrTQ%F4`L6}@q~q?g$F3( zsSAsk>(^979a<4r<#;(x2N(dPYl^S!_#Hx;6}C4diU?c7$l_^c=2p4*PILKsbT9{O z{&-@0S9137*xpMM(+5YpCJOFCEj>~6m9>4B*GHd!c=-BjVci{?FK;{0Gq|a+`B2~R z0v0afk@06$ObU9T{}GKEQC}RJLa(jI`2@E=qCBsU4iVZxQWp-_>RO!D3wp0iMHWez zBLVJ$*}5#dXHe_Xn8Nne%=ebAXU@W90khjs8-Ll^Z_@LcAQEC`dY}(4K5Rgaj`hbm z=`>j(Pz4cE3rdxvW*V<^bqn~clZmtfWz-RX*G^?ZaYij0t_tNzO{R~` zEC;*evBgZT@8ICv=Ii#}#aPlZ^OEXBH5g7N!lSw5^1#B4BRgfwcn7@WH=A48KZ`}s z=K8v`9SakOxYp*+3T!N-E&3plV)zI~2LYQ2`?0rhU_p+O6huhj0TU;CMXMF=zO&84 znXtme@s6T}#Klox4c4R<7w7io4(wt_-&&apUNx|qitVe+Z=D+%G>zq#dwNs(*+eFD z*~He}dv*>?RIYuG!@AuTS{fT3n~P2K;de*md_vG@PHxypWx zc|b9@r#@&#Fb3_O)JZoqKS6#cUOOSp-%Bw`;NvB`8_g4^bRJU}ze6H9L@jB?V0%Mk z%lcV7WjmkebiF7nL#8xg4(aentp#bmR4RC|YY~Kq%1H4=AtlTz=xVAzBpY8au{#g$ zJ8C(aeCitoE#gkqKA-v>k8EQ9+Ndo*!W3W5ZVDUXQJRz(zdtl5+0P&+g@v*Dh|>b) z-;7lTuMHVpFeokLR2~f6q#OcwDOvu&tZEAXgq-?cTLWDTF|R zF7Ofqct;R|^-O(I4{{kWn{0@_;>wO>4kK?9NZQ4vto3RY;SdFng?hq0En8QR1*uwv zWQim+H_t4|<#Kon_=2F60&Nvf$vqoR=TEjB2~SqDH!Srgt(LTJ?<-<*|Mtq|;`-a1LhdFJJk0SmQ z%OKZWM!($Psi8X3>r#Oa`h5>g3;^l@nG}>3i(CcFHo)N#70a>f8*ekSM+}V@zh6AP zJbm^w8p#A7r_Wd5$4CPcf{6AWC)7ZQb>IXonjrNQS)?!_!BELFo9XT9jYkos;%EL1 zu1Nq>h(SaK+IiY}j1kwqE`w620S$M*E2skQ8ZwPc}h-{9EhcwsSD87^OKnw;s) zSI2UmQaxC%@c#A6%20c>Yd?ZiaX+U*DEYzAKZ7C6m2#Dh?(vZs<)4~Rou0*vtP5dzDi2 z=ikvW&}8f-;E_tr*$R%EPviK@{Cq=2$KS%f@hR*ZN==i|@fX2Uze?@3!jI|r``}Ao zrgfoZT$YGpek(|J&;lfi`}bnqxcGUQmX0krFC$q)EB^x>D==@qhk1i6{&G4tG@oW8 zh~!qO2Ac0^Tqf0=YFxH1U)J${diHLn5^iq3yRlEoV%gZ&KF?*PUiLwpKheG(sn9mA zmpA{1jk1Sv{`%%a()~Nn;|&Bn|9#M(Q_T-tV(% zHvj%2*F!C1yXpPK46fhXdjGxa@84|xkqy%OiCws!_fPidM*k4g`9G|m-+!pxKe4la z^UTF=7QfEv_v7X*68)ODwC^um&w^|Wc|eJN-lXXBT<@T`bIA$FI*c z|LY>pAH?-H())vcKa|d+(AmcG=WzaJe*dBN_2<0bRxq;t-G{ z&tBxlOKQnDlfv)U(|N6(J;l#&Mee*`tY1gxaUIS(+28Z?SzJGib&=EtTGzj-bzZu@ zhVweCi=>upYjpj-*7<+o*B5aR=TllMllo~9|AF4K`RnElEXt6G6YKjW zXTK}W&$HiMpP$j@^Mzls441S7`T5d|Tz|HC3mf3qXWIQc=lM_I z`Z&Em=I6_$>t&t&`#H`B`2C04*NYwJ1$py~Jc>GKDZ#*g)!0Ix!tcOSRN$O%!m*XI z`Zl(Jo)3zxFBoqTvPKd$D&2@y(hCXfwXh@N*L|eMkqmQO21$`}fxs1^W@>W5B_vmb zluwdr4qtuURm^e!{{6`lcieSD!#KgNq5H1lzH_bn#-SeIz7YB7z7l?hEO&HYJXjS` z0P0a|C7ng8OiOH)Ij*|y>cij1UD-g?q_{1uOy5oAG~|4}HRX?ew)q{4?JtJ2KmNWU}!=pb?vo;@+i> zdxH|d*dW>ki8SfWxwr3V+vd1&Z_0cm{gcg_a97i{Q`ee5@uB->+59w{o&EFcaPN1( z24fO7LmZ6MgD_+hmI&hG9q=$As*k^~Bq<{3L1n`YQ>xV;rFZp>zvoFd*7(@(eusO! z1^3v`UWp_w(Rv7Qt&816C8(?xa$8d+Il9Xp_JV=`?Cp=e^hxpd#@ia-c;X3(FNibZ zoXo>a+PjfcH1qLx-scRhIKH2o>AaZK-i>*p)Q;gVz3>(Ib0@ZN4bZz9!a!lZkoH8W z6G)4`u8rd9)S?0Ek+f*!WOYcm9#sv7W0gvF)19xe-fKNFt>46cYhE>f?sNSOpkf#@ z)(gN$({uVCL1Zn|sK4qdNDxB0I(Zh6j6=5jPCcISch=YADSe;&oO#tmPobQ>spikc zKZxJOd~yLBQvee1%L@;<(3w1tsAjcEMp5cO22elYqeJE#omFo^vP_+pQ25s&%BKwp zA_eg|Ttyd%d+1t{Zp)}hr4YA2r-v!_;960!1Z^^3>`BBuZltLeYSo^>;vj-63yA{s z>zF5Iv*=w07um)697Ua{tipl}xJW7;K_1w)FOjr^NXxItZB!lK&-wjG<6~@Wk&QP# z+W4b49QJxcA;|pFmqxPv%bVzn2buHPwnetJ@t(!TyTxcQ90>%FKe+wbsDwiFO9=l- zpOBNm3cxEqgKsAI;txraDNJc1P{0r-1wl9pg^=7kScK%Avh&HwC=jV9@IgJdQu~h4y6`FqH+A&-)OFZiSA!ziu^S###w|hlJM9cHz>Kv>m&E zJlqg<_md*Wm4KrJDS*!dKVX`}f`Xn>)f*8oXf!0ti5;#?1U(1sLGWQ6(-Eeia~NgS zM53y2bpFKT4zJhY^m>2H?rfZ9Uuv9nAhVR+?-e%%-0lGVYxEAj>qCP-Og6se_jvK2 zheR9j)GBt$kBeWWYLddH`Vy)}z+aBkE-Hnba>FX&ze89c!rA!R5Jb3EL}5P%7vZ&U zQ~ghNdtJW-zfCEURVWvmspQ0o|& zhcbNulR;}Ux|9BxQLQo03??gHg9*7%^a#sI88vFNr~h4c#I)KyNEgYxQN7+`Fgl`X zd*0*g^HoP(A-&qFR>Eu-a`$-LHBWdj@nPybk%0Qoggb?b_#&|%c1_rH8Em=VpAh@-vmigs31(kXdaO>p{U5LhfLj{h;6oGpEbpJc#H{ zIXC?rl`n4FwrSJi;`kUctH8-)G{Tm*Z`;moJ9li~v~|a-${t3y# zJ8z3Co@(8|W{>${RE5lKZ~g}P!y(hd0}EMSfNvY03Zc?`5~KNb{DO3tu!y9DZS~FI z{xXA7k7`&5e-V*@s>KzMJ5-}l?==Fxe^R{|347cq#EzAgh=o&;RKVvBc|u4y?y|VB zx=b1q*o=y)C>sKcRx9O_!`}WZw~pJN?cdpJ`uhhO_u-DDBK0EhW(N!Nd=~2uTgCPQUw_R{+d46TJIb= zuP^O)Z~uNRy>OCV{a*U>COQnzAKnSxk1`0ZSR zCBaTuf}I$}Ls88WPr_pHd!G8E?1{6_Z#-JKt5U%|XwL@Zg>!GAl&g3jOBL~HZ^MeCEjt|b;c z)%roaE*K^;ARKIbye$m0KO3KFe?zy>uk+W*GxZBggeF+4U+o3*OiM%R*SSlhJ&oGa zYGX-=VVX}DTMwd48_ z{uxtrXZs||!AkBT_^T{73xOhBRbs2LDu)0a2&5*9Lqp$3P0R6gU#V0her0U_hTRR@ zh)Mes*z6wrIM)SakBX1Abb# zbESZgu13hiRubHWJE8nZIIZJwmMlDA#u=A%20ddUrD^4%HX>WWX$s}M*oDt{%^D

`2@K*efKJ4nsG2F9I_Jt=fL)#= zZxKx5ua>u-n~e3GWh>D2?M)NA9=e=L7?)&wJ560K*pR~5Lh|_Pb{YhvkC4u_ewL;) z)LQ5=d^%Gh9!Jdb_S)-S_iNeD8u4Ny4t@1c&2ILu!k@XlYNERwDyAhjU8%?uC0T;h@@#Z;rmWN%)WFF9Iu-o^u2U^4D_jZMLpCM zLH|Bx6drci5nk}w$5b+_MKd4QiAFXjXV#h4f}!ix|A*H{ThDJ_ulXyVPhmCf=WFr) zo!1-w;`=|_rKV@?I@Udaq6B-|tfGW+cL1j)SKOgRq_&jY)d1##;Atv5Du{aFH1Om` zQ81nsjAqejJ}sCnqSnpt_N0(4T%TGrHYX>B03I|L>n^67{Cj*ycr(a9VhlwRFytX_?Z0 z_3)Se-@fwYciw*MEy;8)m+KDZ12$x~sr4W;0k9AoMgrt| z$@4leuowhI7b>PB^BFm}VIFW%^S0Hd^XQy&$Pv(a4o5{)SO;Xlscd5#2+#wcIM*)$ z-z}~m5Om<8NAMrZaDqqlq7A>uNq2z6jEj;;1ucHV?opcDMrJZ9wS+DYz~h~YcMFa2ER|Q3fX0Dt*CU_ zSVDuM1*l1*M|`s$^f0Lv+L%_S5j94&N)b1i^r&WQPzSUItpXl2nW)n^t_d=Y&HD%$&!hyo8*fcHOhM$)HaO`%}KsdlfGvmbuYFqwvr0=_=nkh zKK#74Rm;w9t5zGgyaSRe*{#TE#X6n~{{loOq4TL6h_WFsNQ<6ekWPH5w&%Jz5C*ND@$>)+=aEpI*q%X zI_xyl`0YzNRjA@bm7uaqFV)bQVmSG zmnV*C0ca4DOMolMPHPfjr;*sF@*}vpN^~H150Yc_ZVhKrL+SpRKnQ8xm76QbiRp`f z8Wo@YT^WC3cA&mi@H*o3qAF2Bk*E` zhr(PDW&766=Qjz9I(TMucAed9QX)qe(~^0dB=Xi9$?)EkfYtF@{`kc0S~FAc0{)Zz zT{&9v`aL?81x;)OdCbmFZk8^sBoUf-N&8O6c zwHiGq9bI}oR>v!WfYPh*`)}yz&>vyQz$8H0c&O+K`Dt=V!<4lj1H|1{H!?WVb6u%~ z&tnJZlDr|LoiimDV6eI*a2`YnxIkJ<44fawSYgQP*%D5WUVg05=P|1_#&po()~n1K zlfhd~4bH$gY>fJoZj()mLZ$uC&jH2r)ZCsPcPMPqDB<+bhg_poI6*vldphbc8Z3H! zlxX>W=;ybcub-1I>LB-UcIf9|9;Bc1bI+ikE3ki2sa!Vyy4Tfy&Awe^-~Kb;=QRHg z32$Rx6J6L*!IA1IkPC~LEEp}!IKT!LxXn=bF=PoL1(J!GXe9%1(547&h7@GP9RlD! z+}FTDx)MFVzK^cwEu1Ru|hvq&|oOqA6q|1??d*}b8XoZ^}0q> zcNnlKd!Kx-PN|j2f2t@~L!j3h9z zAs7+T3<&dFEF|Y!O4#3^|J?iVOCm&v)1hTx6@_UYGcpDFAts>e1hB#~q~=A0RuF>O zoRulCS>lbj(^QU1Cvci#cOZALBxA`HDB=Uk0DmK1@9oTM-tU zghFIM!R_T%G$w$nB7Ve!O$_@FCY^*3Y9~^_59|96WJ16V*vA?N5r_BHiL*b&Nm1A) z{DeJ$`~VT)EH16<33#8e5XzZy&Qf{+JGOT6f@9{l0J7In+DC-jbCV!KN3b|$JMkf5 zW>L2W+iNU}Q7*&^Jjyr1A{{(KfzS30;zs>MT;mCfCgc~5xX?!u;!FH(<;=u&%X}tk(*jR1I8BNF!CUl}br0Gp2 z$xcv%tYdm~e!t5Tk9$1Hr02X(u^f&kg26;Q-1zSLx9_*VkzVvGU?(3IP5{dUTYvMn zA8+A?n!lwvf_ap|w0M*FG&7+JlTv)TeO`RJbv_K7YDv5w*9T#n3rXh@BOYp<7n)_U zEdB{nAC?j{EJO44032f;!r!23wSj*XRenwhDd=B+0;OTo#b4kSn!gv`(b6}%>s{*- zBBa|Enu4P9iEfE%vyvcdlZqO9=4Zj9l@CF!>&e113TY0G7dS-U7@z3s%GBfau59|r z?pifgMK)Ia#fa^04hvtGy}v^@oDq)Kj}nMmV(2Op@+bm&1rQTz*=hG1+5jMeT(xy* z$wv-pUVTixI6Tw_MKNJwY-nb9=0a7mbKtG#EtXDR`qzGc1Aztpihr`Ma6B0dLd*Iz zR#zy56^Ac>@fVOBW3t%z2=mI8QSl6XI0Y`+1{1=U#>ly}AS@&kG}1O6j7at~s!G=R z33ZM~Ck9a*+eihnJ3f`l#j(wSTrP0_zur9v5jp=yI_}-m_{g9t7jp(&fm}S9^VwW> zm&fMLCK6eX75lHx9qf*lMniIH6dyf(me+%{h>k?3;Uye_0}3UlvG$o0=T;d)y8HB!I}(bRk1|EF8eBP*SuS*{8cu+Jh`F zzz?c0jgYNdiTE_7{4gzt>yU$)k|JV#995H{pTLXh_7#|cY7Hf&zv!ie)#@RN3wJ!r z1$dL4vzQ7x<7Swj~hPyxmq%L>5)|?*o zZdfI6>%FC_tGuwE`1=G9(BH$HAbZl-gUSPw^Qn6a9Tyr4<()fdE#4$_iMJr1En&q}oIj=@ zt08vL2^%y%lBw4P*`2_IMK=&*UHAp)u;>P0479@DxtRroFR-_QJD%b;$X_=5gwL>L zrqcdx^F57CEuZEl+6DRfcQtFm3m7WfY2Vm<=lSRHZkvE-t}rjeGP35;l!XsL@yEJ|K;sr+K+>cKsfh_jiyGC~U`Beh=ZidvM*)^Va%( z1MEzL()!VRz*bt}c89-gw;vRSr# zJxsRU#2pnsApn?%#?952M%F17r!7rhqnjc2-y{3Iq(a63#W z-X(ZMkA%4=hX|Yv5l4hqt$}(Maj-vBj%+KJwjZvtVB-gc)u~KoYPG<4b^lhp2$qc z7rAyFmU7``)Gs2lM{pH;i{#0#RU^DK2J};C3#A}98FDy$nFZTDY5af%+dcfmMqk>! z<9!-K-hzPGfQ``A!Vk1JL4?{mWI6zBNVEe_L8j=V`T3=(*TtXMy#MgKHofJe2RGps zG!Vj1h3`Nwx#&IY{2nkkq{?J{P>HXL4|LBh|MV>%J+!jr}Ppew|83*R`HM#J&YN+6>+9?`8bGByYywb0&i}cd~zDZ^If%Y2PZm0bNz%lW0c2 z>+m~PN!>{#?+Cmg_W&_-WQVbRX?bT4nu?8vM5jsiZ!$mK58kc+!B0uih`%@z-ic2Y zeg#3L=Zd4WHu>|~PgiN*$|`6l3Q5G%Y{R~yfu9AoVQFWfeHmK|?eT06Wfj5kk`|HD zTAS8pGNQ4@u9S8e7=+yFPeiTW#Z(6EZPi<=unDgw+Mm>K zMe}wxCk`S6w^pwNux!X8Ex;-PfEFs+ffW&cfSdg0D9XAk16-kv5};8PS(S!@IcYv} zi(iaa7{IbFt0+e-9EVb*JHC?D&+&oND!ECNZ7~&UYKJabubPdqwA!mF7Q)EHgqm`` zGrKfdTU~2Y_f3b>2TDWUDOdbtrlb-z-C?a;w_-KAOjCI8=Hqzp0Ny)C)r?`bp-R~@ zY>P4&lsE$=OQlGuwC31ijhQ=gvZ`J}n4tqLey-q0GOu1sGGDWqh$>AOKs7bGOnx}RfyvEHs ztEM&`NnctU27^Sb&#kN~j({^G-i0EF$nH-gOr~|bCQTR#7o zp`HV%4PP1D^>~{N6?0i!q@aV2a08+tWu@sP*|AJ?wwC@(ajh~eG#5**9)4Wdj9D5YJ%1Hi2ycG;fvNL27xgFE9opS+O z5OOYXzlc$X{Ch3G2&P}BUj*-TPfs-J^h7>%<>)w@$OkiChd0;A(+lBoxJL-{hQ`3q zTIY!V!S8c?+eMaMrwn}~p^dXxZeH0P2p_p@dB-u2^Gxyd#7iFEx97(%o;X#;IQ(<- zcHwKdm+m6>La>U1?gfwhI;$PtacyB@=+M41xWfw%AAZqGzIPd(Eedte=4Nq1@PTu| zuLS@*atR>vp8Q&v(&X2|tCM)YwBgrUmMsQgv_cl4R3L>*eTj@p1HTr6sJS_rY@|RQ za0mdo^HrUrby($022<5&JU9eL*0%KI=8AZ9m>Tix|~ z-%`REig-WZaRe}^0a!&ac26~ruo8P1{Z9##^|3fsVFEUAvAq)EjzbbjFfAA`DcvC$ zi3pz@TC9-KA{s`~^pqjhvSjkHJl||V-Jw#0Ew@20ViuIYzzc@mp6H$bo4NOZldCNE zfX{nor?;KnclyruzB}7@b~edwvdO0R2I(P?gd$A{CG?U&Kza!^7z9BpU4afR4THVDHp z47@7-QLWy8!oPVJXj8T>p)QPEmk~JkiAW!p{Q^A`LlbrS}o5YwaS+{@5bCmBJdf3Ar;YqeBFD7~E%gJ(mq=o~cL@Es`o6f7Is{|Bp zpj5fs?3}Hd-X|i-C47A;1~I=o5W(5KZPTfyMFBF+d|*6ZggTqGoYI4%o;yeq^4^&z zpR>kVx1>%zdu4^8a(RtPdAX)-NvnF&f<}GQ--k|0@893Cq^UOf;x7VK=CdWKZf^2V zfcJwK*CLFIVjX%?ohV_80}3R*76Do?b;Ca$0WCT?s*=|!fD%W*a(dll&#;hd0sa}N z6cQEFq?b)TpDGNG5~tuR&_y&_5m;ziXj@piaN1K_j~~8z!#vwPrh5V-gU6rxttFTA zFP_ep%^jI}TL0qyOW5Y=%QgT~feIA07@J85Q|1)VZP0G`)}n+E((#BQbh13v9L#qs zw5a397td^l!$#PQcs7sQT8iHqZDAA@Lf&^^QnDT3jc8zYlxjt`AX8NF#)-36T{3OC zZNuyh=H3l6rKLB#y>w+sN%!o2Rd@Rpmx^|&aq?pJd&x7ju2p0C>>UHUC+^0`}E zN52V47P#YK=tK4iGZ0z3MmSm6m|9m0{DDl(B`cTB^4oMOy>|N4GzzonOOWtbtx&3z z@H-I&g9-H>^n#X*BC;iNSkUWpRT-Bo#w@pPT(@}5k~NDKp!FF;gHs0jk$$AQGM*?m zV{Wdz65U~UG|DHMXIW7rWhO_R3GS`MO(?ROom3KV*5cejGzohOBx|&iP$yKbB$}Y| z=gq*kc#0!m)aropB9DiC9)GwdP*!HD3Hed_!dw#?5LKG$>b6vQS2%TCfBXFMhJPeI=B83_lgClT%8(+{V!zE%U+QY|!FMVmIvmk5d?>ZLT4tVZ zO(JPf*e^0iWjyGq9(H@~qgNp%J*B6e?6x^d)q`>Y`an=)Kt z(60GY)UC7lw4SCOWQT1xTGSQi=$W#^Xw_Qm-GHlZzs5SZ-mj zS)_IAo}l+9)*-FT$mj*^z{rRc0+=HY$>-v2;xAxz=|W|ewW(DjGo}y6qtK+4qxy%Y zHb&`H@S<9tC2-AHkFW~P3Z90VT4cZ0Q~3*^QmT8FMSYD8@Ku?d>TPe6qfc=pTs#kT zJ*%fk3!px1HzPf%|H1+(v$X6;FXgZ~-1d$#v6!5)>dHrWopE zwtUf2?Nrxt5dlvflcu%!o5M;a3VM_c)Wzc_{gTGTsR4gS%+mfVKgQZaXZtLJIwj&#;f>l)hJ(>^%E*_g0Yy6UGU<}5^Arde}_#Nj@ze#9pFf(D1K zCmKur+8VUiqbSc-TeNh^Oke}>quASTDC)ucpOrE>YzCO_B5o(JE!nTg3poKX1R8?P z9Pxq)gfjs0xPwBH-}0;Av=DT$@(>Sl+% zE$3d;zxat+EB39fo*HQBxpzU`aqB9RXPw^IeL@`{XBvFN9^@i1gHd=_={B3n5K zBL*SD+9oeK@4R_yYG(AX%0<(9s@E-+KJ3A-aOTwDQBUBuD2i!bD+jfBW&1ga%dkly z$^)&Y#=zPSrJ75%;==!VT4}iCe-Uoqtkg>0s(Od$s+|MSb3GbY=b z7f?!UdsDhH=`IoB;~au}4bs;;BghrL@DG$Lyg6KFYN#`jA_>ZvS($$-&jU|m5ggFL zwhPmc_2C2{&7Hz8ACM0=3Y_m@m&JyLF!GbX`Zc-do#$ zj%XbjJ*f94yk<2V=-@4`W;f9jHT`v+-3D(>nt8{5^(A|7mwu1C{iDPApL+1q)5m-} z!<)cGuUl@`K5Z~CGH0-QFzuT;e_EaNC!RH7L82$!X|9aN+WWihv!vbArj*T`Go^a4 z9}f-Jec88kqGzBZ|I6{e0LOfq*&!SfcH{iLley;z_K;y@@LC95kj)(fr5(^c9AUxu zb5}RzavTxl*8p(la%ZUZ9-tTCd@{;8qK?OoKfdMo<5};PmM!=nJi&W7tiJ-kC)-${ zi72v)AghW_| zZ1>YJLS&;ZphKRPdM$D$E0ptzgh0$ig(Gw#3!pS|tHrJXHg>f#2CNviF?hl!6;TDW z>|WL*y)L#%*ZfC%i#_ny+JC{A{;@Rw(>}I}eV9y2CraPDm%ZG`E@C3(T{=X!i0QoOU4(AE5C$HfCi~N4K7(M(x@P4yUruet`FyE$(kVy5zW9=5Ie`_Y>!poqPT{-(R_L{r%@%rZTFw zTr;#Xb#&A5-d; z6gUpaRz#KzDAVEpvCW9f8og2j)ea2NsN9MK?V#UU6|k6AK=)6_JqC-`C%> zs?(rLstpULBq9cVht7!g?BViAZat?JuV)u3b9GmVT74x$N%SMGr&go=!g}gxJ$2Bz z>hxGwrAE7uCr`k7Y76U`>g`C@)g-1>O#9!jr<_xgxCYLOgU2FMJ(pgb#6{Ru_k3Yt zuiy(?Q?#%(y>WY+tIraw4EMK0+O1A&h2IH9T&Q|*0;@aV?k}~~n(bCsb)crz0>zpM zuEma0TV=E{Nj&Im6r9-j(=m`>MOTy@^Y)}|I#BkiiIsd>zF{L&@I znU3EeI-8-+mwqVPTO8tP-3M8haaM0?4&f(cqE+4}{uHyY2?1cJ4^yR4gQ(M*wLnZ( zX4F&eAuV{q`ci{gtum2QA-g~Wi62~;PS8?X8NE@bUn>|z(Kuf)8p#-J6v@;}devs? zWc%3Pil}dkOcUe(^r$<7eYnDI*B~Y;SY{8{1Ad>^LluNHHjT|vYBIutCQBL?k~A2l zNQTmLzPrwk+O)1}0WdGKN0es$B)50|Q1z5_xx2hNWcZ%L85syy z)QO{;a}hXn9w;yIU!a$-z^w21LBu8+jFgDY2(U=fRG7Hggk(epjX|r~1|1fHZ$u=b z5!PX|5aefEZ)Uh-Sco@+W~HDpXlD9SZF~{7AMFnpr^>2eNolOFOjac^q6*aij+IxC zk{JI=RRQQ>Nl%Kk^V|DpMJFB8-8N9F45!bz_*`~UMSq=VZg_HMv{78tcP#$(+2c*s?wY=3O9szh z+m(#Z5f_c#*Vj0AecSQ%vo~}!AlFRh?TlG013rmTCM}X_$tDf%Mg_HxO7>GyAQk;u zsNR5jk?IMeSvGGv)dUjY!Cn^dd2^+-cxvM)iz3joD0RB5DoY}vcr#vMHFV?9p#c(9 z=T)tUmM^QDGA}-y8ZrGt`h(?~-T1>UG2efsSsIAJ<2oGE~p*ROm4_31C15O9gEtH&N#FW{4t26{QG7c-WU20i9=d|&vFbP$J08{4EyfsZuwX$KTsy_^Eb-3DWHtWq=B+b^Vq809t z!Rxo{G$neJ#8cRH5DX#?gbky-%s};Elh0~FF?x?Z=I}Ypk+nU@G266HsnWV?V-;~U zp{QQ8Qh^Ym(sH{f26eH{uzhi>rm`|r z=~k{amYGc!oyI02bfK~=5YoF`HoK`*VG3H15Y5;SRIosO>EQfCdRB$M#bt^)9I-&D zB@nm!9PuW7a*-w68Dme|Dqa5m&Uj^S$l4T8Snp+fF@rq{wR@$_UUJ(_+DJ&H_LWHo zTB?l;I&e~&=DVul>>-j*IrrZpJc>9FIQ21$Nv}bQ2NDxhQ16y((?B4?cQQ^R+)j3D zD(s~k_mE!{U=(180Y3$dhx3g3(O{$?Z)%k51waHJp?W!_36&Z{^djU0zXsF5Enj+- z4=S}N{^Q~&$L+*1Fplw~%(x-FH|@FL>`Qi0iqI|VR-G(fCOO5}^y%x@to{c3xSK6z zgY$+tk5`%5B6cV?qF7M?xM*Q&p0)&ZDjf2{P=vrrB;lt5_EV|QCp2VgkS2_tAZQjNF|IdGoE@I;}VtrkBN-xT6Pr+_G97XnFI8!O##4MnsR zqB22n5pE0yZW}`Jk<=4#15FdWCxDR=QUyJ=T&zMyy5;3arPEp7+0tBFQ(aY&pem>k zy5cOZmxZoE%?jE0l@|a=eaKNk(n5?0{1M?95ryy_#Z|}c{#J`E8Aw!ARJa=>gI#6K z4zsP+Gr6*&B{}1g6)TQingUO-wiOyXk*(HhTGb|>%i}d^U6s*>2EDdXYi#nmOxEg} z<`y+OM*1r=OS@mw)EnqiX@%++Sc?+)hy`{i_I@ zL28lT5Bxf}{AH6K$Poo}FItv3a3HZ{VT8WL7B0zu zYj)Ud4*6wu9v7TBJ2r35oO!X?GlS!N#@;B9(f>FF@dL!GduV5LIBJM#1HOU|3Ybfy zQm@rB7@Rfp@upEMHycb^BoWmpX2{$zP<);*<~|3!x+WT=3<9O#jdS7sJWmqWT0wWk zeS1$`%XI55dh^;#F1a?{x04;ms@mG5x7jgmOWM}70i51$!x-T=09Ppkz^tmPP`TyRhpY#rLuBeS zm%Be%EnTouyZ&m{D+Z)9*fX8A(??$lrvtH>_3YWvw%H%jnXby1VNSnTm?f;Bm0C2_ zt%ePo0aGEi1;u6{pORh$$$^1Q;r9dZT@NW0*lHo(RJSe8+tA<;A&cw`P`vWcBwq?;N-2N3Ra;HPnzr((LsJu}IaT3M<;>aDHFKi%=_u`Q z=DWfkW7C0nMZQi5QYNIf}*}KDl<_;33<*VqZ(|tN#Hp` z0brias%uv-E4f!$;&jKug?(7W0wlz7Ke7kvN9w75Af)v`B7;k8?6Pb*ZW%Hbn5 zeuHrOv|6mZK}!NHBC-N4?!E58U1|2*-t?|rXz**c*IF3i6BlEIud=(+(xYschWK^c zv{0M!7$3QU#<+?Z((k^@n?##-G~9@FR0%PPr=Sg*hCk$K&y7Y56|3+W+DE&B%2g?n zw1Nls_uV`o-sF@*?0~#}V5zhF0iS#%n<25J&=9}@?O5!v$MPnKkXa5M=(%fmIxXJl zlAB=%FdkR79kmcoMSvRsN4UyOy*xfZjEOWJ!cI1-f3P|Tw}TqdV`8j&MEBJ3iD^il79w_->)xFi?5zi)IFdkDRI^X>Rk z9<)O${&AWCl?I7ExUcsI=c1XL9HXzD$(|gYbp!QzH0{{r;q2?Z>l(Qoi8TOHS&G^r zdo%4BUnAnIXgK(D_@-fz9DLI#9|9UY_*%{G0ruRJjaOfd6#_?+otH+4rb0NYlyM+8 zF*-)_*5d#>1NqBx zi~x=-C9fnLV2Pq*u)mna_a153eO;Qp{Qdgf_l#K&Ex{ht-h5rL*N3kwnsf0LX{A|E zm@zndh%W36t>@88B z5zYhmw>nv4THF~P-7l8&jVZ>96W?#Xetf?%!tY!Q{Bbwi7?%!QbEQ0IT0ePDCVG|& zQ2D^CozI_&G z$_3sdkA$;C%F2mUf)mZTeftLnoZ?NO1qU#kB6 zKaVcx+xOe_$-Dt^Gk$**bUR@q0E;Sydk%nX?{Pmv)0|I1&>Kc8iZQtN3AxXl zUjgoeG({8$w1;n`V&sCS(rMyC(dL^{ZBd(A@>qg1`Bybc7|NP&lrO1b>iyZ&fO@_tw1Be^O0jehv(u!CQ*v!- z1JD zb=~fUN4}Tdb7%dd>_v{)3Vi%T`OHL?&wQbdZAc?E;l4Vyf|bd0Ebtv)zGpIa9BF@d zU?9pi@=e##o)`FnFQsLSwrqJP+mud+#ho~J<@jw5KWl^up1GDc?!=iZXB(xziS<}v zjDW^ap#5LIPqrjKOX^vjuRf4YpAlipAGiRm{OpXPu}t&~v?fOQXs(U3jdv3SUU>~4 zOMz#Aj3pxgTtEDT7oTr_qM|F?pSdrcJ}=7F5|hBD(7fXKBg^W@$%50($x7t zN&JRe8Xz4GJAiG+n&r;m(du8I@_6}#G)6S~gS`GF*Va+!U-tFfMEVzus^kAa|ANu{ z<%s&13G0Kh3K%SOIH!Oq=KY32(~0GlX*%$ZObd=eHNhFftTs^%O&!;-oc-**H>JgS zo6{t*kL^!Zb^fpV`=UDZ-kV-IHO+pPmhO|)c}0C{QENV{4*mL_f>F(>Op8>U4_x!) zh3V_r%(Rp#@aP4e^8dD9Syju?&x5;2UaWm0eKU*l^$ljv--PIAb561Hdgr$Wu3^`g zN$;~O&Ry=hTIUA23o(wi)+Q=@UoXBzk(a{H0+{SlNZcmA~V zr4z8Q*OW=8ua=c6@Ndnwr6oNtDdt63F~6wnJCZ_W_ZuBI0%<)b{g~YpfzX;-MAOB5 zbK}zrGB3l0&k67{z(F!Ev-kPV@1LLE!?u@6cW`c|xXlTUv!F=1d0*#VC{mjCvOCM9 z@7>4(e3%$#j&4qDe|hp)P|la@ZXRIOFE`%I%Cqy$(awqLOSz7dE6rZ4zv4TjMEUEh z4LgZfl5uU0mS}_t>~E2(@#We#j>9}(YJB5mF_ax!uB{30FQ~DL_#deBUar3h`} zL%M_G-=k?qma);!&38~cJFjO?kIuSoEFPNBcrYctLYF^G?VQWnnF#~(&>9hqk?m3w zuy-_Du=@bigEw@(F~EKyy;H_+8IfTWj)Dcc2Xot((mf=6P1rcsbi#d|58Rm+=dP9> zD`WGnm)pwGy$Ry%0;u12b;lbxl+Q??MA=Q!eD?X+u@PScC@1m#f5jIS>{6Hp>2}zT zlkVz#^d_)l(of3RYT^KMYb(n#6QheohxYDo(M%SxEz$v+$_?z1uh}L)uyn%Mj%)AX zs}uh(>Bmua<1b|G0gW@qziG&hp4CSmnI$OZe7W75+THZlJvXP>0qMnbmLC|?Ue?Y( zZhJY-mYe1IIJwDtPx@k!gI3Y_I4vSwJ1NYM++Y#cY9ZE~pq1_T#;bSTnr4@Nm}U(e z#Qt|}(y?Sac0G8-BWaetIsFXNWcP;7_EDTAP;<1zrG2#1_%dm#Iou2$-B}_Tokn!r z!!>(g_6fcGP{SVC1c`YT+L{o5TM&C6IKB?8vEUhvk4vA5WZ)Vzwu$i=78Ky%J0ShG zh7G3Khtl&6Y~$9$<~dRRr?sHzyX|b6>#g6qyy5Zd*kfO4?`ZUNS#1fe;FUXScJBet za(lzWIi96CyQdT5S=5|oxv=fY0kP>(a6_-67a-=wyrCuEMLFrKgE z4-|QR<;}KT_wy0`gY7*DL%Nb+*O$p;b$nm0e{o?bS$^fsSA%9nz1WyH1OXrN1;Ce!eHlujG}gFK%sm6h!3bm(~7KS|;ZC=bT*ff6uR=XPIBQ z6)5gW@GB41J}>L5a%(YRTwI_OvV!!|oPP8+ejbjtJy|i#DQi|S+(C-r>zQB1p|#mF zIw88#+?tgteo}kwz;!=qx|Zx@GX1K`pVJA^LomEtz+gLWd8zLFN7AB7y80(|Q289* z-h^N%FLB?3s!(aRs7`uYoF~sOH?E1UzdSHbTX#QKchN&>_Ner`AJv`5T2DoT$T^vv z=h4aUtaWFja#u?ZclX`!<8wj$)BlncG==ef4Zc&>(wzFr zcdt&f|2ivuBg|zo{N=`&YxB!(iFpldfjfTSng^~)?|dwM6Z;FSN1a>}6coYex&U76o&LGp1#R&wuN4ZM4K zpm-6&*>XUKI$1 z{X@OYMW4y<-I4j1=o2RiA!HzJ6K<4cFsn_4Jco@9K09(Km60bm^70oPC_Q_4%ws&;*0k(HSPw`%$fM(`t7SrMTU}daBH(ws zjCvu&LK>c(wGb$Vpbj{9tJLHXPHTZ6A(hHj2v49?>~i4NV?#@3PO~`Yjr2^foKn5o z6EiHdcg1F}tBlPnaW{s`0`Ur4qRY3ns=GlmJk&QY=s+r$-_2y!K>g@MsO$B~VW}&J~W|MqPT7`apCVqfxvq%4yeeNkd_pW#ku1(L7(=eu-*^BU%nY?#w(tcMQyY?^ty6?U^0 zFbkyvbs%i#If>aRdU#Cf9eRhQ6wm4$l#-Ya9Z9iTn~?#_5zT+vXRD6ukI^OUAJT`F z`f_ZTt*zami_eG{@*ile#LQb*zrQPn(WXxrX z$~D=F#F@y8BzW9TyWVH;A)gciHmOh&jVV`6hQc2rI2y&sVU-;fkZPmlW01i6ZkH!f z8;ov~5kMpgWPp97z5OM}Qw@A%h)5I%$$ykt3`Hh1876vL)X=yaqb0ikhdyY*tS*Z8n}@N>}dh z@9uvZ^GE+f=-Q6cJ>+sp0z^#vW6?{``EAyO|-+WG*L8c8`(G5a;$sWzq*mwz^teaz*z` z@?72*tA&SfRyBf^+ItLFPqDMyfuZ1?W(1(b4CO8Ql_i$VVs)P;JxM=v1MTbEnV_%) zG5G3|OESkCI*{GR1G#)$nT||@;L7|Q`!rWxC(7YRnfV2_8RwPq-s4SBtl+zXMRMaF z+kS*wOL7F7)fBPR2k6yhLcJd?^?2zjzJh zJ4f!7P*YTWj&sQH3&W7(=WDvV*I-{JW&A<}FrgP#$$eTe*N_a8$a;kB*a8o0%$!mp zF#%IX-#r{2erZJf(XI{-?d^$gCF1 zGPh%IZq2x*x!GK4bMt%Co3RRRaIz+$oyME0EsC9|gTi-74%sAiGsVg4bqLWkF%zmJ z%cDS;QY*zM@%$pHi!2eX+GT1mgyw{dJqI;NFQ{s}?zX-zz9)B}zajKz%k@Xq$-pHc zRRazS;#t6Ihzs#mI0pTRh#(V81`VS4m@U_zoNkYICMPpNc3*9Mhe8a^E>lEIUX3nt zu)bbv(_eF)Srb$#rj}?pj7!Tiv!&-VKgQl9_*{9N<7_-C-)T%v&hnRwz(PEGKHGzI zh20nQyPVPQx?M=YWeY}WeXBsZyE2aehaD%49NJIoD?WZ`e;$VoWCF;qd=S}+W=cOi z^i1~tGr9FguE(hG+sv~VQK-n3iNpv8NQAbmn*I`-zzZ`F?>vnRN?&zZ7`)Kbdy{6FW1$ zFP>NFAn@8ZzIbk6O$0XEg8dQYx+HMUMA|JnH*|}Ez>K|fq;sPL`a$+2wRAAXR>#ur z58V6r?%lkZ!v@RD6wbj~1cl9fe{@8*z^?MTbvB$MKRM}R48X1&Oz;LcL9GY^6rgr~ zn*qCi@$TZ3gOXXfoOY|(WY8gYJ;;K@it}^Cx0%Lu8=1)N}}Ql0=&~Uak#4Tv?&9=!}NSP{gRO3Ps{A$PU2aq7K~K>dbrC_hIRlL%)>I z-JxIR;i42ZXC~7guxZk*GTg9YxM|7^BCpU-aes!i=g=RrzyCx2_mRvLp+EB)Q|qtE ztUL5c_Wmci`_jKNL&Cbu%lQ3l>9RwA%-;WF?tUf&ywIK5i~G~1henn1{ZXZSUtrSy z%sS!x%)Jxm2Wexa{YG}P@upM?+X+7S?Mwu<>wbr76D&igpI~pA@H62DC|^zE+AnRD z`<1rl$JK?sdx=fOymn-+9!2~)ao?kv{J0(z{)f$FeG?y7oW}KFsraqZ|Cs`BL7c11 z7U3~r7rO#&sJ;(_WkYoWGV}>CDgIdZjXjwAUo$T-tN2gc4{|u9Sir(zEM|h0NC5`- z_cuqR+D=8op`Xihd@d6c1~Z@Id|jMbJ>m03#~zFWFFQ&c3nzz60ovf=$O)(zJbLk+ zBcCrhD+|t7+Vz1e9ovu7wU_2CW?Dg;5}-{*{5Y=5K9|HaQTk7Yz%iUUFb-oXkjvDj zS&c$Y(O0WqtiE8Oec{J1z0^v4lk`uV+f#6EJ;Db0+>$*?&MqhOWPUY8D}W8&f*FF` zFC+@6SV@m5kfz2b-xe=8!d(>U!5ER^2@7Rz- zhBj*`SAq)P8s!k^1?%dbXmvdiwBC zeRKU#WT?KSUhJys?yBzYuI}ot;=s%@l9&>nQmld$lI67lCV(e#Jtl`qqJ0H2>CqhV z(IGf2Zd7x3@$oCDA_?kzQv@{CBvB!b8!6HCfv}>)VN=5ORv(Bc6!dYXm;$;lEw6D$ z>zkY=Q>@k-ZQy6=+v4YJh2lvh6$6n}yhUe4d|dHX{w%!>S>Q~P8^J|S!Vqx&geb0k zj%zC6)Q4#PXN$8SgZ>-OjNy^~PjEenh?F>cdyHKNR4@J6CC=i=9=PE{;D+n7xPin+ z!VR_H_uC04>aWTyl+O)lX@QSi1-e#|c@8}92*H`0G|NSduOJq2`ZDM1OF8Mz7g^%P zDM*EA0|ml!k`L$c)hW+u#wQY=p5FZk*5z)o1A*8jphW{5rrWJ35F-Y0K#U_Pzno5+yRD(t2>CqBx_?D zlg8oEwU|1qEoH_~ZCGv7INgTU(#~pY$Ru|7x)ddTe{Wi+Y4;^d)ZVh*v`H%{G7pOV zs8M(e;5RJP3QPG|p_@eZaEx?4jSR($RT!CGEh@DN1n_G$xwL&$fKM|s8lg}kxl+Ey!z5ImafDB!Xa+p-PH`UOGi`1kl6eF_z=Q%#RhH~reD&2% zOmj{4uA;+fcSW35fAI3&G!l_vRzAPqe?smm0fv~_BQ9sz1zRQ9x1g1O%9i_IXK5&ZY}kh93K5F z{{maYVhI@w*ZoFZ7yod)QL8mOJ;$4MT7%Pb#n`WyC(9@^abZ>FSMWi~;*)I58^T7B zDu%#~D1@B17^sHLup>>FEW1MsjH(wd0P?PlR~c6ukGtBq`j^X=_4eY$+orSQ$^0JY zKF(Lr3+@^q41Bd6$3&#`5^#~XjyV#j*LSwJYwKW_=hM?yt~?!&xTTvhhP9CCm4G-i zRxt<w^`1SeOZ{w-V=Ii*TB7j&LzD3|}wYD%>UP5#OO+h6KV) zONpnoNlmM=MJXbS-dZEmcNp3gI@I@AZ(-)H(oTg51*O(In61a&t+4uR*7X7A>-VP= z-k{IB-p!nSo?eBk%;{PiX2F3_T2U4amaUJo=;YWWMWi|!SzpN#gB4R0n!z6y!V&OPJw6Sr>MaO^RQ z7tNkEefrQ)f2yOsv7xde9HJWV1_P3d2zT$fd(XZ1+;-P(cinl%?%Qv;^@dw-x%s9W zzje)3*I#}8m6u<#^U|FcZa??p^DaL7o2Q?1#yQ_OdFyE>o_4~f4O=#DS-bj}^~bJX zzI4&b#VhB}owacG!s#=o&m5UCG(0qn+NJ}k!T!OXuJ*o;zSic3WMi_nx}v_aJ{}Dv z!inPA^K$-om0rbbL&&6KdiLPK^#bXN!<4$%5ywj%k<;=}^)X7%6FuyEIQw0heb0SI znjf@+HV(Uf_;ZJU&-HhBKk#+Twuq!O_$1wY%Q|u8`dg&wx6SnVd?-@tdl9d9@sjSx zA2vdtX5-J}ZQ{z-(Vx(@+vr*^eL90aokgF5__GQx`gIpx53X+=ZEIV1=51o#Ep6-8 zwcWC=t!*o=`%38#{i~DTe7-5U50~DGf6cjX3vwUw_w&EtZES5@f9tL5+uGWIdQh-w zM7U4kL@ZtwM~h^cyUb*XnODJ2{QI? z1Mw1evUQ}@GPkd{r!7JTp;@(22UNo1^FzIp`vcJYu7@7j{HNW9Xjxm_77wC+5HDUr0Up(-UPq|edzshN+UZww06 z!UEp@um%VNRR)mO5QqY1O(2leG7YC>ovsrFbwe^d}iQHUvNcA{ zURD)@O*b+lRoBff6$-rOB!}4-J>;0=Id|)-tGlN5KKHwo%agtBJ;yGEeE4^9m_3dJ z%4+?7;VMO=tOrsw=JY_BzriN@XV@^0%3f(Qvp<=|6_W^QIH8_7I|KHkq9J8I49 znymRg4v8zp_W`Fap@q-064pHhDjO7J57caecm+>vlJFr?L?c!d2Fi;RkR4D!I49=G zxy(vz$4+Wqx?pKbvJ1+I<>{7X3zp!sL0sA2XZ2Vk{(&C6#k^SOe~GuSrxc5@&UL*1 zudW4{r6NdNNm2`rH0ed8fAv0hT4ABZTaH~SFEpLKkxX1{hRmVL^+C~3jM64k{+B_5 zwrm|5g1i|POHSfIA(&*i)x~}=dV^71diF1SyL*?RJ))H@WN(IUt%T^0jXRVkU3Xo6 zYUFCX#JT;czUg>j#qj*&faQzlPA>8eCJc z>F+|1m-()E8dEBw;Hw-$Shzvn@d~Ui62B`^7F7YY5Nc)Oizt5qA3(PQfNX^lI3w>P zgjymic{wU2={~E$03cntavMB`KyE10B5Fy3SuA?KWF_iSfonurDpV$gvL@hjhFxKU z!vN0)H6(GS*B(hpL&Uxr#<4LaR^PO`LB{N%w@eY!B728_4SjdmLgJ5|c4w4SF$0F+ zthg>RJF3d7VHBAkVE%^u{7)>*|Ene#J$ybQW{O0pYdJgo^(F>ZB}x7pVRFJLH44S04R_WCL4L~S{{U@3_YvI!2yPPV}zU9iKMQy8*j zuYwo0R%h8Jw2@e~W>IjBf9aNXv3vNs)a?7`h%0oa^2*9mdsCZNXRr7B>tRVaGLGc@ zdBSpaFvJ;xFj|N^>BKJJ4#{a%?$t zj;?0Mz~zx|?i5!H_YH7dDLs)131{L*L18OzOAlQpIjcZZh=3j9dPBTn%7H|A^EdFq zU}TV$Nu5YQ8^j~XO*-xqvb8!W1fx*E@Y+^H`jy}(auks&dL()N-e^Roi_#O9cJ-H) z`O5<#rzhwzm-#}I+Cp{FkS)^SJaAxBsl)F8c)FA|nz~Ti6jzz6B8Ihj0c-QO;;|QjQ&N&Gt!e-t71I!6zJ6_#qN! zxUbx`iJH2Q$7V44t+C1}qQ?~Pjzra6B}$|(p2PdNAX9_<#BW18m*rh-FzZ;MnTDMm z6aykE)zlEUcM{b_PzW=FvAWU{gFDNnQQ<@E64}4;4z4Caos*v)B@fQNyX*_S_~(Ob zpQA(x?>;M&~QhmX(%jR3%C+SbGS7eHK0o|)$mU?j**_a+zno2b3pOuUWADI? zlFVFOUj<(}J=nS^G@SGK=qMx#saqB6wMeCpVM2D$t8|bXlqGZtzLKE(sGJm!dE8V= zy1Ft}AFmGv+)+;yHCtRpmyE8dawoxI&g+ZTGf^ZY6xYNK7x^G8ZRk$bH1zb=dxAla zD-?J(t4C+I0o8ed)x{Obrp9DjV{@{p%;PDe7f_vy*K_ZfpCWUWcq|KmuXYI$c#39c zSM7DcjZR5kbYCSF#iAZJJfJJ0HL;p9zdPcI*epgpX6ItAoLHpDjog)kThgDaFjLtY zf*o^ZC#`mxtCDTiffl>1HCWx+S{-b5*jwDOpgH7l`MvME`~g=qR3;wVSzp~9iv<%M zb(Ou*c&MVW-R}#zonaPoyMoCihpE5djQ&h91n;&=zH4O$Ep#$DT}Hu@3-32q9$RvJ zL@DY)G}WM)JO{9ooY!6#Rl9Z9UaNDfkz?IQ>p1#>c$)O7;*2bu zOyd1@+;Rd~r1P^oe|h$OcNPE? z&fGTNo}=EmcZ^q*o(11}R^~N6Ud^MWDDx*aR4WS_qJ*pHv-JDeKvh*`Dwe9MuGkYx z<8yzsud2H8)7m67o~^a5_?s;Hj4lK=`mpplyEXHa(4VuNp~||T(;$bupjwZUjk17r zz?pKT0g^P}O-bTYztG>;(~*qDBK8>lGa3T?j4&$DSEg|FmK5GZS++4L)O95&FCxFG z)T0lJrQ!DKP<6-~u(^=GyDAn#!F8|G7VzR?b$hr}>{o|Tc-pRe-TTRh7T+sj_dtJ9%p1d*Rbm?tshSXXDMh3mc0m$48hz zi{yCdtPll63(W2;&0%(Vdu6{VhuzguQ!gqUQ|##acjXbK*|=t<3U!{qTW0(juTYD& zjff`Bdi?o~y!B#lv6Z0Z)B)<2<}f)o%H?~=`j!IB36HxXHT4P?_2+-ArcUIasAs)l zv)pk9MqyB8ztG-hT!5b_vUPp6Z-g4b^^xaZ(C^m(@ zi>m`u%ANIAOP#BHFo3`Dp@4h^(-TUh|6n}NXU9_so7gly%RX3-U?v8y#y@~v)2nqb z#cC)!;tT>Y8ebJk?KC!^W?3cRxiD5kcC2zKiX)Fzu2M{%^E7hQ;UVTKJ1f=|tuUGR zL`HASPQ+xInV-_@`FV~%x#EcyF97G6_GN6sC7I8d+8|^`4k6D0_kVjx)}esDelBhh zK2#vBC;MjRH{$XBpL%YRpAnDGKl5ii^E1UQuy0Pz92n=Bw?u>RYsGD7=d8>J<2*Bp zcAliZO3I-hI&xp1pq<}PJHk}KTX+T@{`v6;R`Gb@MH-LxX~BNvXJC^a6kb(KhOKi> z<|FwTHmH|`QgIKTqm|ZyxB0M$#`o~aVm;nQ|39NK3UkMu>tFE9J{lv+sZ@;9|NZFy zT}2xGFUqyU`?nYR&(7&#R^0!4;_=L(NTdHHnPLH}AUDp4Ln|zH`zy7nt9hKWp03+6tYaqMB&s-!e{N1FDge-$B;T zKSqB3VC*1$amOk6AAn&cmmekkr51u`g)fsD-;}DA@VrpXqIQ7C!4x zpe}MK80Y%m3{_3`c)%Go*=ile$|ifc-x)F6>KY6cE%tbsGeXzt8C(yW?RD!rV;ZAQ ziw*V1>qmv+R|CY{DjJTIM-Kk>8lV&?aS{msHFW`<`ZekSSMzLfc-@J#XE zLGQK!{!jGG%bEWap2@@ysj5a5AQ^Tg45mmbdd7{BH8+c1$}oo*(jO zeu}Xj@fqy?@xpHjw%N~7N%V-%WcC4Wf2Np(XMRyUuQ7eSfp$LQ?YxnBew=o&Cc^vt znKv`P9OoJNj7&yf`^KI7e~X(CvonNe-YPyLW5$!m8SL%iXNuPlIBNsWBkjdInM31@ zXD`mai9hph=GJkZ`55!s&*$~aao6x4Vg&e!*7^O+`$ujEKA*>9-|3lOWzc30?~j?+ z53uiE{>-n(9nbf~2y^mhKFHiSPCK;DuPLz3;xWbRT!2;ZE%&1@EoieR$CJ9Cse=Tg zB)@YUVCdkWk%C_snQ7%HHzhf{J8Wjz7^MITbD{iQ$DDNxxKlx<3*6q_M14e$vWIRiI_Chh99kZ_*?CbM<&t4su zrh#UHUJZa=%>aEl;#%WuY!E)>G;IVCg865P`y#lJ>9BS%^Q&>5c^YdyN^8wN&OAKM zGd}>FiF`csGT$EOnYYl_)3mSRJosQ9c|6bJnFIWpMa5@q%$R?OzDoHsOEM1}xgBX0 zwDsT0Jk8AN?U_G_+8^g)`m{u4fa@;Ih%CE!hIb6XPMKibJ{)L%2N4S4SW<>Z==35Y{r)L@ueVYCKr@7xt*TM?(kbGZ?9r6A! ze!mMe@^_gfhyI-X{h#IE1GaAv=dmbc`M8kc9A_(>3{+6}px7V48k8(s)tM}sP);x* z9150s-EcqvXhUR=vv^MST8?IQesJqbwPc=TU%yxsT&m~^|h^oEv;1x`iYOY3;Vh-^UnXfc2s57jw*c?IHA2R zctY~ytQ}QEG|nb=JtN%V_Bud3t1(FctK7%k$XqKOt@3L zl<5(BrV~&j2j35`iUbfvq@)5p<@99`*xs=FgZ{dhcquu)!2bom`;)kdU8T^Y9oT?S zcaH-Xu5X7ri;;&T1%WEGj%t>(RXNxaTTNWILZ7gUn~I`588e`*187T6ZH*g~Lv7{b zahSncQDItRO7?lgO-5Twt4n9;>_F0RTHE);t?pRe9R^JK#7;z@v%Y#&CHSA&jX8fIAJXNo?YcItznWgdsi?Nl%s zvS^|GPU=rQsn9;}j`#7Ncn15In1Ox|If$?~Lxe8K9FYSYni%1^T*!S%4oHimYT5jJ zY%2xTpOFu4MI5b&`oiB}zgJA*`_-7LLwk@h6l3uzHW29at4>6*UYDIID{%YjvI<=B z8*ubMl~m|!172@{Uh+CVFPF~gStA0LZP03Uyu4HdwMC{p!H zW@a>tC*^!89*0Z@$9sw~`v^1@{y!IsD)AL~W1pznDO0S>&MdvX3A%Na9Fv}@qUqv! zqgVwm;a5Q8?#uj_LM{J7q0ZCDH^kEfH}*r#=1COYiM`HThBzBz=D*i&K-$*dUNUZbSf3@F+1S?i?Y`aB<4W1dv0OU}e@^s3BLEaphS)(y%I0r7%s^ zDSR4}QX6aKltCQ_K?f3ptdqxW85OVc1UWr#qF`MQY}ZAvLu00R(+2M zn-)%liTX+>8%!kM^?`7gxIAumYY-S=D2;{vR6KStMe^DU&T{3R*09{g4@oPM>;`G2L3$ z*49(5F_m=8OZi$1;=fXzR;RVs@1qP4ERz;H*=+F^*q`Ug@DhL-){QE=hk?p+0u_q4 z9u2Ha4SGwA`f3jpJ^ zhn#ME$PEJ@oymes`G;|3N|NuPhk`utvtrq_Sk4n#!N)&OdLZ_vVtrOYJ_8T_I@ill zJMTfal$QsJ+sTlg@Ta`51LMjA`|-^G@MnILdAiU}abKtoOnO4<>qDH=d|zYail0KR z2#^dTf;TTbGiE#w0+0E?x2VOKgyJ`RFaH90{`ZOmyn%Oec+?T^K=hBy>!sM&GO$$u zoy~9whR%3P5rQH%VHO-j9ppAbPqFReJw%>6x%(v-9Nq?8Kp|ja2yR>48O;HvEt%t) zIYNWIl#Vu3>@y-dud}Q|6tyXx=(0skWxdP9O;bwLp@6zn7p+jLOPpGD?^9?}f!N9I z!hZ3$fK{&$7LMa{G$X483p=2EL+F#Jz(jSJg3QU~5+M)@rV}I{gVBlkHlNSN)8P_!XU`2=EuTboK12Lk`gnL$(&k<8(N7_&>D=sBE2KlC?-MU*(I&Z zOt$`wJ_*0ieHwjF_^x~{^Bw6O`4c-mcTHL+nsdLPPt1(i`0t1(;@T9_>t-f{o|8UW z{vfi4zDpMrC_DWQeJIIZP-4l=bU~H-pvH$2afK#3`nL3eSfQwa+-8;|0aF81j0ac| zKKH0c4p~Kc7Cnjehf+taR?de~N4-MJ)JpY;Mgxxyv&Jkt8KV)Z=c~&?83SiSA&a1U z>M&l?RSk*_4MSz46=jlyck$h_p$2jKfdiLacIX=XIRK$zbiH(ec%$MH@QP-f3m2rh zpb&-UMK#v$34THzB#_vSgi-LbRI0W)?V{CeGh1!j!mf}bsI=M6$9V=j2^;k~M6h|a z9)((m0>uWV^Ba8%y-BBETgpskvuQrwOy=cfmVm-+GS8@~si;78<&fX+cH8Zx2$>Ot zn);ghy4s5Bit4J$SR$4vkB6e6Xe11IDOeV8``{_yvAgUpr=!$XYO`A8@D`FZun-nY zBuo0a12C3=l1or`k~bW4Fi|pbtzurl*OyiTodw@#sRr^z4hys095J>@Kj4)QqRU z<@TFBgx^axLce!}bBuQOfhK**pZRCz#c`hbEuQ%`f2KF{?l{l<6VJTHpP2^TQogS- zv?Q%W7^5XEUBAKb;xUK8~~h8*_P=uiLco#sI$Ic+8jVi^Ijw6wih8D0eD$ z;hE{hJWBCgV9O*u^A4>4#&O4)L0=#6zD6>i71n=DUoCj%9{$YC;=YQX0W2AWkGPE{ zB}9(gPA0F<7iNvCKhN72kWHeOBP;;%Jn}Quc;-pP$r#T< z!FA+kz(;0z(}ImumMaVPN5PEo{D#IOEFPEd%-ckSrQ_N}K1Vyx@phJvYoA(%6symQ zucEIGA;f}}HygK@azf`=F~{FMcMz@?);6#!kG#pMdTy6B5ul)^6r(Sg*c;%QVyuVf`s^_@PeQ?~x zXQiO|tqAX~ZAT;Txa zvC~y1LQ4m6BZ1GdMgn5vXH7j3jT)&3P3p3_ZMD~qgNp%J* zB6e?6x^dHHDDV8f+K^T`g-AX4r+;G2dSFBL3oBAO=$u6lS$wi91-Tn#k+B9_Xr;DN z`bKCwbBpALFE)81t{(do!}=g;k;_50A!I}HGe7wwB3%$IO*EQhL@gO#1>&+Xb2ik} z8#mi3>n54lkEG`<>|UeP+nKamd{LhzW+^pR9SC(-wUkd0TV@T8zNqPLt*o_bl+!2# z7IXM-{Am>3pwf|4P={jMTziOHCk>N)v6WXN&*ce$2Y@kQCk<;U_v10- z!6pc&%gG;t7}33z*Wk9Z=lO+o zQ-ilzueW_?pM2(Mfi`W#zFI|QQ$4&^w&g4ZuQab17HhV#4Hw0)9@6Jo z@7`<>xE|ZIaU0+Tv)l4u$gNjiD`|l?SY#;Ebbd&?)mrBC|5XtCRiDVPkb(V4L6Ben z{{8!P>eSBX^dZc?T92B@M@IG~rdIt}L)a%bR5{-Jr}2#r-dM&C}U$X0TUgqGK8D{LZVsUt&eZ!YWQ*Om-{A!|iW39uZ+jU*=2XF50R1^FZ zA6|(zhns!#F0NY<`EFuRu#yqOgU1XS(}x#l{dlNLJshY8+IrY@o)}fHDev4hW>cMP zJOZj;2TirKd1&`jt+(PyvsydTmqfP-9ME^}v@RnSw>m8{XvNumrzTCEp6J)gZ(^L< zIDFpluA#wW=Z8$_FnwzG;O=V1iHC>y2lfv=JN|I5&Rw=7PPiaxLgcvK;|I)}dU1TO zas6jsG<#wcSx9^TnM&JB2TGv*&D-8Q9%HAz=~UJWXz?t?m)kGTOp385c;atBr>_0o zSMB+1&s``tRtYw^S)e z*1V0w%inFY@HfvMvyKk>G)!V@XM)m1AvQzgyZM(K<-}AkJgZfSF7cjMVA5JLc zA(Y`eWnaCP{=9L$H7#;V$2RP#a^{IwD?N)=%SoJUM6=df!&yd+zaiVt4t{1ipPrb3 zHpf`@u#xDlc{+FUX7Sg3bRX5F&*(l~mE!+?#`GE3$4@{i`o zAH7Lck2|rb?jAK)T|aj0iC!n}oTUD(o<4E@iSZLD6dB~XQ}^e+@jje!H%hW2+qP3} zq?2mrZS%^0X*`UoO#yGV>r^lHf4uE^dhIv{qp6Ew7;4+rYq^i@Vp42h5n=FG}?3G@^IsKAk$XZ|o}Ua&BaI{x-hz*tOB1hV<>%kH1|g z0K384k2h``F9zMZsB1*mUR^tPi|7$NY++Q$gmrPwtMT-x^m&d;Qn2Db?6qTU+h#p zCvs@GKP%f#UAxTZz0j{X_iSC$xwGDn+rv9}$Ju@kqhQCN;u$CQ@kjm8t7gP;%^#~% zu;)%SQto91&y!E?`KXh<14cWuh`E7qDi$|$>|um$y?GvKcj4K(X{@|<@1`%?X`{OC zkBW|ty7=OiD=%IhHF@&X!AqA7X6y;}3~Z9!MIs`3JG@ys^Dip&8s->Y<UqP(k_V zevU7Vt8s1qTXf!k5*^=Qo)o1A!7b6AKbR*g%qN2tzf+3Xq=~4LU7We%+_sxeU z_s}MHPIhT@XSUhoZu6xvJoyqF*`KtaJ7>1oEU_C;hJ2w#+LIPKN{2pu`*^kL&ZEOo zBl%*46EPZhoP%wCTpSfWbE>-6=7`N10+27tjoaNd85cR`H|cg96}}MrW4=gJzBEPW z{U_12CR3WDq%deZEwS26v3b&1)~ch=?P{q^D{lJ2$8JtF=1F6Dk!QO_j{n@a7CQcO z;~X6==Tc$fbRiXjHxMI-xCf?jXs7M}cv?=%m_OPlDPHXGwrk^b^y-LL(}{6oNFPo? zclZ06W<09%lsCKe>ea1l@7}tQpZlG3de5GxTWtQE9FKhli4;EIroCkH(5CrdVWQ%j zg8z9`sxTnHM&*78^Z$2T>`(ol#x?My4&jt6%sDBlQ}S?VByEB`U*~g8G!MOP$2IC)3F^Gn5N;xTy zrv{C&5C>KB#wmoieUyAN;?@Z(J4PIl*2aG2A#ZhDf2K9xK0LLEo$(9X9&#e^*9vS1 z8;v+aPkg|}dEN1K$Aj7)YO;47?H`}Jbi6&b0~mC{QOl)k37#Ogf~ zD3!8F^<^CC>v;_Obvi<(EAhOo#>r_!LZdL;HA$sjY6eZ3dq z4cy1#&zci(`-M|Oj>uAdx6ISQlEB#x8+AucaXl^xx{K#0(($uOl4Q&S#6Lh?uqp`KrxJoBv*{@&=;kKz61E zlJ5g0!u9iooX$HNl_pX~sD-kC{21(pA@5zg9kNF4ld-PdDw&~H$$ZsXw&|JZbG@u2 zzvs(qo(HhcCH$F$Z>uZiOw(WDO%L>Y$n&+E?>6sG!G!aC&Xxt`a58Q=*s^GXvg*@P z4z>(VCPL!4ALo{ZEsrMLvan?q?YZ2|cboU8V8X~x%EB!NTNcf@)BPaVzrF( zsWUCQX2~}C#T(UfNl}~S8hJ`G)Nt7$_4I*zu)Bx$Hc|~$C31_EW!s!yg`6YwEw9M{ z!!O+FtE8LhLmeI~quluD=WIE+?TS9h_HX)4>YFV~`Zw#RrGN9{nU2kGTOVwjX-6Mo z^U0>|JM8J(^cRxo)(>kJK|C+ghugYi?_REtd9J5#o2EZ)4DZ_8FxDMAH*I^-)27q5 zAzSuMeTJ>4<`Pep*?}8=+%&VachhLw;aKvcIgifc={9|k@#QyGPV;#5bA++jeAgH~ub@^9^Y;FL8aoEH(bf7z(qbjd=%ilIK0QFX$s9bWhUt zD(!R(=8qC;=6LE<5%D+mCF%?EE|6!O&(Oat;o7F%wjbNC&~MrC!R=r22v7gdNl|0< zaK^fgGS*zgwH@m!WTI{-6V)ry)^tXOA8nkt+_EEXE5~gwEq$KnGUIs4%}D&(@7)of`KRg4|QjWG7zArDD}%a5Kzm?>(lM7Z4VImG_K1_-rrlY8^5`CIYh6mwtvOrt&dxntNTj9UYxm$CaT-ZI8K#dzr@opdF2 zeWOe;eK~7qCiyT=qKqGPc%< z!Es#*Nzn8k{s&oXe`wREr|~VK!+fB>gxNY{%g1et^!@z4&xiUDdf0K`UHY1>d|4_d zK6mRdb*-sP#+qAYwdqRzj$vFGDj{YiW%xL9R#IoXdEPYhx&MIs+j*9~#Pf=|4ITP% ze;WPnznCY{PTe-s+<%UxpL#)tQ{SyQ$jO&zrxiMUjxIB(L(j_$(@RFQ#M?X;t&(u_ zq4=31^801#@(k+JVB&5k6G#K&2X*j$38FrD(S`@BAE+0TJg=Mio;urx+@milS~VBoCgW&vyII-;d`l{kok`+O~BwbJ=fb*G*yw_&?7~9sH&E_&Y)toJDZ2Pr+joTJaHR*F~yQf|ccH5`zb7=RJkB_>N{-gtQ z(@*Fh>>Snh6JwbxGoLV1AP%Al|EiahH@o}D_c`M5d64^BTT!JT-s++xzDB`=88H=Mqiry}8~0 zmD$pNy7ip7e3MC8@oL)%&igsRCZ}s&hFnz>k+8<+mWBsR| zH?2_=<2ijulz!2Z=f*=`ns0!yjj=G+UAHu^nF^`Hc5cz1<3;THC4k?BX`G8KWz8_l zor874u9ms8T}Rou6m7-TrOZ=Fn|Vohrp`VK&!awTSij9n_y?o-*DN4TTYI{x{ac z*32t~ur=7pN+c>s>kMT*9iS@Q{?V@I z8f9?oiCgyvc)m~xJolc1434_cXS-|kvF?1!U8{R;cf%wRj=7o{f*wC0%g)cP^Hgz~ zN|kF~$2|EuX{9D|0@6g<+UfWQ(q5qq>wk+TO~x~UZw)`7jkHFtH!_3ClStB8roo!b zn@76sq%HZzm;#0B7W~{Ii_|TiPxcH67o_gd@zZJloF4XEn{5>RGjc45UWxedF($ z({i8XIn4OkI5+)==WBcN4Zq#^78Iew9Oh1QSceT$dps|Thx}En(R#Q{<9etb;rYU) zFQ?QpuaggDyL`y$1|RaPQML}+dA2p}-c$hfe=qsVT+!AI=1*<)W6W3V{63oecpZ~x zkm^bLwhx%Qba30@B6n}b`kf^Ht~^_JX?yOqogdh>o82d(UAb$P{ajDrd)lTx-!*Mp zr5!ZS-PPx;d2aIjn{}x_{i=^TOx$BwZ}g`vK1Mw$;#3gBe%5T}bQRp+K|N%y#`^Pg z9ZQ<3SUcuO7z|?#>Sq$swSxqbmeDpnZkX?xn|{w+ai5H*KN*jHf%;3n##qdn_Z8Cj z5NUXlzUyD~`HxeUZ;%ARYH2$kkSQ{2z;TyEyd zWVe5@{TTa(W+P=c)$zqTHRbzw?C0TU8TDi(dEbrrhPW)m z97dm>L^<0vvhC+jHGi8UHI9XLFOGb7_l^I|Y#tMTGkcLI{TR1eW437?v+aJfyXV`y zH`hGo{=sB@H`Vh>^O(%oo=2a3Ny`}DJg+##WGuDo(PO8W4{+ZN`>f`5X|vff$7r5w zoNE5X*xoY7Xzp{G&EL&6c!$!xZ!w=~+4K6VeY$4*-}dR6_v;2zukF4Rd*Af6jdPem zn6oIC{!RDI=5PCX?9aP<m;5KVZO87JodSI<-{Z?d_U8Y6Ifd=WdCXc>)?g#ag1=WW7VHv{E6>0;>p9@ zi+K}fHsPY!)A&1XkOf9VYp~;^-QW8g40`c?&P?+-=99#8H|8G9H1;x}xh$IVvpJud z)7+wavrf%%{aqR2W;U>IYI#kD{29d4;qP!d%LJ#JEOc@)Z)q~GbIlRh{l|1WNrYP~ zGo4z@ZwXh4S%LYmjIcDP`%lg0I@n~|bkI-V$XKw?H3!RG?D4#MivD&S=s=lIrC%SV zzry@nMzOy+m~gc&^W~G~e8miDy6%lR%^QfKwf0E6_MtAl!u)HX=Ux2ubJyIIk^05+tDVbXmq2`H z+vjn*nECAko*HvHbM@JbV|GsYk=UbW+o_NEw!!wp6PTZe^Lj)K-#k7@ImI!)EyunS zbGGJwjP(=YLQOKy1P{}f9cDcInX$vp?d;zB0@eeIy5LBiq@#y9IKU!yL7d zN$ADTC3+^SEYC|kml#aH(T_DazqBIkS9o3ma`D7#A`*xBE~b4Z`?r1NNp5vn z@mJ=l;s4<8S@Ob;bB{9z$&t%W27TXyH{b=xgFC^dqZmjF^M7GaTRfGdg)IiT;bf3? z!^xOU?4`A0PIR1dXz+YxhIrmHr|}GC6nVv7sNuXQ(~-TlpV(^~Lf+f*nud8j`dM?b zJV*Jr(a+;cXA`@=jZVqc|BU*M^y4V{gBBzBKlNgXbg%^~0>I_|{wxVP1R0^QIc^IZvO%Gog6TPj=tBWnaMV z$ub{s(?GeZeXLj9=NYVzcowCTsL%Gf%GKy#_htXpG(XjzSC|uBrs|j{^Q_Jt#~IU@ zLtdb+_f)%XtdqI_+FoOxuyUD;p24*ZdkHkCfvk16$w>Oi0KRPsQHPix*D!zU%6cUa zeeU4>Ky)x?!nxG_@8M@Sie9$;+BtVKOL!i_vsC8hN9-DragF&k=eFO*v%^00S9b1s zAK~ucnRjRA=5KM1B6AUUPCwQ2Fneb;^aDk#8E$p=>>tMOW$aVf{r9I>d;Em?Hr$4u zZ?Ug1o^{N1^trux*Fy6xUXnZC|C%+|aJiN}dY(6-V*~PkX20MV<_Y}&_g`@GLS zJK*`$>6GC}blK(3hwNHFQ+_tA=J~(fwJ?B|eCGlIX z|1-$@XQ=zJf5Cio`FjF!w+z*_mkq>BjD9b|D@1x!+i>dwNRsbmA)WJgV>;Mz9Z4kb3JATf!ds``K$=!Ml53)8C&GH@E46Trq3}$Sc_e&-U0)ajoxqDZb$f_Zt32OfI+&g&yH1$Bo9

U_5I^dp};LaleqZx6o;4!;y|v z(igJ~K7^0pbNCsKLk)cGtY9902|8cGdj$5~Ki>Oe{!qYkkGps-wUB3|W$ZC-fCR&D zcT*o@c;+^mHZod&E19ebJL<=nQ;p`mfeF|}NKgAN0_&d9JP!|JULB=+F+PuG@A6^R zV!asW>)EfY=NWG!8Lq5>au4NmjO%RbrX`Vkr#ITw&E`o8^Ccg)r{=#=! zzc8==P&%+5+krj%A9(kpxBSYv#v7=68z|GSsPj|gkV>Uo;{OMXqdkAYI&Qk>Ti$mK z&^I8fmNs)IZD2O*wMzQVRqS8w;n{3w>RKx8^A*yufWCEsyMOG>^%1UT+IKF!9wZ+Y zut&Ur_g6lqk4&adx`#T#99tb{Tqs}+d7Sa^PWD3_nQ!i7?lnOIz0Ts<=V01}ofkjM z_}G`W*okvSucuwIM@K(-mTTJglm<(<)0(gUf@y=neBl_yS5QCj+ey7-?#ZZ2m}m5| z^^vyRi+4_`qZaJ@Vh#Xn1~**~z`irr7VF=dU95?HH%uG0Uz2I?2R6yKer+7qy*Z9k z_4tEK{93s-{$^dQKCUiJ@tqu(m2KneZ%r5m_Syw|H(lJHYM!b`Z~Rz&tr^f{_C85& zi%i`5VsCY`dNk|ln%G+%nssRjgT0kG2~IZc^{IN;Ff&et@T0J6k=J~U{pno)0qjrq zYjtz)u^-Cy;8PgF{qOW(FKMq1WsG88Lpx`mK>D)>a52{$`^7K7i)SC$wYJ?usK;$J zCMOhm?#J&?%y+QkS1>*NsEcdQvo-sCjef*6J+m;k0Pm`MqTy*^U2em9IF*aFn|Pv{ zu6b+5vj=&cZ)eY|^Grp~Y|N!_4(!0~TTHqj&oJBlOYYc^IC+z8tg3 z?}?|l{ug#DF>QHF0~^0BFPrx^jvV-oFRMPr-;0=!aBuT50J_5B~bHjhoin}BRL z{MVQ^?`=HyLp2=XzBMLk_B;w!hK-jv#T&B<)5;(vAK-X z2Rv^u=J5_FYp-G%qeie_v6Q*M-@!BgKlwM$wX=U4|BJ&o8S7K*-}0Lj3!1?X#YujpV0He1SIAfqK?4 z7&2fVUq&V|^(`a2@zWVuU69p*E}HJZpiOO8FfRS?x|44d2#6BUn$Zf zQ{*%Ua6erj4bUkd17qUEZt57T{z|d z><46v42*_@BEkN`G!OEbzJ*e}K7j5aIdtQM8QQ?92L&R-y#YVNxeq0LDEf}r1^6AA z$J8{W3E~V(q*~#BbS2Ay^?={8^&;WuJdUvAku$zp6iawa9dD2mxeIPlZB}8JR%5Gtg~Dok+AFgu_0OnZ!A>USt+>W)bhK2#AAZ zhN`1%ofCIVFhm0J$D}|OphL_7C=;23>^a0grvi{a*Bb&M9Lh!J;ddT>=izr=jqthz z`145t{^sLveiS4?2JD1FD1};)1r7v5BqTvPe8_QOFy{*nffrGXF* zv5*ScKpabpM3&*UEDDHw8F4S$3D&+Gj*27%0QL#>BFp_D6rv#!GGP}G-->vau91)g z>5vBppb`kXDp+K7I3z$Spu=i(SX~NLB4;@e0LVBCS!bm{7Ld-fN&q=$At%Wn&>?B3 z$QopkW->C9k+~@j2*25Z6ySPG6cmZ1ASb0nt4t46T1LiQVDZzC`3Xm;Kpz$=dFh{Xb?Hy2Ld=I z1i5McPzLoP+qvFO*zM7f2>9QgBXR+;ROCTn?Fpy8^c>u)h-bE79?)B#|uQ&ngtzkpfwe2ZY;E1~npAqvJIW1Vbg% zLW4+lARy=39FgmSfN1az$=OpWDMk?hq&z*@gW- zQXoTQH{o{^W;gP8*Nf!Ei0ttNWbQ%c9(1+Sp1s)Z4TMmL1oXJe2Z}`Uqebox7rBRc z?kVSE3+xIKMD8PP_xp=Hfc*mzBKxu-U*y49NQ4w1o(D`E-%u2$6px`=99a0`4!y0AXJ&f_jk> zYgFHS$LC60MuO*7Sj_lW?09l7pM9R?RjR1&*c);$BERi>T zppLJA(W9LB-VO(Jc)JYH@g3aX34((n@8bUMQIQJ#zwa;d0e(L?AW|6$`2R3o&>=)61&R?X79Ok;lU*t=|e;o)>fSj*u znO3I*dVEX#b&(?9leQnY{xL(Op0Gy<_fr6H|5KI7&m|&93G)l_{F)2<0J~%8a4b)x zAqeuB8wWxu^X(AGVUFrR3RDP3s)sWHC8~8I zlykmR6dVxM27hh+p-5Ca+6;Yi+Arg`xT~ue{>0B?0`JnP6 zY**~M5l^>FI4Y`pwkZD)C=t~o9?+==c0KX|**&VDPSk10J}n4{_p}C4r|*Xfz%9TJ zkP$#!0fg`A0O5NQz9->(ao;Nd(5n}5^ukZCG}r~m>4ltL$mxxo-pJ{VoZk59jUK&` z(;GRxkrNmW*aa2>c7fRS;l6Jel#61%srr?O3L>5$uKW7}x(+1nf%T$-_lp{oB5H7y zs3GVP5(wB2%@Z|@FvH434Nu^=>w|%~M&M^;I^@DWz4io^oGM`pwctaqd%NYrf0Xv}x%Ar#KL+})AosCZmg!`cSe<{Xu{9LZDtAwX5oK! zC`3Yos2F6;$%A51%xl$L+~x^ni;7JaHQ!g%0%S5DRSV-p(H*Eb^jd_!GjU%W4Yh!* zcpp(qETnsBfv9CAq7q{H2@le@oG{BvMXe|jwUX;Z!mUE*Ra~zo&CK1@S;$E$6SXD^ z_55HhV(|M8SH<0OV{Y-py5TRMeIL2m|8Tk^<6D@Sk!~TfQ*OoL_M4g>7oj; zE5!dJ_Q@r1#zNYii85d?PUk>SBjrk zi0fdesMm2nlqKp7e^GB1iz?qG>g|J~-XZKe4Wiy9u8Is%@0G(*QSS!;Vct&y!o9x_ z%Aj7<2gvv!7>NIaXo!a*QI*(N=8F0d^F!R2*QgKk0bQ8isE_<02%;bfG9e$3`Eei+ z-^YoN0eMgi=vIZSsszBk3jbB5P%G*a>_5qdDp8+?K|0{}nLpt6Sv;(VLO3d_ItXGR z1#$rU>M}sq=lK1+L=^KX^~EkxhkXHghby2)RE;+z19mmUSwq;Ga;O&dCHj9A0Fe+Y z>g$7|YI8(=69V}8HdEAh5s)XUE=kn)*neLs>IeM(n8gRx*wv>3{_1lfANE5D5Wc=z z)DissM7WnVf^yTc2CP%D~2LaQRtIv5H>n@~uDI?;|lM8bL~5$%-#g`!(U z0q(8w-?~P$H|OJc^CJ%4xuV-3w@sDkwhjbCjOcbj5Dm$Y1N#9z+BJw~Ur_rXkG(6iawP;^&2!U8gg`H3Y zRiZmN5DYPp0=aMiDusuV5CqYX3^`B;Wl$%&i!X#iETljV6pHpE&Ti=7?MGp#v2GK*16M~#! zgdJYMd4RZ$2ogP#aHAkBTl8pjAA|g{T!)81ndoteqQ@5kG9!>b0i7mdP6`$siLA+7 zPe$&PD$!GkC#oEdik=n#k&pygun&+otzPtWe~5rYK#%DKa1f9)1Nk$MJ0k%yARkJh zR&=y4gh4!{Lmresjp&&^5DIaS2D_jbs)ZvlAq1iTxwBGXClmo;XX8E__u075#(g&K zvvHq|dkoieszlFqAQ)mG39^9r=N^Dc(afXtyda2$6vzeK=2^FT(Xsvz0dbHFIj|oN zLbd4m=rA80=10ML$cB6P0X2fgp&6L`VbVEJx1rVyJ*R(JPR(A_S1L0y!&?!yH(zs1kl#3W6aL;vogHAP)`z zaU}}iCz0@pX@FZIZi!`3BfRVY{tyb$kO-Mj0QgyTRP^dVz-@I3?1Vxng(|@PEMEwQ zNQj4Y$OHVGRVO;hA8<=r4>?c(#ZUpbuknUJhywhq!OxllP$haTZfkK{8xFC6``QfH z358G!wW8PgK?EcL@vqB;eNY1A!#eVOJ$b%92%;ew@Uy-Mszq_wqZXU zgj&&OI}i+!kO*mj4rimo*=0~8dZRbsXJZuLXCrbp;%6grHsWVvgXm=3ljDFiB=3U? z(VLLHDHLKM1#+Me%Aij4W?u*eblaQ-2cSmu7JrC_G$?>lz&-_eDS;3H@sJ8RPz1Q2 zL%4IIfpF&(K!s@bhxJy%Yz+r=--;W1#Cj__ZY>4!U>kaD3xjw_fikFudeN!GnOY8{ z@mxQ^-?=f648(ozejpy!ADZ=tKHmrGM5ke&b`X%W9UZo#!*+Do9t)|k6VQ8mmFNq0 ziM|k->G(^>UpoHM;~@pIAP){e8Ptou$R8qMJ!Ff%I7##+_`ReAYDH(Da|UuUkduL& zjC4Ro2JRU(qAwMQg94}$eOUzHemUVTj|9S8j^9jgK(EYP(O39^H7)x9-LE_-`ls~4R^oHqoCzA;VoP1&MzePO5Y z0UK0`zLmIk;`g=;(YL3IzGJ=UT@D0_{zri5JJDk|;ddtha(Cy8&I<6UW`?bx%BypZAh)_m*>DQ<&)caKEog^!=rxAJ{K?Uj`8FK|kRB!5YyI#lZp5 z4-?yP|Tn`m+RP-aoq8|-`7)XP1(T{lp^7kWae~swJ!vOh@SBriEe@`R< zX?r3U_CX0$LcQoGeSr9$jDR>uhD_K6`{5u|Ljym<;RhiQ1qqM}*^m!KP!6@CpK>4o z!XQTU)1iQV&k(MNv^<+5n)Q=@J{Gut9vQ{B{nHmn{|kh9p<48dWui;6M8D(@>44qK zKB7yJ`|nWEuLO&JHAwV9++T|q{d$h*Lr}I$^c%RpiQQYiqRUG}za0VlMZb&eiUXqG z!|weG(X5g52MwYt3HKp#KP(jeQ5;~h*3nhTkS+QX+&(E6{VDnSDSCXC2Ehx5)aoUi5dFqU%V{_k{T& zTJ(>hkSDsH>-rkeM@mKilq;Hbh5oq!4v0RQD*6}V{e`%G-6{H59AI}0e+}uPj|TvL zSwH9#+CD{K^&xtX%!--bud&ytr&04LiP>- z{CLMe5~Ks}-qyWAOdB6DZA$^!tPM=N8ZkcQV%mE{AY_Q?;1Brkh`TTG`<8Gh@li3I zaqEJ=E+G&B2~aJ@FBx)RKO7X(72UcLS2tw)2SJsX9$8{es~2;6G}MXd87HRKPBFbp z#RNhh;_H_PDPn>GfjIl;3NOn-nwWu+VuI5lTg)Kr2bYNnLH5uPF~g8G3>m{y0X;*@ z#f%`1Q9h6igbQ0QX0)G}F~wrW#)=8g5HpUrn6sNRlEg$*ikXlrW@3bxNe=9X8ZnV& zVkQ&+&Y-7XV=p1IdsH1%RA5?nqF<}>Dx0rYq=ZlFC1@wtegkq?Gqhgl$18z&A0a;6swImM?h*|0b=)ANH>clMb zhCoOYlYsmLeq-S|5?1Tm}D+s@WIGFR96%~Nom3|Nc zv4EV!V914ia1g4+tRl>+JTa?rTOA3BfE{x;!`#iRE`vHTXYB{Vuy1PCpab(Rv(|wE zG3&TzK4sRS(>ioo9{^zx2g#5P`C>NggL*M%1XW^)uIKsq3MGqSe`_yb|sOEp_?+j3A0a~+d{`#Gfh zoM_l3W-E4EYs73T7L!^a=G<~I=aq>`Lq?h}VW*g<{Q=p}B#S8`4bS1{dF1>vUCfIjro>ka z00mG1XjuPWZ#< zceqqc%|S6=1&R4OQcP_Y;Qmdfm~Sh@)Fp}e9)I7LiTN=ALd4XUi#bvy=I21D6!Qx@ z{F)%%Q#OaJ%7i4rP6UUG1uDEyefdGhr1W1QnPz06G zAWruHKu-50$bx-YQ-4}!-y+11fs+lfxi*t z+sIhR6=zhkIAP>#SSU~qVU$@IWj2~J8%;c8e1QBMgY2;pfG%T;p-h}`WK2SzN!(8& z-br=hMB+a(81NTK_{oHw5)Zpzzc^D<TGEc z=bRXEw(b{a8*y%{5hoQL&hr6eofig#NyC2{=7s6vq+_36EY3wakSET?=#vo*wc=cw zD$ZpG#km}vF30`yDseKAkx4#fBL511NCLuKQ7_Jw5s(SET@?tqP%TbYC=gc`_B#Rq zxjT@1HGZy6hGKE9@qt9xFHSbs+31i>JlTY~mN3^6=34w+=Ldwju0)*cvA;e6@}Nc> z#$YFB7ZB$SL9iZ>$C&Kg7y~<@LY$kBaTDQgBHT@c%MF7xK>p3XkOT+BxdlCLi3P&l zg3Mb(AQg(l*~#@z!r$f(*-$Rd?ZkO|3Y3U*2ljU)!+vpgIe?s9_}^74&Oag{6UxN7 zlQ4It0oS{OfiSxZp+TIyC_r`|viAf4>DhBYoV}qy_`Mb4+(nqX2!Gc>aqfqpE!>rhk1(gIPpAQF3uA%P%O@q$bB*e zs>ETA;v6W1GN=>hsVIm8;(aO;8pL_J6l%nI#v73NOf;;AEI0_rDG~?-!ZEjTib|kb zoM(~$Eb%`_e9xiN^C5s9&nE(Q&({EP6bAtE{uwIH3x0szi}8>rPAPIrbD>b2e@6mo z`gg53uOQ>qQXrmJ>%}=3E6!^{kT1^b-VhAQkS)$3A0WO%#ZV_sSr`mSwXn>){FCg6cmc{0scQI5vQ_9oDcnh zFdtTm^HB(3_c4AxE`xe;s&KE00K!ydLIG5X^N9n&fc+=fe}erd2cb@!PkkW>NY`gc zkOlhynV%uE8kyC|tR`$VVXFySP1tI}eolHm4~GQEfP5&0T5-NW?iXPY58NN-{xJ84 zxj&o^c~Am1;?(#+D8x09ikS=hp&aVP`O*)Yb(V0CIO1Y z`8Ei!`_3Ei_uT;~g=#n|PMtr506Kgh4;AA4fDS)o0y2NhhC(O@{MQFSBqRZH>-Pci z93dS)A^#`LpZy?1oTEXoUz}e9Aqv(5@_t3$ugLoqdB>1l)`Qp{i2k_UfTD*Ml=d)kD z+6O=u)QeY#1VCnoD)H(l5Ch4uPn3wyBT~Uv>_hq5M$1l4*t|wW?P$MAy!j{6k#A&; zD5nkAE1>aONoVdFueJC{O4GHGFiCH^b|g>=o36biM_5U?a$0d_Sz^<5Yra#yxar!P zZ@yn>x^BbwIkipKZTZgCuj!g|>GhdS*FJm`%L>Pp*s$D@PLgE}cQa+PoF&7!nv6{{=7N?`+uXY~vJIbGd5wIH zB;j)|p;IAH7Ro9@Z$m~ZSILAsi+F|+ll^xFG0o%O46d5PG=~`ayD-hJze%)u{U-BF zH~wwN-$HygEq@lr<|e&{qR|#P7md#%-1w%Hk42+#ZVrsc48>(F<~Y8!93|m07Qa^G zKw?eBg>U0Aw~%)mh|%U>FzFvbyqx0mU#DgRngk-tO5KW?M68=IxAN6mAkx?J_s>gX z4z7*q*-l9|mXi&+&edlf;hM|sk3OUDZ}V!Mn^$q%ts>TwZEZOQ%4%vzAm6#48k5!X zxA3V=;fJ~PC>3ou1BZX-yZ?p}%Wt)57`eEGxQ1i9hBOavO2ZbeY%Tm_=uM}@{+ont z_~zsfx6I?*aNm+TNU}AjQ9=lAr`A7P4=jQ7Xtsv@cbeI>*i_k8x!SE?jruj#Y1@(l zr7x{;A?8f}_Qjmy-rGB?$71Z`BpR(3VcPr2wAKauoy+}9ndXL_hfN@Tfc;i4$_+8o zy|!}akxtvv=W=#vAm8)Obp2S_YtX7OcWgebVc$LlO>IrEsoUb_-UiAx#ck(}J%P=~ zHT?VE4g|m>FOxbRxE}e^MYn0W~wuY_fVe_)NN3a@NpSHAZ z`CC`pd!%C8+_mnup4uL4EBCfd+cvZvJKIm#I$~=}bDOjp{ijkrwLNS@V_UK*#JY{T zVRcK!v}s&N{kJ(E!zd%3de}v;^e6u@9tl4|W`FP1VZ3Z5tr>^fNLy*gIMbf97CMTr zbYlGJB7Tfn-K4wtOAk4XPZk5Dr|=~`Y3M`E=_f&aJwAX_b%W`z2J;1Jhzv!W;q-DN zhzshG zR^DPveM255Z|md+c}L3SZA$HH`9^lg+2s93YC$sPoJR?qL#b?~L~Nga9zDia)b0x? z{R`zHM(hK!M=s%mrVP1MYK8MNR6FIP+N%z#qw^O;*bq86xyDo!m@XR5_2UM*2e)iOCI4N@(is|2-NtxzjfqFSX^tFu&+TBFvg zb!xrZpw3nsRkGToHmfZvMV%whsI6+7N>%5|7jjsgC-IV$bafGPlnVJo z-j@%gk~2T5_(J<*b&1MQm#WLuL!(| zZdSLbTh&f=o4Q@yp?0Z%s5{kem8bToz3MKNukKd&@O!HT>OOV9dO+<{52}aM!>Uj{ zq8?R`sr_T|aQ|f8;j4D#ks^`@6s#yI~y`WxHCF&*hFZHr2NWMcI;6_f8|qEMQlNs#V{pZ`F6IPJOR_P(P}AbwvH7epW}-FX~ryOf{(E>V)EO0DdcwUnJFz_R_6% zYwfMu=(f6@_R;Ni2i;No>Q1_|?xOv4SKUo_*Z#VPK24vlIf;s2x$dn4bsybV_tQbT zzaF3m>R@?P9@B&P&ix5_QcCm?*)NaFOLCdKAXmu)d<7q(hjN1Z^Lm&R>EZIT4%PfJ zfF7m8^k~V}V>D;^>v4L#=FBBMK~L0^bfliFr|79VN>9_%^$Z=YXX;scwvN$r^jtkp z$LjfdfnKQN^dfzxUaaHw61`L}(+PUHUZGd&M7>I{)@SJ?y+*Iq>-2iPL7%NR>SVo1 zZ`NCMiatkg)!THcK3AWo&(~>syS_kQsMGaD`eJ>F&d`_Y%k&y+dEE zuhH51T78|qUgzi=^o{x^ovUwV7JIASsc+M_>pS!={SSSo-mUZW9=%uJrStXO`W}6+ zF3|Vs`}G5QpMFq3q#xFW`Vsx8eoXJzkLxG&ll+SBQ~GKBj4sm8>gV+Hx>)~Hzo1{# zCHf`(Fa5GE)&JJ7=vVbY{hEGVAJS#|4gIEmi(hnqTfd{<)fM_Z{l5M{SLzS-NBU!3 zr9aW1>d$nw{#<{d59=EJrT$8Pt!wo+`dj^-uG8P^AM}s9ULVmv>7Vsc{fqupAJYx` zxIUphbmWR3vNXmqUZ$04ZM;nz)7G>zKBm3tU^*IK)5&x;U5uaUYPy;3#^3ZXr2GYNtTAiNIMCfRHW&UCAG`mfn*<<#ayG*{h+uURBH3jBAbH91O>@yFVhs?vK z&^%%uHIJG7=5h0cdD0v(PnoC9Gp5KqYo0UDn_}}%^MZNNl$e*yzs$>~)co7LVqP@| z&1>d$bI6pLH_V&nEmLmZHt(2sO@(>Syl*}*mF7e9k@?tEnNQ58<}*`mJ~v;O!=}c3 zX}&UFn_BaY`PO`A>dg1%2lJz;H%H7*=4W%%{9=AJ$4rAcZcZ2vi(=(y$2bnZW8TVX z?RYzFoVHFo$H!^!bohUzy$5_8#T7p;$vZQ$0BNL?-g_gRB%~*VRR7=1`|epj@c;Mu|Nijt zy?JkE=e?Po`Mx)0S5C7t)0ySWcIG&9oq5hd&cV(hwBd4?Gv7JfIl?*8Im&5q7C1*c zt@0OoaZYtk zbCx+hPQfWUB`0J~D_JFg+8FVgnwmL)3HfPuwake{`Iit>)Gww_{mpeP0 zoz5<2w{wMarE`^YwR4Sgt#h4oy>o+eqjQsUvvZ4ct8<&P$GP3P!@1MB%emWmh4V`1 zRnDuO*Esh$uXXNq?sHz}-0!@emQ>&9Jm5U&JmkE|dDwZg^A=k0e4F!j=N-;Fop(9! zcHZN>*Lk1wey8e8I*&MyI*&PzJ0Ea9=se*(={)6p$oa7I5$B`M$DF5~k2{}mKIweQ z`Ly#H=d;c;&gY!ZJ6~|V=zPifvhx+^tIpS)uRGsxzUh3+`L^>N=ey4LobNk7aDM3g z$oaAJ6X#jyr_RrupF6*Be(C(m`L*+$^Bd>4&hMP(o!>iuaQ^80$@#PM7w50e-<-cY zFF5~jUUdHH{LA^b^B?EGv?Q{Z{)}v+XcE)t@0$xDg%YmtgciOC#B?!3G>e&HmY6N( zh`C~(I7l2U4iSfn!^C`XxHv)_DUK2?Vu3hXw2EWILUF7(P8=^z5R1fO(I(o(5^Yobz;3ZSDYs{h+c8NxIkPeE)o}uOT_>v0YpyM#Y#I7Zc)gu|w<>yTopBg}72&C9W3Nh-<}l;(Bp|xKZ3BZWgzQTg7c+ zkGNgjA?_4+iMz!s#4E+C#H+M?JH@-iyTyCNd&T?2`$bhuibuqw;xX~K_<;DJctSiWo)RAt9~K`G9~B=H zPm7O>Pl!*7Pl->9&xp^8XT;~k=fxMq7sZ#vm&I4aSH;)F*Tpx)H^sNax5anFcg6R_ z_r(vy55%T{@eTquu~$I0X6338EKEZbzeTp~}D z85zn*W@S!x$h_>7U9wxABu|!0|k;~=ja)mrYu9U0fner@o zwp=aGk!$2yxlXQ^=gRZs2H7jmmlwzjkT=SkBn`5F0H`HcLW{Ji{v{G$Al{IdLt{Hpw#{JQ*x{HFYt{I>j#{I2|- z{J#8w{Gt4j{IUFrd{+Kc{!IQ{{zCpz{!0E@J|}-8e=C0{pO?Rve~^Eaf0BQef02Kc zf0KWgFUWt$7v(?YzvRE=f8>AV|Kwi!Yt@aaNljCZ5=ttiT;(aPd=;qaYKCf7Gu13L zTg_2()jV~OI#?Z|4poP#`RZ_WggR0krCQVib+l?#$EbzsSaqB_UY(#8sl}>IwW}rS zM3qsYid0tRRENr|PSvHl)k*4PwN#y=PF1I=WvWLNR8f^wtP-_covv1>Gt^48N}Z|B zQfI5x>KwI3tySyPdUdWkPi;`W>U?#9x=>xDE>@SQjjF6RsXo=ODr&PDP+QcXx>Rjd zLu#8ERwHV=x=f9#F*U9x)a7c2+NpM_-RcT;rMgO8t*%kms_WGC>IQYAx=G!vZc(?Y z+teO)yShW&sqRvDt5>L3s#mF3tJkP|)N9qf>OS>4b-#MOdV_kSdO$s>9#U^o534t; zx2U(Ox2dT&e}^+EN7dQv^5KBPXZKB7LV zKBk^lA6K7HpH!bxpH`nypHVpoZ>evq@2Kyp z@2T&rAE+OyAE_U!pQvZmPu0)V&($x~FV(NquhnzvH|n?Qcj|fdd-VtPNA)N5XZ08L zSM@jbclCn$hk8-{Q~gW*Tm47K@}RbdPn9bB}jVa2L6Y z-8Q$~UE-eTX57$?+^m~(JKVh6>2|r@?n&;+?o#&@_f+?^hVQt`+#a{!7TuB?yNSEp zJ>6a5p5d-^SGi}pXSrv)tKDox9#W*FDeO;P$%byBD|@x)-?@yO+2d-LkvM z?Q{Fxio4kzaJRUF?xpTlcgWr54!a}ncK0%O)E#rj-3j+{cZa*v-R16fuW+w)uX3+; zuW_$+uXC?=Z*XsPZ*p&TZ*gyRZ*%v!x4U<^ce;1Ece}4}U+KQeeYN`<_a67P?!E4P z?(5w9-PgNsaNp=Y;6CU+wx83i!-*vy|e&79p`$P9f?vLG{xX-#jb${mm z-2H|7OZQjquifX|-?+bZf9F2${@(qA`$zXr?w{SixPNv3=KkG%!TpE(qWe$xU+%x% z|G58k|L5-Y=mWZ5lQ+$CJmE=Cd9LSq+Vj1@o9@lW;c%c`0Suf{x zczLhW>+-t2lf09?rQRvtsorVcGOx!gctx+|#a`kq_fGd#cxQMky;a_s-dWz+-fHh0 zZ;iLsTj#C!&h^goHh8_>`Q8QIh2BNp#oi^}Mz8E`^7_1fui|a?2D~lapm(Xa)f@7* zdBfg_x81wU8}-J#ac{!A+}q*p^mci>y(_#cy{o*dy=%N{z3aT|y&Jq6y_>w7y<5Cn zz1zG!-tFEU-ksiE-re3SyjOa!@?Pz|#=FORt#_|?pZ7ZNe(&|(8@xAq4|orH4|#9$ z9`@esy~TU0_crhC-aEW^dhhby?Y+l)ulGLh{a)3Z^d9jZ^&ayc_dei#(0jsr(tFDL zkoRHlBi={7k9kjfANM}tebW1s_i67l-eF9Wz2AAy zd%ySo;Qi72llN!uFWz6hzj=T6Uhw|mz3BbZ`zR6%o~`HTxq6;HNFS^Z(TD28^n87|K0+USOdm zeXKrCAFof)i}YgMrrY%reWK3jP)9nebGk$4b*Jvq-TEYbvRLI;N59<-VU0ncyY(ydEA^}NtMzO2J^Ho!UVWc_oxWec zUcW)VQ9qy`)DP)5>4){3^;`5?_1pB@^*i)C^}F=D^?USt_51YubyZL5NA#omG5xsy zfc~IyPVC=uhfT=}+s==+EkB^yl>F^%wLP^_TRQ^;h&) z_1E;*^*8i4^|$o5^>_4l_4oAm^$+w9^^f$A^-uJ(`ltG5`sex=`j`4w`q%n7{TuyT z{X6}<{=NQ#{-ger{R{|EvF}_xcTfqu=CD^BrIK z(pSFgd%pI4Kk%pfGyG{!xC5zra7* zZ}pGy7y8Hg$N9(mC-{r}#eSRL?l19A^fP|wM}F4N`5k`V@ASL;ZvQ0zWPhoDihrtq zn!n8N@e6*@FZr>b_{;s%{T2Qh{z`w9f2Mzyf40BcKgVC=ul3jY>-}^6^ZX5duYbOO zfq$WYk$TmUj{B8cQKjLrqFY`zJF@M~j@Gtjw z_&fbw{%-#Y|4RQV|7!ml|62b#|9bxh|3?2N|7QOd|5pDte~*8=e}{jkf0uu^{|f(= z{;T{~`>*lu@n7rT>)+?U&cEM(z5fRPjs64vgZ@MQoBW6UH~Vk#-|D~3f4lz<|DFE3 z{CE5B@!#ve&wsyP^(XyD{73!A{Kx$d_#gD2@SpUb@;~H%*#C(CQU7E9)BeZ(Pxzno zKjnYg|BU}x{~7;t{^$KK_+RwDfrK9g0;cAV100Ia9*$>=nc*fE(k6RE($IVE(taUfy z7hE6Q5ZoBt6xfklOJ;7^(dxQIe*9G?n zuMgf3yfJtncrbVx2cHQ(8$9EjwSLvArs5hk zF+7;b^khIw-pJ*Z(Xqaf(F#in5op%ZY@rCgTlH)!_l=GWt1?sBvuU()c}12jQ9UCA zBg2)gs?5|c_6?5qO>EmdRN3kG?Mpkw{*iH~kA7sSuS~_sekLU2Lk*q3&e8$$-@$5t?mxZ+`y^-tfI%ippu z?VP@;JnC$rAGuJ~Eb%RWsR-Dl^c&UHJzHL_hK@zH4~OUAYdv zE0>pN@m<5*Si|M*BV*&EBipxBrp3bp(<;LQ>TFbR1l2p6t2biF^s~223=fn?C$@%FJnGQf)6cpTSP zkeyZ(oV{((001-Vr)1V=Z>tP&rdflOeLr2o>T(K|@v>N6-nOl*pvO6%MAJx*Shs~z zj!`65mbY&&Q%B#nslVKG=0ww36HOZisS8@oO{)i|t=Tdn)(#GAD^FWjo=~u2+UhNX z(~9(O_1Iuw%1JF~MuwEv;;qlb;SQT1MNOZPF`m9vloanJAm4I>K5is-^Z_&DCTbwN$>A z>a3-@YN_rtmCw{tH68g>N0_UX7uHG%Yo&y>QleTZQLU6HEhQ7x?2BsUMK$}PTFFtZ zfnvPseN3N!0U%TXL zI&w7~xtfk#O-H_FRlasdzII2xc1ON;M`z8d&YCTqHCsAswsh7??yQyESu44-R&r;p zJm^k1$iy5!Q6|+mim!GsInM&U++m4#UJ3)=jx;5EIpZ!%to=05GhNdshWioX^=}%Qe%S=U zy$Re#$144D+u*Q?D8?#%Bg3@Tv9ph0igJ0wV-wpeqk|)(TuxVaE|l9VV}xu@JTW?A zrTH*If20(iPd?0ca3GcK>GmpP;}jE(SNc5)4J(5KTgJBp<69`8;q;ildGPWy9gLAp z!?<95SGGGhy>Hj(;Ly-upY_x;t(Wf`A=qWlIcrLK)Zn%aVxvrF&J;k@hB-t#!CR*o zWimGWB^t6kV@)RB2E9aae+KtwY|uh-l#@xz>BjvwAff9v7)#d}pQ zbfX=+p|2bIx}mQd`nsX78~VDTuN(R>m&^5_9zD?C1N}YF-vj+U(BA|7J<#6+{XNj% z1N}YF-vj+U(BA|71+-%U`Z0sd6`;QW{RQYRKz{-H3(#MH{sQzDpdW)JSAhNk^cSGN z2>nIqFQWcM=r2Nl5&DbJUxfZ5^cSJO2>nIqFQWcM=r2Nl5&BEekHMV7V9u4GzXbgy z=;uM6$(5kL1pOuGFF}6^`b*GXg8mZpm!Lm}{uuR-QU4hFW9W~eKSupy=#Qa4hW;4( zW9W~eKZgDo`eWz^sO1vqPoO`6{sj6H=uec%;S?N^z-;69{PEF5)b`6K8c5Z9-qWRKaWr1p`XVm@zBrXlX&Ro@fpTE zK8d3KJU)p>{ds&6kNWfYBp&tW@ku=D&*PJL)St&E@u)wK&oJikNfh|W|v}&Y=#ZyY9%pSD`dL8n%>VBYq!o!<3V$MsIqx{Rw~#;(Iv^& z^I#1T%!CMLLOA;n&OU^*54-F`$b?-CQel^U2*fi;g$Sgwv-f1LWw>aQY#feh8-@!s&-_`s6KeA3I$_QSj{a2~EJW z(+lD3Lpb{o&OU^*58>=XIQtOJK7_Lm;p}P3WXp}xnGKwG1m_*Wc}H;G5u7(o>2RL~ zE1KrA^Cq-{oa+(6c}H;G5uA4f=N-X$M{wQ|oOi^|I}?T6j#0?C5QU5jQ3(AZw^I~y zJ4GS4QxtMLMG@*3@wkt$%MeBUoT3QjM<_o+`4P&GP`?QEi%`D^^^2fCg8m5lBk0dU ze-`?)9Gpa1=+8ob7W%RC5M`k^3%yzB%|dS$db7~WGgewxgx(zV=AbtRy*cR3K`%BU zq8#d-L%nm*pM(A!^yi>I2mLwd&!OHq=*MI?>VSSMN<ID6!X!GP;A~NASfFd~pO{9Kjby@Wl~)aRgr+!52sH!4Z6L1Ros1 z2S@P15qxk29~{94NASTBd~O6^8^PB`@U;A&ZtjQv*$s(-DBCN?G ztjS_cBa84Ri$|2jBg*0tWzl3=G+7o+mPOc+Mbl-`bXhcA7EPB$(`C_gSu|Z1O_xQ} zWzlq5G+j1bW5AAcN62oV18%>A-9ANu*4+`c?Ld_0xFrr_h_~*BsC73)t-B*?-3?Le zZiw2UPt>|QqE;VK>+XnJcSqDZ0ixCoQS=G@9L5k2{aAx2aTr5#=;ttoc<2wIKZJg+ zFI|Uzt}pS>&tVSn(9dBGMZc&&hdIPscS{uY=P-wO)Str~;-R0z9O6-b4s(b{{W;7b z9`)xihoWuNpTivDQGX6|h=+a-bBKrjEb7nU4#`n}4tI!0{W;tr9`(lpeF+QnB@TBe z+DH94+#w$I=WvI3v_FSC#6v%aJH$gjhdabWKZiTSLqCT*#H0N=+@VLqCHd z@zBp;NIdj&07SDm=;r{4cHn5b@B@0TA)f&&v$NLqCT;#6v%aKEy*mhd#tZKZicVLqCT;G;4)^tZ|mG##zD| zXNi{`C?EPC);LR8<1Ar~vxGIy64p3NSmP{VjkAO`&Jxx*OIYJ9VU4qdHO>;&I7?XL zEMbkagf-3*);LR8<1Ar~vxGIy64p3NSmP{VjkAO`&Jxx*OX(UX@ID6K$H4m-cpn4r zW8i%ZypMtRG4MVH-p9cE71)_c8E32HwZO`xtm11Mg$t zeGI&hf%h@+J_g>$!21|@9|P}W;C&3dkAe3w@ID6K$H4m-cpn4rW8i%ZypMtRG4MVH z-p9cE7wkAd$o@I3~;$H4a(_#Ok_W8ixXe2;wkAd$o@I3~;$H4a(_#Ok_ zW8ixXe2;%yC65vk){7Ha6 z3GgQY{v^Pk1o)Exe-hwN0{lsUKMC+B0sbVwp9J`m0DltTPXhc&fIkWFCjtH>z@G&8 zlK_7b#G47?%>;Oq0FM&jQ35l zc#{Bc65ve&yh(sF3GgKWt|Y*d1bC7FPZHos0z658CkgN*0iGnllLUB@08bL&Ndi1c zfF}v?Bmtfzz>@@ck^oN<;7I~JNq{E_@FW4AB*2pdc#;5565vS!JV}5j3GgIAe3&3U zOb{O?hz}FQhY8}t1bCAGZxY~50=!9pHwo}20p29Qn*?~10B;iDO#-}0fHw*7CIQ|g zz?%eklK^iL;7tO&Nq{#=iZ_T46U2uJ;==^-VS@NDL424XK1>iFCWsFc#D@vu!vyhR zg7`2&e3&3UOb{O?hz}FQhY8}t1o2^l_%K0ym>@n(5FaLp4->?N3F5*8abbeEFhN|H zATCT07bb`c6U2oH;=%-b-wF1<6YPB_*!xbf_nlzRJHeiJf<5m9d)^85yc6tsC)o2& zu;-m%&pW}McY;0d1bf~I_Pi79c_-N8PO!(FV2?Y&9(RI0?gV?>3HG)V>}@C5+fJ~z zonUV}!QOU)z3l{h+X?oz6YOm#*xOF9x1C^bJHg&|g1zkod)o>2wiE1a(+NAXh!rNh z4n&maxe4~R6YOax*waq1r=9Tn59R0iO~UIx#Pj?H2la@z^BdZovT~w!-c2->Tm7_a zW#?H$?R<(ToMYMkr?O#Ou7t#Jj_8(D^{RdI#VgErq^x%0E z*?-W=v#nQ8XN_9D#9O^Y?Qv<*~mY z9_4ivYwv^Da~L(w9>XwWi-xl8Gem8fMD4R9nw?_~vt|o)4w_DvX2UGe%*)8aIvM4C zNj@W+nlJw{<)yOveD&p-d{fKoei{3w%F^;u+0^o;%BJQ^ZAt5E@};u+eD&qk*;k|! zYW3~6pA6;IWK+u9Pc}7QY76YM`BGVZzWVa&>?@eMb+L`L=QdR~wZ0}F-T#t&MmAM% zU3u2NR92sFN_n+>wmg^KOj|;Gcww4|(nQv`ZlOpE@WpPV<9+GU6nV+YUHj@49f5c4 z*>d7$@H=AZsru>m>`}^T%h@60b3Qv@klueN*#c*zcXDpu-p^@|lyPc$Ew!YtyuI0u zu8bL7rnX(+>2UgfJVxJs*Ub~{;%Za%C_mD)50R#eQ{>c4B)8i^9XROFfrAbmIOx#9 z2$kvJ85&WGC`2s+5w!@E>0q=VYSDtIMGK-95s6x)CTbCpDD+01&FK-YOH6x=+SBXg z3D`r&?5PQntv))h603D~@3w3sfEb=TW{*t>X!R1c`iNRRM1ipEnTZFYvM288535s&-WgAtGWxE*^sIWiy$kH9k(;$auhSjc0bUOYn~9`)jx zMo%Y47DSPcBZ!_(jvR=>E{+_Chg}@O6A%3y!4nS;$B{!%Cr1uMQ9efw#G~9oz8P;G zFBux?8#=4!+-AHET;uTqZjHx#oGCwd6>W;9Z0lP~vevFMJS>`+4Dmc-+qm zA2bYcKL?=1qy8Ll5|8?GfJr>+&w(ZlPYeSNG>OMB;6Rgj3{MU;iN|o`1q$K;1H7;h70}XRLKb|1aa7}sK#}jsvqa8S~r{RivV&WC^MmrrgPMYz- zfOI%E<1PM_pF8w2BXREV%Ltn>g3>#i@uGIhPuqOSWzGQsb z*d^mmdC6FxN3WMrNMKX;G%}@Tl=mHOCN`+Ohih}$D1cHL-$t-WeodT`u6>D{64Up+ z%}ty~@IYS?pp+<^UyT>LDd}u7KYjG$_0??Yvk?W!=z9v4etIp=*g9j%w-tz0=F191 zoqqa}mGLb^JpRH0r_4td%ynBkev#Z<;O{HgRH|i0W&awJN`8Z!a!ud&^#yxRt^W9I z0||Bd97)+`;++ALbD-9qQ$EE&7n%pAK2WNM`7ndI%Tz#_Z!Qp>G38?n=Gq|rc!Ra} z;qNV&e1rR2J~-9#K`NNv9<+C-mW#nliP!jqgH6_+pU2;MAfaP2&6x7N1|!=_Kh5T} zd(DZEaEOE+f2hHx(pGKWuRBxS%qJRb4*Lc_rJQZ%XV}R2lMOaiyMMo)Z=Ny5!X|6l zmz)v9j?`L(Khr?686*3*%E**f@kY{in7Q8<9OyQBd)$8Z!u)haYv*K%X#GJgibrqYb`9f*Cse#$o?C{r_iz+`Ic zPamj^Zlh0TY#K6!Ii`J;`7DGfk3SVbDSv!ldClYd7vM~otP{0Hq>hMJmU}vRg^eg2 z7suno!||nYd3utH<8X@qZ9GiWIwYd(X!3}s^N6SOh^O<2r}K!X^N6SOh^O<2r}K!X z^N6SOh^O<2r}K!X^N6SOh^O<2r}K!X^SpA@lSf>gM_ippT%AW;okv`qN4%Uzyqrh8 zoJYKz$BIlID=~RqvG2*l!R8T1=MhKec}1=#&nxjn*^%dQD7AwFCTcg^?nG^S6SV;Y zz1P4eGK$!7C}PKaVTQPp@V)9?Vg?{R-}nFY2th&ILgQ1>BO>Y$b+0- zwy>R*mUVeKIt-&3s|QOX6Pt!AYsW_?`grai(yAxV@oitG`V^)Yr{z39gtOCMz&vDA>XA_^Ubo~q?sz2v3*n;B*QAy8@g~0iaia6Dk1w3UEdRfM5YmsQ@r6z&RBFiUl~S0>H5VXH~%VMTVaT zZQIxZO_Vz%eKf>A-wZz=l3OQBl*e5r?RX3f8QLuAgC2&A?=T#Ndh}5bN~p~v6Qh>Whds>ovB90@y8X6?N!agum<+>%wgA_b zH!`+anF0+pB@*?xNJ_c3Oq(!;nmnddlZT5nDK6J!vISE@+mda->e=MDxoWi7gi&J> zn)R5ZXBBbEXtMc?Dw~gW*(58o8Lc*(vO4;QN48lVHeqy_1az1r>)@2pVe=UsHXrM- zN!DRAS{*iJbqr5z8?`!Y!ssvw=rBpv!6~D|<}*5MKGtEAtixusI!uZI0uLOoyV5?J z^}0m4!-d>YiRTU%az`beJ6y;EfOzX!h*}RsAERNU%&>PRK9yUIGsu5vQ;>>NbfAdO0*tMZIT?r5v`ZuMKHQQ)yK8M zDNV}Ht%6gfWS8|HWKT+wpHFMiFh%`x@eJzkG{2+|2v*AU5un4FHJ^N>enS_=woeTA zjZe@*1bx@hEFa=TD3#5=mp-$Cw&N#_la@z%%NkKP0*w7 zH-nmLT3^u&wvaRE(m3Ku26M%66qX(eN@ia~4+EOnr;(<#VbEne!WrDQX`^%db@UvL zCO56m&*3FDvHzmu0cc$IbEG5H$$lH(1)>*~c=aydnVmkd{UzNJ;YfxE=}|7mF`d#4 zjM?;Y!_8(1fW9n1j5Uxr`mzAEMTOSy?ZsxW>1BKii!uhauVR@pzz__WOSa}|@s>&N z_tU;j*_zX|K@Z|hYxH!dGhWm_1I*3v44FCs&;w@~GO6=NP@Tcqh78UyWN?NdgEI^n zoME6%L+*imwuyGu7&S6{W{J*fa0k!u5-nZl4xQmMPQ)Wv#8aaWeA)6s4u&Y&MGyjv z4>2GzJj11@%lg6;U3tbua{E;2n^|~zweAXgI(ufPII*@oJhqme+~!sD#wKW?$6&_P zAa^Fe&RNU0yk4-XUeN2VvEK{pKZh&B2WBZEhQs1*QQ~v*Xb1f5<;)5-XJ1*Iub{{~VKY+aJ0P;?|5gpaauP?Vd)mwLe zDnC&EdOOnm^>(E4dOPax&mX{!R9(EB>+Pt|*L46pQhB`{sl48fRDPiH z>+R^QYyZvz*pbTX?MUVIcBJxpJ5qUSNBW#PU`LIwv!h;~+EFi0?WmWhcGSz$`ls@g z*4vZH>&s8&_2sAX`g+#ipZ1S>eW|>@o~ism<<#4e=C8M-K402z>g`D7^>(E4dOK42 zfy%G9qrUyq{!wp7DzCRAmDk&m%Iob&<*6NM`=t76d|m&mm#22r%Tqh*<*6O@@_IY! z%T4=Hy?v?tK>6$KNb}d*k;>EOklL5hdcCQdQ*A5 z-c(+%Hwop~)Q)<2YDc|1wWD5M zZ%2K(Y5%CVFO?rCf4v=P{(3u7dA%L=_ow}%-i}mWZ$~OWP&xH>r1|UZsLz-7qk211 zdA%K}yxxvfexUNxewjY6l-BD_<@I_~dA;6LZYMFchFl2B+vwZTv_fXipkU%+r%C2? zs-3K4>C4!5-$`CCn_ti?V zOD1%eoxKvZ%Q|#I+|GuH+Nl&#yLLv@PV|V{i5^iqfg_4NGn|9X;v8%i=U}tEW2M%zIPI?VZ>kK9>@bx%$XuYs%Fz+?M^kFzfL5<+ z3^x%D(`Ip)Hk)qv@f1Ci#U^_eo5Wdcwr8hsREYx8wkxqiIXMtba008un9 zuZIzj#^rq<;$b&04AI*(c(}Z;L%dxWB8u+E3$?_fd|t{R-Y!)TMftq1Lp;jo1!G!o zLixPpK|DGSFVqr`&ckbq#6v$X_!5udz{};tqkcFYn&mY{lB4tT(oiPLOF2ZLKOZ&Q z!DGjgP5JhhPwbesqz?w#GKeO=J$uF14mD3}kXHTa+|}@0YAfNbwi4cI zE8&Z+C48~9gty#Ec+0JXx7%fv58`e8iSi;B>Os7%KT%%ULp_MMU5%(6F7&-E=;wM65B*#Z;-R1GK|J*H znIht$pSLH8=aoU!mw4z;>x;McsH>aaLN6y;{q)9?eJ(_8c|>h}h}!aq+WHW+!k^Ei~2$7|drytZ1x>#8NZu8M&`0~Ph=rF1%kZ|g^t zfuMxfV@v7h%y>zia2L4EOYxMyd(LL^Qo?7W zOL(=mgs;tI`5+V7Xge?6$DmZg*YdJ_;Eb*_h+z;@KKLx&1|S~31FyWNgS&`Lmm*#j zEn@qjn4SpdS2zg^t$v~wJc-&iBWm>$?Px~#qP-tln;#xH^kvuPmuwpxHA99r66{l> z?HgMHQQPW0ojgp5vZhWB{D@~woxJu=ylp|Ewnd2AVNKMwC{g=#h}vOI)DCN+c32a& zEkx9|3{mbyog5O<%aqX1AtCY5&mkf4&>!X-=)cB>#=UzRnoaWm$A7&Iy$vfGRy2LS zp`q!JhK9W}o%a9zpy``?UuZgd?+eUP4&))}i%o~5@}_SQ|KFwsDc^7*{lBVVRny50 z4Yrih-WLsRi0S`R8%}N9((v5g2e?d|z8~eM*N-r{8}08ton+VZQ!Fz&pPypeNew48 zv^BIfF5Y`}W4YmHdmlpixxG7xe{OH81NoZ1(D-1}vrXm3qZ;nEHa^>UAMpc?hce&L zP;F>ysWzor;u%J3^ zVegs?HY{jeFn`a6mg?%&BwE%xzopt`QeC~hEt8zP++RJOMBrMg8FM{jaz4F!Lkn56 zr`%H2t2b;UNsGCrO)76v`Hk~8_V)J9Cu^&^*jH^>v!U9s(qyJw#rZ3%N1D`;E6Y#J zZ0IvNo)8V2dVBlJz17CWy}hl~hSeJ?y}j+#X>BcWOVwFern-pY>J3%VTByp_0@a+d zZfvhQZLL(3mi|exsnBAsm^#j9o6T?4-5B>(r!87Q7mF=>TK15F$&6S?&2r9$jjQLE z*Ys{^?Oo8@QeC!o16`YM8Uy;!{ii;ma%GIV#o2!j|WJOgh zYOkuc7Gr8ab#WRtQ5G_G*~VUzWn;;j>b5eDgCnHlU>3O!6ORT~_!0pvz=q|dsR<)@&1*X%r79R2SH0mDn zTNm^$TF_pd-Zt6P6j%GprS|HKHmX2NOLcnj3{x*kv=(}+GYq$exEaK?SDUGrnYKw< zs0I4S>}qpyW6Pe6E!AdfwD#)Eww3EPOgjCg-eaoME3G@*tFzixp0i=)S{BYT@M4U?wpQ)3nO(DNm= zGZrmqrQ6ap^#*g@+(LuLTPfF(&n@^ zOg1#d>o!zpwia6AYCxUOZ>0`bXxT{WAD=zDksz{A*t2nRwp?7jZSnl0shQ?d-R3NA zug+_mY&7&BYG*?aZkwED=pk*Bj-iLPO$tK~YnzmY&TpGkh92HF=^A=O+oWgc32oF0 z-0oGmky^O5Wl6R1A~QhRtBa;wJg9c@GQPNY%Ee=A7f1Qxk!=mt>5Km_>UR{???|$% zh3aSM0;->(M^pU_ZKe7ddJNUi(1lb#Lyx8U8G0Pm&(PzkeulQSwJf#6r@f7ub@s-V zB0bxUrZ1FC#wfM)~w7y?>ChkRq>yt&lEU4&Q%RcBE)hd^|p%{HkU3RSPTxUy$SYfH=0Jyd4* z{@GfVaCudywUFj+sctk_vh17n#|3bqT6TKUvV4(8#8zz5g&J zOle%q4ydWPvA?xCtyu0Sh-)gA=TmxPFX2M{J!LYDP`@=Pcg?3!T1=>(M64Dn?`4#b zAqI#Y0uOo^f;t@ejXF!H#4_Ey&>BI%?$bq`<)pMNTF3%%EZU-VDK*o{wM$i( zK%}K5XS_0zbr;_PmX) zcv@s{d-ar5n{9nOu8k&^sIPMRh)*T_SYlc+sm>~HSUsOSY0J{yC6h}U=hC=6ZNIB) z=C9uG>azW=ruR-Qul|mnwrbbnsTtCnQiX-KYWLzj)cs5!+e0J$K(^5XT~b{_CKhd# zO*cN4TfSV?tp%>S>CUY*(w5N3;|i4sn8?SN-fw&U|F|4lD8?QL*uX=~T~ z1ylOVf?n7e699KDPFp1*u6ywU^UO@UpfHvnbJM5@)wP(r6{C zDPbk&m^?9ET4VB<#9EWbB-WWcCb7QlQ9{EWN}WroMw>dX?a@Y-ZJ-p(dQHAYlka?! z&!#Rg`E2S!lh39uGCGTt?P8{+GSE(tz9NHWbHDkZPdmm)gIum<*KLBg%M7kM#=4_ z(M`7MGU6^HUrR}oXOxpB&zQ|)E|@&ybo0rzQYS2D@3@?k=8helG}(7jIVW?GyEthw z?B=A&a0TUES}X5L%h~d-;-txbH78B>Yv|6UT;8>uG#Rerq{(nS-6n(Q~zou_blw{X&AxRsM8!)U!(+}UZn`V^D+T4akRxtY>_d0i*+c#~kT<&Du1&Iq2OvKpObxAsvuZN%okWwY;fZ*WK4%Cz-z zh121-DmPg4MC0C@s?MF{-Aj*(e%VkeH8kk)@y+zd%15>g4v)8(MKM}6ZaJkTT+*p^ zx&P9MvGI2Ls=~m~uI*dKNS>juIZ*x~lG<~8B%(#1Vcox@y^qdSkB;x!UTHTQAHxGg zD%;w34EB$2u{Ulm4-IXiW2ST`ZT1Wg)C^`P_kp3{OQJv9z z?9nFItxJ07%H|PstKLC*Ms|!@{dDYe)8NqH_%3@T)zD6x0Nb~l8q(3+%J$S2P5T=3 RNQS5=vT>qjqt?3N{{h+YHRAvP diff --git a/src/Web/Avalonia.Web.Blazor/Avalonia.Web.Blazor.csproj b/src/Web/Avalonia.Web.Blazor/Avalonia.Web.Blazor.csproj index b0101891b1..8b7babe5b1 100644 --- a/src/Web/Avalonia.Web.Blazor/Avalonia.Web.Blazor.csproj +++ b/src/Web/Avalonia.Web.Blazor/Avalonia.Web.Blazor.csproj @@ -34,7 +34,6 @@ - true build\ diff --git a/src/Web/Avalonia.Web.Blazor/BlazorSingleViewLifetime.cs b/src/Web/Avalonia.Web.Blazor/BlazorSingleViewLifetime.cs index 85ac57c746..7970f09a58 100644 --- a/src/Web/Avalonia.Web.Blazor/BlazorSingleViewLifetime.cs +++ b/src/Web/Avalonia.Web.Blazor/BlazorSingleViewLifetime.cs @@ -25,8 +25,6 @@ namespace Avalonia.Web.Blazor .UseSkia() .With(new SkiaOptions { CustomGpuFactory = () => new BlazorSkiaGpu() }); - AvaloniaLocator.CurrentMutable.Bind().ToConstant(new FontManager(new CustomFontManagerImpl())); - return builder; } } diff --git a/src/Web/Avalonia.Web.Blazor/CustomFontManagerImpl.cs b/src/Web/Avalonia.Web.Blazor/CustomFontManagerImpl.cs deleted file mode 100644 index 01837bbcd4..0000000000 --- a/src/Web/Avalonia.Web.Blazor/CustomFontManagerImpl.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System.Globalization; -using Avalonia.Media; -using Avalonia.Platform; -using Avalonia.Skia; -using SkiaSharp; - -namespace Avalonia.Web.Blazor -{ - public class CustomFontManagerImpl : IFontManagerImpl - { - private readonly Typeface[] _customTypefaces; - private readonly string _defaultFamilyName; - - private readonly Typeface _defaultTypeface = - new Typeface("avares://Avalonia.Web.Blazor/Assets#Noto Mono"); - private readonly Typeface _italicTypeface = - new Typeface("avares://Avalonia.Web.Blazor/Assets#Noto Sans"); - - public CustomFontManagerImpl() - { - _customTypefaces = new[] { _italicTypeface, _defaultTypeface }; - _defaultFamilyName = _defaultTypeface.FontFamily.FamilyNames.PrimaryFamilyName; - } - - public string GetDefaultFontFamilyName() - { - return _defaultFamilyName; - } - - public IEnumerable GetInstalledFontFamilyNames(bool checkForUpdates = false) - { - return _customTypefaces.Select(x => x.FontFamily.Name); - } - - public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, FontFamily fontFamily, - CultureInfo culture, out Typeface typeface) - { - foreach (var customTypeface in _customTypefaces) - { - if (customTypeface.GlyphTypeface.GetGlyph((uint)codepoint) == 0) - { - continue; - } - - typeface = new Typeface(customTypeface.FontFamily, fontStyle, fontWeight); - - return true; - } - - typeface = _defaultTypeface; - - return true; - } - - public IGlyphTypefaceImpl CreateGlyphTypeface(Typeface typeface) - { - SKTypeface skTypeface; - - switch (typeface.FontFamily.Name) - { - case "Noto Sans": - { - var typefaceCollection = SKTypefaceCollectionCache.GetOrAddTypefaceCollection(_italicTypeface.FontFamily); - skTypeface = typefaceCollection.Get(typeface); - break; - } - default: - { - var typefaceCollection = SKTypefaceCollectionCache.GetOrAddTypefaceCollection(_defaultTypeface.FontFamily); - skTypeface = typefaceCollection.Get(_defaultTypeface); - break; - } - } - - return new GlyphTypefaceImpl(skTypeface); - } - } -} diff --git a/tests/Avalonia.UnitTests/MockGlyphTypeface.cs b/tests/Avalonia.UnitTests/MockGlyphTypeface.cs index 9c16041205..c9c59a6e32 100644 --- a/tests/Avalonia.UnitTests/MockGlyphTypeface.cs +++ b/tests/Avalonia.UnitTests/MockGlyphTypeface.cs @@ -17,7 +17,7 @@ namespace Avalonia.UnitTests public ushort GetGlyph(uint codepoint) { - return 0; + return (ushort)codepoint; } public ushort[] GetGlyphs(ReadOnlySpan codepoints) diff --git a/tests/Avalonia.Visuals.UnitTests/Media/FontManagerTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/FontManagerTests.cs index 2b0ffa4ed6..6e5b8eb637 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/FontManagerTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Media/FontManagerTests.cs @@ -30,5 +30,35 @@ namespace Avalonia.Visuals.UnitTests.Media Assert.Throws(() => FontManager.Current); } } + + [Fact] + public void Should_Use_FontManagerOptions_DefaultFamilyName() + { + var options = new FontManagerOptions { DefaultFamilyName = "MyFont" }; + + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface + .With(fontManagerImpl: new MockFontManagerImpl()))) + { + AvaloniaLocator.CurrentMutable.Bind().ToConstant(options); + + Assert.Equal("MyFont", FontManager.Current.DefaultFontFamilyName); + } + } + + [Fact] + public void Should_Use_FontManagerOptions_FontFallback() + { + var options = new FontManagerOptions { FontFallbacks = new[] { new FontFallback { FontFamily = new FontFamily("MyFont"), UnicodeRange = UnicodeRange.Default} } }; + + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface + .With(fontManagerImpl: new MockFontManagerImpl()))) + { + AvaloniaLocator.CurrentMutable.Bind().ToConstant(options); + + FontManager.Current.TryMatchCharacter(1, FontStyle.Normal, FontWeight.Normal, FontFamily.Default, null, out var typeface); + + Assert.Equal("MyFont", typeface.FontFamily.Name); + } + } } } diff --git a/tests/Avalonia.Visuals.UnitTests/Media/UnicodeRangeSegmentTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/UnicodeRangeSegmentTests.cs new file mode 100644 index 0000000000..200358d4b9 --- /dev/null +++ b/tests/Avalonia.Visuals.UnitTests/Media/UnicodeRangeSegmentTests.cs @@ -0,0 +1,22 @@ +using Avalonia.Media; +using Xunit; + +namespace Avalonia.Visuals.UnitTests.Media +{ + public class UnicodeRangeSegmentTests + { + [InlineData("u+00-FF", 0, 255)] + [InlineData("U+00-FF", 0, 255)] + [InlineData("U+00-U+FF", 0, 255)] + [InlineData("U+AB??", 43776, 44031)] + [Theory] + public void Should_Parse(string s, int expectedStart, int expectedEnd) + { + var segment = UnicodeRangeSegment.Parse(s); + + Assert.Equal(expectedStart, segment.Start); + + Assert.Equal(expectedEnd, segment.End); + } + } +} diff --git a/tests/Avalonia.Visuals.UnitTests/Media/UnicodeRangeTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/UnicodeRangeTests.cs new file mode 100644 index 0000000000..5edbd613eb --- /dev/null +++ b/tests/Avalonia.Visuals.UnitTests/Media/UnicodeRangeTests.cs @@ -0,0 +1,17 @@ +using System.Linq; +using Avalonia.Media; +using Xunit; + +namespace Avalonia.Visuals.UnitTests.Media +{ + public class UnicodeRangeTests + { + [Fact] + public void Should_Parse_Segments() + { + var range = UnicodeRange.Parse("U+0, U+1, U+2, U+3"); + + Assert.Equal(new[] { 0, 1, 2, 3 }, range.Segments.Select(x => x.Start)); + } + } +} From 048cf388bcdb570f3fb176b5ea77e15919057948 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 13 Dec 2021 17:59:08 +0000 Subject: [PATCH 22/90] allow disabling of osx menu export. --- src/Avalonia.Native/AvaloniaNativeMenuExporter.cs | 7 +++++++ src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs b/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs index b9d8fd3711..bda1c91750 100644 --- a/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs +++ b/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs @@ -142,6 +142,13 @@ namespace Avalonia.Native private void DoLayoutReset(bool forceUpdate = false) { + var macOpts = AvaloniaLocator.Current.GetService(); + + if (macOpts != null && macOpts.DisableNativeMenus) + { + return; + } + if (_resetQueued || forceUpdate) { _resetQueued = false; diff --git a/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs b/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs index eef765e7ec..809c063b47 100644 --- a/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs +++ b/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs @@ -73,5 +73,10 @@ namespace Avalonia /// You can prevent Avalonia from adding those items to the OSX Application Menu with this property. The default value is false. /// public bool DisableDefaultApplicationMenuItems { get; set; } + + /// + /// + /// + public bool DisableNativeMenus { get; set; } } } From a96cb03ad6308f246b6c36454e1c33014e244183 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 15 Dec 2021 11:03:53 +0300 Subject: [PATCH 23/90] [X11] Dispose transparency helper --- src/Avalonia.X11/X11Window.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index b2bc9d1a35..982bca6439 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -785,6 +785,12 @@ namespace Avalonia.X11 void Cleanup() { + if (_transparencyHelper != null) + { + _transparencyHelper.Dispose(); + _transparencyHelper = null; + } + if (_imeControl != null) { _imeControl.Dispose(); From fa07c598a70f06b2fc52af4b6861def580f25820 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Wed, 15 Dec 2021 17:58:20 +0100 Subject: [PATCH 24/90] fixes: remove link to missing file --- Avalonia.sln | 1 - 1 file changed, 1 deletion(-) diff --git a/Avalonia.sln b/Avalonia.sln index a288bacbde..3c2fc7437b 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -60,7 +60,6 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{A689DEF5-D50F-4975-8B72-124C9EB54066}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig - src\Shared\SharedAssemblyInfo.cs = src\Shared\SharedAssemblyInfo.cs EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.ReactiveUI", "src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj", "{6417B24E-49C2-4985-8DB2-3AB9D898EC91}" From 50659d9c2a21aa4e79260d0e2f920f02769225cb Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 15 Dec 2021 23:12:19 +0100 Subject: [PATCH 25/90] Enable nullable reference checking via .props file. The Nullable annotations on netstandard2.0 are incomplete and incorrect in places. Ignore nullable warnings on netstandard2.0 and make them errors on later target frameworks. --- build/NullableEnable.props | 11 +++++++++++ src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj | 2 +- src/Avalonia.Input/Avalonia.Input.csproj | 2 +- src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj | 3 +-- src/Markup/Avalonia.Markup/Avalonia.Markup.csproj | 3 +-- .../Avalonia.Web.Blazor/Avalonia.Web.Blazor.csproj | 4 ++-- 6 files changed, 17 insertions(+), 8 deletions(-) create mode 100644 build/NullableEnable.props diff --git a/build/NullableEnable.props b/build/NullableEnable.props new file mode 100644 index 0000000000..cf5f9069d4 --- /dev/null +++ b/build/NullableEnable.props @@ -0,0 +1,11 @@ + + + + enable + $(WarningsAsErrors);nullable + $(NoWarn);nullable + + diff --git a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj index 7d64e3d82f..871821dd70 100644 --- a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj +++ b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj @@ -3,7 +3,6 @@ netstandard2.0;net6.0 Avalonia Avalonia.Diagnostics - enable @@ -34,4 +33,5 @@ + diff --git a/src/Avalonia.Input/Avalonia.Input.csproj b/src/Avalonia.Input/Avalonia.Input.csproj index 8b33fdcc02..a1640a62f8 100644 --- a/src/Avalonia.Input/Avalonia.Input.csproj +++ b/src/Avalonia.Input/Avalonia.Input.csproj @@ -1,7 +1,6 @@  netstandard2.0;net6.0 - Enable @@ -15,4 +14,5 @@ + diff --git a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj index a5f4b8073a..189b156569 100644 --- a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj +++ b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj @@ -3,8 +3,6 @@ netstandard2.0;net6.0 Avalonia.ReactiveUI false - enable - nullable @@ -12,4 +10,5 @@ + diff --git a/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj b/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj index e560732b41..c8f60924c6 100644 --- a/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj +++ b/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj @@ -2,8 +2,6 @@ netstandard2.0;net6.0 Avalonia - Enable - CS8600;CS8602;CS8603 @@ -19,4 +17,5 @@ + diff --git a/src/Web/Avalonia.Web.Blazor/Avalonia.Web.Blazor.csproj b/src/Web/Avalonia.Web.Blazor/Avalonia.Web.Blazor.csproj index 8b7babe5b1..94350b40cf 100644 --- a/src/Web/Avalonia.Web.Blazor/Avalonia.Web.Blazor.csproj +++ b/src/Web/Avalonia.Web.Blazor/Avalonia.Web.Blazor.csproj @@ -2,7 +2,6 @@ net6.0 - enable enable Avalonia.Web.Blazor preview @@ -32,7 +31,8 @@ - + + true From c9c9d0509815eec47b9712112c5199a92288a619 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 15 Dec 2021 23:13:47 +0100 Subject: [PATCH 26/90] Fix nullable reference errors. The projects that have nullable reference types enabled are now generating errors instead of warnings, so fix those errors. --- .../ViewModels/AvaloniaPropertyViewModel.cs | 2 +- .../ViewModels/ClrPropertyViewModel.cs | 2 +- .../ViewModels/ControlDetailsViewModel.cs | 2 +- .../ViewModels/PropertyViewModel.cs | 11 +++-- .../Diagnostics/Views/MainWindow.xaml.cs | 6 +-- src/Avalonia.Input/AccessKeyHandler.cs | 10 ++-- src/Avalonia.Input/FocusManager.cs | 5 +- .../KeyboardNavigationHandler.cs | 2 +- src/Avalonia.Input/MouseDevice.cs | 13 +++--- src/Avalonia.Input/Pointer.cs | 2 +- .../TextInput/InputMethodManager.cs | 5 +- .../TextInput/TransformTrackingHelper.cs | 6 +-- .../Avalonia.Interactivity.csproj | 3 +- src/Avalonia.Interactivity/RoutedEvent.cs | 4 +- .../Avalonia.Markup/Data/BindingBase.cs | 2 +- .../Avalonia.Markup/Data/TemplateBinding.cs | 4 +- .../Markup/Data/DelayedBinding.cs | 4 +- .../Markup/Parsers/Nodes/StringIndexerNode.cs | 2 +- src/Shared/PlatformSupport/AssetLoader.cs | 46 +++++++++---------- .../Avalonia.Web.Blazor/WindowingPlatform.cs | 9 ++-- 20 files changed, 74 insertions(+), 66 deletions(-) diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs index 04fc038e2b..c23fdc166a 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs @@ -34,7 +34,7 @@ namespace Avalonia.Diagnostics.ViewModels public override System.Type Type => _type; - public override string Value + public override string? Value { get => ConvertToString(_value); set diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs index d5748c1d8d..8cf1be154d 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs @@ -35,7 +35,7 @@ namespace Avalonia.Diagnostics.ViewModels public override System.Type Type => _type; - public override string Value + public override string? Value { get => ConvertToString(_value); set diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs index 3f9c69a2a3..16293e35d2 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs @@ -437,7 +437,7 @@ namespace Avalonia.Diagnostics.ViewModels } } - protected void NavigateToProperty(object o, string entityName) + protected void NavigateToProperty(object o, string? entityName) { var oldSelectedEntity = SelectedEntity; if (oldSelectedEntity is IAvaloniaObject ao1) diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/PropertyViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/PropertyViewModel.cs index 557ad64d86..d5909d0553 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/PropertyViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/PropertyViewModel.cs @@ -16,12 +16,12 @@ namespace Avalonia.Diagnostics.ViewModels public abstract string Group { get; } public abstract Type Type { get; } public abstract Type? DeclaringType { get; } - public abstract string Value { get; set; } + public abstract string? Value { get; set; } public abstract string Priority { get; } public abstract bool? IsAttached { get; } public abstract void Update(); - protected static string ConvertToString(object? value) + protected static string? ConvertToString(object? value) { if (value is null) { @@ -59,8 +59,13 @@ namespace Avalonia.Diagnostics.ViewModels throw new InvalidCastException("Unable to convert value."); } - protected static object? ConvertFromString(string s, Type targetType) + protected static object? ConvertFromString(string? s, Type targetType) { + if (s is null) + { + return null; + } + var converter = TypeDescriptor.GetConverter(targetType); if (converter.CanConvertFrom(typeof(string))) diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs index 73d867bf10..8d4bead3d3 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs @@ -17,7 +17,7 @@ namespace Avalonia.Diagnostics.Views { internal class MainWindow : Window, IStyleHost { - private readonly IDisposable _keySubscription; + private readonly IDisposable? _keySubscription; private readonly Dictionary _frozenPopupStates; private TopLevel? _root; @@ -25,7 +25,7 @@ namespace Avalonia.Diagnostics.Views { InitializeComponent(); - _keySubscription = InputManager.Instance.Process + _keySubscription = InputManager.Instance?.Process .OfType() .Where(x => x.Type == RawKeyEventType.KeyDown) .Subscribe(RawKeyDown); @@ -82,7 +82,7 @@ namespace Avalonia.Diagnostics.Views protected override void OnClosed(EventArgs e) { base.OnClosed(e); - _keySubscription.Dispose(); + _keySubscription?.Dispose(); foreach (var state in _frozenPopupStates) { diff --git a/src/Avalonia.Input/AccessKeyHandler.cs b/src/Avalonia.Input/AccessKeyHandler.cs index 5082265ea6..24feb357b9 100644 --- a/src/Avalonia.Input/AccessKeyHandler.cs +++ b/src/Avalonia.Input/AccessKeyHandler.cs @@ -133,7 +133,7 @@ namespace Avalonia.Input /// /// The event sender. /// The event args. - protected virtual void OnPreviewKeyDown(object sender, KeyEventArgs e) + protected virtual void OnPreviewKeyDown(object? sender, KeyEventArgs e) { if (e.Key == Key.LeftAlt || e.Key == Key.RightAlt) { @@ -172,7 +172,7 @@ namespace Avalonia.Input /// /// The event sender. /// The event args. - protected virtual void OnKeyDown(object sender, KeyEventArgs e) + protected virtual void OnKeyDown(object? sender, KeyEventArgs e) { bool menuIsOpen = MainMenu?.IsOpen == true; @@ -207,7 +207,7 @@ namespace Avalonia.Input /// /// The event sender. /// The event args. - protected virtual void OnPreviewKeyUp(object sender, KeyEventArgs e) + protected virtual void OnPreviewKeyUp(object? sender, KeyEventArgs e) { switch (e.Key) { @@ -234,7 +234,7 @@ namespace Avalonia.Input /// /// The event sender. /// The event args. - protected virtual void OnPreviewPointerPressed(object sender, PointerEventArgs e) + protected virtual void OnPreviewPointerPressed(object? sender, PointerEventArgs e) { if (_showingAccessKeys) { @@ -251,7 +251,7 @@ namespace Avalonia.Input _owner!.ShowAccessKeys = _showingAccessKeys = false; } - private void MainMenuClosed(object sender, EventArgs e) + private void MainMenuClosed(object? sender, EventArgs e) { _owner!.ShowAccessKeys = false; } diff --git a/src/Avalonia.Input/FocusManager.cs b/src/Avalonia.Input/FocusManager.cs index 2efd33df1d..177e6035b0 100644 --- a/src/Avalonia.Input/FocusManager.cs +++ b/src/Avalonia.Input/FocusManager.cs @@ -215,8 +215,11 @@ namespace Avalonia.Input /// /// The event sender. /// The event args. - private static void OnPreviewPointerPressed(object sender, RoutedEventArgs e) + private static void OnPreviewPointerPressed(object? sender, RoutedEventArgs e) { + if (sender is null) + return; + var ev = (PointerPressedEventArgs)e; var visual = (IVisual)sender; diff --git a/src/Avalonia.Input/KeyboardNavigationHandler.cs b/src/Avalonia.Input/KeyboardNavigationHandler.cs index 6493777105..b2ebee9ef5 100644 --- a/src/Avalonia.Input/KeyboardNavigationHandler.cs +++ b/src/Avalonia.Input/KeyboardNavigationHandler.cs @@ -98,7 +98,7 @@ namespace Avalonia.Input /// /// The event sender. /// The event args. - protected virtual void OnKeyDown(object sender, KeyEventArgs e) + protected virtual void OnKeyDown(object? sender, KeyEventArgs e) { var current = FocusManager.Instance.Current; diff --git a/src/Avalonia.Input/MouseDevice.cs b/src/Avalonia.Input/MouseDevice.cs index 401c6cb2ac..6eccb3f101 100644 --- a/src/Avalonia.Input/MouseDevice.cs +++ b/src/Avalonia.Input/MouseDevice.cs @@ -298,10 +298,10 @@ namespace Avalonia.Input root = root ?? throw new ArgumentNullException(nameof(root)); var hit = HitTest(root, p); + var source = GetSource(hit); - if (hit != null) + if (source is not null) { - var source = GetSource(hit); var e = new PointerReleasedEventArgs(source, _pointer, root, p, timestamp, props, inputModifiers, _lastMouseDownButton); @@ -321,10 +321,10 @@ namespace Avalonia.Input root = root ?? throw new ArgumentNullException(nameof(root)); var hit = HitTest(root, p); + var source = GetSource(hit); - if (hit != null) + if (source is not null) { - var source = GetSource(hit); var e = new PointerWheelEventArgs(source, _pointer, root, p, timestamp, props, inputModifiers, delta); source?.RaiseEvent(e); @@ -334,9 +334,10 @@ namespace Avalonia.Input return false; } - private IInteractive GetSource(IVisual hit) + private IInteractive? GetSource(IVisual? hit) { - hit = hit ?? throw new ArgumentNullException(nameof(hit)); + if (hit is null) + return null; return _pointer.Captured ?? (hit as IInteractive) ?? diff --git a/src/Avalonia.Input/Pointer.cs b/src/Avalonia.Input/Pointer.cs index a477711584..433b275ce4 100644 --- a/src/Avalonia.Input/Pointer.cs +++ b/src/Avalonia.Input/Pointer.cs @@ -59,7 +59,7 @@ namespace Avalonia.Input return parent as IInputElement ?? parent.FindAncestorOfType(); } - private void OnCaptureDetached(object sender, VisualTreeAttachmentEventArgs e) + private void OnCaptureDetached(object? sender, VisualTreeAttachmentEventArgs e) { Capture(GetNextCapture(e.Parent)); } diff --git a/src/Avalonia.Input/TextInput/InputMethodManager.cs b/src/Avalonia.Input/TextInput/InputMethodManager.cs index dafd397348..64422a7fdf 100644 --- a/src/Avalonia.Input/TextInput/InputMethodManager.cs +++ b/src/Avalonia.Input/TextInput/InputMethodManager.cs @@ -8,7 +8,6 @@ namespace Avalonia.Input.TextInput private ITextInputMethodImpl? _im; private IInputElement? _focusedElement; private ITextInputMethodClient? _client; - private IDisposable? _subscribeDisposable; private readonly TransformTrackingHelper _transformTracker = new TransformTrackingHelper(); public TextInputMethodManager() @@ -64,7 +63,7 @@ namespace Avalonia.Input.TextInput } } - private void OnTextViewVisualChanged(object sender, EventArgs e) + private void OnTextViewVisualChanged(object? sender, EventArgs e) => _transformTracker.SetVisual(_client?.TextViewVisual); private void UpdateCursorRect() @@ -79,7 +78,7 @@ namespace Avalonia.Input.TextInput _im.SetCursorRect(_client.CursorRectangle.TransformToAABB(transform.Value)); } - private void OnCursorRectangleChanged(object sender, EventArgs e) + private void OnCursorRectangleChanged(object? sender, EventArgs e) { if (sender == _client) UpdateCursorRect(); diff --git a/src/Avalonia.Input/TextInput/TransformTrackingHelper.cs b/src/Avalonia.Input/TextInput/TransformTrackingHelper.cs index 4211360a8f..325c72bcb5 100644 --- a/src/Avalonia.Input/TextInput/TransformTrackingHelper.cs +++ b/src/Avalonia.Input/TextInput/TransformTrackingHelper.cs @@ -80,7 +80,7 @@ namespace Avalonia.Input.TextInput } } - private void OnAttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs visualTreeAttachmentEventArgs) + private void OnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs visualTreeAttachmentEventArgs) { SubscribeToParents(); UpdateMatrix(); @@ -94,13 +94,13 @@ namespace Avalonia.Input.TextInput Dispatcher.UIThread.Post(UpdateMatrix, DispatcherPriority.Render); } - private void PropertyChangedHandler(object sender, AvaloniaPropertyChangedEventArgs e) + private void PropertyChangedHandler(object? sender, AvaloniaPropertyChangedEventArgs e) { if (e.IsEffectiveValueChange && e.Property == Visual.BoundsProperty) EnqueueForUpdate(); } - private void OnDetachedFromVisualTree(object sender, VisualTreeAttachmentEventArgs visualTreeAttachmentEventArgs) + private void OnDetachedFromVisualTree(object? sender, VisualTreeAttachmentEventArgs visualTreeAttachmentEventArgs) { UnsubscribeFromParents(); UpdateMatrix(); diff --git a/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj b/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj index 341b7bff78..effb0e4c99 100644 --- a/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj +++ b/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj @@ -1,8 +1,6 @@  netstandard2.0;net6.0 - Enable - CS8600;CS8602;CS8603 @@ -12,4 +10,5 @@ + \ No newline at end of file diff --git a/src/Avalonia.Interactivity/RoutedEvent.cs b/src/Avalonia.Interactivity/RoutedEvent.cs index 3f38a61048..a9b7dc8c89 100644 --- a/src/Avalonia.Interactivity/RoutedEvent.cs +++ b/src/Avalonia.Interactivity/RoutedEvent.cs @@ -120,7 +120,7 @@ namespace Avalonia.Interactivity bool handledEventsToo = false) where TTarget : class, IInteractive { - void Adapter(object sender, RoutedEventArgs e) + void Adapter(object? sender, RoutedEventArgs e) { if (sender is TTarget target && e is TEventArgs args) { @@ -136,7 +136,7 @@ namespace Avalonia.Interactivity RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble, bool handledEventsToo = false) where TTarget : class, IInteractive { - void Adapter(object sender, RoutedEventArgs e) + void Adapter(object? sender, RoutedEventArgs e) { if (sender is TTarget target && e is TEventArgs args) { diff --git a/src/Markup/Avalonia.Markup/Data/BindingBase.cs b/src/Markup/Avalonia.Markup/Data/BindingBase.cs index e69d9570e2..0cffde6043 100644 --- a/src/Markup/Avalonia.Markup/Data/BindingBase.cs +++ b/src/Markup/Avalonia.Markup/Data/BindingBase.cs @@ -272,7 +272,7 @@ namespace Avalonia.Data _target.PropertyChanged -= PropertyChanged; } - private void PropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e) + private void PropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) { if (e.Property == _property) { diff --git a/src/Markup/Avalonia.Markup/Data/TemplateBinding.cs b/src/Markup/Avalonia.Markup/Data/TemplateBinding.cs index 8c33a7b721..265af3c5da 100644 --- a/src/Markup/Avalonia.Markup/Data/TemplateBinding.cs +++ b/src/Markup/Avalonia.Markup/Data/TemplateBinding.cs @@ -160,7 +160,7 @@ namespace Avalonia.Data PublishValue(); } - private void TargetPropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e) + private void TargetPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) { if (e.Property == StyledElement.TemplatedParentProperty) { @@ -173,7 +173,7 @@ namespace Avalonia.Data } } - private void TemplatedParentPropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e) + private void TemplatedParentPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) { if (e.Property == Property) { diff --git a/src/Markup/Avalonia.Markup/Markup/Data/DelayedBinding.cs b/src/Markup/Avalonia.Markup/Markup/Data/DelayedBinding.cs index b15081155f..3d6703daa3 100644 --- a/src/Markup/Avalonia.Markup/Markup/Data/DelayedBinding.cs +++ b/src/Markup/Avalonia.Markup/Markup/Data/DelayedBinding.cs @@ -95,9 +95,9 @@ namespace Avalonia.Markup.Data } } - private static void ApplyBindings(object sender, EventArgs e) + private static void ApplyBindings(object? sender, EventArgs e) { - var target = (IStyledElement)sender; + var target = (IStyledElement)sender!; ApplyBindings(target); target.Initialized -= ApplyBindings; } diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/StringIndexerNode.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/StringIndexerNode.cs index 57d99ef913..3857c9aff0 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/StringIndexerNode.cs +++ b/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/StringIndexerNode.cs @@ -297,7 +297,7 @@ namespace Avalonia.Markup.Parsers.Nodes protected override bool ShouldUpdate(object? sender, PropertyChangedEventArgs e) { - if (sender is null) + if (sender is null || e.PropertyName is null) return false; var typeInfo = sender.GetType().GetTypeInfo(); return typeInfo.GetDeclaredProperty(e.PropertyName)?.GetIndexParameters().Any() ?? false; diff --git a/src/Shared/PlatformSupport/AssetLoader.cs b/src/Shared/PlatformSupport/AssetLoader.cs index 90b341297c..affb861de0 100644 --- a/src/Shared/PlatformSupport/AssetLoader.cs +++ b/src/Shared/PlatformSupport/AssetLoader.cs @@ -17,7 +17,7 @@ namespace Avalonia.Shared.PlatformSupport private static readonly Dictionary AssemblyNameCache = new Dictionary(); - private AssemblyDescriptor _defaultResmAssembly; + private AssemblyDescriptor? _defaultResmAssembly; /// /// Initializes a new instance of the class. @@ -25,7 +25,7 @@ namespace Avalonia.Shared.PlatformSupport /// /// The default assembly from which to load resm: assets for which no assembly is specified. /// - public AssetLoader(Assembly assembly = null) + public AssetLoader(Assembly? assembly = null) { if (assembly == null) assembly = Assembly.GetEntryAssembly(); @@ -50,7 +50,7 @@ namespace Avalonia.Shared.PlatformSupport /// A base URI to use if is relative. /// /// True if the asset could be found; otherwise false. - public bool Exists(Uri uri, Uri baseUri = null) + public bool Exists(Uri uri, Uri? baseUri = null) { return GetAsset(uri, baseUri) != null; } @@ -66,7 +66,7 @@ namespace Avalonia.Shared.PlatformSupport /// /// The asset could not be found. /// - public Stream Open(Uri uri, Uri baseUri = null) => OpenAndGetAssembly(uri, baseUri).Item1; + public Stream Open(Uri uri, Uri? baseUri = null) => OpenAndGetAssembly(uri, baseUri).Item1; /// /// Opens the asset with the requested URI and returns the asset stream and the @@ -82,7 +82,7 @@ namespace Avalonia.Shared.PlatformSupport /// /// The asset could not be found. /// - public (Stream stream, Assembly assembly) OpenAndGetAssembly(Uri uri, Uri baseUri = null) + public (Stream stream, Assembly assembly) OpenAndGetAssembly(Uri uri, Uri? baseUri = null) { var asset = GetAsset(uri, baseUri); @@ -94,11 +94,11 @@ namespace Avalonia.Shared.PlatformSupport return (asset.GetStream(), asset.Assembly); } - public Assembly GetAssembly(Uri uri, Uri baseUri) + public Assembly? GetAssembly(Uri uri, Uri baseUri) { if (!uri.IsAbsoluteUri && baseUri != null) uri = new Uri(baseUri, uri); - return GetAssembly(uri).Assembly; + return GetAssembly(uri)?.Assembly; } /// @@ -113,7 +113,7 @@ namespace Avalonia.Shared.PlatformSupport { var assembly = GetAssembly(uri); - return assembly?.Resources.Where(x => x.Key.Contains(uri.AbsolutePath)) + return assembly?.Resources?.Where(x => x.Key.Contains(uri.AbsolutePath)) .Select(x =>new Uri($"resm:{x.Key}?assembly={assembly.Name}")) ?? Enumerable.Empty(); } @@ -139,7 +139,7 @@ namespace Avalonia.Shared.PlatformSupport return Enumerable.Empty(); } - private Uri EnsureAbsolute(Uri uri, Uri baseUri) + private Uri EnsureAbsolute(Uri uri, Uri? baseUri) { if (uri.IsAbsoluteUri) return uri; @@ -153,7 +153,7 @@ namespace Avalonia.Shared.PlatformSupport return new Uri(baseUri, uri); } - private IAssetDescriptor GetAsset(Uri uri, Uri baseUri) + private IAssetDescriptor? GetAsset(Uri uri, Uri? baseUri) { if (uri.IsAbsoluteUri && uri.Scheme == "resm") { @@ -166,10 +166,9 @@ namespace Avalonia.Shared.PlatformSupport "don't know where to look up for the resource, try specifying assembly explicitly."); } - IAssetDescriptor rv; - var resourceKey = uri.AbsolutePath; - asm.Resources.TryGetValue(resourceKey, out rv); + IAssetDescriptor? rv = null; + asm.Resources?.TryGetValue(resourceKey, out rv); return rv; } @@ -193,7 +192,7 @@ namespace Avalonia.Shared.PlatformSupport return (asm, uri.AbsolutePath); } - private AssemblyDescriptor GetAssembly(Uri uri) + private AssemblyDescriptor? GetAssembly(Uri? uri) { if (uri != null) { @@ -205,9 +204,8 @@ namespace Avalonia.Shared.PlatformSupport if (uri.Scheme == "resm") { var qs = ParseQueryString(uri); - string assemblyName; - if (qs.TryGetValue("assembly", out assemblyName)) + if (qs.TryGetValue("assembly", out var assemblyName)) { return GetAssembly(assemblyName); } @@ -222,8 +220,7 @@ namespace Avalonia.Shared.PlatformSupport if (name == null) throw new ArgumentNullException(nameof(name)); - AssemblyDescriptor rv; - if (!AssemblyNameCache.TryGetValue(name, out rv)) + if (!AssemblyNameCache.TryGetValue(name, out var rv)) { var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies(); var match = loadedAssemblies.FirstOrDefault(a => a.GetName().Name == name); @@ -275,7 +272,8 @@ namespace Avalonia.Shared.PlatformSupport public Stream GetStream() { - return _asm.GetManifestResourceStream(_name); + var s = _asm.GetManifestResourceStream(_name); + return s ?? throw new InvalidOperationException($"Could not find manifest resource stream '{_name}',"); } public Assembly Assembly => _asm; @@ -296,7 +294,9 @@ namespace Avalonia.Shared.PlatformSupport public Stream GetStream() { - return new SlicedStream(Assembly.GetManifestResourceStream(AvaloniaResourceName), _offset, _length); + var s = Assembly.GetManifestResourceStream(AvaloniaResourceName) ?? + throw new InvalidOperationException($"Could not find manifest resource stream '{AvaloniaResourceName}',"); + return new SlicedStream(s, _offset, _length); } } @@ -383,9 +383,9 @@ namespace Avalonia.Shared.PlatformSupport } public Assembly Assembly { get; } - public Dictionary Resources { get; } - public Dictionary AvaloniaResources { get; } - public string Name { get; } + public Dictionary? Resources { get; } + public Dictionary? AvaloniaResources { get; } + public string? Name { get; } } public static void RegisterResUriParsers() diff --git a/src/Web/Avalonia.Web.Blazor/WindowingPlatform.cs b/src/Web/Avalonia.Web.Blazor/WindowingPlatform.cs index a3f2de5b61..4439ec161c 100644 --- a/src/Web/Avalonia.Web.Blazor/WindowingPlatform.cs +++ b/src/Web/Avalonia.Web.Blazor/WindowingPlatform.cs @@ -12,7 +12,7 @@ namespace Avalonia.Web.Blazor public class BlazorWindowingPlatform : IWindowingPlatform, IPlatformSettings, IPlatformThreadingInterface { private bool _signaled; - private static int s_uiThreadId = -1; + private static KeyboardDevice? s_keyboard; public IWindowImpl CreateWindow() => throw new NotSupportedException(); @@ -26,16 +26,17 @@ namespace Avalonia.Web.Blazor return null; } - public static KeyboardDevice Keyboard { get; private set; } + public static KeyboardDevice Keyboard => s_keyboard ?? + throw new InvalidOperationException("BlazorWindowingPlatform not registered."); public static void Register() { var instance = new BlazorWindowingPlatform(); - Keyboard = new KeyboardDevice(); + s_keyboard = new KeyboardDevice(); AvaloniaLocator.CurrentMutable .Bind().ToSingleton() .Bind().ToSingleton() - .Bind().ToConstant(Keyboard) + .Bind().ToConstant(s_keyboard) .Bind().ToConstant(instance) .Bind().ToConstant(instance) .Bind().ToConstant(new RenderLoop()) From 9c585e85fb5b18600e7fe3565a02acaa15d22445 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 16 Dec 2021 19:46:47 +0100 Subject: [PATCH 27/90] Updated ncrunch configuration. Has changed a lot due to dual targeting net6 and netstandard2. --- .ncrunch/Avalonia.Android.v3.ncrunchproject | 1 - ...imation.UnitTests.netcoreapp2.0.v3.ncrunchproject | 7 ------- .ncrunch/Avalonia.Animation.v3.ncrunchproject | 8 -------- .../Avalonia.Base.UnitTests.net461.v3.ncrunchproject | 5 ----- .../Avalonia.Base.UnitTests.net47.v3.ncrunchproject | 7 ------- ...ia.Base.UnitTests.netcoreapp2.0.v3.ncrunchproject | 7 ------- .ncrunch/Avalonia.Base.UnitTests.v3.ncrunchproject | 8 -------- .ncrunch/Avalonia.Base.v3.ncrunchproject | 8 -------- .ncrunch/Avalonia.Benchmarks.v3.ncrunchproject | 8 -------- .ncrunch/Avalonia.Build.Tasks.v3.ncrunchproject | 3 --- .../Avalonia.Cairo.RenderTests.v3.ncrunchproject | 8 -------- .ncrunch/Avalonia.Cairo.v3.ncrunchproject | 5 ----- .../Avalonia.Controls.DataGrid.v3.ncrunchproject | 7 ------- ...lonia.Controls.UnitTests.net461.v3.ncrunchproject | 5 ----- ...alonia.Controls.UnitTests.net47.v3.ncrunchproject | 8 -------- ...ontrols.UnitTests.netcoreapp2.0.v3.ncrunchproject | 7 ------- .../Avalonia.Controls.UnitTests.v3.ncrunchproject | 8 -------- .ncrunch/Avalonia.Controls.v3.ncrunchproject | 8 -------- ...valonia.Designer.HostApp.net461.v3.ncrunchproject | 4 +--- ....Designer.HostApp.netcoreapp2.0.v3.ncrunchproject | 4 +--- .ncrunch/Avalonia.Designer.v3.ncrunchproject | 5 ----- ...valonia.DesignerSupport.TestApp.v3.ncrunchproject | 1 - .../Avalonia.DesignerSupport.Tests.v3.ncrunchproject | 1 - .ncrunch/Avalonia.DesignerSupport.v3.ncrunchproject | 8 -------- ...ect => Avalonia.Desktop.net6.0.v3.ncrunchproject} | 0 ...valonia.Desktop.netstandard2.0.v3.ncrunchproject} | 0 .../Avalonia.DesktopRuntime.net461.v3.ncrunchproject | 7 ------- ...ia.DesktopRuntime.netcoreapp2.0.v3.ncrunchproject | 7 ------- .ncrunch/Avalonia.DesktopRuntime.v3.ncrunchproject | 3 --- .ncrunch/Avalonia.Diagnostics.v3.ncrunchproject | 8 -------- .../Avalonia.Direct2D1.RenderTests.v3.ncrunchproject | 12 ------------ .../Avalonia.Direct2D1.UnitTests.v3.ncrunchproject | 8 -------- .ncrunch/Avalonia.Direct2D1.v3.ncrunchproject | 8 -------- .ncrunch/Avalonia.Gtk3.v3.ncrunchproject | 7 ------- ...Avalonia.Input.UnitTests.net461.v3.ncrunchproject | 5 ----- .../Avalonia.Input.UnitTests.net47.v3.ncrunchproject | 7 ------- ...a.Input.UnitTests.netcoreapp2.0.v3.ncrunchproject | 7 ------- .ncrunch/Avalonia.Input.UnitTests.v3.ncrunchproject | 5 ----- .ncrunch/Avalonia.Input.v3.ncrunchproject | 8 -------- ....Interactivity.UnitTests.net461.v3.ncrunchproject | 5 ----- ...ctivity.UnitTests.netcoreapp1.1.v3.ncrunchproject | 5 ----- ...valonia.Interactivity.UnitTests.v3.ncrunchproject | 8 -------- .ncrunch/Avalonia.Interactivity.v3.ncrunchproject | 8 -------- ...valonia.Layout.UnitTests.net461.v3.ncrunchproject | 5 ----- .ncrunch/Avalonia.Layout.UnitTests.v3.ncrunchproject | 8 -------- .ncrunch/Avalonia.Layout.v3.ncrunchproject | 8 -------- .ncrunch/Avalonia.LeakTests.v3.ncrunchproject | 8 -------- .ncrunch/Avalonia.LinuxFramebuffer.v3.ncrunchproject | 8 -------- .ncrunch/Avalonia.Logging.Serilog.v3.ncrunchproject | 8 -------- ...valonia.Markup.UnitTests.net461.v3.ncrunchproject | 5 ----- ...Avalonia.Markup.UnitTests.net47.v3.ncrunchproject | 7 ------- ....Markup.UnitTests.netcoreapp1.1.v3.ncrunchproject | 9 --------- ....Markup.UnitTests.netcoreapp2.0.v3.ncrunchproject | 7 ------- .ncrunch/Avalonia.Markup.UnitTests.v3.ncrunchproject | 5 ----- ...ia.Markup.Xaml.UnitTests.net461.v3.ncrunchproject | 5 ----- ...nia.Markup.Xaml.UnitTests.net47.v3.ncrunchproject | 7 ------- ...up.Xaml.UnitTests.netcoreapp2.0.v3.ncrunchproject | 7 ------- .../Avalonia.Markup.Xaml.UnitTests.v3.ncrunchproject | 5 ----- .ncrunch/Avalonia.Markup.Xaml.v3.ncrunchproject | 8 -------- .ncrunch/Avalonia.Markup.v3.ncrunchproject | 8 -------- .ncrunch/Avalonia.MicroCom.v3.ncrunchproject | 3 --- .ncrunch/Avalonia.Native.net6.0.v3.ncrunchproject | 6 ++++++ ...Avalonia.Native.netstandard2.0.v3.ncrunchproject} | 0 .ncrunch/Avalonia.OpenGL.v3.ncrunchproject | 7 ------- .../Avalonia.ReactiveUI.UnitTests.v3.ncrunchproject | 7 ------- .ncrunch/Avalonia.ReactiveUI.v3.ncrunchproject | 8 -------- .ncrunch/Avalonia.RenderTests.v3.ncrunchproject | 3 --- .ncrunch/Avalonia.Skia.Android.v3.ncrunchproject | 6 ------ .ncrunch/Avalonia.Skia.Desktop.v3.ncrunchproject | 8 -------- .ncrunch/Avalonia.Skia.RenderTests.v3.ncrunchproject | 12 ------------ .ncrunch/Avalonia.Skia.UnitTests.v3.ncrunchproject | 7 ------- .ncrunch/Avalonia.Skia.iOS.v3.ncrunchproject | 6 ------ .ncrunch/Avalonia.Skia.v3.ncrunchproject | 7 ------- ...alonia.Styling.UnitTests.net461.v3.ncrunchproject | 5 ----- ...valonia.Styling.UnitTests.net47.v3.ncrunchproject | 7 ------- ...Styling.UnitTests.netcoreapp2.0.v3.ncrunchproject | 7 ------- .../Avalonia.Styling.UnitTests.v3.ncrunchproject | 5 ----- .ncrunch/Avalonia.Styling.v3.ncrunchproject | 8 -------- .ncrunch/Avalonia.Themes.Default.v3.ncrunchproject | 8 -------- .ncrunch/Avalonia.UnitTests.net461.v3.ncrunchproject | 5 ----- .ncrunch/Avalonia.UnitTests.v3.ncrunchproject | 8 -------- ...alonia.Visuals.UnitTests.net461.v3.ncrunchproject | 5 ----- ...valonia.Visuals.UnitTests.net47.v3.ncrunchproject | 7 ------- ...Visuals.UnitTests.netcoreapp2.0.v3.ncrunchproject | 7 ------- .../Avalonia.Visuals.UnitTests.v3.ncrunchproject | 9 --------- .ncrunch/Avalonia.Visuals.v3.ncrunchproject | 8 -------- .ncrunch/Avalonia.Win32.Interop.v3.ncrunchproject | 8 -------- ...oject => Avalonia.Win32.net6.0.v3.ncrunchproject} | 4 ---- .../Avalonia.Win32.netstandard2.0.v3.ncrunchproject | 7 +++++++ .ncrunch/Avalonia.X11.v3.ncrunchproject | 7 ------- .ncrunch/Avalonia.net461.v3.ncrunchproject | 4 +++- ...unchproject => Avalonia.net6.0.v3.ncrunchproject} | 2 +- .ncrunch/Avalonia.netcoreapp2.0.v3.ncrunchproject | 1 - .ncrunch/Avalonia.netstandard2.0.v3.ncrunchproject | 1 - .ncrunch/BindingDemo.net461.v3.ncrunchproject | 5 ----- .ncrunch/BindingDemo.netcoreapp2.0.v3.ncrunchproject | 5 ----- ...hproject => ControlCatalog.Web.v3.ncrunchproject} | 0 .ncrunch/ControlCatalog.v3.ncrunchproject | 1 - ...runchproject => ControlSamples.v3.ncrunchproject} | 0 .ncrunch/Direct3DInteropSample.v3.ncrunchproject | 4 ---- .ncrunch/GtkInteropDemo.v3.ncrunchproject | 5 ----- ....v3.ncrunchproject => MiniMvvm.v3.ncrunchproject} | 0 .ncrunch/PlatformSanityChecks.v3.ncrunchproject | 4 +--- .ncrunch/PlatformSupport.v3.ncrunchproject | 3 --- .ncrunch/RenderDemo.net461.v3.ncrunchproject | 5 ----- .ncrunch/RenderDemo.netcoreapp2.0.v3.ncrunchproject | 5 ----- .ncrunch/RenderHelpers.v3.ncrunchproject | 3 --- .ncrunch/VirtualizationDemo.net461.v3.ncrunchproject | 5 ----- ...irtualizationDemo.netcoreapp2.0.v3.ncrunchproject | 5 ----- .../Resources/Resource.Designer.cs | 2 +- 110 files changed, 21 insertions(+), 611 deletions(-) delete mode 100644 .ncrunch/Avalonia.Animation.UnitTests.netcoreapp2.0.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Animation.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Base.UnitTests.net461.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Base.UnitTests.net47.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Base.UnitTests.netcoreapp2.0.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Base.UnitTests.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Base.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Benchmarks.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Build.Tasks.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Cairo.RenderTests.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Cairo.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Controls.DataGrid.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Controls.UnitTests.net461.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Controls.UnitTests.net47.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Controls.UnitTests.netcoreapp2.0.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Controls.UnitTests.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Controls.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Designer.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.DesignerSupport.v3.ncrunchproject rename .ncrunch/{Avalonia.Designer.HostApp.NetFX.v3.ncrunchproject => Avalonia.Desktop.net6.0.v3.ncrunchproject} (100%) rename .ncrunch/{Avalonia.Designer.HostApp.v3.ncrunchproject => Avalonia.Desktop.netstandard2.0.v3.ncrunchproject} (100%) delete mode 100644 .ncrunch/Avalonia.DesktopRuntime.net461.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.DesktopRuntime.netcoreapp2.0.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.DesktopRuntime.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Diagnostics.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Direct2D1.RenderTests.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Direct2D1.UnitTests.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Direct2D1.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Gtk3.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Input.UnitTests.net461.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Input.UnitTests.net47.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Input.UnitTests.netcoreapp2.0.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Input.UnitTests.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Input.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Interactivity.UnitTests.net461.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Interactivity.UnitTests.netcoreapp1.1.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Interactivity.UnitTests.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Interactivity.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Layout.UnitTests.net461.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Layout.UnitTests.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Layout.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.LeakTests.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.LinuxFramebuffer.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Logging.Serilog.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Markup.UnitTests.net461.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Markup.UnitTests.net47.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Markup.UnitTests.netcoreapp1.1.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Markup.UnitTests.netcoreapp2.0.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Markup.UnitTests.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Markup.Xaml.UnitTests.net461.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Markup.Xaml.UnitTests.net47.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Markup.Xaml.UnitTests.netcoreapp2.0.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Markup.Xaml.UnitTests.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Markup.Xaml.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Markup.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.MicroCom.v3.ncrunchproject create mode 100644 .ncrunch/Avalonia.Native.net6.0.v3.ncrunchproject rename .ncrunch/{Avalonia.Desktop.v3.ncrunchproject => Avalonia.Native.netstandard2.0.v3.ncrunchproject} (100%) delete mode 100644 .ncrunch/Avalonia.OpenGL.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.ReactiveUI.UnitTests.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.ReactiveUI.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.RenderTests.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Skia.Android.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Skia.Desktop.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Skia.RenderTests.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Skia.UnitTests.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Skia.iOS.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Skia.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Styling.UnitTests.net461.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Styling.UnitTests.net47.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Styling.UnitTests.netcoreapp2.0.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Styling.UnitTests.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Styling.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Themes.Default.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.UnitTests.net461.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.UnitTests.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Visuals.UnitTests.net461.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Visuals.UnitTests.net47.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Visuals.UnitTests.netcoreapp2.0.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Visuals.UnitTests.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Visuals.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.Win32.Interop.v3.ncrunchproject rename .ncrunch/{Avalonia.Win32.v3.ncrunchproject => Avalonia.Win32.net6.0.v3.ncrunchproject} (50%) create mode 100644 .ncrunch/Avalonia.Win32.netstandard2.0.v3.ncrunchproject delete mode 100644 .ncrunch/Avalonia.X11.v3.ncrunchproject rename .ncrunch/{Avalonia.Animation.UnitTests.net47.v3.ncrunchproject => Avalonia.net6.0.v3.ncrunchproject} (59%) delete mode 100644 .ncrunch/BindingDemo.net461.v3.ncrunchproject delete mode 100644 .ncrunch/BindingDemo.netcoreapp2.0.v3.ncrunchproject rename .ncrunch/{Avalonia.Native.v3.ncrunchproject => ControlCatalog.Web.v3.ncrunchproject} (100%) rename .ncrunch/{Avalonia.Skia.Desktop.NetStandard.v3.ncrunchproject => ControlSamples.v3.ncrunchproject} (100%) delete mode 100644 .ncrunch/GtkInteropDemo.v3.ncrunchproject rename .ncrunch/{Avalonia.iOSTestApplication.v3.ncrunchproject => MiniMvvm.v3.ncrunchproject} (100%) delete mode 100644 .ncrunch/PlatformSupport.v3.ncrunchproject delete mode 100644 .ncrunch/RenderDemo.net461.v3.ncrunchproject delete mode 100644 .ncrunch/RenderDemo.netcoreapp2.0.v3.ncrunchproject delete mode 100644 .ncrunch/RenderHelpers.v3.ncrunchproject delete mode 100644 .ncrunch/VirtualizationDemo.net461.v3.ncrunchproject delete mode 100644 .ncrunch/VirtualizationDemo.netcoreapp2.0.v3.ncrunchproject diff --git a/.ncrunch/Avalonia.Android.v3.ncrunchproject b/.ncrunch/Avalonia.Android.v3.ncrunchproject index a4953e14f9..319cd523ce 100644 --- a/.ncrunch/Avalonia.Android.v3.ncrunchproject +++ b/.ncrunch/Avalonia.Android.v3.ncrunchproject @@ -1,6 +1,5 @@  True - True \ No newline at end of file diff --git a/.ncrunch/Avalonia.Animation.UnitTests.netcoreapp2.0.v3.ncrunchproject b/.ncrunch/Avalonia.Animation.UnitTests.netcoreapp2.0.v3.ncrunchproject deleted file mode 100644 index e9d39b0c74..0000000000 --- a/.ncrunch/Avalonia.Animation.UnitTests.netcoreapp2.0.v3.ncrunchproject +++ /dev/null @@ -1,7 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Animation.v3.ncrunchproject b/.ncrunch/Avalonia.Animation.v3.ncrunchproject deleted file mode 100644 index e12537d535..0000000000 --- a/.ncrunch/Avalonia.Animation.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Base.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Base.UnitTests.net461.v3.ncrunchproject deleted file mode 100644 index 6800b4a3fe..0000000000 --- a/.ncrunch/Avalonia.Base.UnitTests.net461.v3.ncrunchproject +++ /dev/null @@ -1,5 +0,0 @@ - - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Base.UnitTests.net47.v3.ncrunchproject b/.ncrunch/Avalonia.Base.UnitTests.net47.v3.ncrunchproject deleted file mode 100644 index e9d39b0c74..0000000000 --- a/.ncrunch/Avalonia.Base.UnitTests.net47.v3.ncrunchproject +++ /dev/null @@ -1,7 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Base.UnitTests.netcoreapp2.0.v3.ncrunchproject b/.ncrunch/Avalonia.Base.UnitTests.netcoreapp2.0.v3.ncrunchproject deleted file mode 100644 index e9d39b0c74..0000000000 --- a/.ncrunch/Avalonia.Base.UnitTests.netcoreapp2.0.v3.ncrunchproject +++ /dev/null @@ -1,7 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Base.UnitTests.v3.ncrunchproject b/.ncrunch/Avalonia.Base.UnitTests.v3.ncrunchproject deleted file mode 100644 index 13f78c1d4c..0000000000 --- a/.ncrunch/Avalonia.Base.UnitTests.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - LongTestTimesWithoutParallelExecution - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Base.v3.ncrunchproject b/.ncrunch/Avalonia.Base.v3.ncrunchproject deleted file mode 100644 index e12537d535..0000000000 --- a/.ncrunch/Avalonia.Base.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Benchmarks.v3.ncrunchproject b/.ncrunch/Avalonia.Benchmarks.v3.ncrunchproject deleted file mode 100644 index e12537d535..0000000000 --- a/.ncrunch/Avalonia.Benchmarks.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Build.Tasks.v3.ncrunchproject b/.ncrunch/Avalonia.Build.Tasks.v3.ncrunchproject deleted file mode 100644 index 95a483b433..0000000000 --- a/.ncrunch/Avalonia.Build.Tasks.v3.ncrunchproject +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Cairo.RenderTests.v3.ncrunchproject b/.ncrunch/Avalonia.Cairo.RenderTests.v3.ncrunchproject deleted file mode 100644 index 101c806e63..0000000000 --- a/.ncrunch/Avalonia.Cairo.RenderTests.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - AbnormalReferenceResolution - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Cairo.v3.ncrunchproject b/.ncrunch/Avalonia.Cairo.v3.ncrunchproject deleted file mode 100644 index 6800b4a3fe..0000000000 --- a/.ncrunch/Avalonia.Cairo.v3.ncrunchproject +++ /dev/null @@ -1,5 +0,0 @@ - - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Controls.DataGrid.v3.ncrunchproject b/.ncrunch/Avalonia.Controls.DataGrid.v3.ncrunchproject deleted file mode 100644 index e9d39b0c74..0000000000 --- a/.ncrunch/Avalonia.Controls.DataGrid.v3.ncrunchproject +++ /dev/null @@ -1,7 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Controls.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Controls.UnitTests.net461.v3.ncrunchproject deleted file mode 100644 index 6800b4a3fe..0000000000 --- a/.ncrunch/Avalonia.Controls.UnitTests.net461.v3.ncrunchproject +++ /dev/null @@ -1,5 +0,0 @@ - - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Controls.UnitTests.net47.v3.ncrunchproject b/.ncrunch/Avalonia.Controls.UnitTests.net47.v3.ncrunchproject deleted file mode 100644 index f30a20df78..0000000000 --- a/.ncrunch/Avalonia.Controls.UnitTests.net47.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - Avalonia.Controls.UnitTests.TimePickerTests - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Controls.UnitTests.netcoreapp2.0.v3.ncrunchproject b/.ncrunch/Avalonia.Controls.UnitTests.netcoreapp2.0.v3.ncrunchproject deleted file mode 100644 index e9d39b0c74..0000000000 --- a/.ncrunch/Avalonia.Controls.UnitTests.netcoreapp2.0.v3.ncrunchproject +++ /dev/null @@ -1,7 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Controls.UnitTests.v3.ncrunchproject b/.ncrunch/Avalonia.Controls.UnitTests.v3.ncrunchproject deleted file mode 100644 index 13f78c1d4c..0000000000 --- a/.ncrunch/Avalonia.Controls.UnitTests.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - LongTestTimesWithoutParallelExecution - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Controls.v3.ncrunchproject b/.ncrunch/Avalonia.Controls.v3.ncrunchproject deleted file mode 100644 index e12537d535..0000000000 --- a/.ncrunch/Avalonia.Controls.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Designer.HostApp.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Designer.HostApp.net461.v3.ncrunchproject index e9d39b0c74..319cd523ce 100644 --- a/.ncrunch/Avalonia.Designer.HostApp.net461.v3.ncrunchproject +++ b/.ncrunch/Avalonia.Designer.HostApp.net461.v3.ncrunchproject @@ -1,7 +1,5 @@  - - MissingOrIgnoredProjectReference - + True \ No newline at end of file diff --git a/.ncrunch/Avalonia.Designer.HostApp.netcoreapp2.0.v3.ncrunchproject b/.ncrunch/Avalonia.Designer.HostApp.netcoreapp2.0.v3.ncrunchproject index e9d39b0c74..319cd523ce 100644 --- a/.ncrunch/Avalonia.Designer.HostApp.netcoreapp2.0.v3.ncrunchproject +++ b/.ncrunch/Avalonia.Designer.HostApp.netcoreapp2.0.v3.ncrunchproject @@ -1,7 +1,5 @@  - - MissingOrIgnoredProjectReference - + True \ No newline at end of file diff --git a/.ncrunch/Avalonia.Designer.v3.ncrunchproject b/.ncrunch/Avalonia.Designer.v3.ncrunchproject deleted file mode 100644 index 6800b4a3fe..0000000000 --- a/.ncrunch/Avalonia.Designer.v3.ncrunchproject +++ /dev/null @@ -1,5 +0,0 @@ - - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.DesignerSupport.TestApp.v3.ncrunchproject b/.ncrunch/Avalonia.DesignerSupport.TestApp.v3.ncrunchproject index a4953e14f9..319cd523ce 100644 --- a/.ncrunch/Avalonia.DesignerSupport.TestApp.v3.ncrunchproject +++ b/.ncrunch/Avalonia.DesignerSupport.TestApp.v3.ncrunchproject @@ -1,6 +1,5 @@  True - True \ No newline at end of file diff --git a/.ncrunch/Avalonia.DesignerSupport.Tests.v3.ncrunchproject b/.ncrunch/Avalonia.DesignerSupport.Tests.v3.ncrunchproject index a4953e14f9..319cd523ce 100644 --- a/.ncrunch/Avalonia.DesignerSupport.Tests.v3.ncrunchproject +++ b/.ncrunch/Avalonia.DesignerSupport.Tests.v3.ncrunchproject @@ -1,6 +1,5 @@  True - True \ No newline at end of file diff --git a/.ncrunch/Avalonia.DesignerSupport.v3.ncrunchproject b/.ncrunch/Avalonia.DesignerSupport.v3.ncrunchproject deleted file mode 100644 index e12537d535..0000000000 --- a/.ncrunch/Avalonia.DesignerSupport.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Designer.HostApp.NetFX.v3.ncrunchproject b/.ncrunch/Avalonia.Desktop.net6.0.v3.ncrunchproject similarity index 100% rename from .ncrunch/Avalonia.Designer.HostApp.NetFX.v3.ncrunchproject rename to .ncrunch/Avalonia.Desktop.net6.0.v3.ncrunchproject diff --git a/.ncrunch/Avalonia.Designer.HostApp.v3.ncrunchproject b/.ncrunch/Avalonia.Desktop.netstandard2.0.v3.ncrunchproject similarity index 100% rename from .ncrunch/Avalonia.Designer.HostApp.v3.ncrunchproject rename to .ncrunch/Avalonia.Desktop.netstandard2.0.v3.ncrunchproject diff --git a/.ncrunch/Avalonia.DesktopRuntime.net461.v3.ncrunchproject b/.ncrunch/Avalonia.DesktopRuntime.net461.v3.ncrunchproject deleted file mode 100644 index e9d39b0c74..0000000000 --- a/.ncrunch/Avalonia.DesktopRuntime.net461.v3.ncrunchproject +++ /dev/null @@ -1,7 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.DesktopRuntime.netcoreapp2.0.v3.ncrunchproject b/.ncrunch/Avalonia.DesktopRuntime.netcoreapp2.0.v3.ncrunchproject deleted file mode 100644 index e9d39b0c74..0000000000 --- a/.ncrunch/Avalonia.DesktopRuntime.netcoreapp2.0.v3.ncrunchproject +++ /dev/null @@ -1,7 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.DesktopRuntime.v3.ncrunchproject b/.ncrunch/Avalonia.DesktopRuntime.v3.ncrunchproject deleted file mode 100644 index 95a483b433..0000000000 --- a/.ncrunch/Avalonia.DesktopRuntime.v3.ncrunchproject +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Diagnostics.v3.ncrunchproject b/.ncrunch/Avalonia.Diagnostics.v3.ncrunchproject deleted file mode 100644 index e12537d535..0000000000 --- a/.ncrunch/Avalonia.Diagnostics.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Direct2D1.RenderTests.v3.ncrunchproject b/.ncrunch/Avalonia.Direct2D1.RenderTests.v3.ncrunchproject deleted file mode 100644 index 0846098ce5..0000000000 --- a/.ncrunch/Avalonia.Direct2D1.RenderTests.v3.ncrunchproject +++ /dev/null @@ -1,12 +0,0 @@ - - - - ..\TestFiles\Direct2D1\**.* - - 3000 - - MissingOrIgnoredProjectReference - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Direct2D1.UnitTests.v3.ncrunchproject b/.ncrunch/Avalonia.Direct2D1.UnitTests.v3.ncrunchproject deleted file mode 100644 index e12537d535..0000000000 --- a/.ncrunch/Avalonia.Direct2D1.UnitTests.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Direct2D1.v3.ncrunchproject b/.ncrunch/Avalonia.Direct2D1.v3.ncrunchproject deleted file mode 100644 index e12537d535..0000000000 --- a/.ncrunch/Avalonia.Direct2D1.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Gtk3.v3.ncrunchproject b/.ncrunch/Avalonia.Gtk3.v3.ncrunchproject deleted file mode 100644 index e9d39b0c74..0000000000 --- a/.ncrunch/Avalonia.Gtk3.v3.ncrunchproject +++ /dev/null @@ -1,7 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Input.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Input.UnitTests.net461.v3.ncrunchproject deleted file mode 100644 index 6800b4a3fe..0000000000 --- a/.ncrunch/Avalonia.Input.UnitTests.net461.v3.ncrunchproject +++ /dev/null @@ -1,5 +0,0 @@ - - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Input.UnitTests.net47.v3.ncrunchproject b/.ncrunch/Avalonia.Input.UnitTests.net47.v3.ncrunchproject deleted file mode 100644 index e9d39b0c74..0000000000 --- a/.ncrunch/Avalonia.Input.UnitTests.net47.v3.ncrunchproject +++ /dev/null @@ -1,7 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Input.UnitTests.netcoreapp2.0.v3.ncrunchproject b/.ncrunch/Avalonia.Input.UnitTests.netcoreapp2.0.v3.ncrunchproject deleted file mode 100644 index e9d39b0c74..0000000000 --- a/.ncrunch/Avalonia.Input.UnitTests.netcoreapp2.0.v3.ncrunchproject +++ /dev/null @@ -1,7 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Input.UnitTests.v3.ncrunchproject b/.ncrunch/Avalonia.Input.UnitTests.v3.ncrunchproject deleted file mode 100644 index 6800b4a3fe..0000000000 --- a/.ncrunch/Avalonia.Input.UnitTests.v3.ncrunchproject +++ /dev/null @@ -1,5 +0,0 @@ - - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Input.v3.ncrunchproject b/.ncrunch/Avalonia.Input.v3.ncrunchproject deleted file mode 100644 index e12537d535..0000000000 --- a/.ncrunch/Avalonia.Input.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Interactivity.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Interactivity.UnitTests.net461.v3.ncrunchproject deleted file mode 100644 index 6800b4a3fe..0000000000 --- a/.ncrunch/Avalonia.Interactivity.UnitTests.net461.v3.ncrunchproject +++ /dev/null @@ -1,5 +0,0 @@ - - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Interactivity.UnitTests.netcoreapp1.1.v3.ncrunchproject b/.ncrunch/Avalonia.Interactivity.UnitTests.netcoreapp1.1.v3.ncrunchproject deleted file mode 100644 index 6800b4a3fe..0000000000 --- a/.ncrunch/Avalonia.Interactivity.UnitTests.netcoreapp1.1.v3.ncrunchproject +++ /dev/null @@ -1,5 +0,0 @@ - - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Interactivity.UnitTests.v3.ncrunchproject b/.ncrunch/Avalonia.Interactivity.UnitTests.v3.ncrunchproject deleted file mode 100644 index e12537d535..0000000000 --- a/.ncrunch/Avalonia.Interactivity.UnitTests.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Interactivity.v3.ncrunchproject b/.ncrunch/Avalonia.Interactivity.v3.ncrunchproject deleted file mode 100644 index e12537d535..0000000000 --- a/.ncrunch/Avalonia.Interactivity.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Layout.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Layout.UnitTests.net461.v3.ncrunchproject deleted file mode 100644 index 6800b4a3fe..0000000000 --- a/.ncrunch/Avalonia.Layout.UnitTests.net461.v3.ncrunchproject +++ /dev/null @@ -1,5 +0,0 @@ - - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Layout.UnitTests.v3.ncrunchproject b/.ncrunch/Avalonia.Layout.UnitTests.v3.ncrunchproject deleted file mode 100644 index e12537d535..0000000000 --- a/.ncrunch/Avalonia.Layout.UnitTests.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Layout.v3.ncrunchproject b/.ncrunch/Avalonia.Layout.v3.ncrunchproject deleted file mode 100644 index e12537d535..0000000000 --- a/.ncrunch/Avalonia.Layout.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.LeakTests.v3.ncrunchproject b/.ncrunch/Avalonia.LeakTests.v3.ncrunchproject deleted file mode 100644 index e12537d535..0000000000 --- a/.ncrunch/Avalonia.LeakTests.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.LinuxFramebuffer.v3.ncrunchproject b/.ncrunch/Avalonia.LinuxFramebuffer.v3.ncrunchproject deleted file mode 100644 index e12537d535..0000000000 --- a/.ncrunch/Avalonia.LinuxFramebuffer.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Logging.Serilog.v3.ncrunchproject b/.ncrunch/Avalonia.Logging.Serilog.v3.ncrunchproject deleted file mode 100644 index e12537d535..0000000000 --- a/.ncrunch/Avalonia.Logging.Serilog.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Markup.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Markup.UnitTests.net461.v3.ncrunchproject deleted file mode 100644 index 6800b4a3fe..0000000000 --- a/.ncrunch/Avalonia.Markup.UnitTests.net461.v3.ncrunchproject +++ /dev/null @@ -1,5 +0,0 @@ - - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Markup.UnitTests.net47.v3.ncrunchproject b/.ncrunch/Avalonia.Markup.UnitTests.net47.v3.ncrunchproject deleted file mode 100644 index e9d39b0c74..0000000000 --- a/.ncrunch/Avalonia.Markup.UnitTests.net47.v3.ncrunchproject +++ /dev/null @@ -1,7 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Markup.UnitTests.netcoreapp1.1.v3.ncrunchproject b/.ncrunch/Avalonia.Markup.UnitTests.netcoreapp1.1.v3.ncrunchproject deleted file mode 100644 index 15d9efad87..0000000000 --- a/.ncrunch/Avalonia.Markup.UnitTests.netcoreapp1.1.v3.ncrunchproject +++ /dev/null @@ -1,9 +0,0 @@ - - - - - Avalonia.Markup.UnitTests.Data.Plugins.DataAnnotationsValidationPluginTests.Produces_Aggregate_BindingNotificationsx - - - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Markup.UnitTests.netcoreapp2.0.v3.ncrunchproject b/.ncrunch/Avalonia.Markup.UnitTests.netcoreapp2.0.v3.ncrunchproject deleted file mode 100644 index e9d39b0c74..0000000000 --- a/.ncrunch/Avalonia.Markup.UnitTests.netcoreapp2.0.v3.ncrunchproject +++ /dev/null @@ -1,7 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Markup.UnitTests.v3.ncrunchproject b/.ncrunch/Avalonia.Markup.UnitTests.v3.ncrunchproject deleted file mode 100644 index 6800b4a3fe..0000000000 --- a/.ncrunch/Avalonia.Markup.UnitTests.v3.ncrunchproject +++ /dev/null @@ -1,5 +0,0 @@ - - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Markup.Xaml.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Markup.Xaml.UnitTests.net461.v3.ncrunchproject deleted file mode 100644 index 6800b4a3fe..0000000000 --- a/.ncrunch/Avalonia.Markup.Xaml.UnitTests.net461.v3.ncrunchproject +++ /dev/null @@ -1,5 +0,0 @@ - - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Markup.Xaml.UnitTests.net47.v3.ncrunchproject b/.ncrunch/Avalonia.Markup.Xaml.UnitTests.net47.v3.ncrunchproject deleted file mode 100644 index e9d39b0c74..0000000000 --- a/.ncrunch/Avalonia.Markup.Xaml.UnitTests.net47.v3.ncrunchproject +++ /dev/null @@ -1,7 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Markup.Xaml.UnitTests.netcoreapp2.0.v3.ncrunchproject b/.ncrunch/Avalonia.Markup.Xaml.UnitTests.netcoreapp2.0.v3.ncrunchproject deleted file mode 100644 index e9d39b0c74..0000000000 --- a/.ncrunch/Avalonia.Markup.Xaml.UnitTests.netcoreapp2.0.v3.ncrunchproject +++ /dev/null @@ -1,7 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Markup.Xaml.UnitTests.v3.ncrunchproject b/.ncrunch/Avalonia.Markup.Xaml.UnitTests.v3.ncrunchproject deleted file mode 100644 index 6800b4a3fe..0000000000 --- a/.ncrunch/Avalonia.Markup.Xaml.UnitTests.v3.ncrunchproject +++ /dev/null @@ -1,5 +0,0 @@ - - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Markup.Xaml.v3.ncrunchproject b/.ncrunch/Avalonia.Markup.Xaml.v3.ncrunchproject deleted file mode 100644 index e12537d535..0000000000 --- a/.ncrunch/Avalonia.Markup.Xaml.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Markup.v3.ncrunchproject b/.ncrunch/Avalonia.Markup.v3.ncrunchproject deleted file mode 100644 index e12537d535..0000000000 --- a/.ncrunch/Avalonia.Markup.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.MicroCom.v3.ncrunchproject b/.ncrunch/Avalonia.MicroCom.v3.ncrunchproject deleted file mode 100644 index 95a483b433..0000000000 --- a/.ncrunch/Avalonia.MicroCom.v3.ncrunchproject +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Native.net6.0.v3.ncrunchproject b/.ncrunch/Avalonia.Native.net6.0.v3.ncrunchproject new file mode 100644 index 0000000000..e8de009693 --- /dev/null +++ b/.ncrunch/Avalonia.Native.net6.0.v3.ncrunchproject @@ -0,0 +1,6 @@ + + + True + + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Desktop.v3.ncrunchproject b/.ncrunch/Avalonia.Native.netstandard2.0.v3.ncrunchproject similarity index 100% rename from .ncrunch/Avalonia.Desktop.v3.ncrunchproject rename to .ncrunch/Avalonia.Native.netstandard2.0.v3.ncrunchproject diff --git a/.ncrunch/Avalonia.OpenGL.v3.ncrunchproject b/.ncrunch/Avalonia.OpenGL.v3.ncrunchproject deleted file mode 100644 index e9d39b0c74..0000000000 --- a/.ncrunch/Avalonia.OpenGL.v3.ncrunchproject +++ /dev/null @@ -1,7 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.ReactiveUI.UnitTests.v3.ncrunchproject b/.ncrunch/Avalonia.ReactiveUI.UnitTests.v3.ncrunchproject deleted file mode 100644 index e9d39b0c74..0000000000 --- a/.ncrunch/Avalonia.ReactiveUI.UnitTests.v3.ncrunchproject +++ /dev/null @@ -1,7 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.ReactiveUI.v3.ncrunchproject b/.ncrunch/Avalonia.ReactiveUI.v3.ncrunchproject deleted file mode 100644 index e12537d535..0000000000 --- a/.ncrunch/Avalonia.ReactiveUI.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.RenderTests.v3.ncrunchproject b/.ncrunch/Avalonia.RenderTests.v3.ncrunchproject deleted file mode 100644 index 95a483b433..0000000000 --- a/.ncrunch/Avalonia.RenderTests.v3.ncrunchproject +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Skia.Android.v3.ncrunchproject b/.ncrunch/Avalonia.Skia.Android.v3.ncrunchproject deleted file mode 100644 index a4953e14f9..0000000000 --- a/.ncrunch/Avalonia.Skia.Android.v3.ncrunchproject +++ /dev/null @@ -1,6 +0,0 @@ - - - True - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Skia.Desktop.v3.ncrunchproject b/.ncrunch/Avalonia.Skia.Desktop.v3.ncrunchproject deleted file mode 100644 index ae3801f32b..0000000000 --- a/.ncrunch/Avalonia.Skia.Desktop.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - True - False - True - x86 - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Skia.RenderTests.v3.ncrunchproject b/.ncrunch/Avalonia.Skia.RenderTests.v3.ncrunchproject deleted file mode 100644 index 2966be8f73..0000000000 --- a/.ncrunch/Avalonia.Skia.RenderTests.v3.ncrunchproject +++ /dev/null @@ -1,12 +0,0 @@ - - - - ..\TestFiles\Skia\**.* - - 3000 - - MissingOrIgnoredProjectReference - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Skia.UnitTests.v3.ncrunchproject b/.ncrunch/Avalonia.Skia.UnitTests.v3.ncrunchproject deleted file mode 100644 index e9d39b0c74..0000000000 --- a/.ncrunch/Avalonia.Skia.UnitTests.v3.ncrunchproject +++ /dev/null @@ -1,7 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Skia.iOS.v3.ncrunchproject b/.ncrunch/Avalonia.Skia.iOS.v3.ncrunchproject deleted file mode 100644 index a4953e14f9..0000000000 --- a/.ncrunch/Avalonia.Skia.iOS.v3.ncrunchproject +++ /dev/null @@ -1,6 +0,0 @@ - - - True - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Skia.v3.ncrunchproject b/.ncrunch/Avalonia.Skia.v3.ncrunchproject deleted file mode 100644 index e9d39b0c74..0000000000 --- a/.ncrunch/Avalonia.Skia.v3.ncrunchproject +++ /dev/null @@ -1,7 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Styling.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Styling.UnitTests.net461.v3.ncrunchproject deleted file mode 100644 index 6800b4a3fe..0000000000 --- a/.ncrunch/Avalonia.Styling.UnitTests.net461.v3.ncrunchproject +++ /dev/null @@ -1,5 +0,0 @@ - - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Styling.UnitTests.net47.v3.ncrunchproject b/.ncrunch/Avalonia.Styling.UnitTests.net47.v3.ncrunchproject deleted file mode 100644 index e9d39b0c74..0000000000 --- a/.ncrunch/Avalonia.Styling.UnitTests.net47.v3.ncrunchproject +++ /dev/null @@ -1,7 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Styling.UnitTests.netcoreapp2.0.v3.ncrunchproject b/.ncrunch/Avalonia.Styling.UnitTests.netcoreapp2.0.v3.ncrunchproject deleted file mode 100644 index e9d39b0c74..0000000000 --- a/.ncrunch/Avalonia.Styling.UnitTests.netcoreapp2.0.v3.ncrunchproject +++ /dev/null @@ -1,7 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Styling.UnitTests.v3.ncrunchproject b/.ncrunch/Avalonia.Styling.UnitTests.v3.ncrunchproject deleted file mode 100644 index 6800b4a3fe..0000000000 --- a/.ncrunch/Avalonia.Styling.UnitTests.v3.ncrunchproject +++ /dev/null @@ -1,5 +0,0 @@ - - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Styling.v3.ncrunchproject b/.ncrunch/Avalonia.Styling.v3.ncrunchproject deleted file mode 100644 index e12537d535..0000000000 --- a/.ncrunch/Avalonia.Styling.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Themes.Default.v3.ncrunchproject b/.ncrunch/Avalonia.Themes.Default.v3.ncrunchproject deleted file mode 100644 index e12537d535..0000000000 --- a/.ncrunch/Avalonia.Themes.Default.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.UnitTests.net461.v3.ncrunchproject deleted file mode 100644 index 6800b4a3fe..0000000000 --- a/.ncrunch/Avalonia.UnitTests.net461.v3.ncrunchproject +++ /dev/null @@ -1,5 +0,0 @@ - - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.UnitTests.v3.ncrunchproject b/.ncrunch/Avalonia.UnitTests.v3.ncrunchproject deleted file mode 100644 index e12537d535..0000000000 --- a/.ncrunch/Avalonia.UnitTests.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Visuals.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Visuals.UnitTests.net461.v3.ncrunchproject deleted file mode 100644 index 6800b4a3fe..0000000000 --- a/.ncrunch/Avalonia.Visuals.UnitTests.net461.v3.ncrunchproject +++ /dev/null @@ -1,5 +0,0 @@ - - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Visuals.UnitTests.net47.v3.ncrunchproject b/.ncrunch/Avalonia.Visuals.UnitTests.net47.v3.ncrunchproject deleted file mode 100644 index e9d39b0c74..0000000000 --- a/.ncrunch/Avalonia.Visuals.UnitTests.net47.v3.ncrunchproject +++ /dev/null @@ -1,7 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Visuals.UnitTests.netcoreapp2.0.v3.ncrunchproject b/.ncrunch/Avalonia.Visuals.UnitTests.netcoreapp2.0.v3.ncrunchproject deleted file mode 100644 index e9d39b0c74..0000000000 --- a/.ncrunch/Avalonia.Visuals.UnitTests.netcoreapp2.0.v3.ncrunchproject +++ /dev/null @@ -1,7 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Visuals.UnitTests.v3.ncrunchproject b/.ncrunch/Avalonia.Visuals.UnitTests.v3.ncrunchproject deleted file mode 100644 index ee655751a0..0000000000 --- a/.ncrunch/Avalonia.Visuals.UnitTests.v3.ncrunchproject +++ /dev/null @@ -1,9 +0,0 @@ - - - - AbnormalReferenceResolution - LongTestTimesWithoutParallelExecution - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Visuals.v3.ncrunchproject b/.ncrunch/Avalonia.Visuals.v3.ncrunchproject deleted file mode 100644 index e12537d535..0000000000 --- a/.ncrunch/Avalonia.Visuals.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Win32.Interop.v3.ncrunchproject b/.ncrunch/Avalonia.Win32.Interop.v3.ncrunchproject deleted file mode 100644 index e12537d535..0000000000 --- a/.ncrunch/Avalonia.Win32.Interop.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Win32.v3.ncrunchproject b/.ncrunch/Avalonia.Win32.net6.0.v3.ncrunchproject similarity index 50% rename from .ncrunch/Avalonia.Win32.v3.ncrunchproject rename to .ncrunch/Avalonia.Win32.net6.0.v3.ncrunchproject index bc2ee91007..28b692bb51 100644 --- a/.ncrunch/Avalonia.Win32.v3.ncrunchproject +++ b/.ncrunch/Avalonia.Win32.net6.0.v3.ncrunchproject @@ -3,9 +3,5 @@ ..\..\tools\MicroComGenerator\bin\Debug\net6.0\**.* - - MissingOrIgnoredProjectReference - - True \ No newline at end of file diff --git a/.ncrunch/Avalonia.Win32.netstandard2.0.v3.ncrunchproject b/.ncrunch/Avalonia.Win32.netstandard2.0.v3.ncrunchproject new file mode 100644 index 0000000000..28b692bb51 --- /dev/null +++ b/.ncrunch/Avalonia.Win32.netstandard2.0.v3.ncrunchproject @@ -0,0 +1,7 @@ + + + + ..\..\tools\MicroComGenerator\bin\Debug\net6.0\**.* + + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.X11.v3.ncrunchproject b/.ncrunch/Avalonia.X11.v3.ncrunchproject deleted file mode 100644 index e9d39b0c74..0000000000 --- a/.ncrunch/Avalonia.X11.v3.ncrunchproject +++ /dev/null @@ -1,7 +0,0 @@ - - - - MissingOrIgnoredProjectReference - - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.net461.v3.ncrunchproject b/.ncrunch/Avalonia.net461.v3.ncrunchproject index 319cd523ce..4fdcd169ae 100644 --- a/.ncrunch/Avalonia.net461.v3.ncrunchproject +++ b/.ncrunch/Avalonia.net461.v3.ncrunchproject @@ -1,5 +1,7 @@  - True + + DerivedFilesIncludedInWorkspace + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Animation.UnitTests.net47.v3.ncrunchproject b/.ncrunch/Avalonia.net6.0.v3.ncrunchproject similarity index 59% rename from .ncrunch/Avalonia.Animation.UnitTests.net47.v3.ncrunchproject rename to .ncrunch/Avalonia.net6.0.v3.ncrunchproject index e9d39b0c74..4fdcd169ae 100644 --- a/.ncrunch/Avalonia.Animation.UnitTests.net47.v3.ncrunchproject +++ b/.ncrunch/Avalonia.net6.0.v3.ncrunchproject @@ -1,7 +1,7 @@  - MissingOrIgnoredProjectReference + DerivedFilesIncludedInWorkspace \ No newline at end of file diff --git a/.ncrunch/Avalonia.netcoreapp2.0.v3.ncrunchproject b/.ncrunch/Avalonia.netcoreapp2.0.v3.ncrunchproject index 3f3c53a7fd..4fdcd169ae 100644 --- a/.ncrunch/Avalonia.netcoreapp2.0.v3.ncrunchproject +++ b/.ncrunch/Avalonia.netcoreapp2.0.v3.ncrunchproject @@ -2,7 +2,6 @@ DerivedFilesIncludedInWorkspace - MissingOrIgnoredProjectReference \ No newline at end of file diff --git a/.ncrunch/Avalonia.netstandard2.0.v3.ncrunchproject b/.ncrunch/Avalonia.netstandard2.0.v3.ncrunchproject index 3f3c53a7fd..4fdcd169ae 100644 --- a/.ncrunch/Avalonia.netstandard2.0.v3.ncrunchproject +++ b/.ncrunch/Avalonia.netstandard2.0.v3.ncrunchproject @@ -2,7 +2,6 @@ DerivedFilesIncludedInWorkspace - MissingOrIgnoredProjectReference \ No newline at end of file diff --git a/.ncrunch/BindingDemo.net461.v3.ncrunchproject b/.ncrunch/BindingDemo.net461.v3.ncrunchproject deleted file mode 100644 index 319cd523ce..0000000000 --- a/.ncrunch/BindingDemo.net461.v3.ncrunchproject +++ /dev/null @@ -1,5 +0,0 @@ - - - True - - \ No newline at end of file diff --git a/.ncrunch/BindingDemo.netcoreapp2.0.v3.ncrunchproject b/.ncrunch/BindingDemo.netcoreapp2.0.v3.ncrunchproject deleted file mode 100644 index 319cd523ce..0000000000 --- a/.ncrunch/BindingDemo.netcoreapp2.0.v3.ncrunchproject +++ /dev/null @@ -1,5 +0,0 @@ - - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.Native.v3.ncrunchproject b/.ncrunch/ControlCatalog.Web.v3.ncrunchproject similarity index 100% rename from .ncrunch/Avalonia.Native.v3.ncrunchproject rename to .ncrunch/ControlCatalog.Web.v3.ncrunchproject diff --git a/.ncrunch/ControlCatalog.v3.ncrunchproject b/.ncrunch/ControlCatalog.v3.ncrunchproject index a4953e14f9..319cd523ce 100644 --- a/.ncrunch/ControlCatalog.v3.ncrunchproject +++ b/.ncrunch/ControlCatalog.v3.ncrunchproject @@ -1,6 +1,5 @@  True - True \ No newline at end of file diff --git a/.ncrunch/Avalonia.Skia.Desktop.NetStandard.v3.ncrunchproject b/.ncrunch/ControlSamples.v3.ncrunchproject similarity index 100% rename from .ncrunch/Avalonia.Skia.Desktop.NetStandard.v3.ncrunchproject rename to .ncrunch/ControlSamples.v3.ncrunchproject diff --git a/.ncrunch/Direct3DInteropSample.v3.ncrunchproject b/.ncrunch/Direct3DInteropSample.v3.ncrunchproject index 21fcdee3a6..319cd523ce 100644 --- a/.ncrunch/Direct3DInteropSample.v3.ncrunchproject +++ b/.ncrunch/Direct3DInteropSample.v3.ncrunchproject @@ -1,9 +1,5 @@  - - MissingOrIgnoredProjectReference - True - True \ No newline at end of file diff --git a/.ncrunch/GtkInteropDemo.v3.ncrunchproject b/.ncrunch/GtkInteropDemo.v3.ncrunchproject deleted file mode 100644 index 319cd523ce..0000000000 --- a/.ncrunch/GtkInteropDemo.v3.ncrunchproject +++ /dev/null @@ -1,5 +0,0 @@ - - - True - - \ No newline at end of file diff --git a/.ncrunch/Avalonia.iOSTestApplication.v3.ncrunchproject b/.ncrunch/MiniMvvm.v3.ncrunchproject similarity index 100% rename from .ncrunch/Avalonia.iOSTestApplication.v3.ncrunchproject rename to .ncrunch/MiniMvvm.v3.ncrunchproject diff --git a/.ncrunch/PlatformSanityChecks.v3.ncrunchproject b/.ncrunch/PlatformSanityChecks.v3.ncrunchproject index e9d39b0c74..319cd523ce 100644 --- a/.ncrunch/PlatformSanityChecks.v3.ncrunchproject +++ b/.ncrunch/PlatformSanityChecks.v3.ncrunchproject @@ -1,7 +1,5 @@  - - MissingOrIgnoredProjectReference - + True \ No newline at end of file diff --git a/.ncrunch/PlatformSupport.v3.ncrunchproject b/.ncrunch/PlatformSupport.v3.ncrunchproject deleted file mode 100644 index 95a483b433..0000000000 --- a/.ncrunch/PlatformSupport.v3.ncrunchproject +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/.ncrunch/RenderDemo.net461.v3.ncrunchproject b/.ncrunch/RenderDemo.net461.v3.ncrunchproject deleted file mode 100644 index 319cd523ce..0000000000 --- a/.ncrunch/RenderDemo.net461.v3.ncrunchproject +++ /dev/null @@ -1,5 +0,0 @@ - - - True - - \ No newline at end of file diff --git a/.ncrunch/RenderDemo.netcoreapp2.0.v3.ncrunchproject b/.ncrunch/RenderDemo.netcoreapp2.0.v3.ncrunchproject deleted file mode 100644 index 319cd523ce..0000000000 --- a/.ncrunch/RenderDemo.netcoreapp2.0.v3.ncrunchproject +++ /dev/null @@ -1,5 +0,0 @@ - - - True - - \ No newline at end of file diff --git a/.ncrunch/RenderHelpers.v3.ncrunchproject b/.ncrunch/RenderHelpers.v3.ncrunchproject deleted file mode 100644 index 95a483b433..0000000000 --- a/.ncrunch/RenderHelpers.v3.ncrunchproject +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/.ncrunch/VirtualizationDemo.net461.v3.ncrunchproject b/.ncrunch/VirtualizationDemo.net461.v3.ncrunchproject deleted file mode 100644 index 319cd523ce..0000000000 --- a/.ncrunch/VirtualizationDemo.net461.v3.ncrunchproject +++ /dev/null @@ -1,5 +0,0 @@ - - - True - - \ No newline at end of file diff --git a/.ncrunch/VirtualizationDemo.netcoreapp2.0.v3.ncrunchproject b/.ncrunch/VirtualizationDemo.netcoreapp2.0.v3.ncrunchproject deleted file mode 100644 index 319cd523ce..0000000000 --- a/.ncrunch/VirtualizationDemo.netcoreapp2.0.v3.ncrunchproject +++ /dev/null @@ -1,5 +0,0 @@ - - - True - - \ No newline at end of file diff --git a/samples/ControlCatalog.Android/Resources/Resource.Designer.cs b/samples/ControlCatalog.Android/Resources/Resource.Designer.cs index b1ca548e2c..dccc3f7159 100644 --- a/samples/ControlCatalog.Android/Resources/Resource.Designer.cs +++ b/samples/ControlCatalog.Android/Resources/Resource.Designer.cs @@ -14,7 +14,7 @@ namespace ControlCatalog.Android { - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "12.1.99.62")] public partial class Resource { From 76667dbd0d64701c29c53c579f536e9b073bc259 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 16 Dec 2021 22:59:54 +0100 Subject: [PATCH 28/90] Finished adding nullable annotations to Avalonia.Base. --- src/Avalonia.Base/AttachedProperty.cs | 2 - src/Avalonia.Base/Avalonia.Base.csproj | 1 + .../AvaloniaInternalException.cs | 2 - src/Avalonia.Base/AvaloniaLocator.cs | 19 +++++---- src/Avalonia.Base/AvaloniaObject.cs | 6 +-- src/Avalonia.Base/AvaloniaObjectExtensions.cs | 2 - src/Avalonia.Base/AvaloniaProperty.cs | 6 +-- .../AvaloniaPropertyChangedEventArgs.cs | 2 - .../AvaloniaPropertyChangedEventArgs`1.cs | 2 - src/Avalonia.Base/AvaloniaPropertyMetadata.cs | 2 - src/Avalonia.Base/AvaloniaPropertyRegistry.cs | 2 - src/Avalonia.Base/AvaloniaProperty`1.cs | 10 ++--- .../Collections/AvaloniaDictionary.cs | 18 ++++----- src/Avalonia.Base/Collections/AvaloniaList.cs | 6 +-- .../Collections/AvaloniaListConverter.cs | 13 ++++--- .../Collections/AvaloniaListExtensions.cs | 16 ++++---- .../Collections/IAvaloniaList.cs | 2 - .../Collections/IAvaloniaReadOnlyList.cs | 2 - .../NotifyCollectionChangedExtensions.cs | 8 ++-- .../Collections/Pooled/ClearMode.cs | 2 - .../Pooled/ICollectionDebugView.cs | 2 - .../Collections/Pooled/IReadOnlyPooledList.cs | 2 - .../Collections/Pooled/PooledList.cs | 17 ++++---- .../Collections/Pooled/PooledStack.cs | 4 +- .../Collections/Pooled/StackDebugView.cs | 2 - .../Collections/Pooled/ThrowHelper.cs | 2 - .../Data/AssignBindingAttribute.cs | 2 - .../Data/BindingChainException.cs | 2 - src/Avalonia.Base/Data/BindingMode.cs | 2 - src/Avalonia.Base/Data/BindingNotification.cs | 8 ++-- src/Avalonia.Base/Data/BindingOperations.cs | 2 - src/Avalonia.Base/Data/BindingPriority.cs | 2 - src/Avalonia.Base/Data/BindingValue.cs | 8 ++-- .../Data/Converters/BoolConverters.cs | 2 - .../Data/Converters/DefaultValueConverter.cs | 4 +- .../Converters/FuncMultiValueConverter.cs | 2 - .../Data/Converters/FuncValueConverter.cs | 2 - .../Data/Converters/IMultiValueConverter.cs | 2 - .../Data/Converters/IValueConverter.cs | 2 - .../Converters/MethodToCommandConverter.cs | 38 +++++++++--------- .../Data/Converters/ObjectConverters.cs | 2 - .../Data/Converters/StringConverters.cs | 2 - .../StringFormatMultiValueConverter.cs | 2 - .../Converters/StringFormatValueConverter.cs | 2 - .../Data/Core/AvaloniaPropertyAccessorNode.cs | 2 - .../Data/Core/BindingExpression.cs | 4 +- .../Data/Core/ClrPropertyInfo.cs | 6 +-- .../Data/Core/CommonPropertyNames.cs | 2 - .../Data/Core/EmptyExpressionNode.cs | 4 +- src/Avalonia.Base/Data/Core/ExpressionNode.cs | 2 - .../Data/Core/ExpressionObserver.cs | 2 - .../Data/Core/ExpressionParseException.cs | 2 - src/Avalonia.Base/Data/Core/IPropertyInfo.cs | 2 - src/Avalonia.Base/Data/Core/ITransformNode.cs | 4 +- .../Data/Core/IndexerExpressionNode.cs | 4 +- .../Data/Core/IndexerNodeBase.cs | 8 ++-- src/Avalonia.Base/Data/Core/LogicalNotNode.cs | 2 - .../Data/Core/MarkupBindingChainException.cs | 2 - .../Data/Core/Parsers/ExpressionTreeParser.cs | 2 - .../Parsers/ExpressionVisitorNodeBuilder.cs | 18 ++++----- .../Plugins/AvaloniaPropertyAccessorPlugin.cs | 9 ++--- .../DataAnnotationsValidationPlugin.cs | 16 ++++---- .../Data/Core/Plugins/DataValidationBase.cs | 2 - .../Core/Plugins/ExceptionValidationPlugin.cs | 4 +- .../Core/Plugins/IDataValidationPlugin.cs | 2 - .../Data/Core/Plugins/IPropertyAccessor.cs | 2 - .../Core/Plugins/IPropertyAccessorPlugin.cs | 2 - .../Data/Core/Plugins/IStreamPlugin.cs | 2 - .../Core/Plugins/IndeiValidationPlugin.cs | 4 +- .../Plugins/InpcPropertyAccessorPlugin.cs | 4 +- .../Data/Core/Plugins/MethodAccessorPlugin.cs | 2 - .../Core/Plugins/ObservableStreamPlugin.cs | 8 ++-- .../Data/Core/Plugins/PropertyAccessorBase.cs | 2 - .../Data/Core/Plugins/PropertyError.cs | 2 - .../Data/Core/Plugins/TaskStreamPlugin.cs | 4 +- .../Data/Core/PropertyAccessorNode.cs | 2 - src/Avalonia.Base/Data/Core/PropertyPath.cs | 2 - src/Avalonia.Base/Data/Core/SettableNode.cs | 2 - .../Data/Core/StreamBindingExtensions.cs | 2 - src/Avalonia.Base/Data/Core/StreamNode.cs | 2 - src/Avalonia.Base/Data/Core/TypeCastNode.cs | 2 - .../Data/DataValidationException.cs | 2 - src/Avalonia.Base/Data/IBinding.cs | 2 - src/Avalonia.Base/Data/IndexerBinding.cs | 4 +- src/Avalonia.Base/Data/IndexerDescriptor.cs | 2 - src/Avalonia.Base/Data/InstancedBinding.cs | 2 - src/Avalonia.Base/Data/Optional.cs | 2 - .../Diagnostics/AvaloniaPropertyValue.cs | 8 ++-- .../Diagnostics/IAvaloniaObjectDebug.cs | 2 - .../INotifyCollectionChangedDebug.cs | 2 +- src/Avalonia.Base/DirectProperty.cs | 2 - src/Avalonia.Base/DirectPropertyBase.cs | 4 +- src/Avalonia.Base/DirectPropertyMetadata`1.cs | 2 - src/Avalonia.Base/EnumExtensions.cs | 2 - src/Avalonia.Base/IAvaloniaObject.cs | 2 - src/Avalonia.Base/IDescription.cs | 2 - src/Avalonia.Base/IDirectPropertyAccessor.cs | 2 - src/Avalonia.Base/IDirectPropertyMetadata.cs | 2 - src/Avalonia.Base/IStyledPropertyAccessor.cs | 2 - src/Avalonia.Base/IStyledPropertyMetadata.cs | 2 - src/Avalonia.Base/Logging/ILogSink.cs | 12 +++--- src/Avalonia.Base/Logging/Logger.cs | 4 +- .../Logging/ParametrizedLogger.cs | 14 +++---- src/Avalonia.Base/Logging/TraceLogSink.cs | 26 ++++++------- src/Avalonia.Base/Metadata/TemplateContent.cs | 2 +- src/Avalonia.Base/Platform/IAssetLoader.cs | 8 ++-- src/Avalonia.Base/Platform/IPlatformHandle.cs | 2 +- .../Platform/Interop/Utf8Buffer.cs | 8 ++-- src/Avalonia.Base/Platform/PlatformHandle.cs | 4 +- .../PropertyStore/BindingEntry.cs | 2 - .../PropertyStore/ConstantValueEntry.cs | 2 - .../PropertyStore/IPriorityValueEntry.cs | 4 +- src/Avalonia.Base/PropertyStore/IValue.cs | 2 - src/Avalonia.Base/PropertyStore/IValueSink.cs | 2 - .../PropertyStore/LocalValueEntry.cs | 2 - .../PropertyStore/PriorityValue.cs | 2 - .../AvaloniaPropertyBindingObservable.cs | 15 ++++--- .../AvaloniaPropertyChangedObservable.cs | 2 +- .../Reactive/AvaloniaPropertyObservable.cs | 19 +++++---- .../Reactive/BindingValueAdapter.cs | 2 - .../Reactive/BindingValueExtensions.cs | 2 - .../Reactive/LightweightObservableBase.cs | 19 ++++----- src/Avalonia.Base/Reactive/ObservableEx.cs | 2 +- .../SingleSubscriberObservableBase.cs | 6 +-- .../Reactive/TypedBindingAdapter.cs | 2 - .../Reactive/UntypedBindingAdapter.cs | 2 - src/Avalonia.Base/StyledProperty.cs | 2 - src/Avalonia.Base/StyledPropertyBase.cs | 4 +- src/Avalonia.Base/StyledPropertyMetadata`1.cs | 2 - .../AvaloniaSynchronizationContext.cs | 4 +- src/Avalonia.Base/Threading/Dispatcher.cs | 20 ++++++---- .../Threading/DispatcherTimer.cs | 12 +++--- src/Avalonia.Base/Threading/JobRunner.cs | 14 +++---- .../Utilities/AvaloniaPropertyValueStore.cs | 2 - .../Utilities/AvaloniaResourcesIndex.cs | 14 +++---- src/Avalonia.Base/Utilities/DisposableLock.cs | 6 +-- .../Utilities/IAvaloniaPropertyVisitor.cs | 4 +- .../Utilities/IWeakSubscriber.cs | 2 +- .../ImmutableReadOnlyListStructEnumerator.cs | 15 +++---- .../Utilities/NonPumpingLockHelper.cs | 4 +- src/Avalonia.Base/Utilities/Ref.cs | 10 ++--- .../Utilities/SingleOrDictionary.cs | 8 ++-- src/Avalonia.Base/Utilities/SingleOrQueue.cs | 6 +-- .../Utilities/StringTokenizer.cs | 13 ++++--- .../SynchronousCompletionAsyncResult.cs | 12 +++--- src/Avalonia.Base/Utilities/TypeUtilities.cs | 26 ++++++------- .../Utilities/ValueSingleOrList.cs | 4 +- .../Utilities/WeakEventHandlerManager.cs | 37 +++++++++--------- src/Avalonia.Base/Utilities/WeakObservable.cs | 6 +-- .../Utilities/WeakSubscriptionManager.cs | 39 +++++++++---------- src/Avalonia.Base/Utilities/WeakTimer.cs | 3 +- src/Avalonia.Base/ValueStore.cs | 2 - .../Avalonia.Build.Tasks.csproj | 1 + src/Avalonia.Controls/Application.cs | 8 ++-- src/Avalonia.Controls/ContextMenu.cs | 2 +- src/Avalonia.Controls/Control.cs | 6 ++- src/Avalonia.Controls/Flyouts/FlyoutBase.cs | 2 +- src/Avalonia.Controls/MaskedTextBox.cs | 9 ++++- src/Avalonia.Controls/MenuItem.cs | 2 +- .../Platform/PlatformManager.cs | 3 -- .../Primitives/SelectingItemsControl.cs | 4 ++ src/Avalonia.Controls/SystemDialog.cs | 13 +++++-- .../Templates/DataTemplateExtensions.cs | 2 +- src/Avalonia.Controls/TrayIcon.cs | 7 +++- .../Utils/CollectionChangedEventManager.cs | 2 +- .../Diagnostics/ViewModels/MainViewModel.cs | 17 ++++---- src/Avalonia.Input/AccessKeyHandler.cs | 2 +- src/Avalonia.Input/FocusManager.cs | 2 +- src/Avalonia.Input/InputElement.cs | 2 +- src/Avalonia.Input/InputManager.cs | 2 +- src/Avalonia.Input/KeyboardDevice.cs | 6 +-- .../KeyboardNavigationHandler.cs | 4 +- src/Avalonia.Input/MouseDevice.cs | 5 ++- .../Media/CombinedGeometry.cs | 3 +- src/Avalonia.Visuals/Media/GeometryGroup.cs | 6 ++- src/Avalonia.X11/X11CursorFactory.cs | 8 +++- .../Markup/Parsers/Nodes/StringIndexerNode.cs | 8 ++-- .../Markup/Parsers/SelectorParser.cs | 2 +- src/Shared/PlatformSupport/AssetLoader.cs | 6 ++- .../Avalonia.Web.Blazor/AvaloniaView.razor.cs | 4 +- .../Avalonia.Web.Blazor/WindowingPlatform.cs | 10 +++-- .../Avalonia.Win32/FramebufferManager.cs | 4 +- 182 files changed, 420 insertions(+), 599 deletions(-) diff --git a/src/Avalonia.Base/AttachedProperty.cs b/src/Avalonia.Base/AttachedProperty.cs index 352104897a..12c569c452 100644 --- a/src/Avalonia.Base/AttachedProperty.cs +++ b/src/Avalonia.Base/AttachedProperty.cs @@ -1,7 +1,5 @@ using System; -#nullable enable - namespace Avalonia { /// diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj index e872756a58..07614f31db 100644 --- a/src/Avalonia.Base/Avalonia.Base.csproj +++ b/src/Avalonia.Base/Avalonia.Base.csproj @@ -11,4 +11,5 @@ + diff --git a/src/Avalonia.Base/AvaloniaInternalException.cs b/src/Avalonia.Base/AvaloniaInternalException.cs index 60e3040e01..a536073174 100644 --- a/src/Avalonia.Base/AvaloniaInternalException.cs +++ b/src/Avalonia.Base/AvaloniaInternalException.cs @@ -1,7 +1,5 @@ using System; -#nullable enable - namespace Avalonia { /// diff --git a/src/Avalonia.Base/AvaloniaLocator.cs b/src/Avalonia.Base/AvaloniaLocator.cs index 3163d15c1b..0510852ea7 100644 --- a/src/Avalonia.Base/AvaloniaLocator.cs +++ b/src/Avalonia.Base/AvaloniaLocator.cs @@ -7,10 +7,10 @@ namespace Avalonia { public class AvaloniaLocator : IAvaloniaDependencyResolver { - private readonly IAvaloniaDependencyResolver _parentScope; + private readonly IAvaloniaDependencyResolver? _parentScope; public static IAvaloniaDependencyResolver Current { get; set; } public static AvaloniaLocator CurrentMutable { get; set; } - private readonly Dictionary> _registry = new Dictionary>(); + private readonly Dictionary> _registry = new Dictionary>(); static AvaloniaLocator() { @@ -27,10 +27,9 @@ namespace Avalonia _parentScope = parentScope; } - public object GetService(Type t) + public object? GetService(Type t) { - Func rv; - return _registry.TryGetValue(t, out rv) ? rv() : _parentScope?.GetService(t); + return _registry.TryGetValue(t, out var rv) ? rv() : _parentScope?.GetService(t); } public class RegistrationHelper @@ -57,7 +56,7 @@ namespace Avalonia public AvaloniaLocator ToLazy(Func func) where TImlp : TService { var constructed = false; - TImlp instance = default; + TImlp? instance = default; _locator._registry[typeof (TService)] = () => { if (!constructed) @@ -73,7 +72,7 @@ namespace Avalonia public AvaloniaLocator ToSingleton() where TImpl : class, TService, new() { - TImpl instance = null; + TImpl? instance = null; return ToFunc(() => instance ?? (instance = new TImpl())); } @@ -117,14 +116,14 @@ namespace Avalonia public interface IAvaloniaDependencyResolver { - object GetService(Type t); + object? GetService(Type t); } public static class LocatorExtensions { - public static T GetService(this IAvaloniaDependencyResolver resolver) + public static T? GetService(this IAvaloniaDependencyResolver resolver) { - return (T) resolver.GetService(typeof (T)); + return (T?) resolver.GetService(typeof (T)); } } } diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index 9a1101e637..437339d3b2 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -7,8 +7,6 @@ using Avalonia.Logging; using Avalonia.PropertyStore; using Avalonia.Threading; -#nullable enable - namespace Avalonia { /// @@ -47,7 +45,7 @@ namespace Avalonia /// /// Raised when a value changes on this object. /// - event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged + event PropertyChangedEventHandler? INotifyPropertyChanged.PropertyChanged { add { _inpcChanged += value; } remove { _inpcChanged -= value; } @@ -857,7 +855,7 @@ namespace Avalonia private string GetDescription(object o) { var description = o as IDescription; - return description?.Description ?? o.ToString(); + return description?.Description ?? o.ToString() ?? o.GetType().Name; } /// diff --git a/src/Avalonia.Base/AvaloniaObjectExtensions.cs b/src/Avalonia.Base/AvaloniaObjectExtensions.cs index 16f0ca38a7..51237409bf 100644 --- a/src/Avalonia.Base/AvaloniaObjectExtensions.cs +++ b/src/Avalonia.Base/AvaloniaObjectExtensions.cs @@ -6,8 +6,6 @@ using System.Reactive.Subjects; using Avalonia.Data; using Avalonia.Reactive; -#nullable enable - namespace Avalonia { /// diff --git a/src/Avalonia.Base/AvaloniaProperty.cs b/src/Avalonia.Base/AvaloniaProperty.cs index 3e5cfb24a2..3d1e898f50 100644 --- a/src/Avalonia.Base/AvaloniaProperty.cs +++ b/src/Avalonia.Base/AvaloniaProperty.cs @@ -4,8 +4,6 @@ using Avalonia.Data; using Avalonia.Data.Core; using Avalonia.Utilities; -#nullable enable - namespace Avalonia { /// @@ -544,12 +542,12 @@ namespace Avalonia throw new ArgumentNullException(nameof(type)); } - if (_metadataCache.TryGetValue(type, out AvaloniaPropertyMetadata result)) + if (_metadataCache.TryGetValue(type, out var result)) { return result; } - Type currentType = type; + Type? currentType = type; while (currentType != null) { diff --git a/src/Avalonia.Base/AvaloniaPropertyChangedEventArgs.cs b/src/Avalonia.Base/AvaloniaPropertyChangedEventArgs.cs index 896d86e29d..445f35aad2 100644 --- a/src/Avalonia.Base/AvaloniaPropertyChangedEventArgs.cs +++ b/src/Avalonia.Base/AvaloniaPropertyChangedEventArgs.cs @@ -1,8 +1,6 @@ using System; using Avalonia.Data; -#nullable enable - namespace Avalonia { /// diff --git a/src/Avalonia.Base/AvaloniaPropertyChangedEventArgs`1.cs b/src/Avalonia.Base/AvaloniaPropertyChangedEventArgs`1.cs index 054bf93b3a..734e38596c 100644 --- a/src/Avalonia.Base/AvaloniaPropertyChangedEventArgs`1.cs +++ b/src/Avalonia.Base/AvaloniaPropertyChangedEventArgs`1.cs @@ -1,7 +1,5 @@ using Avalonia.Data; -#nullable enable - namespace Avalonia { /// diff --git a/src/Avalonia.Base/AvaloniaPropertyMetadata.cs b/src/Avalonia.Base/AvaloniaPropertyMetadata.cs index 4090aab558..2963567b14 100644 --- a/src/Avalonia.Base/AvaloniaPropertyMetadata.cs +++ b/src/Avalonia.Base/AvaloniaPropertyMetadata.cs @@ -1,7 +1,5 @@ using Avalonia.Data; -#nullable enable - namespace Avalonia { /// diff --git a/src/Avalonia.Base/AvaloniaPropertyRegistry.cs b/src/Avalonia.Base/AvaloniaPropertyRegistry.cs index 91db8c16c2..d86e723b38 100644 --- a/src/Avalonia.Base/AvaloniaPropertyRegistry.cs +++ b/src/Avalonia.Base/AvaloniaPropertyRegistry.cs @@ -2,8 +2,6 @@ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; -#nullable enable - namespace Avalonia { /// diff --git a/src/Avalonia.Base/AvaloniaProperty`1.cs b/src/Avalonia.Base/AvaloniaProperty`1.cs index 3485f195c5..3937ee5658 100644 --- a/src/Avalonia.Base/AvaloniaProperty`1.cs +++ b/src/Avalonia.Base/AvaloniaProperty`1.cs @@ -3,8 +3,6 @@ using System.Reactive.Subjects; using Avalonia.Data; using Avalonia.Utilities; -#nullable enable - namespace Avalonia { /// @@ -84,15 +82,15 @@ namespace Avalonia protected override IObservable GetChanged() => Changed; - protected BindingValue TryConvert(object? value) + protected BindingValue TryConvert(object? value) { if (value == UnsetValue) { - return BindingValue.Unset; + return BindingValue.Unset; } else if (value == BindingOperations.DoNothing) { - return BindingValue.DoNothing; + return BindingValue.DoNothing; } if (!TypeUtilities.TryConvertImplicit(PropertyType, value, out var converted)) @@ -102,7 +100,7 @@ namespace Avalonia Name, value, value?.GetType().FullName ?? "(null)")); - return BindingValue.BindingError(error); + return BindingValue.BindingError(error); } return converted; diff --git a/src/Avalonia.Base/Collections/AvaloniaDictionary.cs b/src/Avalonia.Base/Collections/AvaloniaDictionary.cs index e5ee1e1eef..0e027712e0 100644 --- a/src/Avalonia.Base/Collections/AvaloniaDictionary.cs +++ b/src/Avalonia.Base/Collections/AvaloniaDictionary.cs @@ -3,11 +3,10 @@ using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Linq; using Avalonia.Data.Core; -#nullable enable - namespace Avalonia.Collections { /// @@ -19,6 +18,7 @@ namespace Avalonia.Collections IDictionary, INotifyCollectionChanged, INotifyPropertyChanged + where TKey : notnull { private Dictionary _inner; @@ -76,8 +76,7 @@ namespace Avalonia.Collections set { - TValue old; - bool replace = _inner.TryGetValue(key, out old); + bool replace = _inner.TryGetValue(key, out var old); _inner[key] = value; if (replace) @@ -89,7 +88,7 @@ namespace Avalonia.Collections var e = new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Replace, new KeyValuePair(key, value), - new KeyValuePair(key, old)); + new KeyValuePair(key, old!)); CollectionChanged(this, e); } } @@ -100,7 +99,7 @@ namespace Avalonia.Collections } } - object IDictionary.this[object key] { get => ((IDictionary)_inner)[key]; set => ((IDictionary)_inner)[key] = value; } + object? IDictionary.this[object key] { get => ((IDictionary)_inner)[key]; set => ((IDictionary)_inner)[key] = value; } /// public void Add(TKey key, TValue value) @@ -145,7 +144,7 @@ namespace Avalonia.Collections /// public bool Remove(TKey key) { - if (_inner.TryGetValue(key, out TValue value)) + if (_inner.TryGetValue(key, out var value)) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count))); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]")); @@ -168,8 +167,7 @@ namespace Avalonia.Collections } /// - public bool TryGetValue(TKey key, out TValue value) => _inner.TryGetValue(key, out value); - + public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) => _inner.TryGetValue(key, out value); /// IEnumerator IEnumerable.GetEnumerator() => _inner.GetEnumerator(); @@ -195,7 +193,7 @@ namespace Avalonia.Collections } /// - void IDictionary.Add(object key, object value) => Add((TKey)key, (TValue)value); + void IDictionary.Add(object key, object? value) => Add((TKey)key, (TValue)value!); /// bool IDictionary.Contains(object key) => ((IDictionary) _inner).Contains(key); diff --git a/src/Avalonia.Base/Collections/AvaloniaList.cs b/src/Avalonia.Base/Collections/AvaloniaList.cs index 5b8ba108c7..6414328e90 100644 --- a/src/Avalonia.Base/Collections/AvaloniaList.cs +++ b/src/Avalonia.Base/Collections/AvaloniaList.cs @@ -6,8 +6,6 @@ using System.ComponentModel; using System.Linq; using Avalonia.Diagnostics; -#nullable enable - namespace Avalonia.Collections { /// @@ -95,7 +93,7 @@ namespace Avalonia.Collections /// /// Raised when a change is made to the collection's items. /// - public event NotifyCollectionChangedEventHandler CollectionChanged + public event NotifyCollectionChangedEventHandler? CollectionChanged { add => _collectionChanged += value; remove => _collectionChanged -= value; @@ -135,7 +133,7 @@ namespace Avalonia.Collections bool ICollection.IsSynchronized => false; /// - object? ICollection.SyncRoot => null; + object ICollection.SyncRoot => this; /// bool ICollection.IsReadOnly => false; diff --git a/src/Avalonia.Base/Collections/AvaloniaListConverter.cs b/src/Avalonia.Base/Collections/AvaloniaListConverter.cs index 444cdec144..b3fc0b01b6 100644 --- a/src/Avalonia.Base/Collections/AvaloniaListConverter.cs +++ b/src/Avalonia.Base/Collections/AvaloniaListConverter.cs @@ -3,8 +3,6 @@ using System.ComponentModel; using System.Globalization; using Avalonia.Utilities; -#nullable enable - namespace Avalonia.Collections { /// @@ -12,23 +10,26 @@ namespace Avalonia.Collections /// public class AvaloniaListConverter : TypeConverter { - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { return sourceType == typeof(string); } - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object? value) { + if (value is not string stringValue) + return null; + var result = new AvaloniaList(); // TODO: Use StringTokenizer here. - var values = ((string)value).Split(','); + var values = stringValue.Split(','); foreach (var s in values) { if (TypeUtilities.TryConvert(typeof(T), s, culture, out var v)) { - result.Add((T)v); + result.Add((T)v!); } else { diff --git a/src/Avalonia.Base/Collections/AvaloniaListExtensions.cs b/src/Avalonia.Base/Collections/AvaloniaListExtensions.cs index 5f3c3964f7..fb70729cf8 100644 --- a/src/Avalonia.Base/Collections/AvaloniaListExtensions.cs +++ b/src/Avalonia.Base/Collections/AvaloniaListExtensions.cs @@ -5,8 +5,6 @@ using System.Collections.Specialized; using System.ComponentModel; using System.Reactive.Disposables; -#nullable enable - namespace Avalonia.Collections { /// @@ -88,7 +86,7 @@ namespace Avalonia.Collections { for (var i = items.Count - 1; i >= 0; --i) { - removed(index + i, (T)items[i]); + removed(index + i, (T)items[i]!); } } @@ -97,22 +95,22 @@ namespace Avalonia.Collections switch (e.Action) { case NotifyCollectionChangedAction.Add: - Add(e.NewStartingIndex, e.NewItems); + Add(e.NewStartingIndex, e.NewItems!); break; case NotifyCollectionChangedAction.Move: case NotifyCollectionChangedAction.Replace: - Remove(e.OldStartingIndex, e.OldItems); + Remove(e.OldStartingIndex, e.OldItems!); int newIndex = e.NewStartingIndex; if(newIndex > e.OldStartingIndex) { - newIndex -= e.OldItems.Count; + newIndex -= e.OldItems!.Count; } - Add(newIndex, e.NewItems); + Add(newIndex, e.NewItems!); break; case NotifyCollectionChangedAction.Remove: - Remove(e.OldStartingIndex, e.OldItems); + Remove(e.OldStartingIndex, e.OldItems!); break; case NotifyCollectionChangedAction.Reset: @@ -166,7 +164,7 @@ namespace Avalonia.Collections /// A disposable used to terminate the subscription. public static IDisposable TrackItemPropertyChanged( this IAvaloniaReadOnlyList collection, - Action> callback) + Action> callback) { List tracked = new List(); diff --git a/src/Avalonia.Base/Collections/IAvaloniaList.cs b/src/Avalonia.Base/Collections/IAvaloniaList.cs index c28fb835e0..250d4faeb9 100644 --- a/src/Avalonia.Base/Collections/IAvaloniaList.cs +++ b/src/Avalonia.Base/Collections/IAvaloniaList.cs @@ -1,7 +1,5 @@ using System.Collections.Generic; -#nullable enable - namespace Avalonia.Collections { /// diff --git a/src/Avalonia.Base/Collections/IAvaloniaReadOnlyList.cs b/src/Avalonia.Base/Collections/IAvaloniaReadOnlyList.cs index 09622dceba..a6a5953827 100644 --- a/src/Avalonia.Base/Collections/IAvaloniaReadOnlyList.cs +++ b/src/Avalonia.Base/Collections/IAvaloniaReadOnlyList.cs @@ -2,8 +2,6 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; -#nullable enable - namespace Avalonia.Collections { /// diff --git a/src/Avalonia.Base/Collections/NotifyCollectionChangedExtensions.cs b/src/Avalonia.Base/Collections/NotifyCollectionChangedExtensions.cs index 7a8c2325e6..dcd32ddd76 100644 --- a/src/Avalonia.Base/Collections/NotifyCollectionChangedExtensions.cs +++ b/src/Avalonia.Base/Collections/NotifyCollectionChangedExtensions.cs @@ -4,8 +4,6 @@ using System.Reactive.Linq; using Avalonia.Reactive; using Avalonia.Utilities; -#nullable enable - namespace Avalonia.Collections { public static class NotifyCollectionChangedExtensions @@ -70,14 +68,14 @@ namespace Avalonia.Collections _sourceReference = source; } - public void OnEvent(object sender, NotifyCollectionChangedEventArgs e) + public void OnEvent(object? sender, NotifyCollectionChangedEventArgs e) { PublishNext(e); } protected override void Initialize() { - if (_sourceReference.TryGetTarget(out INotifyCollectionChanged instance)) + if (_sourceReference.TryGetTarget(out var instance)) { WeakSubscriptionManager.Subscribe( instance, @@ -88,7 +86,7 @@ namespace Avalonia.Collections protected override void Deinitialize() { - if (_sourceReference.TryGetTarget(out INotifyCollectionChanged instance)) + if (_sourceReference.TryGetTarget(out var instance)) { WeakSubscriptionManager.Unsubscribe( instance, diff --git a/src/Avalonia.Base/Collections/Pooled/ClearMode.cs b/src/Avalonia.Base/Collections/Pooled/ClearMode.cs index f13538dce3..d78ac8feab 100644 --- a/src/Avalonia.Base/Collections/Pooled/ClearMode.cs +++ b/src/Avalonia.Base/Collections/Pooled/ClearMode.cs @@ -1,8 +1,6 @@ // This source file is adapted from the Collections.Pooled. // (https://github.com/jtmueller/Collections.Pooled/tree/master/Collections.Pooled/) -#nullable enable - namespace Avalonia.Collections.Pooled { /// diff --git a/src/Avalonia.Base/Collections/Pooled/ICollectionDebugView.cs b/src/Avalonia.Base/Collections/Pooled/ICollectionDebugView.cs index 6f89cb7728..2b15388a13 100644 --- a/src/Avalonia.Base/Collections/Pooled/ICollectionDebugView.cs +++ b/src/Avalonia.Base/Collections/Pooled/ICollectionDebugView.cs @@ -6,8 +6,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; -#nullable enable - namespace Avalonia.Collections.Pooled { internal sealed class ICollectionDebugView diff --git a/src/Avalonia.Base/Collections/Pooled/IReadOnlyPooledList.cs b/src/Avalonia.Base/Collections/Pooled/IReadOnlyPooledList.cs index 7ac22d2baf..7a233a62ab 100644 --- a/src/Avalonia.Base/Collections/Pooled/IReadOnlyPooledList.cs +++ b/src/Avalonia.Base/Collections/Pooled/IReadOnlyPooledList.cs @@ -4,8 +4,6 @@ using System; using System.Collections.Generic; -#nullable enable - namespace Avalonia.Collections.Pooled { /// diff --git a/src/Avalonia.Base/Collections/Pooled/PooledList.cs b/src/Avalonia.Base/Collections/Pooled/PooledList.cs index 73cd602948..200a52fb0d 100644 --- a/src/Avalonia.Base/Collections/Pooled/PooledList.cs +++ b/src/Avalonia.Base/Collections/Pooled/PooledList.cs @@ -13,8 +13,6 @@ using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Threading; -#nullable enable - namespace Avalonia.Collections.Pooled { /// @@ -656,14 +654,15 @@ namespace Avalonia.Collections.Pooled // compatible array type. void ICollection.CopyTo(Array array, int arrayIndex) { - if ((array != null) && (array.Rank != 1)) + _ = array ?? throw new ArgumentNullException(nameof(array)); + + if (array.Rank != 1) { ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported); } try { - // Array.Copy will check for NULL. Array.Copy(_items, 0, array, arrayIndex, _size); } catch (ArrayTypeMismatchException) @@ -1336,7 +1335,7 @@ namespace Avalonia.Collections.Pooled _version++; } - public void Sort(Func comparison) + public void Sort(Func comparison) { if (comparison == null) { @@ -1442,7 +1441,7 @@ namespace Avalonia.Collections.Pooled _version++; } - void IDeserializationCallback.OnDeserialization(object sender) + void IDeserializationCallback.OnDeserialization(object? sender) { // We can't serialize array pools, so deserialized PooledLists will // have to use the shared pool, even if they were using a custom pool @@ -1522,14 +1521,14 @@ namespace Avalonia.Collections.Pooled private readonly struct Comparer : IComparer { - private readonly Func _comparison; + private readonly Func _comparison; - public Comparer(Func comparison) + public Comparer(Func comparison) { _comparison = comparison; } - public int Compare(T x, T y) => _comparison(x, y); + public int Compare(T? x, T? y) => _comparison(x, y); } } } diff --git a/src/Avalonia.Base/Collections/Pooled/PooledStack.cs b/src/Avalonia.Base/Collections/Pooled/PooledStack.cs index 8a03c6f5e4..f721cb1b62 100644 --- a/src/Avalonia.Base/Collections/Pooled/PooledStack.cs +++ b/src/Avalonia.Base/Collections/Pooled/PooledStack.cs @@ -20,8 +20,6 @@ using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Threading; -#nullable enable - namespace Avalonia.Collections.Pooled { /// @@ -613,7 +611,7 @@ namespace Avalonia.Collections.Pooled _version++; } - void IDeserializationCallback.OnDeserialization(object sender) + void IDeserializationCallback.OnDeserialization(object? sender) { // We can't serialize array pools, so deserialized PooledStacks will // have to use the shared pool, even if they were using a custom pool diff --git a/src/Avalonia.Base/Collections/Pooled/StackDebugView.cs b/src/Avalonia.Base/Collections/Pooled/StackDebugView.cs index b55ac110c6..b042388079 100644 --- a/src/Avalonia.Base/Collections/Pooled/StackDebugView.cs +++ b/src/Avalonia.Base/Collections/Pooled/StackDebugView.cs @@ -5,8 +5,6 @@ using System; using System.Diagnostics; -#nullable enable - namespace Avalonia.Collections.Pooled { internal sealed class StackDebugView diff --git a/src/Avalonia.Base/Collections/Pooled/ThrowHelper.cs b/src/Avalonia.Base/Collections/Pooled/ThrowHelper.cs index 3237286107..c0a60b695a 100644 --- a/src/Avalonia.Base/Collections/Pooled/ThrowHelper.cs +++ b/src/Avalonia.Base/Collections/Pooled/ThrowHelper.cs @@ -42,8 +42,6 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.Serialization; -#nullable enable - namespace Avalonia.Collections.Pooled { internal static class ThrowHelper diff --git a/src/Avalonia.Base/Data/AssignBindingAttribute.cs b/src/Avalonia.Base/Data/AssignBindingAttribute.cs index f88055a1e1..15bd32c7c2 100644 --- a/src/Avalonia.Base/Data/AssignBindingAttribute.cs +++ b/src/Avalonia.Base/Data/AssignBindingAttribute.cs @@ -1,7 +1,5 @@ using System; -#nullable enable - namespace Avalonia.Data { /// diff --git a/src/Avalonia.Base/Data/BindingChainException.cs b/src/Avalonia.Base/Data/BindingChainException.cs index 09acf459cc..2890680f9a 100644 --- a/src/Avalonia.Base/Data/BindingChainException.cs +++ b/src/Avalonia.Base/Data/BindingChainException.cs @@ -1,7 +1,5 @@ using System; -#nullable enable - namespace Avalonia.Data { /// diff --git a/src/Avalonia.Base/Data/BindingMode.cs b/src/Avalonia.Base/Data/BindingMode.cs index a57487fa7a..0d04c2eb10 100644 --- a/src/Avalonia.Base/Data/BindingMode.cs +++ b/src/Avalonia.Base/Data/BindingMode.cs @@ -1,5 +1,3 @@ -#nullable enable - namespace Avalonia.Data { /// diff --git a/src/Avalonia.Base/Data/BindingNotification.cs b/src/Avalonia.Base/Data/BindingNotification.cs index 867a7f637a..4b97b1a02c 100644 --- a/src/Avalonia.Base/Data/BindingNotification.cs +++ b/src/Avalonia.Base/Data/BindingNotification.cs @@ -1,7 +1,5 @@ using System; -#nullable enable - namespace Avalonia.Data { /// @@ -214,8 +212,10 @@ namespace Avalonia.Data /// The error type. public void AddError(Exception e, BindingErrorType type) { - Contract.Requires(e != null); - Contract.Requires(type != BindingErrorType.None); + _ = e ?? throw new ArgumentNullException(nameof(e)); + + if (type == BindingErrorType.None) + throw new ArgumentException("BindingErrorType may not be None", nameof(type)); Error = Error != null ? new AggregateException(Error, e) : e; diff --git a/src/Avalonia.Base/Data/BindingOperations.cs b/src/Avalonia.Base/Data/BindingOperations.cs index 05900de01d..5353e8175d 100644 --- a/src/Avalonia.Base/Data/BindingOperations.cs +++ b/src/Avalonia.Base/Data/BindingOperations.cs @@ -2,8 +2,6 @@ using System; using System.Reactive.Disposables; using System.Reactive.Linq; -#nullable enable - namespace Avalonia.Data { public static class BindingOperations diff --git a/src/Avalonia.Base/Data/BindingPriority.cs b/src/Avalonia.Base/Data/BindingPriority.cs index e4fb11cf06..ece64375f2 100644 --- a/src/Avalonia.Base/Data/BindingPriority.cs +++ b/src/Avalonia.Base/Data/BindingPriority.cs @@ -1,5 +1,3 @@ -#nullable enable - namespace Avalonia.Data { /// diff --git a/src/Avalonia.Base/Data/BindingValue.cs b/src/Avalonia.Base/Data/BindingValue.cs index 8e90bed8bc..55be611083 100644 --- a/src/Avalonia.Base/Data/BindingValue.cs +++ b/src/Avalonia.Base/Data/BindingValue.cs @@ -2,8 +2,6 @@ using System.Diagnostics; using Avalonia.Utilities; -#nullable enable - namespace Avalonia.Data { /// @@ -418,15 +416,15 @@ namespace Avalonia.Data { BindingValueType.DoNothing => BindingValue.DoNothing, BindingValueType.UnsetValue => BindingValue.Unset, - BindingValueType.Value => new BindingValue(TypeUtilities.ConvertImplicit(value.Value)), + BindingValueType.Value => new BindingValue(TypeUtilities.ConvertImplicit(value.Value!)), BindingValueType.BindingError => BindingValue.BindingError(value.Error!), BindingValueType.BindingErrorWithFallback => BindingValue.BindingError( value.Error!, - TypeUtilities.ConvertImplicit(value.Value)), + TypeUtilities.ConvertImplicit(value.Value!)), BindingValueType.DataValidationError => BindingValue.DataValidationError(value.Error!), BindingValueType.DataValidationErrorWithFallback => BindingValue.DataValidationError( value.Error!, - TypeUtilities.ConvertImplicit(value.Value)), + TypeUtilities.ConvertImplicit(value.Value!)), _ => throw new NotSupportedException("Invalid BindingValue type."), }; } diff --git a/src/Avalonia.Base/Data/Converters/BoolConverters.cs b/src/Avalonia.Base/Data/Converters/BoolConverters.cs index 6a45ab8593..3985c5e32f 100644 --- a/src/Avalonia.Base/Data/Converters/BoolConverters.cs +++ b/src/Avalonia.Base/Data/Converters/BoolConverters.cs @@ -1,7 +1,5 @@ using System.Linq; -#nullable enable - namespace Avalonia.Data.Converters { /// diff --git a/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs b/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs index f99ac1f81d..11d50afe93 100644 --- a/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs +++ b/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs @@ -3,8 +3,6 @@ using System.Globalization; using System.Windows.Input; using Avalonia.Utilities; -#nullable enable - namespace Avalonia.Data.Converters { /// @@ -38,7 +36,7 @@ namespace Avalonia.Data.Converters return new MethodToCommandConverter(d); } - if (TypeUtilities.TryConvert(targetType, value, culture, out object result)) + if (TypeUtilities.TryConvert(targetType, value, culture, out var result)) { return result; } diff --git a/src/Avalonia.Base/Data/Converters/FuncMultiValueConverter.cs b/src/Avalonia.Base/Data/Converters/FuncMultiValueConverter.cs index 85dc6fc4f4..2577cac743 100644 --- a/src/Avalonia.Base/Data/Converters/FuncMultiValueConverter.cs +++ b/src/Avalonia.Base/Data/Converters/FuncMultiValueConverter.cs @@ -3,8 +3,6 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; -#nullable enable - namespace Avalonia.Data.Converters { /// diff --git a/src/Avalonia.Base/Data/Converters/FuncValueConverter.cs b/src/Avalonia.Base/Data/Converters/FuncValueConverter.cs index fe71f1da1c..8a1f032593 100644 --- a/src/Avalonia.Base/Data/Converters/FuncValueConverter.cs +++ b/src/Avalonia.Base/Data/Converters/FuncValueConverter.cs @@ -2,8 +2,6 @@ using System; using System.Globalization; using Avalonia.Utilities; -#nullable enable - namespace Avalonia.Data.Converters { /// diff --git a/src/Avalonia.Base/Data/Converters/IMultiValueConverter.cs b/src/Avalonia.Base/Data/Converters/IMultiValueConverter.cs index e21746f571..d6e1c9f804 100644 --- a/src/Avalonia.Base/Data/Converters/IMultiValueConverter.cs +++ b/src/Avalonia.Base/Data/Converters/IMultiValueConverter.cs @@ -2,8 +2,6 @@ using System; using System.Collections.Generic; using System.Globalization; -#nullable enable - namespace Avalonia.Data.Converters { /// diff --git a/src/Avalonia.Base/Data/Converters/IValueConverter.cs b/src/Avalonia.Base/Data/Converters/IValueConverter.cs index c591dbda32..26c0e9582a 100644 --- a/src/Avalonia.Base/Data/Converters/IValueConverter.cs +++ b/src/Avalonia.Base/Data/Converters/IValueConverter.cs @@ -1,8 +1,6 @@ using System; using System.Globalization; -#nullable enable - namespace Avalonia.Data.Converters { /// diff --git a/src/Avalonia.Base/Data/Converters/MethodToCommandConverter.cs b/src/Avalonia.Base/Data/Converters/MethodToCommandConverter.cs index ee83776220..0672185a55 100644 --- a/src/Avalonia.Base/Data/Converters/MethodToCommandConverter.cs +++ b/src/Avalonia.Base/Data/Converters/MethodToCommandConverter.cs @@ -7,19 +7,17 @@ using System.Reflection; using System.Windows.Input; using Avalonia.Utilities; -#nullable enable - namespace Avalonia.Data.Converters { class MethodToCommandConverter : ICommand { - readonly static Func AlwaysEnabled = (_) => true; + readonly static Func AlwaysEnabled = (_) => true; readonly static MethodInfo tryConvert = typeof(TypeUtilities) - .GetMethod(nameof(TypeUtilities.TryConvert), BindingFlags.Public | BindingFlags.Static); + .GetMethod(nameof(TypeUtilities.TryConvert), BindingFlags.Public | BindingFlags.Static)!; readonly static PropertyInfo currentCulture = typeof(CultureInfo) - .GetProperty(nameof(CultureInfo.CurrentCulture), BindingFlags.Public | BindingFlags.Static); - readonly Func canExecute; - readonly Action execute; + .GetProperty(nameof(CultureInfo.CurrentCulture), BindingFlags.Public | BindingFlags.Static)!; + readonly Func canExecute; + readonly Action execute; readonly WeakPropertyChangedProxy? weakPropertyChanged; readonly PropertyChangedEventHandler? propertyChangedEventHandler; readonly string[]? dependencyProperties; @@ -40,7 +38,7 @@ namespace Avalonia.Data.Converters execute = CreateExecute(target, action.Method, parameterInfo); } - var canExecuteMethod = action.Method.DeclaringType.GetRuntimeMethods() + var canExecuteMethod = action.Method.DeclaringType?.GetRuntimeMethods() .FirstOrDefault(m => m.Name == canExecuteMethodName && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(object)); @@ -65,7 +63,7 @@ namespace Avalonia.Data.Converters } } - void OnPropertyChanged(object sender, PropertyChangedEventArgs args) + void OnPropertyChanged(object? sender, PropertyChangedEventArgs args) { if (string.IsNullOrWhiteSpace(args.PropertyName) || dependencyProperties?.Contains(args.PropertyName) == true) @@ -79,12 +77,12 @@ namespace Avalonia.Data.Converters public event EventHandler? CanExecuteChanged; #pragma warning restore 0067 - public bool CanExecute(object parameter) => canExecute(parameter); + public bool CanExecute(object? parameter) => canExecute(parameter); - public void Execute(object parameter) => execute(parameter); + public void Execute(object? parameter) => execute(parameter); - static Action CreateExecute(object target + static Action CreateExecute(object? target , System.Reflection.MethodInfo method) { @@ -100,11 +98,11 @@ namespace Avalonia.Data.Converters return Expression - .Lambda>(call, parameter) + .Lambda>(call, parameter) .Compile(); } - static Action CreateExecute(object target + static Action CreateExecute(object? target , System.Reflection.MethodInfo method , Type parameterType) { @@ -143,11 +141,11 @@ namespace Avalonia.Data.Converters } return Expression - .Lambda>(body, parameter) + .Lambda>(body, parameter) .Compile(); } - static Func CreateCanExecute(object target + static Func CreateCanExecute(object? target , System.Reflection.MethodInfo method) { var parameter = Expression.Parameter(typeof(object), "parameter"); @@ -159,12 +157,12 @@ namespace Avalonia.Data.Converters parameter ); return Expression - .Lambda>(call, parameter) + .Lambda>(call, parameter) .Compile(); } - private static Expression? ConvertTarget(object target, MethodInfo method) => - target is null ? null : Expression.Convert(Expression.Constant(target), method.DeclaringType); + private static Expression? ConvertTarget(object? target, MethodInfo method) => + target is null ? null : Expression.Convert(Expression.Constant(target), method.DeclaringType!); internal class WeakPropertyChangedProxy { @@ -199,7 +197,7 @@ namespace Avalonia.Data.Converters _listener.SetTarget(null); } - void OnPropertyChanged(object sender, PropertyChangedEventArgs e) + void OnPropertyChanged(object? sender, PropertyChangedEventArgs e) { if (_listener.TryGetTarget(out var handler) && handler != null) handler(sender, e); diff --git a/src/Avalonia.Base/Data/Converters/ObjectConverters.cs b/src/Avalonia.Base/Data/Converters/ObjectConverters.cs index 3794d0ca3b..6796a1793e 100644 --- a/src/Avalonia.Base/Data/Converters/ObjectConverters.cs +++ b/src/Avalonia.Base/Data/Converters/ObjectConverters.cs @@ -1,5 +1,3 @@ -#nullable enable - namespace Avalonia.Data.Converters { /// diff --git a/src/Avalonia.Base/Data/Converters/StringConverters.cs b/src/Avalonia.Base/Data/Converters/StringConverters.cs index a13cef349d..182eaf2e65 100644 --- a/src/Avalonia.Base/Data/Converters/StringConverters.cs +++ b/src/Avalonia.Base/Data/Converters/StringConverters.cs @@ -1,5 +1,3 @@ -#nullable enable - namespace Avalonia.Data.Converters { /// diff --git a/src/Avalonia.Base/Data/Converters/StringFormatMultiValueConverter.cs b/src/Avalonia.Base/Data/Converters/StringFormatMultiValueConverter.cs index d66a2d2cdf..606ac9e6fa 100644 --- a/src/Avalonia.Base/Data/Converters/StringFormatMultiValueConverter.cs +++ b/src/Avalonia.Base/Data/Converters/StringFormatMultiValueConverter.cs @@ -3,8 +3,6 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; -#nullable enable - namespace Avalonia.Data.Converters { /// diff --git a/src/Avalonia.Base/Data/Converters/StringFormatValueConverter.cs b/src/Avalonia.Base/Data/Converters/StringFormatValueConverter.cs index 1b3451370a..5307ea4b9a 100644 --- a/src/Avalonia.Base/Data/Converters/StringFormatValueConverter.cs +++ b/src/Avalonia.Base/Data/Converters/StringFormatValueConverter.cs @@ -1,8 +1,6 @@ using System; using System.Globalization; -#nullable enable - namespace Avalonia.Data.Converters { /// diff --git a/src/Avalonia.Base/Data/Core/AvaloniaPropertyAccessorNode.cs b/src/Avalonia.Base/Data/Core/AvaloniaPropertyAccessorNode.cs index ce60e12ae1..92ecdafa0c 100644 --- a/src/Avalonia.Base/Data/Core/AvaloniaPropertyAccessorNode.cs +++ b/src/Avalonia.Base/Data/Core/AvaloniaPropertyAccessorNode.cs @@ -2,8 +2,6 @@ using System.Reactive.Linq; using Avalonia.Reactive; -#nullable enable - namespace Avalonia.Data.Core { public class AvaloniaPropertyAccessorNode : SettableNode diff --git a/src/Avalonia.Base/Data/Core/BindingExpression.cs b/src/Avalonia.Base/Data/Core/BindingExpression.cs index 3f7a9722b7..ea903c1042 100644 --- a/src/Avalonia.Base/Data/Core/BindingExpression.cs +++ b/src/Avalonia.Base/Data/Core/BindingExpression.cs @@ -7,8 +7,6 @@ using Avalonia.Logging; using Avalonia.Reactive; using Avalonia.Utilities; -#nullable enable - namespace Avalonia.Data.Core { /// @@ -263,7 +261,7 @@ namespace Avalonia.Data.Core private BindingNotification ConvertFallback() { - object converted; + object? converted; if (_fallbackValue == AvaloniaProperty.UnsetValue) { diff --git a/src/Avalonia.Base/Data/Core/ClrPropertyInfo.cs b/src/Avalonia.Base/Data/Core/ClrPropertyInfo.cs index f52940bc80..24149c17e0 100644 --- a/src/Avalonia.Base/Data/Core/ClrPropertyInfo.cs +++ b/src/Avalonia.Base/Data/Core/ClrPropertyInfo.cs @@ -2,8 +2,6 @@ using System; using System.Linq.Expressions; using System.Reflection; -#nullable enable - namespace Avalonia.Data.Core { public class ClrPropertyInfo : IPropertyInfo @@ -49,7 +47,7 @@ namespace Avalonia.Data.Core var target = Expression.Parameter(typeof(object), "target"); var value = Expression.Parameter(typeof(object), "value"); return Expression.Lambda>( - Expression.Call(Expression.Convert(target, info.DeclaringType), info.SetMethod, + Expression.Call(Expression.Convert(target, info.DeclaringType!), info.SetMethod, Expression.Convert(value, info.SetMethod.GetParameters()[0].ParameterType)), target, value) .Compile(); @@ -61,7 +59,7 @@ namespace Avalonia.Data.Core return null; var target = Expression.Parameter(typeof(object), "target"); return Expression.Lambda>( - Expression.Convert(Expression.Call(Expression.Convert(target, info.DeclaringType), info.GetMethod), + Expression.Convert(Expression.Call(Expression.Convert(target, info.DeclaringType!), info.GetMethod), typeof(object))) .Compile(); } diff --git a/src/Avalonia.Base/Data/Core/CommonPropertyNames.cs b/src/Avalonia.Base/Data/Core/CommonPropertyNames.cs index 908beeca33..da6f407d81 100644 --- a/src/Avalonia.Base/Data/Core/CommonPropertyNames.cs +++ b/src/Avalonia.Base/Data/Core/CommonPropertyNames.cs @@ -1,5 +1,3 @@ -#nullable enable - namespace Avalonia.Data.Core { public static class CommonPropertyNames diff --git a/src/Avalonia.Base/Data/Core/EmptyExpressionNode.cs b/src/Avalonia.Base/Data/Core/EmptyExpressionNode.cs index 115f617270..4e142fbee9 100644 --- a/src/Avalonia.Base/Data/Core/EmptyExpressionNode.cs +++ b/src/Avalonia.Base/Data/Core/EmptyExpressionNode.cs @@ -1,6 +1,4 @@ -#nullable enable - -namespace Avalonia.Data.Core +namespace Avalonia.Data.Core { public class EmptyExpressionNode : ExpressionNode { diff --git a/src/Avalonia.Base/Data/Core/ExpressionNode.cs b/src/Avalonia.Base/Data/Core/ExpressionNode.cs index 25b0102891..54785f18e8 100644 --- a/src/Avalonia.Base/Data/Core/ExpressionNode.cs +++ b/src/Avalonia.Base/Data/Core/ExpressionNode.cs @@ -1,7 +1,5 @@ using System; -#nullable enable - namespace Avalonia.Data.Core { public abstract class ExpressionNode diff --git a/src/Avalonia.Base/Data/Core/ExpressionObserver.cs b/src/Avalonia.Base/Data/Core/ExpressionObserver.cs index 9e38c8bfba..e4affd97de 100644 --- a/src/Avalonia.Base/Data/Core/ExpressionObserver.cs +++ b/src/Avalonia.Base/Data/Core/ExpressionObserver.cs @@ -7,8 +7,6 @@ using Avalonia.Data.Core.Parsers; using Avalonia.Data.Core.Plugins; using Avalonia.Reactive; -#nullable enable - namespace Avalonia.Data.Core { /// diff --git a/src/Avalonia.Base/Data/Core/ExpressionParseException.cs b/src/Avalonia.Base/Data/Core/ExpressionParseException.cs index 8fd7066a7b..a50e79f9cd 100644 --- a/src/Avalonia.Base/Data/Core/ExpressionParseException.cs +++ b/src/Avalonia.Base/Data/Core/ExpressionParseException.cs @@ -1,7 +1,5 @@ using System; -#nullable enable - namespace Avalonia.Data.Core { /// diff --git a/src/Avalonia.Base/Data/Core/IPropertyInfo.cs b/src/Avalonia.Base/Data/Core/IPropertyInfo.cs index 9544565fcb..4d80feb4ba 100644 --- a/src/Avalonia.Base/Data/Core/IPropertyInfo.cs +++ b/src/Avalonia.Base/Data/Core/IPropertyInfo.cs @@ -1,7 +1,5 @@ using System; -#nullable enable - namespace Avalonia.Data.Core { public interface IPropertyInfo diff --git a/src/Avalonia.Base/Data/Core/ITransformNode.cs b/src/Avalonia.Base/Data/Core/ITransformNode.cs index 49c1dea7b9..645d637e44 100644 --- a/src/Avalonia.Base/Data/Core/ITransformNode.cs +++ b/src/Avalonia.Base/Data/Core/ITransformNode.cs @@ -1,6 +1,4 @@ -#nullable enable - -namespace Avalonia.Data.Core +namespace Avalonia.Data.Core { interface ITransformNode { diff --git a/src/Avalonia.Base/Data/Core/IndexerExpressionNode.cs b/src/Avalonia.Base/Data/Core/IndexerExpressionNode.cs index 22a58a245b..7a24f9cb77 100644 --- a/src/Avalonia.Base/Data/Core/IndexerExpressionNode.cs +++ b/src/Avalonia.Base/Data/Core/IndexerExpressionNode.cs @@ -4,8 +4,6 @@ using System.ComponentModel; using System.Linq.Expressions; using System.Reflection; -#nullable enable - namespace Avalonia.Data.Core { class IndexerExpressionNode : IndexerNodeBase @@ -18,7 +16,7 @@ namespace Avalonia.Data.Core public IndexerExpressionNode(IndexExpression expression) { - _parameter = Expression.Parameter(expression.Object.Type); + _parameter = Expression.Parameter(expression.Object!.Type); _expression = expression.Update(_parameter, expression.Arguments); _getDelegate = Expression.Lambda(_expression, _parameter).Compile(); diff --git a/src/Avalonia.Base/Data/Core/IndexerNodeBase.cs b/src/Avalonia.Base/Data/Core/IndexerNodeBase.cs index ef4375a02c..e197e29103 100644 --- a/src/Avalonia.Base/Data/Core/IndexerNodeBase.cs +++ b/src/Avalonia.Base/Data/Core/IndexerNodeBase.cs @@ -7,8 +7,6 @@ using System.Linq; using System.Reactive.Linq; using Avalonia.Utilities; -#nullable enable - namespace Avalonia.Data.Core { public abstract class IndexerNodeBase : SettableNode @@ -72,12 +70,12 @@ namespace Avalonia.Data.Core return index >= e.OldStartingIndex; case NotifyCollectionChangedAction.Replace: return index >= e.NewStartingIndex && - index < e.NewStartingIndex + e.NewItems.Count; + index < e.NewStartingIndex + e.NewItems!.Count; case NotifyCollectionChangedAction.Move: return (index >= e.NewStartingIndex && - index < e.NewStartingIndex + e.NewItems.Count) || + index < e.NewStartingIndex + e.NewItems!.Count) || (index >= e.OldStartingIndex && - index < e.OldStartingIndex + e.OldItems.Count); + index < e.OldStartingIndex + e.OldItems!.Count); case NotifyCollectionChangedAction.Reset: return true; } diff --git a/src/Avalonia.Base/Data/Core/LogicalNotNode.cs b/src/Avalonia.Base/Data/Core/LogicalNotNode.cs index 7ccec51e1a..45837db73d 100644 --- a/src/Avalonia.Base/Data/Core/LogicalNotNode.cs +++ b/src/Avalonia.Base/Data/Core/LogicalNotNode.cs @@ -1,8 +1,6 @@ using System; using System.Globalization; -#nullable enable - namespace Avalonia.Data.Core { public class LogicalNotNode : ExpressionNode, ITransformNode diff --git a/src/Avalonia.Base/Data/Core/MarkupBindingChainException.cs b/src/Avalonia.Base/Data/Core/MarkupBindingChainException.cs index 2dbeae41ae..5d23cecbef 100644 --- a/src/Avalonia.Base/Data/Core/MarkupBindingChainException.cs +++ b/src/Avalonia.Base/Data/Core/MarkupBindingChainException.cs @@ -2,8 +2,6 @@ using System.Collections.Generic; using System.Linq; -#nullable enable - namespace Avalonia.Data.Core { internal class MarkupBindingChainException : BindingChainException diff --git a/src/Avalonia.Base/Data/Core/Parsers/ExpressionTreeParser.cs b/src/Avalonia.Base/Data/Core/Parsers/ExpressionTreeParser.cs index 34409a3e36..d2035a592f 100644 --- a/src/Avalonia.Base/Data/Core/Parsers/ExpressionTreeParser.cs +++ b/src/Avalonia.Base/Data/Core/Parsers/ExpressionTreeParser.cs @@ -1,8 +1,6 @@ using System.Linq; using System.Linq.Expressions; -#nullable enable - namespace Avalonia.Data.Core.Parsers { static class ExpressionTreeParser diff --git a/src/Avalonia.Base/Data/Core/Parsers/ExpressionVisitorNodeBuilder.cs b/src/Avalonia.Base/Data/Core/Parsers/ExpressionVisitorNodeBuilder.cs index caba0c2e78..1e82214d76 100644 --- a/src/Avalonia.Base/Data/Core/Parsers/ExpressionVisitorNodeBuilder.cs +++ b/src/Avalonia.Base/Data/Core/Parsers/ExpressionVisitorNodeBuilder.cs @@ -4,8 +4,6 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; -#nullable enable - namespace Avalonia.Data.Core.Parsers { class ExpressionVisitorNodeBuilder : ExpressionVisitor @@ -18,8 +16,8 @@ namespace Avalonia.Data.Core.Parsers static ExpressionVisitorNodeBuilder() { - AvaloniaObjectIndexer = typeof(AvaloniaObject).GetProperty("Item", new[] { typeof(AvaloniaProperty) }); - CreateDelegateMethod = typeof(MethodInfo).GetMethod("CreateDelegate", new[] { typeof(Type), typeof(object) }); + AvaloniaObjectIndexer = typeof(AvaloniaObject).GetProperty("Item", new[] { typeof(AvaloniaProperty) })!; + CreateDelegateMethod = typeof(MethodInfo).GetMethod("CreateDelegate", new[] { typeof(Type), typeof(object) })!; } public List Nodes { get; } @@ -164,7 +162,7 @@ namespace Avalonia.Data.Core.Parsers if (node.Method == CreateDelegateMethod) { var visited = Visit(node.Arguments[1]); - Nodes.Add(new PropertyAccessorNode(GetArgumentExpressionValue(node.Object).Name, _enableDataValidation)); + Nodes.Add(new PropertyAccessorNode(GetArgumentExpressionValue(node.Object!).Name, _enableDataValidation)); return node; } else if (node.Method.Name == StreamBindingExtensions.StreamBindingName || node.Method.Name.StartsWith(StreamBindingExtensions.StreamBindingName + '`')) @@ -185,20 +183,20 @@ namespace Avalonia.Data.Core.Parsers if (property != null) { - return Visit(Expression.MakeIndex(node.Object, property, node.Arguments)); + return Visit(Expression.MakeIndex(node.Object!, property, node.Arguments)); } - else if (node.Object.Type.IsArray && node.Method.Name == MultiDimensionalArrayGetterMethodName) + else if (node.Object!.Type.IsArray && node.Method.Name == MultiDimensionalArrayGetterMethodName) { return Visit(Expression.MakeIndex(node.Object, null, node.Arguments)); } - throw new ExpressionParseException(0, $"Invalid method call in binding expression: '{node.Method.DeclaringType.AssemblyQualifiedName}.{node.Method.Name}'."); + throw new ExpressionParseException(0, $"Invalid method call in binding expression: '{node.Method.DeclaringType!.AssemblyQualifiedName}.{node.Method.Name}'."); } - private PropertyInfo TryGetPropertyFromMethod(MethodInfo method) + private PropertyInfo? TryGetPropertyFromMethod(MethodInfo method) { var type = method.DeclaringType; - return type.GetRuntimeProperties().FirstOrDefault(prop => prop.GetMethod == method); + return type?.GetRuntimeProperties().FirstOrDefault(prop => prop.GetMethod == method); } protected override Expression VisitSwitch(SwitchExpression node) diff --git a/src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs index b72989ce00..3c4120ad0b 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs @@ -1,8 +1,6 @@ using System; using System.Runtime.ExceptionServices; -#nullable enable - namespace Avalonia.Data.Core.Plugins { /// @@ -74,12 +72,11 @@ namespace Avalonia.Data.Core.Plugins _property = property ?? throw new ArgumentNullException(nameof(property)); } - public AvaloniaObject Instance + public AvaloniaObject? Instance { get { - AvaloniaObject result; - _reference.TryGetTarget(out result); + _reference.TryGetTarget(out var result); return result; } } @@ -91,7 +88,7 @@ namespace Avalonia.Data.Core.Plugins { if (!_property.IsReadOnly) { - Instance.SetValue(_property, value, priority); + Instance?.SetValue(_property, value, priority); return true; } diff --git a/src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs index 4d973d4aaa..361d68dc81 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs @@ -4,8 +4,6 @@ using System.ComponentModel.DataAnnotations; using System.Linq; using System.Reflection; -#nullable enable - namespace Avalonia.Data.Core.Plugins { /// @@ -33,19 +31,23 @@ namespace Avalonia.Data.Core.Plugins private sealed class Accessor : DataValidationBase { - private readonly ValidationContext _context; + private readonly ValidationContext? _context; public Accessor(WeakReference reference, string name, IPropertyAccessor inner) : base(inner) { - reference.TryGetTarget(out var target); - - _context = new ValidationContext(target); - _context.MemberName = name; + if (reference.TryGetTarget(out var target)) + { + _context = new ValidationContext(target); + _context.MemberName = name; + } } protected override void InnerValueChanged(object? value) { + if (_context is null) + return; + var errors = new List(); if (Validator.TryValidateProperty(value, _context, errors)) diff --git a/src/Avalonia.Base/Data/Core/Plugins/DataValidationBase.cs b/src/Avalonia.Base/Data/Core/Plugins/DataValidationBase.cs index 3e98d26fa1..6bef9f69f6 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/DataValidationBase.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/DataValidationBase.cs @@ -1,7 +1,5 @@ using System; -#nullable enable - namespace Avalonia.Data.Core.Plugins { /// diff --git a/src/Avalonia.Base/Data/Core/Plugins/ExceptionValidationPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/ExceptionValidationPlugin.cs index 95855b09f4..bf4a0a88bd 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/ExceptionValidationPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/ExceptionValidationPlugin.cs @@ -1,8 +1,6 @@ using System; using System.Reflection; -#nullable enable - namespace Avalonia.Data.Core.Plugins { /// @@ -32,7 +30,7 @@ namespace Avalonia.Data.Core.Plugins { return base.SetValue(value, priority); } - catch (TargetInvocationException ex) + catch (TargetInvocationException ex) when (ex.InnerException is not null) { PublishValue(new BindingNotification(ex.InnerException, BindingErrorType.DataValidationError)); } diff --git a/src/Avalonia.Base/Data/Core/Plugins/IDataValidationPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/IDataValidationPlugin.cs index 549327bc0f..88e38a8d08 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/IDataValidationPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/IDataValidationPlugin.cs @@ -1,7 +1,5 @@ using System; -#nullable enable - namespace Avalonia.Data.Core.Plugins { /// diff --git a/src/Avalonia.Base/Data/Core/Plugins/IPropertyAccessor.cs b/src/Avalonia.Base/Data/Core/Plugins/IPropertyAccessor.cs index 59017b5aae..8941e86598 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/IPropertyAccessor.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/IPropertyAccessor.cs @@ -1,7 +1,5 @@ using System; -#nullable enable - namespace Avalonia.Data.Core.Plugins { /// diff --git a/src/Avalonia.Base/Data/Core/Plugins/IPropertyAccessorPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/IPropertyAccessorPlugin.cs index 4a391e8489..f000427de3 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/IPropertyAccessorPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/IPropertyAccessorPlugin.cs @@ -1,7 +1,5 @@ using System; -#nullable enable - namespace Avalonia.Data.Core.Plugins { /// diff --git a/src/Avalonia.Base/Data/Core/Plugins/IStreamPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/IStreamPlugin.cs index 543bcba601..b741cfaca2 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/IStreamPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/IStreamPlugin.cs @@ -1,7 +1,5 @@ using System; -#nullable enable - namespace Avalonia.Data.Core.Plugins { /// diff --git a/src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs index 7b37b5d4e7..9f827daf94 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs @@ -4,8 +4,6 @@ using System.ComponentModel; using System.Linq; using Avalonia.Utilities; -#nullable enable - namespace Avalonia.Data.Core.Plugins { /// @@ -39,7 +37,7 @@ namespace Avalonia.Data.Core.Plugins _name = name; } - void IWeakSubscriber.OnEvent(object sender, DataErrorsChangedEventArgs e) + void IWeakSubscriber.OnEvent(object? sender, DataErrorsChangedEventArgs e) { if (e.PropertyName == _name || string.IsNullOrEmpty(e.PropertyName)) { diff --git a/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs index d5bf8da029..fd532f3014 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs @@ -4,8 +4,6 @@ using System.ComponentModel; using System.Reflection; using Avalonia.Utilities; -#nullable enable - namespace Avalonia.Data.Core.Plugins { /// @@ -131,7 +129,7 @@ namespace Avalonia.Data.Core.Plugins return false; } - void IWeakSubscriber.OnEvent(object sender, PropertyChangedEventArgs e) + void IWeakSubscriber.OnEvent(object? sender, PropertyChangedEventArgs e) { if (e.PropertyName == _property.Name || string.IsNullOrEmpty(e.PropertyName)) { diff --git a/src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs index e0d686586c..160c7301f5 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs @@ -3,8 +3,6 @@ using System.Collections.Generic; using System.Linq.Expressions; using System.Reflection; -#nullable enable - namespace Avalonia.Data.Core.Plugins { public class MethodAccessorPlugin : IPropertyAccessorPlugin diff --git a/src/Avalonia.Base/Data/Core/Plugins/ObservableStreamPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/ObservableStreamPlugin.cs index 7d77adfb00..0288dbce91 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/ObservableStreamPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/ObservableStreamPlugin.cs @@ -3,8 +3,6 @@ using System.Linq; using System.Reactive.Linq; using System.Reflection; -#nullable enable - namespace Avalonia.Data.Core.Plugins { /// @@ -57,14 +55,14 @@ namespace Avalonia.Data.Core.Plugins // Make a Box<> delegate of the correct type. var funcType = typeof(Func<,>).MakeGenericType(sourceType, typeof(object)); - var box = GetType().GetMethod(nameof(Box), BindingFlags.Static | BindingFlags.NonPublic) + var box = GetType().GetMethod(nameof(Box), BindingFlags.Static | BindingFlags.NonPublic)! .MakeGenericMethod(sourceType) .CreateDelegate(funcType); // Call Observable.Select(target, box); - return (IObservable)select.Invoke( + return (IObservable)select.Invoke( null, - new object[] { target, box }); + new object[] { target, box })!; } private static MethodInfo GetObservableSelect(Type source) diff --git a/src/Avalonia.Base/Data/Core/Plugins/PropertyAccessorBase.cs b/src/Avalonia.Base/Data/Core/Plugins/PropertyAccessorBase.cs index ba3fca1ab5..df36347e93 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/PropertyAccessorBase.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/PropertyAccessorBase.cs @@ -1,7 +1,5 @@ using System; -#nullable enable - namespace Avalonia.Data.Core.Plugins { /// diff --git a/src/Avalonia.Base/Data/Core/Plugins/PropertyError.cs b/src/Avalonia.Base/Data/Core/Plugins/PropertyError.cs index c2a3214c0d..62629e13bd 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/PropertyError.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/PropertyError.cs @@ -1,7 +1,5 @@ using System; -#nullable enable - namespace Avalonia.Data.Core.Plugins { /// diff --git a/src/Avalonia.Base/Data/Core/Plugins/TaskStreamPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/TaskStreamPlugin.cs index d1a3568a55..377ea9f275 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/TaskStreamPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/TaskStreamPlugin.cs @@ -4,8 +4,6 @@ using System.Reactive.Subjects; using System.Reflection; using System.Threading.Tasks; -#nullable enable - namespace Avalonia.Data.Core.Plugins { /// @@ -72,7 +70,7 @@ namespace Avalonia.Data.Core.Plugins case TaskStatus.RanToCompletion: return Observable.Return(resultProperty.GetValue(task)); case TaskStatus.Faulted: - return Observable.Return(new BindingNotification(task.Exception, BindingErrorType.Error)); + return Observable.Return(new BindingNotification(task.Exception!, BindingErrorType.Error)); default: throw new AvaloniaInternalException("HandleCompleted called for non-completed Task."); } diff --git a/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs b/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs index 92c3b65307..c13c6fcbc8 100644 --- a/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs +++ b/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs @@ -2,8 +2,6 @@ using System; using System.Diagnostics; using Avalonia.Data.Core.Plugins; -#nullable enable - namespace Avalonia.Data.Core { public class PropertyAccessorNode : SettableNode diff --git a/src/Avalonia.Base/Data/Core/PropertyPath.cs b/src/Avalonia.Base/Data/Core/PropertyPath.cs index 4ec402d7e1..665953c4a1 100644 --- a/src/Avalonia.Base/Data/Core/PropertyPath.cs +++ b/src/Avalonia.Base/Data/Core/PropertyPath.cs @@ -2,8 +2,6 @@ using System; using System.Collections.Generic; using System.Linq; -#nullable enable - namespace Avalonia.Data.Core { public class PropertyPath diff --git a/src/Avalonia.Base/Data/Core/SettableNode.cs b/src/Avalonia.Base/Data/Core/SettableNode.cs index 3467a16123..9ad9ace814 100644 --- a/src/Avalonia.Base/Data/Core/SettableNode.cs +++ b/src/Avalonia.Base/Data/Core/SettableNode.cs @@ -1,7 +1,5 @@ using System; -#nullable enable - namespace Avalonia.Data.Core { public abstract class SettableNode : ExpressionNode diff --git a/src/Avalonia.Base/Data/Core/StreamBindingExtensions.cs b/src/Avalonia.Base/Data/Core/StreamBindingExtensions.cs index 75fe37f958..fa2929c6ef 100644 --- a/src/Avalonia.Base/Data/Core/StreamBindingExtensions.cs +++ b/src/Avalonia.Base/Data/Core/StreamBindingExtensions.cs @@ -1,8 +1,6 @@ using System; using System.Threading.Tasks; -#nullable enable - namespace Avalonia { public static class StreamBindingExtensions diff --git a/src/Avalonia.Base/Data/Core/StreamNode.cs b/src/Avalonia.Base/Data/Core/StreamNode.cs index 086fbb7ee2..133e6a75f1 100644 --- a/src/Avalonia.Base/Data/Core/StreamNode.cs +++ b/src/Avalonia.Base/Data/Core/StreamNode.cs @@ -2,8 +2,6 @@ using System; using System.Reactive.Linq; using Avalonia.Data.Core.Plugins; -#nullable enable - namespace Avalonia.Data.Core { public class StreamNode : ExpressionNode diff --git a/src/Avalonia.Base/Data/Core/TypeCastNode.cs b/src/Avalonia.Base/Data/Core/TypeCastNode.cs index 2404c90fe2..3a2ca955fa 100644 --- a/src/Avalonia.Base/Data/Core/TypeCastNode.cs +++ b/src/Avalonia.Base/Data/Core/TypeCastNode.cs @@ -2,8 +2,6 @@ using System.Collections.Generic; using System.Text; -#nullable enable - namespace Avalonia.Data.Core { public class TypeCastNode : ExpressionNode diff --git a/src/Avalonia.Base/Data/DataValidationException.cs b/src/Avalonia.Base/Data/DataValidationException.cs index cafe74cd2c..74f39a1b6f 100644 --- a/src/Avalonia.Base/Data/DataValidationException.cs +++ b/src/Avalonia.Base/Data/DataValidationException.cs @@ -1,7 +1,5 @@ using System; -#nullable enable - namespace Avalonia.Data { /// diff --git a/src/Avalonia.Base/Data/IBinding.cs b/src/Avalonia.Base/Data/IBinding.cs index 3b4add0269..9535cf608e 100644 --- a/src/Avalonia.Base/Data/IBinding.cs +++ b/src/Avalonia.Base/Data/IBinding.cs @@ -1,5 +1,3 @@ -#nullable enable - namespace Avalonia.Data { /// diff --git a/src/Avalonia.Base/Data/IndexerBinding.cs b/src/Avalonia.Base/Data/IndexerBinding.cs index bc8fe85a70..6a33d0c4fc 100644 --- a/src/Avalonia.Base/Data/IndexerBinding.cs +++ b/src/Avalonia.Base/Data/IndexerBinding.cs @@ -1,6 +1,4 @@ -#nullable enable - -namespace Avalonia.Data +namespace Avalonia.Data { public class IndexerBinding : IBinding { diff --git a/src/Avalonia.Base/Data/IndexerDescriptor.cs b/src/Avalonia.Base/Data/IndexerDescriptor.cs index 3c02509063..c823630d3c 100644 --- a/src/Avalonia.Base/Data/IndexerDescriptor.cs +++ b/src/Avalonia.Base/Data/IndexerDescriptor.cs @@ -1,8 +1,6 @@ using System; using System.Reactive; -#nullable enable - namespace Avalonia.Data { /// diff --git a/src/Avalonia.Base/Data/InstancedBinding.cs b/src/Avalonia.Base/Data/InstancedBinding.cs index dd7fcb3007..9ff319eddb 100644 --- a/src/Avalonia.Base/Data/InstancedBinding.cs +++ b/src/Avalonia.Base/Data/InstancedBinding.cs @@ -1,8 +1,6 @@ using System; using System.Reactive.Subjects; -#nullable enable - namespace Avalonia.Data { /// diff --git a/src/Avalonia.Base/Data/Optional.cs b/src/Avalonia.Base/Data/Optional.cs index d2283bf66b..513222d427 100644 --- a/src/Avalonia.Base/Data/Optional.cs +++ b/src/Avalonia.Base/Data/Optional.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable enable - namespace Avalonia.Data { /// diff --git a/src/Avalonia.Base/Diagnostics/AvaloniaPropertyValue.cs b/src/Avalonia.Base/Diagnostics/AvaloniaPropertyValue.cs index c881c389c1..4189fd5234 100644 --- a/src/Avalonia.Base/Diagnostics/AvaloniaPropertyValue.cs +++ b/src/Avalonia.Base/Diagnostics/AvaloniaPropertyValue.cs @@ -17,9 +17,9 @@ namespace Avalonia.Diagnostics /// A diagnostic string. public AvaloniaPropertyValue( AvaloniaProperty property, - object value, + object? value, BindingPriority priority, - string diagnostic) + string? diagnostic) { Property = property; Value = value; @@ -35,7 +35,7 @@ namespace Avalonia.Diagnostics /// /// Gets the current property value. /// - public object Value { get; } + public object? Value { get; } /// /// Gets the priority of the current value. @@ -45,6 +45,6 @@ namespace Avalonia.Diagnostics /// /// Gets a diagnostic string. /// - public string Diagnostic { get; } + public string? Diagnostic { get; } } } diff --git a/src/Avalonia.Base/Diagnostics/IAvaloniaObjectDebug.cs b/src/Avalonia.Base/Diagnostics/IAvaloniaObjectDebug.cs index 4b9f12ddf8..2d88203f67 100644 --- a/src/Avalonia.Base/Diagnostics/IAvaloniaObjectDebug.cs +++ b/src/Avalonia.Base/Diagnostics/IAvaloniaObjectDebug.cs @@ -1,7 +1,5 @@ using System; -#nullable enable - namespace Avalonia.Diagnostics { /// diff --git a/src/Avalonia.Base/Diagnostics/INotifyCollectionChangedDebug.cs b/src/Avalonia.Base/Diagnostics/INotifyCollectionChangedDebug.cs index 36152e6430..00f2949b08 100644 --- a/src/Avalonia.Base/Diagnostics/INotifyCollectionChangedDebug.cs +++ b/src/Avalonia.Base/Diagnostics/INotifyCollectionChangedDebug.cs @@ -17,6 +17,6 @@ namespace Avalonia.Diagnostics /// /// The subscribers or null if no subscribers. /// - Delegate[] GetCollectionChangedSubscribers(); + Delegate[]? GetCollectionChangedSubscribers(); } } diff --git a/src/Avalonia.Base/DirectProperty.cs b/src/Avalonia.Base/DirectProperty.cs index eb04cfdce0..a5905a6081 100644 --- a/src/Avalonia.Base/DirectProperty.cs +++ b/src/Avalonia.Base/DirectProperty.cs @@ -2,8 +2,6 @@ using System; using System.Diagnostics.CodeAnalysis; using Avalonia.Data; -#nullable enable - namespace Avalonia { /// diff --git a/src/Avalonia.Base/DirectPropertyBase.cs b/src/Avalonia.Base/DirectPropertyBase.cs index 4b54ee75ab..9c4896b96c 100644 --- a/src/Avalonia.Base/DirectPropertyBase.cs +++ b/src/Avalonia.Base/DirectPropertyBase.cs @@ -3,8 +3,6 @@ using Avalonia.Data; using Avalonia.Reactive; using Avalonia.Utilities; -#nullable enable - namespace Avalonia { /// @@ -155,7 +153,7 @@ namespace Avalonia if (v.HasValue) { - o.SetValue(this, (TValue)v.Value); + o.SetValue(this, (TValue)v.Value!); } else if (v.Type == BindingValueType.UnsetValue) { diff --git a/src/Avalonia.Base/DirectPropertyMetadata`1.cs b/src/Avalonia.Base/DirectPropertyMetadata`1.cs index a265032139..fe1cdd0e65 100644 --- a/src/Avalonia.Base/DirectPropertyMetadata`1.cs +++ b/src/Avalonia.Base/DirectPropertyMetadata`1.cs @@ -1,7 +1,5 @@ using Avalonia.Data; -#nullable enable - namespace Avalonia { /// diff --git a/src/Avalonia.Base/EnumExtensions.cs b/src/Avalonia.Base/EnumExtensions.cs index 1f7c532cce..19eb42a700 100644 --- a/src/Avalonia.Base/EnumExtensions.cs +++ b/src/Avalonia.Base/EnumExtensions.cs @@ -1,8 +1,6 @@ using System; using System.Runtime.CompilerServices; -#nullable enable - namespace Avalonia { /// diff --git a/src/Avalonia.Base/IAvaloniaObject.cs b/src/Avalonia.Base/IAvaloniaObject.cs index 2e992f8616..654528658f 100644 --- a/src/Avalonia.Base/IAvaloniaObject.cs +++ b/src/Avalonia.Base/IAvaloniaObject.cs @@ -1,8 +1,6 @@ using System; using Avalonia.Data; -#nullable enable - namespace Avalonia { /// diff --git a/src/Avalonia.Base/IDescription.cs b/src/Avalonia.Base/IDescription.cs index 17a7f98776..e1ff52476d 100644 --- a/src/Avalonia.Base/IDescription.cs +++ b/src/Avalonia.Base/IDescription.cs @@ -1,5 +1,3 @@ -#nullable enable - namespace Avalonia { /// diff --git a/src/Avalonia.Base/IDirectPropertyAccessor.cs b/src/Avalonia.Base/IDirectPropertyAccessor.cs index e75ca238ad..e34483fa7b 100644 --- a/src/Avalonia.Base/IDirectPropertyAccessor.cs +++ b/src/Avalonia.Base/IDirectPropertyAccessor.cs @@ -1,7 +1,5 @@ using System; -#nullable enable - namespace Avalonia { /// diff --git a/src/Avalonia.Base/IDirectPropertyMetadata.cs b/src/Avalonia.Base/IDirectPropertyMetadata.cs index 7b132d192e..4fd4a3a6b7 100644 --- a/src/Avalonia.Base/IDirectPropertyMetadata.cs +++ b/src/Avalonia.Base/IDirectPropertyMetadata.cs @@ -1,5 +1,3 @@ -#nullable enable - namespace Avalonia { /// diff --git a/src/Avalonia.Base/IStyledPropertyAccessor.cs b/src/Avalonia.Base/IStyledPropertyAccessor.cs index 6ef6391010..c4a0005f55 100644 --- a/src/Avalonia.Base/IStyledPropertyAccessor.cs +++ b/src/Avalonia.Base/IStyledPropertyAccessor.cs @@ -1,7 +1,5 @@ using System; -#nullable enable - namespace Avalonia { /// diff --git a/src/Avalonia.Base/IStyledPropertyMetadata.cs b/src/Avalonia.Base/IStyledPropertyMetadata.cs index 2a0d09d6a7..6b29b5f977 100644 --- a/src/Avalonia.Base/IStyledPropertyMetadata.cs +++ b/src/Avalonia.Base/IStyledPropertyMetadata.cs @@ -1,5 +1,3 @@ -#nullable enable - namespace Avalonia { /// diff --git a/src/Avalonia.Base/Logging/ILogSink.cs b/src/Avalonia.Base/Logging/ILogSink.cs index 71268d5965..27558ba0ee 100644 --- a/src/Avalonia.Base/Logging/ILogSink.cs +++ b/src/Avalonia.Base/Logging/ILogSink.cs @@ -23,7 +23,7 @@ namespace Avalonia.Logging void Log( LogEventLevel level, string area, - object source, + object? source, string messageTemplate); /// @@ -37,7 +37,7 @@ namespace Avalonia.Logging void Log( LogEventLevel level, string area, - object source, + object? source, string messageTemplate, T0 propertyValue0); @@ -53,7 +53,7 @@ namespace Avalonia.Logging void Log( LogEventLevel level, string area, - object source, + object? source, string messageTemplate, T0 propertyValue0, T1 propertyValue1); @@ -71,7 +71,7 @@ namespace Avalonia.Logging void Log( LogEventLevel level, string area, - object source, + object? source, string messageTemplate, T0 propertyValue0, T1 propertyValue1, @@ -88,8 +88,8 @@ namespace Avalonia.Logging void Log( LogEventLevel level, string area, - object source, + object? source, string messageTemplate, - params object[] propertyValues); + params object?[] propertyValues); } } diff --git a/src/Avalonia.Base/Logging/Logger.cs b/src/Avalonia.Base/Logging/Logger.cs index ed3fad93fc..c3fffb62de 100644 --- a/src/Avalonia.Base/Logging/Logger.cs +++ b/src/Avalonia.Base/Logging/Logger.cs @@ -8,7 +8,7 @@ namespace Avalonia.Logging /// /// Gets or sets the application-defined sink that receives the messages. /// - public static ILogSink Sink { get; set; } + public static ILogSink? Sink { get; set; } /// /// Checks if given log level is enabled. @@ -34,7 +34,7 @@ namespace Avalonia.Logging return null; } - return new ParametrizedLogger(Sink, level, area); + return new ParametrizedLogger(Sink!, level, area); } /// diff --git a/src/Avalonia.Base/Logging/ParametrizedLogger.cs b/src/Avalonia.Base/Logging/ParametrizedLogger.cs index adadb0f990..6b7331b504 100644 --- a/src/Avalonia.Base/Logging/ParametrizedLogger.cs +++ b/src/Avalonia.Base/Logging/ParametrizedLogger.cs @@ -30,7 +30,7 @@ namespace Avalonia.Logging /// The message template. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Log( - object source, + object? source, string messageTemplate) { _sink.Log(_level, _area, source, messageTemplate); @@ -44,7 +44,7 @@ namespace Avalonia.Logging /// Message property value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Log( - object source, + object? source, string messageTemplate, T0 propertyValue0) { @@ -60,7 +60,7 @@ namespace Avalonia.Logging /// Message property value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Log( - object source, + object? source, string messageTemplate, T0 propertyValue0, T1 propertyValue1) @@ -78,7 +78,7 @@ namespace Avalonia.Logging /// Message property value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Log( - object source, + object? source, string messageTemplate, T0 propertyValue0, T1 propertyValue1, @@ -98,7 +98,7 @@ namespace Avalonia.Logging /// Message property value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Log( - object source, + object? source, string messageTemplate, T0 propertyValue0, T1 propertyValue1, @@ -120,7 +120,7 @@ namespace Avalonia.Logging /// Message property value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Log( - object source, + object? source, string messageTemplate, T0 propertyValue0, T1 propertyValue1, @@ -144,7 +144,7 @@ namespace Avalonia.Logging /// Message property value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Log( - object source, + object? source, string messageTemplate, T0 propertyValue0, T1 propertyValue1, diff --git a/src/Avalonia.Base/Logging/TraceLogSink.cs b/src/Avalonia.Base/Logging/TraceLogSink.cs index f597844378..02ed191d2c 100644 --- a/src/Avalonia.Base/Logging/TraceLogSink.cs +++ b/src/Avalonia.Base/Logging/TraceLogSink.cs @@ -9,11 +9,11 @@ namespace Avalonia.Logging public class TraceLogSink : ILogSink { private readonly LogEventLevel _level; - private readonly IList _areas; + private readonly IList? _areas; public TraceLogSink( LogEventLevel minimumLevel, - IList areas = null) + IList? areas = null) { _level = minimumLevel; _areas = areas?.Count > 0 ? areas : null; @@ -24,7 +24,7 @@ namespace Avalonia.Logging return level >= _level && (_areas?.Contains(area) ?? true); } - public void Log(LogEventLevel level, string area, object source, string messageTemplate) + public void Log(LogEventLevel level, string area, object? source, string messageTemplate) { if (IsEnabled(level, area)) { @@ -32,7 +32,7 @@ namespace Avalonia.Logging } } - public void Log(LogEventLevel level, string area, object source, string messageTemplate, T0 propertyValue0) + public void Log(LogEventLevel level, string area, object? source, string messageTemplate, T0 propertyValue0) { if (IsEnabled(level, area)) { @@ -40,7 +40,7 @@ namespace Avalonia.Logging } } - public void Log(LogEventLevel level, string area, object source, string messageTemplate, T0 propertyValue0, T1 propertyValue1) + public void Log(LogEventLevel level, string area, object? source, string messageTemplate, T0 propertyValue0, T1 propertyValue1) { if (IsEnabled(level, area)) { @@ -48,7 +48,7 @@ namespace Avalonia.Logging } } - public void Log(LogEventLevel level, string area, object source, string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) + public void Log(LogEventLevel level, string area, object? source, string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) { if (IsEnabled(level, area)) { @@ -56,7 +56,7 @@ namespace Avalonia.Logging } } - public void Log(LogEventLevel level, string area, object source, string messageTemplate, params object[] propertyValues) + public void Log(LogEventLevel level, string area, object? source, string messageTemplate, params object?[] propertyValues) { if (IsEnabled(level, area)) { @@ -67,10 +67,10 @@ namespace Avalonia.Logging private static string Format( string area, string template, - object source, - T0 v0 = default, - T1 v1 = default, - T2 v2 = default) + object? source, + T0? v0 = default, + T1? v1 = default, + T2? v2 = default) { var result = new StringBuilder(template.Length); var r = new CharacterReader(template.AsSpan()); @@ -127,8 +127,8 @@ namespace Avalonia.Logging private static string Format( string area, string template, - object source, - object[] v) + object? source, + object?[] v) { var result = new StringBuilder(template.Length); var r = new CharacterReader(template.AsSpan()); diff --git a/src/Avalonia.Base/Metadata/TemplateContent.cs b/src/Avalonia.Base/Metadata/TemplateContent.cs index 7f9e878419..258154aba4 100644 --- a/src/Avalonia.Base/Metadata/TemplateContent.cs +++ b/src/Avalonia.Base/Metadata/TemplateContent.cs @@ -8,6 +8,6 @@ namespace Avalonia.Metadata [AttributeUsage(AttributeTargets.Property)] public class TemplateContentAttribute : Attribute { - public Type TemplateResultType { get; set; } + public Type? TemplateResultType { get; set; } } } diff --git a/src/Avalonia.Base/Platform/IAssetLoader.cs b/src/Avalonia.Base/Platform/IAssetLoader.cs index c2e422cabb..ec1f97f178 100644 --- a/src/Avalonia.Base/Platform/IAssetLoader.cs +++ b/src/Avalonia.Base/Platform/IAssetLoader.cs @@ -27,7 +27,7 @@ namespace Avalonia.Platform /// A base URI to use if is relative. /// /// True if the asset could be found; otherwise false. - bool Exists(Uri uri, Uri baseUri = null); + bool Exists(Uri uri, Uri? baseUri = null); /// /// Opens the asset with the requested URI. @@ -40,7 +40,7 @@ namespace Avalonia.Platform /// /// The asset could not be found. /// - Stream Open(Uri uri, Uri baseUri = null); + Stream Open(Uri uri, Uri? baseUri = null); /// /// Opens the asset with the requested URI and returns the asset stream and the @@ -56,7 +56,7 @@ namespace Avalonia.Platform /// /// The asset could not be found. /// - (Stream stream, Assembly assembly) OpenAndGetAssembly(Uri uri, Uri baseUri = null); + (Stream stream, Assembly assembly) OpenAndGetAssembly(Uri uri, Uri? baseUri = null); /// /// Extracts assembly information from URI @@ -66,7 +66,7 @@ namespace Avalonia.Platform /// A base URI to use if is relative. /// /// Assembly associated with the Uri - Assembly GetAssembly(Uri uri, Uri baseUri = null); + Assembly? GetAssembly(Uri uri, Uri? baseUri = null); /// /// Gets all assets of a folder and subfolders that match specified uri. diff --git a/src/Avalonia.Base/Platform/IPlatformHandle.cs b/src/Avalonia.Base/Platform/IPlatformHandle.cs index a3b9d8f07c..e1e9888045 100644 --- a/src/Avalonia.Base/Platform/IPlatformHandle.cs +++ b/src/Avalonia.Base/Platform/IPlatformHandle.cs @@ -15,6 +15,6 @@ namespace Avalonia.Platform /// /// Gets an optional string that describes what represents. /// - string HandleDescriptor { get; } + string? HandleDescriptor { get; } } } diff --git a/src/Avalonia.Base/Platform/Interop/Utf8Buffer.cs b/src/Avalonia.Base/Platform/Interop/Utf8Buffer.cs index 7117576b3b..7a0b5b34cd 100644 --- a/src/Avalonia.Base/Platform/Interop/Utf8Buffer.cs +++ b/src/Avalonia.Base/Platform/Interop/Utf8Buffer.cs @@ -8,9 +8,9 @@ namespace Avalonia.Platform.Interop public class Utf8Buffer : SafeHandle { private GCHandle _gcHandle; - private byte[] _data; + private byte[]? _data; - public Utf8Buffer(string s) : base(IntPtr.Zero, true) + public Utf8Buffer(string? s) : base(IntPtr.Zero, true) { if (s == null) return; @@ -19,7 +19,7 @@ namespace Avalonia.Platform.Interop handle = _gcHandle.AddrOfPinnedObject(); } - public int ByteLen => _data.Length; + public int ByteLen => _data?.Length ?? 0; protected override bool ReleaseHandle() { @@ -34,7 +34,7 @@ namespace Avalonia.Platform.Interop public override bool IsInvalid => handle == IntPtr.Zero; - public static unsafe string StringFromPtr(IntPtr s) + public static unsafe string? StringFromPtr(IntPtr s) { var pstr = (byte*)s; if (pstr == null) diff --git a/src/Avalonia.Base/Platform/PlatformHandle.cs b/src/Avalonia.Base/Platform/PlatformHandle.cs index bed25a3b5b..9cc2741a12 100644 --- a/src/Avalonia.Base/Platform/PlatformHandle.cs +++ b/src/Avalonia.Base/Platform/PlatformHandle.cs @@ -14,7 +14,7 @@ namespace Avalonia.Platform /// /// An optional string that describes what represents. /// - public PlatformHandle(IntPtr handle, string descriptor) + public PlatformHandle(IntPtr handle, string? descriptor) { Handle = handle; HandleDescriptor = descriptor; @@ -28,6 +28,6 @@ namespace Avalonia.Platform /// /// Gets an optional string that describes what represents. /// - public string HandleDescriptor { get; } + public string? HandleDescriptor { get; } } } diff --git a/src/Avalonia.Base/PropertyStore/BindingEntry.cs b/src/Avalonia.Base/PropertyStore/BindingEntry.cs index 315e28f84a..cffbaed6b0 100644 --- a/src/Avalonia.Base/PropertyStore/BindingEntry.cs +++ b/src/Avalonia.Base/PropertyStore/BindingEntry.cs @@ -2,8 +2,6 @@ using Avalonia.Data; using Avalonia.Threading; -#nullable enable - namespace Avalonia.PropertyStore { /// diff --git a/src/Avalonia.Base/PropertyStore/ConstantValueEntry.cs b/src/Avalonia.Base/PropertyStore/ConstantValueEntry.cs index 7e904f57df..c7fbf56abc 100644 --- a/src/Avalonia.Base/PropertyStore/ConstantValueEntry.cs +++ b/src/Avalonia.Base/PropertyStore/ConstantValueEntry.cs @@ -2,8 +2,6 @@ using System.Diagnostics.CodeAnalysis; using Avalonia.Data; -#nullable enable - namespace Avalonia.PropertyStore { /// diff --git a/src/Avalonia.Base/PropertyStore/IPriorityValueEntry.cs b/src/Avalonia.Base/PropertyStore/IPriorityValueEntry.cs index 4d82381323..26665ab683 100644 --- a/src/Avalonia.Base/PropertyStore/IPriorityValueEntry.cs +++ b/src/Avalonia.Base/PropertyStore/IPriorityValueEntry.cs @@ -1,6 +1,4 @@ -#nullable enable - -namespace Avalonia.PropertyStore +namespace Avalonia.PropertyStore { /// /// Represents an untyped interface to . diff --git a/src/Avalonia.Base/PropertyStore/IValue.cs b/src/Avalonia.Base/PropertyStore/IValue.cs index bbf4739e6a..a4ec06e64e 100644 --- a/src/Avalonia.Base/PropertyStore/IValue.cs +++ b/src/Avalonia.Base/PropertyStore/IValue.cs @@ -1,7 +1,5 @@ using Avalonia.Data; -#nullable enable - namespace Avalonia.PropertyStore { /// diff --git a/src/Avalonia.Base/PropertyStore/IValueSink.cs b/src/Avalonia.Base/PropertyStore/IValueSink.cs index 3a1e9731d8..738fdef267 100644 --- a/src/Avalonia.Base/PropertyStore/IValueSink.cs +++ b/src/Avalonia.Base/PropertyStore/IValueSink.cs @@ -1,7 +1,5 @@ using Avalonia.Data; -#nullable enable - namespace Avalonia.PropertyStore { /// diff --git a/src/Avalonia.Base/PropertyStore/LocalValueEntry.cs b/src/Avalonia.Base/PropertyStore/LocalValueEntry.cs index 4afc90e71d..cb5f3556d1 100644 --- a/src/Avalonia.Base/PropertyStore/LocalValueEntry.cs +++ b/src/Avalonia.Base/PropertyStore/LocalValueEntry.cs @@ -1,8 +1,6 @@ using System.Diagnostics.CodeAnalysis; using Avalonia.Data; -#nullable enable - namespace Avalonia.PropertyStore { /// diff --git a/src/Avalonia.Base/PropertyStore/PriorityValue.cs b/src/Avalonia.Base/PropertyStore/PriorityValue.cs index f5723cb3a7..f670160e75 100644 --- a/src/Avalonia.Base/PropertyStore/PriorityValue.cs +++ b/src/Avalonia.Base/PropertyStore/PriorityValue.cs @@ -3,8 +3,6 @@ using System.Collections.Generic; using System.ComponentModel; using Avalonia.Data; -#nullable enable - namespace Avalonia.PropertyStore { /// diff --git a/src/Avalonia.Base/Reactive/AvaloniaPropertyBindingObservable.cs b/src/Avalonia.Base/Reactive/AvaloniaPropertyBindingObservable.cs index 9a0dc61ba3..238bb16e3e 100644 --- a/src/Avalonia.Base/Reactive/AvaloniaPropertyBindingObservable.cs +++ b/src/Avalonia.Base/Reactive/AvaloniaPropertyBindingObservable.cs @@ -2,17 +2,14 @@ using System.Collections.Generic; using Avalonia.Data; -#nullable enable - namespace Avalonia.Reactive { internal class AvaloniaPropertyBindingObservable : LightweightObservableBase>, IDescription { private readonly WeakReference _target; private readonly AvaloniaProperty _property; - private T _value; + private BindingValue _value = BindingValue.Unset; -#nullable disable public AvaloniaPropertyBindingObservable( IAvaloniaObject target, AvaloniaProperty property) @@ -20,7 +17,6 @@ namespace Avalonia.Reactive _target = new WeakReference(target); _property = property; } -#nullable enable public string Description => $"{_target.GetType().Name}.{_property.Name}"; @@ -43,10 +39,13 @@ namespace Avalonia.Reactive protected override void Subscribed(IObserver> observer, bool first) { - observer.OnNext(new BindingValue(_value)); + if (_value.Type != BindingValueType.UnsetValue) + { + observer.OnNext(_value); + } } - private void PropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e) + private void PropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) { if (e.Property == _property) { @@ -54,7 +53,7 @@ namespace Avalonia.Reactive { var newValue = e.Sender.GetValue(typedArgs.Property); - if (!typedArgs.OldValue.HasValue || !EqualityComparer.Default.Equals(newValue, _value)) + if (!_value.HasValue || !EqualityComparer.Default.Equals(newValue, _value.Value)) { _value = newValue; PublishNext(_value); diff --git a/src/Avalonia.Base/Reactive/AvaloniaPropertyChangedObservable.cs b/src/Avalonia.Base/Reactive/AvaloniaPropertyChangedObservable.cs index 5ef0d25133..dc29ddbdff 100644 --- a/src/Avalonia.Base/Reactive/AvaloniaPropertyChangedObservable.cs +++ b/src/Avalonia.Base/Reactive/AvaloniaPropertyChangedObservable.cs @@ -35,7 +35,7 @@ namespace Avalonia.Reactive } } - private void PropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e) + private void PropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) { if (e.Property == _property) { diff --git a/src/Avalonia.Base/Reactive/AvaloniaPropertyObservable.cs b/src/Avalonia.Base/Reactive/AvaloniaPropertyObservable.cs index 6a3f9b0b30..fbc126bb05 100644 --- a/src/Avalonia.Base/Reactive/AvaloniaPropertyObservable.cs +++ b/src/Avalonia.Base/Reactive/AvaloniaPropertyObservable.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Avalonia.Data; namespace Avalonia.Reactive { @@ -7,7 +8,7 @@ namespace Avalonia.Reactive { private readonly WeakReference _target; private readonly AvaloniaProperty _property; - private T _value; + private Optional _value; public AvaloniaPropertyObservable( IAvaloniaObject target, @@ -23,7 +24,7 @@ namespace Avalonia.Reactive { if (_target.TryGetTarget(out var target)) { - _value = (T)target.GetValue(_property); + _value = (T)target.GetValue(_property)!; target.PropertyChanged += PropertyChanged; } } @@ -34,14 +35,17 @@ namespace Avalonia.Reactive { target.PropertyChanged -= PropertyChanged; } + + _value = default; } protected override void Subscribed(IObserver observer, bool first) { - observer.OnNext(_value); + if (_value.HasValue) + observer.OnNext(_value.Value); } - private void PropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e) + private void PropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) { if (e.Property == _property) { @@ -53,13 +57,14 @@ namespace Avalonia.Reactive } else { - newValue = (T)e.Sender.GetValue(e.Property); + newValue = (T)e.Sender.GetValue(e.Property)!; } - if (!EqualityComparer.Default.Equals(newValue, _value)) + if (!_value.HasValue || + !EqualityComparer.Default.Equals(newValue, _value.Value)) { _value = newValue; - PublishNext(_value); + PublishNext(_value.Value!); } } } diff --git a/src/Avalonia.Base/Reactive/BindingValueAdapter.cs b/src/Avalonia.Base/Reactive/BindingValueAdapter.cs index 8c80e9f48c..8eaf21dcdc 100644 --- a/src/Avalonia.Base/Reactive/BindingValueAdapter.cs +++ b/src/Avalonia.Base/Reactive/BindingValueAdapter.cs @@ -2,8 +2,6 @@ using System.Reactive.Subjects; using Avalonia.Data; -#nullable enable - namespace Avalonia.Reactive { internal class BindingValueAdapter : SingleSubscriberObservableBase>, diff --git a/src/Avalonia.Base/Reactive/BindingValueExtensions.cs b/src/Avalonia.Base/Reactive/BindingValueExtensions.cs index 6f0d29dd0f..770da2cab4 100644 --- a/src/Avalonia.Base/Reactive/BindingValueExtensions.cs +++ b/src/Avalonia.Base/Reactive/BindingValueExtensions.cs @@ -2,8 +2,6 @@ using System.Reactive.Subjects; using Avalonia.Data; -#nullable enable - namespace Avalonia.Reactive { public static class BindingValueExtensions diff --git a/src/Avalonia.Base/Reactive/LightweightObservableBase.cs b/src/Avalonia.Base/Reactive/LightweightObservableBase.cs index f5052e5858..9a3ab89b62 100644 --- a/src/Avalonia.Base/Reactive/LightweightObservableBase.cs +++ b/src/Avalonia.Base/Reactive/LightweightObservableBase.cs @@ -18,12 +18,13 @@ namespace Avalonia.Reactive /// public abstract class LightweightObservableBase : IObservable { - private Exception _error; - private List> _observers = new List>(); + private Exception? _error; + private List>? _observers = new List>(); public IDisposable Subscribe(IObserver observer) { - Contract.Requires(observer != null); + _ = observer ?? throw new ArgumentNullException(nameof(observer)); + Dispatcher.UIThread.VerifyAccess(); var first = false; @@ -91,9 +92,9 @@ namespace Avalonia.Reactive sealed class RemoveObserver : IDisposable { - LightweightObservableBase _parent; + LightweightObservableBase? _parent; - IObserver _observer; + IObserver? _observer; public RemoveObserver(LightweightObservableBase parent, IObserver observer) { @@ -104,7 +105,7 @@ namespace Avalonia.Reactive public void Dispose() { var observer = _observer; - Interlocked.Exchange(ref _parent, null)?.Remove(observer); + Interlocked.Exchange(ref _parent, null)?.Remove(observer!); _observer = null; } } @@ -116,8 +117,8 @@ namespace Avalonia.Reactive { if (Volatile.Read(ref _observers) != null) { - IObserver[] observers = null; - IObserver singleObserver = null; + IObserver[]? observers = null; + IObserver? singleObserver = null; lock (this) { if (_observers == null) @@ -139,7 +140,7 @@ namespace Avalonia.Reactive } else { - foreach (var observer in observers) + foreach (var observer in observers!) { observer.OnNext(value); } diff --git a/src/Avalonia.Base/Reactive/ObservableEx.cs b/src/Avalonia.Base/Reactive/ObservableEx.cs index 8e963fb9d5..1cea568c88 100644 --- a/src/Avalonia.Base/Reactive/ObservableEx.cs +++ b/src/Avalonia.Base/Reactive/ObservableEx.cs @@ -34,4 +34,4 @@ namespace Avalonia.Reactive } } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Reactive/SingleSubscriberObservableBase.cs b/src/Avalonia.Base/Reactive/SingleSubscriberObservableBase.cs index d3ac3dac8a..5e3a6bf79a 100644 --- a/src/Avalonia.Base/Reactive/SingleSubscriberObservableBase.cs +++ b/src/Avalonia.Base/Reactive/SingleSubscriberObservableBase.cs @@ -5,13 +5,13 @@ namespace Avalonia.Reactive { public abstract class SingleSubscriberObservableBase : IObservable, IDisposable { - private Exception _error; - private IObserver _observer; + private Exception? _error; + private IObserver? _observer; private bool _completed; public IDisposable Subscribe(IObserver observer) { - Contract.Requires(observer != null); + _ = observer ?? throw new ArgumentNullException(nameof(observer)); Dispatcher.UIThread.VerifyAccess(); if (_observer != null) diff --git a/src/Avalonia.Base/Reactive/TypedBindingAdapter.cs b/src/Avalonia.Base/Reactive/TypedBindingAdapter.cs index 44803bc7d6..f60ef247d5 100644 --- a/src/Avalonia.Base/Reactive/TypedBindingAdapter.cs +++ b/src/Avalonia.Base/Reactive/TypedBindingAdapter.cs @@ -2,8 +2,6 @@ using Avalonia.Data; using Avalonia.Logging; -#nullable enable - namespace Avalonia.Reactive { internal class TypedBindingAdapter : SingleSubscriberObservableBase>, diff --git a/src/Avalonia.Base/Reactive/UntypedBindingAdapter.cs b/src/Avalonia.Base/Reactive/UntypedBindingAdapter.cs index 03c1afcea9..58fbee7b8f 100644 --- a/src/Avalonia.Base/Reactive/UntypedBindingAdapter.cs +++ b/src/Avalonia.Base/Reactive/UntypedBindingAdapter.cs @@ -2,8 +2,6 @@ using System.Reactive.Subjects; using Avalonia.Data; -#nullable enable - namespace Avalonia.Reactive { internal class UntypedBindingAdapter : SingleSubscriberObservableBase, diff --git a/src/Avalonia.Base/StyledProperty.cs b/src/Avalonia.Base/StyledProperty.cs index 4bd24d4a42..d00a675d0f 100644 --- a/src/Avalonia.Base/StyledProperty.cs +++ b/src/Avalonia.Base/StyledProperty.cs @@ -1,7 +1,5 @@ using System; -#nullable enable - namespace Avalonia { /// diff --git a/src/Avalonia.Base/StyledPropertyBase.cs b/src/Avalonia.Base/StyledPropertyBase.cs index 526b1175b4..16ba571a5a 100644 --- a/src/Avalonia.Base/StyledPropertyBase.cs +++ b/src/Avalonia.Base/StyledPropertyBase.cs @@ -4,8 +4,6 @@ using Avalonia.Data; using Avalonia.Reactive; using Avalonia.Utilities; -#nullable enable - namespace Avalonia { /// @@ -207,7 +205,7 @@ namespace Avalonia if (v.HasValue) { - return o.SetValue(this, (TValue)v.Value, priority); + return o.SetValue(this, (TValue)v.Value!, priority); } else if (v.Type == BindingValueType.UnsetValue) { diff --git a/src/Avalonia.Base/StyledPropertyMetadata`1.cs b/src/Avalonia.Base/StyledPropertyMetadata`1.cs index a9f7eb38de..0a38960228 100644 --- a/src/Avalonia.Base/StyledPropertyMetadata`1.cs +++ b/src/Avalonia.Base/StyledPropertyMetadata`1.cs @@ -1,8 +1,6 @@ using System; using Avalonia.Data; -#nullable enable - namespace Avalonia { /// diff --git a/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs b/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs index 326d1a3f53..b991f9eb40 100644 --- a/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs +++ b/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs @@ -28,13 +28,13 @@ namespace Avalonia.Threading } /// - public override void Post(SendOrPostCallback d, object state) + public override void Post(SendOrPostCallback d, object? state) { Dispatcher.UIThread.Post(() => d(state), DispatcherPriority.Send); } /// - public override void Send(SendOrPostCallback d, object state) + public override void Send(SendOrPostCallback d, object? state) { if (Dispatcher.UIThread.CheckAccess()) d(state); diff --git a/src/Avalonia.Base/Threading/Dispatcher.cs b/src/Avalonia.Base/Threading/Dispatcher.cs index fe2cec11f0..949c11fbe0 100644 --- a/src/Avalonia.Base/Threading/Dispatcher.cs +++ b/src/Avalonia.Base/Threading/Dispatcher.cs @@ -15,12 +15,12 @@ namespace Avalonia.Threading public class Dispatcher : IDispatcher { private readonly JobRunner _jobRunner; - private IPlatformThreadingInterface _platform; + private IPlatformThreadingInterface? _platform; public static Dispatcher UIThread { get; } = new Dispatcher(AvaloniaLocator.Current.GetService()); - public Dispatcher(IPlatformThreadingInterface platform) + public Dispatcher(IPlatformThreadingInterface? platform) { _platform = platform; _jobRunner = new JobRunner(platform); @@ -57,6 +57,10 @@ namespace Avalonia.Threading public void MainLoop(CancellationToken cancellationToken) { var platform = AvaloniaLocator.Current.GetService(); + + if (platform is null) + throw new InvalidOperationException("Unable to locate IPlatformThreadingInterface"); + cancellationToken.Register(() => platform.Signal(DispatcherPriority.Send)); platform.RunLoop(cancellationToken); } @@ -78,35 +82,35 @@ namespace Avalonia.Threading /// public Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal) { - Contract.Requires(action != null); + _ = action ?? throw new ArgumentNullException(nameof(action)); return _jobRunner.InvokeAsync(action, priority); } /// public Task InvokeAsync(Func function, DispatcherPriority priority = DispatcherPriority.Normal) { - Contract.Requires(function != null); + _ = function ?? throw new ArgumentNullException(nameof(function)); return _jobRunner.InvokeAsync(function, priority); } /// public Task InvokeAsync(Func function, DispatcherPriority priority = DispatcherPriority.Normal) { - Contract.Requires(function != null); + _ = function ?? throw new ArgumentNullException(nameof(function)); return _jobRunner.InvokeAsync(function, priority).Unwrap(); } /// public Task InvokeAsync(Func> function, DispatcherPriority priority = DispatcherPriority.Normal) { - Contract.Requires(function != null); + _ = function ?? throw new ArgumentNullException(nameof(function)); return _jobRunner.InvokeAsync(function, priority).Unwrap(); } /// public void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal) { - Contract.Requires(action != null); + _ = action ?? throw new ArgumentNullException(nameof(action)); _jobRunner.Post(action, priority); } @@ -142,4 +146,4 @@ namespace Avalonia.Threading } } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Threading/DispatcherTimer.cs b/src/Avalonia.Base/Threading/DispatcherTimer.cs index 56cde9738e..93023b90a5 100644 --- a/src/Avalonia.Base/Threading/DispatcherTimer.cs +++ b/src/Avalonia.Base/Threading/DispatcherTimer.cs @@ -9,7 +9,7 @@ namespace Avalonia.Threading /// public class DispatcherTimer { - private IDisposable _timer; + private IDisposable? _timer; private readonly DispatcherPriority _priority; @@ -58,7 +58,7 @@ namespace Avalonia.Threading /// /// Raised when the timer ticks. /// - public event EventHandler Tick; + public event EventHandler? Tick; /// /// Gets or sets the interval at which the timer ticks. @@ -108,7 +108,7 @@ namespace Avalonia.Threading /// /// Gets or sets user-defined data associated with the timer. /// - public object Tag + public object? Tag { get; set; @@ -176,7 +176,7 @@ namespace Avalonia.Threading { if (!IsEnabled) { - IPlatformThreadingInterface threading = AvaloniaLocator.Current.GetService(); + var threading = AvaloniaLocator.Current.GetService(); if (threading == null) { @@ -194,7 +194,7 @@ namespace Avalonia.Threading { if (IsEnabled) { - _timer.Dispose(); + _timer!.Dispose(); _timer = null; } } @@ -210,4 +210,4 @@ namespace Avalonia.Threading Tick?.Invoke(this, EventArgs.Empty); } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Threading/JobRunner.cs b/src/Avalonia.Base/Threading/JobRunner.cs index 58f623fb3f..eb7c5e1af3 100644 --- a/src/Avalonia.Base/Threading/JobRunner.cs +++ b/src/Avalonia.Base/Threading/JobRunner.cs @@ -11,12 +11,12 @@ namespace Avalonia.Threading /// internal class JobRunner { - private IPlatformThreadingInterface _platform; + private IPlatformThreadingInterface? _platform; private readonly Queue[] _queues = Enumerable.Range(0, (int) DispatcherPriority.MaxValue + 1) .Select(_ => new Queue()).ToArray(); - public JobRunner(IPlatformThreadingInterface platform) + public JobRunner(IPlatformThreadingInterface? platform) { _platform = platform; } @@ -48,7 +48,7 @@ namespace Avalonia.Threading { var job = new Job(action, priority, false); AddJob(job); - return job.Task; + return job.Task!; } /// @@ -96,7 +96,7 @@ namespace Avalonia.Threading _platform?.Signal(job.Priority); } - private IJob GetNextJob(DispatcherPriority minimumPriority) + private IJob? GetNextJob(DispatcherPriority minimumPriority) { for (int c = (int) DispatcherPriority.MaxValue; c >= (int) minimumPriority; c--) { @@ -135,7 +135,7 @@ namespace Avalonia.Threading /// /// The task completion source. /// - private readonly TaskCompletionSource _taskCompletionSource; + private readonly TaskCompletionSource? _taskCompletionSource; /// /// Initializes a new instance of the class. @@ -147,7 +147,7 @@ namespace Avalonia.Threading { _action = action; Priority = priority; - _taskCompletionSource = throwOnUiThread ? null : new TaskCompletionSource(); + _taskCompletionSource = throwOnUiThread ? null : new TaskCompletionSource(); } /// @@ -156,7 +156,7 @@ namespace Avalonia.Threading /// /// The task. /// - public Task Task => _taskCompletionSource?.Task; + public Task? Task => _taskCompletionSource?.Task; /// void IJob.Run() diff --git a/src/Avalonia.Base/Utilities/AvaloniaPropertyValueStore.cs b/src/Avalonia.Base/Utilities/AvaloniaPropertyValueStore.cs index c513f75962..cbe3771577 100644 --- a/src/Avalonia.Base/Utilities/AvaloniaPropertyValueStore.cs +++ b/src/Avalonia.Base/Utilities/AvaloniaPropertyValueStore.cs @@ -2,8 +2,6 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -#nullable enable - namespace Avalonia.Utilities { /// diff --git a/src/Avalonia.Base/Utilities/AvaloniaResourcesIndex.cs b/src/Avalonia.Base/Utilities/AvaloniaResourcesIndex.cs index ed5bc697b0..09e408cb42 100644 --- a/src/Avalonia.Base/Utilities/AvaloniaResourcesIndex.cs +++ b/src/Avalonia.Base/Utilities/AvaloniaResourcesIndex.cs @@ -7,8 +7,6 @@ using System.Runtime.Serialization.Json; using System.Xml.Linq; using System.Linq; -// ReSharper disable AssignNullToNotNullAttribute - namespace Avalonia.Utilities { #if !BUILDTASK @@ -24,14 +22,14 @@ namespace Avalonia.Utilities throw new Exception("Resources index format version is not known"); var assetDoc = XDocument.Load(stream); - XNamespace assetNs = assetDoc.Root.Attribute("xmlns").Value; + XNamespace assetNs = assetDoc.Root!.Attribute("xmlns")!.Value; List entries= - (from entry in assetDoc.Root.Element(assetNs + "Entries").Elements(assetNs + "AvaloniaResourcesIndexEntry") + (from entry in assetDoc.Root.Element(assetNs + "Entries")!.Elements(assetNs + "AvaloniaResourcesIndexEntry") select new AvaloniaResourcesIndexEntry { - Path = entry.Element(assetNs + "Path").Value, - Offset = int.Parse(entry.Element(assetNs + "Offset").Value), - Size = int.Parse(entry.Element(assetNs + "Size").Value) + Path = entry.Element(assetNs + "Path")!.Value, + Offset = int.Parse(entry.Element(assetNs + "Offset")!.Value), + Size = int.Parse(entry.Element(assetNs + "Size")!.Value) }).ToList(); return entries; @@ -95,7 +93,7 @@ namespace Avalonia.Utilities class AvaloniaResourcesIndexEntry { [DataMember] - public string Path { get; set; } + public string? Path { get; set; } [DataMember] public int Offset { get; set; } diff --git a/src/Avalonia.Base/Utilities/DisposableLock.cs b/src/Avalonia.Base/Utilities/DisposableLock.cs index b06e97da00..c6f01b24fa 100644 --- a/src/Avalonia.Base/Utilities/DisposableLock.cs +++ b/src/Avalonia.Base/Utilities/DisposableLock.cs @@ -11,7 +11,7 @@ namespace Avalonia.Utilities /// Tries to take a lock /// /// IDisposable if succeeded to obtain the lock - public IDisposable TryLock() + public IDisposable? TryLock() { if (Monitor.TryEnter(_lock)) return new UnlockDisposable(_lock); @@ -29,7 +29,7 @@ namespace Avalonia.Utilities private sealed class UnlockDisposable : IDisposable { - private object _lock; + private object? _lock; public UnlockDisposable(object @lock) { @@ -38,7 +38,7 @@ namespace Avalonia.Utilities public void Dispose() { - object @lock = Interlocked.Exchange(ref _lock, null); + object? @lock = Interlocked.Exchange(ref _lock, null); if (@lock != null) { diff --git a/src/Avalonia.Base/Utilities/IAvaloniaPropertyVisitor.cs b/src/Avalonia.Base/Utilities/IAvaloniaPropertyVisitor.cs index 4b889eb129..6a8df91b81 100644 --- a/src/Avalonia.Base/Utilities/IAvaloniaPropertyVisitor.cs +++ b/src/Avalonia.Base/Utilities/IAvaloniaPropertyVisitor.cs @@ -1,6 +1,4 @@ -#nullable enable - -namespace Avalonia.Utilities +namespace Avalonia.Utilities { /// /// A visitor to resolve an untyped to a typed property. diff --git a/src/Avalonia.Base/Utilities/IWeakSubscriber.cs b/src/Avalonia.Base/Utilities/IWeakSubscriber.cs index a2907eea2e..613c931165 100644 --- a/src/Avalonia.Base/Utilities/IWeakSubscriber.cs +++ b/src/Avalonia.Base/Utilities/IWeakSubscriber.cs @@ -13,6 +13,6 @@ namespace Avalonia.Utilities /// /// The event sender. /// The event arguments. - void OnEvent(object sender, T e); + void OnEvent(object? sender, T e); } } diff --git a/src/Avalonia.Base/Utilities/ImmutableReadOnlyListStructEnumerator.cs b/src/Avalonia.Base/Utilities/ImmutableReadOnlyListStructEnumerator.cs index a1246c57b5..90d1c52ff5 100644 --- a/src/Avalonia.Base/Utilities/ImmutableReadOnlyListStructEnumerator.cs +++ b/src/Avalonia.Base/Utilities/ImmutableReadOnlyListStructEnumerator.cs @@ -7,21 +7,18 @@ namespace Avalonia.Utilities { private readonly IReadOnlyList _readOnlyList; private int _pos; + private T? _current; public ImmutableReadOnlyListStructEnumerator(IReadOnlyList readOnlyList) { _readOnlyList = readOnlyList; _pos = -1; - Current = default; + _current = default; } - public T Current - { - get; - private set; - } + public T Current => _current!; - object IEnumerator.Current => Current; + object? IEnumerator.Current => Current; public void Dispose() { } @@ -32,7 +29,7 @@ namespace Avalonia.Utilities return false; } - Current = _readOnlyList[++_pos]; + _current = _readOnlyList[++_pos]; return true; @@ -42,7 +39,7 @@ namespace Avalonia.Utilities { _pos = -1; - Current = default; + _current = default; } } } diff --git a/src/Avalonia.Base/Utilities/NonPumpingLockHelper.cs b/src/Avalonia.Base/Utilities/NonPumpingLockHelper.cs index fd4e5d2ace..4cc1ba5190 100644 --- a/src/Avalonia.Base/Utilities/NonPumpingLockHelper.cs +++ b/src/Avalonia.Base/Utilities/NonPumpingLockHelper.cs @@ -6,9 +6,9 @@ namespace Avalonia.Utilities { public interface IHelperImpl { - IDisposable Use(); + IDisposable? Use(); } - public static IDisposable Use() => AvaloniaLocator.Current.GetService()?.Use(); + public static IDisposable? Use() => AvaloniaLocator.Current.GetService()?.Use(); } } diff --git a/src/Avalonia.Base/Utilities/Ref.cs b/src/Avalonia.Base/Utilities/Ref.cs index c9ebeefebc..7209f02720 100644 --- a/src/Avalonia.Base/Utilities/Ref.cs +++ b/src/Avalonia.Base/Utilities/Ref.cs @@ -82,7 +82,7 @@ namespace Avalonia.Utilities class RefCounter { - private IDisposable _item; + private IDisposable? _item; private volatile int _refs; public RefCounter(IDisposable item) @@ -120,7 +120,7 @@ namespace Avalonia.Utilities { if (old == 1) { - _item.Dispose(); + _item?.Dispose(); _item = null; } break; @@ -134,7 +134,7 @@ namespace Avalonia.Utilities class Ref : CriticalFinalizerObject, IRef where T : class { - private T _item; + private T? _item; private RefCounter _counter; private object _lock = new object(); @@ -168,7 +168,7 @@ namespace Avalonia.Utilities { lock (_lock) { - return _item; + return _item!; } } } @@ -206,4 +206,4 @@ namespace Avalonia.Utilities } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Utilities/SingleOrDictionary.cs b/src/Avalonia.Base/Utilities/SingleOrDictionary.cs index 140da6750d..00cc5864f5 100644 --- a/src/Avalonia.Base/Utilities/SingleOrDictionary.cs +++ b/src/Avalonia.Base/Utilities/SingleOrDictionary.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; namespace Avalonia.Utilities @@ -11,9 +12,10 @@ namespace Avalonia.Utilities /// The type of the key. /// The type of the value. public class SingleOrDictionary : IEnumerable> + where TKey : notnull { private KeyValuePair? _singleValue; - private Dictionary dictionary; + private Dictionary? dictionary; public void Add(TKey key, TValue value) { @@ -34,7 +36,7 @@ namespace Avalonia.Utilities } } - public bool TryGetValue(TKey key, out TValue value) + public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) { if (dictionary == null) { @@ -120,7 +122,7 @@ namespace Avalonia.Utilities } } - object IEnumerator.Current => Current; + object? IEnumerator.Current => Current; public void Dispose() { diff --git a/src/Avalonia.Base/Utilities/SingleOrQueue.cs b/src/Avalonia.Base/Utilities/SingleOrQueue.cs index 643b0ba04d..99a92f6788 100644 --- a/src/Avalonia.Base/Utilities/SingleOrQueue.cs +++ b/src/Avalonia.Base/Utilities/SingleOrQueue.cs @@ -9,8 +9,8 @@ namespace Avalonia.Utilities /// The type of items held in the queue. public class SingleOrQueue { - private T _head; - private Queue _tail; + private T? _head; + private Queue? _tail; private Queue Tail => _tail ?? (_tail = new Queue()); @@ -51,7 +51,7 @@ namespace Avalonia.Utilities Empty = true; } - return result; + return result!; } } } diff --git a/src/Avalonia.Base/Utilities/StringTokenizer.cs b/src/Avalonia.Base/Utilities/StringTokenizer.cs index 24e99febb9..748eb09209 100644 --- a/src/Avalonia.Base/Utilities/StringTokenizer.cs +++ b/src/Avalonia.Base/Utilities/StringTokenizer.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using static System.Char; @@ -14,19 +15,19 @@ namespace Avalonia.Utilities private readonly string _s; private readonly int _length; private readonly char _separator; - private readonly string _exceptionMessage; + private readonly string? _exceptionMessage; private readonly IFormatProvider _formatProvider; private int _index; private int _tokenIndex; private int _tokenLength; - public StringTokenizer(string s, IFormatProvider formatProvider, string exceptionMessage = null) + public StringTokenizer(string s, IFormatProvider formatProvider, string? exceptionMessage = null) : this(s, GetSeparatorFromFormatProvider(formatProvider), exceptionMessage) { _formatProvider = formatProvider; } - public StringTokenizer(string s, char separator = DefaultSeparatorChar, string exceptionMessage = null) + public StringTokenizer(string s, char separator = DefaultSeparatorChar, string? exceptionMessage = null) { _s = s ?? throw new ArgumentNullException(nameof(s)); _length = s?.Length ?? 0; @@ -43,7 +44,7 @@ namespace Avalonia.Utilities } } - public string CurrentToken => _tokenIndex < 0 ? null : _s.Substring(_tokenIndex, _tokenLength); + public string? CurrentToken => _tokenIndex < 0 ? null : _s.Substring(_tokenIndex, _tokenLength); public void Dispose() { @@ -101,7 +102,7 @@ namespace Avalonia.Utilities return result; } - public bool TryReadString(out string result, char? separator = null) + public bool TryReadString([MaybeNullWhen(false)] out string result, char? separator = null) { var success = TryReadToken(separator ?? _separator); result = CurrentToken; @@ -219,4 +220,4 @@ namespace Avalonia.Utilities return c; } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Utilities/SynchronousCompletionAsyncResult.cs b/src/Avalonia.Base/Utilities/SynchronousCompletionAsyncResult.cs index f321b2b2f1..fb8d3ed224 100644 --- a/src/Avalonia.Base/Utilities/SynchronousCompletionAsyncResult.cs +++ b/src/Avalonia.Base/Utilities/SynchronousCompletionAsyncResult.cs @@ -10,8 +10,8 @@ namespace Avalonia.Utilities /// public struct SynchronousCompletionAsyncResult : INotifyCompletion { - private readonly SynchronousCompletionAsyncResultSource _source; - private readonly T _result; + private readonly SynchronousCompletionAsyncResultSource? _source; + private readonly T? _result; private readonly bool _isValid; internal SynchronousCompletionAsyncResult(SynchronousCompletionAsyncResultSource source) { @@ -44,7 +44,7 @@ namespace Avalonia.Utilities { if (!_isValid) ThrowNotInitialized(); - return _source == null ? _result : _source.Result; + return _source == null ? _result! : _source.Result; } @@ -67,15 +67,15 @@ namespace Avalonia.Utilities /// public class SynchronousCompletionAsyncResultSource { - private T _result; + private T? _result; internal bool IsCompleted { get; private set; } public SynchronousCompletionAsyncResult AsyncResult => new SynchronousCompletionAsyncResult(this); internal T Result => IsCompleted ? - _result : + _result! : throw new InvalidOperationException("Asynchronous operation is not yet completed"); - private List _continuations; + private List? _continuations; internal void OnCompleted(Action continuation) { diff --git a/src/Avalonia.Base/Utilities/TypeUtilities.cs b/src/Avalonia.Base/Utilities/TypeUtilities.cs index 0978308ef6..d29335d3d0 100644 --- a/src/Avalonia.Base/Utilities/TypeUtilities.cs +++ b/src/Avalonia.Base/Utilities/TypeUtilities.cs @@ -112,7 +112,7 @@ namespace Avalonia.Utilities /// The type to cast to /// The value to check if cast possible /// True if the cast is possible, otherwise false. - public static bool CanCast(object value) + public static bool CanCast(object? value) { return value is T || (value is null && AcceptsNull()); } @@ -125,7 +125,7 @@ namespace Avalonia.Utilities /// The culture to use. /// If successful, contains the convert value. /// True if the cast was successful, otherwise false. - public static bool TryConvert(Type to, object value, CultureInfo culture, out object result) + public static bool TryConvert(Type to, object? value, CultureInfo? culture, out object? result) { if (value == null) { @@ -167,9 +167,9 @@ namespace Avalonia.Utilities { result = null; - if (TryConvert(Enum.GetUnderlyingType(toUnderl), value, culture, out object enumValue)) + if (TryConvert(Enum.GetUnderlyingType(toUnderl), value, culture, out var enumValue)) { - result = Enum.ToObject(toUnderl, enumValue); + result = Enum.ToObject(toUnderl, enumValue!); return true; } } @@ -244,7 +244,7 @@ namespace Avalonia.Utilities /// The value to convert. /// If successful, contains the converted value. /// True if the convert was successful, otherwise false. - public static bool TryConvertImplicit(Type to, object value, out object result) + public static bool TryConvertImplicit(Type to, object? value, out object? result) { if (value == null) { @@ -306,9 +306,9 @@ namespace Avalonia.Utilities /// The type to convert to.. /// The culture to use. /// A value of . - public static object ConvertOrDefault(object value, Type type, CultureInfo culture) + public static object? ConvertOrDefault(object? value, Type type, CultureInfo culture) { - return TryConvert(type, value, culture, out object result) ? result : Default(type); + return TryConvert(type, value, culture, out var result) ? result : Default(type); } /// @@ -318,16 +318,16 @@ namespace Avalonia.Utilities /// The value to convert. /// The type to convert to. /// A value of . - public static object ConvertImplicitOrDefault(object value, Type type) + public static object? ConvertImplicitOrDefault(object? value, Type type) { - return TryConvertImplicit(type, value, out object result) ? result : Default(type); + return TryConvertImplicit(type, value, out var result) ? result : Default(type); } public static T ConvertImplicit(object value) { if (TryConvertImplicit(typeof(T), value, out var result)) { - return (T)result; + return (T)result!; } throw new InvalidCastException( @@ -339,7 +339,7 @@ namespace Avalonia.Utilities /// /// The type. /// The default value. - public static object Default(Type type) + public static object? Default(Type type) { if (type.IsValueType) { @@ -367,7 +367,7 @@ namespace Avalonia.Utilities return false; } - Type underlyingType = Nullable.GetUnderlyingType(type); + var underlyingType = Nullable.GetUnderlyingType(type); if (underlyingType != null) { @@ -391,7 +391,7 @@ namespace Avalonia.Utilities return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); } - private static MethodInfo FindTypeConversionOperatorMethod(Type fromType, Type toType, OperatorType operatorType) + private static MethodInfo? FindTypeConversionOperatorMethod(Type fromType, Type toType, OperatorType operatorType) { const string implicitName = "op_Implicit"; const string explicitName = "op_Explicit"; diff --git a/src/Avalonia.Base/Utilities/ValueSingleOrList.cs b/src/Avalonia.Base/Utilities/ValueSingleOrList.cs index dc32cedb76..c1e5d912a8 100644 --- a/src/Avalonia.Base/Utilities/ValueSingleOrList.cs +++ b/src/Avalonia.Base/Utilities/ValueSingleOrList.cs @@ -58,7 +58,7 @@ namespace Avalonia.Utilities List.Add(Single); List.Add(value); - Single = default; + Single = default!; } } } @@ -81,7 +81,7 @@ namespace Avalonia.Utilities if (EqualityComparer.Default.Equals(Single, value)) { - Single = default; + Single = default!; _isSingleSet = false; diff --git a/src/Avalonia.Base/Utilities/WeakEventHandlerManager.cs b/src/Avalonia.Base/Utilities/WeakEventHandlerManager.cs index 28c9dcc262..4605aba800 100644 --- a/src/Avalonia.Base/Utilities/WeakEventHandlerManager.cs +++ b/src/Avalonia.Base/Utilities/WeakEventHandlerManager.cs @@ -23,10 +23,11 @@ namespace Avalonia.Utilities public static void Subscribe(TTarget target, string eventName, EventHandler subscriber) where TEventArgs : EventArgs where TSubscriber : class { + _ = target ?? throw new ArgumentNullException(nameof(target)); + var dic = SubscriptionTypeStorage.Subscribers.GetOrCreateValue(target); - Subscription sub; - if (!dic.TryGetValue(eventName, out sub)) + if (!dic.TryGetValue(eventName, out var sub)) { dic[eventName] = sub = new Subscription(dic, typeof(TTarget), target, eventName); } @@ -45,13 +46,9 @@ namespace Avalonia.Utilities public static void Unsubscribe(object target, string eventName, EventHandler subscriber) where TEventArgs : EventArgs where TSubscriber : class { - SubscriptionDic dic; - - if (SubscriptionTypeStorage.Subscribers.TryGetValue(target, out dic)) + if (SubscriptionTypeStorage.Subscribers.TryGetValue(target, out var dic)) { - Subscription sub; - - if (dic.TryGetValue(eventName, out sub)) + if (dic.TryGetValue(eventName, out var sub)) { sub.Remove(subscriber); } @@ -100,11 +97,14 @@ namespace Avalonia.Utilities _sdic = sdic; _target = target; _eventName = eventName; - Dictionary evDic; - if (!Accessors.TryGetValue(targetType, out evDic)) + if (!Accessors.TryGetValue(targetType, out var evDic)) Accessors[targetType] = evDic = new Dictionary(); - if (!evDic.TryGetValue(eventName, out _info)) + if (evDic.TryGetValue(eventName, out var info)) + { + _info = info; + } + else { var ev = targetType.GetRuntimeEvents().FirstOrDefault(x => x.Name == eventName); @@ -118,13 +118,13 @@ namespace Avalonia.Utilities } var del = new Action(OnEvent); - _delegate = del.GetMethodInfo().CreateDelegate(_info.EventHandlerType, del.Target); - _info.AddMethod.Invoke(target, new[] { _delegate }); + _delegate = del.GetMethodInfo().CreateDelegate(_info.EventHandlerType!, del.Target); + _info.AddMethod!.Invoke(target, new[] { _delegate }); } void Destroy() { - _info.RemoveMethod.Invoke(_target, new[] { _delegate }); + _info.RemoveMethod!.Invoke(_target, new[] { _delegate }); _sdic.Remove(_eventName); } @@ -141,7 +141,7 @@ namespace Avalonia.Utilities MethodInfo method = s.Method; - var subscriber = (TSubscriber)s.Target; + var subscriber = (TSubscriber)s.Target!; if (!s_Callers.TryGetValue(method, out var caller)) s_Callers[method] = caller = (CallerDelegate)Delegate.CreateDelegate(typeof(CallerDelegate), null, method); @@ -161,7 +161,7 @@ namespace Avalonia.Utilities { var reference = _data[c].Subscriber; - if (reference != null && reference.TryGetTarget(out TSubscriber instance) && Equals(instance, s.Target)) + if (reference != null && reference.TryGetTarget(out var instance) && Equals(instance, s.Target)) { _data[c] = default; removed = true; @@ -181,7 +181,7 @@ namespace Avalonia.Utilities { var r = _data[c]; - TSubscriber target = null; + TSubscriber? target = null; r.Subscriber?.TryGetTarget(out target); @@ -208,8 +208,7 @@ namespace Avalonia.Utilities for(var c=0; c<_count; c++) { var r = _data[c].Subscriber; - TSubscriber sub; - if (r.TryGetTarget(out sub)) + if (r.TryGetTarget(out var sub)) { _data[c].Caller(sub, sender, eventArgs); } diff --git a/src/Avalonia.Base/Utilities/WeakObservable.cs b/src/Avalonia.Base/Utilities/WeakObservable.cs index 20e01c751f..52edc7ad1a 100644 --- a/src/Avalonia.Base/Utilities/WeakObservable.cs +++ b/src/Avalonia.Base/Utilities/WeakObservable.cs @@ -23,8 +23,8 @@ namespace Avalonia.Utilities string eventName) where TEventArgs : EventArgs { - Contract.Requires(target != null); - Contract.Requires(eventName != null); + _ = target ?? throw new ArgumentNullException(nameof(target)); + _ = eventName ?? throw new ArgumentNullException(nameof(eventName)); return Observable.Create>(observer => { @@ -43,7 +43,7 @@ namespace Avalonia.Utilities _observer = observer; } - public void OnEvent(object sender, TEventArgs e) + public void OnEvent(object? sender, TEventArgs e) { _observer.OnNext(new EventPattern(sender, e)); } diff --git a/src/Avalonia.Base/Utilities/WeakSubscriptionManager.cs b/src/Avalonia.Base/Utilities/WeakSubscriptionManager.cs index 65128e7932..66223e513d 100644 --- a/src/Avalonia.Base/Utilities/WeakSubscriptionManager.cs +++ b/src/Avalonia.Base/Utilities/WeakSubscriptionManager.cs @@ -22,10 +22,11 @@ namespace Avalonia.Utilities public static void Subscribe(TTarget target, string eventName, IWeakSubscriber subscriber) where TEventArgs : EventArgs { + _ = target ?? throw new ArgumentNullException(nameof(target)); + var dic = SubscriptionTypeStorage.Subscribers.GetOrCreateValue(target); - Subscription sub; - if (!dic.TryGetValue(eventName, out sub)) + if (!dic.TryGetValue(eventName, out var sub)) { dic[eventName] = sub = new Subscription(dic, typeof(TTarget), target, eventName); } @@ -43,13 +44,9 @@ namespace Avalonia.Utilities public static void Unsubscribe(object target, string eventName, IWeakSubscriber subscriber) where T : EventArgs { - SubscriptionDic dic; - - if (SubscriptionTypeStorage.Subscribers.TryGetValue(target, out dic)) + if (SubscriptionTypeStorage.Subscribers.TryGetValue(target, out var dic)) { - Subscription sub; - - if (dic.TryGetValue(eventName, out sub)) + if (dic.TryGetValue(eventName, out var sub)) { sub.Remove(subscriber); } @@ -79,7 +76,7 @@ namespace Avalonia.Utilities private readonly string _eventName; private readonly Delegate _delegate; - private WeakReference>[] _data = new WeakReference>[16]; + private WeakReference>?[] _data = new WeakReference>?[16]; private int _count = 0; public Subscription(SubscriptionDic sdic, Type targetType, object target, string eventName) @@ -87,11 +84,14 @@ namespace Avalonia.Utilities _sdic = sdic; _target = target; _eventName = eventName; - Dictionary evDic; - if (!Accessors.TryGetValue(targetType, out evDic)) + if (!Accessors.TryGetValue(targetType, out var evDic)) Accessors[targetType] = evDic = new Dictionary(); - if (!evDic.TryGetValue(eventName, out _info)) + if (evDic.TryGetValue(eventName, out var info)) + { + _info = info; + } + else { var ev = targetType.GetRuntimeEvents().FirstOrDefault(x => x.Name == eventName); @@ -105,13 +105,13 @@ namespace Avalonia.Utilities } var del = new Action(OnEvent); - _delegate = del.GetMethodInfo().CreateDelegate(_info.EventHandlerType, del.Target); - _info.AddMethod.Invoke(target, new[] { _delegate }); + _delegate = del.GetMethodInfo().CreateDelegate(_info.EventHandlerType!, del.Target); + _info.AddMethod!.Invoke(target, new[] { _delegate }); } void Destroy() { - _info.RemoveMethod.Invoke(_target, new[] { _delegate }); + _info.RemoveMethod!.Invoke(_target, new[] { _delegate }); _sdic.Remove(_eventName); } @@ -120,11 +120,11 @@ namespace Avalonia.Utilities if (_count == _data.Length) { //Extend capacity - var ndata = new WeakReference>[_data.Length*2]; + var ndata = new WeakReference>?[_data.Length*2]; Array.Copy(_data, ndata, _data.Length); _data = ndata; } - _data[_count] = s; + _data[_count] = s!; _count++; } @@ -135,7 +135,7 @@ namespace Avalonia.Utilities for (int c = 0; c < _count; ++c) { var reference = _data[c]; - IWeakSubscriber instance; + IWeakSubscriber? instance; if (reference != null && reference.TryGetTarget(out instance) && instance == s) { @@ -179,8 +179,7 @@ namespace Avalonia.Utilities for(var c=0; c<_count; c++) { var r = _data[c]; - IWeakSubscriber sub; - if (r.TryGetTarget(out sub)) + if (r?.TryGetTarget(out var sub) == true) sub.OnEvent(sender, eventArgs); else needCompact = true; diff --git a/src/Avalonia.Base/Utilities/WeakTimer.cs b/src/Avalonia.Base/Utilities/WeakTimer.cs index a4d18ca13b..ab784fca4d 100644 --- a/src/Avalonia.Base/Utilities/WeakTimer.cs +++ b/src/Avalonia.Base/Utilities/WeakTimer.cs @@ -23,8 +23,7 @@ namespace Avalonia.Utilities private void OnTick() { - IWeakTimerSubscriber subscriber; - if (!_subscriber.TryGetTarget(out subscriber) || !subscriber.Tick()) + if (!_subscriber.TryGetTarget(out var subscriber) || !subscriber.Tick()) Stop(); } diff --git a/src/Avalonia.Base/ValueStore.cs b/src/Avalonia.Base/ValueStore.cs index 9f036fbd34..6b7b598086 100644 --- a/src/Avalonia.Base/ValueStore.cs +++ b/src/Avalonia.Base/ValueStore.cs @@ -5,8 +5,6 @@ using Avalonia.Data; using Avalonia.PropertyStore; using Avalonia.Utilities; -#nullable enable - namespace Avalonia { /// diff --git a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj index 47b4451817..7884f13ddb 100644 --- a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj +++ b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj @@ -89,6 +89,7 @@ Markup/%(RecursiveDir)%(FileName)%(Extension) + diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index df55d9e250..584c3db23b 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -38,8 +38,8 @@ namespace Avalonia /// private DataTemplates? _dataTemplates; - private readonly Lazy _clipboard = - new Lazy(() => (IClipboard)AvaloniaLocator.Current.GetService(typeof(IClipboard))); + private readonly Lazy _clipboard = + new Lazy(() => (IClipboard?)AvaloniaLocator.Current.GetService(typeof(IClipboard))); private readonly Styler _styler = new Styler(); private Styles? _styles; private IResourceDictionary? _resources; @@ -85,7 +85,7 @@ namespace Avalonia /// /// The current instance of the class. /// - public static Application Current + public static Application? Current { get { return AvaloniaLocator.Current.GetService(); } } @@ -125,7 +125,7 @@ namespace Avalonia /// /// Gets the application clipboard. /// - public IClipboard Clipboard => _clipboard.Value; + public IClipboard? Clipboard => _clipboard.Value; /// /// Gets the application's global resource dictionary. diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs index d8fd864e9b..d94bff37df 100644 --- a/src/Avalonia.Controls/ContextMenu.cs +++ b/src/Avalonia.Controls/ContextMenu.cs @@ -417,7 +417,7 @@ namespace Avalonia.Controls { var keymap = AvaloniaLocator.Current.GetService(); - if (keymap.OpenContextMenu.Any(k => k.Matches(e)) + if (keymap?.OpenContextMenu.Any(k => k.Matches(e)) == true && !CancelClosing()) { Close(); diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index fe731b9d4b..441421181c 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -248,7 +248,11 @@ namespace Avalonia.Controls if (e.Source == this && !e.Handled) { - var keymap = AvaloniaLocator.Current.GetService().OpenContextMenu; + var keymap = AvaloniaLocator.Current.GetService()?.OpenContextMenu; + + if (keymap is null) + return; + var matches = false; for (var index = 0; index < keymap.Count; index++) diff --git a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs index d78ddd4169..a08b82f512 100644 --- a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs +++ b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs @@ -399,7 +399,7 @@ namespace Avalonia.Controls.Primitives { var keymap = AvaloniaLocator.Current.GetService(); - if (keymap.OpenContextMenu.Any(k => k.Matches(e))) + if (keymap?.OpenContextMenu.Any(k => k.Matches(e)) == true) { e.Handled = HideCore(); } diff --git a/src/Avalonia.Controls/MaskedTextBox.cs b/src/Avalonia.Controls/MaskedTextBox.cs index a72c617f05..f59cef8223 100644 --- a/src/Avalonia.Controls/MaskedTextBox.cs +++ b/src/Avalonia.Controls/MaskedTextBox.cs @@ -207,9 +207,14 @@ namespace Avalonia.Controls bool Match(List gestures) => gestures.Any(g => g.Matches(e)); - if (Match(keymap.Paste)) + if (keymap is not null && Match(keymap.Paste)) { - var text = await ((IClipboard)AvaloniaLocator.Current.GetService(typeof(IClipboard))).GetTextAsync(); + var clipboard = (IClipboard?)AvaloniaLocator.Current.GetService(typeof(IClipboard)); + + if (clipboard is null) + return; + + var text = await clipboard.GetTextAsync(); if (text == null) return; diff --git a/src/Avalonia.Controls/MenuItem.cs b/src/Avalonia.Controls/MenuItem.cs index b661bcfe33..b843992f04 100644 --- a/src/Avalonia.Controls/MenuItem.cs +++ b/src/Avalonia.Controls/MenuItem.cs @@ -715,7 +715,7 @@ namespace Avalonia.Controls /// /// The service type. /// A service of the requested type. - public object GetService(Type serviceType) + public object? GetService(Type serviceType) { if (serviceType == typeof(IAccessKeyHandler)) { diff --git a/src/Avalonia.Controls/Platform/PlatformManager.cs b/src/Avalonia.Controls/Platform/PlatformManager.cs index acd45ad29a..8e9f64b3ad 100644 --- a/src/Avalonia.Controls/Platform/PlatformManager.cs +++ b/src/Avalonia.Controls/Platform/PlatformManager.cs @@ -8,9 +8,6 @@ namespace Avalonia.Controls.Platform { public static partial class PlatformManager { - static IPlatformSettings GetSettings() - => AvaloniaLocator.Current.GetService(); - static bool s_designerMode; public static IDisposable DesignerMode() diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index 07bc789cc1..80f3d345a6 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -545,6 +545,10 @@ namespace Avalonia.Controls.Primitives if (!e.Handled) { var keymap = AvaloniaLocator.Current.GetService(); + + if (keymap is null) + return; + bool Match(List gestures) => gestures.Any(g => g.Matches(e)); if (ItemCount > 0 && diff --git a/src/Avalonia.Controls/SystemDialog.cs b/src/Avalonia.Controls/SystemDialog.cs index d81926ecc7..3fac10b2d1 100644 --- a/src/Avalonia.Controls/SystemDialog.cs +++ b/src/Avalonia.Controls/SystemDialog.cs @@ -66,8 +66,9 @@ namespace Avalonia.Controls { if(parent == null) throw new ArgumentNullException(nameof(parent)); - return ((await AvaloniaLocator.Current.GetService() - .ShowFileDialogAsync(this, parent)) ?? + var service = AvaloniaLocator.Current.GetService() ?? + throw new InvalidOperationException("Unable to locate ISystemDialogImpl."); + return (await service.ShowFileDialogAsync(this, parent) ?? Array.Empty()).FirstOrDefault(); } } @@ -94,7 +95,9 @@ namespace Avalonia.Controls { if(parent == null) throw new ArgumentNullException(nameof(parent)); - return AvaloniaLocator.Current.GetService().ShowFileDialogAsync(this, parent); + var service = AvaloniaLocator.Current.GetService() ?? + throw new InvalidOperationException("Unable to locate ISystemDialogImpl."); + return service.ShowFileDialogAsync(this, parent); } } @@ -122,7 +125,9 @@ namespace Avalonia.Controls { if(parent == null) throw new ArgumentNullException(nameof(parent)); - return AvaloniaLocator.Current.GetService().ShowFolderDialogAsync(this, parent); + var service = AvaloniaLocator.Current.GetService() ?? + throw new InvalidOperationException("Unable to locate ISystemDialogImpl."); + return service.ShowFolderDialogAsync(this, parent); } } diff --git a/src/Avalonia.Controls/Templates/DataTemplateExtensions.cs b/src/Avalonia.Controls/Templates/DataTemplateExtensions.cs index 2b115aec7e..82a1758c26 100644 --- a/src/Avalonia.Controls/Templates/DataTemplateExtensions.cs +++ b/src/Avalonia.Controls/Templates/DataTemplateExtensions.cs @@ -47,7 +47,7 @@ namespace Avalonia.Controls.Templates currentTemplateHost = currentTemplateHost.LogicalParent; } - IGlobalDataTemplates global = AvaloniaLocator.Current.GetService(); + var global = AvaloniaLocator.Current.GetService(); if (global != null && global.IsDataTemplatesInitialized) { diff --git a/src/Avalonia.Controls/TrayIcon.cs b/src/Avalonia.Controls/TrayIcon.cs index 9fbc2fc8b2..03ffe830ed 100644 --- a/src/Avalonia.Controls/TrayIcon.cs +++ b/src/Avalonia.Controls/TrayIcon.cs @@ -65,7 +65,9 @@ namespace Avalonia.Controls } }); - if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifetime) + var app = Application.Current ?? throw new InvalidOperationException("Application not yet initialized."); + + if (app.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifetime) { lifetime.Exit += Lifetime_Exit; } @@ -186,7 +188,8 @@ namespace Avalonia.Controls private static void Lifetime_Exit(object sender, ControlledApplicationLifetimeExitEventArgs e) { - var trayIcons = GetIcons(Application.Current); + var app = Application.Current ?? throw new InvalidOperationException("Application not yet initialized."); + var trayIcons = GetIcons(app); RemoveIcons(trayIcons); } diff --git a/src/Avalonia.Controls/Utils/CollectionChangedEventManager.cs b/src/Avalonia.Controls/Utils/CollectionChangedEventManager.cs index c26c5e0513..1a190391b7 100644 --- a/src/Avalonia.Controls/Utils/CollectionChangedEventManager.cs +++ b/src/Avalonia.Controls/Utils/CollectionChangedEventManager.cs @@ -107,7 +107,7 @@ namespace Avalonia.Controls.Utils this); } - void IWeakSubscriber.OnEvent(object sender, NotifyCollectionChangedEventArgs e) + void IWeakSubscriber.OnEvent(object? sender, NotifyCollectionChangedEventArgs e) { static void Notify( INotifyCollectionChanged incc, diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs index d0a4ad38c5..74f2f05948 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs @@ -14,7 +14,7 @@ namespace Avalonia.Diagnostics.ViewModels private readonly TreePageViewModel _visualTree; private readonly EventsPageViewModel _events; private readonly IDisposable _pointerOverSubscription; - private ViewModelBase _content; + private ViewModelBase? _content; private int _selectedTab; private string? _focusedControl; private string? _pointerOverElement; @@ -23,10 +23,7 @@ namespace Avalonia.Diagnostics.ViewModels private bool _showFpsOverlay; private bool _freezePopups; -#nullable disable - // Remove "nullable disable" after MemberNotNull will work on our CI. public MainViewModel(TopLevel root) -#nullable restore { _root = root; _logicalTree = new TreePageViewModel(this, LogicalTreeNode.Create(root)); @@ -34,7 +31,9 @@ namespace Avalonia.Diagnostics.ViewModels _events = new EventsPageViewModel(this); UpdateFocusedControl(); - KeyboardDevice.Instance.PropertyChanged += KeyboardPropertyChanged; + + if (KeyboardDevice.Instance is not null) + KeyboardDevice.Instance.PropertyChanged += KeyboardPropertyChanged; SelectedTab = 0; _pointerOverSubscription = root.GetObservable(TopLevel.PointerOverElementProperty) .Subscribe(x => PointerOverElement = x?.GetType().Name); @@ -90,10 +89,9 @@ namespace Avalonia.Diagnostics.ViewModels public ConsoleViewModel Console { get; } - public ViewModelBase Content + public ViewModelBase? Content { get { return _content; } - // [MemberNotNull(nameof(_content))] private set { if (_content is TreePageViewModel oldTree && @@ -185,7 +183,8 @@ namespace Avalonia.Diagnostics.ViewModels public void Dispose() { - KeyboardDevice.Instance.PropertyChanged -= KeyboardPropertyChanged; + if (KeyboardDevice.Instance is not null) + KeyboardDevice.Instance.PropertyChanged -= KeyboardPropertyChanged; _pointerOverSubscription.Dispose(); _logicalTree.Dispose(); _visualTree.Dispose(); @@ -195,7 +194,7 @@ namespace Avalonia.Diagnostics.ViewModels private void UpdateFocusedControl() { - FocusedControl = KeyboardDevice.Instance.FocusedElement?.GetType().Name; + FocusedControl = KeyboardDevice.Instance?.FocusedElement?.GetType().Name; } private void KeyboardPropertyChanged(object? sender, PropertyChangedEventArgs e) diff --git a/src/Avalonia.Input/AccessKeyHandler.cs b/src/Avalonia.Input/AccessKeyHandler.cs index 24feb357b9..a3330b65ae 100644 --- a/src/Avalonia.Input/AccessKeyHandler.cs +++ b/src/Avalonia.Input/AccessKeyHandler.cs @@ -143,7 +143,7 @@ namespace Avalonia.Input { // TODO: Use FocusScopes to store the current element and restore it when context menu is closed. // Save currently focused input element. - _restoreFocusElement = FocusManager.Instance.Current; + _restoreFocusElement = FocusManager.Instance?.Current; // When Alt is pressed without a main menu, or with a closed main menu, show // access key markers in the window (i.e. "_File"). diff --git a/src/Avalonia.Input/FocusManager.cs b/src/Avalonia.Input/FocusManager.cs index 177e6035b0..30301788bc 100644 --- a/src/Avalonia.Input/FocusManager.cs +++ b/src/Avalonia.Input/FocusManager.cs @@ -32,7 +32,7 @@ namespace Avalonia.Input /// /// Gets the instance of the . /// - public static IFocusManager Instance => AvaloniaLocator.Current.GetService(); + public static IFocusManager? Instance => AvaloniaLocator.Current.GetService(); /// /// Gets the currently focused . diff --git a/src/Avalonia.Input/InputElement.cs b/src/Avalonia.Input/InputElement.cs index 26ebf86857..c2197ee64d 100644 --- a/src/Avalonia.Input/InputElement.cs +++ b/src/Avalonia.Input/InputElement.cs @@ -499,7 +499,7 @@ namespace Avalonia.Input if (IsFocused) { - FocusManager.Instance.Focus(null); + FocusManager.Instance?.Focus(null); } } diff --git a/src/Avalonia.Input/InputManager.cs b/src/Avalonia.Input/InputManager.cs index 129b9cf474..f604ff8e74 100644 --- a/src/Avalonia.Input/InputManager.cs +++ b/src/Avalonia.Input/InputManager.cs @@ -17,7 +17,7 @@ namespace Avalonia.Input /// /// Gets the global instance of the input manager. /// - public static IInputManager Instance => AvaloniaLocator.Current.GetService(); + public static IInputManager? Instance => AvaloniaLocator.Current.GetService(); /// public IObservable PreProcess => _preProcess; diff --git a/src/Avalonia.Input/KeyboardDevice.cs b/src/Avalonia.Input/KeyboardDevice.cs index a159b19026..438e3fcadb 100644 --- a/src/Avalonia.Input/KeyboardDevice.cs +++ b/src/Avalonia.Input/KeyboardDevice.cs @@ -14,11 +14,11 @@ namespace Avalonia.Input public event PropertyChangedEventHandler? PropertyChanged; - public static IKeyboardDevice Instance => AvaloniaLocator.Current.GetService(); + public static IKeyboardDevice? Instance => AvaloniaLocator.Current.GetService(); - public IInputManager InputManager => AvaloniaLocator.Current.GetService(); + public IInputManager? InputManager => AvaloniaLocator.Current.GetService(); - public IFocusManager FocusManager => AvaloniaLocator.Current.GetService(); + public IFocusManager? FocusManager => AvaloniaLocator.Current.GetService(); // This should live in the FocusManager, but with the current outdated architecture // the source of truth about the input focus is in KeyboardDevice diff --git a/src/Avalonia.Input/KeyboardNavigationHandler.cs b/src/Avalonia.Input/KeyboardNavigationHandler.cs index b2ebee9ef5..f73203f6b7 100644 --- a/src/Avalonia.Input/KeyboardNavigationHandler.cs +++ b/src/Avalonia.Input/KeyboardNavigationHandler.cs @@ -89,7 +89,7 @@ namespace Avalonia.Input var method = direction == NavigationDirection.Next || direction == NavigationDirection.Previous ? NavigationMethod.Tab : NavigationMethod.Directional; - FocusManager.Instance.Focus(next, method, keyModifiers); + FocusManager.Instance?.Focus(next, method, keyModifiers); } } @@ -100,7 +100,7 @@ namespace Avalonia.Input /// The event args. protected virtual void OnKeyDown(object? sender, KeyEventArgs e) { - var current = FocusManager.Instance.Current; + var current = FocusManager.Instance?.Current; if (current != null && e.Key == Key.Tab) { diff --git a/src/Avalonia.Input/MouseDevice.cs b/src/Avalonia.Input/MouseDevice.cs index 6eccb3f101..a2c43013fd 100644 --- a/src/Avalonia.Input/MouseDevice.cs +++ b/src/Avalonia.Input/MouseDevice.cs @@ -240,7 +240,8 @@ namespace Avalonia.Input if (source != null) { var settings = AvaloniaLocator.Current.GetService(); - var doubleClickTime = settings.DoubleClickTime.TotalMilliseconds; + var doubleClickTime = settings?.DoubleClickTime.TotalMilliseconds ?? 500; + var doubleClickSize = settings?.DoubleClickSize ?? new Size(4, 4); if (!_lastClickRect.Contains(p) || timestamp - _lastClickTime > doubleClickTime) { @@ -250,7 +251,7 @@ namespace Avalonia.Input ++_clickCount; _lastClickTime = timestamp; _lastClickRect = new Rect(p, new Size()) - .Inflate(new Thickness(settings.DoubleClickSize.Width / 2, settings.DoubleClickSize.Height / 2)); + .Inflate(new Thickness(doubleClickSize.Width / 2, doubleClickSize.Height / 2)); _lastMouseDownButton = properties.PointerUpdateKind.GetMouseButton(); var e = new PointerPressedEventArgs(source, _pointer, root, p, timestamp, properties, inputModifiers, _clickCount); source.RaiseEvent(e); diff --git a/src/Avalonia.Visuals/Media/CombinedGeometry.cs b/src/Avalonia.Visuals/Media/CombinedGeometry.cs index 2202030b7a..b76c496c4e 100644 --- a/src/Avalonia.Visuals/Media/CombinedGeometry.cs +++ b/src/Avalonia.Visuals/Media/CombinedGeometry.cs @@ -154,7 +154,8 @@ namespace Avalonia.Media if (g1 is object && g2 is object) { - var factory = AvaloniaLocator.Current.GetService(); + var factory = AvaloniaLocator.Current.GetService() ?? + throw new InvalidOperationException("Unable to locate IPlatformRenderInterface."); return factory.CreateCombinedGeometry(GeometryCombineMode, g1, g2); } else if (GeometryCombineMode == GeometryCombineMode.Intersect) diff --git a/src/Avalonia.Visuals/Media/GeometryGroup.cs b/src/Avalonia.Visuals/Media/GeometryGroup.cs index edbe63d4bb..01d4923456 100644 --- a/src/Avalonia.Visuals/Media/GeometryGroup.cs +++ b/src/Avalonia.Visuals/Media/GeometryGroup.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using Avalonia.Metadata; using Avalonia.Platform; @@ -60,7 +61,8 @@ namespace Avalonia.Media { if (_children?.Count > 0) { - var factory = AvaloniaLocator.Current.GetService(); + var factory = AvaloniaLocator.Current.GetService() ?? + throw new InvalidOperationException("Unable to locate IPlatformRenderInterface."); return factory.CreateGeometryGroup(FillRule, _children); } diff --git a/src/Avalonia.X11/X11CursorFactory.cs b/src/Avalonia.X11/X11CursorFactory.cs index 9c37de86bf..8e5cac1400 100644 --- a/src/Avalonia.X11/X11CursorFactory.cs +++ b/src/Avalonia.X11/X11CursorFactory.cs @@ -94,9 +94,13 @@ namespace Avalonia.X11 { var size = Marshal.SizeOf() + (bitmap.PixelSize.Width * bitmap.PixelSize.Height * 4); + var runtimePlatform = AvaloniaLocator.Current.GetService() ?? + throw new InvalidOperationException("Unable to locate IRuntimePlatform"); + var platformRenderInterface = AvaloniaLocator.Current.GetService() ?? + throw new InvalidOperationException("Unable to locate IPlatformRenderInterface"); _pixelSize = bitmap.PixelSize; - _blob = AvaloniaLocator.Current.GetService().AllocBlob(size); + _blob = runtimePlatform.AllocBlob(size); var image = (XcursorImage*)_blob.Address; image->version = 1; @@ -107,7 +111,7 @@ namespace Avalonia.X11 image->yhot = hotSpot.Y; image->pixels = (IntPtr)(image + 1); - using (var renderTarget = AvaloniaLocator.Current.GetService().CreateRenderTarget(new[] { this })) + using (var renderTarget = platformRenderInterface.CreateRenderTarget(new[] { this })) using (var ctx = renderTarget.CreateDrawingContext(null)) { var r = new Rect(_pixelSize.ToSize(1)); diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/StringIndexerNode.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/StringIndexerNode.cs index 3857c9aff0..9c505486b7 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/StringIndexerNode.cs +++ b/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/StringIndexerNode.cs @@ -45,7 +45,7 @@ namespace Avalonia.Markup.Parsers.Nodes return false; } - convertedObjectArray[i] = temp; + convertedObjectArray[i] = temp!; } var intArgs = convertedObjectArray.OfType().ToArray(); @@ -163,7 +163,7 @@ namespace Avalonia.Markup.Parsers.Nodes return AvaloniaProperty.UnsetValue; } - convertedObjectArray[i] = temp; + convertedObjectArray[i] = temp!; } var intArgs = convertedObjectArray.OfType().ToArray(); @@ -238,14 +238,14 @@ namespace Avalonia.Markup.Parsers.Nodes for (int i = 0; i < Arguments.Count; ++i) { - object value; + object? value; if (!TypeUtilities.TryConvert(typeof(int), Arguments[i], CultureInfo.InvariantCulture, out value)) { return false; } - intArgs[i] = (int)value; + intArgs[i] = (int)value!; } return true; } diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs index 11fb287d46..0a3986ce2b 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs +++ b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs @@ -75,7 +75,7 @@ namespace Avalonia.Markup.Parsers throw new InvalidOperationException($"Cannot find '{property.Property}' on '{type}"); } - object typedValue; + object? typedValue; if (TypeUtilities.TryConvert( targetProperty.PropertyType, diff --git a/src/Shared/PlatformSupport/AssetLoader.cs b/src/Shared/PlatformSupport/AssetLoader.cs index affb861de0..95ac79ae31 100644 --- a/src/Shared/PlatformSupport/AssetLoader.cs +++ b/src/Shared/PlatformSupport/AssetLoader.cs @@ -6,6 +6,8 @@ using System.Reflection; using Avalonia.Platform; using Avalonia.Utilities; +#nullable enable + namespace Avalonia.Shared.PlatformSupport { /// @@ -94,7 +96,7 @@ namespace Avalonia.Shared.PlatformSupport return (asset.GetStream(), asset.Assembly); } - public Assembly? GetAssembly(Uri uri, Uri baseUri) + public Assembly? GetAssembly(Uri uri, Uri? baseUri) { if (!uri.IsAbsoluteUri && baseUri != null) uri = new Uri(baseUri, uri); @@ -375,7 +377,7 @@ namespace Avalonia.Shared.PlatformSupport var indexLength = new BinaryReader(resources).ReadInt32(); var index = AvaloniaResourcesIndexReaderWriter.Read(new SlicedStream(resources, 4, indexLength)); var baseOffset = indexLength + 4; - AvaloniaResources = index.ToDictionary(r => "/" + r.Path.TrimStart('/'), r => (IAssetDescriptor) + AvaloniaResources = index.ToDictionary(r => "/" + r.Path!.TrimStart('/'), r => (IAssetDescriptor) new AvaloniaResourceDescriptor(assembly, baseOffset + r.Offset, r.Size)); } } diff --git a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs index e1f8cd5b72..bc2a2f3103 100644 --- a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs +++ b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs @@ -41,7 +41,7 @@ namespace Avalonia.Web.Blazor _topLevel = new EmbeddableControlRoot(_topLevelImpl); - if (Application.Current.ApplicationLifetime is ISingleViewApplicationLifetime lifetime) + if (Application.Current?.ApplicationLifetime is ISingleViewApplicationLifetime lifetime) { _topLevel.Content = lifetime.MainView; } @@ -280,7 +280,7 @@ namespace Avalonia.Web.Blazor var options = AvaloniaLocator.Current.GetService(); // bump the default resource cache limit - _context.SetResourceCacheLimit(options.MaxGpuResourceSizeBytes ?? 32 * 1024 * 1024); + _context.SetResourceCacheLimit(options?.MaxGpuResourceSizeBytes ?? 32 * 1024 * 1024); Console.WriteLine("glcontext created and resource limit set"); } diff --git a/src/Web/Avalonia.Web.Blazor/WindowingPlatform.cs b/src/Web/Avalonia.Web.Blazor/WindowingPlatform.cs index 4439ec161c..4804016ab2 100644 --- a/src/Web/Avalonia.Web.Blazor/WindowingPlatform.cs +++ b/src/Web/Avalonia.Web.Blazor/WindowingPlatform.cs @@ -58,7 +58,7 @@ namespace Avalonia.Web.Blazor public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick) { - return AvaloniaLocator.Current.GetService() + return GetRuntimePlatform() .StartSystemTimer(interval, () => { Dispatcher.UIThread.RunJobs(priority); @@ -75,7 +75,7 @@ namespace Avalonia.Web.Blazor IDisposable? disp = null; - disp = AvaloniaLocator.Current.GetService() + disp = GetRuntimePlatform() .StartSystemTimer(TimeSpan.FromMilliseconds(1), () => { @@ -96,6 +96,10 @@ namespace Avalonia.Web.Blazor public event Action? Signaled; - + private static IRuntimePlatform GetRuntimePlatform() + { + return AvaloniaLocator.Current.GetService() ?? + throw new InvalidOperationException("Unable to locate IRuntimePlatform."); + } } } diff --git a/src/Windows/Avalonia.Win32/FramebufferManager.cs b/src/Windows/Avalonia.Win32/FramebufferManager.cs index 0240ac9701..119edcebbf 100644 --- a/src/Windows/Avalonia.Win32/FramebufferManager.cs +++ b/src/Windows/Avalonia.Win32/FramebufferManager.cs @@ -107,7 +107,9 @@ namespace Avalonia.Win32 private static FramebufferData AllocateFramebufferData(int width, int height) { - var bitmapBlob = AvaloniaLocator.Current.GetService().AllocBlob(width * height * _bytesPerPixel); + var service = AvaloniaLocator.Current.GetService() ?? + throw new InvalidOperationException("Unable to locate IRuntimePlatform."); + var bitmapBlob = service.AllocBlob(width * height * _bytesPerPixel); return new FramebufferData(bitmapBlob, width, height); } From e4b19743ca3eda862946313f90c304a8231cb982 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 16 Dec 2021 23:37:27 +0100 Subject: [PATCH 29/90] baseUri can be null. --- src/Avalonia.Base/Platform/IAssetLoader.cs | 2 +- src/Shared/PlatformSupport/AssetLoader.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/Platform/IAssetLoader.cs b/src/Avalonia.Base/Platform/IAssetLoader.cs index ec1f97f178..e3899784ad 100644 --- a/src/Avalonia.Base/Platform/IAssetLoader.cs +++ b/src/Avalonia.Base/Platform/IAssetLoader.cs @@ -74,6 +74,6 @@ namespace Avalonia.Platform /// The URI. /// The base URI. /// All matching assets as a tuple of the absolute path to the asset and the assembly containing the asset - IEnumerable GetAssets(Uri uri, Uri baseUri); + IEnumerable GetAssets(Uri uri, Uri? baseUri); } } diff --git a/src/Shared/PlatformSupport/AssetLoader.cs b/src/Shared/PlatformSupport/AssetLoader.cs index 95ac79ae31..43f9211889 100644 --- a/src/Shared/PlatformSupport/AssetLoader.cs +++ b/src/Shared/PlatformSupport/AssetLoader.cs @@ -109,7 +109,7 @@ namespace Avalonia.Shared.PlatformSupport /// The URI. /// Base URI that is used if is relative. /// All matching assets as a tuple of the absolute path to the asset and the assembly containing the asset - public IEnumerable GetAssets(Uri uri, Uri baseUri) + public IEnumerable GetAssets(Uri uri, Uri? baseUri) { if (uri.IsAbsoluteUri && uri.Scheme == "resm") { From 18115363f8445e3ea9894a098497ddd48ab0f745 Mon Sep 17 00:00:00 2001 From: Jumar Macato <16554748+jmacato@users.noreply.github.com> Date: Fri, 17 Dec 2021 10:45:35 +0800 Subject: [PATCH 30/90] Update src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs Co-authored-by: Max Katz --- src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs b/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs index 809c063b47..10619d675b 100644 --- a/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs +++ b/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs @@ -75,7 +75,7 @@ namespace Avalonia public bool DisableDefaultApplicationMenuItems { get; set; } /// - /// + /// Gets or sets a value indicating whether the native macOS menu bar will be enabled for the application. /// public bool DisableNativeMenus { get; set; } } From 647cfe68bca8601b6e105a3a4599f5e724d3f231 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 17 Dec 2021 00:01:08 -0500 Subject: [PATCH 31/90] Update ListBoxItem.xaml --- src/Avalonia.Themes.Fluent/Controls/ListBoxItem.xaml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/ListBoxItem.xaml b/src/Avalonia.Themes.Fluent/Controls/ListBoxItem.xaml index 64f52f53db..14409dda59 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ListBoxItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ListBoxItem.xaml @@ -18,7 +18,6 @@ diff --git a/samples/ControlCatalog/App.xaml.cs b/samples/ControlCatalog/App.xaml.cs index 36b6fc2dcd..085de2ae7a 100644 --- a/samples/ControlCatalog/App.xaml.cs +++ b/samples/ControlCatalog/App.xaml.cs @@ -96,7 +96,6 @@ namespace ControlCatalog public override void Initialize() { - Styles.Insert(0, FluentLight); AvaloniaXamlLoader.Load(this); } diff --git a/samples/ControlCatalog/MainView.xaml.cs b/samples/ControlCatalog/MainView.xaml.cs index abedda3c85..3898e9da85 100644 --- a/samples/ControlCatalog/MainView.xaml.cs +++ b/samples/ControlCatalog/MainView.xaml.cs @@ -11,6 +11,7 @@ using Avalonia.Media.Immutable; using Avalonia.Platform; using ControlCatalog.Pages; using ControlCatalog.Models; +using Avalonia.Themes.Fluent; namespace ControlCatalog { @@ -43,14 +44,7 @@ namespace ControlCatalog { if (themes.SelectedItem is CatalogTheme theme) { - Application.Current.Styles[0] = theme switch - { - CatalogTheme.FluentLight => App.FluentLight, - CatalogTheme.FluentDark => App.FluentDark, - CatalogTheme.DefaultLight => App.DefaultLight, - CatalogTheme.DefaultDark => App.DefaultDark, - _ => Application.Current.Styles[0] - }; + (Application.Current.Styles[0] as FluentTheme).Mode = FluentThemeMode.Dark; } }; diff --git a/src/Avalonia.Themes.Fluent/FluentTheme.cs b/src/Avalonia.Themes.Fluent/FluentTheme.cs index 43b71567fa..812bd22839 100644 --- a/src/Avalonia.Themes.Fluent/FluentTheme.cs +++ b/src/Avalonia.Themes.Fluent/FluentTheme.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using Avalonia.Controls; using Avalonia.Markup.Xaml; +using Avalonia.Markup.Xaml.Styling; using Avalonia.Styling; #nullable enable @@ -22,6 +23,7 @@ namespace Avalonia.Themes.Fluent private readonly Uri _baseUri; private IStyle[]? _loaded; private bool _isLoading; + private FluentThemeMode _mode; /// /// Initializes a new instance of the class. @@ -44,7 +46,20 @@ namespace Avalonia.Themes.Fluent /// /// Gets or sets the mode of the fluent theme (light, dark). /// - public FluentThemeMode Mode { get; set; } + public FluentThemeMode Mode + { + get => _mode; + set + { + if (_mode != value) + { + _mode = value; + (Loaded as Styles)[3] = FluentDark[0]; + (Loaded as Styles)[4] = FluentDark[1]; + } + + } + } public IResourceHost? Owner => (Loaded as IResourceProvider)?.Owner; @@ -58,8 +73,25 @@ namespace Avalonia.Themes.Fluent if (_loaded == null) { _isLoading = true; - var loaded = (IStyle)AvaloniaXamlLoader.Load(GetUri(), _baseUri); - _loaded = new[] { loaded }; + Styles? resultStyle = new Styles(); + + resultStyle.AddRange(SharedStyles); + + if (Mode == FluentThemeMode.Light) + { + for (int i = 0; i < FluentLight.Count; i++) + { + resultStyle.Add(FluentLight[i]); + } + } + else if (Mode == FluentThemeMode.Dark) + { + for (int i = 0; i < FluentDark.Count; i++) + { + resultStyle.Add(FluentDark[i]); + } + } + _loaded = new[] { resultStyle }; _isLoading = false; } @@ -105,10 +137,43 @@ namespace Avalonia.Themes.Fluent void IResourceProvider.AddOwner(IResourceHost owner) => (Loaded as IResourceProvider)?.AddOwner(owner); void IResourceProvider.RemoveOwner(IResourceHost owner) => (Loaded as IResourceProvider)?.RemoveOwner(owner); - private Uri GetUri() => Mode switch + private static Styles SharedStyles = new Styles + { + new StyleInclude(new Uri("resm:Styles?assembly=Avalonia.Themes.Fluent")) + { + Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/AccentColors.xaml") + }, + new StyleInclude(new Uri("resm:Styles?assembly=Avalonia.Themes.Fluent")) + { + Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/Base.xaml") + }, + new StyleInclude(new Uri("resm:Styles?assembly=Avalonia.Themes.Fluent")) + { + Source = new Uri("avares://Avalonia.Themes.Fluent/Controls/FluentControls.xaml") + } + }; + + private static Styles FluentLight = new Styles { - FluentThemeMode.Dark => new Uri("avares://Avalonia.Themes.Fluent/FluentDark.xaml", UriKind.Absolute), - _ => new Uri("avares://Avalonia.Themes.Fluent/FluentLight.xaml", UriKind.Absolute), + new StyleInclude(new Uri("resm:Styles?assembly=Avalonia.Themes.Fluent")) + { + Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseLight.xaml") + }, + new StyleInclude(new Uri("resm:Styles?assembly=Avalonia.Themes.Fluent")) + { + Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml") + } + }; + private static Styles FluentDark = new Styles + { + new StyleInclude(new Uri("resm:Styles?assembly=Avalonia.Themes.Fluent")) + { + Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseDark.xaml") + }, + new StyleInclude(new Uri("resm:Styles?assembly=Avalonia.Themes.Fluent")) + { + Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml") + } }; } } From 032b78cd0698215e084f5352159c77706239429a Mon Sep 17 00:00:00 2001 From: Takoooooo Date: Sun, 19 Dec 2021 11:34:16 +0200 Subject: [PATCH 52/90] wip --- src/Avalonia.Themes.Fluent/FluentTheme.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/FluentTheme.cs b/src/Avalonia.Themes.Fluent/FluentTheme.cs index 812bd22839..73bf5b7226 100644 --- a/src/Avalonia.Themes.Fluent/FluentTheme.cs +++ b/src/Avalonia.Themes.Fluent/FluentTheme.cs @@ -21,7 +21,7 @@ namespace Avalonia.Themes.Fluent public class FluentTheme : IStyle, IResourceProvider { private readonly Uri _baseUri; - private IStyle[]? _loaded; + private IStyle? _loaded; private bool _isLoading; private FluentThemeMode _mode; @@ -91,17 +91,17 @@ namespace Avalonia.Themes.Fluent resultStyle.Add(FluentDark[i]); } } - _loaded = new[] { resultStyle }; + _loaded = resultStyle; _isLoading = false; } - return _loaded?[0]!; + return _loaded; } } bool IResourceNode.HasResources => (Loaded as IResourceProvider)?.HasResources ?? false; - IReadOnlyList IStyle.Children => _loaded ?? Array.Empty(); + IReadOnlyList IStyle.Children => _loaded?.Children ?? Array.Empty(); public event EventHandler OwnerChanged { From 9c8b68ff5d0f5fd6cf5a4158b2aa4c093e6d3047 Mon Sep 17 00:00:00 2001 From: Takoooooo Date: Sun, 19 Dec 2021 12:09:08 +0200 Subject: [PATCH 53/90] more refactorings --- src/Avalonia.Themes.Fluent/FluentTheme.cs | 97 ++++++++++++----------- 1 file changed, 50 insertions(+), 47 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/FluentTheme.cs b/src/Avalonia.Themes.Fluent/FluentTheme.cs index 73bf5b7226..08d9dc317d 100644 --- a/src/Avalonia.Themes.Fluent/FluentTheme.cs +++ b/src/Avalonia.Themes.Fluent/FluentTheme.cs @@ -21,8 +21,11 @@ namespace Avalonia.Themes.Fluent public class FluentTheme : IStyle, IResourceProvider { private readonly Uri _baseUri; - private IStyle? _loaded; + private Styles _fluentDark = new(); + private Styles _fluentLight = new(); + private Styles _sharedStyles = new(); private bool _isLoading; + private IStyle? _loaded; private FluentThemeMode _mode; /// @@ -32,6 +35,7 @@ namespace Avalonia.Themes.Fluent public FluentTheme(Uri baseUri) { _baseUri = baseUri; + InitStyles(baseUri); } /// @@ -41,6 +45,7 @@ namespace Avalonia.Themes.Fluent public FluentTheme(IServiceProvider serviceProvider) { _baseUri = ((IUriContext)serviceProvider.GetService(typeof(IUriContext))).BaseUri; + InitStyles(_baseUri); } /// @@ -54,8 +59,8 @@ namespace Avalonia.Themes.Fluent if (_mode != value) { _mode = value; - (Loaded as Styles)[3] = FluentDark[0]; - (Loaded as Styles)[4] = FluentDark[1]; + (Loaded as Styles)![1] = _fluentDark[0]; + (Loaded as Styles)![2] = _fluentDark[1]; } } @@ -73,23 +78,17 @@ namespace Avalonia.Themes.Fluent if (_loaded == null) { _isLoading = true; - Styles? resultStyle = new Styles(); - - resultStyle.AddRange(SharedStyles); + Styles? resultStyle = new Styles() { _sharedStyles }; if (Mode == FluentThemeMode.Light) { - for (int i = 0; i < FluentLight.Count; i++) - { - resultStyle.Add(FluentLight[i]); - } + resultStyle.Add(_fluentLight[0]); + resultStyle.Add(_fluentLight[1]); } else if (Mode == FluentThemeMode.Dark) { - for (int i = 0; i < FluentDark.Count; i++) - { - resultStyle.Add(FluentDark[i]); - } + resultStyle.Add(_fluentDark[0]); + resultStyle.Add(_fluentDark[1]); } _loaded = resultStyle; _isLoading = false; @@ -137,43 +136,47 @@ namespace Avalonia.Themes.Fluent void IResourceProvider.AddOwner(IResourceHost owner) => (Loaded as IResourceProvider)?.AddOwner(owner); void IResourceProvider.RemoveOwner(IResourceHost owner) => (Loaded as IResourceProvider)?.RemoveOwner(owner); - private static Styles SharedStyles = new Styles + private void InitStyles(Uri baseUri) { - new StyleInclude(new Uri("resm:Styles?assembly=Avalonia.Themes.Fluent")) + _sharedStyles = new Styles { - Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/AccentColors.xaml") - }, - new StyleInclude(new Uri("resm:Styles?assembly=Avalonia.Themes.Fluent")) - { - Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/Base.xaml") - }, - new StyleInclude(new Uri("resm:Styles?assembly=Avalonia.Themes.Fluent")) - { - Source = new Uri("avares://Avalonia.Themes.Fluent/Controls/FluentControls.xaml") - } - }; + new StyleInclude(baseUri) + { + Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/AccentColors.xaml") + }, + new StyleInclude(baseUri) + { + Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/Base.xaml") + }, + new StyleInclude(baseUri) + { + Source = new Uri("avares://Avalonia.Themes.Fluent/Controls/FluentControls.xaml") + } + }; - private static Styles FluentLight = new Styles - { - new StyleInclude(new Uri("resm:Styles?assembly=Avalonia.Themes.Fluent")) - { - Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseLight.xaml") - }, - new StyleInclude(new Uri("resm:Styles?assembly=Avalonia.Themes.Fluent")) + _fluentLight = new Styles { - Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml") - } - }; - private static Styles FluentDark = new Styles - { - new StyleInclude(new Uri("resm:Styles?assembly=Avalonia.Themes.Fluent")) - { - Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseDark.xaml") - }, - new StyleInclude(new Uri("resm:Styles?assembly=Avalonia.Themes.Fluent")) + new StyleInclude(baseUri) + { + Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseLight.xaml") + }, + new StyleInclude(baseUri) + { + Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml") + } + }; + + _fluentDark = new Styles { - Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml") - } - }; + new StyleInclude(baseUri) + { + Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseDark.xaml") + }, + new StyleInclude(baseUri) + { + Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml") + } + }; + } } } From 22be08125735ab59155c549f2a534b29bfbf335e Mon Sep 17 00:00:00 2001 From: Takoooooo Date: Sun, 19 Dec 2021 13:10:05 +0200 Subject: [PATCH 54/90] more refactorings --- src/Avalonia.Themes.Fluent/FluentTheme.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/FluentTheme.cs b/src/Avalonia.Themes.Fluent/FluentTheme.cs index 08d9dc317d..1c61c03c97 100644 --- a/src/Avalonia.Themes.Fluent/FluentTheme.cs +++ b/src/Avalonia.Themes.Fluent/FluentTheme.cs @@ -59,8 +59,18 @@ namespace Avalonia.Themes.Fluent if (_mode != value) { _mode = value; - (Loaded as Styles)![1] = _fluentDark[0]; - (Loaded as Styles)![2] = _fluentDark[1]; + if (_mode == FluentThemeMode.Dark) + { + (Loaded as Styles)![1] = _fluentDark[0]; + (Loaded as Styles)![2] = _fluentDark[1]; + } + else + { + (Loaded as Styles)![1] = _fluentLight[0]; + (Loaded as Styles)![2] = _fluentLight[1]; + } + + } } From 2de57da8ff54c43ca49c85105cedeb2733d0ab2a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 19 Dec 2021 23:26:14 +0100 Subject: [PATCH 55/90] Added nullable annotations to Avalonia.Layout. --- src/Avalonia.Layout/AttachedLayout.cs | 6 +- src/Avalonia.Layout/Avalonia.Layout.csproj | 1 + src/Avalonia.Layout/ElementManager.cs | 40 +++++------ src/Avalonia.Layout/FlowLayoutAlgorithm.cs | 68 +++++++++---------- .../IFlowLayoutAlgorithmDelegates.cs | 4 +- src/Avalonia.Layout/LayoutContext.cs | 4 +- src/Avalonia.Layout/LayoutContextAdapter.cs | 2 +- src/Avalonia.Layout/LayoutHelper.cs | 2 +- src/Avalonia.Layout/LayoutQueue.cs | 1 + src/Avalonia.Layout/Layoutable.cs | 2 +- .../NonVirtualizingLayoutContext.cs | 4 +- src/Avalonia.Layout/StackLayout.cs | 22 +++--- src/Avalonia.Layout/UniformGridLayout.cs | 26 +++---- src/Avalonia.Layout/UniformGridLayoutState.cs | 2 +- src/Avalonia.Layout/Utils/ListUtils.cs | 5 -- .../VirtualLayoutContextAdapter.cs | 4 +- .../VirtualizingLayoutContext.cs | 2 +- src/Avalonia.Layout/WrapLayout/UvMeasure.cs | 2 +- src/Avalonia.Layout/WrapLayout/WrapItem.cs | 2 +- src/Avalonia.Layout/WrapLayout/WrapLayout.cs | 6 +- .../WrapLayout/WrapLayoutState.cs | 9 ++- 21 files changed, 107 insertions(+), 107 deletions(-) diff --git a/src/Avalonia.Layout/AttachedLayout.cs b/src/Avalonia.Layout/AttachedLayout.cs index 047c01343f..6c884641f8 100644 --- a/src/Avalonia.Layout/AttachedLayout.cs +++ b/src/Avalonia.Layout/AttachedLayout.cs @@ -12,17 +12,17 @@ namespace Avalonia.Layout /// public abstract class AttachedLayout : AvaloniaObject { - public string LayoutId { get; set; } + public string? LayoutId { get; set; } /// /// Occurs when the measurement state (layout) has been invalidated. /// - public event EventHandler MeasureInvalidated; + public event EventHandler? MeasureInvalidated; /// /// Occurs when the arrange state (layout) has been invalidated. /// - public event EventHandler ArrangeInvalidated; + public event EventHandler? ArrangeInvalidated; /// /// Initializes any per-container state the layout requires when it is attached to an diff --git a/src/Avalonia.Layout/Avalonia.Layout.csproj b/src/Avalonia.Layout/Avalonia.Layout.csproj index 2842ccf3a8..c1cd19c394 100644 --- a/src/Avalonia.Layout/Avalonia.Layout.csproj +++ b/src/Avalonia.Layout/Avalonia.Layout.csproj @@ -9,4 +9,5 @@ + diff --git a/src/Avalonia.Layout/ElementManager.cs b/src/Avalonia.Layout/ElementManager.cs index 681d23a61f..039eb52317 100644 --- a/src/Avalonia.Layout/ElementManager.cs +++ b/src/Avalonia.Layout/ElementManager.cs @@ -13,10 +13,10 @@ namespace Avalonia.Layout { internal class ElementManager { - private readonly List _realizedElements = new List(); + private readonly List _realizedElements = new List(); private readonly List _realizedElementLayoutBounds = new List(); private int _firstRealizedDataIndex; - private VirtualizingLayoutContext _context; + private VirtualizingLayoutContext? _context; private bool IsVirtualizingContext { @@ -58,7 +58,7 @@ namespace Avalonia.Layout // Make sure there is enough space for the bounds. // Note: We could optimize when the count becomes smaller, but keeping // it always up to date is the simplest option for now. - _realizedElementLayoutBounds.Resize(count); + _realizedElementLayoutBounds.Resize(count, default); } } } @@ -66,12 +66,12 @@ namespace Avalonia.Layout public int GetRealizedElementCount() { - return IsVirtualizingContext ? _realizedElements.Count : _context.ItemCount; + return IsVirtualizingContext ? _realizedElements.Count : _context!.ItemCount; } public ILayoutable GetAt(int realizedIndex) { - ILayoutable element; + ILayoutable? element; if (IsVirtualizingContext) { @@ -80,7 +80,7 @@ namespace Avalonia.Layout // Sentinel. Create the element now since we need it. int dataIndex = GetDataIndexFromRealizedRangeIndex(realizedIndex); Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "Creating element for sentinal with data index {Index}", dataIndex); - element = _context.GetOrCreateElementAt( + element = _context!.GetOrCreateElementAt( dataIndex, ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle); _realizedElements[realizedIndex] = element; @@ -93,12 +93,12 @@ namespace Avalonia.Layout else { // realizedIndex and dataIndex are the same (everything is realized) - element = _context.GetOrCreateElementAt( + element = _context!.GetOrCreateElementAt( realizedIndex, ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle); } - return element; + return element!; } public void Add(ILayoutable element, int dataIndex) @@ -112,7 +112,7 @@ namespace Avalonia.Layout _realizedElementLayoutBounds.Add(default); } - public void Insert(int realizedIndex, int dataIndex, ILayoutable element) + public void Insert(int realizedIndex, int dataIndex, ILayoutable? element) { if (realizedIndex == 0) { @@ -136,7 +136,7 @@ namespace Avalonia.Layout if (elementRef != null) { - _context.RecycleElement(elementRef); + _context!.RecycleElement(elementRef); } } @@ -203,26 +203,26 @@ namespace Avalonia.Layout else { // Non virtualized - everything is realized - return index >= 0 && index < _context.ItemCount; + return index >= 0 && index < _context!.ItemCount; } } - public bool IsIndexValidInData(int currentIndex) => (uint)currentIndex < _context.ItemCount; + public bool IsIndexValidInData(int currentIndex) => (uint)currentIndex < _context!.ItemCount; - public ILayoutable GetRealizedElement(int dataIndex) + public ILayoutable? GetRealizedElement(int dataIndex) { return IsVirtualizingContext ? GetAt(GetRealizedRangeIndexFromDataIndex(dataIndex)) : - _context.GetOrCreateElementAt( + _context!.GetOrCreateElementAt( dataIndex, ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle); } - public void EnsureElementRealized(bool forward, int dataIndex, string layoutId) + public void EnsureElementRealized(bool forward, int dataIndex, string? layoutId) { if (IsDataIndexRealized(dataIndex) == false) { - var element = _context.GetOrCreateElementAt( + var element = _context!.GetOrCreateElementAt( dataIndex, ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle); @@ -273,14 +273,14 @@ namespace Avalonia.Layout { case NotifyCollectionChangedAction.Add: { - OnItemsAdded(args.NewStartingIndex, args.NewItems.Count); + OnItemsAdded(args.NewStartingIndex, args.NewItems!.Count); } break; case NotifyCollectionChangedAction.Replace: { - int oldSize = args.OldItems.Count; - int newSize = args.NewItems.Count; + int oldSize = args.OldItems!.Count; + int newSize = args.NewItems!.Count; int oldStartIndex = args.OldStartingIndex; int newStartIndex = args.NewStartingIndex; @@ -301,7 +301,7 @@ namespace Avalonia.Layout if (elementRef != null) { - _context.RecycleElement(elementRef); + _context!.RecycleElement(elementRef); _realizedElements[realizedIndex] = null; } } diff --git a/src/Avalonia.Layout/FlowLayoutAlgorithm.cs b/src/Avalonia.Layout/FlowLayoutAlgorithm.cs index 63343fd1a7..40b392225f 100644 --- a/src/Avalonia.Layout/FlowLayoutAlgorithm.cs +++ b/src/Avalonia.Layout/FlowLayoutAlgorithm.cs @@ -16,8 +16,8 @@ namespace Avalonia.Layout private Size _lastAvailableSize; private double _lastItemSpacing; private bool _collectionChangePending; - private VirtualizingLayoutContext _context; - private IFlowLayoutAlgorithmDelegates _algorithmCallbacks; + private VirtualizingLayoutContext? _context; + private IFlowLayoutAlgorithmDelegates? _algorithmCallbacks; private Rect _lastExtent; private int _firstRealizedDataIndexInsideRealizationWindow = -1; private int _lastRealizedDataIndexInsideRealizationWindow = -1; @@ -46,7 +46,7 @@ namespace Avalonia.Layout } } - private Rect RealizationRect => IsVirtualizingContext ? _context.RealizationRect : new Rect(Size.Infinity); + private Rect RealizationRect => IsVirtualizingContext ? _context!.RealizationRect : new Rect(Size.Infinity); public void InitializeForContext(VirtualizingLayoutContext context, IFlowLayoutAlgorithmDelegates callbacks) { @@ -76,7 +76,7 @@ namespace Avalonia.Layout int maxItemsPerLine, ScrollOrientation orientation, bool disableVirtualization, - string layoutId) + string? layoutId) { _orientation.ScrollOrientation = orientation; @@ -87,7 +87,7 @@ namespace Avalonia.Layout layoutId, realizationRect); - var suggestedAnchorIndex = _context.RecommendedAnchorIndex; + var suggestedAnchorIndex = _context!.RecommendedAnchorIndex; if (_elementManager.IsIndexValidInData(suggestedAnchorIndex)) { var anchorRealized = _elementManager.IsDataIndexRealized(suggestedAnchorIndex); @@ -124,7 +124,7 @@ namespace Avalonia.Layout VirtualizingLayoutContext context, bool isWrapping, LineAlignment lineAlignment, - string layoutId) + string? layoutId) { Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: ArrangeLayout", layoutId); ArrangeVirtualizingLayout(finalSize, lineAlignment, isWrapping, layoutId); @@ -149,7 +149,7 @@ namespace Avalonia.Layout Size availableSize, VirtualizingLayoutContext context) { - var measureSize = _algorithmCallbacks.Algorithm_GetMeasureSize(index, availableSize, context); + var measureSize = _algorithmCallbacks!.Algorithm_GetMeasureSize(index, availableSize, context); element.Measure(measureSize); var provisionalArrangeSize = _algorithmCallbacks.Algorithm_GetProvisionalArrangeSize(index, measureSize, element.DesiredSize, context); _algorithmCallbacks.Algorithm_OnElementMeasured(element, index, availableSize, measureSize, element.DesiredSize, provisionalArrangeSize, context); @@ -161,7 +161,7 @@ namespace Avalonia.Layout Size availableSize, bool isWrapping, double minItemSpacing, - string layoutId) + string? layoutId) { int anchorIndex = -1; var anchorPosition= new Point(); @@ -170,7 +170,7 @@ namespace Avalonia.Layout if (!IsVirtualizingContext) { // Non virtualizing host, start generating from the element 0 - anchorIndex = context.ItemCount > 0 ? 0 : -1; + anchorIndex = context!.ItemCount > 0 ? 0 : -1; } else { @@ -183,7 +183,7 @@ namespace Avalonia.Layout _lastItemSpacing != minItemSpacing || _collectionChangePending); - var suggestedAnchorIndex = _context.RecommendedAnchorIndex; + var suggestedAnchorIndex = _context!.RecommendedAnchorIndex; var isAnchorSuggestionValid = suggestedAnchorIndex >= 0 && _elementManager.IsDataIndexRealized(suggestedAnchorIndex); @@ -191,10 +191,10 @@ namespace Avalonia.Layout if (isAnchorSuggestionValid) { Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Using suggested anchor {Anchor}", layoutId, suggestedAnchorIndex); - anchorIndex = _algorithmCallbacks.Algorithm_GetAnchorForTargetElement( + anchorIndex = _algorithmCallbacks!.Algorithm_GetAnchorForTargetElement( suggestedAnchorIndex, availableSize, - context).Index; + context!).Index; if (_elementManager.IsDataIndexRealized(anchorIndex)) { @@ -235,7 +235,7 @@ namespace Avalonia.Layout // The anchor is based on the realization window because a connected ItemsRepeater might intersect the realization window // but not the visible window. In that situation, we still need to produce a valid anchor. - var anchorInfo = _algorithmCallbacks.Algorithm_GetAnchorForRealizationRect(availableSize, context); + var anchorInfo = _algorithmCallbacks!.Algorithm_GetAnchorForRealizationRect(availableSize, context!); anchorIndex = anchorInfo.Index; anchorPosition = _orientation.MinorMajorPoint(0, anchorInfo.Offset); } @@ -259,12 +259,12 @@ namespace Avalonia.Layout Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId} Disconnected Window - throwing away all realized elements", layoutId); _elementManager.ClearRealizedRange(); - var anchor = _context.GetOrCreateElementAt(anchorIndex, ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle); + var anchor = _context!.GetOrCreateElementAt(anchorIndex, ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle); _elementManager.Add(anchor, anchorIndex); } var anchorElement = _elementManager.GetRealizedElement(anchorIndex); - var desiredSize = MeasureElement(anchorElement, anchorIndex, availableSize, _context); + var desiredSize = MeasureElement(anchorElement!, anchorIndex, availableSize, _context!); var layoutBounds = new Rect(anchorPosition.X, anchorPosition.Y, desiredSize.Width, desiredSize.Height); _elementManager.SetLayoutBoundsForDataIndex(anchorIndex, layoutBounds); @@ -296,7 +296,7 @@ namespace Avalonia.Layout double lineSpacing, int maxItemsPerLine, bool disableVirtualization, - string layoutId) + string? layoutId) { if (anchorIndex != -1) { @@ -322,7 +322,7 @@ namespace Avalonia.Layout // Ensure layout element. _elementManager.EnsureElementRealized(direction == GenerateDirection.Forward, currentIndex, layoutId); var currentElement = _elementManager.GetRealizedElement(currentIndex); - var desiredSize = MeasureElement(currentElement, currentIndex, availableSize, _context); + var desiredSize = MeasureElement(currentElement!, currentIndex, availableSize, _context!); ++count; // Lay it out. @@ -333,7 +333,7 @@ namespace Avalonia.Layout if (direction == GenerateDirection.Forward) { double remainingSpace = _orientation.Minor(availableSize) - (_orientation.MinorStart(previousElementBounds) + _orientation.MinorSize(previousElementBounds) + minItemSpacing + _orientation.Minor(desiredSize)); - if (countInLine >= maxItemsPerLine || _algorithmCallbacks.Algorithm_ShouldBreakLine(currentIndex, remainingSpace)) + if (countInLine >= maxItemsPerLine || _algorithmCallbacks!.Algorithm_ShouldBreakLine(currentIndex, remainingSpace)) { // No more space in this row. wrap to next row. _orientation.SetMinorStart(ref currentBounds, 0); @@ -371,7 +371,7 @@ namespace Avalonia.Layout { // Backward double remainingSpace = _orientation.MinorStart(previousElementBounds) - (_orientation.Minor(desiredSize) + minItemSpacing); - if (countInLine >= maxItemsPerLine || _algorithmCallbacks.Algorithm_ShouldBreakLine(currentIndex, remainingSpace)) + if (countInLine >= maxItemsPerLine || _algorithmCallbacks!.Algorithm_ShouldBreakLine(currentIndex, remainingSpace)) { // Does not fit, wrap to the previous row var availableSizeMinor = _orientation.Minor(availableSize); @@ -432,13 +432,13 @@ namespace Avalonia.Layout // account for that element in the indices inside the realization window. if (direction == GenerateDirection.Forward) { - int dataCount = _context.ItemCount; + int dataCount = _context!.ItemCount; _lastRealizedDataIndexInsideRealizationWindow = previousIndex == dataCount - 1 ? dataCount - 1 : previousIndex - 1; _lastRealizedDataIndexInsideRealizationWindow = Math.Max(0, _lastRealizedDataIndexInsideRealizationWindow); } else { - int dataCount = _context.ItemCount; + int dataCount = _context!.ItemCount; _firstRealizedDataIndexInsideRealizationWindow = previousIndex == 0 ? 0 : previousIndex + 1; _firstRealizedDataIndexInsideRealizationWindow = Math.Min(dataCount - 1, _firstRealizedDataIndexInsideRealizationWindow); } @@ -454,7 +454,7 @@ namespace Avalonia.Layout { _elementManager.ClearRealizedRange(); // FlowLayout requires that the anchor is the first element in the row. - var internalAnchor = _algorithmCallbacks.Algorithm_GetAnchorForTargetElement(index, availableSize, context); + var internalAnchor = _algorithmCallbacks!.Algorithm_GetAnchorForTargetElement(index, availableSize, context); // No need to set the position of the anchor. // (0,0) is fine for now since the extent can @@ -487,7 +487,7 @@ namespace Avalonia.Layout } else { - var realizationRect = _context.RealizationRect; + var realizationRect = _context!.RealizationRect; var elementBounds = _elementManager.GetLayoutBoundsForDataIndex(index); var elementMajorStart = _orientation.MajorStart(elementBounds); @@ -510,11 +510,11 @@ namespace Avalonia.Layout return shouldContinue; } - private Rect EstimateExtent(Size availableSize, string layoutId) + private Rect EstimateExtent(Size availableSize, string? layoutId) { - ILayoutable firstRealizedElement = null; + ILayoutable? firstRealizedElement = null; Rect firstBounds = new Rect(); - ILayoutable lastRealizedElement = null; + ILayoutable? lastRealizedElement = null; Rect lastBounds = new Rect(); int firstDataIndex = -1; int lastDataIndex = -1; @@ -531,9 +531,9 @@ namespace Avalonia.Layout lastBounds = _elementManager.GetLayoutBoundsForRealizedIndex(last); } - Rect extent = _algorithmCallbacks.Algorithm_GetExtent( + Rect extent = _algorithmCallbacks!.Algorithm_GetExtent( availableSize, - _context, + _context!, firstRealizedElement, firstDataIndex, firstBounds, @@ -563,7 +563,7 @@ namespace Avalonia.Layout if (_orientation.MajorStart(currentBounds) != currentLineOffset) { // Staring a new line - _algorithmCallbacks.Algorithm_OnLineArranged(currentDataIndex - countInLine, countInLine, currentLineSize, _context); + _algorithmCallbacks!.Algorithm_OnLineArranged(currentDataIndex - countInLine, countInLine, currentLineSize, _context!); countInLine = 0; currentLineOffset = _orientation.MajorStart(currentBounds); currentLineSize = 0; @@ -575,7 +575,7 @@ namespace Avalonia.Layout } // Raise for the last line. - _algorithmCallbacks.Algorithm_OnLineArranged(_lastRealizedDataIndexInsideRealizationWindow - countInLine + 1, countInLine, currentLineSize, _context); + _algorithmCallbacks!.Algorithm_OnLineArranged(_lastRealizedDataIndexInsideRealizationWindow - countInLine + 1, countInLine, currentLineSize, _context!); } } } @@ -584,7 +584,7 @@ namespace Avalonia.Layout Size finalSize, LineAlignment lineAlignment, bool isWrapping, - string layoutId) + string? layoutId) { // Walk through the realized elements one line at a time and // align them, Then call element.Arrange with the arranged bounds. @@ -636,7 +636,7 @@ namespace Avalonia.Layout LineAlignment lineAlignment, bool isWrapping, Size finalSize, - string layoutId) + string? layoutId) { for (int rangeIndex = lineStartIndex; rangeIndex < lineStartIndex + countInLine; ++rangeIndex) { @@ -723,11 +723,11 @@ namespace Avalonia.Layout { if (IsVirtualizingContext) { - _context.LayoutOrigin = new Point(_lastExtent.X, _lastExtent.Y); + _context!.LayoutOrigin = new Point(_lastExtent.X, _lastExtent.Y); } } - public ILayoutable GetElementIfRealized(int dataIndex) + public ILayoutable? GetElementIfRealized(int dataIndex) { if (_elementManager.IsDataIndexRealized(dataIndex)) { diff --git a/src/Avalonia.Layout/IFlowLayoutAlgorithmDelegates.cs b/src/Avalonia.Layout/IFlowLayoutAlgorithmDelegates.cs index 907a3adf0f..e3eae0caf7 100644 --- a/src/Avalonia.Layout/IFlowLayoutAlgorithmDelegates.cs +++ b/src/Avalonia.Layout/IFlowLayoutAlgorithmDelegates.cs @@ -21,10 +21,10 @@ namespace Avalonia.Layout Rect Algorithm_GetExtent( Size availableSize, VirtualizingLayoutContext context, - ILayoutable firstRealized, + ILayoutable? firstRealized, int firstRealizedItemIndex, Rect firstRealizedLayoutBounds, - ILayoutable lastRealized, + ILayoutable? lastRealized, int lastRealizedItemIndex, Rect lastRealizedLayoutBounds); void Algorithm_OnElementMeasured( diff --git a/src/Avalonia.Layout/LayoutContext.cs b/src/Avalonia.Layout/LayoutContext.cs index dadce58c0c..90bfc0200c 100644 --- a/src/Avalonia.Layout/LayoutContext.cs +++ b/src/Avalonia.Layout/LayoutContext.cs @@ -14,7 +14,7 @@ namespace Avalonia.Layout /// /// Gets or sets an object that represents the state of a layout. /// - public object LayoutState + public object? LayoutState { get => LayoutStateCore; set => LayoutStateCore = value; @@ -23,6 +23,6 @@ namespace Avalonia.Layout /// /// Implements the behavior of in a derived or custom LayoutContext. /// - protected virtual object LayoutStateCore { get; set; } + protected virtual object? LayoutStateCore { get; set; } } } diff --git a/src/Avalonia.Layout/LayoutContextAdapter.cs b/src/Avalonia.Layout/LayoutContextAdapter.cs index 695866df94..25132c2802 100644 --- a/src/Avalonia.Layout/LayoutContextAdapter.cs +++ b/src/Avalonia.Layout/LayoutContextAdapter.cs @@ -16,7 +16,7 @@ namespace Avalonia.Layout _nonVirtualizingContext = nonVirtualizingContext; } - protected override object LayoutStateCore + protected override object? LayoutStateCore { get => _nonVirtualizingContext.LayoutState; set => _nonVirtualizingContext.LayoutState = value; diff --git a/src/Avalonia.Layout/LayoutHelper.cs b/src/Avalonia.Layout/LayoutHelper.cs index 2fe59b49ca..71a2fa085b 100644 --- a/src/Avalonia.Layout/LayoutHelper.cs +++ b/src/Avalonia.Layout/LayoutHelper.cs @@ -101,7 +101,7 @@ namespace Avalonia.Layout if (result == 0 || double.IsNaN(result) || double.IsInfinity(result)) { - throw new Exception($"Invalid LayoutScaling returned from {visualRoot.GetType()}"); + throw new Exception($"Invalid LayoutScaling returned from {visualRoot!.GetType()}"); } return result; diff --git a/src/Avalonia.Layout/LayoutQueue.cs b/src/Avalonia.Layout/LayoutQueue.cs index 1a9eb6b785..24adeb0793 100644 --- a/src/Avalonia.Layout/LayoutQueue.cs +++ b/src/Avalonia.Layout/LayoutQueue.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; namespace Avalonia.Layout { internal class LayoutQueue : IReadOnlyCollection, IDisposable + where T : notnull { private struct Info { diff --git a/src/Avalonia.Layout/Layoutable.cs b/src/Avalonia.Layout/Layoutable.cs index 7568ea8e09..09e0c4263a 100644 --- a/src/Avalonia.Layout/Layoutable.cs +++ b/src/Avalonia.Layout/Layoutable.cs @@ -794,7 +794,7 @@ namespace Avalonia.Layout /// /// The sender. /// The event args. - private void LayoutManagedLayoutUpdated(object sender, EventArgs e) => _layoutUpdated?.Invoke(this, e); + private void LayoutManagedLayoutUpdated(object? sender, EventArgs e) => _layoutUpdated?.Invoke(this, e); /// /// Tests whether any of a 's properties include negative values, diff --git a/src/Avalonia.Layout/NonVirtualizingLayoutContext.cs b/src/Avalonia.Layout/NonVirtualizingLayoutContext.cs index cef551f32e..91348f5e6d 100644 --- a/src/Avalonia.Layout/NonVirtualizingLayoutContext.cs +++ b/src/Avalonia.Layout/NonVirtualizingLayoutContext.cs @@ -12,7 +12,7 @@ namespace Avalonia.Layout /// public abstract class NonVirtualizingLayoutContext : LayoutContext { - private VirtualizingLayoutContext _contextAdapter; + private VirtualizingLayoutContext? _contextAdapter; /// /// Gets the collection of child controls from the container that provides the context. @@ -26,6 +26,6 @@ namespace Avalonia.Layout protected abstract IReadOnlyList ChildrenCore { get; } internal VirtualizingLayoutContext GetVirtualizingContextAdapter() => - _contextAdapter ?? (_contextAdapter = new LayoutContextAdapter(this)); + _contextAdapter ??= new LayoutContextAdapter(this); } } diff --git a/src/Avalonia.Layout/StackLayout.cs b/src/Avalonia.Layout/StackLayout.cs index 4a93c8344f..32e9c4dc8e 100644 --- a/src/Avalonia.Layout/StackLayout.cs +++ b/src/Avalonia.Layout/StackLayout.cs @@ -78,10 +78,10 @@ namespace Avalonia.Layout internal Rect GetExtent( Size availableSize, VirtualizingLayoutContext context, - ILayoutable firstRealized, + ILayoutable? firstRealized, int firstRealizedItemIndex, Rect firstRealizedLayoutBounds, - ILayoutable lastRealized, + ILayoutable? lastRealized, int lastRealizedItemIndex, Rect lastRealizedLayoutBounds) { @@ -89,7 +89,7 @@ namespace Avalonia.Layout // Constants int itemsCount = context.ItemCount; - var stackState = (StackLayoutState)context.LayoutState; + var stackState = (StackLayoutState)context.LayoutState!; double averageElementSize = GetAverageElementSize(availableSize, context, stackState) + Spacing; _orientation.SetMinorSize(ref extent, stackState.MaxArrangeBounds); @@ -131,7 +131,7 @@ namespace Avalonia.Layout { if (context is VirtualizingLayoutContext virtualContext) { - var stackState = (StackLayoutState)virtualContext.LayoutState; + var stackState = (StackLayoutState)virtualContext.LayoutState!; var provisionalArrangeSizeWinRt = provisionalArrangeSize; stackState.OnElementMeasured( index, @@ -177,7 +177,7 @@ namespace Avalonia.Layout if (targetIndex >= 0 && targetIndex < itemsCount) { index = targetIndex; - var state = (StackLayoutState)context.LayoutState; + var state = (StackLayoutState)context.LayoutState!; double averageElementSize = GetAverageElementSize(availableSize, context, state) + Spacing; offset = index * averageElementSize + _orientation.MajorStart(state.FlowAlgorithm.LastExtent); } @@ -188,10 +188,10 @@ namespace Avalonia.Layout Rect IFlowLayoutAlgorithmDelegates.Algorithm_GetExtent( Size availableSize, VirtualizingLayoutContext context, - ILayoutable firstRealized, + ILayoutable? firstRealized, int firstRealizedItemIndex, Rect firstRealizedLayoutBounds, - ILayoutable lastRealized, + ILayoutable? lastRealized, int lastRealizedItemIndex, Rect lastRealizedLayoutBounds) { @@ -234,7 +234,7 @@ namespace Avalonia.Layout if (itemsCount > 0) { var realizationRect = context.RealizationRect; - var state = (StackLayoutState)context.LayoutState; + var state = (StackLayoutState)context.LayoutState!; var lastExtent = state.FlowAlgorithm.LastExtent; double averageElementSize = GetAverageElementSize(availableSize, context, state) + Spacing; @@ -279,13 +279,13 @@ namespace Avalonia.Layout protected internal override void UninitializeForContextCore(VirtualizingLayoutContext context) { - var stackState = (StackLayoutState)context.LayoutState; + var stackState = (StackLayoutState)context.LayoutState!; stackState.UninitializeForContext(context); } protected internal override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize) { - ((StackLayoutState)context.LayoutState).OnMeasureStart(); + ((StackLayoutState)context.LayoutState!).OnMeasureStart(); var desiredSize = GetFlowAlgorithm(context).Measure( availableSize, @@ -358,6 +358,6 @@ namespace Avalonia.Layout private void InvalidateLayout() => InvalidateMeasure(); - private FlowLayoutAlgorithm GetFlowAlgorithm(VirtualizingLayoutContext context) => ((StackLayoutState)context.LayoutState).FlowAlgorithm; + private FlowLayoutAlgorithm GetFlowAlgorithm(VirtualizingLayoutContext context) => ((StackLayoutState)context.LayoutState!).FlowAlgorithm; } } diff --git a/src/Avalonia.Layout/UniformGridLayout.cs b/src/Avalonia.Layout/UniformGridLayout.cs index 6b147590ab..508113834a 100644 --- a/src/Avalonia.Layout/UniformGridLayout.cs +++ b/src/Avalonia.Layout/UniformGridLayout.cs @@ -258,7 +258,7 @@ namespace Avalonia.Layout Size availableSize, VirtualizingLayoutContext context) { - var gridState = (UniformGridLayoutState)context.LayoutState; + var gridState = (UniformGridLayoutState)context.LayoutState!; return new Size(gridState.EffectiveItemWidth, gridState.EffectiveItemHeight); } @@ -268,7 +268,7 @@ namespace Avalonia.Layout Size desiredSize, VirtualizingLayoutContext context) { - var gridState = (UniformGridLayoutState)context.LayoutState; + var gridState = (UniformGridLayoutState)context.LayoutState!; return new Size(gridState.EffectiveItemWidth, gridState.EffectiveItemHeight); } @@ -285,7 +285,7 @@ namespace Avalonia.Layout var realizationRect = context.RealizationRect; if (itemsCount > 0 && _orientation.MajorSize(realizationRect) > 0) { - var gridState = (UniformGridLayoutState)context.LayoutState; + var gridState = (UniformGridLayoutState)context.LayoutState!; var lastExtent = gridState.FlowAlgorithm.LastExtent; var itemsPerLine = Math.Min( // note use of unsigned ints Math.Max(1u, (uint)(_orientation.Minor(availableSize) / GetMinorSizeWithSpacing(context))), @@ -324,7 +324,7 @@ namespace Avalonia.Layout Math.Max(1u, _maximumRowsOrColumns)); int indexOfFirstInLine = (targetIndex / itemsPerLine) * itemsPerLine; index = indexOfFirstInLine; - var state = context.LayoutState as UniformGridLayoutState; + var state = (UniformGridLayoutState)context.LayoutState!; offset = _orientation.MajorStart(GetLayoutRectForDataIndex(availableSize, indexOfFirstInLine, state.FlowAlgorithm.LastExtent, context)); } @@ -338,10 +338,10 @@ namespace Avalonia.Layout Rect IFlowLayoutAlgorithmDelegates.Algorithm_GetExtent( Size availableSize, VirtualizingLayoutContext context, - ILayoutable firstRealized, + ILayoutable? firstRealized, int firstRealizedItemIndex, Rect firstRealizedLayoutBounds, - ILayoutable lastRealized, + ILayoutable? lastRealized, int lastRealizedItemIndex, Rect lastRealizedLayoutBounds) { @@ -421,7 +421,7 @@ namespace Avalonia.Layout protected internal override void UninitializeForContextCore(VirtualizingLayoutContext context) { - var gridState = (UniformGridLayoutState)context.LayoutState; + var gridState = (UniformGridLayoutState)context.LayoutState!; gridState.UninitializeForContext(context); } @@ -429,7 +429,7 @@ namespace Avalonia.Layout { // Set the width and height on the grid state. If the user already set them then use the preset. // If not, we have to measure the first element and get back a size which we're going to be using for the rest of the items. - var gridState = (UniformGridLayoutState)context.LayoutState; + var gridState = (UniformGridLayoutState)context.LayoutState!; gridState.EnsureElementSize(availableSize, context, _minItemWidth, _minItemHeight, _itemsStretch, Orientation, MinRowSpacing, MinColumnSpacing, _maximumRowsOrColumns); var desiredSize = GetFlowAlgorithm(context).Measure( @@ -467,7 +467,7 @@ namespace Avalonia.Layout // Always invalidate layout to keep the view accurate. InvalidateLayout(); - var gridState = (UniformGridLayoutState)context.LayoutState; + var gridState = (UniformGridLayoutState)context.LayoutState!; gridState.ClearElementOnDataSourceChange(context, args); } @@ -517,7 +517,7 @@ namespace Avalonia.Layout private double GetMinorSizeWithSpacing(VirtualizingLayoutContext context) { var minItemSpacing = MinItemSpacing; - var gridState = (UniformGridLayoutState)context.LayoutState; + var gridState = (UniformGridLayoutState)context.LayoutState!; return _orientation.ScrollOrientation == ScrollOrientation.Vertical? gridState.EffectiveItemWidth + minItemSpacing : gridState.EffectiveItemHeight + minItemSpacing; @@ -526,7 +526,7 @@ namespace Avalonia.Layout private double GetMajorSizeWithSpacing(VirtualizingLayoutContext context) { var lineSpacing = LineSpacing; - var gridState = (UniformGridLayoutState)context.LayoutState; + var gridState = (UniformGridLayoutState)context.LayoutState!; return _orientation.ScrollOrientation == ScrollOrientation.Vertical ? gridState.EffectiveItemHeight + lineSpacing : gridState.EffectiveItemWidth + lineSpacing; @@ -544,7 +544,7 @@ namespace Avalonia.Layout int rowIndex = (int)(index / itemsPerLine); int indexInRow = index - (rowIndex * itemsPerLine); - var gridState = (UniformGridLayoutState)context.LayoutState; + var gridState = (UniformGridLayoutState)context.LayoutState!; Rect bounds = _orientation.MinorMajorRect( indexInRow * GetMinorSizeWithSpacing(context) + _orientation.MinorStart(lastExtent), rowIndex * GetMajorSizeWithSpacing(context) + _orientation.MajorStart(lastExtent), @@ -556,6 +556,6 @@ namespace Avalonia.Layout private void InvalidateLayout() => InvalidateMeasure(); - private FlowLayoutAlgorithm GetFlowAlgorithm(VirtualizingLayoutContext context) => ((UniformGridLayoutState)context.LayoutState).FlowAlgorithm; + private FlowLayoutAlgorithm GetFlowAlgorithm(VirtualizingLayoutContext context) => ((UniformGridLayoutState)context.LayoutState!).FlowAlgorithm; } } diff --git a/src/Avalonia.Layout/UniformGridLayoutState.cs b/src/Avalonia.Layout/UniformGridLayoutState.cs index 282bbab1a8..d6b5a30bfc 100644 --- a/src/Avalonia.Layout/UniformGridLayoutState.cs +++ b/src/Avalonia.Layout/UniformGridLayoutState.cs @@ -18,7 +18,7 @@ namespace Avalonia.Layout // If it does not, then we need to do context.GetElement(0) at which point we have requested an element and are on point to clear it. // If we are responsible for clearing element 0 we keep m_cachedFirstElement valid. // If we are not (because FlowLayoutAlgorithm is holding it for us) then we just null out this field and use the one from FlowLayoutAlgorithm. - private ILayoutable _cachedFirstElement; + private ILayoutable? _cachedFirstElement; internal FlowLayoutAlgorithm FlowAlgorithm { get; } = new FlowLayoutAlgorithm(); internal double EffectiveItemWidth { get; private set; } diff --git a/src/Avalonia.Layout/Utils/ListUtils.cs b/src/Avalonia.Layout/Utils/ListUtils.cs index eb2480acd3..9ca8bb0525 100644 --- a/src/Avalonia.Layout/Utils/ListUtils.cs +++ b/src/Avalonia.Layout/Utils/ListUtils.cs @@ -23,10 +23,5 @@ namespace Avalonia.Layout.Utils list.AddRange(Enumerable.Repeat(value, size - cur)); } } - - public static void Resize(this List list, int count) - { - Resize(list, count, default); - } } } diff --git a/src/Avalonia.Layout/VirtualLayoutContextAdapter.cs b/src/Avalonia.Layout/VirtualLayoutContextAdapter.cs index 80ccee2114..f068187523 100644 --- a/src/Avalonia.Layout/VirtualLayoutContextAdapter.cs +++ b/src/Avalonia.Layout/VirtualLayoutContextAdapter.cs @@ -6,14 +6,14 @@ namespace Avalonia.Layout public class VirtualLayoutContextAdapter : NonVirtualizingLayoutContext { private readonly VirtualizingLayoutContext _virtualizingContext; - private ChildrenCollection _children; + private ChildrenCollection? _children; public VirtualLayoutContextAdapter(VirtualizingLayoutContext virtualizingContext) { _virtualizingContext = virtualizingContext; } - protected override object LayoutStateCore + protected override object? LayoutStateCore { get => _virtualizingContext.LayoutState; set => _virtualizingContext.LayoutState = value; diff --git a/src/Avalonia.Layout/VirtualizingLayoutContext.cs b/src/Avalonia.Layout/VirtualizingLayoutContext.cs index 079b91a90f..f48c9b0ef2 100644 --- a/src/Avalonia.Layout/VirtualizingLayoutContext.cs +++ b/src/Avalonia.Layout/VirtualizingLayoutContext.cs @@ -43,7 +43,7 @@ namespace Avalonia.Layout /// public abstract class VirtualizingLayoutContext : LayoutContext { - private NonVirtualizingLayoutContext _contextAdapter; + private NonVirtualizingLayoutContext? _contextAdapter; /// /// Gets the number of items in the data. diff --git a/src/Avalonia.Layout/WrapLayout/UvMeasure.cs b/src/Avalonia.Layout/WrapLayout/UvMeasure.cs index e10e6bd0f8..91fa459acb 100644 --- a/src/Avalonia.Layout/WrapLayout/UvMeasure.cs +++ b/src/Avalonia.Layout/WrapLayout/UvMeasure.cs @@ -27,7 +27,7 @@ namespace Avalonia.Layout } } - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (obj is UvMeasure measure) { diff --git a/src/Avalonia.Layout/WrapLayout/WrapItem.cs b/src/Avalonia.Layout/WrapLayout/WrapItem.cs index 2f0b39a2d1..e823ade6d4 100644 --- a/src/Avalonia.Layout/WrapLayout/WrapItem.cs +++ b/src/Avalonia.Layout/WrapLayout/WrapItem.cs @@ -20,6 +20,6 @@ namespace Avalonia.Layout public UvMeasure? Position { get; internal set; } - public ILayoutable Element { get; internal set; } + public ILayoutable? Element { get; internal set; } } } diff --git a/src/Avalonia.Layout/WrapLayout/WrapLayout.cs b/src/Avalonia.Layout/WrapLayout/WrapLayout.cs index ded2afc3dd..d12a83d1d7 100644 --- a/src/Avalonia.Layout/WrapLayout/WrapLayout.cs +++ b/src/Avalonia.Layout/WrapLayout/WrapLayout.cs @@ -88,7 +88,7 @@ namespace Avalonia.Layout /// protected internal override void OnItemsChangedCore(VirtualizingLayoutContext context, object source, NotifyCollectionChangedEventArgs args) { - var state = (WrapLayoutState)context.LayoutState; + var state = (WrapLayoutState)context.LayoutState!; switch (args.Action) { @@ -126,7 +126,7 @@ namespace Avalonia.Layout var realizationBounds = new UvBounds(Orientation, context.RealizationRect); var position = UvMeasure.Zero; - var state = (WrapLayoutState)context.LayoutState; + var state = (WrapLayoutState)context.LayoutState!; if (state.Orientation != Orientation) { state.SetOrientation(Orientation); @@ -261,7 +261,7 @@ namespace Avalonia.Layout var spacingMeasure = new UvMeasure(Orientation, HorizontalSpacing, VerticalSpacing); var realizationBounds = new UvBounds(Orientation, context.RealizationRect); - var state = (WrapLayoutState)context.LayoutState; + var state = (WrapLayoutState)context.LayoutState!; bool Arrange(WrapItem item, bool isLast = false) { if (item.Measure.HasValue == false) diff --git a/src/Avalonia.Layout/WrapLayout/WrapLayoutState.cs b/src/Avalonia.Layout/WrapLayout/WrapLayoutState.cs index 7955ea983c..720ab96830 100644 --- a/src/Avalonia.Layout/WrapLayout/WrapLayoutState.cs +++ b/src/Avalonia.Layout/WrapLayout/WrapLayoutState.cs @@ -62,8 +62,11 @@ namespace Avalonia.Layout internal void SetOrientation(Orientation orientation) { - foreach (var item in _items.Where(i => i.Measure.HasValue)) + foreach (var item in _items) { + if (!item.Measure.HasValue) + continue; + UvMeasure measure = item.Measure.Value; double v = measure.V; measure.V = measure.U; @@ -120,10 +123,10 @@ namespace Avalonia.Layout } lastPosition = item.Position; - maxV = Math.Max(maxV, item.Measure.Value.V); + maxV = Math.Max(maxV, item.Measure!.Value.V); } - double totalHeight = lastPosition.Value.V + maxV; + double totalHeight = lastPosition!.Value.V + maxV; if (calculateAverage) { return (totalHeight / itemCount) * _context.ItemCount; From ecb981da2d1ca386a350d108b72e30835d77f044 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 20 Dec 2021 09:30:10 +0100 Subject: [PATCH 56/90] Small tidy up. --- src/Avalonia.Layout/ElementManager.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Layout/ElementManager.cs b/src/Avalonia.Layout/ElementManager.cs index 039eb52317..9a033ca447 100644 --- a/src/Avalonia.Layout/ElementManager.cs +++ b/src/Avalonia.Layout/ElementManager.cs @@ -75,7 +75,9 @@ namespace Avalonia.Layout if (IsVirtualizingContext) { - if (_realizedElements[realizedIndex] == null) + element = _realizedElements[realizedIndex]; + + if (element == null) { // Sentinel. Create the element now since we need it. int dataIndex = GetDataIndexFromRealizedRangeIndex(realizedIndex); @@ -85,10 +87,6 @@ namespace Avalonia.Layout ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle); _realizedElements[realizedIndex] = element; } - else - { - element = _realizedElements[realizedIndex]; - } } else { @@ -98,7 +96,7 @@ namespace Avalonia.Layout ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle); } - return element!; + return element; } public void Add(ILayoutable element, int dataIndex) From f9956e5ba96eedfe5e2872c84125e80f5ad14faa Mon Sep 17 00:00:00 2001 From: workgroupengineering Date: Mon, 20 Dec 2021 10:50:48 +0100 Subject: [PATCH 57/90] fixes:(XMLDOC): update xml comment (#6999) * fixes:(XMLDOC): update xml comment * Update src/Avalonia.Controls/Grid.cs Co-authored-by: Steven Kirk Co-authored-by: Steven Kirk --- src/Avalonia.Controls/Grid.cs | 2 +- src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/Grid.cs b/src/Avalonia.Controls/Grid.cs index 4e60b52f83..74401c450a 100644 --- a/src/Avalonia.Controls/Grid.cs +++ b/src/Avalonia.Controls/Grid.cs @@ -978,7 +978,7 @@ namespace Avalonia.Controls /// width is not registered in columns. /// Passed through to MeasureCell. /// When "true" cells' desired height is not registered in rows. - /// return true when desired size has changed + /// When the method exits, indicates whether the desired size has changed. private void MeasureCellsGroup( int cellsHead, Size referenceSize, diff --git a/src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs b/src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs index 32818dfdd2..4a7d4d4ac8 100644 --- a/src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs +++ b/src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs @@ -20,8 +20,8 @@ namespace Avalonia.Skia /// /// Creates an offscreen render target surface /// - /// size in pixels - /// current Skia render session + /// size in pixels. + /// An optional custom render session. ISkiaSurface TryCreateSurface(PixelSize size, ISkiaGpuRenderSession session); } From 1aa0d5b68c2660bb2038ea6460ad41c2dbd7d4fa Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Mon, 20 Dec 2021 15:09:52 +0100 Subject: [PATCH 58/90] fixes: null annotation --- src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs | 2 +- src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs index 620477b6cd..a9fc18474c 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs +++ b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs @@ -515,7 +515,7 @@ namespace Avalonia.Markup.Parsers public string Value { get; set; } = string.Empty; - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is AttachedPropertySyntax syntax && syntax.Xmlns == Xmlns diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs index e2d6570790..1b7ee9025e 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs +++ b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs @@ -119,7 +119,7 @@ namespace Avalonia.Markup.Parsers } { - object typedValue; + object? typedValue; if (TypeUtilities.TryConvert( targetAttachedProperty.PropertyType, From f8f4b91d93a4b0545c9b2045913ada25991db9b1 Mon Sep 17 00:00:00 2001 From: Tim Date: Fri, 17 Dec 2021 20:10:56 +0100 Subject: [PATCH 59/90] Update Calendar in CalendarDatePicker Popup properly - Use `TemplateBinding` - Reflect changes of the blackout dates collection --- .../Calendar/CalendarDatePicker.cs | 63 ++++++++++++++----- .../CalendarDatePicker.xaml | 6 +- .../Controls/CalendarDatePicker.xaml | 6 +- 3 files changed, 58 insertions(+), 17 deletions(-) diff --git a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs b/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs index af3fdbd662..ced72651af 100644 --- a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs +++ b/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs @@ -1,10 +1,11 @@ -// (c) Copyright Microsoft Corporation. +// (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Collections.ObjectModel; +using System.Collections.Specialized; using System.Diagnostics; using System.Globalization; using Avalonia.Controls.Primitives; @@ -425,9 +426,6 @@ namespace Avalonia.Controls { FocusableProperty.OverrideDefaultValue(true); - DisplayDateProperty.Changed.AddClassHandler((x,e) => x.OnDisplayDateChanged(e)); - DisplayDateStartProperty.Changed.AddClassHandler((x,e) => x.OnDisplayDateStartChanged(e)); - DisplayDateEndProperty.Changed.AddClassHandler((x,e) => x.OnDisplayDateEndChanged(e)); IsDropDownOpenProperty.Changed.AddClassHandler((x,e) => x.OnIsDropDownOpenChanged(e)); SelectedDateProperty.Changed.AddClassHandler((x,e) => x.OnSelectedDateChanged(e)); SelectedDateFormatProperty.Changed.AddClassHandler((x,e) => x.OnSelectedDateFormatChanged(e)); @@ -443,6 +441,7 @@ namespace Avalonia.Controls FirstDayOfWeek = DateTimeHelper.GetCurrentDateFormat().FirstDayOfWeek; _defaultText = string.Empty; DisplayDate = DateTime.Today; + BlackoutDates.CollectionChanged += BlackoutDates_CollectionChanged; } protected override void OnApplyTemplate(TemplateAppliedEventArgs e) @@ -459,18 +458,12 @@ namespace Avalonia.Controls if (_calendar != null) { _calendar.SelectionMode = CalendarSelectionMode.SingleDate; - _calendar.SelectedDate = SelectedDate; - SetCalendarDisplayDate(DisplayDate); - SetCalendarDisplayDateStart(DisplayDateStart); - SetCalendarDisplayDateEnd(DisplayDateEnd); _calendar.DayButtonMouseUp += Calendar_DayButtonMouseUp; _calendar.DisplayDateChanged += Calendar_DisplayDateChanged; _calendar.SelectedDatesChanged += Calendar_SelectedDatesChanged; _calendar.PointerReleased += Calendar_PointerReleased; _calendar.KeyDown += Calendar_KeyDown; - //_calendar.SizeChanged += new SizeChangedEventHandler(Calendar_SizeChanged); - //_calendar.IsTabStop = true; var currentBlackoutDays = BlackoutDates; BlackoutDates = _calendar.BlackoutDates; @@ -670,11 +663,6 @@ namespace Avalonia.Controls var addedDate = (DateTime?)e.NewValue; var removedDate = (DateTime?)e.OldValue; - if (_calendar != null && addedDate != _calendar.SelectedDate) - { - _calendar.SelectedDate = addedDate; - } - if (SelectedDate != null) { DateTime day = SelectedDate.Value; @@ -1200,5 +1188,50 @@ namespace Avalonia.Controls return newD; } } + + private void BlackoutDates_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + foreach (CalendarDateRange item in e.NewItems) + { + _calendar.BlackoutDates.Add(item); + } + break; + + case NotifyCollectionChangedAction.Move: + // We do not need to handle this case as the result will not have an effect. + break; + case NotifyCollectionChangedAction.Remove: + foreach (CalendarDateRange item in e.OldItems) + { + _calendar.BlackoutDates.Remove(item); + } + break; + + case NotifyCollectionChangedAction.Replace: + foreach (CalendarDateRange item in e.OldItems) + { + _calendar.BlackoutDates.Remove(item); + } + foreach (CalendarDateRange item in e.NewItems) + { + _calendar.BlackoutDates.Add(item); + } + break; + + case NotifyCollectionChangedAction.Reset: + _calendar.BlackoutDates.Clear(); + foreach (CalendarDateRange item in BlackoutDates) + { + _calendar.BlackoutDates.Add(item); + } + break; + + default: + throw new ArgumentOutOfRangeException(nameof(e.Action), e.Action, "Invalid NotifyCollectionChangedAction"); + } + } } } diff --git a/src/Avalonia.Themes.Default/CalendarDatePicker.xaml b/src/Avalonia.Themes.Default/CalendarDatePicker.xaml index 57b77f70ea..3e2e497d2c 100644 --- a/src/Avalonia.Themes.Default/CalendarDatePicker.xaml +++ b/src/Avalonia.Themes.Default/CalendarDatePicker.xaml @@ -115,7 +115,11 @@ StaysOpen="False"> + IsTodayHighlighted="{TemplateBinding IsTodayHighlighted}" + SelectedDate="{TemplateBinding SelectedDate, Mode=TwoWay}" + DisplayDate="{TemplateBinding DisplayDate}" + DisplayDateStart="{TemplateBinding DisplayDateStart}" + DisplayDateEnd="{TemplateBinding DisplayDateEnd}" /> diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml index 995cca2b82..6c4e94caf1 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml @@ -129,7 +129,11 @@ IsLightDismissEnabled="True"> + IsTodayHighlighted="{TemplateBinding IsTodayHighlighted}" + SelectedDate="{TemplateBinding SelectedDate, Mode=TwoWay}" + DisplayDate="{TemplateBinding DisplayDate}" + DisplayDateStart="{TemplateBinding DisplayDateStart}" + DisplayDateEnd="{TemplateBinding DisplayDateEnd}" /> From f69e3e642efc67f431476370c024338b7d04f1b2 Mon Sep 17 00:00:00 2001 From: Tim U Date: Mon, 20 Dec 2021 09:52:14 +0100 Subject: [PATCH 60/90] Fix: BlackoutDates test was failing - Revert listen to colleciton changes as the collection is already synced - Setup the needed bindings in the test control --- .../Calendar/CalendarDatePicker.cs | 47 ------------------- .../CalendarDatePickerTests.cs | 6 ++- 2 files changed, 5 insertions(+), 48 deletions(-) diff --git a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs b/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs index ced72651af..acfcfecdd8 100644 --- a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs +++ b/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs @@ -5,7 +5,6 @@ using System; using System.Collections.ObjectModel; -using System.Collections.Specialized; using System.Diagnostics; using System.Globalization; using Avalonia.Controls.Primitives; @@ -441,7 +440,6 @@ namespace Avalonia.Controls FirstDayOfWeek = DateTimeHelper.GetCurrentDateFormat().FirstDayOfWeek; _defaultText = string.Empty; DisplayDate = DateTime.Today; - BlackoutDates.CollectionChanged += BlackoutDates_CollectionChanged; } protected override void OnApplyTemplate(TemplateAppliedEventArgs e) @@ -1188,50 +1186,5 @@ namespace Avalonia.Controls return newD; } } - - private void BlackoutDates_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - switch (e.Action) - { - case NotifyCollectionChangedAction.Add: - foreach (CalendarDateRange item in e.NewItems) - { - _calendar.BlackoutDates.Add(item); - } - break; - - case NotifyCollectionChangedAction.Move: - // We do not need to handle this case as the result will not have an effect. - break; - case NotifyCollectionChangedAction.Remove: - foreach (CalendarDateRange item in e.OldItems) - { - _calendar.BlackoutDates.Remove(item); - } - break; - - case NotifyCollectionChangedAction.Replace: - foreach (CalendarDateRange item in e.OldItems) - { - _calendar.BlackoutDates.Remove(item); - } - foreach (CalendarDateRange item in e.NewItems) - { - _calendar.BlackoutDates.Add(item); - } - break; - - case NotifyCollectionChangedAction.Reset: - _calendar.BlackoutDates.Clear(); - foreach (CalendarDateRange item in BlackoutDates) - { - _calendar.BlackoutDates.Add(item); - } - break; - - default: - throw new ArgumentOutOfRangeException(nameof(e.Action), e.Action, "Invalid NotifyCollectionChangedAction"); - } - } } } diff --git a/tests/Avalonia.Controls.UnitTests/CalendarDatePickerTests.cs b/tests/Avalonia.Controls.UnitTests/CalendarDatePickerTests.cs index d77c7b87fa..a73e14939d 100644 --- a/tests/Avalonia.Controls.UnitTests/CalendarDatePickerTests.cs +++ b/tests/Avalonia.Controls.UnitTests/CalendarDatePickerTests.cs @@ -105,7 +105,11 @@ namespace Avalonia.Controls.UnitTests var calendar = new Calendar { - Name = "PART_Calendar" + Name = "PART_Calendar", + [!Calendar.SelectedDateProperty] = control[!CalendarDatePicker.SelectedDateProperty], + [!Calendar.DisplayDateProperty] = control[!CalendarDatePicker.DisplayDateProperty], + [!Calendar.DisplayDateStartProperty] = control[!CalendarDatePicker.DisplayDateStartProperty], + [!Calendar.DisplayDateEndProperty] = control[!CalendarDatePicker.DisplayDateEndProperty] }.RegisterInNameScope(scope); var popup = new Popup From 0f3b97a7525ec209a946fa89d0262b25888a4a8a Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 20 Dec 2021 20:14:34 +0300 Subject: [PATCH 61/90] Use pointer position relative to the scroll gesture recognizer target --- .../GestureRecognizers/ScrollGestureRecognizer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Input/GestureRecognizers/ScrollGestureRecognizer.cs b/src/Avalonia.Input/GestureRecognizers/ScrollGestureRecognizer.cs index 84a26a0cc3..7532676f18 100644 --- a/src/Avalonia.Input/GestureRecognizers/ScrollGestureRecognizer.cs +++ b/src/Avalonia.Input/GestureRecognizers/ScrollGestureRecognizer.cs @@ -85,7 +85,7 @@ namespace Avalonia.Input.GestureRecognizers { if (e.Pointer == _tracking) { - var rootPoint = e.GetPosition(null); + var rootPoint = e.GetPosition(_target); if (!_scrolling) { if (CanHorizontallyScroll && Math.Abs(_trackedRootPoint.X - rootPoint.X) > ScrollStartDistance) From dea04d8a7455cee215860ea6ab1de063ee1b121e Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 20 Dec 2021 17:20:57 +0000 Subject: [PATCH 62/90] Update readme.md --- readme.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/readme.md b/readme.md index a8a6399f2f..9895d3b68f 100644 --- a/readme.md +++ b/readme.md @@ -3,6 +3,8 @@
[![NuGet](https://img.shields.io/nuget/v/Avalonia.svg)](https://www.nuget.org/packages/Avalonia) [![downloads](https://img.shields.io/nuget/dt/avalonia)](https://www.nuget.org/packages/Avalonia) [![MyGet](https://img.shields.io/myget/avalonia-ci/vpre/Avalonia.svg?label=myget)](https://www.myget.org/gallery/avalonia-ci) ![Size](https://img.shields.io/github/repo-size/avaloniaui/avalonia.svg) +Tips: BTC: bc1q05wx78qemgy9x6ytl5ljk2xrt00yqargyjm8gx + ## 📖 About AvaloniaUI Avalonia is a cross-platform XAML-based UI framework providing a flexible styling system and supporting a wide range of Operating Systems such as Windows via .NET Framework and .NET Core, Linux via Xorg, macOS. Avalonia is ready for **General-Purpose Desktop App Development**. However, there may be some bugs and breaking changes as we continue along into this project's development. From e8f6f72ebaa610c9448282cb06659c31a8f691c2 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 20 Dec 2021 17:26:54 +0000 Subject: [PATCH 63/90] Update readme.md --- readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.md b/readme.md index 9895d3b68f..3efc3211ee 100644 --- a/readme.md +++ b/readme.md @@ -4,6 +4,7 @@ [![NuGet](https://img.shields.io/nuget/v/Avalonia.svg)](https://www.nuget.org/packages/Avalonia) [![downloads](https://img.shields.io/nuget/dt/avalonia)](https://www.nuget.org/packages/Avalonia) [![MyGet](https://img.shields.io/myget/avalonia-ci/vpre/Avalonia.svg?label=myget)](https://www.myget.org/gallery/avalonia-ci) ![Size](https://img.shields.io/github/repo-size/avaloniaui/avalonia.svg) Tips: BTC: bc1q05wx78qemgy9x6ytl5ljk2xrt00yqargyjm8gx +This will be shared with the community and awarded for significant contributions. ## 📖 About AvaloniaUI From e7286a8df87ca4ec1940bcd46f07dff926295aa2 Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 20 Dec 2021 19:57:13 +0100 Subject: [PATCH 64/90] Remove unused private voids --- .../Calendar/CalendarDatePicker.cs | 52 ------------------- 1 file changed, 52 deletions(-) diff --git a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs b/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs index acfcfecdd8..a856ee071c 100644 --- a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs +++ b/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs @@ -578,58 +578,6 @@ namespace Avalonia.Controls SetSelectedDate(); } - private void SetCalendarDisplayDate(DateTime value) - { - if (DateTimeHelper.CompareYearMonth(_calendar.DisplayDate, value) != 0) - { - _calendar.DisplayDate = DisplayDate; - if (DateTime.Compare(_calendar.DisplayDate, DisplayDate) != 0) - { - DisplayDate = _calendar.DisplayDate; - } - } - } - private void OnDisplayDateChanged(AvaloniaPropertyChangedEventArgs e) - { - if (_calendar != null) - { - var value = (DateTime)e.NewValue; - SetCalendarDisplayDate(value); - } - } - private void SetCalendarDisplayDateStart(DateTime? value) - { - _calendar.DisplayDateStart = value; - if (_calendar.DisplayDateStart.HasValue && DisplayDateStart.HasValue && DateTime.Compare(_calendar.DisplayDateStart.Value, DisplayDateStart.Value) != 0) - { - DisplayDateStart = _calendar.DisplayDateStart; - } - } - private void OnDisplayDateStartChanged(AvaloniaPropertyChangedEventArgs e) - { - if (_calendar != null) - { - var value = (DateTime?)e.NewValue; - SetCalendarDisplayDateStart(value); - } - } - private void SetCalendarDisplayDateEnd(DateTime? value) - { - _calendar.DisplayDateEnd = value; - if (_calendar.DisplayDateEnd.HasValue && DisplayDateEnd.HasValue && DateTime.Compare(_calendar.DisplayDateEnd.Value, DisplayDateEnd.Value) != 0) - { - DisplayDateEnd = _calendar.DisplayDateEnd; - } - - } - private void OnDisplayDateEndChanged(AvaloniaPropertyChangedEventArgs e) - { - if (_calendar != null) - { - var value = (DateTime?)e.NewValue; - SetCalendarDisplayDateEnd(value); - } - } private void OnIsDropDownOpenChanged(AvaloniaPropertyChangedEventArgs e) { var oldValue = (bool)e.OldValue; From 077364f2555f4e9fc3657e150cd07abb63e9d737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Su=C3=A1rez=20Ruiz?= Date: Tue, 21 Dec 2021 17:47:13 +0100 Subject: [PATCH 65/90] Allow launch ControlCatalog in Android --- .../ControlCatalog.Android.csproj | 8 ++- .../Avalonia.Android/AndroidPlatform.cs | 3 +- src/Android/Avalonia.Android/Stubs.cs | 56 +++++++++++++++++++ 3 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 src/Android/Avalonia.Android/Stubs.cs diff --git a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj index 1a68c4d732..617b6b6ab0 100644 --- a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj +++ b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj @@ -32,7 +32,7 @@ True False False - armeabi-v7a;x86 + armeabi-v7a;x86;x86_64 Xamarin False False @@ -51,7 +51,7 @@ True False False - armeabi-v7a,x86 + armeabi-v7a,x86;x86_64 Xamarin False False @@ -125,6 +125,10 @@ {42472427-4774-4c81-8aff-9f27b8e31721} Avalonia.Layout + + {c42d2fc1-a531-4ed4-84b9-89aec7c962fc} + Avalonia.Themes.Fluent + {eb582467-6abb-43a1-b052-e981ba910e3a} Avalonia.Visuals diff --git a/src/Android/Avalonia.Android/AndroidPlatform.cs b/src/Android/Avalonia.Android/AndroidPlatform.cs index 5e11d8eab2..57f22e7a05 100644 --- a/src/Android/Avalonia.Android/AndroidPlatform.cs +++ b/src/Android/Avalonia.Android/AndroidPlatform.cs @@ -43,11 +43,12 @@ namespace Avalonia.Android AvaloniaLocator.CurrentMutable .Bind().ToTransient() .Bind().ToTransient() + .Bind().ToConstant(new WindowingPlatformStub()) .Bind().ToSingleton() .Bind().ToConstant(Instance) .Bind().ToConstant(new AndroidThreadingInterface()) .Bind().ToTransient() - .Bind().ToSingleton() + .Bind().ToSingleton() .Bind().ToConstant(new ChoreographerTimer()) .Bind().ToConstant(new RenderLoop()) .Bind().ToSingleton() diff --git a/src/Android/Avalonia.Android/Stubs.cs b/src/Android/Avalonia.Android/Stubs.cs new file mode 100644 index 0000000000..f36c01dbc8 --- /dev/null +++ b/src/Android/Avalonia.Android/Stubs.cs @@ -0,0 +1,56 @@ +using System; +using System.IO; +using Avalonia.Platform; + +namespace Avalonia.Android +{ + class WindowingPlatformStub : IWindowingPlatform + { + public IWindowImpl CreateWindow() => throw new NotSupportedException(); + + public IWindowImpl CreateEmbeddableWindow() => throw new NotSupportedException(); + + public ITrayIconImpl CreateTrayIcon() => null; + } + + class PlatformIconLoaderStub : IPlatformIconLoader + { + public IWindowIconImpl LoadIcon(IBitmapImpl bitmap) + { + using (var stream = new MemoryStream()) + { + bitmap.Save(stream); + return LoadIcon(stream); + } + } + + public IWindowIconImpl LoadIcon(Stream stream) + { + var ms = new MemoryStream(); + stream.CopyTo(ms); + return new IconStub(ms); + } + + public IWindowIconImpl LoadIcon(string fileName) + { + using (var file = File.Open(fileName, FileMode.Open)) + return LoadIcon(file); + } + } + + public class IconStub : IWindowIconImpl + { + private readonly MemoryStream _ms; + + public IconStub(MemoryStream stream) + { + _ms = stream; + } + + public void Save(Stream outputStream) + { + _ms.Position = 0; + _ms.CopyTo(outputStream); + } + } +} From daee30758e478bdd1d222b242de2012f280863b6 Mon Sep 17 00:00:00 2001 From: Tako <53405089+Takoooooo@users.noreply.github.com> Date: Wed, 22 Dec 2021 11:50:25 +0200 Subject: [PATCH 66/90] Update readme --- readme.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index 3efc3211ee..b38552a010 100644 --- a/readme.md +++ b/readme.md @@ -3,9 +3,6 @@
[![NuGet](https://img.shields.io/nuget/v/Avalonia.svg)](https://www.nuget.org/packages/Avalonia) [![downloads](https://img.shields.io/nuget/dt/avalonia)](https://www.nuget.org/packages/Avalonia) [![MyGet](https://img.shields.io/myget/avalonia-ci/vpre/Avalonia.svg?label=myget)](https://www.myget.org/gallery/avalonia-ci) ![Size](https://img.shields.io/github/repo-size/avaloniaui/avalonia.svg) -Tips: BTC: bc1q05wx78qemgy9x6ytl5ljk2xrt00yqargyjm8gx -This will be shared with the community and awarded for significant contributions. - ## 📖 About AvaloniaUI Avalonia is a cross-platform XAML-based UI framework providing a flexible styling system and supporting a wide range of Operating Systems such as Windows via .NET Framework and .NET Core, Linux via Xorg, macOS. Avalonia is ready for **General-Purpose Desktop App Development**. However, there may be some bugs and breaking changes as we continue along into this project's development. @@ -78,6 +75,12 @@ For more information see the [.NET Foundation Code of Conduct](https://dotnetfou Avalonia is licenced under the [MIT licence](licence.md). +## Support Avalonia + +**BTC**: bc1q05wx78qemgy9x6ytl5ljk2xrt00yqargyjm8gx + +This will be shared with the community and awarded for significant contributions. + ### Backers Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/Avalonia#backer)] From 328199b2d22aafcb194f961508ac8b59d394b820 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 22 Dec 2021 14:02:28 +0100 Subject: [PATCH 67/90] Added nullable annotations to Avalonia.Styling. --- src/Avalonia.Styling/Avalonia.Styling.csproj | 1 + .../Controls/ChildNameScope.cs | 12 ++--- src/Avalonia.Styling/Controls/INameScope.cs | 4 +- src/Avalonia.Styling/Controls/NameScope.cs | 30 ++++++----- .../Controls/NameScopeExtensions.cs | 28 +++++------ .../Controls/NameScopeLocator.cs | 8 +-- .../Controls/PseudoClassesExtensions.cs | 2 +- .../Controls/ResourceDictionary.cs | 2 +- .../Controls/ResourceNodeExtensions.cs | 6 +-- src/Avalonia.Styling/INamed.cs | 2 +- .../LogicalTree/ControlLocator.cs | 16 +++--- src/Avalonia.Styling/LogicalTree/ILogical.cs | 6 +-- .../LogicalTree/LogicalExtensions.cs | 26 +++++----- .../LogicalTreeAttachmentEventArgs.cs | 7 +-- src/Avalonia.Styling/StyledElement.cs | 18 +++---- .../Styling/Activators/NthChildActivator.cs | 2 +- .../Styling/Activators/StyleClassActivator.cs | 2 +- src/Avalonia.Styling/Styling/ChildSelector.cs | 6 +-- src/Avalonia.Styling/Styling/OrSelector.cs | 2 +- .../Styling/PropertyEqualsSelector.cs | 2 +- src/Avalonia.Styling/Styling/Selectors.cs | 50 +++++++++++-------- src/Avalonia.Styling/Styling/Styles.cs | 14 +++--- .../Styling/TemplateSelector.cs | 6 +-- src/Avalonia.Visuals/Visual.cs | 2 +- .../Markup/Parsers/SelectorParser.cs | 6 +-- 25 files changed, 132 insertions(+), 128 deletions(-) diff --git a/src/Avalonia.Styling/Avalonia.Styling.csproj b/src/Avalonia.Styling/Avalonia.Styling.csproj index 20b3183b00..3548749846 100644 --- a/src/Avalonia.Styling/Avalonia.Styling.csproj +++ b/src/Avalonia.Styling/Avalonia.Styling.csproj @@ -9,4 +9,5 @@ + diff --git a/src/Avalonia.Styling/Controls/ChildNameScope.cs b/src/Avalonia.Styling/Controls/ChildNameScope.cs index e6707e71db..58114a57fd 100644 --- a/src/Avalonia.Styling/Controls/ChildNameScope.cs +++ b/src/Avalonia.Styling/Controls/ChildNameScope.cs @@ -15,20 +15,20 @@ namespace Avalonia.Controls public void Register(string name, object element) => _inner.Register(name, element); - public SynchronousCompletionAsyncResult FindAsync(string name) + public SynchronousCompletionAsyncResult FindAsync(string name) { var found = Find(name); if (found != null) - return new SynchronousCompletionAsyncResult(found); + return new SynchronousCompletionAsyncResult(found); // Not found and both current and parent scope are in completed state if(IsCompleted) - return new SynchronousCompletionAsyncResult(null); + return new SynchronousCompletionAsyncResult(null); return DoFindAsync(name); } - public SynchronousCompletionAsyncResult DoFindAsync(string name) + public SynchronousCompletionAsyncResult DoFindAsync(string name) { - var src = new SynchronousCompletionAsyncResultSource(); + var src = new SynchronousCompletionAsyncResultSource(); void ParentSearch() { @@ -56,7 +56,7 @@ namespace Avalonia.Controls return src.AsyncResult; } - public object Find(string name) + public object? Find(string name) { var found = _inner.Find(name); if (found != null) diff --git a/src/Avalonia.Styling/Controls/INameScope.cs b/src/Avalonia.Styling/Controls/INameScope.cs index 2d5295fe45..1ca7db2f37 100644 --- a/src/Avalonia.Styling/Controls/INameScope.cs +++ b/src/Avalonia.Styling/Controls/INameScope.cs @@ -22,14 +22,14 @@ namespace Avalonia.Controls /// /// The name. /// The element, or null if the name was not found. - SynchronousCompletionAsyncResult FindAsync(string name); + SynchronousCompletionAsyncResult FindAsync(string name); /// /// Finds a named element in the name scope, returns immediately, doesn't traverse the name scope stack /// /// The name. /// The element, or null if the name was not found. - object Find(string name); + object? Find(string name); /// /// Marks the name scope as completed, no further registrations will be allowed diff --git a/src/Avalonia.Styling/Controls/NameScope.cs b/src/Avalonia.Styling/Controls/NameScope.cs index 62a04eac8b..77f98f85c4 100644 --- a/src/Avalonia.Styling/Controls/NameScope.cs +++ b/src/Avalonia.Styling/Controls/NameScope.cs @@ -22,8 +22,8 @@ namespace Avalonia.Controls private readonly Dictionary _inner = new Dictionary(); - private readonly Dictionary> _pendingSearches = - new Dictionary>(); + private readonly Dictionary> _pendingSearches = + new Dictionary>(); /// /// Gets the value of the attached on a styled element. @@ -32,7 +32,7 @@ namespace Avalonia.Controls /// The value of the NameScope attached property. public static INameScope GetNameScope(StyledElement styled) { - Contract.Requires(styled != null); + _ = styled ?? throw new ArgumentNullException(nameof(styled)); return styled.GetValue(NameScopeProperty); } @@ -44,7 +44,7 @@ namespace Avalonia.Controls /// The value to set. public static void SetNameScope(StyledElement styled, INameScope value) { - Contract.Requires(styled != null); + _ = styled ?? throw new ArgumentNullException(nameof(styled)); styled.SetValue(NameScopeProperty, value); } @@ -54,12 +54,11 @@ namespace Avalonia.Controls { if (IsCompleted) throw new InvalidOperationException("NameScope is completed, no further registrations are allowed"); - Contract.Requires(name != null); - Contract.Requires(element != null); - object existing; + _ = name ?? throw new ArgumentNullException(nameof(name)); + _ = element ?? throw new ArgumentNullException(nameof(element)); - if (_inner.TryGetValue(name, out existing)) + if (_inner.TryGetValue(name, out var existing)) { if (existing != element) { @@ -77,27 +76,26 @@ namespace Avalonia.Controls } } - public SynchronousCompletionAsyncResult FindAsync(string name) + public SynchronousCompletionAsyncResult FindAsync(string name) { var found = Find(name); if (found != null) - return new SynchronousCompletionAsyncResult(found); + return new SynchronousCompletionAsyncResult(found); if (IsCompleted) - return new SynchronousCompletionAsyncResult((object)null); + return new SynchronousCompletionAsyncResult(null); if (!_pendingSearches.TryGetValue(name, out var tcs)) // We are intentionally running continuations synchronously here - _pendingSearches[name] = tcs = new SynchronousCompletionAsyncResultSource(); + _pendingSearches[name] = tcs = new SynchronousCompletionAsyncResultSource(); return tcs.AsyncResult; } /// - public object Find(string name) + public object? Find(string name) { - Contract.Requires(name != null); + _ = name ?? throw new ArgumentNullException(nameof(name)); - object result; - _inner.TryGetValue(name, out result); + _inner.TryGetValue(name, out var result); return result; } diff --git a/src/Avalonia.Styling/Controls/NameScopeExtensions.cs b/src/Avalonia.Styling/Controls/NameScopeExtensions.cs index 75630711b8..3895b6ceb9 100644 --- a/src/Avalonia.Styling/Controls/NameScopeExtensions.cs +++ b/src/Avalonia.Styling/Controls/NameScopeExtensions.cs @@ -17,11 +17,11 @@ namespace Avalonia.Controls /// The name scope. /// The name. /// The named element or null if not found. - public static T Find(this INameScope nameScope, string name) + public static T? Find(this INameScope nameScope, string name) where T : class { - Contract.Requires(nameScope != null); - Contract.Requires(name != null); + _ = nameScope ?? throw new ArgumentNullException(nameof(nameScope)); + _ = name ?? throw new ArgumentNullException(nameof(name)); var result = nameScope.Find(name); @@ -31,7 +31,7 @@ namespace Avalonia.Controls $"Expected control '{name}' to be '{typeof(T)} but it was '{result.GetType()}'."); } - return (T)result; + return (T?)result; } /// @@ -41,11 +41,11 @@ namespace Avalonia.Controls /// The control to take the name scope from. /// The name. /// The named element or null if not found. - public static T Find(this ILogical anchor, string name) + public static T? Find(this ILogical anchor, string name) where T : class { - Contract.Requires(anchor != null); - Contract.Requires(name != null); + _ = anchor ?? throw new ArgumentNullException(nameof(anchor)); + _ = name ?? throw new ArgumentNullException(nameof(name)); var styledAnchor = anchor as StyledElement; if (styledAnchor == null) return null; @@ -64,8 +64,8 @@ namespace Avalonia.Controls public static T Get(this INameScope nameScope, string name) where T : class { - Contract.Requires(nameScope != null); - Contract.Requires(name != null); + _ = nameScope ?? throw new ArgumentNullException(nameof(nameScope)); + _ = name ?? throw new ArgumentNullException(nameof(name)); var result = nameScope.Find(name); @@ -94,9 +94,9 @@ namespace Avalonia.Controls public static T Get(this ILogical anchor, string name) where T : class { - Contract.Requires(anchor != null); - Contract.Requires(name != null); - + _ = anchor ?? throw new ArgumentNullException(nameof(anchor)); + _ = name ?? throw new ArgumentNullException(nameof(name)); + var nameScope = (anchor as INameScope) ?? NameScope.GetNameScope((StyledElement)anchor); if (nameScope == null) throw new InvalidOperationException( @@ -105,9 +105,9 @@ namespace Avalonia.Controls return nameScope.Get(name); } - public static INameScope FindNameScope(this ILogical control) + public static INameScope? FindNameScope(this ILogical control) { - Contract.Requires(control != null); + _ = control ?? throw new ArgumentNullException(nameof(control)); var scope = control.GetSelfAndLogicalAncestors() .OfType() diff --git a/src/Avalonia.Styling/Controls/NameScopeLocator.cs b/src/Avalonia.Styling/Controls/NameScopeLocator.cs index 51f4c5c4eb..f0ce7f8a5b 100644 --- a/src/Avalonia.Styling/Controls/NameScopeLocator.cs +++ b/src/Avalonia.Styling/Controls/NameScopeLocator.cs @@ -11,9 +11,9 @@ namespace Avalonia.Controls /// /// The scope relative from which the object should be resolved. /// The name of the object to find. - public static IObservable Track(INameScope scope, string name) + public static IObservable Track(INameScope scope, string name) { - return new NeverEndingSynchronousCompletionAsyncResultObservable(scope.FindAsync(name)); + return new NeverEndingSynchronousCompletionAsyncResultObservable(scope.FindAsync(name)); } // This class is implemented in such weird way because for some reason @@ -22,7 +22,7 @@ namespace Avalonia.Controls private class NeverEndingSynchronousCompletionAsyncResultObservable : IObservable { - private T _value; + private T? _value; private SynchronousCompletionAsyncResult? _asyncResult; public NeverEndingSynchronousCompletionAsyncResultObservable(SynchronousCompletionAsyncResult task) @@ -47,7 +47,7 @@ namespace Avalonia.Controls observer.OnNext(_asyncResult.Value.GetResult()); }); else - observer.OnNext(_value); + observer.OnNext(_value!); return Disposable.Empty; } diff --git a/src/Avalonia.Styling/Controls/PseudoClassesExtensions.cs b/src/Avalonia.Styling/Controls/PseudoClassesExtensions.cs index 7b28d1a911..4cddc8c8fb 100644 --- a/src/Avalonia.Styling/Controls/PseudoClassesExtensions.cs +++ b/src/Avalonia.Styling/Controls/PseudoClassesExtensions.cs @@ -12,7 +12,7 @@ namespace Avalonia.Controls /// True to add the pseudoclass or false to remove. public static void Set(this IPseudoClasses classes, string name, bool value) { - Contract.Requires(classes != null); + _ = classes ?? throw new ArgumentNullException(nameof(classes)); if (value) { diff --git a/src/Avalonia.Styling/Controls/ResourceDictionary.cs b/src/Avalonia.Styling/Controls/ResourceDictionary.cs index e797f8cf8b..3af14daa83 100644 --- a/src/Avalonia.Styling/Controls/ResourceDictionary.cs +++ b/src/Avalonia.Styling/Controls/ResourceDictionary.cs @@ -177,7 +177,7 @@ namespace Avalonia.Controls } } - private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { Owner?.NotifyHostedResourcesChanged(ResourcesChangedEventArgs.Empty); } diff --git a/src/Avalonia.Styling/Controls/ResourceNodeExtensions.cs b/src/Avalonia.Styling/Controls/ResourceNodeExtensions.cs index be1069a4da..513b3f2424 100644 --- a/src/Avalonia.Styling/Controls/ResourceNodeExtensions.cs +++ b/src/Avalonia.Styling/Controls/ResourceNodeExtensions.cs @@ -109,7 +109,7 @@ namespace Avalonia.Controls observer.OnNext(Convert(_target.FindResource(_key))); } - private void ResourcesChanged(object sender, ResourcesChangedEventArgs e) + private void ResourcesChanged(object? sender, ResourcesChangedEventArgs e) { PublishNext(Convert(_target.FindResource(_key))); } @@ -159,7 +159,7 @@ namespace Avalonia.Controls } } - private void OwnerChanged(object sender, EventArgs e) + private void OwnerChanged(object? sender, EventArgs e) { if (_owner is object) { @@ -176,7 +176,7 @@ namespace Avalonia.Controls PublishNext(); } - private void ResourcesChanged(object sender, ResourcesChangedEventArgs e) + private void ResourcesChanged(object? sender, ResourcesChangedEventArgs e) { PublishNext(); } diff --git a/src/Avalonia.Styling/INamed.cs b/src/Avalonia.Styling/INamed.cs index 1f8d269b07..df83f3b6ef 100644 --- a/src/Avalonia.Styling/INamed.cs +++ b/src/Avalonia.Styling/INamed.cs @@ -8,6 +8,6 @@ namespace Avalonia /// /// Gets the element name. /// - string Name { get; } + string? Name { get; } } } diff --git a/src/Avalonia.Styling/LogicalTree/ControlLocator.cs b/src/Avalonia.Styling/LogicalTree/ControlLocator.cs index 6d0302ace4..f60ed5aab8 100644 --- a/src/Avalonia.Styling/LogicalTree/ControlLocator.cs +++ b/src/Avalonia.Styling/LogicalTree/ControlLocator.cs @@ -9,19 +9,19 @@ namespace Avalonia.LogicalTree /// public static class ControlLocator { - public static IObservable Track(ILogical relativeTo, int ancestorLevel, Type ancestorType = null) + public static IObservable Track(ILogical relativeTo, int ancestorLevel, Type? ancestorType = null) { return new ControlTracker(relativeTo, ancestorLevel, ancestorType); } - private class ControlTracker : LightweightObservableBase + private class ControlTracker : LightweightObservableBase { private readonly ILogical _relativeTo; private readonly int _ancestorLevel; - private readonly Type _ancestorType; - private ILogical _value; + private readonly Type? _ancestorType; + private ILogical? _value; - public ControlTracker(ILogical relativeTo, int ancestorLevel, Type ancestorType) + public ControlTracker(ILogical relativeTo, int ancestorLevel, Type? ancestorType) { _relativeTo = relativeTo; _ancestorLevel = ancestorLevel; @@ -43,18 +43,18 @@ namespace Avalonia.LogicalTree _value = null; } - protected override void Subscribed(IObserver observer, bool first) + protected override void Subscribed(IObserver observer, bool first) { observer.OnNext(_value); } - private void Attached(object sender, LogicalTreeAttachmentEventArgs e) + private void Attached(object? sender, LogicalTreeAttachmentEventArgs e) { Update(); PublishNext(_value); } - private void Detached(object sender, LogicalTreeAttachmentEventArgs e) + private void Detached(object? sender, LogicalTreeAttachmentEventArgs e) { _value = null; PublishNext(null); diff --git a/src/Avalonia.Styling/LogicalTree/ILogical.cs b/src/Avalonia.Styling/LogicalTree/ILogical.cs index 9c4223618f..caff3d8150 100644 --- a/src/Avalonia.Styling/LogicalTree/ILogical.cs +++ b/src/Avalonia.Styling/LogicalTree/ILogical.cs @@ -12,12 +12,12 @@ namespace Avalonia.LogicalTree /// /// Raised when the control is attached to a rooted logical tree. /// - event EventHandler AttachedToLogicalTree; + event EventHandler? AttachedToLogicalTree; /// /// Raised when the control is detached from a rooted logical tree. /// - event EventHandler DetachedFromLogicalTree; + event EventHandler? DetachedFromLogicalTree; /// /// Gets a value indicating whether the element is attached to a rooted logical tree. @@ -27,7 +27,7 @@ namespace Avalonia.LogicalTree /// /// Gets the logical parent. /// - ILogical LogicalParent { get; } + ILogical? LogicalParent { get; } /// /// Gets the logical children. diff --git a/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs b/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs index 458ab0fce2..74720c0a77 100644 --- a/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs +++ b/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs @@ -15,14 +15,14 @@ namespace Avalonia.LogicalTree /// The logical's ancestors. public static IEnumerable GetLogicalAncestors(this ILogical logical) { - Contract.Requires(logical != null); + _ = logical ?? throw new ArgumentNullException(nameof(logical)); - logical = logical.LogicalParent; + ILogical? l = logical.LogicalParent; - while (logical != null) + while (l != null) { - yield return logical; - logical = logical.LogicalParent; + yield return l; + l = l.LogicalParent; } } @@ -48,14 +48,14 @@ namespace Avalonia.LogicalTree /// The logical. /// If given logical should be included in search. /// First ancestor of given type. - public static T FindLogicalAncestorOfType(this ILogical logical, bool includeSelf = false) where T : class + public static T? FindLogicalAncestorOfType(this ILogical logical, bool includeSelf = false) where T : class { if (logical is null) { return null; } - ILogical parent = includeSelf ? logical : logical.LogicalParent; + var parent = includeSelf ? logical : logical.LogicalParent; while (parent != null) { @@ -120,7 +120,7 @@ namespace Avalonia.LogicalTree /// The logical. /// If given logical should be included in search. /// First descendant of given type. - public static T FindLogicalDescendantOfType(this ILogical logical, bool includeSelf = false) where T : class + public static T? FindLogicalDescendantOfType(this ILogical logical, bool includeSelf = false) where T : class { if (logical is null) { @@ -140,7 +140,7 @@ namespace Avalonia.LogicalTree /// /// The logical. /// The parent, or null if the logical is unparented. - public static ILogical GetLogicalParent(this ILogical logical) + public static ILogical? GetLogicalParent(this ILogical logical) { return logical.LogicalParent; } @@ -153,7 +153,7 @@ namespace Avalonia.LogicalTree /// /// The parent, or null if the logical is unparented or its parent is not of type . /// - public static T GetLogicalParent(this ILogical logical) where T : class + public static T? GetLogicalParent(this ILogical logical) where T : class { return logical.LogicalParent as T; } @@ -165,7 +165,7 @@ namespace Avalonia.LogicalTree /// The logical siblings. public static IEnumerable GetLogicalSiblings(this ILogical logical) { - ILogical parent = logical.LogicalParent; + var parent = logical.LogicalParent; if (parent != null) { @@ -187,7 +187,7 @@ namespace Avalonia.LogicalTree /// public static bool IsLogicalAncestorOf(this ILogical logical, ILogical target) { - ILogical current = target?.LogicalParent; + var current = target?.LogicalParent; while (current != null) { @@ -202,7 +202,7 @@ namespace Avalonia.LogicalTree return false; } - private static T FindDescendantOfTypeCore(ILogical logical) where T : class + private static T? FindDescendantOfTypeCore(ILogical logical) where T : class { var logicalChildren = logical.LogicalChildren; var logicalChildrenCount = logicalChildren.Count; diff --git a/src/Avalonia.Styling/LogicalTree/LogicalTreeAttachmentEventArgs.cs b/src/Avalonia.Styling/LogicalTree/LogicalTreeAttachmentEventArgs.cs index 891724599c..826fc5b2a4 100644 --- a/src/Avalonia.Styling/LogicalTree/LogicalTreeAttachmentEventArgs.cs +++ b/src/Avalonia.Styling/LogicalTree/LogicalTreeAttachmentEventArgs.cs @@ -19,11 +19,8 @@ namespace Avalonia.LogicalTree ILogical source, ILogical parent) { - Contract.Requires(root != null); - Contract.Requires(source != null); - - Root = root; - Source = source; + Root = root ?? throw new ArgumentNullException(nameof(root)); + Source = source ?? throw new ArgumentNullException(nameof(source)); Parent = parent; } diff --git a/src/Avalonia.Styling/StyledElement.cs b/src/Avalonia.Styling/StyledElement.cs index 2292f5c518..5f498623e1 100644 --- a/src/Avalonia.Styling/StyledElement.cs +++ b/src/Avalonia.Styling/StyledElement.cs @@ -427,7 +427,7 @@ namespace Avalonia if (_logicalRoot != null) { - var e = new LogicalTreeAttachmentEventArgs(_logicalRoot, this, old); + var e = new LogicalTreeAttachmentEventArgs(_logicalRoot, this, old!); OnDetachedFromLogicalTreeCore(e); } @@ -435,7 +435,7 @@ namespace Avalonia if (newRoot is object) { - var e = new LogicalTreeAttachmentEventArgs(newRoot, this, parent); + var e = new LogicalTreeAttachmentEventArgs(newRoot, this, parent!); OnAttachedToLogicalTreeCore(e); } else if (parent is null) @@ -495,21 +495,21 @@ namespace Avalonia DetachStylesFromThisAndDescendents(allStyles); } - protected virtual void LogicalChildrenCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + protected virtual void LogicalChildrenCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { switch (e.Action) { case NotifyCollectionChangedAction.Add: - SetLogicalParent(e.NewItems); + SetLogicalParent(e.NewItems!); break; case NotifyCollectionChangedAction.Remove: - ClearLogicalParent(e.OldItems); + ClearLogicalParent(e.OldItems!); break; case NotifyCollectionChangedAction.Replace: - ClearLogicalParent(e.OldItems); - SetLogicalParent(e.NewItems); + ClearLogicalParent(e.OldItems!); + SetLogicalParent(e.NewItems!); break; case NotifyCollectionChangedAction.Reset: @@ -729,7 +729,7 @@ namespace Avalonia for (var i = 0; i < count; i++) { - var logical = (ILogical) children[i]; + var logical = (ILogical) children[i]!; if (logical.LogicalParent is null) { @@ -744,7 +744,7 @@ namespace Avalonia for (var i = 0; i < count; i++) { - var logical = (ILogical) children[i]; + var logical = (ILogical) children[i]!; if (logical.LogicalParent == this) { diff --git a/src/Avalonia.Styling/Styling/Activators/NthChildActivator.cs b/src/Avalonia.Styling/Styling/Activators/NthChildActivator.cs index 803809a8ce..6f54cd5904 100644 --- a/src/Avalonia.Styling/Styling/Activators/NthChildActivator.cs +++ b/src/Avalonia.Styling/Styling/Activators/NthChildActivator.cs @@ -37,7 +37,7 @@ namespace Avalonia.Styling.Activators _provider.ChildIndexChanged -= ChildIndexChanged; } - private void ChildIndexChanged(object sender, ChildIndexChangedEventArgs e) + private void ChildIndexChanged(object? sender, ChildIndexChangedEventArgs e) { // Run matching again if: // 1. Selector is reversed, so other item insertion/deletion might affect total count without changing subscribed item index. diff --git a/src/Avalonia.Styling/Styling/Activators/StyleClassActivator.cs b/src/Avalonia.Styling/Styling/Activators/StyleClassActivator.cs index 7906a29cb5..98d3f16a0a 100644 --- a/src/Avalonia.Styling/Styling/Activators/StyleClassActivator.cs +++ b/src/Avalonia.Styling/Styling/Activators/StyleClassActivator.cs @@ -66,7 +66,7 @@ namespace Avalonia.Styling.Activators _classes.CollectionChanged -= ClassesChangedHandler; } - private void ClassesChanged(object sender, NotifyCollectionChangedEventArgs e) + private void ClassesChanged(object? sender, NotifyCollectionChangedEventArgs e) { if (e.Action != NotifyCollectionChangedAction.Move) { diff --git a/src/Avalonia.Styling/Styling/ChildSelector.cs b/src/Avalonia.Styling/Styling/ChildSelector.cs index 85e7aaabde..5c92182b80 100644 --- a/src/Avalonia.Styling/Styling/ChildSelector.cs +++ b/src/Avalonia.Styling/Styling/ChildSelector.cs @@ -6,7 +6,7 @@ namespace Avalonia.Styling internal class ChildSelector : Selector { private readonly Selector _parent; - private string _selectorString; + private string? _selectorString; public ChildSelector(Selector parent) { @@ -25,7 +25,7 @@ namespace Avalonia.Styling public override bool IsCombinator => true; /// - public override Type TargetType => null; + public override Type? TargetType => null; public override string ToString() { @@ -64,6 +64,6 @@ namespace Avalonia.Styling } } - protected override Selector MovePrevious() => null; + protected override Selector? MovePrevious() => null; } } diff --git a/src/Avalonia.Styling/Styling/OrSelector.cs b/src/Avalonia.Styling/Styling/OrSelector.cs index 8251915504..3d6db9b01e 100644 --- a/src/Avalonia.Styling/Styling/OrSelector.cs +++ b/src/Avalonia.Styling/Styling/OrSelector.cs @@ -120,7 +120,7 @@ namespace Avalonia.Styling } else { - while (!result.IsAssignableFrom(selector.TargetType)) + while (result is not null && !result.IsAssignableFrom(selector.TargetType)) { result = result.BaseType; } diff --git a/src/Avalonia.Styling/Styling/PropertyEqualsSelector.cs b/src/Avalonia.Styling/Styling/PropertyEqualsSelector.cs index ebdfff222a..1cd1a650ef 100644 --- a/src/Avalonia.Styling/Styling/PropertyEqualsSelector.cs +++ b/src/Avalonia.Styling/Styling/PropertyEqualsSelector.cs @@ -109,7 +109,7 @@ namespace Avalonia.Styling var converter = TypeDescriptor.GetConverter(propertyType); if (converter?.CanConvertFrom(valueType) == true) { - return Equals(propertyValue, converter.ConvertFrom(null, CultureInfo.InvariantCulture, value)); + return Equals(propertyValue, converter.ConvertFrom(null, CultureInfo.InvariantCulture, value!)); } return false; diff --git a/src/Avalonia.Styling/Styling/Selectors.cs b/src/Avalonia.Styling/Styling/Selectors.cs index 64d0a0e96b..7c66469cf1 100644 --- a/src/Avalonia.Styling/Styling/Selectors.cs +++ b/src/Avalonia.Styling/Styling/Selectors.cs @@ -25,10 +25,14 @@ namespace Avalonia.Styling /// The previous selector. /// The name of the style class. /// The selector. - public static Selector Class(this Selector previous, string name) + public static Selector Class(this Selector? previous, string name) { - Contract.Requires(name != null); - Contract.Requires(!string.IsNullOrWhiteSpace(name)); + _ = name ?? throw new ArgumentNullException(nameof(name)); + + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentException("Name may not be empty", nameof(name)); + } var tac = previous as TypeNameAndClassSelector; @@ -48,7 +52,7 @@ namespace Avalonia.Styling /// /// The previous selector. /// The selector. - public static Selector Descendant(this Selector previous) + public static Selector Descendant(this Selector? previous) { return new DescendantSelector(previous); } @@ -59,9 +63,9 @@ namespace Avalonia.Styling /// The previous selector. /// The type. /// The selector. - public static Selector Is(this Selector previous, Type type) + public static Selector Is(this Selector? previous, Type type) { - Contract.Requires(type != null); + _ = type ?? throw new ArgumentNullException(nameof(type)); return TypeNameAndClassSelector.Is(previous, type); } @@ -72,7 +76,7 @@ namespace Avalonia.Styling /// The type. /// The previous selector. /// The selector. - public static Selector Is(this Selector previous) where T : IStyleable + public static Selector Is(this Selector? previous) where T : IStyleable { return previous.Is(typeof(T)); } @@ -83,10 +87,14 @@ namespace Avalonia.Styling /// The previous selector. /// The name. /// The selector. - public static Selector Name(this Selector previous, string name) + public static Selector Name(this Selector? previous, string name) { - Contract.Requires(name != null); - Contract.Requires(!string.IsNullOrWhiteSpace(name)); + _ = name ?? throw new ArgumentNullException(nameof(name)); + + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentException("Name may not be empty", nameof(name)); + } var tac = previous as TypeNameAndClassSelector; @@ -107,7 +115,7 @@ namespace Avalonia.Styling /// The previous selector. /// The selector to be not-ed. /// The selector. - public static Selector Not(this Selector previous, Func argument) + public static Selector Not(this Selector? previous, Func argument) { return new NotSelector(previous, argument(null)); } @@ -118,7 +126,7 @@ namespace Avalonia.Styling /// The previous selector. /// The selector to be not-ed. /// The selector. - public static Selector Not(this Selector previous, Selector argument) + public static Selector Not(this Selector? previous, Selector argument) { return new NotSelector(previous, argument); } @@ -126,7 +134,7 @@ namespace Avalonia.Styling /// /// /// The selector. - public static Selector NthChild(this Selector previous, int step, int offset) + public static Selector NthChild(this Selector? previous, int step, int offset) { return new NthChildSelector(previous, step, offset); } @@ -134,7 +142,7 @@ namespace Avalonia.Styling /// /// /// The selector. - public static Selector NthLastChild(this Selector previous, int step, int offset) + public static Selector NthLastChild(this Selector? previous, int step, int offset) { return new NthLastChildSelector(previous, step, offset); } @@ -145,9 +153,9 @@ namespace Avalonia.Styling /// The previous selector. /// The type. /// The selector. - public static Selector OfType(this Selector previous, Type type) + public static Selector OfType(this Selector? previous, Type type) { - Contract.Requires(type != null); + _ = type ?? throw new ArgumentNullException(nameof(type)); return TypeNameAndClassSelector.OfType(previous, type); } @@ -158,7 +166,7 @@ namespace Avalonia.Styling /// The type. /// The previous selector. /// The selector. - public static Selector OfType(this Selector previous) where T : IStyleable + public static Selector OfType(this Selector? previous) where T : IStyleable { return previous.OfType(typeof(T)); } @@ -191,9 +199,9 @@ namespace Avalonia.Styling /// The property. /// The property value. /// The selector. - public static Selector PropertyEquals(this Selector previous, AvaloniaProperty property, object value) + public static Selector PropertyEquals(this Selector? previous, AvaloniaProperty property, object? value) { - Contract.Requires(property != null); + _ = property ?? throw new ArgumentNullException(nameof(property)); return new PropertyEqualsSelector(previous, property, value); } @@ -205,9 +213,9 @@ namespace Avalonia.Styling /// The property. /// The property value. /// The selector. - public static Selector PropertyEquals(this Selector previous, AvaloniaProperty property, object value) + public static Selector PropertyEquals(this Selector? previous, AvaloniaProperty property, object? value) { - Contract.Requires(property != null); + _ = property ?? throw new ArgumentNullException(nameof(property)); return new PropertyEqualsSelector(previous, property, value); } diff --git a/src/Avalonia.Styling/Styling/Styles.cs b/src/Avalonia.Styling/Styling/Styles.cs index c752bdfeb8..81502f1570 100644 --- a/src/Avalonia.Styling/Styling/Styles.cs +++ b/src/Avalonia.Styling/Styling/Styles.cs @@ -262,7 +262,7 @@ namespace Avalonia.Styling } } - private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { static IReadOnlyList ToReadOnlyList(IList list) { @@ -282,7 +282,7 @@ namespace Avalonia.Styling { for (var i = 0; i < items.Count; ++i) { - var style = (IStyle)items[i]; + var style = (IStyle)items[i]!; if (Owner is object && style is IResourceProvider resourceProvider) { @@ -299,7 +299,7 @@ namespace Avalonia.Styling { for (var i = 0; i < items.Count; ++i) { - var style = (IStyle)items[i]; + var style = (IStyle)items[i]!; if (Owner is object && style is IResourceProvider resourceProvider) { @@ -315,14 +315,14 @@ namespace Avalonia.Styling switch (e.Action) { case NotifyCollectionChangedAction.Add: - Add(e.NewItems); + Add(e.NewItems!); break; case NotifyCollectionChangedAction.Remove: - Remove(e.OldItems); + Remove(e.OldItems!); break; case NotifyCollectionChangedAction.Replace: - Remove(e.OldItems); - Add(e.NewItems); + Remove(e.OldItems!); + Add(e.NewItems!); break; case NotifyCollectionChangedAction.Reset: throw new InvalidOperationException("Reset should not be called on Styles."); diff --git a/src/Avalonia.Styling/Styling/TemplateSelector.cs b/src/Avalonia.Styling/Styling/TemplateSelector.cs index 5ea8defeda..e8051efa6d 100644 --- a/src/Avalonia.Styling/Styling/TemplateSelector.cs +++ b/src/Avalonia.Styling/Styling/TemplateSelector.cs @@ -5,7 +5,7 @@ namespace Avalonia.Styling internal class TemplateSelector : Selector { private readonly Selector _parent; - private string _selectorString; + private string? _selectorString; public TemplateSelector(Selector parent) { @@ -24,7 +24,7 @@ namespace Avalonia.Styling public override bool IsCombinator => true; /// - public override Type TargetType => null; + public override Type? TargetType => null; public override string ToString() { @@ -48,6 +48,6 @@ namespace Avalonia.Styling return _parent.Match(templatedParent, subscribe); } - protected override Selector MovePrevious() => null; + protected override Selector? MovePrevious() => null; } } diff --git a/src/Avalonia.Visuals/Visual.cs b/src/Avalonia.Visuals/Visual.cs index 78c6d9c057..324b253a0f 100644 --- a/src/Avalonia.Visuals/Visual.cs +++ b/src/Avalonia.Visuals/Visual.cs @@ -377,7 +377,7 @@ namespace Avalonia } } - protected override void LogicalChildrenCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + protected override void LogicalChildrenCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { base.LogicalChildrenCollectionChanged(sender, e); VisualRoot?.Renderer?.RecalculateChildren(this); diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs index 1b7ee9025e..5b6522064a 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs +++ b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs @@ -138,16 +138,16 @@ namespace Avalonia.Markup.Parsers break; case SelectorGrammar.ChildSyntax child: - result = result.Child(); + result = result!.Child(); break; case SelectorGrammar.DescendantSyntax descendant: result = result.Descendant(); break; case SelectorGrammar.TemplateSyntax template: - result = result.Template(); + result = result!.Template(); break; case SelectorGrammar.NotSyntax not: - result = result.Not(x => Create(not.Argument)); + result = result.Not(x => Create(not.Argument)!); break; case SelectorGrammar.NthChildSyntax nth: result = result.NthChild(nth.Step, nth.Offset); From e84b5f824475a05ab54a9e49b8246b42bb1aa4e7 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 22 Dec 2021 15:07:55 +0100 Subject: [PATCH 68/90] Added AvaloniaLocation.GetRequiredService. Using the pattern from elsewhere e.g. https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.serviceproviderserviceextensions.getrequiredservice?view=dotnet-plat-ext-6.0 --- src/Avalonia.Base/AvaloniaLocator.cs | 10 ++++++++++ src/Avalonia.Controls/SystemDialog.cs | 9 +++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Base/AvaloniaLocator.cs b/src/Avalonia.Base/AvaloniaLocator.cs index 0510852ea7..8ec245507b 100644 --- a/src/Avalonia.Base/AvaloniaLocator.cs +++ b/src/Avalonia.Base/AvaloniaLocator.cs @@ -125,6 +125,16 @@ namespace Avalonia { return (T?) resolver.GetService(typeof (T)); } + + public static object GetRequiredService(this IAvaloniaDependencyResolver resolver, Type t) + { + return resolver.GetService(t) ?? throw new InvalidOperationException($"Unable to locate '{t}'."); + } + + public static T GetRequiredService(this IAvaloniaDependencyResolver resolver) + { + return (T?)resolver.GetService(typeof(T)) ?? throw new InvalidOperationException($"Unable to locate '{typeof(T)}'."); + } } } diff --git a/src/Avalonia.Controls/SystemDialog.cs b/src/Avalonia.Controls/SystemDialog.cs index 3fac10b2d1..3487f427d7 100644 --- a/src/Avalonia.Controls/SystemDialog.cs +++ b/src/Avalonia.Controls/SystemDialog.cs @@ -66,8 +66,7 @@ namespace Avalonia.Controls { if(parent == null) throw new ArgumentNullException(nameof(parent)); - var service = AvaloniaLocator.Current.GetService() ?? - throw new InvalidOperationException("Unable to locate ISystemDialogImpl."); + var service = AvaloniaLocator.Current.GetRequiredService(); return (await service.ShowFileDialogAsync(this, parent) ?? Array.Empty()).FirstOrDefault(); } @@ -95,8 +94,7 @@ namespace Avalonia.Controls { if(parent == null) throw new ArgumentNullException(nameof(parent)); - var service = AvaloniaLocator.Current.GetService() ?? - throw new InvalidOperationException("Unable to locate ISystemDialogImpl."); + var service = AvaloniaLocator.Current.GetRequiredService(); return service.ShowFileDialogAsync(this, parent); } } @@ -125,8 +123,7 @@ namespace Avalonia.Controls { if(parent == null) throw new ArgumentNullException(nameof(parent)); - var service = AvaloniaLocator.Current.GetService() ?? - throw new InvalidOperationException("Unable to locate ISystemDialogImpl."); + var service = AvaloniaLocator.Current.GetRequiredService(); return service.ShowFolderDialogAsync(this, parent); } } From 1fbcd61e6f83f6d827af66148e8e8a34d06bb818 Mon Sep 17 00:00:00 2001 From: Tako <53405089+Takoooooo@users.noreply.github.com> Date: Wed, 22 Dec 2021 16:19:10 +0200 Subject: [PATCH 69/90] Fix DoubleTapped on touch (#7213) DoubleTapped now can be triggered by touch Fixed DoubleTapped triggering logic, previously it could be triggered only when you click twice, didn't work when you click 4 times,6, etc. Fixed Tapped to be triggered only once when you double-click, now it triggers Tapped once and DoubleTapped once instead of triggering Tapped two times and DoubleTapped once.Matches UWP behaviour. --- src/Avalonia.Input/Gestures.cs | 18 +- src/Avalonia.Input/PointerEventArgs.cs | 9 +- src/Avalonia.Input/TouchDevice.cs | 44 ++- .../TouchDeviceTests.cs | 272 ++++++++++++++++++ 4 files changed, 323 insertions(+), 20 deletions(-) create mode 100644 tests/Avalonia.Input.UnitTests/TouchDeviceTests.cs diff --git a/src/Avalonia.Input/Gestures.cs b/src/Avalonia.Input/Gestures.cs index 639b4ef117..a5ee558f47 100644 --- a/src/Avalonia.Input/Gestures.cs +++ b/src/Avalonia.Input/Gestures.cs @@ -6,6 +6,7 @@ namespace Avalonia.Input { public static class Gestures { + private static bool s_isDoubleTapped = false; public static readonly RoutedEvent TappedEvent = RoutedEvent.Register( "Tapped", RoutingStrategies.Bubble, @@ -81,20 +82,23 @@ namespace Avalonia.Input var e = (PointerPressedEventArgs)ev; var visual = (IVisual)ev.Source; -#pragma warning disable CS0618 // Type or member is obsolete - var clickCount = e.ClickCount; -#pragma warning restore CS0618 // Type or member is obsolete - if (clickCount <= 1) + if (e.ClickCount <= 1) { + s_isDoubleTapped = false; s_lastPress.SetTarget(ev.Source); } - else if (clickCount == 2 && e.GetCurrentPoint(visual).Properties.IsLeftButtonPressed) + else if (e.ClickCount % 2 == 0 && e.GetCurrentPoint(visual).Properties.IsLeftButtonPressed) { if (s_lastPress.TryGetTarget(out var target) && target == e.Source) { + s_isDoubleTapped = true; e.Source.RaiseEvent(new TappedEventArgs(DoubleTappedEvent, e)); } } + else + { + s_isDoubleTapped = false; + } } } @@ -112,7 +116,9 @@ namespace Avalonia.Input { e.Source.RaiseEvent(new TappedEventArgs(RightTappedEvent, e)); } - else + //s_isDoubleTapped needed here to prevent invoking Tapped event when DoubleTapped is called. + //This behaviour matches UWP behaviour. + else if (s_isDoubleTapped == false) { e.Source.RaiseEvent(new TappedEventArgs(TappedEvent, e)); } diff --git a/src/Avalonia.Input/PointerEventArgs.cs b/src/Avalonia.Input/PointerEventArgs.cs index ba39f7ca8e..8c86cd4637 100644 --- a/src/Avalonia.Input/PointerEventArgs.cs +++ b/src/Avalonia.Input/PointerEventArgs.cs @@ -114,7 +114,7 @@ namespace Avalonia.Input public class PointerPressedEventArgs : PointerEventArgs { - private readonly int _obsoleteClickCount; + private readonly int _clickCount; public PointerPressedEventArgs( IInteractive source, @@ -123,15 +123,14 @@ namespace Avalonia.Input ulong timestamp, PointerPointProperties properties, KeyModifiers modifiers, - int obsoleteClickCount = 1) + int clickCount = 1) : base(InputElement.PointerPressedEvent, source, pointer, rootVisual, rootVisualPosition, timestamp, properties, modifiers) { - _obsoleteClickCount = obsoleteClickCount; + _clickCount = clickCount; } - [Obsolete("Use DoubleTapped event or Gestures.DoubleRightTapped attached event")] - public int ClickCount => _obsoleteClickCount; + public int ClickCount => _clickCount; [Obsolete("Use PointerPressedEventArgs.GetCurrentPoint(this).Properties")] public MouseButton MouseButton => Properties.PointerUpdateKind.GetMouseButton(); diff --git a/src/Avalonia.Input/TouchDevice.cs b/src/Avalonia.Input/TouchDevice.cs index d6ad836f37..0f832d9add 100644 --- a/src/Avalonia.Input/TouchDevice.cs +++ b/src/Avalonia.Input/TouchDevice.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using Avalonia.Input.Raw; -using Avalonia.VisualTree; +using Avalonia.Platform; namespace Avalonia.Input { @@ -16,7 +16,9 @@ namespace Avalonia.Input { private readonly Dictionary _pointers = new Dictionary(); private bool _disposed; - + private int _clickCount; + private Rect _lastClickRect; + private ulong _lastClickTime; KeyModifiers GetKeyModifiers(RawInputModifiers modifiers) => (KeyModifiers)(modifiers & RawInputModifiers.KeyboardMask); @@ -27,10 +29,10 @@ namespace Avalonia.Input rv |= RawInputModifiers.LeftMouseButton; return rv; } - + public void ProcessRawEvent(RawInputEventArgs ev) { - if(_disposed) + if (_disposed) return; var args = (RawTouchEventArgs)ev; if (!_pointers.TryGetValue(args.TouchPointId, out var pointer)) @@ -43,16 +45,40 @@ namespace Avalonia.Input PointerType.Touch, _pointers.Count == 0); pointer.Capture(hit); } - + var target = pointer.Captured ?? args.Root; if (args.Type == RawPointerEventType.TouchBegin) { + if (_pointers.Count > 1) + { + _clickCount = 1; + _lastClickTime = 0; + _lastClickRect = new Rect(); + } + else + { + var settings = AvaloniaLocator.Current.GetService(); + if (settings == null) + { + throw new Exception("IPlatformSettings can not be null"); + } + if (!_lastClickRect.Contains(args.Position) + || ev.Timestamp - _lastClickTime > settings.DoubleClickTime.TotalMilliseconds) + { + _clickCount = 0; + } + ++_clickCount; + _lastClickTime = ev.Timestamp; + _lastClickRect = new Rect(args.Position, new Size()) + .Inflate(new Thickness(16, 16)); + } + target.RaiseEvent(new PointerPressedEventArgs(target, pointer, args.Root, args.Position, ev.Timestamp, new PointerPointProperties(GetModifiers(args.InputModifiers, true), PointerUpdateKind.LeftButtonPressed), - GetKeyModifiers(args.InputModifiers))); + GetKeyModifiers(args.InputModifiers), _clickCount)); } if (args.Type == RawPointerEventType.TouchEnd) @@ -84,12 +110,12 @@ namespace Avalonia.Input GetKeyModifiers(args.InputModifiers))); } - + } public void Dispose() { - if(_disposed) + if (_disposed) return; var values = _pointers.Values.ToList(); _pointers.Clear(); @@ -97,6 +123,6 @@ namespace Avalonia.Input foreach (var p in values) p.Dispose(); } - + } } diff --git a/tests/Avalonia.Input.UnitTests/TouchDeviceTests.cs b/tests/Avalonia.Input.UnitTests/TouchDeviceTests.cs new file mode 100644 index 0000000000..6c4416be47 --- /dev/null +++ b/tests/Avalonia.Input.UnitTests/TouchDeviceTests.cs @@ -0,0 +1,272 @@ +using System; +using Avalonia.Input.Raw; +using Avalonia.Platform; +using Avalonia.UnitTests; +using Moq; +using Xunit; + +namespace Avalonia.Input.UnitTests +{ + public class TouchDeviceTests + { + [Fact] + public void Tapped_Event_Is_Fired_With_Touch() + { + using (UnitTestApplication.Start( + new TestServices(inputManager: new InputManager()))) + { + var root = new TestRoot(); + var touchDevice = new TouchDevice(); + + var isTapped = false; + var executedTimes = 0; + root.Tapped += (a, e) => + { + isTapped = true; + executedTimes++; + }; + TapOnce(InputManager.Instance, touchDevice, root); + Assert.True(isTapped); + Assert.Equal(1, executedTimes); + } + } + + [Fact] + public void DoubleTapped_Event_Is_Fired_With_Touch() + { + var platformSettingsMock = new Mock(); + platformSettingsMock.Setup(x => x.DoubleClickTime).Returns(new TimeSpan(200)); + AvaloniaLocator.CurrentMutable.BindToSelf(this) + .Bind().ToConstant(platformSettingsMock.Object); + using (UnitTestApplication.Start( + new TestServices(inputManager: new InputManager()))) + { + var root = new TestRoot(); + var touchDevice = new TouchDevice(); + + var isDoubleTapped = false; + var doubleTappedExecutedTimes = 0; + var tappedExecutedTimes = 0; + root.DoubleTapped += (a, e) => + { + isDoubleTapped = true; + doubleTappedExecutedTimes++; + }; + root.Tapped += (a, e) => + { + tappedExecutedTimes++; + }; + TapOnce(InputManager.Instance, touchDevice, root); + TapOnce(InputManager.Instance, touchDevice, root, touchPointId: 1); + Assert.Equal(1, tappedExecutedTimes); + Assert.True(isDoubleTapped); + Assert.Equal(1, doubleTappedExecutedTimes); + } + } + + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(3)] + [InlineData(4)] + [InlineData(5)] + public void PointerPressed_Counts_Clicks_Correctly(int clickCount) + { + var platformSettingsMock = new Mock(); + platformSettingsMock.Setup(x => x.DoubleClickTime).Returns(new TimeSpan(200)); + AvaloniaLocator.CurrentMutable.BindToSelf(this) + .Bind().ToConstant(platformSettingsMock.Object); + using (UnitTestApplication.Start( + new TestServices(inputManager: new InputManager()))) + { + var root = new TestRoot(); + var touchDevice = new TouchDevice(); + + var pointerPressedExecutedTimes = 0; + var pointerPressedClicks = 0; + root.PointerPressed += (a, e) => + { + pointerPressedClicks = e.ClickCount; + pointerPressedExecutedTimes++; + }; + for (int i = 0; i < clickCount; i++) + { + TapOnce(InputManager.Instance, touchDevice, root, touchPointId: i); + } + + Assert.Equal(clickCount, pointerPressedExecutedTimes); + Assert.Equal(pointerPressedClicks, clickCount); + } + } + + [Fact] + public void DoubleTapped_Not_Fired_When_Click_Too_Late() + { + var platformSettingsMock = new Mock(); + platformSettingsMock.Setup(x => x.DoubleClickTime).Returns(new TimeSpan(0, 0, 0, 0, 20)); + AvaloniaLocator.CurrentMutable.BindToSelf(this) + .Bind().ToConstant(platformSettingsMock.Object); + using (UnitTestApplication.Start( + new TestServices(inputManager: new InputManager()))) + { + var root = new TestRoot(); + var touchDevice = new TouchDevice(); + + var isDoubleTapped = false; + var doubleTappedExecutedTimes = 0; + var tappedExecutedTimes = 0; + root.DoubleTapped += (a, e) => + { + isDoubleTapped = true; + doubleTappedExecutedTimes++; + }; + root.Tapped += (a, e) => + { + tappedExecutedTimes++; + }; + TapOnce(InputManager.Instance, touchDevice, root); + TapOnce(InputManager.Instance, touchDevice, root, 21, 1); + Assert.Equal(2, tappedExecutedTimes); + Assert.False(isDoubleTapped); + Assert.Equal(0, doubleTappedExecutedTimes); + } + } + + [Fact] + public void DoubleTapped_Not_Fired_When_Second_Click_Is_From_Different_Touch_Contact() + { + var tmp = new Mock(); + tmp.Setup(x => x.DoubleClickTime).Returns(new TimeSpan(200)); + AvaloniaLocator.CurrentMutable.BindToSelf(this) + .Bind().ToConstant(tmp.Object); + using (UnitTestApplication.Start( + new TestServices(inputManager: new InputManager()))) + { + var root = new TestRoot(); + var touchDevice = new TouchDevice(); + + var isDoubleTapped = false; + var doubleTappedExecutedTimes = 0; + var tappedExecutedTimes = 0; + root.DoubleTapped += (a, e) => + { + isDoubleTapped = true; + doubleTappedExecutedTimes++; + }; + root.Tapped += (a, e) => + { + tappedExecutedTimes++; + }; + SendXTouchContactsWithIds(InputManager.Instance, touchDevice, root, RawPointerEventType.TouchBegin, 0, 1); + SendXTouchContactsWithIds(InputManager.Instance, touchDevice, root, RawPointerEventType.TouchEnd, 0, 1); + Assert.Equal(2, tappedExecutedTimes); + Assert.False(isDoubleTapped); + Assert.Equal(0, doubleTappedExecutedTimes); + } + } + + [Fact] + public void Click_Counting_Should_Work_Correctly_With_Few_Touch_Contacts() + { + var tmp = new Mock(); + tmp.Setup(x => x.DoubleClickTime).Returns(new TimeSpan(200)); + AvaloniaLocator.CurrentMutable.BindToSelf(this) + .Bind().ToConstant(tmp.Object); + using (UnitTestApplication.Start( + new TestServices(inputManager: new InputManager()))) + { + var root = new TestRoot(); + var touchDevice = new TouchDevice(); + + var pointerPressedExecutedTimes = 0; + var tappedExecutedTimes = 0; + var isDoubleTapped = false; + var doubleTappedExecutedTimes = 0; + root.PointerPressed += (a, e) => + { + pointerPressedExecutedTimes++; + switch (pointerPressedExecutedTimes) + { + case <= 2: + Assert.True(e.ClickCount == 1); + break; + case 3: + Assert.True(e.ClickCount == 2); + break; + case 4: + Assert.True(e.ClickCount == 3); + break; + case 5: + Assert.True(e.ClickCount == 4); + break; + case 6: + Assert.True(e.ClickCount == 5); + break; + case 7: + Assert.True(e.ClickCount == 1); + break; + case 8: + Assert.True(e.ClickCount == 1); + break; + case 9: + Assert.True(e.ClickCount == 2); + break; + default: + break; + } + }; + root.DoubleTapped += (a, e) => + { + isDoubleTapped = true; + doubleTappedExecutedTimes++; + }; + root.Tapped += (a, e) => + { + tappedExecutedTimes++; + }; + SendXTouchContactsWithIds(InputManager.Instance, touchDevice, root, RawPointerEventType.TouchBegin, 0, 1); + SendXTouchContactsWithIds(InputManager.Instance, touchDevice, root, RawPointerEventType.TouchEnd, 0, 1); + TapOnce(InputManager.Instance, touchDevice, root, touchPointId: 2); + TapOnce(InputManager.Instance, touchDevice, root, touchPointId: 3); + TapOnce(InputManager.Instance, touchDevice, root, touchPointId: 4); + SendXTouchContactsWithIds(InputManager.Instance, touchDevice, root, RawPointerEventType.TouchBegin, 5, 6, 7); + SendXTouchContactsWithIds(InputManager.Instance, touchDevice, root, RawPointerEventType.TouchEnd, 5, 6, 7); + TapOnce(InputManager.Instance, touchDevice, root, touchPointId: 8); + Assert.Equal(6, tappedExecutedTimes); + Assert.Equal(9, pointerPressedExecutedTimes); + Assert.True(isDoubleTapped); + Assert.Equal(3, doubleTappedExecutedTimes); + + } + } + private static void SendXTouchContactsWithIds(IInputManager inputManager, TouchDevice device, IInputRoot root, RawPointerEventType type, params long[] touchPointIds) + { + for (int i = 0; i < touchPointIds.Length; i++) + { + inputManager.ProcessInput(new RawTouchEventArgs(device, 0, + root, + type, + new Point(0, 0), + RawInputModifiers.None, + touchPointIds[i])); + } + } + + + private static void TapOnce(IInputManager inputManager, TouchDevice device, IInputRoot root, ulong timestamp = 0, long touchPointId = 0) + { + inputManager.ProcessInput(new RawTouchEventArgs(device, timestamp, + root, + RawPointerEventType.TouchBegin, + new Point(0, 0), + RawInputModifiers.None, + touchPointId)); + inputManager.ProcessInput(new RawTouchEventArgs(device, timestamp, + root, + RawPointerEventType.TouchEnd, + new Point(0, 0), + RawInputModifiers.None, + touchPointId)); + } + } +} From a0c10cd6bf6c4e2be2e0c8170a690df0bc8d989e Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 22 Dec 2021 15:23:05 +0100 Subject: [PATCH 70/90] Use GetRequiredService throughout. --- src/Avalonia.Base/Threading/Dispatcher.cs | 6 +----- src/Avalonia.Visuals/Animation/RenderLoopClock.cs | 4 +--- src/Avalonia.Visuals/Media/CombinedGeometry.cs | 3 +-- src/Avalonia.Visuals/Media/EllipseGeometry.cs | 3 +-- src/Avalonia.Visuals/Media/Fonts/FontFamilyLoader.cs | 7 ++----- src/Avalonia.Visuals/Media/FormattedText.cs | 3 +-- src/Avalonia.Visuals/Media/GeometryGroup.cs | 3 +-- src/Avalonia.Visuals/Media/GlyphRun.cs | 3 +-- src/Avalonia.Visuals/Media/Imaging/Bitmap.cs | 3 +-- src/Avalonia.Visuals/Media/Imaging/RenderTargetBitmap.cs | 3 +-- src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs | 3 +-- src/Avalonia.Visuals/Media/LineGeometry.cs | 3 +-- src/Avalonia.Visuals/Media/PathGeometry.cs | 3 +-- src/Avalonia.Visuals/Media/PolylineGeometry.cs | 3 +-- src/Avalonia.Visuals/Media/RectangleGeometry.cs | 3 +-- src/Avalonia.Visuals/Media/StreamGeometry.cs | 3 +-- src/Avalonia.Visuals/Rendering/DefaultRenderTimer.cs | 3 +-- src/Avalonia.X11/X11CursorFactory.cs | 6 ++---- src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs | 3 +-- src/Web/Avalonia.Web.Blazor/WindowingPlatform.cs | 3 +-- src/Windows/Avalonia.Win32/FramebufferManager.cs | 3 +-- 21 files changed, 23 insertions(+), 51 deletions(-) diff --git a/src/Avalonia.Base/Threading/Dispatcher.cs b/src/Avalonia.Base/Threading/Dispatcher.cs index 949c11fbe0..ffc013268d 100644 --- a/src/Avalonia.Base/Threading/Dispatcher.cs +++ b/src/Avalonia.Base/Threading/Dispatcher.cs @@ -56,11 +56,7 @@ namespace Avalonia.Threading /// public void MainLoop(CancellationToken cancellationToken) { - var platform = AvaloniaLocator.Current.GetService(); - - if (platform is null) - throw new InvalidOperationException("Unable to locate IPlatformThreadingInterface"); - + var platform = AvaloniaLocator.Current.GetRequiredService(); cancellationToken.Register(() => platform.Signal(DispatcherPriority.Send)); platform.RunLoop(cancellationToken); } diff --git a/src/Avalonia.Visuals/Animation/RenderLoopClock.cs b/src/Avalonia.Visuals/Animation/RenderLoopClock.cs index 942e7eb7c6..de86b5fee2 100644 --- a/src/Avalonia.Visuals/Animation/RenderLoopClock.cs +++ b/src/Avalonia.Visuals/Animation/RenderLoopClock.cs @@ -9,9 +9,7 @@ namespace Avalonia.Animation { protected override void Stop() { - var loop = AvaloniaLocator.Current.GetService() ?? - throw new InvalidOperationException("Unable to locate IRenderLoop."); - loop.Remove(this); + AvaloniaLocator.Current.GetRequiredService().Remove(this); } bool IRenderLoopTask.NeedsUpdate => HasSubscriptions; diff --git a/src/Avalonia.Visuals/Media/CombinedGeometry.cs b/src/Avalonia.Visuals/Media/CombinedGeometry.cs index b76c496c4e..8f080d05c7 100644 --- a/src/Avalonia.Visuals/Media/CombinedGeometry.cs +++ b/src/Avalonia.Visuals/Media/CombinedGeometry.cs @@ -154,8 +154,7 @@ namespace Avalonia.Media if (g1 is object && g2 is object) { - var factory = AvaloniaLocator.Current.GetService() ?? - throw new InvalidOperationException("Unable to locate IPlatformRenderInterface."); + var factory = AvaloniaLocator.Current.GetRequiredService(); return factory.CreateCombinedGeometry(GeometryCombineMode, g1, g2); } else if (GeometryCombineMode == GeometryCombineMode.Intersect) diff --git a/src/Avalonia.Visuals/Media/EllipseGeometry.cs b/src/Avalonia.Visuals/Media/EllipseGeometry.cs index 7f9abaf531..8211855324 100644 --- a/src/Avalonia.Visuals/Media/EllipseGeometry.cs +++ b/src/Avalonia.Visuals/Media/EllipseGeometry.cs @@ -98,8 +98,7 @@ namespace Avalonia.Media /// protected override IGeometryImpl? CreateDefiningGeometry() { - var factory = AvaloniaLocator.Current.GetService() ?? - throw new InvalidOperationException("Unable to locate IPlatformRenderInterface."); + var factory = AvaloniaLocator.Current.GetRequiredService(); if (Rect != default) return factory.CreateEllipseGeometry(Rect); diff --git a/src/Avalonia.Visuals/Media/Fonts/FontFamilyLoader.cs b/src/Avalonia.Visuals/Media/Fonts/FontFamilyLoader.cs index 203c8dc221..a54dad7623 100644 --- a/src/Avalonia.Visuals/Media/Fonts/FontFamilyLoader.cs +++ b/src/Avalonia.Visuals/Media/Fonts/FontFamilyLoader.cs @@ -32,9 +32,7 @@ namespace Avalonia.Media.Fonts /// private static IEnumerable GetFontAssetsBySource(FontFamilyKey fontFamilyKey) { - var assetLoader = AvaloniaLocator.Current.GetService() ?? - throw new InvalidOperationException("Unable to locate IAssetLoader."); - + var assetLoader = AvaloniaLocator.Current.GetRequiredService(); var availableAssets = assetLoader.GetAssets(fontFamilyKey.Source, fontFamilyKey.BaseUri); var matchingAssets = @@ -51,8 +49,7 @@ namespace Avalonia.Media.Fonts /// private static IEnumerable GetFontAssetsByExpression(FontFamilyKey fontFamilyKey) { - var assetLoader = AvaloniaLocator.Current.GetService() ?? - throw new InvalidOperationException("Unable to locate IAssetLoader."); + var assetLoader = AvaloniaLocator.Current.GetRequiredService(); var fileName = GetFileName(fontFamilyKey, out var fileExtension, out var location); diff --git a/src/Avalonia.Visuals/Media/FormattedText.cs b/src/Avalonia.Visuals/Media/FormattedText.cs index 499761d96c..f6129eaf6a 100644 --- a/src/Avalonia.Visuals/Media/FormattedText.cs +++ b/src/Avalonia.Visuals/Media/FormattedText.cs @@ -24,8 +24,7 @@ namespace Avalonia.Media /// public FormattedText() { - _platform = AvaloniaLocator.Current.GetService() ?? - throw new InvalidOperationException("Unable to locate IPlatformRenderInterface."); + _platform = AvaloniaLocator.Current.GetRequiredService(); } /// diff --git a/src/Avalonia.Visuals/Media/GeometryGroup.cs b/src/Avalonia.Visuals/Media/GeometryGroup.cs index 01d4923456..c6b666b2eb 100644 --- a/src/Avalonia.Visuals/Media/GeometryGroup.cs +++ b/src/Avalonia.Visuals/Media/GeometryGroup.cs @@ -61,8 +61,7 @@ namespace Avalonia.Media { if (_children?.Count > 0) { - var factory = AvaloniaLocator.Current.GetService() ?? - throw new InvalidOperationException("Unable to locate IPlatformRenderInterface."); + var factory = AvaloniaLocator.Current.GetRequiredService(); return factory.CreateGeometryGroup(FillRule, _children); } diff --git a/src/Avalonia.Visuals/Media/GlyphRun.cs b/src/Avalonia.Visuals/Media/GlyphRun.cs index e15a19306a..53b35fb31b 100644 --- a/src/Avalonia.Visuals/Media/GlyphRun.cs +++ b/src/Avalonia.Visuals/Media/GlyphRun.cs @@ -627,8 +627,7 @@ namespace Avalonia.Media throw new InvalidOperationException(); } - var platformRenderInterface = AvaloniaLocator.Current.GetService() ?? - throw new InvalidOperationException("Unable to locate IPlatformRenderInterface"); + var platformRenderInterface = AvaloniaLocator.Current.GetRequiredService(); _glyphRunImpl = platformRenderInterface.CreateGlyphRun(this); } diff --git a/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs b/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs index 020940d9c6..647ce13528 100644 --- a/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs +++ b/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs @@ -169,8 +169,7 @@ namespace Avalonia.Media.Imaging private static IPlatformRenderInterface GetFactory() { - return AvaloniaLocator.Current.GetService() ?? - throw new InvalidOperationException("Unable to locate IPlatformRenderInterface."); + return AvaloniaLocator.Current.GetRequiredService(); } } } diff --git a/src/Avalonia.Visuals/Media/Imaging/RenderTargetBitmap.cs b/src/Avalonia.Visuals/Media/Imaging/RenderTargetBitmap.cs index fe8890b3ba..94a5abd918 100644 --- a/src/Avalonia.Visuals/Media/Imaging/RenderTargetBitmap.cs +++ b/src/Avalonia.Visuals/Media/Imaging/RenderTargetBitmap.cs @@ -54,8 +54,7 @@ namespace Avalonia.Media.Imaging /// The platform-specific implementation. private static IRenderTargetBitmapImpl CreateImpl(PixelSize size, Vector dpi) { - IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService() ?? - throw new InvalidOperationException("Unable to locate IPlatformRenderInterface."); + IPlatformRenderInterface factory = AvaloniaLocator.Current.GetRequiredService(); return factory.CreateRenderTargetBitmap(size, dpi); } diff --git a/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs b/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs index 4dfb22440d..40a24db338 100644 --- a/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs +++ b/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs @@ -93,8 +93,7 @@ namespace Avalonia.Media.Imaging private static IPlatformRenderInterface GetFactory() { - return AvaloniaLocator.Current.GetService() ?? - throw new InvalidOperationException("Unable to locate IPlatformRenderInterface."); + return AvaloniaLocator.Current.GetRequiredService(); } } } diff --git a/src/Avalonia.Visuals/Media/LineGeometry.cs b/src/Avalonia.Visuals/Media/LineGeometry.cs index b01a223aca..6ac92ea33b 100644 --- a/src/Avalonia.Visuals/Media/LineGeometry.cs +++ b/src/Avalonia.Visuals/Media/LineGeometry.cs @@ -70,8 +70,7 @@ namespace Avalonia.Media /// protected override IGeometryImpl? CreateDefiningGeometry() { - var factory = AvaloniaLocator.Current.GetService() ?? - throw new InvalidOperationException("Unable to locate IPlatformRenderInterface."); + var factory = AvaloniaLocator.Current.GetRequiredService(); return factory.CreateLineGeometry(StartPoint, EndPoint); } diff --git a/src/Avalonia.Visuals/Media/PathGeometry.cs b/src/Avalonia.Visuals/Media/PathGeometry.cs index 0b6a157280..2c8a51c541 100644 --- a/src/Avalonia.Visuals/Media/PathGeometry.cs +++ b/src/Avalonia.Visuals/Media/PathGeometry.cs @@ -88,8 +88,7 @@ namespace Avalonia.Media if (figures is null) return null; - var factory = AvaloniaLocator.Current.GetService() ?? - throw new InvalidOperationException("Unable to locate IPlatformRenderInterface."); + var factory = AvaloniaLocator.Current.GetRequiredService(); var geometry = factory.CreateStreamGeometry(); using (var ctx = new StreamGeometryContext(geometry.Open())) diff --git a/src/Avalonia.Visuals/Media/PolylineGeometry.cs b/src/Avalonia.Visuals/Media/PolylineGeometry.cs index 8e040a6043..dd3c298b5b 100644 --- a/src/Avalonia.Visuals/Media/PolylineGeometry.cs +++ b/src/Avalonia.Visuals/Media/PolylineGeometry.cs @@ -76,8 +76,7 @@ namespace Avalonia.Media protected override IGeometryImpl? CreateDefiningGeometry() { - var factory = AvaloniaLocator.Current.GetService() ?? - throw new InvalidOperationException("Unable to locate IPlatformRenderInterface."); + var factory = AvaloniaLocator.Current.GetRequiredService(); var geometry = factory.CreateStreamGeometry(); using (var context = geometry.Open()) diff --git a/src/Avalonia.Visuals/Media/RectangleGeometry.cs b/src/Avalonia.Visuals/Media/RectangleGeometry.cs index 97b4869d71..0bf9eb5664 100644 --- a/src/Avalonia.Visuals/Media/RectangleGeometry.cs +++ b/src/Avalonia.Visuals/Media/RectangleGeometry.cs @@ -49,8 +49,7 @@ namespace Avalonia.Media protected override IGeometryImpl? CreateDefiningGeometry() { - var factory = AvaloniaLocator.Current.GetService() ?? - throw new InvalidOperationException("Unable to locate IPlatformRenderInterface."); + var factory = AvaloniaLocator.Current.GetRequiredService(); return factory.CreateRectangleGeometry(Rect); } diff --git a/src/Avalonia.Visuals/Media/StreamGeometry.cs b/src/Avalonia.Visuals/Media/StreamGeometry.cs index b034ff7823..fb79488e0f 100644 --- a/src/Avalonia.Visuals/Media/StreamGeometry.cs +++ b/src/Avalonia.Visuals/Media/StreamGeometry.cs @@ -66,8 +66,7 @@ namespace Avalonia.Media { if (_impl == null) { - var factory = AvaloniaLocator.Current.GetService() ?? - throw new InvalidOperationException("Unable to locate IPlatformRenderInterface."); + var factory = AvaloniaLocator.Current.GetRequiredService(); _impl = factory.CreateStreamGeometry(); } diff --git a/src/Avalonia.Visuals/Rendering/DefaultRenderTimer.cs b/src/Avalonia.Visuals/Rendering/DefaultRenderTimer.cs index 43e00f3215..82d3892975 100644 --- a/src/Avalonia.Visuals/Rendering/DefaultRenderTimer.cs +++ b/src/Avalonia.Visuals/Rendering/DefaultRenderTimer.cs @@ -77,8 +77,7 @@ namespace Avalonia.Rendering /// protected virtual IDisposable StartCore(Action tick) { - _runtime ??= AvaloniaLocator.Current.GetService() ?? - throw new InvalidOperationException("Unable to locate IRuntimePlatform."); + _runtime ??= AvaloniaLocator.Current.GetRequiredService(); return _runtime.StartSystemTimer( TimeSpan.FromSeconds(1.0 / FramesPerSecond), diff --git a/src/Avalonia.X11/X11CursorFactory.cs b/src/Avalonia.X11/X11CursorFactory.cs index 8e5cac1400..60f035cd7e 100644 --- a/src/Avalonia.X11/X11CursorFactory.cs +++ b/src/Avalonia.X11/X11CursorFactory.cs @@ -94,10 +94,8 @@ namespace Avalonia.X11 { var size = Marshal.SizeOf() + (bitmap.PixelSize.Width * bitmap.PixelSize.Height * 4); - var runtimePlatform = AvaloniaLocator.Current.GetService() ?? - throw new InvalidOperationException("Unable to locate IRuntimePlatform"); - var platformRenderInterface = AvaloniaLocator.Current.GetService() ?? - throw new InvalidOperationException("Unable to locate IPlatformRenderInterface"); + var runtimePlatform = AvaloniaLocator.Current.GetRequiredService(); + var platformRenderInterface = AvaloniaLocator.Current.GetRequiredService(); _pixelSize = bitmap.PixelSize; _blob = runtimePlatform.AllocBlob(size); diff --git a/src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs b/src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs index a971c8e997..3e9849d4dc 100644 --- a/src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs +++ b/src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs @@ -106,8 +106,7 @@ namespace Avalonia.Web.Blazor public IRenderer CreateRenderer(IRenderRoot root) { - var loop = AvaloniaLocator.Current.GetService() ?? - throw new InvalidOperationException("Unable to locate IRenderLoop."); + var loop = AvaloniaLocator.Current.GetRequiredService(); return new DeferredRenderer(root, loop); } diff --git a/src/Web/Avalonia.Web.Blazor/WindowingPlatform.cs b/src/Web/Avalonia.Web.Blazor/WindowingPlatform.cs index 4804016ab2..f30a36b8c9 100644 --- a/src/Web/Avalonia.Web.Blazor/WindowingPlatform.cs +++ b/src/Web/Avalonia.Web.Blazor/WindowingPlatform.cs @@ -98,8 +98,7 @@ namespace Avalonia.Web.Blazor private static IRuntimePlatform GetRuntimePlatform() { - return AvaloniaLocator.Current.GetService() ?? - throw new InvalidOperationException("Unable to locate IRuntimePlatform."); + return AvaloniaLocator.Current.GetRequiredService(); } } } diff --git a/src/Windows/Avalonia.Win32/FramebufferManager.cs b/src/Windows/Avalonia.Win32/FramebufferManager.cs index 119edcebbf..7f4b1c976d 100644 --- a/src/Windows/Avalonia.Win32/FramebufferManager.cs +++ b/src/Windows/Avalonia.Win32/FramebufferManager.cs @@ -107,8 +107,7 @@ namespace Avalonia.Win32 private static FramebufferData AllocateFramebufferData(int width, int height) { - var service = AvaloniaLocator.Current.GetService() ?? - throw new InvalidOperationException("Unable to locate IRuntimePlatform."); + var service = AvaloniaLocator.Current.GetRequiredService(); var bitmapBlob = service.AllocBlob(width * height * _bytesPerPixel); return new FramebufferData(bitmapBlob, width, height); From 7fad72cca614198b8e294292236f12cffe3b0b3d Mon Sep 17 00:00:00 2001 From: Takoooooo Date: Wed, 22 Dec 2021 19:32:40 +0200 Subject: [PATCH 71/90] fix --- src/Avalonia.Input/TouchDevice.cs | 2 +- .../TouchDeviceTests.cs | 348 ++++++++---------- 2 files changed, 163 insertions(+), 187 deletions(-) diff --git a/src/Avalonia.Input/TouchDevice.cs b/src/Avalonia.Input/TouchDevice.cs index 0f832d9add..af8851a82b 100644 --- a/src/Avalonia.Input/TouchDevice.cs +++ b/src/Avalonia.Input/TouchDevice.cs @@ -61,7 +61,7 @@ namespace Avalonia.Input var settings = AvaloniaLocator.Current.GetService(); if (settings == null) { - throw new Exception("IPlatformSettings can not be null"); + throw new Exception("IPlatformSettings can not be null."); } if (!_lastClickRect.Contains(args.Position) || ev.Timestamp - _lastClickTime > settings.DoubleClickTime.TotalMilliseconds) diff --git a/tests/Avalonia.Input.UnitTests/TouchDeviceTests.cs b/tests/Avalonia.Input.UnitTests/TouchDeviceTests.cs index 6c4416be47..4700ead548 100644 --- a/tests/Avalonia.Input.UnitTests/TouchDeviceTests.cs +++ b/tests/Avalonia.Input.UnitTests/TouchDeviceTests.cs @@ -1,4 +1,5 @@ using System; +using System.Reactive.Disposables; using Avalonia.Input.Raw; using Avalonia.Platform; using Avalonia.UnitTests; @@ -12,56 +13,47 @@ namespace Avalonia.Input.UnitTests [Fact] public void Tapped_Event_Is_Fired_With_Touch() { - using (UnitTestApplication.Start( - new TestServices(inputManager: new InputManager()))) + using var app = UnitTestApp(new TimeSpan(200)); + var root = new TestRoot(); + var touchDevice = new TouchDevice(); + + var isTapped = false; + var executedTimes = 0; + root.Tapped += (a, e) => { - var root = new TestRoot(); - var touchDevice = new TouchDevice(); + isTapped = true; + executedTimes++; + }; + TapOnce(InputManager.Instance, touchDevice, root); + Assert.True(isTapped); + Assert.Equal(1, executedTimes); - var isTapped = false; - var executedTimes = 0; - root.Tapped += (a, e) => - { - isTapped = true; - executedTimes++; - }; - TapOnce(InputManager.Instance, touchDevice, root); - Assert.True(isTapped); - Assert.Equal(1, executedTimes); - } } [Fact] public void DoubleTapped_Event_Is_Fired_With_Touch() { - var platformSettingsMock = new Mock(); - platformSettingsMock.Setup(x => x.DoubleClickTime).Returns(new TimeSpan(200)); - AvaloniaLocator.CurrentMutable.BindToSelf(this) - .Bind().ToConstant(platformSettingsMock.Object); - using (UnitTestApplication.Start( - new TestServices(inputManager: new InputManager()))) + using var app = UnitTestApp(new TimeSpan(200)); + var root = new TestRoot(); + var touchDevice = new TouchDevice(); + + var isDoubleTapped = false; + var doubleTappedExecutedTimes = 0; + var tappedExecutedTimes = 0; + root.DoubleTapped += (a, e) => { - var root = new TestRoot(); - var touchDevice = new TouchDevice(); - - var isDoubleTapped = false; - var doubleTappedExecutedTimes = 0; - var tappedExecutedTimes = 0; - root.DoubleTapped += (a, e) => - { - isDoubleTapped = true; - doubleTappedExecutedTimes++; - }; - root.Tapped += (a, e) => - { - tappedExecutedTimes++; - }; - TapOnce(InputManager.Instance, touchDevice, root); - TapOnce(InputManager.Instance, touchDevice, root, touchPointId: 1); - Assert.Equal(1, tappedExecutedTimes); - Assert.True(isDoubleTapped); - Assert.Equal(1, doubleTappedExecutedTimes); - } + isDoubleTapped = true; + doubleTappedExecutedTimes++; + }; + root.Tapped += (a, e) => + { + tappedExecutedTimes++; + }; + TapOnce(InputManager.Instance, touchDevice, root); + TapOnce(InputManager.Instance, touchDevice, root, touchPointId: 1); + Assert.Equal(1, tappedExecutedTimes); + Assert.True(isDoubleTapped); + Assert.Equal(1, doubleTappedExecutedTimes); } [Theory] @@ -72,172 +64,156 @@ namespace Avalonia.Input.UnitTests [InlineData(5)] public void PointerPressed_Counts_Clicks_Correctly(int clickCount) { - var platformSettingsMock = new Mock(); - platformSettingsMock.Setup(x => x.DoubleClickTime).Returns(new TimeSpan(200)); - AvaloniaLocator.CurrentMutable.BindToSelf(this) - .Bind().ToConstant(platformSettingsMock.Object); - using (UnitTestApplication.Start( - new TestServices(inputManager: new InputManager()))) - { - var root = new TestRoot(); - var touchDevice = new TouchDevice(); + using var app = UnitTestApp(new TimeSpan(200)); + var root = new TestRoot(); + var touchDevice = new TouchDevice(); - var pointerPressedExecutedTimes = 0; - var pointerPressedClicks = 0; - root.PointerPressed += (a, e) => - { - pointerPressedClicks = e.ClickCount; - pointerPressedExecutedTimes++; - }; - for (int i = 0; i < clickCount; i++) - { - TapOnce(InputManager.Instance, touchDevice, root, touchPointId: i); - } - - Assert.Equal(clickCount, pointerPressedExecutedTimes); - Assert.Equal(pointerPressedClicks, clickCount); + var pointerPressedExecutedTimes = 0; + var pointerPressedClicks = 0; + root.PointerPressed += (a, e) => + { + pointerPressedClicks = e.ClickCount; + pointerPressedExecutedTimes++; + }; + for (int i = 0; i < clickCount; i++) + { + TapOnce(InputManager.Instance, touchDevice, root, touchPointId: i); } + + Assert.Equal(clickCount, pointerPressedExecutedTimes); + Assert.Equal(pointerPressedClicks, clickCount); } [Fact] public void DoubleTapped_Not_Fired_When_Click_Too_Late() { - var platformSettingsMock = new Mock(); - platformSettingsMock.Setup(x => x.DoubleClickTime).Returns(new TimeSpan(0, 0, 0, 0, 20)); - AvaloniaLocator.CurrentMutable.BindToSelf(this) - .Bind().ToConstant(platformSettingsMock.Object); - using (UnitTestApplication.Start( - new TestServices(inputManager: new InputManager()))) + using var app = UnitTestApp(new TimeSpan(0, 0, 0, 0, 20)); + var root = new TestRoot(); + var touchDevice = new TouchDevice(); + + var isDoubleTapped = false; + var doubleTappedExecutedTimes = 0; + var tappedExecutedTimes = 0; + root.DoubleTapped += (a, e) => + { + isDoubleTapped = true; + doubleTappedExecutedTimes++; + }; + root.Tapped += (a, e) => { - var root = new TestRoot(); - var touchDevice = new TouchDevice(); + tappedExecutedTimes++; + }; + TapOnce(InputManager.Instance, touchDevice, root); + TapOnce(InputManager.Instance, touchDevice, root, 21, 1); + Assert.Equal(2, tappedExecutedTimes); + Assert.False(isDoubleTapped); + Assert.Equal(0, doubleTappedExecutedTimes); - var isDoubleTapped = false; - var doubleTappedExecutedTimes = 0; - var tappedExecutedTimes = 0; - root.DoubleTapped += (a, e) => - { - isDoubleTapped = true; - doubleTappedExecutedTimes++; - }; - root.Tapped += (a, e) => - { - tappedExecutedTimes++; - }; - TapOnce(InputManager.Instance, touchDevice, root); - TapOnce(InputManager.Instance, touchDevice, root, 21, 1); - Assert.Equal(2, tappedExecutedTimes); - Assert.False(isDoubleTapped); - Assert.Equal(0, doubleTappedExecutedTimes); - } } [Fact] public void DoubleTapped_Not_Fired_When_Second_Click_Is_From_Different_Touch_Contact() { - var tmp = new Mock(); - tmp.Setup(x => x.DoubleClickTime).Returns(new TimeSpan(200)); - AvaloniaLocator.CurrentMutable.BindToSelf(this) - .Bind().ToConstant(tmp.Object); - using (UnitTestApplication.Start( - new TestServices(inputManager: new InputManager()))) + using var app = UnitTestApp(new TimeSpan(200)); + var root = new TestRoot(); + var touchDevice = new TouchDevice(); + + var isDoubleTapped = false; + var doubleTappedExecutedTimes = 0; + var tappedExecutedTimes = 0; + root.DoubleTapped += (a, e) => { - var root = new TestRoot(); - var touchDevice = new TouchDevice(); - - var isDoubleTapped = false; - var doubleTappedExecutedTimes = 0; - var tappedExecutedTimes = 0; - root.DoubleTapped += (a, e) => - { - isDoubleTapped = true; - doubleTappedExecutedTimes++; - }; - root.Tapped += (a, e) => - { - tappedExecutedTimes++; - }; - SendXTouchContactsWithIds(InputManager.Instance, touchDevice, root, RawPointerEventType.TouchBegin, 0, 1); - SendXTouchContactsWithIds(InputManager.Instance, touchDevice, root, RawPointerEventType.TouchEnd, 0, 1); - Assert.Equal(2, tappedExecutedTimes); - Assert.False(isDoubleTapped); - Assert.Equal(0, doubleTappedExecutedTimes); - } + isDoubleTapped = true; + doubleTappedExecutedTimes++; + }; + root.Tapped += (a, e) => + { + tappedExecutedTimes++; + }; + SendXTouchContactsWithIds(InputManager.Instance, touchDevice, root, RawPointerEventType.TouchBegin, 0, 1); + SendXTouchContactsWithIds(InputManager.Instance, touchDevice, root, RawPointerEventType.TouchEnd, 0, 1); + Assert.Equal(2, tappedExecutedTimes); + Assert.False(isDoubleTapped); + Assert.Equal(0, doubleTappedExecutedTimes); } [Fact] public void Click_Counting_Should_Work_Correctly_With_Few_Touch_Contacts() { - var tmp = new Mock(); - tmp.Setup(x => x.DoubleClickTime).Returns(new TimeSpan(200)); - AvaloniaLocator.CurrentMutable.BindToSelf(this) - .Bind().ToConstant(tmp.Object); - using (UnitTestApplication.Start( - new TestServices(inputManager: new InputManager()))) - { - var root = new TestRoot(); - var touchDevice = new TouchDevice(); + using var app = UnitTestApp(new TimeSpan(200)); - var pointerPressedExecutedTimes = 0; - var tappedExecutedTimes = 0; - var isDoubleTapped = false; - var doubleTappedExecutedTimes = 0; - root.PointerPressed += (a, e) => - { - pointerPressedExecutedTimes++; - switch (pointerPressedExecutedTimes) - { - case <= 2: - Assert.True(e.ClickCount == 1); - break; - case 3: - Assert.True(e.ClickCount == 2); - break; - case 4: - Assert.True(e.ClickCount == 3); - break; - case 5: - Assert.True(e.ClickCount == 4); - break; - case 6: - Assert.True(e.ClickCount == 5); - break; - case 7: - Assert.True(e.ClickCount == 1); - break; - case 8: - Assert.True(e.ClickCount == 1); - break; - case 9: - Assert.True(e.ClickCount == 2); - break; - default: - break; - } - }; - root.DoubleTapped += (a, e) => - { - isDoubleTapped = true; - doubleTappedExecutedTimes++; - }; - root.Tapped += (a, e) => - { - tappedExecutedTimes++; - }; - SendXTouchContactsWithIds(InputManager.Instance, touchDevice, root, RawPointerEventType.TouchBegin, 0, 1); - SendXTouchContactsWithIds(InputManager.Instance, touchDevice, root, RawPointerEventType.TouchEnd, 0, 1); - TapOnce(InputManager.Instance, touchDevice, root, touchPointId: 2); - TapOnce(InputManager.Instance, touchDevice, root, touchPointId: 3); - TapOnce(InputManager.Instance, touchDevice, root, touchPointId: 4); - SendXTouchContactsWithIds(InputManager.Instance, touchDevice, root, RawPointerEventType.TouchBegin, 5, 6, 7); - SendXTouchContactsWithIds(InputManager.Instance, touchDevice, root, RawPointerEventType.TouchEnd, 5, 6, 7); - TapOnce(InputManager.Instance, touchDevice, root, touchPointId: 8); - Assert.Equal(6, tappedExecutedTimes); - Assert.Equal(9, pointerPressedExecutedTimes); - Assert.True(isDoubleTapped); - Assert.Equal(3, doubleTappedExecutedTimes); + var root = new TestRoot(); + var touchDevice = new TouchDevice(); - } + var pointerPressedExecutedTimes = 0; + var tappedExecutedTimes = 0; + var isDoubleTapped = false; + var doubleTappedExecutedTimes = 0; + root.PointerPressed += (a, e) => + { + pointerPressedExecutedTimes++; + switch (pointerPressedExecutedTimes) + { + case <= 2: + Assert.True(e.ClickCount == 1); + break; + case 3: + Assert.True(e.ClickCount == 2); + break; + case 4: + Assert.True(e.ClickCount == 3); + break; + case 5: + Assert.True(e.ClickCount == 4); + break; + case 6: + Assert.True(e.ClickCount == 5); + break; + case 7: + Assert.True(e.ClickCount == 1); + break; + case 8: + Assert.True(e.ClickCount == 1); + break; + case 9: + Assert.True(e.ClickCount == 2); + break; + default: + break; + } + }; + root.DoubleTapped += (a, e) => + { + isDoubleTapped = true; + doubleTappedExecutedTimes++; + }; + root.Tapped += (a, e) => + { + tappedExecutedTimes++; + }; + SendXTouchContactsWithIds(InputManager.Instance, touchDevice, root, RawPointerEventType.TouchBegin, 0, 1); + SendXTouchContactsWithIds(InputManager.Instance, touchDevice, root, RawPointerEventType.TouchEnd, 0, 1); + TapOnce(InputManager.Instance, touchDevice, root, touchPointId: 2); + TapOnce(InputManager.Instance, touchDevice, root, touchPointId: 3); + TapOnce(InputManager.Instance, touchDevice, root, touchPointId: 4); + SendXTouchContactsWithIds(InputManager.Instance, touchDevice, root, RawPointerEventType.TouchBegin, 5, 6, 7); + SendXTouchContactsWithIds(InputManager.Instance, touchDevice, root, RawPointerEventType.TouchEnd, 5, 6, 7); + TapOnce(InputManager.Instance, touchDevice, root, touchPointId: 8); + Assert.Equal(6, tappedExecutedTimes); + Assert.Equal(9, pointerPressedExecutedTimes); + Assert.True(isDoubleTapped); + Assert.Equal(3, doubleTappedExecutedTimes); + } + private IDisposable UnitTestApp(TimeSpan doubleClickTime = new TimeSpan()) + { + var unitTestApp = UnitTestApplication.Start( + new TestServices(inputManager: new InputManager())); + var scope = AvaloniaLocator.EnterScope(); + var iSettingsMock = new Mock(); + iSettingsMock.Setup(x => x.DoubleClickTime).Returns(doubleClickTime); + AvaloniaLocator.CurrentMutable.BindToSelf(this) + .Bind().ToConstant(iSettingsMock.Object); + return new CompositeDisposable(unitTestApp, scope); } private static void SendXTouchContactsWithIds(IInputManager inputManager, TouchDevice device, IInputRoot root, RawPointerEventType type, params long[] touchPointIds) { From c2145db99bad500028cd87411745874c946fc774 Mon Sep 17 00:00:00 2001 From: Takoooooo Date: Wed, 22 Dec 2021 22:41:52 +0200 Subject: [PATCH 72/90] more fixes --- tests/Avalonia.Input.UnitTests/TouchDeviceTests.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/Avalonia.Input.UnitTests/TouchDeviceTests.cs b/tests/Avalonia.Input.UnitTests/TouchDeviceTests.cs index 4700ead548..dc9eb9d3b4 100644 --- a/tests/Avalonia.Input.UnitTests/TouchDeviceTests.cs +++ b/tests/Avalonia.Input.UnitTests/TouchDeviceTests.cs @@ -1,5 +1,4 @@ using System; -using System.Reactive.Disposables; using Avalonia.Input.Raw; using Avalonia.Platform; using Avalonia.UnitTests; @@ -208,12 +207,11 @@ namespace Avalonia.Input.UnitTests { var unitTestApp = UnitTestApplication.Start( new TestServices(inputManager: new InputManager())); - var scope = AvaloniaLocator.EnterScope(); var iSettingsMock = new Mock(); iSettingsMock.Setup(x => x.DoubleClickTime).Returns(doubleClickTime); AvaloniaLocator.CurrentMutable.BindToSelf(this) .Bind().ToConstant(iSettingsMock.Object); - return new CompositeDisposable(unitTestApp, scope); + return unitTestApp; } private static void SendXTouchContactsWithIds(IInputManager inputManager, TouchDevice device, IInputRoot root, RawPointerEventType type, params long[] touchPointIds) { From 5992d89b7d528e4b0b2cea543510fb94d781bf5b Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 22 Dec 2021 17:11:28 -0600 Subject: [PATCH 73/90] Add a DataType property on CompiledBindingExtension to enable specifying the start type explicitly on a binding. Fixes #6158 --- .../Transformers/AvaloniaXamlIlBindingPathTransformer.cs | 9 ++++++++- .../MarkupExtensions/CompiledBindingExtension.cs | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs index 4d21a29eb9..048a6220c5 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using XamlX.Ast; using XamlX.Transform; +using XamlX.Transform.Transformers; using XamlX.TypeSystem; namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers @@ -15,7 +16,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers { IXamlType startType = null; var sourceProperty = binding.Children.OfType().FirstOrDefault(c => c.Property.Name == "Source"); - if ((sourceProperty?.Values.Count ?? 0) == 1) + var dataTypeProperty = binding.Children.OfType().FirstOrDefault(c => c.Property.Name == "DataType"); + if (sourceProperty?.Values.Count is 1) { var sourceValue = sourceProperty.Values[0]; switch (sourceValue) @@ -99,6 +101,11 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers } } + if (dataTypeProperty?.Values.Count is 1 && dataTypeProperty.Values[0] is XamlAstTextNode text) + { + startType = TypeReferenceResolver.ResolveType(context, text.Text, isMarkupExtension: false, text, strict: true).Type; + } + Func startTypeResolver = startType is not null ? () => startType : () => { var parentDataContextNode = context.ParentNodes().OfType().FirstOrDefault(); diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs index 41de2355aa..0125156750 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs @@ -71,5 +71,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions public CompiledBindingPath Path { get; set; } public object Source { get; set; } + + public Type DataType { get; set; } } } From 46ffcf9eee6bddeb2710803b85e4c25ad34b81a5 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 22 Dec 2021 17:19:34 -0600 Subject: [PATCH 74/90] Support the DataType property in a binding in the DataContext property. --- .../AvaloniaXamlIlDataContextTypeTransformer.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs index 24dd3f7771..cf691db860 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs @@ -155,6 +155,12 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers { Func startTypeResolver = () => { + var dataTypeProperty = obj.Children.OfType().FirstOrDefault(c => c.Property.Name == "DataType"); + if (dataTypeProperty?.Values.Count is 1 && dataTypeProperty.Values[0] is XamlAstTextNode text) + { + return TypeReferenceResolver.ResolveType(context, text.Text, isMarkupExtension: false, text, strict: true).Type; + } + var parentDataContextNode = context.ParentNodes().OfType().FirstOrDefault(); if (parentDataContextNode is null) { From 240050dd8a7395bdab3c309d00b86fd3f0689ea6 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 23 Dec 2021 10:42:05 +0300 Subject: [PATCH 75/90] Stopgap measure to fix #7245 --- src/Avalonia.X11/X11Window.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 982bca6439..211a8e4f79 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -1157,7 +1157,7 @@ namespace Avalonia.X11 public ITextInputMethodImpl TextInputMethod => _ime; public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel) => - _transparencyHelper.SetTransparencyRequest(transparencyLevel); + _transparencyHelper?.SetTransparencyRequest(transparencyLevel); public void SetWindowManagerAddShadowHint(bool enabled) { From 6ef87ff047f6fddc7afdc2bd2ff073f4141cb313 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 23 Dec 2021 10:23:26 +0100 Subject: [PATCH 76/90] Improve ButtonSpinner OnPointerWheel - Only handle the PointerWheelEvent if the control has the focus - When the pointer wheel should change the selection, mark the event as handled. So any surrounding ScrollViewer does not do the scroll. - Make the Control Focusable by default in Style --- src/Avalonia.Controls/ButtonSpinner.cs | 7 ++++--- src/Avalonia.Themes.Default/ButtonSpinner.xaml | 1 + src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml | 1 + 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/ButtonSpinner.cs b/src/Avalonia.Controls/ButtonSpinner.cs index 5fe2cf3704..5a967ab488 100644 --- a/src/Avalonia.Controls/ButtonSpinner.cs +++ b/src/Avalonia.Controls/ButtonSpinner.cs @@ -1,4 +1,4 @@ -using System; +using System; using Avalonia.Controls.Metadata; using Avalonia.Controls.Primitives; using Avalonia.Data; @@ -196,13 +196,14 @@ namespace Avalonia.Controls protected override void OnPointerWheelChanged(PointerWheelEventArgs e) { base.OnPointerWheelChanged(e); - if (!e.Handled && AllowSpin) + + if (AllowSpin && IsFocused) { if (e.Delta.Y != 0) { var spinnerEventArgs = new SpinEventArgs(SpinEvent, (e.Delta.Y < 0) ? SpinDirection.Decrease : SpinDirection.Increase, true); OnSpin(spinnerEventArgs); - e.Handled = spinnerEventArgs.Handled; + e.Handled = true; } } } diff --git a/src/Avalonia.Themes.Default/ButtonSpinner.xaml b/src/Avalonia.Themes.Default/ButtonSpinner.xaml index ce2b85d2b5..561ca8bedd 100644 --- a/src/Avalonia.Themes.Default/ButtonSpinner.xaml +++ b/src/Avalonia.Themes.Default/ButtonSpinner.xaml @@ -5,6 +5,7 @@ + diff --git a/samples/ControlCatalog/App.xaml.cs b/samples/ControlCatalog/App.xaml.cs index 085de2ae7a..b21b9d80f0 100644 --- a/samples/ControlCatalog/App.xaml.cs +++ b/samples/ControlCatalog/App.xaml.cs @@ -16,12 +16,12 @@ namespace ControlCatalog DataContext = new ApplicationViewModel(); } - private static readonly StyleInclude DataGridFluent = new StyleInclude(new Uri("avares://ControlCatalog/Styles")) + public static readonly StyleInclude DataGridFluent = new StyleInclude(new Uri("avares://ControlCatalog/Styles")) { Source = new Uri("avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml") }; - private static readonly StyleInclude DataGridDefault = new StyleInclude(new Uri("avares://ControlCatalog/Styles")) + public static readonly StyleInclude DataGridDefault = new StyleInclude(new Uri("avares://ControlCatalog/Styles")) { Source = new Uri("avares://Avalonia.Controls.DataGrid/Themes/Default.xaml") }; @@ -31,8 +31,7 @@ namespace ControlCatalog new StyleInclude(new Uri("avares://ControlCatalog/Styles")) { Source = new Uri("avares://Avalonia.Themes.Fluent/FluentDark.xaml") - }, - DataGridFluent + } }; public static Styles FluentLight = new Styles @@ -40,8 +39,7 @@ namespace ControlCatalog new StyleInclude(new Uri("avares://ControlCatalog/Styles")) { Source = new Uri("avares://Avalonia.Themes.Fluent/FluentLight.xaml") - }, - DataGridFluent + } }; public static Styles DefaultLight = new Styles @@ -65,8 +63,7 @@ namespace ControlCatalog new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog")) { Source = new Uri("avares://Avalonia.Themes.Default/DefaultTheme.xaml") - }, - DataGridDefault + } }; public static Styles DefaultDark = new Styles @@ -90,8 +87,7 @@ namespace ControlCatalog new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog")) { Source = new Uri("avares://Avalonia.Themes.Default/DefaultTheme.xaml") - }, - DataGridDefault + } }; public override void Initialize() diff --git a/samples/ControlCatalog/MainView.xaml.cs b/samples/ControlCatalog/MainView.xaml.cs index 3898e9da85..b36c2b6997 100644 --- a/samples/ControlCatalog/MainView.xaml.cs +++ b/samples/ControlCatalog/MainView.xaml.cs @@ -3,15 +3,12 @@ using System.Collections; using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; -using Avalonia.Markup.Xaml.MarkupExtensions; -using Avalonia.Markup.Xaml.Styling; -using Avalonia.Markup.Xaml.XamlIl; using Avalonia.Media; using Avalonia.Media.Immutable; using Avalonia.Platform; -using ControlCatalog.Pages; -using ControlCatalog.Models; using Avalonia.Themes.Fluent; +using ControlCatalog.Models; +using ControlCatalog.Pages; namespace ControlCatalog { @@ -44,7 +41,47 @@ namespace ControlCatalog { if (themes.SelectedItem is CatalogTheme theme) { - (Application.Current.Styles[0] as FluentTheme).Mode = FluentThemeMode.Dark; + var themeStyle = Application.Current.Styles[0]; + if (theme == CatalogTheme.FluentLight) + { + if (themeStyle is FluentTheme fluentTheme) + { + if (fluentTheme.Mode == FluentThemeMode.Dark) + { + fluentTheme.Mode = FluentThemeMode.Light; + } + } + else + { + Application.Current.Styles[0] = new FluentTheme(new Uri("avares://ControlCatalog/Styles")); + Application.Current.Styles[1] = App.DataGridFluent; + } + } + else if (theme == CatalogTheme.FluentDark) + { + if (themeStyle is FluentTheme fluentTheme) + { + if (fluentTheme.Mode == FluentThemeMode.Light) + { + fluentTheme.Mode = FluentThemeMode.Dark; + } + } + else + { + Application.Current.Styles[0] = new FluentTheme(new Uri("avares://ControlCatalog/Styles")) { Mode = FluentThemeMode.Dark }; + Application.Current.Styles[1] = App.DataGridFluent; + } + } + else if (theme == CatalogTheme.DefaultLight) + { + Application.Current.Styles[0] = App.DefaultLight; + Application.Current.Styles[1] = App.DataGridDefault; + } + else if (theme == CatalogTheme.DefaultDark) + { + Application.Current.Styles[0] = App.DefaultDark; + Application.Current.Styles[1] = App.DataGridDefault; + } } }; diff --git a/src/Avalonia.Themes.Fluent/FluentTheme.cs b/src/Avalonia.Themes.Fluent/FluentTheme.cs index 1c61c03c97..5790de42b1 100644 --- a/src/Avalonia.Themes.Fluent/FluentTheme.cs +++ b/src/Avalonia.Themes.Fluent/FluentTheme.cs @@ -18,7 +18,7 @@ namespace Avalonia.Themes.Fluent /// /// Includes the fluent theme in an application. /// - public class FluentTheme : IStyle, IResourceProvider + public class FluentTheme : AvaloniaObject, IStyle, IResourceProvider { private readonly Uri _baseUri; private Styles _fluentDark = new(); @@ -26,7 +26,6 @@ namespace Avalonia.Themes.Fluent private Styles _sharedStyles = new(); private bool _isLoading; private IStyle? _loaded; - private FluentThemeMode _mode; /// /// Initializes a new instance of the class. @@ -48,31 +47,32 @@ namespace Avalonia.Themes.Fluent InitStyles(_baseUri); } + + public static readonly StyledProperty ModeProperty = + AvaloniaProperty.Register(nameof(Mode)); /// /// Gets or sets the mode of the fluent theme (light, dark). /// public FluentThemeMode Mode { - get => _mode; - set + get => GetValue(ModeProperty); + set => SetValue(ModeProperty, value); + } + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + if (change.Property == ModeProperty) { - if (_mode != value) + if (Mode == FluentThemeMode.Dark) { - _mode = value; - if (_mode == FluentThemeMode.Dark) - { - (Loaded as Styles)![1] = _fluentDark[0]; - (Loaded as Styles)![2] = _fluentDark[1]; - } - else - { - (Loaded as Styles)![1] = _fluentLight[0]; - (Loaded as Styles)![2] = _fluentLight[1]; - } - - + (Loaded as Styles)![1] = _fluentDark[0]; + (Loaded as Styles)![2] = _fluentDark[1]; + } + else + { + (Loaded as Styles)![1] = _fluentLight[0]; + (Loaded as Styles)![2] = _fluentLight[1]; } - } } From 5172771a79a032e9df38c25fa3636d042c6e57b2 Mon Sep 17 00:00:00 2001 From: Takoooooo Date: Thu, 23 Dec 2021 15:53:14 +0200 Subject: [PATCH 80/90] refactoring --- src/Avalonia.Themes.Fluent/FluentTheme.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/FluentTheme.cs b/src/Avalonia.Themes.Fluent/FluentTheme.cs index 5790de42b1..53be41e4d1 100644 --- a/src/Avalonia.Themes.Fluent/FluentTheme.cs +++ b/src/Avalonia.Themes.Fluent/FluentTheme.cs @@ -88,23 +88,19 @@ namespace Avalonia.Themes.Fluent if (_loaded == null) { _isLoading = true; - Styles? resultStyle = new Styles() { _sharedStyles }; if (Mode == FluentThemeMode.Light) { - resultStyle.Add(_fluentLight[0]); - resultStyle.Add(_fluentLight[1]); + _loaded = new Styles() { _sharedStyles , _fluentLight[0], _fluentLight[1] }; } else if (Mode == FluentThemeMode.Dark) { - resultStyle.Add(_fluentDark[0]); - resultStyle.Add(_fluentDark[1]); + _loaded = new Styles() { _sharedStyles, _fluentDark[0], _fluentDark[1] }; } - _loaded = resultStyle; _isLoading = false; } - return _loaded; + return _loaded!; } } From c6264191137fff72ca9ffd1f2a1b523d4ef79a7c Mon Sep 17 00:00:00 2001 From: Takoooooo Date: Thu, 23 Dec 2021 16:00:43 +0200 Subject: [PATCH 81/90] more refactorings --- samples/ControlCatalog/App.xaml | 2 -- samples/ControlCatalog/App.xaml.cs | 20 +++++--------------- samples/ControlCatalog/MainView.xaml.cs | 4 ++-- 3 files changed, 7 insertions(+), 19 deletions(-) diff --git a/samples/ControlCatalog/App.xaml b/samples/ControlCatalog/App.xaml index ab3aa5a4ae..227b31bf20 100644 --- a/samples/ControlCatalog/App.xaml +++ b/samples/ControlCatalog/App.xaml @@ -5,8 +5,6 @@ x:CompileBindings="True" x:Class="ControlCatalog.App"> - - diff --git a/samples/ControlCatalog/App.xaml.cs b/samples/ControlCatalog/App.xaml.cs index b21b9d80f0..4e86c60285 100644 --- a/samples/ControlCatalog/App.xaml.cs +++ b/samples/ControlCatalog/App.xaml.cs @@ -5,6 +5,7 @@ using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml.Styling; using Avalonia.Styling; +using Avalonia.Themes.Fluent; using ControlCatalog.ViewModels; namespace ControlCatalog @@ -26,21 +27,9 @@ namespace ControlCatalog Source = new Uri("avares://Avalonia.Controls.DataGrid/Themes/Default.xaml") }; - public static Styles FluentDark = new Styles - { - new StyleInclude(new Uri("avares://ControlCatalog/Styles")) - { - Source = new Uri("avares://Avalonia.Themes.Fluent/FluentDark.xaml") - } - }; + public static FluentTheme FluentDark = new FluentTheme(new Uri("avares://ControlCatalog/Styles")) { Mode = FluentThemeMode.Dark }; - public static Styles FluentLight = new Styles - { - new StyleInclude(new Uri("avares://ControlCatalog/Styles")) - { - Source = new Uri("avares://Avalonia.Themes.Fluent/FluentLight.xaml") - } - }; + public static FluentTheme FluentLight = new FluentTheme(new Uri("avares://ControlCatalog/Styles")); public static Styles DefaultLight = new Styles { @@ -92,7 +81,8 @@ namespace ControlCatalog public override void Initialize() { - + Styles.Insert(0, FluentLight); + Styles.Insert(1, DataGridFluent); AvaloniaXamlLoader.Load(this); } diff --git a/samples/ControlCatalog/MainView.xaml.cs b/samples/ControlCatalog/MainView.xaml.cs index b36c2b6997..696062db3f 100644 --- a/samples/ControlCatalog/MainView.xaml.cs +++ b/samples/ControlCatalog/MainView.xaml.cs @@ -53,7 +53,7 @@ namespace ControlCatalog } else { - Application.Current.Styles[0] = new FluentTheme(new Uri("avares://ControlCatalog/Styles")); + Application.Current.Styles[0] = App.FluentLight; Application.Current.Styles[1] = App.DataGridFluent; } } @@ -68,7 +68,7 @@ namespace ControlCatalog } else { - Application.Current.Styles[0] = new FluentTheme(new Uri("avares://ControlCatalog/Styles")) { Mode = FluentThemeMode.Dark }; + Application.Current.Styles[0] = App.FluentDark; Application.Current.Styles[1] = App.DataGridFluent; } } From 4fbb93a03cac66ceb63d9426be1f2e84663827ef Mon Sep 17 00:00:00 2001 From: Giacomo Gozzi Date: Thu, 23 Dec 2021 17:49:00 +0300 Subject: [PATCH 82/90] Fix #7227 --- .../GestureRecognizers/ScrollGestureRecognizer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Input/GestureRecognizers/ScrollGestureRecognizer.cs b/src/Avalonia.Input/GestureRecognizers/ScrollGestureRecognizer.cs index 7532676f18..f8cedb636f 100644 --- a/src/Avalonia.Input/GestureRecognizers/ScrollGestureRecognizer.cs +++ b/src/Avalonia.Input/GestureRecognizers/ScrollGestureRecognizer.cs @@ -71,7 +71,7 @@ namespace Avalonia.Input.GestureRecognizers EndGesture(); _tracking = e.Pointer; _gestureId = ScrollGestureEventArgs.GetNextFreeId();; - _trackedRootPoint = e.GetPosition(null); + _trackedRootPoint = e.GetPosition(_target); } } From 7daccf91ae96422fba7208428b1df8e61424a20a Mon Sep 17 00:00:00 2001 From: Takoooooo Date: Thu, 23 Dec 2021 17:37:11 +0200 Subject: [PATCH 83/90] cleanup --- samples/ControlCatalog/App.xaml.cs | 6 ++---- samples/ControlCatalog/MainView.xaml.cs | 5 +++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/samples/ControlCatalog/App.xaml.cs b/samples/ControlCatalog/App.xaml.cs index 4e86c60285..aba22a4af6 100644 --- a/samples/ControlCatalog/App.xaml.cs +++ b/samples/ControlCatalog/App.xaml.cs @@ -27,9 +27,7 @@ namespace ControlCatalog Source = new Uri("avares://Avalonia.Controls.DataGrid/Themes/Default.xaml") }; - public static FluentTheme FluentDark = new FluentTheme(new Uri("avares://ControlCatalog/Styles")) { Mode = FluentThemeMode.Dark }; - - public static FluentTheme FluentLight = new FluentTheme(new Uri("avares://ControlCatalog/Styles")); + public static FluentTheme Fluent = new FluentTheme(new Uri("avares://ControlCatalog/Styles")); public static Styles DefaultLight = new Styles { @@ -81,7 +79,7 @@ namespace ControlCatalog public override void Initialize() { - Styles.Insert(0, FluentLight); + Styles.Insert(0, Fluent); Styles.Insert(1, DataGridFluent); AvaloniaXamlLoader.Load(this); } diff --git a/samples/ControlCatalog/MainView.xaml.cs b/samples/ControlCatalog/MainView.xaml.cs index 696062db3f..d36ec3d272 100644 --- a/samples/ControlCatalog/MainView.xaml.cs +++ b/samples/ControlCatalog/MainView.xaml.cs @@ -53,7 +53,7 @@ namespace ControlCatalog } else { - Application.Current.Styles[0] = App.FluentLight; + Application.Current.Styles[0] = App.Fluent; Application.Current.Styles[1] = App.DataGridFluent; } } @@ -68,7 +68,8 @@ namespace ControlCatalog } else { - Application.Current.Styles[0] = App.FluentDark; + App.Fluent.Mode = FluentThemeMode.Dark; + Application.Current.Styles[0] = App.Fluent; Application.Current.Styles[1] = App.DataGridFluent; } } From 3bfafc5f9397893ca247b149091a1ba02167fd6b Mon Sep 17 00:00:00 2001 From: Takoooooo Date: Thu, 23 Dec 2021 17:56:30 +0200 Subject: [PATCH 84/90] more cleanup --- samples/ControlCatalog/MainView.xaml.cs | 28 +++++++------------------ 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/samples/ControlCatalog/MainView.xaml.cs b/samples/ControlCatalog/MainView.xaml.cs index d36ec3d272..0579355831 100644 --- a/samples/ControlCatalog/MainView.xaml.cs +++ b/samples/ControlCatalog/MainView.xaml.cs @@ -44,34 +44,22 @@ namespace ControlCatalog var themeStyle = Application.Current.Styles[0]; if (theme == CatalogTheme.FluentLight) { - if (themeStyle is FluentTheme fluentTheme) + if (App.Fluent.Mode != FluentThemeMode.Light) { - if (fluentTheme.Mode == FluentThemeMode.Dark) - { - fluentTheme.Mode = FluentThemeMode.Light; - } - } - else - { - Application.Current.Styles[0] = App.Fluent; - Application.Current.Styles[1] = App.DataGridFluent; + App.Fluent.Mode = FluentThemeMode.Light; } + Application.Current.Styles[0] = App.Fluent; + Application.Current.Styles[1] = App.DataGridFluent; } else if (theme == CatalogTheme.FluentDark) { - if (themeStyle is FluentTheme fluentTheme) - { - if (fluentTheme.Mode == FluentThemeMode.Light) - { - fluentTheme.Mode = FluentThemeMode.Dark; - } - } - else + + if (App.Fluent.Mode != FluentThemeMode.Dark) { App.Fluent.Mode = FluentThemeMode.Dark; - Application.Current.Styles[0] = App.Fluent; - Application.Current.Styles[1] = App.DataGridFluent; } + Application.Current.Styles[0] = App.Fluent; + Application.Current.Styles[1] = App.DataGridFluent; } else if (theme == CatalogTheme.DefaultLight) { From 9e5050cab99a0a55fbfb07ac12998feb18765df1 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Fri, 24 Dec 2021 00:16:34 +0300 Subject: [PATCH 85/90] Fixed some of Rider's complaints --- src/Avalonia.Base/Utilities/WeakSubscriptionManager.cs | 2 +- .../Media/TextFormatting/Unicode/LineBreakEnumerator.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Base/Utilities/WeakSubscriptionManager.cs b/src/Avalonia.Base/Utilities/WeakSubscriptionManager.cs index 66223e513d..88b1e3c807 100644 --- a/src/Avalonia.Base/Utilities/WeakSubscriptionManager.cs +++ b/src/Avalonia.Base/Utilities/WeakSubscriptionManager.cs @@ -180,7 +180,7 @@ namespace Avalonia.Utilities { var r = _data[c]; if (r?.TryGetTarget(out var sub) == true) - sub.OnEvent(sender, eventArgs); + sub!.OnEvent(sender, eventArgs); else needCompact = true; } diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakEnumerator.cs b/src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakEnumerator.cs index 40891a700d..0f6b81975a 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakEnumerator.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakEnumerator.cs @@ -80,12 +80,12 @@ namespace Avalonia.Media.TextFormatting.Unicode } } - var shouldBreak = GetSimpleBreak() ?? (bool?)GetPairTableBreak(lastClass); + var shouldBreak = GetSimpleBreak() ?? GetPairTableBreak(lastClass); // Rule LB8a _lb8a = _nextClass == LineBreakClass.ZWJ; - if (shouldBreak.Value) + if (shouldBreak) { Current = new LineBreak(FindPriorNonWhitespace(_lastPosition), _lastPosition); return true; From 69525b8d55c87481b91c8d22f6f72e088a31d8a8 Mon Sep 17 00:00:00 2001 From: Tim Date: Fri, 24 Dec 2021 14:43:58 +0100 Subject: [PATCH 86/90] Use IsKeyboradFocusWithin instead of IsFocused --- src/Avalonia.Controls/ButtonSpinner.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/ButtonSpinner.cs b/src/Avalonia.Controls/ButtonSpinner.cs index 5a967ab488..f1c1a55a17 100644 --- a/src/Avalonia.Controls/ButtonSpinner.cs +++ b/src/Avalonia.Controls/ButtonSpinner.cs @@ -197,7 +197,7 @@ namespace Avalonia.Controls { base.OnPointerWheelChanged(e); - if (AllowSpin && IsFocused) + if (AllowSpin && IsKeyboardFocusWithin) { if (e.Delta.Y != 0) { From fe42fbfe6b3d7fcd6951a7aca60749c83b05f42d Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Sat, 25 Dec 2021 00:56:20 +0300 Subject: [PATCH 87/90] Add ViewContract to ViewModelViewHost --- src/Avalonia.ReactiveUI/ViewModelViewHost.cs | 52 +++++++++++--- .../ViewModelViewHostTest.cs | 72 ++++++++++++++++++- 2 files changed, 113 insertions(+), 11 deletions(-) diff --git a/src/Avalonia.ReactiveUI/ViewModelViewHost.cs b/src/Avalonia.ReactiveUI/ViewModelViewHost.cs index c88323d674..16dee00ebc 100644 --- a/src/Avalonia.ReactiveUI/ViewModelViewHost.cs +++ b/src/Avalonia.ReactiveUI/ViewModelViewHost.cs @@ -3,7 +3,7 @@ using System.Reactive.Disposables; using ReactiveUI; using Splat; -namespace Avalonia.ReactiveUI +namespace Avalonia.ReactiveUI { /// /// This content control will automatically load the View associated with @@ -18,6 +18,12 @@ namespace Avalonia.ReactiveUI public static readonly AvaloniaProperty ViewModelProperty = AvaloniaProperty.Register(nameof(ViewModel)); + /// + /// for the property. + /// + public static readonly StyledProperty ViewContractProperty = + AvaloniaProperty.Register(nameof(ViewContract)); + /// /// Initializes a new instance of the class. /// @@ -25,8 +31,8 @@ namespace Avalonia.ReactiveUI { this.WhenActivated(disposables => { - this.WhenAnyValue(x => x.ViewModel) - .Subscribe(NavigateToViewModel) + this.WhenAnyValue(x => x.ViewModel, x => x.ViewContract) + .Subscribe(tuple => NavigateToViewModel(tuple.Item1, tuple.Item2)) .DisposeWith(disposables); }); } @@ -39,7 +45,16 @@ namespace Avalonia.ReactiveUI get => GetValue(ViewModelProperty); set => SetValue(ViewModelProperty, value); } - + + /// + /// Gets or sets the view contract. + /// + public string? ViewContract + { + get => GetValue(ViewContractProperty); + set => SetValue(ViewContractProperty, value); + } + /// /// Gets or sets the view locator. /// @@ -49,7 +64,8 @@ namespace Avalonia.ReactiveUI /// Invoked when ReactiveUI router navigates to a view model. /// /// ViewModel to which the user navigates. - private void NavigateToViewModel(object? viewModel) + /// The contract for view resolution. + private void NavigateToViewModel(object? viewModel, string? contract) { if (viewModel == null) { @@ -57,17 +73,33 @@ namespace Avalonia.ReactiveUI Content = DefaultContent; return; } - + var viewLocator = ViewLocator ?? global::ReactiveUI.ViewLocator.Current; - var viewInstance = viewLocator.ResolveView(viewModel); + var viewInstance = viewLocator.ResolveView(viewModel, contract); if (viewInstance == null) { - this.Log().Warn($"Couldn't find view for '{viewModel}'. Is it registered? Falling back to default content."); + if (contract == null) + { + this.Log().Warn($"Couldn't find view for '{viewModel}'. Is it registered? Falling back to default content."); + } + else + { + this.Log().Warn($"Couldn't find view with contract '{contract}' for '{viewModel}'. Is it registered? Falling back to default content."); + } + Content = DefaultContent; return; } - - this.Log().Info($"Ready to show {viewInstance} with autowired {viewModel}."); + + if (contract == null) + { + this.Log().Info($"Ready to show {viewInstance} with autowired {viewModel}."); + } + else + { + this.Log().Info($"Ready to show {viewInstance} with autowired {viewModel} and contract '{contract}'."); + } + viewInstance.ViewModel = viewModel; if (viewInstance is IStyledElement styled) styled.DataContext = viewModel; diff --git a/tests/Avalonia.ReactiveUI.UnitTests/ViewModelViewHostTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/ViewModelViewHostTest.cs index 35d1cbf62d..858c476227 100644 --- a/tests/Avalonia.ReactiveUI.UnitTests/ViewModelViewHostTest.cs +++ b/tests/Avalonia.ReactiveUI.UnitTests/ViewModelViewHostTest.cs @@ -12,15 +12,23 @@ namespace Avalonia.ReactiveUI.UnitTests public class FirstView : ReactiveUserControl { } + public class AlternativeFirstView : ReactiveUserControl { } + public class SecondViewModel : ReactiveObject { } public class SecondView : ReactiveUserControl { } + public class AlternativeSecondView : ReactiveUserControl { } + + public static string AlternativeViewContract => "AlternativeView"; + public ViewModelViewHostTest() { Locator.CurrentMutable.RegisterConstant(new AvaloniaActivationForViewFetcher(), typeof(IActivationForViewFetcher)); Locator.CurrentMutable.Register(() => new FirstView(), typeof(IViewFor)); Locator.CurrentMutable.Register(() => new SecondView(), typeof(IViewFor)); + Locator.CurrentMutable.Register(() => new AlternativeFirstView(), typeof(IViewFor), AlternativeViewContract); + Locator.CurrentMutable.Register(() => new AlternativeSecondView(), typeof(IViewFor), AlternativeViewContract); } [Fact] @@ -67,5 +75,67 @@ namespace Avalonia.ReactiveUI.UnitTests Assert.Equal(first, ((FirstView)host.Content).DataContext); Assert.Equal(first, ((FirstView)host.Content).ViewModel); } + + [Fact] + public void ViewModelViewHost_View_Should_Stay_In_Sync_With_ViewModel_And_Contract() + { + var defaultContent = new TextBlock(); + var host = new ViewModelViewHost + { + DefaultContent = defaultContent, + PageTransition = null + }; + + var root = new TestRoot + { + Child = host + }; + + Assert.NotNull(host.Content); + Assert.Equal(typeof(TextBlock), host.Content.GetType()); + Assert.Equal(defaultContent, host.Content); + + var first = new FirstViewModel(); + host.ViewModel = first; + + host.ViewContract = null; + Assert.NotNull(host.Content); + Assert.Equal(typeof(FirstView), host.Content.GetType()); + Assert.Equal(first, ((FirstView)host.Content).DataContext); + Assert.Equal(first, ((FirstView)host.Content).ViewModel); + + host.ViewContract = AlternativeViewContract; + Assert.NotNull(host.Content); + Assert.Equal(typeof(AlternativeFirstView), host.Content.GetType()); + Assert.Equal(first, ((AlternativeFirstView)host.Content).DataContext); + Assert.Equal(first, ((AlternativeFirstView)host.Content).ViewModel); + + var second = new SecondViewModel(); + host.ViewModel = second; + + host.ViewContract = null; + Assert.NotNull(host.Content); + Assert.Equal(typeof(SecondView), host.Content.GetType()); + Assert.Equal(second, ((SecondView)host.Content).DataContext); + Assert.Equal(second, ((SecondView)host.Content).ViewModel); + + host.ViewContract = AlternativeViewContract; + Assert.NotNull(host.Content); + Assert.Equal(typeof(AlternativeSecondView), host.Content.GetType()); + Assert.Equal(second, ((AlternativeSecondView)host.Content).DataContext); + Assert.Equal(second, ((AlternativeSecondView)host.Content).ViewModel); + + host.ViewModel = null; + + host.ViewContract = null; + Assert.NotNull(host.Content); + Assert.Equal(typeof(TextBlock), host.Content.GetType()); + Assert.Equal(defaultContent, host.Content); + + host.ViewContract = AlternativeViewContract; + Assert.NotNull(host.Content); + Assert.Equal(typeof(TextBlock), host.Content.GetType()); + Assert.Equal(defaultContent, host.Content); + } } -} \ No newline at end of file +} From af902ae969573099b67dc506d7516ca2dde1a165 Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Sat, 25 Dec 2021 00:56:36 +0300 Subject: [PATCH 88/90] Add ViewContract to RoutedViewHost --- src/Avalonia.ReactiveUI/RoutedViewHost.cs | 57 ++++++++++++--- .../RoutedViewHostTest.cs | 73 +++++++++++++++++++ 2 files changed, 119 insertions(+), 11 deletions(-) diff --git a/src/Avalonia.ReactiveUI/RoutedViewHost.cs b/src/Avalonia.ReactiveUI/RoutedViewHost.cs index 63456bc13a..a475cf5eac 100644 --- a/src/Avalonia.ReactiveUI/RoutedViewHost.cs +++ b/src/Avalonia.ReactiveUI/RoutedViewHost.cs @@ -57,7 +57,13 @@ namespace Avalonia.ReactiveUI /// public static readonly StyledProperty RouterProperty = AvaloniaProperty.Register(nameof(Router)); - + + /// + /// for the property. + /// + public static readonly StyledProperty ViewContractProperty = + AvaloniaProperty.Register(nameof(ViewContract)); + /// /// Initializes a new instance of the class. /// @@ -70,15 +76,18 @@ namespace Avalonia.ReactiveUI .Where(router => router == null)! .Cast(); + var viewContract = this.WhenAnyValue(x => x.ViewContract); + this.WhenAnyValue(x => x.Router) .Where(router => router != null) .SelectMany(router => router!.CurrentViewModel) .Merge(routerRemoved) - .Subscribe(NavigateToViewModel) + .CombineLatest(viewContract) + .Subscribe(tuple => NavigateToViewModel(tuple.First, tuple.Second)) .DisposeWith(disposables); }); } - + /// /// Gets or sets the of the view model stack. /// @@ -87,17 +96,27 @@ namespace Avalonia.ReactiveUI get => GetValue(RouterProperty); set => SetValue(RouterProperty, value); } - + + /// + /// Gets or sets the view contract. + /// + public string? ViewContract + { + get => GetValue(ViewContractProperty); + set => SetValue(ViewContractProperty, value); + } + /// /// Gets or sets the ReactiveUI view locator used by this router. /// public IViewLocator? ViewLocator { get; set; } - + /// /// Invoked when ReactiveUI router navigates to a view model. /// /// ViewModel to which the user navigates. - private void NavigateToViewModel(object? viewModel) + /// The contract for view resolution. + private void NavigateToViewModel(object? viewModel, string? contract) { if (Router == null) { @@ -112,17 +131,33 @@ namespace Avalonia.ReactiveUI Content = DefaultContent; return; } - + var viewLocator = ViewLocator ?? global::ReactiveUI.ViewLocator.Current; - var viewInstance = viewLocator.ResolveView(viewModel); + var viewInstance = viewLocator.ResolveView(viewModel, contract); if (viewInstance == null) { - this.Log().Warn($"Couldn't find view for '{viewModel}'. Is it registered? Falling back to default content."); + if (contract == null) + { + this.Log().Warn($"Couldn't find view for '{viewModel}'. Is it registered? Falling back to default content."); + } + else + { + this.Log().Warn($"Couldn't find view with contract '{contract}' for '{viewModel}'. Is it registered? Falling back to default content."); + } + Content = DefaultContent; return; } - - this.Log().Info($"Ready to show {viewInstance} with autowired {viewModel}."); + + if (contract == null) + { + this.Log().Info($"Ready to show {viewInstance} with autowired {viewModel}."); + } + else + { + this.Log().Info($"Ready to show {viewInstance} with autowired {viewModel} and contract '{contract}'."); + } + viewInstance.ViewModel = viewModel; if (viewInstance is IDataContextProvider provider) provider.DataContext = viewModel; diff --git a/tests/Avalonia.ReactiveUI.UnitTests/RoutedViewHostTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/RoutedViewHostTest.cs index b82b1b1acc..244b5abc4e 100644 --- a/tests/Avalonia.ReactiveUI.UnitTests/RoutedViewHostTest.cs +++ b/tests/Avalonia.ReactiveUI.UnitTests/RoutedViewHostTest.cs @@ -29,6 +29,8 @@ namespace Avalonia.ReactiveUI.UnitTests public class FirstRoutableView : ReactiveUserControl { } + public class AlternativeFirstRoutableView : ReactiveUserControl { } + public class SecondRoutableViewModel : ReactiveObject, IRoutableViewModel { public string UrlPathSegment => "second"; @@ -38,16 +40,22 @@ namespace Avalonia.ReactiveUI.UnitTests public class SecondRoutableView : ReactiveUserControl { } + public class AlternativeSecondRoutableView : ReactiveUserControl { } + public class ScreenViewModel : ReactiveObject, IScreen { public RoutingState Router { get; } = new RoutingState(); } + public static string AlternativeViewContract => "AlternativeView"; + public RoutedViewHostTest() { Locator.CurrentMutable.RegisterConstant(new AvaloniaActivationForViewFetcher(), typeof(IActivationForViewFetcher)); Locator.CurrentMutable.Register(() => new FirstRoutableView(), typeof(IViewFor)); Locator.CurrentMutable.Register(() => new SecondRoutableView(), typeof(IViewFor)); + Locator.CurrentMutable.Register(() => new AlternativeFirstRoutableView(), typeof(IViewFor), AlternativeViewContract); + Locator.CurrentMutable.Register(() => new AlternativeSecondRoutableView(), typeof(IViewFor), AlternativeViewContract); } [Fact] @@ -101,6 +109,71 @@ namespace Avalonia.ReactiveUI.UnitTests Assert.Equal(defaultContent, host.Content); } + [Fact] + public void RoutedViewHost_Should_Stay_In_Sync_With_RoutingState_And_Contract() + { + var screen = new ScreenViewModel(); + var defaultContent = new TextBlock(); + var host = new RoutedViewHost + { + Router = screen.Router, + DefaultContent = defaultContent, + PageTransition = null + }; + + var root = new TestRoot + { + Child = host + }; + + Assert.NotNull(host.Content); + Assert.IsType(host.Content); + Assert.Equal(defaultContent, host.Content); + + var first = new FirstRoutableViewModel(); + screen.Router.Navigate.Execute(first).Subscribe(); + + host.ViewContract = null; + Assert.NotNull(host.Content); + Assert.IsType(host.Content); + Assert.Equal(first, ((FirstRoutableView)host.Content).DataContext); + Assert.Equal(first, ((FirstRoutableView)host.Content).ViewModel); + + host.ViewContract = AlternativeViewContract; + Assert.NotNull(host.Content); + Assert.IsType(host.Content); + Assert.Equal(first, ((AlternativeFirstRoutableView)host.Content).DataContext); + Assert.Equal(first, ((AlternativeFirstRoutableView)host.Content).ViewModel); + + var second = new SecondRoutableViewModel(); + screen.Router.Navigate.Execute(second).Subscribe(); + + host.ViewContract = null; + Assert.NotNull(host.Content); + Assert.IsType(host.Content); + Assert.Equal(second, ((SecondRoutableView)host.Content).DataContext); + Assert.Equal(second, ((SecondRoutableView)host.Content).ViewModel); + + host.ViewContract = AlternativeViewContract; + Assert.NotNull(host.Content); + Assert.IsType(host.Content); + Assert.Equal(second, ((AlternativeSecondRoutableView)host.Content).DataContext); + Assert.Equal(second, ((AlternativeSecondRoutableView)host.Content).ViewModel); + + screen.Router.NavigateBack.Execute(Unit.Default).Subscribe(); + + Assert.NotNull(host.Content); + Assert.IsType(host.Content); + Assert.Equal(first, ((AlternativeFirstRoutableView)host.Content).DataContext); + Assert.Equal(first, ((AlternativeFirstRoutableView)host.Content).ViewModel); + + screen.Router.NavigateBack.Execute(Unit.Default).Subscribe(); + + Assert.NotNull(host.Content); + Assert.IsType(host.Content); + Assert.Equal(defaultContent, host.Content); + } + [Fact] public void RoutedViewHost_Should_Show_Default_Content_When_Router_Is_Null() { From c39cc1b0833fa74c6a911a75426de1b64c00910a Mon Sep 17 00:00:00 2001 From: workgroupengineering Date: Sat, 25 Dec 2021 07:13:23 +0100 Subject: [PATCH 89/90] feat(DevTools): Allow to attach DevTools at Application (#6771) * feat(DevTools): Allow to attach DevTools at Application * fixes(DevTools): PointerOverElement when attach to Application * feat: Add AvaloniaVersion * feat: IsDevelopmentBuild * feat: Add some useful properties to Application Decorator * fixes: Hide Layout Viewer when select appliction node * fix: removed MachineId * fixes: DesignerSupportTests fails * fix(DevTools): Update XML Comment and nits * fix(DevTools): Avoid interaction of Layout Visualizer with keyboard when it is hidden. * Added some comment * fixes: Code formatting * fixes(DevTools): Strip unnecessary property from Application Decorator * fixes(ControlCatalog): remove AttachDevTools from DecoratedWindow * fixes(DevTools): Application doesn't close when the last window closed when DevTools is open * fixes: Missing Application properties decoration * fixes(DevTools): Nullable annotations * fixes(DevTools): Unified the behavior of AttachDevTools * fixes(DevTools): typo * fixes(DevTools): Null Annotation Co-authored-by: Max Katz --- samples/ControlCatalog/App.xaml.cs | 5 + .../ControlCatalog/DecoratedWindow.xaml.cs | 1 - samples/ControlCatalog/MainWindow.xaml.cs | 5 +- .../DevToolsExtensions.cs | 44 +++++++ .../Diagnostics/Behaviors/ColumnDefinition.cs | 47 ++++++++ .../Diagnostics/Controls/Application.cs | 109 ++++++++++++++++++ .../Diagnostics/DevTools.cs | 95 ++++++++++++--- .../Diagnostics/DevToolsOptions.cs | 1 + .../Diagnostics/KeyGestureExtesions.cs | 28 +++++ .../ViewModels/ControlDetailsViewModel.cs | 27 ++--- .../Diagnostics/ViewModels/LogicalTreeNode.cs | 90 ++++++++++++++- .../Diagnostics/ViewModels/MainViewModel.cs | 66 ++++++++--- .../Diagnostics/ViewModels/TreeNode.cs | 9 +- .../ViewModels/TreeNodeCollection.cs | 14 +++ .../Diagnostics/ViewModels/VisualTreeNode.cs | 80 ++++++++++++- .../Diagnostics/Views/ControlDetailsView.xaml | 22 +++- .../Diagnostics/Views/MainView.xaml | 2 +- .../Diagnostics/Views/MainWindow.xaml.cs | 21 ++-- .../Diagnostics/Views/TreePageView.xaml.cs | 7 +- 19 files changed, 593 insertions(+), 80 deletions(-) create mode 100644 src/Avalonia.Diagnostics/Diagnostics/Behaviors/ColumnDefinition.cs create mode 100644 src/Avalonia.Diagnostics/Diagnostics/Controls/Application.cs create mode 100644 src/Avalonia.Diagnostics/Diagnostics/KeyGestureExtesions.cs diff --git a/samples/ControlCatalog/App.xaml.cs b/samples/ControlCatalog/App.xaml.cs index aba22a4af6..816356bee9 100644 --- a/samples/ControlCatalog/App.xaml.cs +++ b/samples/ControlCatalog/App.xaml.cs @@ -94,6 +94,11 @@ namespace ControlCatalog singleViewLifetime.MainView = new MainView(); base.OnFrameworkInitializationCompleted(); + + this.AttachDevTools(new Avalonia.Diagnostics.DevToolsOptions() + { + StartupScreenIndex = 1, + }); } } } diff --git a/samples/ControlCatalog/DecoratedWindow.xaml.cs b/samples/ControlCatalog/DecoratedWindow.xaml.cs index bdf5b8fbee..a1383b9107 100644 --- a/samples/ControlCatalog/DecoratedWindow.xaml.cs +++ b/samples/ControlCatalog/DecoratedWindow.xaml.cs @@ -11,7 +11,6 @@ namespace ControlCatalog public DecoratedWindow() { this.InitializeComponent(); - this.AttachDevTools(); } void SetupSide(string name, StandardCursorType cursor, WindowEdge edge) diff --git a/samples/ControlCatalog/MainWindow.xaml.cs b/samples/ControlCatalog/MainWindow.xaml.cs index dd62698cc7..20591103b7 100644 --- a/samples/ControlCatalog/MainWindow.xaml.cs +++ b/samples/ControlCatalog/MainWindow.xaml.cs @@ -17,10 +17,7 @@ namespace ControlCatalog public MainWindow() { this.InitializeComponent(); - this.AttachDevTools(new Avalonia.Diagnostics.DevToolsOptions() - { - StartupScreenIndex = 1, - }); + //Renderer.DrawFps = true; //Renderer.DrawDirtyRects = Renderer.DrawFps = true; diff --git a/src/Avalonia.Diagnostics/DevToolsExtensions.cs b/src/Avalonia.Diagnostics/DevToolsExtensions.cs index aa585dca40..68deaf8335 100644 --- a/src/Avalonia.Diagnostics/DevToolsExtensions.cs +++ b/src/Avalonia.Diagnostics/DevToolsExtensions.cs @@ -37,5 +37,49 @@ namespace Avalonia { DevTools.Attach(root, options); } + + /// + /// Attaches DevTools to a Application, to be opened with the specified options. + /// + /// The Application to attach DevTools to. + public static void AttachDevTools(this Application application) + { + DevTools.Attach(application, new DevToolsOptions()); + } + + /// + /// Attaches DevTools to a Application, to be opened with the specified options. + /// + /// The Application to attach DevTools to. + /// Additional settings of DevTools. + /// + /// Attach DevTools should only be called after application initialization is complete. A good point is + /// + /// + /// + /// public class App : Application + /// { + /// public override void OnFrameworkInitializationCompleted() + /// { + /// if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime) + /// { + /// desktopLifetime.MainWindow = new MainWindow(); + /// } + /// else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewLifetime) + /// singleViewLifetime.MainView = new MainView(); + /// + /// base.OnFrameworkInitializationCompleted(); + /// this.AttachDevTools(new Avalonia.Diagnostics.DevToolsOptions() + /// { + /// StartupScreenIndex = 1, + /// }); + /// } + /// } + /// + /// + public static void AttachDevTools(this Application application, DevToolsOptions options) + { + DevTools.Attach(application, options); + } } } diff --git a/src/Avalonia.Diagnostics/Diagnostics/Behaviors/ColumnDefinition.cs b/src/Avalonia.Diagnostics/Diagnostics/Behaviors/ColumnDefinition.cs new file mode 100644 index 0000000000..beccf022d7 --- /dev/null +++ b/src/Avalonia.Diagnostics/Diagnostics/Behaviors/ColumnDefinition.cs @@ -0,0 +1,47 @@ +namespace Avalonia.Diagnostics.Behaviors +{ + /// + /// See discussion https://github.com/AvaloniaUI/Avalonia/discussions/6773 + /// + static class ColumnDefinition + { + private readonly static Avalonia.Controls.GridLength ZeroWidth = + new Avalonia.Controls.GridLength(0, Avalonia.Controls.GridUnitType.Pixel); + + private readonly static AttachedProperty LastWidthProperty = + AvaloniaProperty.RegisterAttached("LastWidth" + , typeof(ColumnDefinition) + , default); + + public readonly static AttachedProperty IsVisibleProperty = + AvaloniaProperty.RegisterAttached("IsVisible" + , typeof(ColumnDefinition) + , true + , coerce: (element, visibility) => + { + + var lastWidth = element.GetValue(LastWidthProperty); + if (visibility == true && lastWidth is { }) + { + element.SetValue(Avalonia.Controls.ColumnDefinition.WidthProperty, lastWidth); + } + else if (visibility == false) + { + element.SetValue(LastWidthProperty, element.GetValue(Avalonia.Controls.ColumnDefinition.WidthProperty)); + element.SetValue(Avalonia.Controls.ColumnDefinition.WidthProperty, ZeroWidth); + } + return visibility; + } + ); + + public static bool GetIsVisible(Avalonia.Controls.ColumnDefinition columnDefinition) + { + return columnDefinition.GetValue(IsVisibleProperty); + } + + public static void SetIsVisible(Avalonia.Controls.ColumnDefinition columnDefinition, bool visibility) + { + columnDefinition.SetValue(IsVisibleProperty, visibility); + } + } +} diff --git a/src/Avalonia.Diagnostics/Diagnostics/Controls/Application.cs b/src/Avalonia.Diagnostics/Diagnostics/Controls/Application.cs new file mode 100644 index 0000000000..b2f2974440 --- /dev/null +++ b/src/Avalonia.Diagnostics/Diagnostics/Controls/Application.cs @@ -0,0 +1,109 @@ +using System; +using Avalonia.Controls; +using Lifetimes = Avalonia.Controls.ApplicationLifetimes; +using App = Avalonia.Application; + +namespace Avalonia.Diagnostics.Controls +{ + class Application : AvaloniaObject + , Input.ICloseable + + { + private readonly App _application; + private static readonly Version s_version = typeof(IAvaloniaObject).Assembly?.GetName()?.Version + ?? Version.Parse("0.0.00"); + public event EventHandler? Closed; + + public Application(App application) + { + _application = application; + + if (_application.ApplicationLifetime is Lifetimes.IControlledApplicationLifetime controller) + { + EventHandler eh = default!; + eh = (s, e) => + { + controller.Exit -= eh; + Closed?.Invoke(s, e); + }; + controller.Exit += eh; + } + + } + + internal App Instance => _application; + + /// + /// Defines the property. + /// + public object? DataContext => + _application.DataContext; + + /// + /// Gets or sets the application's global data templates. + /// + /// + /// The application's global data templates. + /// + public Avalonia.Controls.Templates.DataTemplates DataTemplates => + _application.DataTemplates; + + /// + /// Gets the application's focus manager. + /// + /// + /// The application's focus manager. + /// + public Input.IFocusManager? FocusManager => + _application.FocusManager; + + /// + /// Gets the application's input manager. + /// + /// + /// The application's input manager. + /// + public Input.InputManager? InputManager => + _application.InputManager; + + /// + /// Gets the application clipboard. + /// + public Input.Platform.IClipboard? Clipboard => + _application.Clipboard; + + /// + /// Gets the application's global resource dictionary. + /// + public IResourceDictionary Resources => + _application.Resources; + + /// + /// Gets the application's global styles. + /// + /// + /// The application's global styles. + /// + /// + /// Global styles apply to all windows in the application. + /// + public Styling.Styles Styles => + _application.Styles; + + /// + /// Application lifetime, use it for things like setting the main window and exiting the app from code + /// Currently supported lifetimes are: + /// - + /// - + /// - + /// + public Lifetimes.IApplicationLifetime? ApplicationLifetime => + _application.ApplicationLifetime; + + /// + /// Application name to be used for various platform-specific purposes + /// + public string? Name => + _application.Name; + } +} diff --git a/src/Avalonia.Diagnostics/Diagnostics/DevTools.cs b/src/Avalonia.Diagnostics/Diagnostics/DevTools.cs index 2a386f106e..683c2e6549 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/DevTools.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/DevTools.cs @@ -1,17 +1,21 @@ using System; using System.Collections.Generic; using System.Reactive.Disposables; +using System.Reactive.Linq; using Avalonia.Controls; using Avalonia.Diagnostics.Views; using Avalonia.Input; +using Avalonia.Input.Raw; using Avalonia.Interactivity; namespace Avalonia.Diagnostics { public static class DevTools { - private static readonly Dictionary s_open = - new Dictionary(); + private static readonly Dictionary s_open = + new Dictionary(); + + private static bool s_attachedToApplication; public static IDisposable Attach(TopLevel root, KeyGesture gesture) { @@ -23,6 +27,11 @@ namespace Avalonia.Diagnostics public static IDisposable Attach(TopLevel root, DevToolsOptions options) { + if (s_attachedToApplication == true) + { + throw new ArgumentException("DevTools already attached to application", nameof(root)); + } + void PreviewKeyDown(object? sender, KeyEventArgs e) { if (options.Gesture.Matches(e)) @@ -37,45 +46,95 @@ namespace Avalonia.Diagnostics RoutingStrategies.Tunnel); } - public static IDisposable Open(TopLevel root) => Open(root, new DevToolsOptions()); + public static IDisposable Open(TopLevel root) => + Open(Application.Current,new DevToolsOptions(),root as Window); + + public static IDisposable Open(TopLevel root, DevToolsOptions options) => + Open(Application.Current, options, root as Window); - public static IDisposable Open(TopLevel root, DevToolsOptions options) + private static void DevToolsClosed(object? sender, EventArgs e) { - if (s_open.TryGetValue(root, out var window)) + var window = (MainWindow)sender!; + window.Closed -= DevToolsClosed; + if (window.Root is Controls.Application host) + { + s_open.Remove(host.Instance); + } + else { + s_open.Remove(window.Root!); + } + } + + internal static IDisposable Attach(Application? application, DevToolsOptions options, Window? owner = null) + { + if (application is null) + { + throw new ArgumentNullException(nameof(application)); + } + var result = Disposable.Empty; + // Skip if call on Design Mode + if (!Avalonia.Controls.Design.IsDesignMode + && !s_attachedToApplication) + { + + var lifeTime = application.ApplicationLifetime + as Avalonia.Controls.ApplicationLifetimes.IControlledApplicationLifetime; + + if (lifeTime is null) + { + throw new ArgumentNullException(nameof(Application.ApplicationLifetime)); + } + + if (application.InputManager is { }) + { + s_attachedToApplication = true; + + application.InputManager.PreProcess.OfType().Subscribe(e => + { + if (options.Gesture.Matches(e)) + { + result = Open(application, options, owner); + } + }); + + } + } + return result; + } + + private static IDisposable Open(Application? application, DevToolsOptions options, Window? owner = default) + { + if (application is null) + { + throw new ArgumentNullException(nameof(application)); + } + if (s_open.TryGetValue(application, out var window)) + { window.Activate(); } else { window = new MainWindow { - Root = root, + Root = new Controls.Application(application), Width = options.Size.Width, Height = options.Size.Height, }; window.SetOptions(options); window.Closed += DevToolsClosed; - s_open.Add(root, window); - - if (options.ShowAsChildWindow && root is Window inspectedWindow) + s_open.Add(application, window); + if (options.ShowAsChildWindow && owner is { }) { - window.Show(inspectedWindow); + window.Show(owner); } else { window.Show(); } } - return Disposable.Create(() => window?.Close()); } - - private static void DevToolsClosed(object? sender, EventArgs e) - { - var window = (MainWindow)sender!; - s_open.Remove(window.Root!); - window.Closed -= DevToolsClosed; - } } } diff --git a/src/Avalonia.Diagnostics/Diagnostics/DevToolsOptions.cs b/src/Avalonia.Diagnostics/Diagnostics/DevToolsOptions.cs index 5336dca65b..4181989388 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/DevToolsOptions.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/DevToolsOptions.cs @@ -16,6 +16,7 @@ namespace Avalonia.Diagnostics /// Gets or sets a value indicating whether DevTools should be displayed as a child window /// of the window being inspected. The default value is true. /// + /// This setting is ignored if DevTools is attached to public bool ShowAsChildWindow { get; set; } = true; /// diff --git a/src/Avalonia.Diagnostics/Diagnostics/KeyGestureExtesions.cs b/src/Avalonia.Diagnostics/Diagnostics/KeyGestureExtesions.cs new file mode 100644 index 0000000000..bb3ebc4708 --- /dev/null +++ b/src/Avalonia.Diagnostics/Diagnostics/KeyGestureExtesions.cs @@ -0,0 +1,28 @@ +using Avalonia.Input; +using Avalonia.Input.Raw; + +namespace Avalonia.Diagnostics +{ + static class KeyGestureExtesions + { + public static bool Matches(this KeyGesture gesture, RawKeyEventArgs keyEvent) => + keyEvent != null && + (KeyModifiers)(keyEvent.Modifiers & RawInputModifiers.KeyboardMask) == gesture.KeyModifiers && + ResolveNumPadOperationKey(keyEvent.Key) == ResolveNumPadOperationKey(gesture.Key); + + private static Key ResolveNumPadOperationKey(Key key) + { + switch (key) + { + case Key.Add: + return Key.OemPlus; + case Key.Subtract: + return Key.OemMinus; + case Key.Decimal: + return Key.OemPeriod; + default: + return key; + } + } + } +} diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs index 16293e35d2..c96172d5bb 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs @@ -16,7 +16,7 @@ namespace Avalonia.Diagnostics.ViewModels { internal class ControlDetailsViewModel : ViewModelBase, IDisposable { - private readonly IVisual _control; + private readonly IAvaloniaObject _avaloniaObject; private IDictionary>? _propertyIndex; private PropertyViewModel? _selectedProperty; private DataGridCollectionView? _propertiesView; @@ -28,20 +28,21 @@ namespace Avalonia.Diagnostics.ViewModels private string? _selectedEntityName; private string? _selectedEntityType; - public ControlDetailsViewModel(TreePageViewModel treePage, IVisual control) + public ControlDetailsViewModel(TreePageViewModel treePage, IAvaloniaObject avaloniaObject) { - _control = control; + _avaloniaObject = avaloniaObject; - TreePage = treePage; - - Layout = new ControlLayoutViewModel(control); + TreePage = treePage; + Layout = avaloniaObject is IVisual + ? new ControlLayoutViewModel((IVisual)avaloniaObject) + : default; - NavigateToProperty(control, (control as IControl)?.Name ?? control.ToString()); + NavigateToProperty(_avaloniaObject, (_avaloniaObject as IControl)?.Name ?? _avaloniaObject.ToString()); AppliedStyles = new ObservableCollection(); PseudoClasses = new ObservableCollection(); - if (control is StyledElement styledElement) + if (avaloniaObject is StyledElement styledElement) { styledElement.Classes.CollectionChanged += OnClassesChanged; @@ -181,7 +182,7 @@ namespace Avalonia.Diagnostics.ViewModels set => RaiseAndSetIfChanged(ref _styleStatus, value); } - public ControlLayoutViewModel Layout { get; } + public ControlLayoutViewModel? Layout { get; } protected override void OnPropertyChanged(PropertyChangedEventArgs e) { @@ -215,17 +216,17 @@ namespace Avalonia.Diagnostics.ViewModels public void Dispose() { - if (_control is INotifyPropertyChanged inpc) + if (_avaloniaObject is INotifyPropertyChanged inpc) { inpc.PropertyChanged -= ControlPropertyChanged; } - if (_control is AvaloniaObject ao) + if (_avaloniaObject is AvaloniaObject ao) { ao.PropertyChanged -= ControlPropertyChanged; } - if (_control is StyledElement se) + if (_avaloniaObject is StyledElement se) { se.Classes.CollectionChanged -= OnClassesChanged; } @@ -278,7 +279,7 @@ namespace Avalonia.Diagnostics.ViewModels } } - Layout.ControlPropertyChanged(sender, e); + Layout?.ControlPropertyChanged(sender, e); } private void ControlPropertyChanged(object? sender, PropertyChangedEventArgs e) diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/LogicalTreeNode.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/LogicalTreeNode.cs index 04215fa8ae..d627eba154 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/LogicalTreeNode.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/LogicalTreeNode.cs @@ -1,23 +1,31 @@ using System; +using System.Reactive.Disposables; using Avalonia.Collections; using Avalonia.Controls; using Avalonia.LogicalTree; +using Lifetimes = Avalonia.Controls.ApplicationLifetimes; +using System.Linq; namespace Avalonia.Diagnostics.ViewModels { internal class LogicalTreeNode : TreeNode { - public LogicalTreeNode(ILogical logical, TreeNode? parent) - : base((Control)logical, parent) + public LogicalTreeNode(IAvaloniaObject avaloniaObject, TreeNode? parent) + : base(avaloniaObject, parent) { - Children = new LogicalTreeNodeCollection(this, logical); + Children = avaloniaObject switch + { + ILogical logical => new LogicalTreeNodeCollection(this, logical), + Controls.Application host => new ApplicationHostLogical(this, host), + _ => TreeNodeCollection.Empty + }; } public override TreeNodeCollection Children { get; } public static LogicalTreeNode[] Create(object control) { - var logical = control as ILogical; + var logical = control as IAvaloniaObject; return logical != null ? new[] { new LogicalTreeNode(logical, null) } : Array.Empty(); } @@ -41,10 +49,82 @@ namespace Avalonia.Diagnostics.ViewModels protected override void Initialize(AvaloniaList nodes) { _subscription = _control.LogicalChildren.ForEachItem( - (i, item) => nodes.Insert(i, new LogicalTreeNode(item, Owner)), + (i, item) => nodes.Insert(i, new LogicalTreeNode((IAvaloniaObject)item, Owner)), (i, item) => nodes.RemoveAt(i), () => nodes.Clear()); } } + + internal class ApplicationHostLogical : TreeNodeCollection + { + readonly Controls.Application _application; + CompositeDisposable _subscriptions = new CompositeDisposable(2); + public ApplicationHostLogical(TreeNode owner, Controls.Application host) : + base(owner) + { + _application = host; + } + + protected override void Initialize(AvaloniaList nodes) + { + if (_application.ApplicationLifetime is Lifetimes.ISingleViewApplicationLifetime single) + { + nodes.Add(new LogicalTreeNode(single.MainView, Owner)); + } + if (_application.ApplicationLifetime is Lifetimes.IClassicDesktopStyleApplicationLifetime classic) + { + + for (int i = 0; i < classic.Windows.Count; i++) + { + var window = classic.Windows[i]; + if (window is Views.MainWindow) + { + continue; + } + nodes.Add(new LogicalTreeNode(window, Owner)); + } + _subscriptions = new System.Reactive.Disposables.CompositeDisposable() + { + Window.WindowOpenedEvent.AddClassHandler(typeof(Window), (s,e)=> + { + if (s is Views.MainWindow) + { + return; + } + nodes.Add(new LogicalTreeNode((IAvaloniaObject)s!,Owner)); + }), + Window.WindowClosedEvent.AddClassHandler(typeof(Window), (s,e)=> + { + if (s is Views.MainWindow) + { + return; + } + var item = nodes.FirstOrDefault(node=>object.ReferenceEquals(node.Visual,s)); + if(!(item is null)) + { + nodes.Remove(item); + } + if(nodes.Count == 0) + { + if (Avalonia.Application.Current?.ApplicationLifetime is Lifetimes.IControlledApplicationLifetime controller) + { + controller.Shutdown(); + } + else + { + Environment.Exit(0); + } + } + }), + }; + } + } + + public override void Dispose() + { + _subscriptions?.Dispose(); + base.Dispose(); + } + } } } diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs index 74f2f05948..d809768a47 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs @@ -4,12 +4,14 @@ using Avalonia.Controls; using Avalonia.Diagnostics.Models; using Avalonia.Input; using Avalonia.Threading; +using System.Reactive.Linq; +using System.Linq; namespace Avalonia.Diagnostics.ViewModels { internal class MainViewModel : ViewModelBase, IDisposable { - private readonly TopLevel _root; + private readonly AvaloniaObject _root; private readonly TreePageViewModel _logicalTree; private readonly TreePageViewModel _visualTree; private readonly EventsPageViewModel _events; @@ -17,13 +19,14 @@ namespace Avalonia.Diagnostics.ViewModels private ViewModelBase? _content; private int _selectedTab; private string? _focusedControl; - private string? _pointerOverElement; + private IInputElement? _pointerOverElement; private bool _shouldVisualizeMarginPadding = true; private bool _shouldVisualizeDirtyRects; private bool _showFpsOverlay; private bool _freezePopups; - - public MainViewModel(TopLevel root) + private string? _pointerOverElementName; + private IInputRoot? _pointerOverRoot; + public MainViewModel(AvaloniaObject root) { _root = root; _logicalTree = new TreePageViewModel(this, LogicalTreeNode.Create(root)); @@ -35,8 +38,24 @@ namespace Avalonia.Diagnostics.ViewModels if (KeyboardDevice.Instance is not null) KeyboardDevice.Instance.PropertyChanged += KeyboardPropertyChanged; SelectedTab = 0; - _pointerOverSubscription = root.GetObservable(TopLevel.PointerOverElementProperty) - .Subscribe(x => PointerOverElement = x?.GetType().Name); + if (root is TopLevel topLevel) + { + _pointerOverSubscription = topLevel.GetObservable(TopLevel.PointerOverElementProperty) + .Subscribe(x => PointerOverElement = x); + + } + else + { +#nullable disable + _pointerOverSubscription = InputManager.Instance.PreProcess + .OfType() + .Subscribe(e => + { + PointerOverRoot = e.Root; + PointerOverElement = e.Root.GetInputElementsAt(e.Position).FirstOrDefault(); + }); +#nullable restore + } Console = new ConsoleViewModel(UpdateConsoleContext); } @@ -51,13 +70,13 @@ namespace Avalonia.Diagnostics.ViewModels get => _shouldVisualizeMarginPadding; set => RaiseAndSetIfChanged(ref _shouldVisualizeMarginPadding, value); } - + public bool ShouldVisualizeDirtyRects { get => _shouldVisualizeDirtyRects; set { - _root.Renderer.DrawDirtyRects = value; + ((TopLevel)_root).Renderer.DrawDirtyRects = value; RaiseAndSetIfChanged(ref _shouldVisualizeDirtyRects, value); } } @@ -77,7 +96,7 @@ namespace Avalonia.Diagnostics.ViewModels get => _showFpsOverlay; set { - _root.Renderer.DrawFps = value; + ((TopLevel)_root).Renderer.DrawFps = value; RaiseAndSetIfChanged(ref _showFpsOverlay, value); } } @@ -150,12 +169,28 @@ namespace Avalonia.Diagnostics.ViewModels private set { RaiseAndSetIfChanged(ref _focusedControl, value); } } - public string? PointerOverElement + public IInputRoot? PointerOverRoot + { + get => _pointerOverRoot; + private set => RaiseAndSetIfChanged( ref _pointerOverRoot , value); + } + + public IInputElement? PointerOverElement { get { return _pointerOverElement; } - private set { RaiseAndSetIfChanged(ref _pointerOverElement, value); } + private set + { + RaiseAndSetIfChanged(ref _pointerOverElement, value); + PointerOverElementName = value?.GetType()?.Name; + } } - + + public string? PointerOverElementName + { + get => _pointerOverElementName; + private set => RaiseAndSetIfChanged(ref _pointerOverElementName, value); + } + private void UpdateConsoleContext(ConsoleContext context) { context.root = _root; @@ -188,8 +223,11 @@ namespace Avalonia.Diagnostics.ViewModels _pointerOverSubscription.Dispose(); _logicalTree.Dispose(); _visualTree.Dispose(); - _root.Renderer.DrawDirtyRects = false; - _root.Renderer.DrawFps = false; + if (_root is TopLevel top) + { + top.Renderer.DrawDirtyRects = false; + top.Renderer.DrawFps = false; + } } private void UpdateFocusedControl() diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs index 94707ac189..5459810db9 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs @@ -16,12 +16,13 @@ namespace Avalonia.Diagnostics.ViewModels private string _classes; private bool _isExpanded; - protected TreeNode(IVisual visual, TreeNode? parent, string? customName = null) + protected TreeNode(IAvaloniaObject avaloniaObject, TreeNode? parent, string? customName = null) { _classes = string.Empty; Parent = parent; - Type = customName ?? visual.GetType().Name; - Visual = visual; + var visual = avaloniaObject ; + Type = customName ?? avaloniaObject.GetType().Name; + Visual = visual!; FontWeight = IsRoot ? FontWeight.Bold : FontWeight.Normal; if (visual is IControl control) @@ -76,7 +77,7 @@ namespace Avalonia.Diagnostics.ViewModels get; } - public IVisual Visual + public IAvaloniaObject Visual { get; } diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNodeCollection.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNodeCollection.cs index c007411f49..953afa280b 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNodeCollection.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNodeCollection.cs @@ -10,6 +10,20 @@ namespace Avalonia.Diagnostics.ViewModels { internal abstract class TreeNodeCollection : IAvaloniaReadOnlyList, IDisposable { + private class EmptyTreeNodeCollection : TreeNodeCollection + { + public EmptyTreeNodeCollection():base(default!) + { + + } + protected override void Initialize(AvaloniaList nodes) + { + + } + } + + static readonly internal TreeNodeCollection Empty = new EmptyTreeNodeCollection(); + private AvaloniaList? _inner; public TreeNodeCollection(TreeNode owner) => Owner = owner; diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs index 6a430897ba..5e1128d4b7 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs @@ -7,15 +7,22 @@ using Avalonia.Controls.Diagnostics; using Avalonia.Controls.Primitives; using Avalonia.Styling; using Avalonia.VisualTree; +using Lifetimes = Avalonia.Controls.ApplicationLifetimes; +using System.Linq; namespace Avalonia.Diagnostics.ViewModels { internal class VisualTreeNode : TreeNode { - public VisualTreeNode(IVisual visual, TreeNode? parent, string? customName = null) - : base(visual, parent, customName) + public VisualTreeNode(IAvaloniaObject avaloniaObject, TreeNode? parent, string? customName = null) + : base(avaloniaObject, parent, customName) { - Children = new VisualTreeNodeCollection(this, visual); + Children = avaloniaObject switch + { + IVisual visual => new VisualTreeNodeCollection(this, visual), + Controls.Application host => new ApplicationHostVisuals(this, host), + _ => TreeNodeCollection.Empty + }; if (Visual is IStyleable styleable) IsInTemplate = styleable.TemplatedParent != null; @@ -27,7 +34,7 @@ namespace Avalonia.Diagnostics.ViewModels public static VisualTreeNode[] Create(object control) { - return control is IVisual visual ? + return control is IAvaloniaObject visual ? new[] { new VisualTreeNode(visual, null) } : Array.Empty(); } @@ -130,7 +137,7 @@ namespace Avalonia.Diagnostics.ViewModels _subscriptions.Add( _control.VisualChildren.ForEachItem( - (i, item) => nodes.Insert(i, new VisualTreeNode(item, Owner)), + (i, item) => nodes.Insert(i, new VisualTreeNode((IAvaloniaObject)item, Owner)), (i, item) => nodes.RemoveAt(i), () => nodes.Clear())); } @@ -147,5 +154,68 @@ namespace Avalonia.Diagnostics.ViewModels public string? CustomName { get; } } } + + internal class ApplicationHostVisuals : TreeNodeCollection + { + readonly Controls.Application _application; + CompositeDisposable _subscriptions = new CompositeDisposable(2); + public ApplicationHostVisuals(TreeNode owner, Controls.Application host) : + base(owner) + { + _application = host; + } + + protected override void Initialize(AvaloniaList nodes) + { + if (_application.ApplicationLifetime is Lifetimes.ISingleViewApplicationLifetime single) + { + nodes.Add(new VisualTreeNode(single.MainView, Owner)); + } + if (_application.ApplicationLifetime is Lifetimes.IClassicDesktopStyleApplicationLifetime classic) + { + + for (int i = 0; i < classic.Windows.Count; i++) + { + var window = classic.Windows[i]; + if (window is Views.MainWindow) + { + continue; + } + nodes.Add(new VisualTreeNode(window, Owner)); + } + _subscriptions = new System.Reactive.Disposables.CompositeDisposable() + { + Window.WindowOpenedEvent.AddClassHandler(typeof(Window), (s,e)=> + { + if (s is Views.MainWindow) + { + return; + } + nodes.Add(new VisualTreeNode((IAvaloniaObject)s!,Owner)); + }), + Window.WindowClosedEvent.AddClassHandler(typeof(Window), (s,e)=> + { + if (s is Views.MainWindow) + { + return; + } + var item = nodes.FirstOrDefault(node=>object.ReferenceEquals(node.Visual,s)); + if(!(item is null)) + { + nodes.Remove(item); + } + }), + }; + + + } + } + + public override void Dispose() + { + _subscriptions?.Dispose(); + base.Dispose(); + } + } } } diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml b/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml index cf74fa1b34..7f924af144 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml @@ -4,6 +4,7 @@ xmlns:local="clr-namespace:Avalonia.Diagnostics.Views" xmlns:controls="clr-namespace:Avalonia.Diagnostics.Controls" xmlns:vm="clr-namespace:Avalonia.Diagnostics.ViewModels" + xmlns:lb="using:Avalonia.Diagnostics.Behaviors" x:Class="Avalonia.Diagnostics.Views.ControlDetailsView" x:Name="Main"> @@ -11,8 +12,20 @@ - - + + + + + + + + @@ -53,9 +66,10 @@ - + - + diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml b/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml index 6f2ac96a66..7e0dab1d6e 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml @@ -73,7 +73,7 @@ Pointer Over: - + _frozenPopupStates; - private TopLevel? _root; + private AvaloniaObject? _root; public MainWindow() { @@ -50,23 +50,23 @@ namespace Avalonia.Diagnostics.Views this.Opened += lh; } - public TopLevel? Root + public AvaloniaObject? Root { get => _root; set { if (_root != value) { - if (_root != null) + if (_root is ICloseable oldClosable) { - _root.Closed -= RootClosed; + oldClosable.Closed -= RootClosed; } _root = value; - if (_root != null) + if (_root is ICloseable newClosable) { - _root.Closed += RootClosed; + newClosable.Closed += RootClosed; DataContext = new MainViewModel(_root); } else @@ -91,9 +91,9 @@ namespace Avalonia.Diagnostics.Views _frozenPopupStates.Clear(); - if (_root != null) + if (_root is ICloseable cloneable) { - _root.Closed -= RootClosed; + cloneable.Closed -= RootClosed; _root = null; } @@ -123,7 +123,7 @@ namespace Avalonia.Diagnostics.Views .FirstOrDefault(); } - private static List GetPopupRoots(IVisual root) + private static List GetPopupRoots(TopLevel root) { var popupRoots = new List(); @@ -160,7 +160,8 @@ namespace Avalonia.Diagnostics.Views return; } - var root = Root; + var root = Root as TopLevel + ?? vm.PointerOverRoot as TopLevel; if (root is null) { return; diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs index 3543b1adea..649b65c521 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs @@ -47,7 +47,12 @@ namespace Avalonia.Diagnostics.Views return; } - var visual = (Visual)node.Visual; + var visual = node.Visual as Visual; + + if (visual is null) + { + return; + } _currentLayer = AdornerLayer.GetAdornerLayer(visual); From 1d34536236ee84dace3f8414ff3a73b71cbac654 Mon Sep 17 00:00:00 2001 From: Sergey Mikolaitis Date: Sun, 26 Dec 2021 22:47:31 +0300 Subject: [PATCH 90/90] [WASM] Fix wrong DPI value --- src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs b/src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs index 3e9849d4dc..acd23a98b0 100644 --- a/src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs +++ b/src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs @@ -142,7 +142,7 @@ namespace Avalonia.Web.Blazor public Size ClientSize => _clientSize; public Size? FrameSize => null; - public double RenderScaling => 1; + public double RenderScaling => _currentSurface?.Scaling ?? 1; public IEnumerable Surfaces => new object[] { _currentSurface! };