Browse Source

Merge branch 'master' into RemoveNugetWorkaround

pull/983/head
Wiesław Šoltés 9 years ago
committed by GitHub
parent
commit
2e590d84a1
  1. 1
      Avalonia.sln.DotSettings
  2. 1
      appveyor.yml
  3. 37
      build.cake
  4. 14
      parameters.cake
  5. 1
      samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj
  6. 2
      samples/interop/Direct3DInteropSample/MainWindow.cs
  7. 1
      samples/interop/GtkInteropDemo/GtkInteropDemo.csproj
  8. 1
      samples/interop/WindowsInteropTest/WindowsInteropTest.csproj
  9. 11
      src/Avalonia.Base/AvaloniaObject.cs
  10. 25
      src/Avalonia.Base/Collections/AvaloniaDictionary.cs
  11. 123
      src/Avalonia.Base/Collections/AvaloniaListExtensions.cs
  12. 2
      src/Avalonia.Base/PriorityValue.cs
  13. 24
      src/Avalonia.Controls/AppBuilderBase.cs
  14. 2
      src/Avalonia.Controls/Button.cs
  15. 4
      src/Avalonia.Controls/Control.cs
  16. 12
      src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs
  17. 2
      src/Avalonia.Controls/ItemsControl.cs
  18. 4
      src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
  19. 7
      src/Avalonia.Controls/Primitives/PopupRoot.cs
  20. 4
      src/Avalonia.Controls/Templates/TemplateExtensions.cs
  21. 7
      src/Avalonia.Controls/TextBox.cs
  22. 37
      src/Avalonia.Controls/TopLevel.cs
  23. 43
      src/Avalonia.Controls/Window.cs
  24. 34
      src/Avalonia.Controls/WindowBase.cs
  25. 4
      src/Avalonia.DesignerSupport/DesignerAssist.cs
  26. 1
      src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
  27. 3
      src/Avalonia.Diagnostics/DevTools.xaml.cs
  28. 3
      src/Avalonia.Diagnostics/ViewModels/ControlDetailsViewModel.cs
  29. 74
      src/Avalonia.Diagnostics/ViewModels/DevToolsViewModel.cs
  30. 4
      src/Avalonia.Diagnostics/ViewModels/LogicalTreeNode.cs
  31. 11
      src/Avalonia.Diagnostics/ViewModels/PropertyDetails.cs
  32. 10
      src/Avalonia.Diagnostics/ViewModels/TreeNode.cs
  33. 24
      src/Avalonia.Diagnostics/ViewModels/TreePageViewModel.cs
  34. 32
      src/Avalonia.Diagnostics/ViewModels/ViewModelBase.cs
  35. 7
      src/Avalonia.Diagnostics/ViewModels/VisualTreeNode.cs
  36. 20
      src/Avalonia.Diagnostics/Views/ControlDetailsView.cs
  37. 30
      src/Avalonia.Diagnostics/Views/PropertyChangedExtenions.cs
  38. 8
      src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs
  39. 3
      src/Avalonia.Input/IKeyboardDevice.cs
  40. 46
      src/Avalonia.Input/Navigation/DirectionalNavigation.cs
  41. 6
      src/Avalonia.Input/Navigation/FocusExtensions.cs
  42. 46
      src/Avalonia.Input/Navigation/TabNavigation.cs
  43. 9
      src/Avalonia.Layout/Layoutable.cs
  44. 2
      src/Avalonia.Styling/Controls/NameScope.cs
  45. 6
      src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs
  46. 14
      src/Avalonia.Styling/Styling/DescendentSelector.cs
  47. 6
      src/Avalonia.Styling/Styling/Selectors.cs
  48. 2
      src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs
  49. 2
      src/Avalonia.Visuals/Rendering/ZIndexComparer.cs
  50. 6
      src/Avalonia.Visuals/Visual.cs
  51. 16
      src/Avalonia.Visuals/VisualTree/VisualExtensions.cs
  52. 1
      src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs
  53. 1
      src/Gtk/Avalonia.Gtk3/SystemDialogs.cs
  54. 10
      src/Markup/Avalonia.Markup.Xaml/Parsers/SelectorGrammar.cs
  55. 8
      src/Markup/Avalonia.Markup.Xaml/Parsers/SelectorParser.cs
  56. 4
      src/Markup/Avalonia.Markup/Data/Plugins/TaskStreamPlugin.cs
  57. 6
      src/Shared/SharedAssemblyInfo.cs
  58. 4
      src/Windows/Avalonia.Win32/Embedding/WinFormsAvaloniaControlHost.cs
  59. 134
      tests/Avalonia.Base.UnitTests/Collections/AvaloniaListExtenionsTests.cs
  60. 2
      tests/Avalonia.Controls.UnitTests/ListBoxTests.cs
  61. 8
      tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs
  62. 2
      tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
  63. 4
      tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests.cs
  64. 2
      tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
  65. 77
      tests/Avalonia.Controls.UnitTests/WindowTests.cs
  66. 2
      tests/Avalonia.Interactivity.UnitTests/InteractiveTests.cs
  67. 55
      tests/Avalonia.Layout.UnitTests/MeasureTests.cs
  68. 4
      tests/Avalonia.Markup.Xaml.UnitTests/Parsers/SelectorGrammarTests.cs
  69. 26
      tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs
  70. 4
      tests/Avalonia.Styling.UnitTests/SelectorTests_Multiple.cs
  71. 2
      tests/Avalonia.Visuals.UnitTests/VisualTests.cs

1
Avalonia.sln.DotSettings

@ -1,4 +1,5 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=3E53A01A_002DB331_002D47F3_002DB828_002D4A5717E77A24_002Fd_003Aglass/@EntryIndexedValue">ExplicitlyExcluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=6417B24E_002D49C2_002D4985_002D8DB2_002D3AB9D898EC91/@EntryIndexedValue">ExplicitlyExcluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=6417B24E_002D49C2_002D4985_002D8DB2_002D3AB9D898EC91/@EntryIndexedValue">ExplicitlyExcluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=E3A1060B_002D50D0_002D44E8_002D88B6_002DF44EF2E5BD72_002Ff_003Ahtml_002Ehtm/@EntryIndexedValue">ExplicitlyExcluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=E3A1060B_002D50D0_002D44E8_002D88B6_002DF44EF2E5BD72_002Ff_003Ahtml_002Ehtm/@EntryIndexedValue">ExplicitlyExcluded</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantUsingDirective/@EntryIndexedValue">HINT</s:String> <s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantUsingDirective/@EntryIndexedValue">HINT</s:String>

1
appveyor.yml

@ -33,6 +33,7 @@ test: off
artifacts: artifacts:
- path: artifacts\nuget\*.nupkg - path: artifacts\nuget\*.nupkg
- path: artifacts\zip\*.zip - path: artifacts\zip\*.zip
- path: artifacts\inspectcode.xml
cache: cache:
- gtk-sharp-2.12.26.msi - gtk-sharp-2.12.26.msi
- dotnet-1.0.1.exe - dotnet-1.0.1.exe

37
build.cake

