diff --git a/Avalonia.sln.DotSettings b/Avalonia.sln.DotSettings index ab21d6e50b..1fd6f8d092 100644 --- a/Avalonia.sln.DotSettings +++ b/Avalonia.sln.DotSettings @@ -1,4 +1,5 @@  + ExplicitlyExcluded ExplicitlyExcluded ExplicitlyExcluded HINT diff --git a/appveyor.yml b/appveyor.yml index 7f2b1bb395..a52b2dfa70 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -33,6 +33,7 @@ test: off artifacts: - path: artifacts\nuget\*.nupkg - path: artifacts\zip\*.zip + - path: artifacts\inspectcode.xml cache: - gtk-sharp-2.12.26.msi - dotnet-1.0.1.exe diff --git a/build.cake b/build.cake index b091dba1a2..021e1e0cc3 100644 --- a/build.cake +++ b/build.cake @@ -105,7 +105,7 @@ Task("Restore-NuGet-Packages") .Does(() => { var maxRetryCount = 5; - var toolTimeout = 1d; + var toolTimeout = 2d; Policy .Handle() .Retry(maxRetryCount, (exception, retryCount, context) => { @@ -287,6 +287,7 @@ Task("Zip-Files") Task("Create-NuGet-Packages") .IsDependentOn("Run-Unit-Tests") + .IsDependentOn("Inspect") .Does(() => { foreach(var nuspec in packages.NuspecNuGetSettings) @@ -334,7 +335,6 @@ Task("Publish-NuGet") .WithCriteria(() => !parameters.IsLocalBuild) .WithCriteria(() => !parameters.IsPullRequest) .WithCriteria(() => parameters.IsMainRepo) - .WithCriteria(() => parameters.IsMasterBranch) .WithCriteria(() => parameters.IsNuGetRelease) .Does(() => { @@ -363,6 +363,39 @@ Task("Publish-NuGet") Information("Publish-NuGet Task failed, but continuing with next Task..."); }); +Task("Inspect") + .WithCriteria(parameters.IsRunningOnWindows) + .IsDependentOn("Restore-NuGet-Packages") + .Does(() => + { + var badIssues = new []{"PossibleNullReferenceException"}; + var whitelist = new []{"tests", "src\\android", "src\\ios", + "src\\windows\\avalonia.designer", "src\\avalonia.htmlrenderer\\external"}; + Information("Running code inspections"); + + + StartProcess("tools\\JetBrains.ReSharper.CommandLineTools\\tools\\inspectcode.exe", + new ProcessSettings{ Arguments = "--output=artifacts\\inspectcode.xml --profile=Avalonia.sln.DotSettings Avalonia.sln" }); + Information("Analyzing report"); + var doc = XDocument.Parse(System.IO.File.ReadAllText("artifacts\\inspectcode.xml")); + var failBuild = false; + foreach(var xml in doc.Descendants("Issue")) + { + var typeId = xml.Attribute("TypeId").Value.ToString(); + if(badIssues.Contains(typeId)) + { + var file = xml.Attribute("File").Value.ToString().ToLower(); + if(whitelist.Any(wh => file.StartsWith(wh))) + continue; + var line = xml.Attribute("Line").Value.ToString(); + Error(typeId + " - " + file + " on line " + line); + failBuild = true; + } + } + if(failBuild) + throw new Exception("Issues found"); + }); + /////////////////////////////////////////////////////////////////////////////// // TARGETS /////////////////////////////////////////////////////////////////////////////// diff --git a/parameters.cake b/parameters.cake index 4a7270011c..12f448303e 100644 --- a/parameters.cake +++ b/parameters.cake @@ -75,17 +75,25 @@ public class Parameters IsReleasable = StringComparer.OrdinalIgnoreCase.Equals(ReleasePlatform, Platform) && StringComparer.OrdinalIgnoreCase.Equals(ReleaseConfiguration, Configuration); IsMyGetRelease = !IsTagged && IsReleasable; - IsNuGetRelease = IsTagged && IsReleasable; + // VERSION Version = context.Argument("force-nuget-version", context.ParseAssemblyInfo(AssemblyInfoPath).AssemblyVersion); if (IsRunningOnAppVeyor) { + string tagVersion = null; if (IsTagged) { - // Use Tag Name as version - Version = buildSystem.AppVeyor.Environment.Repository.Tag.Name; + var tag = buildSystem.AppVeyor.Environment.Repository.Tag.Name; + var nugetReleasePrefix = "nuget-release-"; + IsNuGetRelease = IsTagged && IsReleasable && tag.StartsWith(nugetReleasePrefix); + if(IsNuGetRelease) + tagVersion = tag.Substring(nugetReleasePrefix.Length); + } + if(tagVersion != null) + { + Version = tagVersion; } else { diff --git a/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj b/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj index 627b879c23..55384d787c 100644 --- a/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj +++ b/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj @@ -22,6 +22,7 @@ + diff --git a/samples/interop/Direct3DInteropSample/MainWindow.cs b/samples/interop/Direct3DInteropSample/MainWindow.cs index 1ff1e1938b..ad40e81895 100644 --- a/samples/interop/Direct3DInteropSample/MainWindow.cs +++ b/samples/interop/Direct3DInteropSample/MainWindow.cs @@ -58,7 +58,7 @@ namespace Direct3DInteropSample new ModeDescription((int)ClientSize.Width, (int)ClientSize.Height, new Rational(60, 1), Format.R8G8B8A8_UNorm), IsWindowed = true, - OutputHandle = PlatformImpl.Handle.Handle, + OutputHandle = PlatformImpl?.Handle.Handle ?? IntPtr.Zero, SampleDescription = new SampleDescription(1, 0), SwapEffect = SwapEffect.Discard, Usage = Usage.RenderTargetOutput diff --git a/samples/interop/GtkInteropDemo/GtkInteropDemo.csproj b/samples/interop/GtkInteropDemo/GtkInteropDemo.csproj index 13dfd0055f..8cbaf7a627 100644 --- a/samples/interop/GtkInteropDemo/GtkInteropDemo.csproj +++ b/samples/interop/GtkInteropDemo/GtkInteropDemo.csproj @@ -149,5 +149,6 @@ ControlCatalog + \ No newline at end of file diff --git a/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj b/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj index 028892ea94..3891c2bc5c 100644 --- a/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj +++ b/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj @@ -179,5 +179,6 @@ MSBuild:Compile + \ No newline at end of file diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index 1492c14c5f..1bdf1eb5e3 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -622,14 +622,9 @@ namespace Avalonia /// The default value. private object GetDefaultValue(AvaloniaProperty property) { - if (property.Inherits && _inheritanceParent != null) - { - return (_inheritanceParent as AvaloniaObject).GetValueInternal(property); - } - else - { - return ((IStyledPropertyAccessor)property).GetDefaultValue(GetType()); - } + if (property.Inherits && _inheritanceParent is AvaloniaObject aobj) + return aobj.GetValueInternal(property); + return ((IStyledPropertyAccessor) property).GetDefaultValue(GetType()); } /// diff --git a/src/Avalonia.Base/Collections/AvaloniaDictionary.cs b/src/Avalonia.Base/Collections/AvaloniaDictionary.cs index d63a5baf16..1aa239180c 100644 --- a/src/Avalonia.Base/Collections/AvaloniaDictionary.cs +++ b/src/Avalonia.Base/Collections/AvaloniaDictionary.cs @@ -103,11 +103,9 @@ namespace Avalonia.Collections _inner = new Dictionary(); - if (PropertyChanged != null) - { - PropertyChanged(this, new PropertyChangedEventArgs("Count")); - PropertyChanged(this, new PropertyChangedEventArgs($"Item[]")); - } + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count")); + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[]")); + if (CollectionChanged != null) { @@ -144,12 +142,9 @@ namespace Avalonia.Collections if (_inner.TryGetValue(key, out value)) { - if (PropertyChanged != null) - { - PropertyChanged(this, new PropertyChangedEventArgs("Count")); - PropertyChanged(this, new PropertyChangedEventArgs($"Item[{key}]")); - } - + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count")); + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]")); + if (CollectionChanged != null) { var e = new NotifyCollectionChangedEventArgs( @@ -199,11 +194,9 @@ namespace Avalonia.Collections private void NotifyAdd(TKey key, TValue value) { - if (PropertyChanged != null) - { - PropertyChanged(this, new PropertyChangedEventArgs("Count")); - PropertyChanged(this, new PropertyChangedEventArgs($"Item[{key}]")); - } + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count")); + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]")); + if (CollectionChanged != null) { diff --git a/src/Avalonia.Base/Collections/AvaloniaListExtensions.cs b/src/Avalonia.Base/Collections/AvaloniaListExtensions.cs index ffb7199619..54cd132b95 100644 --- a/src/Avalonia.Base/Collections/AvaloniaListExtensions.cs +++ b/src/Avalonia.Base/Collections/AvaloniaListExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; @@ -59,7 +60,8 @@ namespace Avalonia.Collections /// the index in the collection and the item. /// /// - /// An action called when the collection is reset. + /// An action called when the collection is reset. This will be followed by calls to + /// for each item present in the collection after the reset. /// /// A disposable used to terminate the subscription. public static IDisposable ForEachItem( @@ -68,112 +70,38 @@ namespace Avalonia.Collections Action removed, Action reset) { - int index; - - NotifyCollectionChangedEventHandler handler = (_, e) => + void Add(int index, IList items) { - switch (e.Action) + foreach (T item in items) { - case NotifyCollectionChangedAction.Add: - index = e.NewStartingIndex; - - foreach (T item in e.NewItems) - { - added(index++, item); - } - - break; - - case NotifyCollectionChangedAction.Replace: - index = e.OldStartingIndex; - - foreach (T item in e.OldItems) - { - removed(index++, item); - } - - index = e.NewStartingIndex; - - foreach (T item in e.NewItems) - { - added(index++, item); - } - - break; - - case NotifyCollectionChangedAction.Remove: - index = e.OldStartingIndex; - - foreach (T item in e.OldItems) - { - removed(index++, item); - } - - break; - - case NotifyCollectionChangedAction.Reset: - if (reset == null) - { - throw new InvalidOperationException( - "Reset called on collection without reset handler."); - } - - reset(); - break; + added(index++, item); } - }; + } - index = 0; - foreach (T i in collection) + void Remove(int index, IList items) { - added(index++, i); + for (var i = items.Count - 1; i >= 0; --i) + { + removed(index + i, (T)items[i]); + } } - collection.CollectionChanged += handler; - - return Disposable.Create(() => collection.CollectionChanged -= handler); - } - - /// - /// Invokes an action for each item in a collection and subsequently each item added or - /// removed from the collection. - /// - /// The type of the collection items. - /// The collection. - /// - /// An action called initially with all items in the collection and subsequently with a - /// list of items added to the collection. The parameters passed are the index of the - /// first item added to the collection and the items added. - /// - /// - /// An action called with all items removed from the collection. The parameters passed - /// are the index of the first item removed from the collection and the items removed. - /// - /// - /// An action called when the collection is reset. - /// - /// A disposable used to terminate the subscription. - public static IDisposable ForEachItem( - this IAvaloniaReadOnlyList collection, - Action> added, - Action> removed, - Action reset) - { NotifyCollectionChangedEventHandler handler = (_, e) => { switch (e.Action) { case NotifyCollectionChangedAction.Add: - added(e.NewStartingIndex, e.NewItems.Cast()); + Add(e.NewStartingIndex, e.NewItems); break; + case NotifyCollectionChangedAction.Move: case NotifyCollectionChangedAction.Replace: - removed(e.OldStartingIndex, e.OldItems.Cast()); - added(e.NewStartingIndex, e.NewItems.Cast()); + Remove(e.OldStartingIndex, e.OldItems); + Add(e.NewStartingIndex, e.NewItems); break; case NotifyCollectionChangedAction.Remove: - removed(e.OldStartingIndex, e.OldItems.Cast()); + Remove(e.OldStartingIndex, e.OldItems); break; case NotifyCollectionChangedAction.Reset: @@ -184,16 +112,31 @@ namespace Avalonia.Collections } reset(); + Add(0, (IList)collection); break; } }; - added(0, collection); + Add(0, (IList)collection); collection.CollectionChanged += handler; return Disposable.Create(() => collection.CollectionChanged -= handler); } + public static IAvaloniaReadOnlyList CreateDerivedList( + this IAvaloniaReadOnlyList collection, + Func select) + { + var result = new AvaloniaList(); + + collection.ForEachItem( + (i, item) => result.Insert(i, select(item)), + (i, item) => result.RemoveAt(i), + () => result.Clear()); + + return result; + } + /// /// Listens for property changed events from all items in a collection. /// diff --git a/src/Avalonia.Base/PriorityValue.cs b/src/Avalonia.Base/PriorityValue.cs index 21467f3962..c33d50ff0e 100644 --- a/src/Avalonia.Base/PriorityValue.cs +++ b/src/Avalonia.Base/PriorityValue.cs @@ -285,7 +285,7 @@ namespace Avalonia Property.Name, _valueType, value, - value.GetType()); + value?.GetType()); } } } diff --git a/src/Avalonia.Controls/AppBuilderBase.cs b/src/Avalonia.Controls/AppBuilderBase.cs index 97de093a59..1610d33f9b 100644 --- a/src/Avalonia.Controls/AppBuilderBase.cs +++ b/src/Avalonia.Controls/AppBuilderBase.cs @@ -55,8 +55,7 @@ namespace Avalonia.Controls public Action AfterSetupCallback { get; private set; } = builder => { }; /// - /// Gets or sets a method to call before is called on the - /// . + /// Gets or sets a method to call before Startis called on the . /// public Action BeforeStartCallback { get; private set; } = builder => { }; @@ -94,8 +93,7 @@ namespace Avalonia.Controls protected TAppBuilder Self => (TAppBuilder) this; /// - /// Registers a callback to call before is called on the - /// . + /// Registers a callback to call before Start is called on the . /// /// The callback. /// An instance. @@ -129,6 +127,24 @@ namespace Avalonia.Controls Instance.Run(window); } + /// + /// Starts the application with the provided instance of . + /// + /// The window type. + /// Instance of type TMainWindow to use when starting the app + /// A delegate that will be called to create a data context for the window (optional). + public void Start(TMainWindow mainWindow, Func dataContextProvider = null) + where TMainWindow : Window + { + Setup(); + BeforeStartCallback(Self); + + if (dataContextProvider != null) + mainWindow.DataContext = dataContextProvider(); + mainWindow.Show(); + Instance.Run(mainWindow); + } + /// /// Sets up the platform-specific services for the application, but does not run it. /// diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs index 24daa545ba..7ed1c7fd8c 100644 --- a/src/Avalonia.Controls/Button.cs +++ b/src/Avalonia.Controls/Button.cs @@ -275,7 +275,7 @@ namespace Avalonia.Controls { var button = e.Sender as Button; var isDefault = (bool)e.NewValue; - var inputRoot = button.VisualRoot as IInputElement; + var inputRoot = button?.VisualRoot as IInputElement; if (inputRoot != null) { diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index 516da813ef..758f7bbf55 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -469,7 +469,7 @@ namespace Avalonia.Controls { if (!IsInitialized) { - foreach (var i in this.GetSelfAndVisualDescendents()) + foreach (var i in this.GetSelfAndVisualDescendants()) { var c = i as IControl; @@ -645,7 +645,7 @@ namespace Avalonia.Controls if (_focusAdorner != null) { - var adornerLayer = _focusAdorner.Parent as Panel; + var adornerLayer = (IPanel)_focusAdorner.Parent; adornerLayer.Children.Remove(_focusAdorner); _focusAdorner = null; } diff --git a/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs b/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs index 4688017187..b8d54fa67b 100644 --- a/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs +++ b/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs @@ -4,6 +4,7 @@ using Avalonia.Input; using Avalonia.Layout; using Avalonia.Platform; using Avalonia.Styling; +using JetBrains.Annotations; namespace Avalonia.Controls.Embedding { @@ -18,6 +19,7 @@ namespace Avalonia.Controls.Embedding { } + [CanBeNull] public new IEmbeddableWindowImpl PlatformImpl => (IEmbeddableWindowImpl) base.PlatformImpl; public void Prepare() @@ -39,8 +41,9 @@ namespace Avalonia.Controls.Embedding protected override Size MeasureOverride(Size availableSize) { - base.MeasureOverride(PlatformImpl.ClientSize); - return PlatformImpl.ClientSize; + var cs = PlatformImpl?.ClientSize ?? default(Size); + base.MeasureOverride(cs); + return cs; } private readonly NameScope _nameScope = new NameScope(); @@ -63,9 +66,6 @@ namespace Avalonia.Controls.Embedding public void Unregister(string name) => _nameScope.Unregister(name); Type IStyleable.StyleKey => typeof(EmbeddableControlRoot); - public void Dispose() - { - PlatformImpl.Dispose(); - } + public void Dispose() => PlatformImpl?.Dispose(); } } diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index 5d12c9963f..aa209e0462 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -354,7 +354,7 @@ namespace Avalonia.Controls } var collection = sender as ICollection; - PseudoClasses.Set(":empty", collection.Count == 0); + PseudoClasses.Set(":empty", collection == null || collection.Count == 0); } /// diff --git a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs index c217290226..e41c4e1e28 100644 --- a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs @@ -111,7 +111,7 @@ namespace Avalonia.Controls.Presenters /// The target visual. /// The portion of the target visual to bring into view. /// True if the scroll offset was changed; otherwise false. - public bool BringDescendentIntoView(IVisual target, Rect targetRect) + public bool BringDescendantIntoView(IVisual target, Rect targetRect) { if (Child == null) { @@ -262,7 +262,7 @@ namespace Avalonia.Controls.Presenters private void BringIntoViewRequested(object sender, RequestBringIntoViewEventArgs e) { - e.Handled = BringDescendentIntoView(e.TargetObject, e.TargetRect); + e.Handled = BringDescendantIntoView(e.TargetObject, e.TargetRect); } private void ChildChanged(AvaloniaPropertyChangedEventArgs e) diff --git a/src/Avalonia.Controls/Primitives/PopupRoot.cs b/src/Avalonia.Controls/Primitives/PopupRoot.cs index 86c1b47521..a999e4ae37 100644 --- a/src/Avalonia.Controls/Primitives/PopupRoot.cs +++ b/src/Avalonia.Controls/Primitives/PopupRoot.cs @@ -9,6 +9,7 @@ using Avalonia.Layout; using Avalonia.Media; using Avalonia.Platform; using Avalonia.VisualTree; +using JetBrains.Annotations; namespace Avalonia.Controls.Primitives { @@ -49,6 +50,7 @@ namespace Avalonia.Controls.Primitives /// /// Gets the platform-specific window implementation. /// + [CanBeNull] public new IPopupImpl PlatformImpl => (IPopupImpl)base.PlatformImpl; /// @@ -65,10 +67,7 @@ namespace Avalonia.Controls.Primitives IVisual IHostedVisualTreeRoot.Host => Parent; /// - public void Dispose() - { - this.PlatformImpl.Dispose(); - } + public void Dispose() => PlatformImpl?.Dispose(); /// protected override void OnTemplateApplied(TemplateAppliedEventArgs e) diff --git a/src/Avalonia.Controls/Templates/TemplateExtensions.cs b/src/Avalonia.Controls/Templates/TemplateExtensions.cs index 535be23b23..09da737836 100644 --- a/src/Avalonia.Controls/Templates/TemplateExtensions.cs +++ b/src/Avalonia.Controls/Templates/TemplateExtensions.cs @@ -31,9 +31,9 @@ namespace Avalonia.Controls.Templates if (child.TemplatedParent != null) { - foreach (var descendent in GetTemplateChildren(child, templatedParent)) + foreach (var descendant in GetTemplateChildren(child, templatedParent)) { - yield return descendent; + yield return descendant; } } } diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 9748e5e772..d2e8085d8c 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -720,7 +720,7 @@ namespace Avalonia.Controls if (pos < text.Length) { --pos; - if (pos > 0 && Text[pos - 1] == '\r' && Text[pos] == '\n') + if (pos > 0 && text[pos - 1] == '\r' && text[pos] == '\n') { --pos; } @@ -771,6 +771,9 @@ namespace Avalonia.Controls private string GetSelection() { + var text = Text; + if (string.IsNullOrEmpty(text)) + return ""; var selectionStart = SelectionStart; var selectionEnd = SelectionEnd; var start = Math.Min(selectionStart, selectionEnd); @@ -779,7 +782,7 @@ namespace Avalonia.Controls { return ""; } - return Text.Substring(start, end - start); + return text.Substring(start, end - start); } private int GetLine(int caretIndex, IList lines) diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index f3a6ab92d0..1f1a29afa3 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -14,6 +14,7 @@ using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Styling; using Avalonia.VisualTree; +using JetBrains.Annotations; namespace Avalonia.Controls { @@ -92,25 +93,25 @@ namespace Avalonia.Controls var rendererFactory = TryGetService(dependencyResolver); Renderer = rendererFactory?.CreateRenderer(this, renderLoop); - PlatformImpl.SetInputRoot(this); + impl.SetInputRoot(this); - PlatformImpl.Closed = HandleClosed; - PlatformImpl.Input = HandleInput; - PlatformImpl.Paint = HandlePaint; - PlatformImpl.Resized = HandleResized; - PlatformImpl.ScalingChanged = HandleScalingChanged; + impl.Closed = HandleClosed; + impl.Input = HandleInput; + impl.Paint = HandlePaint; + impl.Resized = HandleResized; + impl.ScalingChanged = HandleScalingChanged; _keyboardNavigationHandler?.SetOwner(this); _accessKeyHandler?.SetOwner(this); styler?.ApplyStyles(this); - ClientSize = PlatformImpl.ClientSize; + ClientSize = impl.ClientSize; this.GetObservable(PointerOverElementProperty) .Select( x => (x as InputElement)?.GetObservable(CursorProperty) ?? Observable.Empty()) - .Switch().Subscribe(cursor => PlatformImpl.SetCursor(cursor?.PlatformCursor)); + .Switch().Subscribe(cursor => PlatformImpl?.SetCursor(cursor?.PlatformCursor)); if (_applicationLifecycle != null) { @@ -135,10 +136,8 @@ namespace Avalonia.Controls /// /// Gets the platform-specific window implementation. /// - public ITopLevelImpl PlatformImpl - { - get; - } + [CanBeNull] + public ITopLevelImpl PlatformImpl { get; private set; } /// /// Gets the renderer for the window. @@ -177,7 +176,7 @@ namespace Avalonia.Controls Size ILayoutRoot.MaxClientSize => Size.Infinity; /// - double ILayoutRoot.LayoutScaling => PlatformImpl.Scaling; + double ILayoutRoot.LayoutScaling => PlatformImpl?.Scaling ?? 1; IStyleHost IStyleHost.StylingParent { @@ -189,25 +188,27 @@ namespace Avalonia.Controls /// protected virtual IRenderTarget CreateRenderTarget() { + if(PlatformImpl == null) + throw new InvalidOperationException("Cann't create render target, PlatformImpl is null (might be already disposed)"); return _renderInterface.CreateRenderTarget(PlatformImpl.Surfaces); } /// void IRenderRoot.Invalidate(Rect rect) { - PlatformImpl.Invalidate(rect); + PlatformImpl?.Invalidate(rect); } /// Point IRenderRoot.PointToClient(Point p) { - return PlatformImpl.PointToClient(p); + return PlatformImpl?.PointToClient(p) ?? default(Point); } /// Point IRenderRoot.PointToScreen(Point p) { - return PlatformImpl.PointToScreen(p); + return PlatformImpl?.PointToScreen(p) ?? default(Point); } /// @@ -224,6 +225,8 @@ namespace Avalonia.Controls /// protected virtual void HandleClosed() { + PlatformImpl = null; + Closed?.Invoke(this, EventArgs.Empty); Renderer?.Dispose(); Renderer = null; @@ -250,7 +253,7 @@ namespace Avalonia.Controls /// The window scaling. protected virtual void HandleScalingChanged(double scaling) { - foreach (ILayoutable control in this.GetSelfAndVisualDescendents()) + foreach (ILayoutable control in this.GetSelfAndVisualDescendants()) { control.InvalidateMeasure(); } diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 75587dcaec..3802f2b6ea 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -12,6 +12,7 @@ using Avalonia.Platform; using Avalonia.Styling; using System.Collections.Generic; using System.Linq; +using JetBrains.Annotations; namespace Avalonia.Controls { @@ -46,12 +47,12 @@ namespace Avalonia.Controls /// public class Window : WindowBase, IStyleable, IFocusScope, ILayoutRoot, INameScope { - private static IList s_windows = new List(); + private static List s_windows = new List(); /// /// Retrieves an enumeration of all Windows in the currently running application. /// - public static IList OpenWindows => s_windows; + public static IReadOnlyList OpenWindows => s_windows; /// /// Defines the property. @@ -87,11 +88,11 @@ namespace Avalonia.Controls static Window() { BackgroundProperty.OverrideDefaultValue(typeof(Window), Brushes.White); - TitleProperty.Changed.AddClassHandler((s, e) => s.PlatformImpl.SetTitle((string)e.NewValue)); + TitleProperty.Changed.AddClassHandler((s, e) => s.PlatformImpl?.SetTitle((string)e.NewValue)); HasSystemDecorationsProperty.Changed.AddClassHandler( - (s, e) => s.PlatformImpl.SetSystemDecorations((bool) e.NewValue)); + (s, e) => s.PlatformImpl?.SetSystemDecorations((bool) e.NewValue)); - IconProperty.Changed.AddClassHandler((s, e) => s.PlatformImpl.SetIcon(((WindowIcon)e.NewValue).PlatformImpl)); + IconProperty.Changed.AddClassHandler((s, e) => s.PlatformImpl?.SetIcon(((WindowIcon)e.NewValue).PlatformImpl)); } /// @@ -109,7 +110,7 @@ namespace Avalonia.Controls public Window(IWindowImpl impl) : base(impl) { - _maxPlatformClientSize = this.PlatformImpl.MaxClientSize; + _maxPlatformClientSize = PlatformImpl?.MaxClientSize ?? default(Size); } /// @@ -129,6 +130,7 @@ namespace Avalonia.Controls /// /// Gets the platform-specific window implementation. /// + [CanBeNull] public new IWindowImpl PlatformImpl => (IWindowImpl)base.PlatformImpl; /// @@ -164,8 +166,12 @@ namespace Avalonia.Controls /// public WindowState WindowState { - get { return this.PlatformImpl.WindowState; } - set { this.PlatformImpl.WindowState = value; } + get { return PlatformImpl?.WindowState ?? WindowState.Normal; } + set + { + if (PlatformImpl != null) + PlatformImpl.WindowState = value; + } } /// @@ -189,7 +195,7 @@ namespace Avalonia.Controls public void Close() { s_windows.Remove(this); - PlatformImpl.Dispose(); + PlatformImpl?.Dispose(); IsVisible = false; } @@ -221,7 +227,7 @@ namespace Avalonia.Controls { using (BeginAutoSizing()) { - PlatformImpl.Hide(); + PlatformImpl?.Hide(); } IsVisible = false; @@ -232,6 +238,11 @@ namespace Avalonia.Controls /// public override void Show() { + if (IsVisible) + { + return; + } + s_windows.Add(this); EnsureInitialized(); @@ -240,7 +251,7 @@ namespace Avalonia.Controls using (BeginAutoSizing()) { - PlatformImpl.Show(); + PlatformImpl?.Show(); } } @@ -266,6 +277,11 @@ namespace Avalonia.Controls /// public Task ShowDialog() { + if (IsVisible) + { + throw new InvalidOperationException("The window is already being shown."); + } + s_windows.Add(this); EnsureInitialized(); @@ -278,7 +294,7 @@ namespace Avalonia.Controls var activated = affectedWindows.Where(w => w.IsActive).FirstOrDefault(); SetIsEnabled(affectedWindows, false); - var modal = PlatformImpl.ShowDialog(); + var modal = PlatformImpl?.ShowDialog(); var result = new TaskCompletionSource(); Observable.FromEventPattern( @@ -287,7 +303,7 @@ namespace Avalonia.Controls .Take(1) .Subscribe(_ => { - modal.Dispose(); + modal?.Dispose(); SetIsEnabled(affectedWindows, true); activated?.Activate(); result.SetResult((TResult)_dialogResult); @@ -354,6 +370,7 @@ namespace Avalonia.Controls protected override void HandleClosed() { IsVisible = false; + s_windows.Remove(this); base.HandleClosed(); } diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs index 21c248db0c..1f484fd6cb 100644 --- a/src/Avalonia.Controls/WindowBase.cs +++ b/src/Avalonia.Controls/WindowBase.cs @@ -9,6 +9,7 @@ using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.Layout; using Avalonia.Platform; +using JetBrains.Annotations; namespace Avalonia.Controls { @@ -43,10 +44,10 @@ namespace Avalonia.Controls public WindowBase(IWindowBaseImpl impl, IAvaloniaDependencyResolver dependencyResolver) : base(impl, dependencyResolver) { - PlatformImpl.Activated = HandleActivated; - PlatformImpl.Deactivated = HandleDeactivated; - PlatformImpl.PositionChanged = HandlePositionChanged; - this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl.Resize(x)); + impl.Activated = HandleActivated; + impl.Deactivated = HandleDeactivated; + impl.PositionChanged = HandlePositionChanged; + this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl?.Resize(x)); } /// @@ -64,6 +65,7 @@ namespace Avalonia.Controls /// public event EventHandler PositionChanged; + [CanBeNull] public new IWindowBaseImpl PlatformImpl => (IWindowBaseImpl) base.PlatformImpl; /// @@ -80,8 +82,12 @@ namespace Avalonia.Controls /// public Point Position { - get { return PlatformImpl.Position; } - set { PlatformImpl.Position = value; } + get { return PlatformImpl?.Position ?? default(Point); } + set + { + if (PlatformImpl is IWindowBaseImpl impl) + impl.Position = value; + } } /// @@ -98,7 +104,7 @@ namespace Avalonia.Controls /// public void Activate() { - PlatformImpl.Activate(); + PlatformImpl?.Activate(); } /// @@ -110,7 +116,7 @@ namespace Avalonia.Controls try { - PlatformImpl.Hide(); + PlatformImpl?.Hide(); IsVisible = false; } finally @@ -131,7 +137,7 @@ namespace Avalonia.Controls EnsureInitialized(); IsVisible = true; LayoutManager.Instance.ExecuteInitialLayoutPass(this); - PlatformImpl.Show(); + PlatformImpl?.Show(); } finally { @@ -163,10 +169,10 @@ namespace Avalonia.Controls { using (BeginAutoSizing()) { - PlatformImpl.Resize(finalSize); + PlatformImpl?.Resize(finalSize); } - return base.ArrangeOverride(PlatformImpl.ClientSize); + return base.ArrangeOverride(PlatformImpl?.ClientSize ?? default(Size)); } /// @@ -174,7 +180,7 @@ namespace Avalonia.Controls /// protected void EnsureInitialized() { - if (!this.IsInitialized) + if (!IsInitialized) { var init = (ISupportInitialize)this; init.BeginInit(); @@ -268,12 +274,12 @@ namespace Avalonia.Controls /// /// Starts moving a window with left button being held. Should be called from left mouse button press event handler /// - public void BeginMoveDrag() => PlatformImpl.BeginMoveDrag(); + public void BeginMoveDrag() => PlatformImpl?.BeginMoveDrag(); /// /// Starts resizing a window. This function is used if an application has window resizing controls. /// Should be called from left mouse button press event handler /// - public void BeginResizeDrag(WindowEdge edge) => PlatformImpl.BeginResizeDrag(edge); + public void BeginResizeDrag(WindowEdge edge) => PlatformImpl?.BeginResizeDrag(edge); } } diff --git a/src/Avalonia.DesignerSupport/DesignerAssist.cs b/src/Avalonia.DesignerSupport/DesignerAssist.cs index c9ae89354c..8d30f3cf25 100644 --- a/src/Avalonia.DesignerSupport/DesignerAssist.cs +++ b/src/Avalonia.DesignerSupport/DesignerAssist.cs @@ -75,7 +75,7 @@ namespace Avalonia.DesignerSupport private static void SetScalingFactor(double factor) { PlatformManager.SetDesignerScalingFactor(factor); - s_currentWindow?.PlatformImpl.Resize(s_currentWindow.ClientSize); + s_currentWindow?.PlatformImpl?.Resize(s_currentWindow.ClientSize); } static Window s_currentWindow; @@ -149,6 +149,8 @@ namespace Avalonia.DesignerSupport s_currentWindow = window; window.Show(); Design.ApplyDesignerProperties(window, control); + // ReSharper disable once PossibleNullReferenceException + // Always not null at this point Api.OnWindowCreated?.Invoke(window.PlatformImpl.Handle.Handle); Api.OnResize?.Invoke(); } diff --git a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj index a060827f27..e4752f6662 100644 --- a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj +++ b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj @@ -34,7 +34,6 @@ - diff --git a/src/Avalonia.Diagnostics/DevTools.xaml.cs b/src/Avalonia.Diagnostics/DevTools.xaml.cs index 5be252c5c3..10337d7386 100644 --- a/src/Avalonia.Diagnostics/DevTools.xaml.cs +++ b/src/Avalonia.Diagnostics/DevTools.xaml.cs @@ -11,7 +11,6 @@ using Avalonia.Input.Raw; using Avalonia.Interactivity; using Avalonia.Markup.Xaml; using Avalonia.VisualTree; -using ReactiveUI; namespace Avalonia { @@ -74,7 +73,7 @@ namespace Avalonia.Diagnostics Content = devTools, DataTemplates = new DataTemplates { - new ViewLocator(), + new ViewLocator(), } }; diff --git a/src/Avalonia.Diagnostics/ViewModels/ControlDetailsViewModel.cs b/src/Avalonia.Diagnostics/ViewModels/ControlDetailsViewModel.cs index 0339d724f7..d723890196 100644 --- a/src/Avalonia.Diagnostics/ViewModels/ControlDetailsViewModel.cs +++ b/src/Avalonia.Diagnostics/ViewModels/ControlDetailsViewModel.cs @@ -4,11 +4,10 @@ using System.Collections.Generic; using System.Linq; using Avalonia.VisualTree; -using ReactiveUI; namespace Avalonia.Diagnostics.ViewModels { - internal class ControlDetailsViewModel : ReactiveObject + internal class ControlDetailsViewModel : ViewModelBase { public ControlDetailsViewModel(IVisual control) { diff --git a/src/Avalonia.Diagnostics/ViewModels/DevToolsViewModel.cs b/src/Avalonia.Diagnostics/ViewModels/DevToolsViewModel.cs index dbee9ad624..2d3f978462 100644 --- a/src/Avalonia.Diagnostics/ViewModels/DevToolsViewModel.cs +++ b/src/Avalonia.Diagnostics/ViewModels/DevToolsViewModel.cs @@ -5,32 +5,50 @@ using System; using System.Reactive.Linq; using Avalonia.Controls; using Avalonia.Input; -using ReactiveUI; namespace Avalonia.Diagnostics.ViewModels { - internal class DevToolsViewModel : ReactiveObject + internal class DevToolsViewModel : ViewModelBase { - private ReactiveObject _content; - + private ViewModelBase _content; private int _selectedTab; - private TreePageViewModel _logicalTree; - private TreePageViewModel _visualTree; - - private readonly ObservableAsPropertyHelper _focusedControl; - - private readonly ObservableAsPropertyHelper _pointerOverElement; + private string _focusedControl; + private string _pointerOverElement; public DevToolsViewModel(IControl root) { _logicalTree = new TreePageViewModel(LogicalTreeNode.Create(root)); _visualTree = new TreePageViewModel(VisualTreeNode.Create(root)); - this.WhenAnyValue(x => x.SelectedTab).Subscribe(index => + UpdateFocusedControl(); + KeyboardDevice.Instance.PropertyChanged += (s, e) => { - switch (index) + if (e.PropertyName == nameof(KeyboardDevice.Instance.FocusedElement)) + { + UpdateFocusedControl(); + } + }; + + root.GetObservable(TopLevel.PointerOverElementProperty) + .Subscribe(x => PointerOverElement = x?.GetType().Name); + } + + public ViewModelBase Content + { + get { return _content; } + private set { RaiseAndSetIfChanged(ref _content, value); } + } + + public int SelectedTab + { + get { return _selectedTab; } + set + { + _selectedTab = value; + + switch (value) { case 0: Content = _logicalTree; @@ -39,34 +57,23 @@ namespace Avalonia.Diagnostics.ViewModels Content = _visualTree; break; } - }); - _focusedControl = KeyboardDevice.Instance - .WhenAnyValue(x => x.FocusedElement) - .Select(x => x?.GetType().Name) - .ToProperty(this, x => x.FocusedControl); - - _pointerOverElement = root.GetObservable(TopLevel.PointerOverElementProperty) - .Select(x => x?.GetType().Name) - .ToProperty(this, x => x.PointerOverElement); + RaisePropertyChanged(); + } } - public ReactiveObject Content + public string FocusedControl { - get { return _content; } - private set { this.RaiseAndSetIfChanged(ref _content, value); } + get { return _focusedControl; } + private set { RaiseAndSetIfChanged(ref _focusedControl, value); } } - public int SelectedTab + public string PointerOverElement { - get { return _selectedTab; } - set { this.RaiseAndSetIfChanged(ref _selectedTab, value); } + get { return _pointerOverElement; } + private set { RaiseAndSetIfChanged(ref _pointerOverElement, value); } } - public string FocusedControl => _focusedControl.Value; - - public string PointerOverElement => _pointerOverElement.Value; - public void SelectControl(IControl control) { var tree = Content as TreePageViewModel; @@ -76,5 +83,10 @@ namespace Avalonia.Diagnostics.ViewModels tree.SelectControl(control); } } + + private void UpdateFocusedControl() + { + _focusedControl = KeyboardDevice.Instance.FocusedElement?.GetType().Name; + } } } diff --git a/src/Avalonia.Diagnostics/ViewModels/LogicalTreeNode.cs b/src/Avalonia.Diagnostics/ViewModels/LogicalTreeNode.cs index dcb4e79402..e8a8951f0d 100644 --- a/src/Avalonia.Diagnostics/ViewModels/LogicalTreeNode.cs +++ b/src/Avalonia.Diagnostics/ViewModels/LogicalTreeNode.cs @@ -2,9 +2,9 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using Avalonia.Collections; using Avalonia.Controls; using Avalonia.LogicalTree; -using ReactiveUI; namespace Avalonia.Diagnostics.ViewModels { @@ -13,7 +13,7 @@ namespace Avalonia.Diagnostics.ViewModels public LogicalTreeNode(ILogical logical, TreeNode parent) : base((Control)logical, parent) { - Children = logical.LogicalChildren.CreateDerivedCollection(x => new LogicalTreeNode(x, this)); + Children = logical.LogicalChildren.CreateDerivedList(x => new LogicalTreeNode(x, this)); } public static LogicalTreeNode[] Create(object control) diff --git a/src/Avalonia.Diagnostics/ViewModels/PropertyDetails.cs b/src/Avalonia.Diagnostics/ViewModels/PropertyDetails.cs index c06f2415cf..2609b74ce0 100644 --- a/src/Avalonia.Diagnostics/ViewModels/PropertyDetails.cs +++ b/src/Avalonia.Diagnostics/ViewModels/PropertyDetails.cs @@ -3,16 +3,13 @@ using System; using Avalonia.Data; -using ReactiveUI; namespace Avalonia.Diagnostics.ViewModels { - internal class PropertyDetails : ReactiveObject + internal class PropertyDetails : ViewModelBase { private object _value; - private string _priority; - private string _diagnostic; public PropertyDetails(AvaloniaObject o, AvaloniaProperty property) @@ -41,19 +38,19 @@ namespace Avalonia.Diagnostics.ViewModels public string Priority { get { return _priority; } - private set { this.RaiseAndSetIfChanged(ref _priority, value); } + private set { RaiseAndSetIfChanged(ref _priority, value); } } public string Diagnostic { get { return _diagnostic; } - private set { this.RaiseAndSetIfChanged(ref _diagnostic, value); } + private set { RaiseAndSetIfChanged(ref _diagnostic, value); } } public object Value { get { return _value; } - private set { this.RaiseAndSetIfChanged(ref _value, value); } + private set { RaiseAndSetIfChanged(ref _value, value); } } } } diff --git a/src/Avalonia.Diagnostics/ViewModels/TreeNode.cs b/src/Avalonia.Diagnostics/ViewModels/TreeNode.cs index 0b0b7a94fe..7c403e1b04 100644 --- a/src/Avalonia.Diagnostics/ViewModels/TreeNode.cs +++ b/src/Avalonia.Diagnostics/ViewModels/TreeNode.cs @@ -5,13 +5,13 @@ using System; using System.Collections.Specialized; using System.Reactive; using System.Reactive.Linq; +using Avalonia.Collections; using Avalonia.Styling; using Avalonia.VisualTree; -using ReactiveUI; namespace Avalonia.Diagnostics.ViewModels { - internal class TreeNode : ReactiveObject + internal class TreeNode : ViewModelBase { private string _classes; private bool _isExpanded; @@ -47,7 +47,7 @@ namespace Avalonia.Diagnostics.ViewModels } } - public IReadOnlyReactiveList Children + public IAvaloniaReadOnlyList Children { get; protected set; @@ -56,7 +56,7 @@ namespace Avalonia.Diagnostics.ViewModels public string Classes { get { return _classes; } - private set { this.RaiseAndSetIfChanged(ref _classes, value); } + private set { RaiseAndSetIfChanged(ref _classes, value); } } public IVisual Visual @@ -67,7 +67,7 @@ namespace Avalonia.Diagnostics.ViewModels public bool IsExpanded { get { return _isExpanded; } - set { this.RaiseAndSetIfChanged(ref _isExpanded, value); } + set { RaiseAndSetIfChanged(ref _isExpanded, value); } } public TreeNode Parent diff --git a/src/Avalonia.Diagnostics/ViewModels/TreePageViewModel.cs b/src/Avalonia.Diagnostics/ViewModels/TreePageViewModel.cs index 97bd971e74..dba44c5d0c 100644 --- a/src/Avalonia.Diagnostics/ViewModels/TreePageViewModel.cs +++ b/src/Avalonia.Diagnostics/ViewModels/TreePageViewModel.cs @@ -1,25 +1,19 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. -using System.Reactive.Linq; using Avalonia.Controls; using Avalonia.VisualTree; -using ReactiveUI; namespace Avalonia.Diagnostics.ViewModels { - internal class TreePageViewModel : ReactiveObject + internal class TreePageViewModel : ViewModelBase { private TreeNode _selected; - - private readonly ObservableAsPropertyHelper _details; + private ControlDetailsViewModel _details; public TreePageViewModel(TreeNode[] nodes) { Nodes = nodes; - _details = this.WhenAnyValue(x => x.SelectedNode) - .Select(x => x != null ? new ControlDetailsViewModel(x.Visual) : null) - .ToProperty(this, x => x.Details); } public TreeNode[] Nodes { get; protected set; } @@ -27,10 +21,20 @@ namespace Avalonia.Diagnostics.ViewModels public TreeNode SelectedNode { get { return _selected; } - set { this.RaiseAndSetIfChanged(ref _selected, value); } + set + { + if (RaiseAndSetIfChanged(ref _selected, value)) + { + Details = value != null ? new ControlDetailsViewModel(value.Visual) : null; + } + } } - public ControlDetailsViewModel Details => _details.Value; + public ControlDetailsViewModel Details + { + get { return _details; } + private set { RaiseAndSetIfChanged(ref _details, value); } + } public TreeNode FindNode(IControl control) { diff --git a/src/Avalonia.Diagnostics/ViewModels/ViewModelBase.cs b/src/Avalonia.Diagnostics/ViewModels/ViewModelBase.cs new file mode 100644 index 0000000000..349404603a --- /dev/null +++ b/src/Avalonia.Diagnostics/ViewModels/ViewModelBase.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; + +namespace Avalonia.Diagnostics.ViewModels +{ + public class ViewModelBase : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + [NotifyPropertyChangedInvocator] + protected bool RaiseAndSetIfChanged(ref T field, T value, [CallerMemberName] string propertyName = null) + { + if (!EqualityComparer.Default.Equals(field, value)) + { + field = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + return true; + } + + return false; + } + + [NotifyPropertyChangedInvocator] + protected void RaisePropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/src/Avalonia.Diagnostics/ViewModels/VisualTreeNode.cs b/src/Avalonia.Diagnostics/ViewModels/VisualTreeNode.cs index f1b02a61c9..8c070261d9 100644 --- a/src/Avalonia.Diagnostics/ViewModels/VisualTreeNode.cs +++ b/src/Avalonia.Diagnostics/ViewModels/VisualTreeNode.cs @@ -1,10 +1,9 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. -using Avalonia.Controls; +using Avalonia.Collections; using Avalonia.Styling; using Avalonia.VisualTree; -using ReactiveUI; namespace Avalonia.Diagnostics.ViewModels { @@ -17,11 +16,11 @@ namespace Avalonia.Diagnostics.ViewModels if (host?.Root == null) { - Children = visual.VisualChildren.CreateDerivedCollection(x => new VisualTreeNode(x, this)); + Children = visual.VisualChildren.CreateDerivedList(x => new VisualTreeNode(x, this)); } else { - Children = new ReactiveList(new[] { new VisualTreeNode(host.Root, this) }); + Children = new AvaloniaList(new[] { new VisualTreeNode(host.Root, this) }); } if ((Visual is IStyleable styleable)) diff --git a/src/Avalonia.Diagnostics/Views/ControlDetailsView.cs b/src/Avalonia.Diagnostics/Views/ControlDetailsView.cs index 7cb74ebb33..d7bd6fd128 100644 --- a/src/Avalonia.Diagnostics/Views/ControlDetailsView.cs +++ b/src/Avalonia.Diagnostics/Views/ControlDetailsView.cs @@ -8,7 +8,6 @@ using Avalonia.Controls; using Avalonia.Diagnostics.ViewModels; using Avalonia.Media; using Avalonia.Styling; -using ReactiveUI; namespace Avalonia.Diagnostics.Views { @@ -16,6 +15,7 @@ namespace Avalonia.Diagnostics.Views { private static readonly StyledProperty ViewModelProperty = AvaloniaProperty.Register("ViewModel"); + private SimpleGrid _grid; public ControlDetailsView() { @@ -27,7 +27,11 @@ namespace Avalonia.Diagnostics.Views public ControlDetailsViewModel ViewModel { get { return GetValue(ViewModelProperty); } - private set { SetValue(ViewModelProperty, value); } + private set + { + SetValue(ViewModelProperty, value); + _grid[GridRepeater.ItemsProperty] = value?.Properties; + } } private void InitializeComponent() @@ -36,7 +40,7 @@ namespace Avalonia.Diagnostics.Views Content = new ScrollViewer { - Content = new SimpleGrid + Content = _grid = new SimpleGrid { Styles = new Styles { @@ -49,7 +53,6 @@ namespace Avalonia.Diagnostics.Views }, }, [GridRepeater.TemplateProperty] = pt, - [!GridRepeater.ItemsProperty] = this.WhenAnyValue(x => x.ViewModel.Properties).ToBinding(), } }; } @@ -62,16 +65,13 @@ namespace Avalonia.Diagnostics.Views { Text = property.Name, TextWrapping = TextWrapping.NoWrap, - [!ToolTip.TipProperty] = property - .WhenAnyValue(x => x.Diagnostic) - .ToBinding(), + [!ToolTip.TipProperty] = property.GetObservable(nameof(property.Diagnostic)).ToBinding(), }; yield return new TextBlock { TextWrapping = TextWrapping.NoWrap, - [!TextBlock.TextProperty] = property - .WhenAnyValue(v => v.Value) + [!TextBlock.TextProperty] = property.GetObservable(nameof(property.Value)) .Select(v => v?.ToString()) .ToBinding(), }; @@ -79,7 +79,7 @@ namespace Avalonia.Diagnostics.Views yield return new TextBlock { TextWrapping = TextWrapping.NoWrap, - [!TextBlock.TextProperty] = property.WhenAnyValue(x => x.Priority).ToBinding(), + [!TextBlock.TextProperty] = property.GetObservable((nameof(property.Priority))).ToBinding(), }; } } diff --git a/src/Avalonia.Diagnostics/Views/PropertyChangedExtenions.cs b/src/Avalonia.Diagnostics/Views/PropertyChangedExtenions.cs new file mode 100644 index 0000000000..1d4ad24fd0 --- /dev/null +++ b/src/Avalonia.Diagnostics/Views/PropertyChangedExtenions.cs @@ -0,0 +1,30 @@ +using System; +using System.ComponentModel; +using System.Reactive.Linq; +using System.Reflection; + +namespace Avalonia.Diagnostics.Views +{ + internal static class PropertyChangedExtenions + { + public static IObservable GetObservable(this INotifyPropertyChanged source, string propertyName) + { + Contract.Requires(source != null); + Contract.Requires(propertyName != null); + + var property = source.GetType().GetTypeInfo().GetDeclaredProperty(propertyName); + + if (property == null) + { + throw new ArgumentException($"Property '{propertyName}' not found on '{source}."); + } + + return Observable.FromEventPattern( + e => source.PropertyChanged += e, + e => source.PropertyChanged -= e) + .Where(e => e.EventArgs.PropertyName == propertyName) + .Select(_ => (T)property.GetValue(source)) + .StartWith((T)property.GetValue(source)); + } + } +} diff --git a/src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs b/src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs index fc7fdd2431..9a54cdbab0 100644 --- a/src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs +++ b/src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs @@ -82,7 +82,13 @@ namespace Avalonia private void LoadAssembliesInDirectory() { - foreach (var file in new FileInfo(Assembly.GetEntryAssembly().Location).Directory.EnumerateFiles("*.dll")) + var location = Assembly.GetEntryAssembly().Location; + if (string.IsNullOrWhiteSpace(location)) + return; + var dir = new FileInfo(location).Directory; + if (dir == null) + return; + foreach (var file in dir.EnumerateFiles("*.dll")) { try { diff --git a/src/Avalonia.Input/IKeyboardDevice.cs b/src/Avalonia.Input/IKeyboardDevice.cs index f27fec6907..1410476267 100644 --- a/src/Avalonia.Input/IKeyboardDevice.cs +++ b/src/Avalonia.Input/IKeyboardDevice.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using System.ComponentModel; namespace Avalonia.Input { @@ -26,7 +27,7 @@ namespace Avalonia.Input Toggled = 2, } - public interface IKeyboardDevice : IInputDevice + public interface IKeyboardDevice : IInputDevice, INotifyPropertyChanged { IInputElement FocusedElement { get; } diff --git a/src/Avalonia.Input/Navigation/DirectionalNavigation.cs b/src/Avalonia.Input/Navigation/DirectionalNavigation.cs index b2f7a35799..a88ed1e8aa 100644 --- a/src/Avalonia.Input/Navigation/DirectionalNavigation.cs +++ b/src/Avalonia.Input/Navigation/DirectionalNavigation.cs @@ -44,7 +44,7 @@ namespace Avalonia.Input.Navigation GetFirstInNextContainer(element, direction); case KeyboardNavigationMode.Cycle: return GetNextInContainer(element, container, direction) ?? - GetFocusableDescendent(container, direction); + GetFocusableDescendant(container, direction); case KeyboardNavigationMode.Contained: return GetNextInContainer(element, container, direction); default: @@ -53,7 +53,7 @@ namespace Avalonia.Input.Navigation } else { - return GetFocusableDescendents(element).FirstOrDefault(); + return GetFocusableDescendants(element).FirstOrDefault(); } } @@ -71,24 +71,24 @@ namespace Avalonia.Input.Navigation } /// - /// Gets the first or last focusable descendent of the specified element. + /// Gets the first or last focusable descendant of the specified element. /// /// The element. /// The direction to search. /// The element or null if not found.## - private static IInputElement GetFocusableDescendent(IInputElement container, NavigationDirection direction) + private static IInputElement GetFocusableDescendant(IInputElement container, NavigationDirection direction) { return IsForward(direction) ? - GetFocusableDescendents(container).FirstOrDefault() : - GetFocusableDescendents(container).LastOrDefault(); + GetFocusableDescendants(container).FirstOrDefault() : + GetFocusableDescendants(container).LastOrDefault(); } /// - /// Gets the focusable descendents of the specified element. + /// Gets the focusable descendants of the specified element. /// /// The element. - /// The element's focusable descendents. - private static IEnumerable GetFocusableDescendents(IInputElement element) + /// The element's focusable descendants. + private static IEnumerable GetFocusableDescendants(IInputElement element) { var children = element.GetVisualChildren().OfType(); @@ -99,11 +99,11 @@ namespace Avalonia.Input.Navigation yield return child; } - if (child.CanFocusDescendents()) + if (child.CanFocusDescendants()) { - foreach (var descendent in GetFocusableDescendents(child)) + foreach (var descendant in GetFocusableDescendants(child)) { - yield return descendent; + yield return descendant; } } } @@ -123,11 +123,11 @@ namespace Avalonia.Input.Navigation { if (direction == NavigationDirection.Down) { - var descendent = GetFocusableDescendents(element).FirstOrDefault(); + var descendant = GetFocusableDescendants(element).FirstOrDefault(); - if (descendent != null) + if (descendant != null) { - return descendent; + return descendant; } } @@ -156,11 +156,11 @@ namespace Avalonia.Input.Navigation if (element != null && direction == NavigationDirection.Up) { - var descendent = GetFocusableDescendents(element).LastOrDefault(); + var descendant = GetFocusableDescendants(element).LastOrDefault(); - if (descendent != null) + if (descendant != null) { - return descendent; + return descendant; } } @@ -193,7 +193,7 @@ namespace Avalonia.Input.Navigation var siblings = parent.GetVisualChildren() .OfType() - .Where(FocusExtensions.CanFocusDescendents); + .Where(FocusExtensions.CanFocusDescendants); var sibling = isForward ? siblings.SkipWhile(x => x != container).Skip(1).FirstOrDefault() : siblings.TakeWhile(x => x != container).LastOrDefault(); @@ -207,8 +207,8 @@ namespace Avalonia.Input.Navigation else { next = isForward ? - GetFocusableDescendents(sibling).FirstOrDefault() : - GetFocusableDescendents(sibling).LastOrDefault(); + GetFocusableDescendants(sibling).FirstOrDefault() : + GetFocusableDescendants(sibling).LastOrDefault(); } } @@ -220,8 +220,8 @@ namespace Avalonia.Input.Navigation else { next = isForward ? - GetFocusableDescendents(container).FirstOrDefault() : - GetFocusableDescendents(container).LastOrDefault(); + GetFocusableDescendants(container).FirstOrDefault() : + GetFocusableDescendants(container).LastOrDefault(); } return next; diff --git a/src/Avalonia.Input/Navigation/FocusExtensions.cs b/src/Avalonia.Input/Navigation/FocusExtensions.cs index 36fda1abb1..41e7c4cd7b 100644 --- a/src/Avalonia.Input/Navigation/FocusExtensions.cs +++ b/src/Avalonia.Input/Navigation/FocusExtensions.cs @@ -16,10 +16,10 @@ namespace Avalonia.Input.Navigation public static bool CanFocus(this IInputElement e) => e.Focusable && e.IsEnabledCore && e.IsVisible; /// - /// Checks if descendents of the specified element can be focused. + /// Checks if descendants of the specified element can be focused. /// /// The element. - /// True if descendents of the element can be focused. - public static bool CanFocusDescendents(this IInputElement e) => e.IsEnabledCore && e.IsVisible; + /// True if descendants of the element can be focused. + public static bool CanFocusDescendants(this IInputElement e) => e.IsEnabledCore && e.IsVisible; } } diff --git a/src/Avalonia.Input/Navigation/TabNavigation.cs b/src/Avalonia.Input/Navigation/TabNavigation.cs index bc3826d90e..6ba7ab1a0c 100644 --- a/src/Avalonia.Input/Navigation/TabNavigation.cs +++ b/src/Avalonia.Input/Navigation/TabNavigation.cs @@ -44,7 +44,7 @@ namespace Avalonia.Input.Navigation GetFirstInNextContainer(element, direction); case KeyboardNavigationMode.Cycle: return GetNextInContainer(element, container, direction) ?? - GetFocusableDescendent(container, direction); + GetFocusableDescendant(container, direction); case KeyboardNavigationMode.Contained: return GetNextInContainer(element, container, direction); default: @@ -53,29 +53,29 @@ namespace Avalonia.Input.Navigation } else { - return GetFocusableDescendents(element).FirstOrDefault(); + return GetFocusableDescendants(element).FirstOrDefault(); } } /// - /// Gets the first or last focusable descendent of the specified element. + /// Gets the first or last focusable descendant of the specified element. /// /// The element. /// The direction to search. /// The element or null if not found.## - private static IInputElement GetFocusableDescendent(IInputElement container, NavigationDirection direction) + private static IInputElement GetFocusableDescendant(IInputElement container, NavigationDirection direction) { return direction == NavigationDirection.Next ? - GetFocusableDescendents(container).FirstOrDefault() : - GetFocusableDescendents(container).LastOrDefault(); + GetFocusableDescendants(container).FirstOrDefault() : + GetFocusableDescendants(container).LastOrDefault(); } /// - /// Gets the focusable descendents of the specified element. + /// Gets the focusable descendants of the specified element. /// /// The element. - /// The element's focusable descendents. - private static IEnumerable GetFocusableDescendents(IInputElement element) + /// The element's focusable descendants. + private static IEnumerable GetFocusableDescendants(IInputElement element) { var mode = KeyboardNavigation.GetTabNavigation((InputElement)element); @@ -108,11 +108,11 @@ namespace Avalonia.Input.Navigation yield return child; } - if (child.CanFocusDescendents()) + if (child.CanFocusDescendants()) { - foreach (var descendent in GetFocusableDescendents(child)) + foreach (var descendant in GetFocusableDescendants(child)) { - yield return descendent; + yield return descendant; } } } @@ -132,11 +132,11 @@ namespace Avalonia.Input.Navigation { if (direction == NavigationDirection.Next) { - var descendent = GetFocusableDescendents(element).FirstOrDefault(); + var descendant = GetFocusableDescendants(element).FirstOrDefault(); - if (descendent != null) + if (descendant != null) { - return descendent; + return descendant; } } @@ -167,11 +167,11 @@ namespace Avalonia.Input.Navigation if (element != null && direction == NavigationDirection.Previous) { - var descendent = GetFocusableDescendents(element).LastOrDefault(); + var descendant = GetFocusableDescendants(element).LastOrDefault(); - if (descendent != null) + if (descendant != null) { - return descendent; + return descendant; } } @@ -203,7 +203,7 @@ namespace Avalonia.Input.Navigation var siblings = parent.GetVisualChildren() .OfType() - .Where(FocusExtensions.CanFocusDescendents); + .Where(FocusExtensions.CanFocusDescendants); var sibling = direction == NavigationDirection.Next ? siblings.SkipWhile(x => x != container).Skip(1).FirstOrDefault() : siblings.TakeWhile(x => x != container).LastOrDefault(); @@ -217,8 +217,8 @@ namespace Avalonia.Input.Navigation else { next = direction == NavigationDirection.Next ? - GetFocusableDescendents(sibling).FirstOrDefault() : - GetFocusableDescendents(sibling).LastOrDefault(); + GetFocusableDescendants(sibling).FirstOrDefault() : + GetFocusableDescendants(sibling).LastOrDefault(); } } @@ -230,8 +230,8 @@ namespace Avalonia.Input.Navigation else { next = direction == NavigationDirection.Next ? - GetFocusableDescendents(container).FirstOrDefault() : - GetFocusableDescendents(container).LastOrDefault(); + GetFocusableDescendants(container).FirstOrDefault() : + GetFocusableDescendants(container).LastOrDefault(); } return next; diff --git a/src/Avalonia.Layout/Layoutable.cs b/src/Avalonia.Layout/Layoutable.cs index 5abd6c52f7..20050058bf 100644 --- a/src/Avalonia.Layout/Layoutable.cs +++ b/src/Avalonia.Layout/Layoutable.cs @@ -456,10 +456,9 @@ namespace Avalonia.Layout ApplyTemplate(); - var constrained = LayoutHelper - .ApplyLayoutConstraints(this, availableSize) - .Deflate(margin); - + var constrained = LayoutHelper.ApplyLayoutConstraints( + this, + availableSize.Deflate(margin)); var measured = MeasureOverride(constrained); var width = measured.Width; @@ -613,7 +612,7 @@ namespace Avalonia.Layout /// protected override sealed void OnVisualParentChanged(IVisual oldParent, IVisual newParent) { - foreach (ILayoutable i in this.GetSelfAndVisualDescendents()) + foreach (ILayoutable i in this.GetSelfAndVisualDescendants()) { i.InvalidateMeasure(); } diff --git a/src/Avalonia.Styling/Controls/NameScope.cs b/src/Avalonia.Styling/Controls/NameScope.cs index 4c5875479e..8b5bd81d3c 100644 --- a/src/Avalonia.Styling/Controls/NameScope.cs +++ b/src/Avalonia.Styling/Controls/NameScope.cs @@ -50,7 +50,7 @@ namespace Avalonia.Controls return result; } - visual = (visual as ILogical).LogicalParent as Visual; + visual = (visual as ILogical)?.LogicalParent as Visual; } return null; diff --git a/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs b/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs index 0d9c0f6daa..a72a558258 100644 --- a/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs +++ b/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs @@ -37,15 +37,15 @@ namespace Avalonia.LogicalTree return logical.LogicalChildren; } - public static IEnumerable GetLogicalDescendents(this ILogical logical) + public static IEnumerable GetLogicalDescendants(this ILogical logical) { foreach (ILogical child in logical.LogicalChildren) { yield return child; - foreach (ILogical descendent in child.GetLogicalDescendents()) + foreach (ILogical descendant in child.GetLogicalDescendants()) { - yield return descendent; + yield return descendant; } } } diff --git a/src/Avalonia.Styling/Styling/DescendentSelector.cs b/src/Avalonia.Styling/Styling/DescendentSelector.cs index 525f31cbf1..943b3f0161 100644 --- a/src/Avalonia.Styling/Styling/DescendentSelector.cs +++ b/src/Avalonia.Styling/Styling/DescendentSelector.cs @@ -7,16 +7,16 @@ using Avalonia.LogicalTree; namespace Avalonia.Styling { - internal class DescendentSelector : Selector + internal class DescendantSelector : Selector { private readonly Selector _parent; private string _selectorString; - public DescendentSelector(Selector parent) + public DescendantSelector(Selector parent) { if (parent == null) { - throw new InvalidOperationException("Descendent selector must be preceeded by a selector."); + throw new InvalidOperationException("Descendant selector must be preceeded by a selector."); } _parent = parent; @@ -41,7 +41,7 @@ namespace Avalonia.Styling protected override SelectorMatch Evaluate(IStyleable control, bool subscribe) { ILogical c = (ILogical)control; - List> descendentMatches = new List>(); + List> descendantMatches = new List>(); while (c != null) { @@ -60,14 +60,14 @@ namespace Avalonia.Styling } else { - descendentMatches.Add(match.ObservableResult); + descendantMatches.Add(match.ObservableResult); } } } - if (descendentMatches.Count > 0) + if (descendantMatches.Count > 0) { - return new SelectorMatch(StyleActivator.Or(descendentMatches)); + return new SelectorMatch(StyleActivator.Or(descendantMatches)); } else { diff --git a/src/Avalonia.Styling/Styling/Selectors.cs b/src/Avalonia.Styling/Styling/Selectors.cs index 1b4c876f81..c91cc7af04 100644 --- a/src/Avalonia.Styling/Styling/Selectors.cs +++ b/src/Avalonia.Styling/Styling/Selectors.cs @@ -42,13 +42,13 @@ namespace Avalonia.Styling } /// - /// Returns a selector which matches a descendent of a previous selector. + /// Returns a selector which matches a descendant of a previous selector. /// /// The previous selector. /// The selector. - public static Selector Descendent(this Selector previous) + public static Selector Descendant(this Selector previous) { - return new DescendentSelector(previous); + return new DescendantSelector(previous); } /// diff --git a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs b/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs index 4879532d6e..7d2222e449 100644 --- a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs +++ b/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs @@ -164,7 +164,7 @@ namespace Avalonia.Rendering private static void ClearTransformedBounds(IVisual visual) { - foreach (var e in visual.GetSelfAndVisualDescendents()) + foreach (var e in visual.GetSelfAndVisualDescendants()) { BoundsTracker.SetTransformedBounds((Visual)visual, null); } diff --git a/src/Avalonia.Visuals/Rendering/ZIndexComparer.cs b/src/Avalonia.Visuals/Rendering/ZIndexComparer.cs index b9c43bcbc3..491541cd2e 100644 --- a/src/Avalonia.Visuals/Rendering/ZIndexComparer.cs +++ b/src/Avalonia.Visuals/Rendering/ZIndexComparer.cs @@ -8,6 +8,6 @@ namespace Avalonia.Rendering { public static readonly ZIndexComparer Instance = new ZIndexComparer(); - public int Compare(IVisual x, IVisual y) => x.ZIndex.CompareTo(y.ZIndex); + public int Compare(IVisual x, IVisual y) => (x?.ZIndex ?? 0).CompareTo(y?.ZIndex ?? 0); } } diff --git a/src/Avalonia.Visuals/Visual.cs b/src/Avalonia.Visuals/Visual.cs index 20772d8f0d..cfe6bce7e0 100644 --- a/src/Avalonia.Visuals/Visual.cs +++ b/src/Avalonia.Visuals/Visual.cs @@ -314,7 +314,7 @@ namespace Avalonia /// /// Calls the method - /// for this control and all of its visual descendents. + /// for this control and all of its visual descendants. /// /// The event args. protected virtual void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e) @@ -342,7 +342,7 @@ namespace Avalonia /// /// Calls the method - /// for this control and all of its visual descendents. + /// for this control and all of its visual descendants. /// /// The event args. protected virtual void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) @@ -422,7 +422,7 @@ namespace Avalonia if (visual == null) { - throw new ArgumentException("'visual' is not a descendent of 'ancestor'."); + throw new ArgumentException("'visual' is not a descendant of 'ancestor'."); } } diff --git a/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs b/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs index 11fbae4321..289c4134d1 100644 --- a/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs +++ b/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs @@ -123,33 +123,33 @@ namespace Avalonia.VisualTree } /// - /// Enumerates the descendents of an in the visual tree. + /// Enumerates the descendants of an in the visual tree. /// /// The visual. /// The visual's ancestors. - public static IEnumerable GetVisualDescendents(this IVisual visual) + public static IEnumerable GetVisualDescendants(this IVisual visual) { foreach (IVisual child in visual.VisualChildren) { yield return child; - foreach (IVisual descendent in child.GetVisualDescendents()) + foreach (IVisual descendant in child.GetVisualDescendants()) { - yield return descendent; + yield return descendant; } } } /// - /// Enumerates an and its descendents in the visual tree. + /// Enumerates an and its descendants in the visual tree. /// /// The visual. /// The visual and its ancestors. - public static IEnumerable GetSelfAndVisualDescendents(this IVisual visual) + public static IEnumerable GetSelfAndVisualDescendants(this IVisual visual) { yield return visual; - foreach (var ancestor in visual.GetVisualDescendents()) + foreach (var ancestor in visual.GetVisualDescendants()) { yield return ancestor; } @@ -196,7 +196,7 @@ namespace Avalonia.VisualTree /// Tests whether an is an ancestor of another visual. /// /// The visual. - /// The potential descendent. + /// The potential descendant. /// /// True if is an ancestor of ; /// otherwise false. diff --git a/src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs b/src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs index 7d1776db0b..99b0a2ec73 100644 --- a/src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs +++ b/src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs @@ -9,6 +9,7 @@ using Avalonia.Cairo.Media.Imaging; using Avalonia.Media; using Avalonia.Platform; using Avalonia.Rendering; +// ReSharper disable PossibleNullReferenceException namespace Avalonia.Cairo.Media { diff --git a/src/Gtk/Avalonia.Gtk3/SystemDialogs.cs b/src/Gtk/Avalonia.Gtk3/SystemDialogs.cs index efdadc2379..f6232ac68e 100644 --- a/src/Gtk/Avalonia.Gtk3/SystemDialogs.cs +++ b/src/Gtk/Avalonia.Gtk3/SystemDialogs.cs @@ -28,6 +28,7 @@ namespace Avalonia.Gtk3 List disposables = null; Action dispose = () => { + // ReSharper disable once PossibleNullReferenceException foreach (var d in disposables) d.Dispose(); disposables.Clear(); diff --git a/src/Markup/Avalonia.Markup.Xaml/Parsers/SelectorGrammar.cs b/src/Markup/Avalonia.Markup.Xaml/Parsers/SelectorGrammar.cs index d11df1a9e9..671fdfff30 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Parsers/SelectorGrammar.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Parsers/SelectorGrammar.cs @@ -93,9 +93,9 @@ namespace Avalonia.Markup.Xaml.Parsers public static readonly Parser Child = Parse.Char('>').Token().Return(new ChildSyntax()); - public static readonly Parser Descendent = + public static readonly Parser Descendant = from child in Parse.WhiteSpace.Many() - select new DescendentSyntax(); + select new DescendantSyntax(); public static readonly Parser Template = from template in Parse.String("/template/").Token() @@ -115,7 +115,7 @@ namespace Avalonia.Markup.Xaml.Parsers .Or(Property) .Or(Child) .Or(Template) - .Or(Descendent); + .Or(Descendant); public static readonly Parser> Selector = SingleSelector.Many().End(); @@ -191,11 +191,11 @@ namespace Avalonia.Markup.Xaml.Parsers } } - public class DescendentSyntax : ISyntax + public class DescendantSyntax : ISyntax { public override bool Equals(object obj) { - return obj is DescendentSyntax; + return obj is DescendantSyntax; } } diff --git a/src/Markup/Avalonia.Markup.Xaml/Parsers/SelectorParser.cs b/src/Markup/Avalonia.Markup.Xaml/Parsers/SelectorParser.cs index 8cf6f9794e..1cecb21f17 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Parsers/SelectorParser.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Parsers/SelectorParser.cs @@ -47,7 +47,7 @@ namespace Avalonia.Markup.Xaml.Parsers var name = i as SelectorGrammar.NameSyntax; var property = i as SelectorGrammar.PropertySyntax; var child = i as SelectorGrammar.ChildSyntax; - var descendent = i as SelectorGrammar.DescendentSyntax; + var descendant = i as SelectorGrammar.DescendantSyntax; var template = i as SelectorGrammar.TemplateSyntax; if (ofType != null) @@ -68,7 +68,7 @@ namespace Avalonia.Markup.Xaml.Parsers } else if (property != null) { - var type = result.TargetType; + var type = result?.TargetType; if (type == null) { @@ -102,9 +102,9 @@ namespace Avalonia.Markup.Xaml.Parsers { result = result.Child(); } - else if (descendent != null) + else if (descendant != null) { - result = result.Descendent(); + result = result.Descendant(); } else if (template != null) { diff --git a/src/Markup/Avalonia.Markup/Data/Plugins/TaskStreamPlugin.cs b/src/Markup/Avalonia.Markup/Data/Plugins/TaskStreamPlugin.cs index d2c8c1b064..02fe8104b8 100644 --- a/src/Markup/Avalonia.Markup/Data/Plugins/TaskStreamPlugin.cs +++ b/src/Markup/Avalonia.Markup/Data/Plugins/TaskStreamPlugin.cs @@ -36,7 +36,7 @@ namespace Avalonia.Markup.Data.Plugins if (task != null) { - var resultProperty = task.GetType().GetTypeInfo().GetDeclaredProperty("Result"); + var resultProperty = task.GetType().GetRuntimeProperty("Result"); if (resultProperty != null) { @@ -61,7 +61,7 @@ namespace Avalonia.Markup.Data.Plugins protected IObservable HandleCompleted(Task task) { - var resultProperty = task.GetType().GetTypeInfo().GetDeclaredProperty("Result"); + var resultProperty = task.GetType().GetRuntimeProperty("Result"); if (resultProperty != null) { diff --git a/src/Shared/SharedAssemblyInfo.cs b/src/Shared/SharedAssemblyInfo.cs index 2ae067092c..81c31e2687 100644 --- a/src/Shared/SharedAssemblyInfo.cs +++ b/src/Shared/SharedAssemblyInfo.cs @@ -13,6 +13,6 @@ using System.Resources; [assembly: AssemblyTrademark("")] [assembly: NeutralResourcesLanguage("en")] -[assembly: AssemblyVersion("0.5.0")] -[assembly: AssemblyFileVersion("0.5.0")] -[assembly: AssemblyInformationalVersion("0.5.0")] +[assembly: AssemblyVersion("0.5.1")] +[assembly: AssemblyFileVersion("0.5.1")] +[assembly: AssemblyInformationalVersion("0.5.1")] diff --git a/src/Windows/Avalonia.Win32/Embedding/WinFormsAvaloniaControlHost.cs b/src/Windows/Avalonia.Win32/Embedding/WinFormsAvaloniaControlHost.cs index bdee85c91e..a484d6c0d2 100644 --- a/src/Windows/Avalonia.Win32/Embedding/WinFormsAvaloniaControlHost.cs +++ b/src/Windows/Avalonia.Win32/Embedding/WinFormsAvaloniaControlHost.cs @@ -15,7 +15,7 @@ namespace Avalonia.Win32.Embedding { private readonly EmbeddableControlRoot _root = new EmbeddableControlRoot(); - private IntPtr WindowHandle => ((WindowImpl) _root.PlatformImpl).Handle.Handle; + private IntPtr WindowHandle => ((WindowImpl) _root?.PlatformImpl)?.Handle?.Handle ?? IntPtr.Zero; public WinFormsAvaloniaControlHost() { @@ -25,6 +25,8 @@ namespace Avalonia.Win32.Embedding if (_root.IsFocused) FocusManager.Instance.Focus(null); _root.GotFocus += RootGotFocus; + // ReSharper disable once PossibleNullReferenceException + // Always non-null at this point _root.PlatformImpl.LostFocus += PlatformImpl_LostFocus; FixPosition(); } diff --git a/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListExtenionsTests.cs b/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListExtenionsTests.cs new file mode 100644 index 0000000000..b996db8d48 --- /dev/null +++ b/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListExtenionsTests.cs @@ -0,0 +1,134 @@ +using System; +using System.Linq; +using Avalonia.Collections; +using Xunit; + +namespace Avalonia.Base.UnitTests.Collections +{ + public class AvaloniaListExtenionsTests + { + [Fact] + public void CreateDerivedList_Creates_Initial_Items() + { + var source = new AvaloniaList(new[] { 0, 1, 2, 3 }); + var target = source.CreateDerivedList(x => new Wrapper(x)); + var result = target.Select(x => x.Value).ToList(); + + Assert.Equal(source, result); + } + + [Fact] + public void CreateDerivedList_Handles_Add() + { + var source = new AvaloniaList(new[] { 0, 1, 2, 3 }); + var target = source.CreateDerivedList(x => new Wrapper(x)); + + source.Add(4); + + var result = target.Select(x => x.Value).ToList(); + + Assert.Equal(source, result); + } + + [Fact] + public void CreateDerivedList_Handles_Insert() + { + var source = new AvaloniaList(new[] { 0, 1, 2, 3 }); + var target = source.CreateDerivedList(x => new Wrapper(x)); + + source.Insert(1, 4); + + var result = target.Select(x => x.Value).ToList(); + + Assert.Equal(source, result); + } + + [Fact] + public void CreateDerivedList_Handles_Remove() + { + var source = new AvaloniaList(new[] { 0, 1, 2, 3 }); + var target = source.CreateDerivedList(x => new Wrapper(x)); + + source.Remove(2); + + var result = target.Select(x => x.Value).ToList(); + + Assert.Equal(source, result); + } + + [Fact] + public void CreateDerivedList_Handles_RemoveRange() + { + var source = new AvaloniaList(new[] { 0, 1, 2, 3 }); + var target = source.CreateDerivedList(x => new Wrapper(x)); + + source.RemoveRange(1, 2); + + var result = target.Select(x => x.Value).ToList(); + + Assert.Equal(source, result); + } + + [Fact] + public void CreateDerivedList_Handles_Move() + { + var source = new AvaloniaList(new[] { 0, 1, 2, 3 }); + var target = source.CreateDerivedList(x => new Wrapper(x)); + + source.Move(2, 0); + + var result = target.Select(x => x.Value).ToList(); + + Assert.Equal(source, result); + } + + [Fact] + public void CreateDerivedList_Handles_MoveRange() + { + var source = new AvaloniaList(new[] { 0, 1, 2, 3 }); + var target = source.CreateDerivedList(x => new Wrapper(x)); + + source.MoveRange(1, 2, 0); + + var result = target.Select(x => x.Value).ToList(); + + Assert.Equal(source, result); + } + + [Fact] + public void CreateDerivedList_Handles_Replace() + { + var source = new AvaloniaList(new[] { 0, 1, 2, 3 }); + var target = source.CreateDerivedList(x => new Wrapper(x)); + + source[1] = 4; + + var result = target.Select(x => x.Value).ToList(); + + Assert.Equal(source, result); + } + + [Fact] + public void CreateDerivedList_Handles_Clear() + { + var source = new AvaloniaList(new[] { 0, 1, 2, 3 }); + var target = source.CreateDerivedList(x => new Wrapper(x)); + + source.Clear(); + + var result = target.Select(x => x.Value).ToList(); + + Assert.Equal(source, result); + } + + private class Wrapper + { + public Wrapper(int value) + { + Value = value; + } + + public int Value { get; } + } + } +} diff --git a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs index a588e88eb2..e58542bfb4 100644 --- a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs @@ -245,7 +245,7 @@ namespace Avalonia.Controls.UnitTests // The items were created before the template was applied, so now we need to go back // and re-arrange everything. - foreach (IControl i in target.GetSelfAndVisualDescendents()) + foreach (IControl i in target.GetSelfAndVisualDescendants()) { i.InvalidateMeasure(); } diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs index 33ed26d6d8..ab16552e12 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs @@ -219,7 +219,7 @@ namespace Avalonia.Controls.UnitTests.Presenters } [Fact] - public void BringDescendentIntoView_Should_Update_Offset() + public void BringDescendantIntoView_Should_Update_Offset() { var target = new ScrollContentPresenter { @@ -235,13 +235,13 @@ namespace Avalonia.Controls.UnitTests.Presenters target.UpdateChild(); target.Measure(Size.Infinity); target.Arrange(new Rect(0, 0, 100, 100)); - target.BringDescendentIntoView(target.Child, new Rect(200, 200, 0, 0)); + target.BringDescendantIntoView(target.Child, new Rect(200, 200, 0, 0)); Assert.Equal(new Vector(100, 100), target.Offset); } [Fact] - public void BringDescendentIntoView_Should_Handle_Child_Margin() + public void BringDescendantIntoView_Should_Handle_Child_Margin() { Border border; var target = new ScrollContentPresenter @@ -262,7 +262,7 @@ namespace Avalonia.Controls.UnitTests.Presenters target.UpdateChild(); target.Measure(Size.Infinity); target.Arrange(new Rect(0, 0, 100, 100)); - target.BringDescendentIntoView(border, new Rect(200, 200, 0, 0)); + target.BringDescendantIntoView(border, new Rect(200, 200, 0, 0)); Assert.Equal(new Vector(150, 150), target.Offset); } diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs index 367070b37a..f192e87f08 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs @@ -222,7 +222,7 @@ namespace Avalonia.Controls.UnitTests.Primitives popup.Open(); var popupRoot = popup.PopupRoot; - var children = popupRoot.GetVisualDescendents().ToList(); + var children = popupRoot.GetVisualDescendants().ToList(); var types = children.Select(x => x.GetType().Name).ToList(); Assert.Equal( diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests.cs index 3c2f2e4f5c..636492ed1c 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests.cs @@ -78,7 +78,7 @@ namespace Avalonia.Controls.UnitTests.Primitives target.ApplyTemplate(); - var types = target.GetVisualDescendents().Select(x => x.GetType()).ToList(); + var types = target.GetVisualDescendants().Select(x => x.GetType()).ToList(); Assert.Equal( new[] @@ -135,7 +135,7 @@ namespace Avalonia.Controls.UnitTests.Primitives target.ApplyTemplate(); - var templatedParents = target.GetVisualDescendents() + var templatedParents = target.GetVisualDescendants() .OfType() .Select(x => x.TemplatedParent) .ToList(); diff --git a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs index eb0ee5a231..52d36a33fa 100644 --- a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs @@ -80,7 +80,7 @@ namespace Avalonia.Controls.UnitTests } [Fact] - public void Root_TreeContainerFromItem_Should_Return_Descendent_Item() + public void Root_TreeContainerFromItem_Should_Return_Descendant_Item() { var tree = CreateTestTreeData(); var target = new TreeView diff --git a/tests/Avalonia.Controls.UnitTests/WindowTests.cs b/tests/Avalonia.Controls.UnitTests/WindowTests.cs index 96afecc966..e0dd908bbb 100644 --- a/tests/Avalonia.Controls.UnitTests/WindowTests.cs +++ b/tests/Avalonia.Controls.UnitTests/WindowTests.cs @@ -4,6 +4,7 @@ // // ----------------------------------------------------------------------- +using System.Collections.Generic; using Avalonia.Platform; using Avalonia.UnitTests; using Moq; @@ -114,5 +115,81 @@ namespace Avalonia.Controls.UnitTests Assert.False(window.IsVisible); } } + + [Fact] + public void Show_Should_Add_Window_To_OpenWindows() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + ClearOpenWindows(); + var window = new Window(); + + window.Show(); + + Assert.Equal(new[] { window }, Window.OpenWindows); + } + } + + [Fact] + public void Window_Should_Be_Added_To_OpenWindows_Only_Once() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + ClearOpenWindows(); + var window = new Window(); + + window.Show(); + window.Show(); + window.IsVisible = true; + + Assert.Equal(new[] { window }, Window.OpenWindows); + + window.Close(); + } + } + + [Fact] + public void Close_Should_Remove_Window_From_OpenWindows() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + ClearOpenWindows(); + var window = new Window(); + + window.Show(); + window.Close(); + + Assert.Empty(Window.OpenWindows); + } + } + + [Fact] + public void Impl_Closing_Should_Remove_Window_From_OpenWindows() + { + var windowImpl = new Mock(); + windowImpl.SetupProperty(x => x.Closed); + windowImpl.Setup(x => x.Scaling).Returns(1); + + var services = TestServices.StyledWindow.With( + windowingPlatform: new MockWindowingPlatform(() => windowImpl.Object)); + + using (UnitTestApplication.Start(services)) + { + ClearOpenWindows(); + var window = new Window(); + + window.Show(); + windowImpl.Object.Closed(); + + Assert.Empty(Window.OpenWindows); + } + } + + private void ClearOpenWindows() + { + // HACK: We really need a decent way to have "statics" that can be scoped to + // AvaloniaLocator scopes. + ((IList)Window.OpenWindows).Clear(); + } } } diff --git a/tests/Avalonia.Interactivity.UnitTests/InteractiveTests.cs b/tests/Avalonia.Interactivity.UnitTests/InteractiveTests.cs index dd0974fad0..067cf85c3c 100644 --- a/tests/Avalonia.Interactivity.UnitTests/InteractiveTests.cs +++ b/tests/Avalonia.Interactivity.UnitTests/InteractiveTests.cs @@ -391,7 +391,7 @@ namespace Avalonia.Interactivity.UnitTests if (handler != null) { - foreach (var i in tree.GetSelfAndVisualDescendents().Cast()) + foreach (var i in tree.GetSelfAndVisualDescendants().Cast()) { i.AddHandler(ev, handler, handlerRoutes, handledEventsToo); } diff --git a/tests/Avalonia.Layout.UnitTests/MeasureTests.cs b/tests/Avalonia.Layout.UnitTests/MeasureTests.cs index b5a3539da9..99983fd19b 100644 --- a/tests/Avalonia.Layout.UnitTests/MeasureTests.cs +++ b/tests/Avalonia.Layout.UnitTests/MeasureTests.cs @@ -45,7 +45,7 @@ namespace Avalonia.Layout.UnitTests } [Fact] - public void Removing_From_Parent_Should_Invalidate_Measure_Of_Control_And_Descendents() + public void Removing_From_Parent_Should_Invalidate_Measure_Of_Control_And_Descendants() { var panel = new StackPanel(); var child2 = new Border(); @@ -100,5 +100,58 @@ namespace Avalonia.Layout.UnitTests Assert.Equal(0, target.DesiredSize.Height); } + + [Fact] + public void Margin_Should_Affect_AvailableSize() + { + MeasureTest target; + + var outer = new Decorator + { + Width = 100, + Height = 100, + Child = target = new MeasureTest + { + Margin = new Thickness(10), + } + }; + + outer.Measure(Size.Infinity); + + Assert.Equal(new Size(80, 80), target.AvailableSize); + } + + [Fact] + public void Margin_Should_Be_Applied_Before_Width_Height() + { + MeasureTest target; + + var outer = new Decorator + { + Width = 100, + Height = 100, + Child = target = new MeasureTest + { + Width = 80, + Height = 80, + Margin = new Thickness(10), + } + }; + + outer.Measure(Size.Infinity); + + Assert.Equal(new Size(80, 80), target.AvailableSize); + } + + class MeasureTest : Control + { + public Size? AvailableSize { get; private set; } + + protected override Size MeasureOverride(Size availableSize) + { + AvailableSize = availableSize; + return availableSize; + } + } } } diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Parsers/SelectorGrammarTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Parsers/SelectorGrammarTests.cs index 676d6288d6..ad2c1bf8d3 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Parsers/SelectorGrammarTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Parsers/SelectorGrammarTests.cs @@ -157,7 +157,7 @@ namespace Avalonia.Xaml.Base.UnitTest.Parsers } [Fact] - public void OfType_Descendent_Class() + public void OfType_Descendant_Class() { var result = SelectorGrammar.Selector.Parse("Button .foo").ToList(); @@ -165,7 +165,7 @@ namespace Avalonia.Xaml.Base.UnitTest.Parsers new SelectorGrammar.ISyntax[] { new SelectorGrammar.OfTypeSyntax { TypeName = "Button" }, - new SelectorGrammar.DescendentSyntax { }, + new SelectorGrammar.DescendantSyntax { }, new SelectorGrammar.ClassSyntax { Class = "foo" }, }, result); diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs b/tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs index 38491f9164..b4c284e7c9 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs +++ b/tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs @@ -14,23 +14,23 @@ using Xunit; namespace Avalonia.Styling.UnitTests { - public class SelectorTests_Descendent + public class SelectorTests_Descendant { [Fact] - public void Descendent_Matches_Control_When_It_Is_Child_OfType() + public void Descendant_Matches_Control_When_It_Is_Child_OfType() { var parent = new TestLogical1(); var child = new TestLogical2(); child.LogicalParent = parent; - var selector = default(Selector).OfType().Descendent().OfType(); + var selector = default(Selector).OfType().Descendant().OfType(); Assert.True(selector.Match(child).ImmediateResult); } [Fact] - public void Descendent_Matches_Control_When_It_Is_Descendent_OfType() + public void Descendant_Matches_Control_When_It_Is_Descendant_OfType() { var grandparent = new TestLogical1(); var parent = new TestLogical2(); @@ -39,13 +39,13 @@ namespace Avalonia.Styling.UnitTests parent.LogicalParent = grandparent; child.LogicalParent = parent; - var selector = default(Selector).OfType().Descendent().OfType(); + var selector = default(Selector).OfType().Descendant().OfType(); Assert.True(selector.Match(child).ImmediateResult); } [Fact] - public async Task Descendent_Matches_Control_When_It_Is_Descendent_OfType_And_Class() + public async Task Descendant_Matches_Control_When_It_Is_Descendant_OfType_And_Class() { var grandparent = new TestLogical1(); var parent = new TestLogical2(); @@ -55,14 +55,14 @@ namespace Avalonia.Styling.UnitTests parent.LogicalParent = grandparent; child.LogicalParent = parent; - var selector = default(Selector).OfType().Class("foo").Descendent().OfType(); + var selector = default(Selector).OfType().Class("foo").Descendant().OfType(); var activator = selector.Match(child).ObservableResult; Assert.True(await activator.Take(1)); } [Fact] - public async Task Descendent_Doesnt_Match_Control_When_It_Is_Descendent_OfType_But_Wrong_Class() + public async Task Descendant_Doesnt_Match_Control_When_It_Is_Descendant_OfType_But_Wrong_Class() { var grandparent = new TestLogical1(); var parent = new TestLogical2(); @@ -73,14 +73,14 @@ namespace Avalonia.Styling.UnitTests parent.Classes.Add("foo"); child.LogicalParent = parent; - var selector = default(Selector).OfType().Class("foo").Descendent().OfType(); + var selector = default(Selector).OfType().Class("foo").Descendant().OfType(); var activator = selector.Match(child).ObservableResult; Assert.False(await activator.Take(1)); } [Fact] - public async Task Descendent_Matches_Any_Ancestor() + public async Task Descendant_Matches_Any_Ancestor() { var grandparent = new TestLogical1(); var parent = new TestLogical1(); @@ -89,7 +89,7 @@ namespace Avalonia.Styling.UnitTests parent.LogicalParent = grandparent; child.LogicalParent = parent; - var selector = default(Selector).OfType().Class("foo").Descendent().OfType(); + var selector = default(Selector).OfType().Class("foo").Descendant().OfType(); var activator = selector.Match(child).ObservableResult; Assert.False(await activator.Take(1)); @@ -104,9 +104,9 @@ namespace Avalonia.Styling.UnitTests } [Fact] - public void Descendent_Selector_Should_Have_Correct_String_Representation() + public void Descendant_Selector_Should_Have_Correct_String_Representation() { - var selector = default(Selector).OfType().Class("foo").Descendent().OfType(); + var selector = default(Selector).OfType().Class("foo").Descendant().OfType(); Assert.Equal("TestLogical1.foo TestLogical3", selector.ToString()); } diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Multiple.cs b/tests/Avalonia.Styling.UnitTests/SelectorTests_Multiple.cs index 5a06734819..067b08b296 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Multiple.cs +++ b/tests/Avalonia.Styling.UnitTests/SelectorTests_Multiple.cs @@ -91,11 +91,11 @@ namespace Avalonia.Styling.UnitTests } [Fact] - public void TargetType_Descendent() + public void TargetType_Descendant() { var selector = default(Selector) .OfType