diff --git a/Avalonia.sln b/Avalonia.sln
index 922c8f57dd..66777f33eb 100644
--- a/Avalonia.sln
+++ b/Avalonia.sln
@@ -222,6 +222,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless.Vnc", "sr
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Markup.Xaml.Loader", "src\Markup\Avalonia.Markup.Xaml.Loader\Avalonia.Markup.Xaml.Loader.csproj", "{909A8CBD-7D0E-42FD-B841-022AD8925820}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.ReactiveUI.Events", "src\Avalonia.ReactiveUI.Events\Avalonia.ReactiveUI.Events.csproj", "{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.ReactiveUI.Events.UnitTests", "tests\Avalonia.ReactiveUI.Events.UnitTests\Avalonia.ReactiveUI.Events.UnitTests.csproj", "{780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}"
+EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13
@@ -2012,6 +2016,30 @@ Global
{909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|iPhone.Build.0 = Release|Any CPU
{909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+ {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+ {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+ {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+ {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+ {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+ {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+ {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.AppStore|iPhone.Build.0 = Debug|Any CPU
+ {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Release|iPhone.Build.0 = Release|Any CPU
+ {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -2070,6 +2098,7 @@ Global
{351337F5-D66F-461B-A957-4EF60BDB4BA6} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{3C84E04B-36CF-4D0D-B965-C26DD649D1F3} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}
{909A8CBD-7D0E-42FD-B841-022AD8925820} = {8B6A8209-894F-4BA1-B880-965FD453982C}
+ {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 54645e461e..29d2c62caf 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -35,16 +35,9 @@ jobs:
vmImage: 'macOS-10.14'
steps:
- task: UseDotNet@2
- displayName: 'Use .NET Core SDK 3.1.101'
+ displayName: 'Use .NET Core SDK 3.1.401'
inputs:
- packageType: sdk
- version: 3.1.101
-
- - task: UseDotNet@2
- displayName: 'Use .NET Core Runtime 3.1.1'
- inputs:
- packageType: runtime
- version: 3.1.1
+ version: 3.1.401
- task: CmdLine@2
displayName: 'Install Mono 5.18'
@@ -88,7 +81,7 @@ jobs:
export PATH="$PATH:$HOME/.dotnet/tools"
dotnet --info
printenv
- nuke --target CiAzureOSX --configuration Release
+ nuke --target CiAzureOSX --configuration Release --skip-previewer
- task: PublishTestResults@2
inputs:
@@ -112,6 +105,11 @@ jobs:
pool:
vmImage: 'windows-2019'
steps:
+ - task: UseDotNet@2
+ displayName: 'Use .NET Core SDK 3.1.401'
+ inputs:
+ version: 3.1.401
+
- task: CmdLine@2
displayName: 'Install Nuke'
inputs:
diff --git a/dirs.proj b/dirs.proj
index a48ad6e03d..bf32abef72 100644
--- a/dirs.proj
+++ b/dirs.proj
@@ -7,6 +7,7 @@
+
diff --git a/global.json b/global.json
index 128511eb48..b2b2da7c4f 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
{
"sdk": {
- "version": "3.1.101"
+ "version": "3.1.401"
},
"msbuild-sdks": {
"Microsoft.Build.Traversal": "1.0.43",
diff --git a/native/Avalonia.Native/src/OSX/rendertarget.mm b/native/Avalonia.Native/src/OSX/rendertarget.mm
index 1565417c1a..93a33bbbb0 100644
--- a/native/Avalonia.Native/src/OSX/rendertarget.mm
+++ b/native/Avalonia.Native/src/OSX/rendertarget.mm
@@ -110,7 +110,7 @@
if(_renderbuffer != 0)
glDeleteRenderbuffers(1, &_renderbuffer);
}
- IOSurfaceDecrementUseCount(surface);
+ CFRelease(surface);
}
@end
diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs
index fbfbf47e1b..24398accbb 100644
--- a/nukebuild/Build.cs
+++ b/nukebuild/Build.cs
@@ -5,6 +5,7 @@ using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
+using System.Threading.Tasks;
using System.Xml.Linq;
using Nuke.Common;
using Nuke.Common.Git;
@@ -15,6 +16,7 @@ using Nuke.Common.Tools.MSBuild;
using Nuke.Common.Tools.Npm;
using Nuke.Common.Utilities;
using Nuke.Common.Utilities.Collections;
+using Pharmacist.Core;
using static Nuke.Common.EnvironmentInfo;
using static Nuke.Common.IO.FileSystemTasks;
using static Nuke.Common.IO.PathConstruction;
@@ -124,6 +126,7 @@ partial class Build : NukeBuild
Target CompileHtmlPreviewer => _ => _
.DependsOn(Clean)
+ .OnlyWhenStatic(() => !Parameters.SkipPreviewer)
.Executes(() =>
{
var webappDir = RootDirectory / "src" / "Avalonia.DesignerSupport" / "Remote" / "HtmlTransport" / "webapp";
@@ -139,7 +142,7 @@ partial class Build : NukeBuild
Target Compile => _ => _
.DependsOn(Clean)
.DependsOn(CompileHtmlPreviewer)
- .Executes(() =>
+ .Executes(async () =>
{
if (Parameters.IsRunningOnWindows)
MsBuildCommon(Parameters.MSBuildSolution, c => c
@@ -153,8 +156,44 @@ partial class Build : NukeBuild
.AddProperty("PackageVersion", Parameters.Version)
.SetConfiguration(Parameters.Configuration)
);
+
+ await CompileReactiveEvents();
});
+ async Task CompileReactiveEvents()
+ {
+ var avaloniaBuildOutput = Path.Combine(RootDirectory, "packages", "Avalonia", "bin", Parameters.Configuration);
+ var avaloniaAssemblies = GlobFiles(avaloniaBuildOutput, "**/Avalonia*.dll")
+ .Where(file => !file.Contains("Avalonia.Build.Tasks") &&
+ !file.Contains("Avalonia.Remote.Protocol"));
+
+ var eventsDirectory = GlobDirectories($"{RootDirectory}/src/**/Avalonia.ReactiveUI.Events").First();
+ var eventsBuildFile = Path.Combine(eventsDirectory, "Events_Avalonia.cs");
+ if (File.Exists(eventsBuildFile))
+ File.Delete(eventsBuildFile);
+
+ using (var stream = File.Create(eventsBuildFile))
+ using (var writer = new StreamWriter(stream))
+ {
+ await ObservablesForEventGenerator.ExtractEventsFromAssemblies(
+ writer, avaloniaAssemblies, new string[0], "netstandard2.0"
+ );
+ }
+
+ var eventsProject = Path.Combine(eventsDirectory, "Avalonia.ReactiveUI.Events.csproj");
+ if (Parameters.IsRunningOnWindows)
+ MsBuildCommon(eventsProject, c => c
+ .SetArgumentConfigurator(a => a.Add("/r"))
+ .AddTargets("Build")
+ );
+ else
+ DotNetBuild(c => c
+ .SetProjectFile(eventsProject)
+ .AddProperty("PackageVersion", Parameters.Version)
+ .SetConfiguration(Parameters.Configuration)
+ );
+ }
+
void RunCoreTest(string projectName)
{
Information($"Running tests from {projectName}");
@@ -202,6 +241,7 @@ partial class Build : NukeBuild
RunCoreTest("Avalonia.Visuals.UnitTests");
RunCoreTest("Avalonia.Skia.UnitTests");
RunCoreTest("Avalonia.ReactiveUI.UnitTests");
+ RunCoreTest("Avalonia.ReactiveUI.Events.UnitTests");
});
Target RunRenderTests => _ => _
diff --git a/nukebuild/BuildParameters.cs b/nukebuild/BuildParameters.cs
index 149716b416..a167e9d892 100644
--- a/nukebuild/BuildParameters.cs
+++ b/nukebuild/BuildParameters.cs
@@ -19,10 +19,14 @@ public partial class Build
[Parameter("force-nuget-version")]
public string ForceNugetVersion { get; set; }
+ [Parameter("skip-previewer")]
+ public bool SkipPreviewer { get; set; }
+
public class BuildParameters
{
public string Configuration { get; }
public bool SkipTests { get; }
+ public bool SkipPreviewer {get;}
public string MainRepo { get; }
public string MasterBranch { get; }
public string RepositoryName { get; }
@@ -63,6 +67,7 @@ public partial class Build
// ARGUMENTS
Configuration = b.Configuration ?? "Release";
SkipTests = b.SkipTests;
+ SkipPreviewer = b.SkipPreviewer;
// CONFIGURATION
MainRepo = "https://github.com/AvaloniaUI/Avalonia";
diff --git a/nukebuild/_build.csproj b/nukebuild/_build.csproj
index 4c64d4ff93..b06e49f2eb 100644
--- a/nukebuild/_build.csproj
+++ b/nukebuild/_build.csproj
@@ -17,6 +17,7 @@
+
diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs
index 1c49b24f52..bf7d0e232a 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs
@@ -9,7 +9,7 @@ namespace Avalonia.Diagnostics.ViewModels
{
internal class MainViewModel : ViewModelBase, IDisposable
{
- private readonly IControl _root;
+ private readonly TopLevel _root;
private readonly TreePageViewModel _logicalTree;
private readonly TreePageViewModel _visualTree;
private readonly EventsPageViewModel _events;
@@ -19,8 +19,10 @@ namespace Avalonia.Diagnostics.ViewModels
private string _focusedControl;
private string _pointerOverElement;
private bool _shouldVisualizeMarginPadding = true;
+ private bool _shouldVisualizeDirtyRects;
+ private bool _showFpsOverlay;
- public MainViewModel(IControl root)
+ public MainViewModel(TopLevel root)
{
_root = root;
_logicalTree = new TreePageViewModel(this, LogicalTreeNode.Create(root));
@@ -40,12 +42,42 @@ namespace Avalonia.Diagnostics.ViewModels
get => _shouldVisualizeMarginPadding;
set => RaiseAndSetIfChanged(ref _shouldVisualizeMarginPadding, value);
}
+
+ public bool ShouldVisualizeDirtyRects
+ {
+ get => _shouldVisualizeDirtyRects;
+ set
+ {
+ _root.Renderer.DrawDirtyRects = value;
+ RaiseAndSetIfChanged(ref _shouldVisualizeDirtyRects, value);
+ }
+ }
+
+ public void ToggleVisualizeDirtyRects()
+ {
+ ShouldVisualizeDirtyRects = !ShouldVisualizeDirtyRects;
+ }
public void ToggleVisualizeMarginPadding()
{
ShouldVisualizeMarginPadding = !ShouldVisualizeMarginPadding;
}
+ public bool ShowFpsOverlay
+ {
+ get => _showFpsOverlay;
+ set
+ {
+ _root.Renderer.DrawFps = value;
+ RaiseAndSetIfChanged(ref _showFpsOverlay, value);
+ }
+ }
+
+ public void ToggleFpsOverlay()
+ {
+ ShowFpsOverlay = !ShowFpsOverlay;
+ }
+
public ConsoleViewModel Console { get; }
public ViewModelBase Content
@@ -128,10 +160,7 @@ namespace Avalonia.Diagnostics.ViewModels
{
var tree = Content as TreePageViewModel;
- if (tree != null)
- {
- tree.SelectControl(control);
- }
+ tree?.SelectControl(control);
}
public void Dispose()
@@ -140,6 +169,8 @@ namespace Avalonia.Diagnostics.ViewModels
_pointerOverSubscription.Dispose();
_logicalTree.Dispose();
_visualTree.Dispose();
+ _root.Renderer.DrawDirtyRects = false;
+ _root.Renderer.DrawFps = false;
}
private void UpdateFocusedControl()
diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml b/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml
index 0165398718..8c4db33f91 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml
+++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml
@@ -24,6 +24,20 @@
IsEnabled="False"/>
+
+
diff --git a/src/Avalonia.ReactiveUI.Events/Avalonia.ReactiveUI.Events.csproj b/src/Avalonia.ReactiveUI.Events/Avalonia.ReactiveUI.Events.csproj
new file mode 100644
index 0000000000..b95c8946e2
--- /dev/null
+++ b/src/Avalonia.ReactiveUI.Events/Avalonia.ReactiveUI.Events.csproj
@@ -0,0 +1,12 @@
+
+
+ netstandard2.0
+ Avalonia.ReactiveUI
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia.Visuals/Visual.cs b/src/Avalonia.Visuals/Visual.cs
index cd6eb6aac7..283d9deb52 100644
--- a/src/Avalonia.Visuals/Visual.cs
+++ b/src/Avalonia.Visuals/Visual.cs
@@ -455,7 +455,7 @@ namespace Avalonia
}
///
- /// Called when the control is added to a visual tree.
+ /// Called when the control is added to a rooted visual tree.
///
/// The event args.
protected virtual void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
@@ -463,7 +463,7 @@ namespace Avalonia
}
///
- /// Called when the control is removed from a visual tree.
+ /// Called when the control is removed from a rooted visual tree.
///
/// The event args.
protected virtual void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
diff --git a/tests/Avalonia.ReactiveUI.Events.UnitTests/Avalonia.ReactiveUI.Events.UnitTests.csproj b/tests/Avalonia.ReactiveUI.Events.UnitTests/Avalonia.ReactiveUI.Events.UnitTests.csproj
new file mode 100644
index 0000000000..19a6fd138e
--- /dev/null
+++ b/tests/Avalonia.ReactiveUI.Events.UnitTests/Avalonia.ReactiveUI.Events.UnitTests.csproj
@@ -0,0 +1,15 @@
+
+
+ netcoreapp3.1
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/Avalonia.ReactiveUI.Events.UnitTests/BasicControlEventsTest.cs b/tests/Avalonia.ReactiveUI.Events.UnitTests/BasicControlEventsTest.cs
new file mode 100644
index 0000000000..1092c98246
--- /dev/null
+++ b/tests/Avalonia.ReactiveUI.Events.UnitTests/BasicControlEventsTest.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Reactive.Linq;
+using Avalonia.Controls;
+using Avalonia.UnitTests;
+using Xunit;
+
+namespace Avalonia.ReactiveUI.Events.UnitTests
+{
+ public class BasicControlEventsTest
+ {
+ public class EventsControl : UserControl
+ {
+ public bool IsAttached { get; private set; }
+
+ public EventsControl()
+ {
+ var attached = this
+ .Events()
+ .AttachedToVisualTree
+ .Select(args => true);
+
+ this.Events()
+ .DetachedFromVisualTree
+ .Select(args => false)
+ .Merge(attached)
+ .Subscribe(marker => IsAttached = marker);
+ }
+ }
+
+ [Fact]
+ public void Should_Generate_Events_Wrappers()
+ {
+ var root = new TestRoot();
+ var control = new EventsControl();
+ Assert.False(control.IsAttached);
+
+ root.Child = control;
+ Assert.True(control.IsAttached);
+
+ root.Child = null;
+ Assert.False(control.IsAttached);
+ }
+ }
+}