@ -105,7 +105,7 @@ Task("Restore-NuGet-Packages")
.Does(() => .Does(() =>
{ {
var maxRetryCount = 5; var maxRetryCount = 5;
var toolTimeout = 1d; var toolTimeout = 2d;
Policy Policy
.Handle<Exception>() .Handle<Exception>()
.Retry(maxRetryCount, (exception, retryCount, context) => { .Retry(maxRetryCount, (exception, retryCount, context) => {
@ -287,6 +287,7 @@ Task("Zip-Files")
Task("Create-NuGet-Packages") Task("Create-NuGet-Packages")
.IsDependentOn("Run-Unit-Tests") .IsDependentOn("Run-Unit-Tests")
.IsDependentOn("Inspect")
.Does(() => .Does(() =>
{ {
foreach(var nuspec in packages.NuspecNuGetSettings) foreach(var nuspec in packages.NuspecNuGetSettings)
@ -334,7 +335,6 @@ Task("Publish-NuGet")
.WithCriteria(() => !parameters.IsLocalBuild) .WithCriteria(() => !parameters.IsLocalBuild)
.WithCriteria(() => !parameters.IsPullRequest) .WithCriteria(() => !parameters.IsPullRequest)
.WithCriteria(() => parameters.IsMainRepo) .WithCriteria(() => parameters.IsMainRepo)
.WithCriteria(() => parameters.IsMasterBranch)
.WithCriteria(() => parameters.IsNuGetRelease) .WithCriteria(() => parameters.IsNuGetRelease)
.Does(() => .Does(() =>
{ {
@ -363,6 +363,39 @@ Task("Publish-NuGet")
Information("Publish-NuGet Task failed, but continuing with next Task..."); 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 // TARGETS
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////

14
parameters.cake

@ -75,17 +75,25 @@ public class Parameters
IsReleasable = StringComparer.OrdinalIgnoreCase.Equals(ReleasePlatform, Platform) IsReleasable = StringComparer.OrdinalIgnoreCase.Equals(ReleasePlatform, Platform)
&& StringComparer.OrdinalIgnoreCase.Equals(ReleaseConfiguration, Configuration); && StringComparer.OrdinalIgnoreCase.Equals(ReleaseConfiguration, Configuration);
IsMyGetRelease = !IsTagged && IsReleasable; IsMyGetRelease = !IsTagged && IsReleasable;
IsNuGetRelease = IsTagged && IsReleasable;
// VERSION // VERSION
Version = context.Argument("force-nuget-version", context.ParseAssemblyInfo(AssemblyInfoPath).AssemblyVersion); Version = context.Argument("force-nuget-version", context.ParseAssemblyInfo(AssemblyInfoPath).AssemblyVersion);
if (IsRunningOnAppVeyor) if (IsRunningOnAppVeyor)
{ {
string tagVersion = null;
if (IsTagged) if (IsTagged)
{ {
// Use Tag Name as version var tag = buildSystem.AppVeyor.Environment.Repository.Tag.Name;
Version = 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 else
{ {

1
samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj

@ -22,6 +22,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\src\Avalonia.DesignerSupport\Avalonia.DesignerSupport.csproj" /> <ProjectReference Include="..\..\..\src\Avalonia.DesignerSupport\Avalonia.DesignerSupport.csproj" />
<ProjectReference Include="..\..\..\src\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj" /> <ProjectReference Include="..\..\..\src\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj" />
<ProjectReference Include="..\..\..\src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj" />
<ProjectReference Include="..\..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" /> <ProjectReference Include="..\..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
<ProjectReference Include="..\..\..\src\Windows\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj" /> <ProjectReference Include="..\..\..\src\Windows\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj" />
<ProjectReference Include="..\..\..\src\Windows\Avalonia.Win32\Avalonia.Win32.csproj" /> <ProjectReference Include="..\..\..\src\Windows\Avalonia.Win32\Avalonia.Win32.csproj" />

2
samples/interop/Direct3DInteropSample/MainWindow.cs

@ -58,7 +58,7 @@ namespace Direct3DInteropSample
new ModeDescription((int)ClientSize.Width, (int)ClientSize.Height, new ModeDescription((int)ClientSize.Width, (int)ClientSize.Height,
new Rational(60, 1), Format.R8G8B8A8_UNorm), new Rational(60, 1), Format.R8G8B8A8_UNorm),
IsWindowed = true, IsWindowed = true,
OutputHandle = PlatformImpl.Handle.Handle, OutputHandle = PlatformImpl?.Handle.Handle ?? IntPtr.Zero,
SampleDescription = new SampleDescription(1, 0), SampleDescription = new SampleDescription(1, 0),
SwapEffect = SwapEffect.Discard, SwapEffect = SwapEffect.Discard,
Usage = Usage.RenderTargetOutput Usage = Usage.RenderTargetOutput

1
samples/interop/GtkInteropDemo/GtkInteropDemo.csproj

@ -149,5 +149,6 @@
<Name>ControlCatalog</Name> <Name>ControlCatalog</Name>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<Import Project="..\..\..\build\Rx.props" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project> </Project>

1
samples/interop/WindowsInteropTest/WindowsInteropTest.csproj

@ -179,5 +179,6 @@
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
</ItemGroup> </ItemGroup>
<Import Project="..\..\..\build\Rx.props" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project> </Project>

11
src/Avalonia.Base/AvaloniaObject.cs

@ -622,14 +622,9 @@ namespace Avalonia
/// <returns>The default value.</returns> /// <returns>The default value.</returns>
private object GetDefaultValue(AvaloniaProperty property) private object GetDefaultValue(AvaloniaProperty property)
{ {
if (property.Inherits && _inheritanceParent != null) if (property.Inherits && _inheritanceParent is AvaloniaObject aobj)
{ return aobj.GetValueInternal(property);
return (_inheritanceParent as AvaloniaObject).GetValueInternal(property); return ((IStyledPropertyAccessor) property).GetDefaultValue(GetType());
}
else
{
return ((IStyledPropertyAccessor)property).GetDefaultValue(GetType());
}
} }
/// <summary> /// <summary>

25
src/Avalonia.Base/Collections/AvaloniaDictionary.cs

@ -103,11 +103,9 @@ namespace Avalonia.Collections
_inner = new Dictionary<TKey, TValue>(); _inner = new Dictionary<TKey, TValue>();
if (PropertyChanged != null) PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count"));
{ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[]"));
PropertyChanged(this, new PropertyChangedEventArgs("Count"));
PropertyChanged(this, new PropertyChangedEventArgs($"Item[]"));
}
if (CollectionChanged != null) if (CollectionChanged != null)
{ {
@ -144,12 +142,9 @@ namespace Avalonia.Collections
if (_inner.TryGetValue(key, out value)) if (_inner.TryGetValue(key, out value))
{ {
if (PropertyChanged != null) PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count"));
{ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]"));
PropertyChanged(this, new PropertyChangedEventArgs("Count"));
PropertyChanged(this, new PropertyChangedEventArgs($"Item[{key}]"));
}
if (CollectionChanged != null) if (CollectionChanged != null)
{ {
var e = new NotifyCollectionChangedEventArgs( var e = new NotifyCollectionChangedEventArgs(
@ -199,11 +194,9 @@ namespace Avalonia.Collections
private void NotifyAdd(TKey key, TValue value) private void NotifyAdd(TKey key, TValue value)
{ {
if (PropertyChanged != null) PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count"));
{ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]"));
PropertyChanged(this, new PropertyChangedEventArgs("Count"));
PropertyChanged(this, new PropertyChangedEventArgs($"Item[{key}]"));
}
if (CollectionChanged != null) if (CollectionChanged != null)
{ {

123
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. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.ComponentModel; using System.ComponentModel;
@ -59,7 +60,8 @@ namespace Avalonia.Collections
/// the index in the collection and the item. /// the index in the collection and the item.
/// </param> /// </param>
/// <param name="reset"> /// <param name="reset">
/// An action called when the collection is reset. /// An action called when the collection is reset. This will be followed by calls to
/// <paramref name="added"/> for each item present in the collection after the reset.
/// </param> /// </param>
/// <returns>A disposable used to terminate the subscription.</returns> /// <returns>A disposable used to terminate the subscription.</returns>
public static IDisposable ForEachItem<T>( public static IDisposable ForEachItem<T>(
@ -68,112 +70,38 @@ namespace Avalonia.Collections
Action<int, T> removed, Action<int, T> removed,
Action reset) Action reset)
{ {
int index; void Add(int index, IList items)
NotifyCollectionChangedEventHandler handler = (_, e) =>
{ {
switch (e.Action) foreach (T item in items)
{ {
case NotifyCollectionChangedAction.Add: added(index++, item);
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;
} }
}; }
index = 0; void Remove(int index, IList items)
foreach (T i in collection)
{ {
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);
}
/// <summary>
/// Invokes an action for each item in a collection and subsequently each item added or
/// removed from the collection.
/// </summary>
/// <typeparam name="T">The type of the collection items.</typeparam>
/// <param name="collection">The collection.</param>
/// <param name="added">
/// 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.
/// </param>
/// <param name="removed">
/// 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.
/// </param>
/// <param name="reset">
/// An action called when the collection is reset.
/// </param>
/// <returns>A disposable used to terminate the subscription.</returns>
public static IDisposable ForEachItem<T>(
this IAvaloniaReadOnlyList<T> collection,
Action<int, IEnumerable<T>> added,
Action<int, IEnumerable<T>> removed,
Action reset)
{
NotifyCollectionChangedEventHandler handler = (_, e) => NotifyCollectionChangedEventHandler handler = (_, e) =>
{ {
switch (e.Action) switch (e.Action)
{ {
case NotifyCollectionChangedAction.Add: case NotifyCollectionChangedAction.Add:
added(e.NewStartingIndex, e.NewItems.Cast<T>()); Add(e.NewStartingIndex, e.NewItems);
break; break;
case NotifyCollectionChangedAction.Move:
case NotifyCollectionChangedAction.Replace: case NotifyCollectionChangedAction.Replace:
removed(e.OldStartingIndex, e.OldItems.Cast<T>()); Remove(e.OldStartingIndex, e.OldItems);
added(e.NewStartingIndex, e.NewItems.Cast<T>()); Add(e.NewStartingIndex, e.NewItems);
break; break;
case NotifyCollectionChangedAction.Remove: case NotifyCollectionChangedAction.Remove:
removed(e.OldStartingIndex, e.OldItems.Cast<T>()); Remove(e.OldStartingIndex, e.OldItems);
break; break;
case NotifyCollectionChangedAction.Reset: case NotifyCollectionChangedAction.Reset:
@ -184,16 +112,31 @@ namespace Avalonia.Collections
} }
reset(); reset();
Add(0, (IList)collection);
break; break;
} }
}; };
added(0, collection); Add(0, (IList)collection);
collection.CollectionChanged += handler; collection.CollectionChanged += handler;
return Disposable.Create(() => collection.CollectionChanged -= handler); return Disposable.Create(() => collection.CollectionChanged -= handler);
} }
public static IAvaloniaReadOnlyList<TDerived> CreateDerivedList<TSource, TDerived>(
this IAvaloniaReadOnlyList<TSource> collection,
Func<TSource, TDerived> select)
{
var result = new AvaloniaList<TDerived>();
collection.ForEachItem(
(i, item) => result.Insert(i, select(item)),
(i, item) => result.RemoveAt(i),
() => result.Clear());
return result;
}
/// <summary> /// <summary>
/// Listens for property changed events from all items in a collection. /// Listens for property changed events from all items in a collection.
/// </summary> /// </summary>

2
src/Avalonia.Base/PriorityValue.cs

@ -285,7 +285,7 @@ namespace Avalonia
Property.Name, Property.Name,
_valueType, _valueType,
value, value,
value.GetType()); value?.GetType());
} }
} }
} }

24
src/Avalonia.Controls/AppBuilderBase.cs

@ -55,8 +55,7 @@ namespace Avalonia.Controls
public Action<TAppBuilder> AfterSetupCallback { get; private set; } = builder => { }; public Action<TAppBuilder> AfterSetupCallback { get; private set; } = builder => { };
/// <summary> /// <summary>
/// Gets or sets a method to call before <see cref="Start{TMainWindow}"/> is called on the /// Gets or sets a method to call before Startis called on the <see cref="Application"/>.
/// <see cref="Application"/>.
/// </summary> /// </summary>
public Action<TAppBuilder> BeforeStartCallback { get; private set; } = builder => { }; public Action<TAppBuilder> BeforeStartCallback { get; private set; } = builder => { };
@ -94,8 +93,7 @@ namespace Avalonia.Controls
protected TAppBuilder Self => (TAppBuilder) this; protected TAppBuilder Self => (TAppBuilder) this;
/// <summary> /// <summary>
/// Registers a callback to call before <see cref="Start{TMainWindow}"/> is called on the /// Registers a callback to call before Start is called on the <see cref="Application"/>.
/// <see cref="Application"/>.
/// </summary> /// </summary>
/// <param name="callback">The callback.</param> /// <param name="callback">The callback.</param>
/// <returns>An <typeparamref name="TAppBuilder"/> instance.</returns> /// <returns>An <typeparamref name="TAppBuilder"/> instance.</returns>
@ -129,6 +127,24 @@ namespace Avalonia.Controls
Instance.Run(window); Instance.Run(window);
} }
/// <summary>
/// Starts the application with the provided instance of <typeparamref name="TMainWindow"/>.
/// </summary>
/// <typeparam name="TMainWindow">The window type.</typeparam>
/// <param name="mainWindow">Instance of type TMainWindow to use when starting the app</param>
/// <param name="dataContextProvider">A delegate that will be called to create a data context for the window (optional).</param>
public void Start<TMainWindow>(TMainWindow mainWindow, Func<object> dataContextProvider = null)
where TMainWindow : Window
{
Setup();
BeforeStartCallback(Self);
if (dataContextProvider != null)
mainWindow.DataContext = dataContextProvider();
mainWindow.Show();
Instance.Run(mainWindow);
}
/// <summary> /// <summary>
/// Sets up the platform-specific services for the application, but does not run it. /// Sets up the platform-specific services for the application, but does not run it.
/// </summary> /// </summary>

2
src/Avalonia.Controls/Button.cs

@ -275,7 +275,7 @@ namespace Avalonia.Controls
{ {
var button = e.Sender as Button; var button = e.Sender as Button;
var isDefault = (bool)e.NewValue; var isDefault = (bool)e.NewValue;
var inputRoot = button.VisualRoot as IInputElement; var inputRoot = button?.VisualRoot as IInputElement;
if (inputRoot != null) if (inputRoot != null)
{ {

4
src/Avalonia.Controls/Control.cs

@ -469,7 +469,7 @@ namespace Avalonia.Controls
{ {
if (!IsInitialized) if (!IsInitialized)
{ {
foreach (var i in this.GetSelfAndVisualDescendents()) foreach (var i in this.GetSelfAndVisualDescendants())
{ {
var c = i as IControl; var c = i as IControl;
@ -645,7 +645,7 @@ namespace Avalonia.Controls
if (_focusAdorner != null) if (_focusAdorner != null)
{ {
var adornerLayer = _focusAdorner.Parent as Panel; var adornerLayer = (IPanel)_focusAdorner.Parent;
adornerLayer.Children.Remove(_focusAdorner); adornerLayer.Children.Remove(_focusAdorner);
_focusAdorner = null; _focusAdorner = null;
} }

12
src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs

@ -4,6 +4,7 @@ using Avalonia.Input;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Styling; using Avalonia.Styling;
using JetBrains.Annotations;
namespace Avalonia.Controls.Embedding namespace Avalonia.Controls.Embedding
{ {
@ -18,6 +19,7 @@ namespace Avalonia.Controls.Embedding
{ {
} }
[CanBeNull]
public new IEmbeddableWindowImpl PlatformImpl => (IEmbeddableWindowImpl) base.PlatformImpl; public new IEmbeddableWindowImpl PlatformImpl => (IEmbeddableWindowImpl) base.PlatformImpl;
public void Prepare() public void Prepare()
@ -39,8 +41,9 @@ namespace Avalonia.Controls.Embedding
protected override Size MeasureOverride(Size availableSize) protected override Size MeasureOverride(Size availableSize)
{ {
base.MeasureOverride(PlatformImpl.ClientSize); var cs = PlatformImpl?.ClientSize ?? default(Size);
return PlatformImpl.ClientSize; base.MeasureOverride(cs);
return cs;
} }
private readonly NameScope _nameScope = new NameScope(); private readonly NameScope _nameScope = new NameScope();
@ -63,9 +66,6 @@ namespace Avalonia.Controls.Embedding
public void Unregister(string name) => _nameScope.Unregister(name); public void Unregister(string name) => _nameScope.Unregister(name);
Type IStyleable.StyleKey => typeof(EmbeddableControlRoot); Type IStyleable.StyleKey => typeof(EmbeddableControlRoot);
public void Dispose() public void Dispose() => PlatformImpl?.Dispose();
{
PlatformImpl.Dispose();
}
} }
} }

2
src/Avalonia.Controls/ItemsControl.cs

@ -354,7 +354,7 @@ namespace Avalonia.Controls
} }
var collection = sender as ICollection; var collection = sender as ICollection;
PseudoClasses.Set(":empty", collection.Count == 0); PseudoClasses.Set(":empty", collection == null || collection.Count == 0);
} }
/// <summary> /// <summary>

4
src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs

@ -111,7 +111,7 @@ namespace Avalonia.Controls.Presenters
/// <param name="target">The target visual.</param> /// <param name="target">The target visual.</param>
/// <param name="targetRect">The portion of the target visual to bring into view.</param> /// <param name="targetRect">The portion of the target visual to bring into view.</param>
/// <returns>True if the scroll offset was changed; otherwise false.</returns> /// <returns>True if the scroll offset was changed; otherwise false.</returns>
public bool BringDescendentIntoView(IVisual target, Rect targetRect) public bool BringDescendantIntoView(IVisual target, Rect targetRect)
{ {
if (Child == null) if (Child == null)
{ {
@ -262,7 +262,7 @@ namespace Avalonia.Controls.Presenters
private void BringIntoViewRequested(object sender, RequestBringIntoViewEventArgs e) 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) private void ChildChanged(AvaloniaPropertyChangedEventArgs e)

7
src/Avalonia.Controls/Primitives/PopupRoot.cs

@ -9,6 +9,7 @@ using Avalonia.Layout;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using JetBrains.Annotations;
namespace Avalonia.Controls.Primitives namespace Avalonia.Controls.Primitives
{ {
@ -49,6 +50,7 @@ namespace Avalonia.Controls.Primitives
/// <summary> /// <summary>
/// Gets the platform-specific window implementation. /// Gets the platform-specific window implementation.
/// </summary> /// </summary>
[CanBeNull]
public new IPopupImpl PlatformImpl => (IPopupImpl)base.PlatformImpl; public new IPopupImpl PlatformImpl => (IPopupImpl)base.PlatformImpl;
/// <summary> /// <summary>
@ -65,10 +67,7 @@ namespace Avalonia.Controls.Primitives
IVisual IHostedVisualTreeRoot.Host => Parent; IVisual IHostedVisualTreeRoot.Host => Parent;
/// <inheritdoc/> /// <inheritdoc/>
public void Dispose() public void Dispose() => PlatformImpl?.Dispose();
{
this.PlatformImpl.Dispose();
}
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnTemplateApplied(TemplateAppliedEventArgs e) protected override void OnTemplateApplied(TemplateAppliedEventArgs e)

4
src/Avalonia.Controls/Templates/TemplateExtensions.cs

@ -31,9 +31,9 @@ namespace Avalonia.Controls.Templates
if (child.TemplatedParent != null) if (child.TemplatedParent != null)
{ {
foreach (var descendent in GetTemplateChildren(child, templatedParent)) foreach (var descendant in GetTemplateChildren(child, templatedParent))
{ {
yield return descendent; yield return descendant;
} }
} }
} }

7
src/Avalonia.Controls/TextBox.cs

@ -720,7 +720,7 @@ namespace Avalonia.Controls
if (pos < text.Length) if (pos < text.Length)
{ {
--pos; --pos;
if (pos > 0 && Text[pos - 1] == '\r' && Text[pos] == '\n') if (pos > 0 && text[pos - 1] == '\r' && text[pos] == '\n')
{ {
--pos; --pos;
} }
@ -771,6 +771,9 @@ namespace Avalonia.Controls
private string GetSelection() private string GetSelection()
{ {
var text = Text;
if (string.IsNullOrEmpty(text))
return "";
var selectionStart = SelectionStart; var selectionStart = SelectionStart;
var selectionEnd = SelectionEnd; var selectionEnd = SelectionEnd;
var start = Math.Min(selectionStart, selectionEnd); var start = Math.Min(selectionStart, selectionEnd);
@ -779,7 +782,7 @@ namespace Avalonia.Controls
{ {
return ""; return "";
} }
return Text.Substring(start, end - start); return text.Substring(start, end - start);
} }
private int GetLine(int caretIndex, IList<FormattedTextLine> lines) private int GetLine(int caretIndex, IList<FormattedTextLine> lines)

37
src/Avalonia.Controls/TopLevel.cs

@ -14,6 +14,7 @@ using Avalonia.Platform;
using Avalonia.Rendering; using Avalonia.Rendering;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using JetBrains.Annotations;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
@ -92,25 +93,25 @@ namespace Avalonia.Controls
var rendererFactory = TryGetService<IRendererFactory>(dependencyResolver); var rendererFactory = TryGetService<IRendererFactory>(dependencyResolver);
Renderer = rendererFactory?.CreateRenderer(this, renderLoop); Renderer = rendererFactory?.CreateRenderer(this, renderLoop);
PlatformImpl.SetInputRoot(this); impl.SetInputRoot(this);
PlatformImpl.Closed = HandleClosed; impl.Closed = HandleClosed;
PlatformImpl.Input = HandleInput; impl.Input = HandleInput;
PlatformImpl.Paint = HandlePaint; impl.Paint = HandlePaint;
PlatformImpl.Resized = HandleResized; impl.Resized = HandleResized;
PlatformImpl.ScalingChanged = HandleScalingChanged; impl.ScalingChanged = HandleScalingChanged;
_keyboardNavigationHandler?.SetOwner(this); _keyboardNavigationHandler?.SetOwner(this);
_accessKeyHandler?.SetOwner(this); _accessKeyHandler?.SetOwner(this);
styler?.ApplyStyles(this); styler?.ApplyStyles(this);
ClientSize = PlatformImpl.ClientSize; ClientSize = impl.ClientSize;
this.GetObservable(PointerOverElementProperty) this.GetObservable(PointerOverElementProperty)
.Select( .Select(
x => (x as InputElement)?.GetObservable(CursorProperty) ?? Observable.Empty<Cursor>()) x => (x as InputElement)?.GetObservable(CursorProperty) ?? Observable.Empty<Cursor>())
.Switch().Subscribe(cursor => PlatformImpl.SetCursor(cursor?.PlatformCursor)); .Switch().Subscribe(cursor => PlatformImpl?.SetCursor(cursor?.PlatformCursor));
if (_applicationLifecycle != null) if (_applicationLifecycle != null)
{ {
@ -135,10 +136,8 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets the platform-specific window implementation. /// Gets the platform-specific window implementation.
/// </summary> /// </summary>
public ITopLevelImpl PlatformImpl [CanBeNull]
{ public ITopLevelImpl PlatformImpl { get; private set; }
get;
}
/// <summary> /// <summary>
/// Gets the renderer for the window. /// Gets the renderer for the window.
@ -177,7 +176,7 @@ namespace Avalonia.Controls
Size ILayoutRoot.MaxClientSize => Size.Infinity; Size ILayoutRoot.MaxClientSize => Size.Infinity;
/// <inheritdoc/> /// <inheritdoc/>
double ILayoutRoot.LayoutScaling => PlatformImpl.Scaling; double ILayoutRoot.LayoutScaling => PlatformImpl?.Scaling ?? 1;
IStyleHost IStyleHost.StylingParent IStyleHost IStyleHost.StylingParent
{ {
@ -189,25 +188,27 @@ namespace Avalonia.Controls
/// <inheritdoc/> /// <inheritdoc/>
protected virtual IRenderTarget CreateRenderTarget() 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); return _renderInterface.CreateRenderTarget(PlatformImpl.Surfaces);
} }
/// <inheritdoc/> /// <inheritdoc/>
void IRenderRoot.Invalidate(Rect rect) void IRenderRoot.Invalidate(Rect rect)
{ {
PlatformImpl.Invalidate(rect); PlatformImpl?.Invalidate(rect);
} }
/// <inheritdoc/> /// <inheritdoc/>
Point IRenderRoot.PointToClient(Point p) Point IRenderRoot.PointToClient(Point p)
{ {
return PlatformImpl.PointToClient(p); return PlatformImpl?.PointToClient(p) ?? default(Point);
} }
/// <inheritdoc/> /// <inheritdoc/>
Point IRenderRoot.PointToScreen(Point p) Point IRenderRoot.PointToScreen(Point p)
{ {
return PlatformImpl.PointToScreen(p); return PlatformImpl?.PointToScreen(p) ?? default(Point);
} }
/// <summary> /// <summary>
@ -224,6 +225,8 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
protected virtual void HandleClosed() protected virtual void HandleClosed()
{ {
PlatformImpl = null;
Closed?.Invoke(this, EventArgs.Empty); Closed?.Invoke(this, EventArgs.Empty);
Renderer?.Dispose(); Renderer?.Dispose();
Renderer = null; Renderer = null;
@ -250,7 +253,7 @@ namespace Avalonia.Controls
/// <param name="scaling">The window scaling.</param> /// <param name="scaling">The window scaling.</param>
protected virtual void HandleScalingChanged(double scaling) protected virtual void HandleScalingChanged(double scaling)
{ {
foreach (ILayoutable control in this.GetSelfAndVisualDescendents()) foreach (ILayoutable control in this.GetSelfAndVisualDescendants())
{ {
control.InvalidateMeasure(); control.InvalidateMeasure();
} }

43
src/Avalonia.Controls/Window.cs

@ -12,6 +12,7 @@ using Avalonia.Platform;
using Avalonia.Styling; using Avalonia.Styling;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using JetBrains.Annotations;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
@ -46,12 +47,12 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
public class Window : WindowBase, IStyleable, IFocusScope, ILayoutRoot, INameScope public class Window : WindowBase, IStyleable, IFocusScope, ILayoutRoot, INameScope
{ {
private static IList<Window> s_windows = new List<Window>(); private static List<Window> s_windows = new List<Window>();
/// <summary> /// <summary>
/// Retrieves an enumeration of all Windows in the currently running application. /// Retrieves an enumeration of all Windows in the currently running application.
/// </summary> /// </summary>
public static IList<Window> OpenWindows => s_windows; public static IReadOnlyList<Window> OpenWindows => s_windows;
/// <summary> /// <summary>
/// Defines the <see cref="SizeToContent"/> property. /// Defines the <see cref="SizeToContent"/> property.
@ -87,11 +88,11 @@ namespace Avalonia.Controls
static Window() static Window()
{ {
BackgroundProperty.OverrideDefaultValue(typeof(Window), Brushes.White); BackgroundProperty.OverrideDefaultValue(typeof(Window), Brushes.White);
TitleProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl.SetTitle((string)e.NewValue)); TitleProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl?.SetTitle((string)e.NewValue));
HasSystemDecorationsProperty.Changed.AddClassHandler<Window>( HasSystemDecorationsProperty.Changed.AddClassHandler<Window>(
(s, e) => s.PlatformImpl.SetSystemDecorations((bool) e.NewValue)); (s, e) => s.PlatformImpl?.SetSystemDecorations((bool) e.NewValue));
IconProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl.SetIcon(((WindowIcon)e.NewValue).PlatformImpl)); IconProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl?.SetIcon(((WindowIcon)e.NewValue).PlatformImpl));
} }
/// <summary> /// <summary>
@ -109,7 +110,7 @@ namespace Avalonia.Controls
public Window(IWindowImpl impl) public Window(IWindowImpl impl)
: base(impl) : base(impl)
{ {
_maxPlatformClientSize = this.PlatformImpl.MaxClientSize; _maxPlatformClientSize = PlatformImpl?.MaxClientSize ?? default(Size);
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -129,6 +130,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets the platform-specific window implementation. /// Gets the platform-specific window implementation.
/// </summary> /// </summary>
[CanBeNull]
public new IWindowImpl PlatformImpl => (IWindowImpl)base.PlatformImpl; public new IWindowImpl PlatformImpl => (IWindowImpl)base.PlatformImpl;
/// <summary> /// <summary>
@ -164,8 +166,12 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
public WindowState WindowState public WindowState WindowState
{ {
get { return this.PlatformImpl.WindowState; } get { return PlatformImpl?.WindowState ?? WindowState.Normal; }
set { this.PlatformImpl.WindowState = value; } set
{
if (PlatformImpl != null)
PlatformImpl.WindowState = value;
}
} }
/// <summary> /// <summary>
@ -189,7 +195,7 @@ namespace Avalonia.Controls
public void Close() public void Close()
{ {
s_windows.Remove(this); s_windows.Remove(this);
PlatformImpl.Dispose(); PlatformImpl?.Dispose();
IsVisible = false; IsVisible = false;
} }
@ -221,7 +227,7 @@ namespace Avalonia.Controls
{ {
using (BeginAutoSizing()) using (BeginAutoSizing())
{ {
PlatformImpl.Hide(); PlatformImpl?.Hide();
} }
IsVisible = false; IsVisible = false;
@ -232,6 +238,11 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
public override void Show() public override void Show()
{ {
if (IsVisible)
{
return;
}
s_windows.Add(this); s_windows.Add(this);
EnsureInitialized(); EnsureInitialized();
@ -240,7 +251,7 @@ namespace Avalonia.Controls
using (BeginAutoSizing()) using (BeginAutoSizing())
{ {
PlatformImpl.Show(); PlatformImpl?.Show();
} }
} }
@ -266,6 +277,11 @@ namespace Avalonia.Controls
/// </returns> /// </returns>
public Task<TResult> ShowDialog<TResult>() public Task<TResult> ShowDialog<TResult>()
{ {
if (IsVisible)
{
throw new InvalidOperationException("The window is already being shown.");
}
s_windows.Add(this); s_windows.Add(this);
EnsureInitialized(); EnsureInitialized();
@ -278,7 +294,7 @@ namespace Avalonia.Controls
var activated = affectedWindows.Where(w => w.IsActive).FirstOrDefault(); var activated = affectedWindows.Where(w => w.IsActive).FirstOrDefault();
SetIsEnabled(affectedWindows, false); SetIsEnabled(affectedWindows, false);
var modal = PlatformImpl.ShowDialog(); var modal = PlatformImpl?.ShowDialog();
var result = new TaskCompletionSource<TResult>(); var result = new TaskCompletionSource<TResult>();
Observable.FromEventPattern<EventHandler, EventArgs>( Observable.FromEventPattern<EventHandler, EventArgs>(
@ -287,7 +303,7 @@ namespace Avalonia.Controls
.Take(1) .Take(1)
.Subscribe(_ => .Subscribe(_ =>
{ {
modal.Dispose(); modal?.Dispose();
SetIsEnabled(affectedWindows, true); SetIsEnabled(affectedWindows, true);
activated?.Activate(); activated?.Activate();
result.SetResult((TResult)_dialogResult); result.SetResult((TResult)_dialogResult);
@ -354,6 +370,7 @@ namespace Avalonia.Controls
protected override void HandleClosed() protected override void HandleClosed()
{ {
IsVisible = false; IsVisible = false;
s_windows.Remove(this);
base.HandleClosed(); base.HandleClosed();
} }

34
src/Avalonia.Controls/WindowBase.cs

@ -9,6 +9,7 @@ using Avalonia.Controls.Primitives;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Platform; using Avalonia.Platform;
using JetBrains.Annotations;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
@ -43,10 +44,10 @@ namespace Avalonia.Controls
public WindowBase(IWindowBaseImpl impl, IAvaloniaDependencyResolver dependencyResolver) : base(impl, dependencyResolver) public WindowBase(IWindowBaseImpl impl, IAvaloniaDependencyResolver dependencyResolver) : base(impl, dependencyResolver)
{ {
PlatformImpl.Activated = HandleActivated; impl.Activated = HandleActivated;
PlatformImpl.Deactivated = HandleDeactivated; impl.Deactivated = HandleDeactivated;
PlatformImpl.PositionChanged = HandlePositionChanged; impl.PositionChanged = HandlePositionChanged;
this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl.Resize(x)); this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl?.Resize(x));
} }
/// <summary> /// <summary>
@ -64,6 +65,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
public event EventHandler<PointEventArgs> PositionChanged; public event EventHandler<PointEventArgs> PositionChanged;
[CanBeNull]
public new IWindowBaseImpl PlatformImpl => (IWindowBaseImpl) base.PlatformImpl; public new IWindowBaseImpl PlatformImpl => (IWindowBaseImpl) base.PlatformImpl;
/// <summary> /// <summary>
@ -80,8 +82,12 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
public Point Position public Point Position
{ {
get { return PlatformImpl.Position; } get { return PlatformImpl?.Position ?? default(Point); }
set { PlatformImpl.Position = value; } set
{
if (PlatformImpl is IWindowBaseImpl impl)
impl.Position = value;
}
} }
/// <summary> /// <summary>
@ -98,7 +104,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
public void Activate() public void Activate()
{ {
PlatformImpl.Activate(); PlatformImpl?.Activate();
} }
/// <summary> /// <summary>
@ -110,7 +116,7 @@ namespace Avalonia.Controls
try try
{ {
PlatformImpl.Hide(); PlatformImpl?.Hide();
IsVisible = false; IsVisible = false;
} }
finally finally
@ -131,7 +137,7 @@ namespace Avalonia.Controls
EnsureInitialized(); EnsureInitialized();
IsVisible = true; IsVisible = true;
LayoutManager.Instance.ExecuteInitialLayoutPass(this); LayoutManager.Instance.ExecuteInitialLayoutPass(this);
PlatformImpl.Show(); PlatformImpl?.Show();
} }
finally finally
{ {
@ -163,10 +169,10 @@ namespace Avalonia.Controls
{ {
using (BeginAutoSizing()) using (BeginAutoSizing())
{ {
PlatformImpl.Resize(finalSize); PlatformImpl?.Resize(finalSize);
} }
return base.ArrangeOverride(PlatformImpl.ClientSize); return base.ArrangeOverride(PlatformImpl?.ClientSize ?? default(Size));
} }
/// <summary> /// <summary>
@ -174,7 +180,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
protected void EnsureInitialized() protected void EnsureInitialized()
{ {
if (!this.IsInitialized) if (!IsInitialized)
{ {
var init = (ISupportInitialize)this; var init = (ISupportInitialize)this;
init.BeginInit(); init.BeginInit();
@ -268,12 +274,12 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Starts moving a window with left button being held. Should be called from left mouse button press event handler /// Starts moving a window with left button being held. Should be called from left mouse button press event handler
/// </summary> /// </summary>
public void BeginMoveDrag() => PlatformImpl.BeginMoveDrag(); public void BeginMoveDrag() => PlatformImpl?.BeginMoveDrag();
/// <summary> /// <summary>
/// Starts resizing a window. This function is used if an application has window resizing controls. /// 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 /// Should be called from left mouse button press event handler
/// </summary> /// </summary>
public void BeginResizeDrag(WindowEdge edge) => PlatformImpl.BeginResizeDrag(edge); public void BeginResizeDrag(WindowEdge edge) => PlatformImpl?.BeginResizeDrag(edge);
} }
} }

4
src/Avalonia.DesignerSupport/DesignerAssist.cs

@ -75,7 +75,7 @@ namespace Avalonia.DesignerSupport
private static void SetScalingFactor(double factor) private static void SetScalingFactor(double factor)
{ {
PlatformManager.SetDesignerScalingFactor(factor); PlatformManager.SetDesignerScalingFactor(factor);
s_currentWindow?.PlatformImpl.Resize(s_currentWindow.ClientSize); s_currentWindow?.PlatformImpl?.Resize(s_currentWindow.ClientSize);
} }
static Window s_currentWindow; static Window s_currentWindow;
@ -149,6 +149,8 @@ namespace Avalonia.DesignerSupport
s_currentWindow = window; s_currentWindow = window;
window.Show(); window.Show();
Design.ApplyDesignerProperties(window, control); Design.ApplyDesignerProperties(window, control);
// ReSharper disable once PossibleNullReferenceException
// Always not null at this point
Api.OnWindowCreated?.Invoke(window.PlatformImpl.Handle.Handle); Api.OnWindowCreated?.Invoke(window.PlatformImpl.Handle.Handle);
Api.OnResize?.Invoke(); Api.OnResize?.Invoke();
} }

1
src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj

@ -34,7 +34,6 @@
<ProjectReference Include="..\Avalonia.Input\Avalonia.Input.csproj" /> <ProjectReference Include="..\Avalonia.Input\Avalonia.Input.csproj" />
<ProjectReference Include="..\Avalonia.Interactivity\Avalonia.Interactivity.csproj" /> <ProjectReference Include="..\Avalonia.Interactivity\Avalonia.Interactivity.csproj" />
<ProjectReference Include="..\Avalonia.Layout\Avalonia.Layout.csproj" /> <ProjectReference Include="..\Avalonia.Layout\Avalonia.Layout.csproj" />
<ProjectReference Include="..\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj" />
<ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" /> <ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
<ProjectReference Include="..\Avalonia.Styling\Avalonia.Styling.csproj" /> <ProjectReference Include="..\Avalonia.Styling\Avalonia.Styling.csproj" />
<ProjectReference Include="..\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" /> <ProjectReference Include="..\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />

3
src/Avalonia.Diagnostics/DevTools.xaml.cs

@ -11,7 +11,6 @@ using Avalonia.Input.Raw;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using ReactiveUI;
namespace Avalonia namespace Avalonia
{ {
@ -74,7 +73,7 @@ namespace Avalonia.Diagnostics
Content = devTools, Content = devTools,
DataTemplates = new DataTemplates DataTemplates = new DataTemplates
{ {
new ViewLocator<ReactiveObject>(), new ViewLocator<ViewModelBase>(),
} }
}; };

3
src/Avalonia.Diagnostics/ViewModels/ControlDetailsViewModel.cs

@ -4,11 +4,10 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using ReactiveUI;
namespace Avalonia.Diagnostics.ViewModels namespace Avalonia.Diagnostics.ViewModels
{ {
internal class ControlDetailsViewModel : ReactiveObject internal class ControlDetailsViewModel : ViewModelBase
{ {
public ControlDetailsViewModel(IVisual control) public ControlDetailsViewModel(IVisual control)
{ {

74
src/Avalonia.Diagnostics/ViewModels/DevToolsViewModel.cs

@ -5,32 +5,50 @@ using System;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using ReactiveUI;
namespace Avalonia.Diagnostics.ViewModels namespace Avalonia.Diagnostics.ViewModels
{ {
internal class DevToolsViewModel : ReactiveObject internal class DevToolsViewModel : ViewModelBase
{ {
private ReactiveObject _content; private ViewModelBase _content;
private int _selectedTab; private int _selectedTab;
private TreePageViewModel _logicalTree; private TreePageViewModel _logicalTree;
private TreePageViewModel _visualTree; private TreePageViewModel _visualTree;
private string _focusedControl;
private readonly ObservableAsPropertyHelper<string> _focusedControl; private string _pointerOverElement;
private readonly ObservableAsPropertyHelper<string> _pointerOverElement;
public DevToolsViewModel(IControl root) public DevToolsViewModel(IControl root)
{ {
_logicalTree = new TreePageViewModel(LogicalTreeNode.Create(root)); _logicalTree = new TreePageViewModel(LogicalTreeNode.Create(root));
_visualTree = new TreePageViewModel(VisualTreeNode.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: case 0:
Content = _logicalTree; Content = _logicalTree;
@ -39,34 +57,23 @@ namespace Avalonia.Diagnostics.ViewModels
Content = _visualTree; Content = _visualTree;
break; break;
} }
});
_focusedControl = KeyboardDevice.Instance RaisePropertyChanged();
.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);
} }
public ReactiveObject Content public string FocusedControl
{ {
get { return _content; } get { return _focusedControl; }
private set { this.RaiseAndSetIfChanged(ref _content, value); } private set { RaiseAndSetIfChanged(ref _focusedControl, value); }
} }
public int SelectedTab public string PointerOverElement
{ {
get { return _selectedTab; } get { return _pointerOverElement; }
set { this.RaiseAndSetIfChanged(ref _selectedTab, value); } private set { RaiseAndSetIfChanged(ref _pointerOverElement, value); }
} }
public string FocusedControl => _focusedControl.Value;
public string PointerOverElement => _pointerOverElement.Value;
public void SelectControl(IControl control) public void SelectControl(IControl control)
{ {
var tree = Content as TreePageViewModel; var tree = Content as TreePageViewModel;
@ -76,5 +83,10 @@ namespace Avalonia.Diagnostics.ViewModels
tree.SelectControl(control); tree.SelectControl(control);
} }
} }
private void UpdateFocusedControl()
{
_focusedControl = KeyboardDevice.Instance.FocusedElement?.GetType().Name;
}
} }
} }

4
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. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System; using System;
using Avalonia.Collections;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.LogicalTree; using Avalonia.LogicalTree;
using ReactiveUI;
namespace Avalonia.Diagnostics.ViewModels namespace Avalonia.Diagnostics.ViewModels
{ {
@ -13,7 +13,7 @@ namespace Avalonia.Diagnostics.ViewModels
public LogicalTreeNode(ILogical logical, TreeNode parent) public LogicalTreeNode(ILogical logical, TreeNode parent)
: base((Control)logical, 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) public static LogicalTreeNode[] Create(object control)

11
src/Avalonia.Diagnostics/ViewModels/PropertyDetails.cs

@ -3,16 +3,13 @@
using System; using System;
using Avalonia.Data; using Avalonia.Data;
using ReactiveUI;
namespace Avalonia.Diagnostics.ViewModels namespace Avalonia.Diagnostics.ViewModels
{ {
internal class PropertyDetails : ReactiveObject internal class PropertyDetails : ViewModelBase
{ {
private object _value; private object _value;
private string _priority; private string _priority;
private string _diagnostic; private string _diagnostic;
public PropertyDetails(AvaloniaObject o, AvaloniaProperty property) public PropertyDetails(AvaloniaObject o, AvaloniaProperty property)
@ -41,19 +38,19 @@ namespace Avalonia.Diagnostics.ViewModels
public string Priority public string Priority
{ {
get { return _priority; } get { return _priority; }
private set { this.RaiseAndSetIfChanged(ref _priority, value); } private set { RaiseAndSetIfChanged(ref _priority, value); }
} }
public string Diagnostic public string Diagnostic
{ {
get { return _diagnostic; } get { return _diagnostic; }
private set { this.RaiseAndSetIfChanged(ref _diagnostic, value); } private set { RaiseAndSetIfChanged(ref _diagnostic, value); }
} }
public object Value public object Value
{ {
get { return _value; } get { return _value; }
private set { this.RaiseAndSetIfChanged(ref _value, value); } private set { RaiseAndSetIfChanged(ref _value, value); }
} }
} }
} }

10
src/Avalonia.Diagnostics/ViewModels/TreeNode.cs

@ -5,13 +5,13 @@ using System;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Reactive; using System.Reactive;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Collections;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using ReactiveUI;
namespace Avalonia.Diagnostics.ViewModels namespace Avalonia.Diagnostics.ViewModels
{ {
internal class TreeNode : ReactiveObject internal class TreeNode : ViewModelBase
{ {
private string _classes; private string _classes;
private bool _isExpanded; private bool _isExpanded;
@ -47,7 +47,7 @@ namespace Avalonia.Diagnostics.ViewModels
} }
} }
public IReadOnlyReactiveList<TreeNode> Children public IAvaloniaReadOnlyList<TreeNode> Children
{ {
get; get;
protected set; protected set;
@ -56,7 +56,7 @@ namespace Avalonia.Diagnostics.ViewModels
public string Classes public string Classes
{ {
get { return _classes; } get { return _classes; }
private set { this.RaiseAndSetIfChanged(ref _classes, value); } private set { RaiseAndSetIfChanged(ref _classes, value); }
} }
public IVisual Visual public IVisual Visual
@ -67,7 +67,7 @@ namespace Avalonia.Diagnostics.ViewModels
public bool IsExpanded public bool IsExpanded
{ {
get { return _isExpanded; } get { return _isExpanded; }
set { this.RaiseAndSetIfChanged(ref _isExpanded, value); } set { RaiseAndSetIfChanged(ref _isExpanded, value); }
} }
public TreeNode Parent public TreeNode Parent

24
src/Avalonia.Diagnostics/ViewModels/TreePageViewModel.cs

@ -1,25 +1,19 @@
// Copyright (c) The Avalonia Project. All rights reserved. // 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. // 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.Controls;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using ReactiveUI;
namespace Avalonia.Diagnostics.ViewModels namespace Avalonia.Diagnostics.ViewModels
{ {
internal class TreePageViewModel : ReactiveObject internal class TreePageViewModel : ViewModelBase
{ {
private TreeNode _selected; private TreeNode _selected;
private ControlDetailsViewModel _details;
private readonly ObservableAsPropertyHelper<ControlDetailsViewModel> _details;
public TreePageViewModel(TreeNode[] nodes) public TreePageViewModel(TreeNode[] nodes)
{ {
Nodes = 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; } public TreeNode[] Nodes { get; protected set; }
@ -27,10 +21,20 @@ namespace Avalonia.Diagnostics.ViewModels
public TreeNode SelectedNode public TreeNode SelectedNode
{ {
get { return _selected; } 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) public TreeNode FindNode(IControl control)
{ {

32
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<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (!EqualityComparer<T>.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));
}
}
}

7
src/Avalonia.Diagnostics/ViewModels/VisualTreeNode.cs

@ -1,10 +1,9 @@
// Copyright (c) The Avalonia Project. All rights reserved. // 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. // 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.Styling;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using ReactiveUI;
namespace Avalonia.Diagnostics.ViewModels namespace Avalonia.Diagnostics.ViewModels
{ {
@ -17,11 +16,11 @@ namespace Avalonia.Diagnostics.ViewModels
if (host?.Root == null) if (host?.Root == null)
{ {
Children = visual.VisualChildren.CreateDerivedCollection(x => new VisualTreeNode(x, this)); Children = visual.VisualChildren.CreateDerivedList(x => new VisualTreeNode(x, this));
} }
else else
{ {
Children = new ReactiveList<VisualTreeNode>(new[] { new VisualTreeNode(host.Root, this) }); Children = new AvaloniaList<VisualTreeNode>(new[] { new VisualTreeNode(host.Root, this) });
} }
if ((Visual is IStyleable styleable)) if ((Visual is IStyleable styleable))

20
src/Avalonia.Diagnostics/Views/ControlDetailsView.cs

@ -8,7 +8,6 @@ using Avalonia.Controls;
using Avalonia.Diagnostics.ViewModels; using Avalonia.Diagnostics.ViewModels;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Styling; using Avalonia.Styling;
using ReactiveUI;
namespace Avalonia.Diagnostics.Views namespace Avalonia.Diagnostics.Views
{ {
@ -16,6 +15,7 @@ namespace Avalonia.Diagnostics.Views
{ {
private static readonly StyledProperty<ControlDetailsViewModel> ViewModelProperty = private static readonly StyledProperty<ControlDetailsViewModel> ViewModelProperty =
AvaloniaProperty.Register<ControlDetailsView, ControlDetailsViewModel>("ViewModel"); AvaloniaProperty.Register<ControlDetailsView, ControlDetailsViewModel>("ViewModel");
private SimpleGrid _grid;
public ControlDetailsView() public ControlDetailsView()
{ {
@ -27,7 +27,11 @@ namespace Avalonia.Diagnostics.Views
public ControlDetailsViewModel ViewModel public ControlDetailsViewModel ViewModel
{ {
get { return GetValue(ViewModelProperty); } get { return GetValue(ViewModelProperty); }
private set { SetValue(ViewModelProperty, value); } private set
{
SetValue(ViewModelProperty, value);
_grid[GridRepeater.ItemsProperty] = value?.Properties;
}
} }
private void InitializeComponent() private void InitializeComponent()
@ -36,7 +40,7 @@ namespace Avalonia.Diagnostics.Views
Content = new ScrollViewer Content = new ScrollViewer
{ {
Content = new SimpleGrid Content = _grid = new SimpleGrid
{ {
Styles = new Styles Styles = new Styles
{ {
@ -49,7 +53,6 @@ namespace Avalonia.Diagnostics.Views
}, },
}, },
[GridRepeater.TemplateProperty] = pt, [GridRepeater.TemplateProperty] = pt,
[!GridRepeater.ItemsProperty] = this.WhenAnyValue(x => x.ViewModel.Properties).ToBinding(),
} }
}; };
} }
@ -62,16 +65,13 @@ namespace Avalonia.Diagnostics.Views
{ {
Text = property.Name, Text = property.Name,
TextWrapping = TextWrapping.NoWrap, TextWrapping = TextWrapping.NoWrap,
[!ToolTip.TipProperty] = property [!ToolTip.TipProperty] = property.GetObservable<string>(nameof(property.Diagnostic)).ToBinding(),
.WhenAnyValue(x => x.Diagnostic)
.ToBinding(),
}; };
yield return new TextBlock yield return new TextBlock
{ {
TextWrapping = TextWrapping.NoWrap, TextWrapping = TextWrapping.NoWrap,
[!TextBlock.TextProperty] = property [!TextBlock.TextProperty] = property.GetObservable<object>(nameof(property.Value))
.WhenAnyValue(v => v.Value)
.Select(v => v?.ToString()) .Select(v => v?.ToString())
.ToBinding(), .ToBinding(),
}; };
@ -79,7 +79,7 @@ namespace Avalonia.Diagnostics.Views
yield return new TextBlock yield return new TextBlock
{ {
TextWrapping = TextWrapping.NoWrap, TextWrapping = TextWrapping.NoWrap,
[!TextBlock.TextProperty] = property.WhenAnyValue(x => x.Priority).ToBinding(), [!TextBlock.TextProperty] = property.GetObservable<string>((nameof(property.Priority))).ToBinding(),
}; };
} }
} }

30
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<T> GetObservable<T>(this INotifyPropertyChanged source, string propertyName)
{
Contract.Requires<ArgumentNullException>(source != null);
Contract.Requires<ArgumentNullException>(propertyName != null);
var property = source.GetType().GetTypeInfo().GetDeclaredProperty(propertyName);
if (property == null)
{
throw new ArgumentException($"Property '{propertyName}' not found on '{source}.");
}
return Observable.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>(
e => source.PropertyChanged += e,
e => source.PropertyChanged -= e)
.Where(e => e.EventArgs.PropertyName == propertyName)
.Select(_ => (T)property.GetValue(source))
.StartWith((T)property.GetValue(source));
}
}
}

8
src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs

@ -82,7 +82,13 @@ namespace Avalonia
private void LoadAssembliesInDirectory() 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 try
{ {

3
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. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System; using System;
using System.ComponentModel;
namespace Avalonia.Input namespace Avalonia.Input
{ {
@ -26,7 +27,7 @@ namespace Avalonia.Input
Toggled = 2, Toggled = 2,
} }
public interface IKeyboardDevice : IInputDevice public interface IKeyboardDevice : IInputDevice, INotifyPropertyChanged
{ {
IInputElement FocusedElement { get; } IInputElement FocusedElement { get; }

46
src/Avalonia.Input/Navigation/DirectionalNavigation.cs

@ -44,7 +44,7 @@ namespace Avalonia.Input.Navigation
GetFirstInNextContainer(element, direction); GetFirstInNextContainer(element, direction);
case KeyboardNavigationMode.Cycle: case KeyboardNavigationMode.Cycle:
return GetNextInContainer(element, container, direction) ?? return GetNextInContainer(element, container, direction) ??
GetFocusableDescendent(container, direction); GetFocusableDescendant(container, direction);
case KeyboardNavigationMode.Contained: case KeyboardNavigationMode.Contained:
return GetNextInContainer(element, container, direction); return GetNextInContainer(element, container, direction);
default: default:
@ -53,7 +53,7 @@ namespace Avalonia.Input.Navigation
} }
else else
{ {
return GetFocusableDescendents(element).FirstOrDefault(); return GetFocusableDescendants(element).FirstOrDefault();
} }
} }
@ -71,24 +71,24 @@ namespace Avalonia.Input.Navigation
} }
/// <summary> /// <summary>
/// Gets the first or last focusable descendent of the specified element. /// Gets the first or last focusable descendant of the specified element.
/// </summary> /// </summary>
/// <param name="container">The element.</param> /// <param name="container">The element.</param>
/// <param name="direction">The direction to search.</param> /// <param name="direction">The direction to search.</param>
/// <returns>The element or null if not found.##</returns> /// <returns>The element or null if not found.##</returns>
private static IInputElement GetFocusableDescendent(IInputElement container, NavigationDirection direction) private static IInputElement GetFocusableDescendant(IInputElement container, NavigationDirection direction)
{ {
return IsForward(direction) ? return IsForward(direction) ?
GetFocusableDescendents(container).FirstOrDefault() : GetFocusableDescendants(container).FirstOrDefault() :
GetFocusableDescendents(container).LastOrDefault(); GetFocusableDescendants(container).LastOrDefault();
} }
/// <summary> /// <summary>
/// Gets the focusable descendents of the specified element. /// Gets the focusable descendants of the specified element.
/// </summary> /// </summary>
/// <param name="element">The element.</param> /// <param name="element">The element.</param>
/// <returns>The element's focusable descendents.</returns> /// <returns>The element's focusable descendants.</returns>
private static IEnumerable<IInputElement> GetFocusableDescendents(IInputElement element) private static IEnumerable<IInputElement> GetFocusableDescendants(IInputElement element)
{ {
var children = element.GetVisualChildren().OfType<IInputElement>(); var children = element.GetVisualChildren().OfType<IInputElement>();
@ -99,11 +99,11 @@ namespace Avalonia.Input.Navigation
yield return child; 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) 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) 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() var siblings = parent.GetVisualChildren()
.OfType<IInputElement>() .OfType<IInputElement>()
.Where(FocusExtensions.CanFocusDescendents); .Where(FocusExtensions.CanFocusDescendants);
var sibling = isForward ? var sibling = isForward ?
siblings.SkipWhile(x => x != container).Skip(1).FirstOrDefault() : siblings.SkipWhile(x => x != container).Skip(1).FirstOrDefault() :
siblings.TakeWhile(x => x != container).LastOrDefault(); siblings.TakeWhile(x => x != container).LastOrDefault();
@ -207,8 +207,8 @@ namespace Avalonia.Input.Navigation
else else
{ {
next = isForward ? next = isForward ?
GetFocusableDescendents(sibling).FirstOrDefault() : GetFocusableDescendants(sibling).FirstOrDefault() :
GetFocusableDescendents(sibling).LastOrDefault(); GetFocusableDescendants(sibling).LastOrDefault();
} }
} }
@ -220,8 +220,8 @@ namespace Avalonia.Input.Navigation
else else
{ {
next = isForward ? next = isForward ?
GetFocusableDescendents(container).FirstOrDefault() : GetFocusableDescendants(container).FirstOrDefault() :
GetFocusableDescendents(container).LastOrDefault(); GetFocusableDescendants(container).LastOrDefault();
} }
return next; return next;

6
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; public static bool CanFocus(this IInputElement e) => e.Focusable && e.IsEnabledCore && e.IsVisible;
/// <summary> /// <summary>
/// Checks if descendents of the specified element can be focused. /// Checks if descendants of the specified element can be focused.
/// </summary> /// </summary>
/// <param name="e">The element.</param> /// <param name="e">The element.</param>
/// <returns>True if descendents of the element can be focused.</returns> /// <returns>True if descendants of the element can be focused.</returns>
public static bool CanFocusDescendents(this IInputElement e) => e.IsEnabledCore && e.IsVisible; public static bool CanFocusDescendants(this IInputElement e) => e.IsEnabledCore && e.IsVisible;
} }
} }

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

@ -44,7 +44,7 @@ namespace Avalonia.Input.Navigation
GetFirstInNextContainer(element, direction); GetFirstInNextContainer(element, direction);
case KeyboardNavigationMode.Cycle: case KeyboardNavigationMode.Cycle:
return GetNextInContainer(element, container, direction) ?? return GetNextInContainer(element, container, direction) ??
GetFocusableDescendent(container, direction); GetFocusableDescendant(container, direction);
case KeyboardNavigationMode.Contained: case KeyboardNavigationMode.Contained:
return GetNextInContainer(element, container, direction); return GetNextInContainer(element, container, direction);
default: default:
@ -53,29 +53,29 @@ namespace Avalonia.Input.Navigation
} }
else else
{ {
return GetFocusableDescendents(element).FirstOrDefault(); return GetFocusableDescendants(element).FirstOrDefault();
} }
} }
/// <summary> /// <summary>
/// Gets the first or last focusable descendent of the specified element. /// Gets the first or last focusable descendant of the specified element.
/// </summary> /// </summary>
/// <param name="container">The element.</param> /// <param name="container">The element.</param>
/// <param name="direction">The direction to search.</param> /// <param name="direction">The direction to search.</param>
/// <returns>The element or null if not found.##</returns> /// <returns>The element or null if not found.##</returns>
private static IInputElement GetFocusableDescendent(IInputElement container, NavigationDirection direction) private static IInputElement GetFocusableDescendant(IInputElement container, NavigationDirection direction)
{ {
return direction == NavigationDirection.Next ? return direction == NavigationDirection.Next ?
GetFocusableDescendents(container).FirstOrDefault() : GetFocusableDescendants(container).FirstOrDefault() :
GetFocusableDescendents(container).LastOrDefault(); GetFocusableDescendants(container).LastOrDefault();
} }
/// <summary> /// <summary>
/// Gets the focusable descendents of the specified element. /// Gets the focusable descendants of the specified element.
/// </summary> /// </summary>
/// <param name="element">The element.</param> /// <param name="element">The element.</param>
/// <returns>The element's focusable descendents.</returns> /// <returns>The element's focusable descendants.</returns>
private static IEnumerable<IInputElement> GetFocusableDescendents(IInputElement element) private static IEnumerable<IInputElement> GetFocusableDescendants(IInputElement element)
{ {
var mode = KeyboardNavigation.GetTabNavigation((InputElement)element); var mode = KeyboardNavigation.GetTabNavigation((InputElement)element);
@ -108,11 +108,11 @@ namespace Avalonia.Input.Navigation
yield return child; 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) 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) 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() var siblings = parent.GetVisualChildren()
.OfType<IInputElement>() .OfType<IInputElement>()
.Where(FocusExtensions.CanFocusDescendents); .Where(FocusExtensions.CanFocusDescendants);
var sibling = direction == NavigationDirection.Next ? var sibling = direction == NavigationDirection.Next ?
siblings.SkipWhile(x => x != container).Skip(1).FirstOrDefault() : siblings.SkipWhile(x => x != container).Skip(1).FirstOrDefault() :
siblings.TakeWhile(x => x != container).LastOrDefault(); siblings.TakeWhile(x => x != container).LastOrDefault();
@ -217,8 +217,8 @@ namespace Avalonia.Input.Navigation
else else
{ {
next = direction == NavigationDirection.Next ? next = direction == NavigationDirection.Next ?
GetFocusableDescendents(sibling).FirstOrDefault() : GetFocusableDescendants(sibling).FirstOrDefault() :
GetFocusableDescendents(sibling).LastOrDefault(); GetFocusableDescendants(sibling).LastOrDefault();
} }
} }
@ -230,8 +230,8 @@ namespace Avalonia.Input.Navigation
else else
{ {
next = direction == NavigationDirection.Next ? next = direction == NavigationDirection.Next ?
GetFocusableDescendents(container).FirstOrDefault() : GetFocusableDescendants(container).FirstOrDefault() :
GetFocusableDescendents(container).LastOrDefault(); GetFocusableDescendants(container).LastOrDefault();
} }
return next; return next;

9
src/Avalonia.Layout/Layoutable.cs

@ -456,10 +456,9 @@ namespace Avalonia.Layout
ApplyTemplate(); ApplyTemplate();
var constrained = LayoutHelper var constrained = LayoutHelper.ApplyLayoutConstraints(
.ApplyLayoutConstraints(this, availableSize) this,
.Deflate(margin); availableSize.Deflate(margin));
var measured = MeasureOverride(constrained); var measured = MeasureOverride(constrained);
var width = measured.Width; var width = measured.Width;
@ -613,7 +612,7 @@ namespace Avalonia.Layout
/// <inheritdoc/> /// <inheritdoc/>
protected override sealed void OnVisualParentChanged(IVisual oldParent, IVisual newParent) protected override sealed void OnVisualParentChanged(IVisual oldParent, IVisual newParent)
{ {
foreach (ILayoutable i in this.GetSelfAndVisualDescendents()) foreach (ILayoutable i in this.GetSelfAndVisualDescendants())
{ {
i.InvalidateMeasure(); i.InvalidateMeasure();
} }

2
src/Avalonia.Styling/Controls/NameScope.cs

@ -50,7 +50,7 @@ namespace Avalonia.Controls
return result; return result;
} }
visual = (visual as ILogical).LogicalParent as Visual; visual = (visual as ILogical)?.LogicalParent as Visual;
} }
return null; return null;

6
src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs

@ -37,15 +37,15 @@ namespace Avalonia.LogicalTree
return logical.LogicalChildren; return logical.LogicalChildren;
} }
public static IEnumerable<ILogical> GetLogicalDescendents(this ILogical logical) public static IEnumerable<ILogical> GetLogicalDescendants(this ILogical logical)
{ {
foreach (ILogical child in logical.LogicalChildren) foreach (ILogical child in logical.LogicalChildren)
{ {
yield return child; yield return child;
foreach (ILogical descendent in child.GetLogicalDescendents()) foreach (ILogical descendant in child.GetLogicalDescendants())
{ {
yield return descendent; yield return descendant;
} }
} }
} }

14
src/Avalonia.Styling/Styling/DescendentSelector.cs

@ -7,16 +7,16 @@ using Avalonia.LogicalTree;
namespace Avalonia.Styling namespace Avalonia.Styling
{ {
internal class DescendentSelector : Selector internal class DescendantSelector : Selector
{ {
private readonly Selector _parent; private readonly Selector _parent;
private string _selectorString; private string _selectorString;
public DescendentSelector(Selector parent) public DescendantSelector(Selector parent)
{ {
if (parent == null) 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; _parent = parent;
@ -41,7 +41,7 @@ namespace Avalonia.Styling
protected override SelectorMatch Evaluate(IStyleable control, bool subscribe) protected override SelectorMatch Evaluate(IStyleable control, bool subscribe)
{ {
ILogical c = (ILogical)control; ILogical c = (ILogical)control;
List<IObservable<bool>> descendentMatches = new List<IObservable<bool>>(); List<IObservable<bool>> descendantMatches = new List<IObservable<bool>>();
while (c != null) while (c != null)
{ {
@ -60,14 +60,14 @@ namespace Avalonia.Styling
} }
else 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 else
{ {

6
src/Avalonia.Styling/Styling/Selectors.cs

@ -42,13 +42,13 @@ namespace Avalonia.Styling
} }
/// <summary> /// <summary>
/// Returns a selector which matches a descendent of a previous selector. /// Returns a selector which matches a descendant of a previous selector.
/// </summary> /// </summary>
/// <param name="previous">The previous selector.</param> /// <param name="previous">The previous selector.</param>
/// <returns>The selector.</returns> /// <returns>The selector.</returns>
public static Selector Descendent(this Selector previous) public static Selector Descendant(this Selector previous)
{ {
return new DescendentSelector(previous); return new DescendantSelector(previous);
} }
/// <summary> /// <summary>

2
src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs

@ -164,7 +164,7 @@ namespace Avalonia.Rendering
private static void ClearTransformedBounds(IVisual visual) private static void ClearTransformedBounds(IVisual visual)
{ {
foreach (var e in visual.GetSelfAndVisualDescendents()) foreach (var e in visual.GetSelfAndVisualDescendants())
{ {
BoundsTracker.SetTransformedBounds((Visual)visual, null); BoundsTracker.SetTransformedBounds((Visual)visual, null);
} }

2
src/Avalonia.Visuals/Rendering/ZIndexComparer.cs

@ -8,6 +8,6 @@ namespace Avalonia.Rendering
{ {
public static readonly ZIndexComparer Instance = new ZIndexComparer(); 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);
} }
} }

6
src/Avalonia.Visuals/Visual.cs

@ -314,7 +314,7 @@ namespace Avalonia
/// <summary> /// <summary>
/// Calls the <see cref="OnAttachedToVisualTree(VisualTreeAttachmentEventArgs)"/> method /// Calls the <see cref="OnAttachedToVisualTree(VisualTreeAttachmentEventArgs)"/> method
/// for this control and all of its visual descendents. /// for this control and all of its visual descendants.
/// </summary> /// </summary>
/// <param name="e">The event args.</param> /// <param name="e">The event args.</param>
protected virtual void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e) protected virtual void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e)
@ -342,7 +342,7 @@ namespace Avalonia
/// <summary> /// <summary>
/// Calls the <see cref="OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs)"/> method /// Calls the <see cref="OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs)"/> method
/// for this control and all of its visual descendents. /// for this control and all of its visual descendants.
/// </summary> /// </summary>
/// <param name="e">The event args.</param> /// <param name="e">The event args.</param>
protected virtual void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) protected virtual void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e)
@ -422,7 +422,7 @@ namespace Avalonia
if (visual == null) if (visual == null)
{ {
throw new ArgumentException("'visual' is not a descendent of 'ancestor'."); throw new ArgumentException("'visual' is not a descendant of 'ancestor'.");
} }
} }

16
src/Avalonia.Visuals/VisualTree/VisualExtensions.cs

@ -123,33 +123,33 @@ namespace Avalonia.VisualTree
} }
/// <summary> /// <summary>
/// Enumerates the descendents of an <see cref="IVisual"/> in the visual tree. /// Enumerates the descendants of an <see cref="IVisual"/> in the visual tree.
/// </summary> /// </summary>
/// <param name="visual">The visual.</param> /// <param name="visual">The visual.</param>
/// <returns>The visual's ancestors.</returns> /// <returns>The visual's ancestors.</returns>
public static IEnumerable<IVisual> GetVisualDescendents(this IVisual visual) public static IEnumerable<IVisual> GetVisualDescendants(this IVisual visual)
{ {
foreach (IVisual child in visual.VisualChildren) foreach (IVisual child in visual.VisualChildren)
{ {
yield return child; yield return child;
foreach (IVisual descendent in child.GetVisualDescendents()) foreach (IVisual descendant in child.GetVisualDescendants())
{ {
yield return descendent; yield return descendant;
} }
} }
} }
/// <summary> /// <summary>
/// Enumerates an <see cref="IVisual"/> and its descendents in the visual tree. /// Enumerates an <see cref="IVisual"/> and its descendants in the visual tree.
/// </summary> /// </summary>
/// <param name="visual">The visual.</param> /// <param name="visual">The visual.</param>
/// <returns>The visual and its ancestors.</returns> /// <returns>The visual and its ancestors.</returns>
public static IEnumerable<IVisual> GetSelfAndVisualDescendents(this IVisual visual) public static IEnumerable<IVisual> GetSelfAndVisualDescendants(this IVisual visual)
{ {
yield return visual; yield return visual;
foreach (var ancestor in visual.GetVisualDescendents()) foreach (var ancestor in visual.GetVisualDescendants())
{ {
yield return ancestor; yield return ancestor;
} }
@ -196,7 +196,7 @@ namespace Avalonia.VisualTree
/// Tests whether an <see cref="IVisual"/> is an ancestor of another visual. /// Tests whether an <see cref="IVisual"/> is an ancestor of another visual.
/// </summary> /// </summary>
/// <param name="visual">The visual.</param> /// <param name="visual">The visual.</param>
/// <param name="target">The potential descendent.</param> /// <param name="target">The potential descendant.</param>
/// <returns> /// <returns>
/// True if <paramref name="visual"/> is an ancestor of <paramref name="target"/>; /// True if <paramref name="visual"/> is an ancestor of <paramref name="target"/>;
/// otherwise false. /// otherwise false.

1
src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs

@ -9,6 +9,7 @@ using Avalonia.Cairo.Media.Imaging;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Rendering; using Avalonia.Rendering;
// ReSharper disable PossibleNullReferenceException
namespace Avalonia.Cairo.Media namespace Avalonia.Cairo.Media
{ {

1
src/Gtk/Avalonia.Gtk3/SystemDialogs.cs

@ -28,6 +28,7 @@ namespace Avalonia.Gtk3
List<IDisposable> disposables = null; List<IDisposable> disposables = null;
Action dispose = () => Action dispose = () =>
{ {
// ReSharper disable once PossibleNullReferenceException
foreach (var d in disposables) foreach (var d in disposables)
d.Dispose(); d.Dispose();
disposables.Clear(); disposables.Clear();

10
src/Markup/Avalonia.Markup.Xaml/Parsers/SelectorGrammar.cs

@ -93,9 +93,9 @@ namespace Avalonia.Markup.Xaml.Parsers
public static readonly Parser<ChildSyntax> Child = Parse.Char('>').Token().Return(new ChildSyntax()); public static readonly Parser<ChildSyntax> Child = Parse.Char('>').Token().Return(new ChildSyntax());
public static readonly Parser<DescendentSyntax> Descendent = public static readonly Parser<DescendantSyntax> Descendant =
from child in Parse.WhiteSpace.Many() from child in Parse.WhiteSpace.Many()
select new DescendentSyntax(); select new DescendantSyntax();
public static readonly Parser<TemplateSyntax> Template = public static readonly Parser<TemplateSyntax> Template =
from template in Parse.String("/template/").Token() from template in Parse.String("/template/").Token()
@ -115,7 +115,7 @@ namespace Avalonia.Markup.Xaml.Parsers
.Or<ISyntax>(Property) .Or<ISyntax>(Property)
.Or<ISyntax>(Child) .Or<ISyntax>(Child)
.Or<ISyntax>(Template) .Or<ISyntax>(Template)
.Or<ISyntax>(Descendent); .Or<ISyntax>(Descendant);
public static readonly Parser<IEnumerable<ISyntax>> Selector = SingleSelector.Many().End(); public static readonly Parser<IEnumerable<ISyntax>> 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) public override bool Equals(object obj)
{ {
return obj is DescendentSyntax; return obj is DescendantSyntax;
} }
} }

8
src/Markup/Avalonia.Markup.Xaml/Parsers/SelectorParser.cs

@ -47,7 +47,7 @@ namespace Avalonia.Markup.Xaml.Parsers
var name = i as SelectorGrammar.NameSyntax; var name = i as SelectorGrammar.NameSyntax;
var property = i as SelectorGrammar.PropertySyntax; var property = i as SelectorGrammar.PropertySyntax;
var child = i as SelectorGrammar.ChildSyntax; var child = i as SelectorGrammar.ChildSyntax;
var descendent = i as SelectorGrammar.DescendentSyntax; var descendant = i as SelectorGrammar.DescendantSyntax;
var template = i as SelectorGrammar.TemplateSyntax; var template = i as SelectorGrammar.TemplateSyntax;
if (ofType != null) if (ofType != null)
@ -68,7 +68,7 @@ namespace Avalonia.Markup.Xaml.Parsers
} }
else if (property != null) else if (property != null)
{ {
var type = result.TargetType; var type = result?.TargetType;
if (type == null) if (type == null)
{ {
@ -102,9 +102,9 @@ namespace Avalonia.Markup.Xaml.Parsers
{ {
result = result.Child(); result = result.Child();
} }
else if (descendent != null) else if (descendant != null)
{ {
result = result.Descendent(); result = result.Descendant();
} }
else if (template != null) else if (template != null)
{ {

4
src/Markup/Avalonia.Markup/Data/Plugins/TaskStreamPlugin.cs

@ -36,7 +36,7 @@ namespace Avalonia.Markup.Data.Plugins
if (task != null) if (task != null)
{ {
var resultProperty = task.GetType().GetTypeInfo().GetDeclaredProperty("Result"); var resultProperty = task.GetType().GetRuntimeProperty("Result");
if (resultProperty != null) if (resultProperty != null)
{ {
@ -61,7 +61,7 @@ namespace Avalonia.Markup.Data.Plugins
protected IObservable<object> HandleCompleted(Task task) protected IObservable<object> HandleCompleted(Task task)
{ {
var resultProperty = task.GetType().GetTypeInfo().GetDeclaredProperty("Result"); var resultProperty = task.GetType().GetRuntimeProperty("Result");
if (resultProperty != null) if (resultProperty != null)
{ {

6
src/Shared/SharedAssemblyInfo.cs

@ -13,6 +13,6 @@ using System.Resources;
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: NeutralResourcesLanguage("en")] [assembly: NeutralResourcesLanguage("en")]
[assembly: AssemblyVersion("0.5.0")] [assembly: AssemblyVersion("0.5.1")]
[assembly: AssemblyFileVersion("0.5.0")] [assembly: AssemblyFileVersion("0.5.1")]
[assembly: AssemblyInformationalVersion("0.5.0")] [assembly: AssemblyInformationalVersion("0.5.1")]

4
src/Windows/Avalonia.Win32/Embedding/WinFormsAvaloniaControlHost.cs

@ -15,7 +15,7 @@ namespace Avalonia.Win32.Embedding
{ {
private readonly EmbeddableControlRoot _root = new EmbeddableControlRoot(); 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() public WinFormsAvaloniaControlHost()
{ {
@ -25,6 +25,8 @@ namespace Avalonia.Win32.Embedding
if (_root.IsFocused) if (_root.IsFocused)
FocusManager.Instance.Focus(null); FocusManager.Instance.Focus(null);
_root.GotFocus += RootGotFocus; _root.GotFocus += RootGotFocus;
// ReSharper disable once PossibleNullReferenceException
// Always non-null at this point
_root.PlatformImpl.LostFocus += PlatformImpl_LostFocus; _root.PlatformImpl.LostFocus += PlatformImpl_LostFocus;
FixPosition(); FixPosition();
} }

134
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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(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; }
}
}
}

2
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 // The items were created before the template was applied, so now we need to go back
// and re-arrange everything. // and re-arrange everything.
foreach (IControl i in target.GetSelfAndVisualDescendents()) foreach (IControl i in target.GetSelfAndVisualDescendants())
{ {
i.InvalidateMeasure(); i.InvalidateMeasure();
} }

8
tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs

@ -219,7 +219,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
} }
[Fact] [Fact]
public void BringDescendentIntoView_Should_Update_Offset() public void BringDescendantIntoView_Should_Update_Offset()
{ {
var target = new ScrollContentPresenter var target = new ScrollContentPresenter
{ {
@ -235,13 +235,13 @@ namespace Avalonia.Controls.UnitTests.Presenters
target.UpdateChild(); target.UpdateChild();
target.Measure(Size.Infinity); target.Measure(Size.Infinity);
target.Arrange(new Rect(0, 0, 100, 100)); 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); Assert.Equal(new Vector(100, 100), target.Offset);
} }
[Fact] [Fact]
public void BringDescendentIntoView_Should_Handle_Child_Margin() public void BringDescendantIntoView_Should_Handle_Child_Margin()
{ {
Border border; Border border;
var target = new ScrollContentPresenter var target = new ScrollContentPresenter
@ -262,7 +262,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
target.UpdateChild(); target.UpdateChild();
target.Measure(Size.Infinity); target.Measure(Size.Infinity);
target.Arrange(new Rect(0, 0, 100, 100)); 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); Assert.Equal(new Vector(150, 150), target.Offset);
} }

2
tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs

@ -222,7 +222,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
popup.Open(); popup.Open();
var popupRoot = popup.PopupRoot; var popupRoot = popup.PopupRoot;
var children = popupRoot.GetVisualDescendents().ToList(); var children = popupRoot.GetVisualDescendants().ToList();
var types = children.Select(x => x.GetType().Name).ToList(); var types = children.Select(x => x.GetType().Name).ToList();
Assert.Equal( Assert.Equal(

4
tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests.cs

@ -78,7 +78,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.ApplyTemplate(); target.ApplyTemplate();
var types = target.GetVisualDescendents().Select(x => x.GetType()).ToList(); var types = target.GetVisualDescendants().Select(x => x.GetType()).ToList();
Assert.Equal( Assert.Equal(
new[] new[]
@ -135,7 +135,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.ApplyTemplate(); target.ApplyTemplate();
var templatedParents = target.GetVisualDescendents() var templatedParents = target.GetVisualDescendants()
.OfType<IControl>() .OfType<IControl>()
.Select(x => x.TemplatedParent) .Select(x => x.TemplatedParent)
.ToList(); .ToList();

2
tests/Avalonia.Controls.UnitTests/TreeViewTests.cs

@ -80,7 +80,7 @@ namespace Avalonia.Controls.UnitTests
} }
[Fact] [Fact]
public void Root_TreeContainerFromItem_Should_Return_Descendent_Item() public void Root_TreeContainerFromItem_Should_Return_Descendant_Item()
{ {
var tree = CreateTestTreeData(); var tree = CreateTestTreeData();
var target = new TreeView var target = new TreeView

77
tests/Avalonia.Controls.UnitTests/WindowTests.cs

@ -4,6 +4,7 @@
// </copyright> // </copyright>
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System.Collections.Generic;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.UnitTests; using Avalonia.UnitTests;
using Moq; using Moq;
@ -114,5 +115,81 @@ namespace Avalonia.Controls.UnitTests
Assert.False(window.IsVisible); 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<IWindowImpl>();
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>)Window.OpenWindows).Clear();
}
} }
} }

2
tests/Avalonia.Interactivity.UnitTests/InteractiveTests.cs

@ -391,7 +391,7 @@ namespace Avalonia.Interactivity.UnitTests
if (handler != null) if (handler != null)
{ {
foreach (var i in tree.GetSelfAndVisualDescendents().Cast<Interactive>()) foreach (var i in tree.GetSelfAndVisualDescendants().Cast<Interactive>())
{ {
i.AddHandler(ev, handler, handlerRoutes, handledEventsToo); i.AddHandler(ev, handler, handlerRoutes, handledEventsToo);
} }

55
tests/Avalonia.Layout.UnitTests/MeasureTests.cs

@ -45,7 +45,7 @@ namespace Avalonia.Layout.UnitTests
} }
[Fact] [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 panel = new StackPanel();
var child2 = new Border(); var child2 = new Border();
@ -100,5 +100,58 @@ namespace Avalonia.Layout.UnitTests
Assert.Equal(0, target.DesiredSize.Height); 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;
}
}
} }
} }

4
tests/Avalonia.Markup.Xaml.UnitTests/Parsers/SelectorGrammarTests.cs

@ -157,7 +157,7 @@ namespace Avalonia.Xaml.Base.UnitTest.Parsers
} }
[Fact] [Fact]
public void OfType_Descendent_Class() public void OfType_Descendant_Class()
{ {
var result = SelectorGrammar.Selector.Parse("Button .foo").ToList(); var result = SelectorGrammar.Selector.Parse("Button .foo").ToList();
@ -165,7 +165,7 @@ namespace Avalonia.Xaml.Base.UnitTest.Parsers
new SelectorGrammar.ISyntax[] new SelectorGrammar.ISyntax[]
{ {
new SelectorGrammar.OfTypeSyntax { TypeName = "Button" }, new SelectorGrammar.OfTypeSyntax { TypeName = "Button" },
new SelectorGrammar.DescendentSyntax { }, new SelectorGrammar.DescendantSyntax { },
new SelectorGrammar.ClassSyntax { Class = "foo" }, new SelectorGrammar.ClassSyntax { Class = "foo" },
}, },
result); result);

26
tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs

@ -14,23 +14,23 @@ using Xunit;
namespace Avalonia.Styling.UnitTests namespace Avalonia.Styling.UnitTests
{ {
public class SelectorTests_Descendent public class SelectorTests_Descendant
{ {
[Fact] [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 parent = new TestLogical1();
var child = new TestLogical2(); var child = new TestLogical2();
child.LogicalParent = parent; child.LogicalParent = parent;
var selector = default(Selector).OfType<TestLogical1>().Descendent().OfType<TestLogical2>(); var selector = default(Selector).OfType<TestLogical1>().Descendant().OfType<TestLogical2>();
Assert.True(selector.Match(child).ImmediateResult); Assert.True(selector.Match(child).ImmediateResult);
} }
[Fact] [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 grandparent = new TestLogical1();
var parent = new TestLogical2(); var parent = new TestLogical2();
@ -39,13 +39,13 @@ namespace Avalonia.Styling.UnitTests
parent.LogicalParent = grandparent; parent.LogicalParent = grandparent;
child.LogicalParent = parent; child.LogicalParent = parent;
var selector = default(Selector).OfType<TestLogical1>().Descendent().OfType<TestLogical3>(); var selector = default(Selector).OfType<TestLogical1>().Descendant().OfType<TestLogical3>();
Assert.True(selector.Match(child).ImmediateResult); Assert.True(selector.Match(child).ImmediateResult);
} }
[Fact] [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 grandparent = new TestLogical1();
var parent = new TestLogical2(); var parent = new TestLogical2();
@ -55,14 +55,14 @@ namespace Avalonia.Styling.UnitTests
parent.LogicalParent = grandparent; parent.LogicalParent = grandparent;
child.LogicalParent = parent; child.LogicalParent = parent;
var selector = default(Selector).OfType<TestLogical1>().Class("foo").Descendent().OfType<TestLogical3>(); var selector = default(Selector).OfType<TestLogical1>().Class("foo").Descendant().OfType<TestLogical3>();
var activator = selector.Match(child).ObservableResult; var activator = selector.Match(child).ObservableResult;
Assert.True(await activator.Take(1)); Assert.True(await activator.Take(1));
} }
[Fact] [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 grandparent = new TestLogical1();
var parent = new TestLogical2(); var parent = new TestLogical2();
@ -73,14 +73,14 @@ namespace Avalonia.Styling.UnitTests
parent.Classes.Add("foo"); parent.Classes.Add("foo");
child.LogicalParent = parent; child.LogicalParent = parent;
var selector = default(Selector).OfType<TestLogical1>().Class("foo").Descendent().OfType<TestLogical3>(); var selector = default(Selector).OfType<TestLogical1>().Class("foo").Descendant().OfType<TestLogical3>();
var activator = selector.Match(child).ObservableResult; var activator = selector.Match(child).ObservableResult;
Assert.False(await activator.Take(1)); Assert.False(await activator.Take(1));
} }
[Fact] [Fact]
public async Task Descendent_Matches_Any_Ancestor() public async Task Descendant_Matches_Any_Ancestor()
{ {
var grandparent = new TestLogical1(); var grandparent = new TestLogical1();
var parent = new TestLogical1(); var parent = new TestLogical1();
@ -89,7 +89,7 @@ namespace Avalonia.Styling.UnitTests
parent.LogicalParent = grandparent; parent.LogicalParent = grandparent;
child.LogicalParent = parent; child.LogicalParent = parent;
var selector = default(Selector).OfType<TestLogical1>().Class("foo").Descendent().OfType<TestLogical3>(); var selector = default(Selector).OfType<TestLogical1>().Class("foo").Descendant().OfType<TestLogical3>();
var activator = selector.Match(child).ObservableResult; var activator = selector.Match(child).ObservableResult;
Assert.False(await activator.Take(1)); Assert.False(await activator.Take(1));
@ -104,9 +104,9 @@ namespace Avalonia.Styling.UnitTests
} }
[Fact] [Fact]
public void Descendent_Selector_Should_Have_Correct_String_Representation() public void Descendant_Selector_Should_Have_Correct_String_Representation()
{ {
var selector = default(Selector).OfType<TestLogical1>().Class("foo").Descendent().OfType<TestLogical3>(); var selector = default(Selector).OfType<TestLogical1>().Class("foo").Descendant().OfType<TestLogical3>();
Assert.Equal("TestLogical1.foo TestLogical3", selector.ToString()); Assert.Equal("TestLogical1.foo TestLogical3", selector.ToString());
} }

4
tests/Avalonia.Styling.UnitTests/SelectorTests_Multiple.cs

@ -91,11 +91,11 @@ namespace Avalonia.Styling.UnitTests
} }
[Fact] [Fact]
public void TargetType_Descendent() public void TargetType_Descendant()
{ {
var selector = default(Selector) var selector = default(Selector)
.OfType<Button>() .OfType<Button>()
.Descendent() .Descendant()
.OfType<TextBlock>(); .OfType<TextBlock>();
Assert.Equal(typeof(TextBlock), selector.TargetType); Assert.Equal(typeof(TextBlock), selector.TargetType);

2
tests/Avalonia.Visuals.UnitTests/VisualTests.cs

@ -135,7 +135,7 @@ namespace Avalonia.Visuals.UnitTests
} }
[Fact] [Fact]
public void Descendents_Should_RetunVisualRoot() public void Descendants_Should_RetunVisualRoot()
{ {
var root = new TestRoot(); var root = new TestRoot();
var child1 = new Decorator(); var child1 = new Decorator();

Loading…
Cancel
Save