From dd528260febb3cfc88e7e39eb39e37758fa84417 Mon Sep 17 00:00:00 2001 From: artyom Date: Sun, 7 Oct 2018 02:18:10 +0300 Subject: [PATCH 1/6] feature: Support ReactiveUI WhenActivated --- .../AppBuilderExtensions.cs | 4 ++ .../AvaloniaActivationForViewFetcher.cs | 38 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs diff --git a/src/Avalonia.ReactiveUI/AppBuilderExtensions.cs b/src/Avalonia.ReactiveUI/AppBuilderExtensions.cs index 3eab54115a..d763febdf3 100644 --- a/src/Avalonia.ReactiveUI/AppBuilderExtensions.cs +++ b/src/Avalonia.ReactiveUI/AppBuilderExtensions.cs @@ -4,6 +4,7 @@ using Avalonia.Controls; using Avalonia.Threading; using ReactiveUI; +using Splat; namespace Avalonia { @@ -15,6 +16,9 @@ namespace Avalonia return builder.AfterSetup(_ => { RxApp.MainThreadScheduler = AvaloniaScheduler.Instance; + Locator.CurrentMutable.Register( + () => new AvaloniaActivationForViewFetcher(), + typeof(IActivationForViewFetcher)); }); } } diff --git a/src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs b/src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs new file mode 100644 index 0000000000..828d8024e6 --- /dev/null +++ b/src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs @@ -0,0 +1,38 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System; +using System.Reflection; +using System.Reactive.Linq; +using Avalonia; +using Avalonia.VisualTree; +using ReactiveUI; + +namespace Avalonia +{ + public class AvaloniaActivationForViewFetcher : IActivationForViewFetcher + { + public int GetAffinityForView(Type view) + { + return typeof(IVisual).GetTypeInfo().IsAssignableFrom(view.GetTypeInfo()) ? 10 : 0; + } + + public IObservable GetActivationForView(IActivatable view) + { + if (!(view is IVisual visual)) return Observable.Return(false); + var viewLoaded = Observable + .FromEventPattern( + x => visual.AttachedToVisualTree += x, + x => visual.DetachedFromVisualTree -= x) + .Select(args => true); + var viewUnloaded = Observable + .FromEventPattern( + x => visual.DetachedFromVisualTree += x, + x => visual.DetachedFromVisualTree -= x) + .Select(args => false); + return viewLoaded + .Merge(viewUnloaded) + .DistinctUntilChanged(); + } + } +} \ No newline at end of file From d36a55f6158cdc28186ff3b33eec9cbdb6f842ce Mon Sep 17 00:00:00 2001 From: artyom Date: Sun, 7 Oct 2018 11:45:04 +0300 Subject: [PATCH 2/6] Add ActivationForViewFetcher tests --- .../Avalonia.ReactiveUI.csproj | 5 +- .../Avalonia.ReactiveUI.UnitTests.csproj | 13 +++ .../AvaloniaActivationForViewFetcherTest.cs | 95 +++++++++++++++++++ 3 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 tests/Avalonia.ReactiveUI.UnitTests/Avalonia.ReactiveUI.UnitTests.csproj create mode 100644 tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs diff --git a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj index ae1ef60464..83dbfa41f9 100644 --- a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj +++ b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj @@ -12,6 +12,7 @@ - - + + + \ No newline at end of file diff --git a/tests/Avalonia.ReactiveUI.UnitTests/Avalonia.ReactiveUI.UnitTests.csproj b/tests/Avalonia.ReactiveUI.UnitTests/Avalonia.ReactiveUI.UnitTests.csproj new file mode 100644 index 0000000000..88f4e08886 --- /dev/null +++ b/tests/Avalonia.ReactiveUI.UnitTests/Avalonia.ReactiveUI.UnitTests.csproj @@ -0,0 +1,13 @@ + + + netcoreapp2.0 + + + + + + + + + + diff --git a/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs new file mode 100644 index 0000000000..a3c6b1a44a --- /dev/null +++ b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs @@ -0,0 +1,95 @@ +using System; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; +using Avalonia.Controls; +using Avalonia.Rendering; +using Avalonia.Platform; +using Avalonia; +using ReactiveUI; +using DynamicData; +using Xunit; +using Splat; + +namespace Avalonia +{ + public class AvaloniaActivationForViewFetcherTest + { + public class TestUserControl : UserControl, IActivatable { } + + public class FakeRenderDecorator : Decorator, IRenderRoot + { + public Size ClientSize => new Size(100, 100); + + public IRenderer Renderer { get; } + + public double RenderScaling => 1; + + public IRenderTarget CreateRenderTarget() => null; + + public void Invalidate(Rect rect) { } + + public Point PointToClient(Point point) => point; + + public Point PointToScreen(Point point) => point; + } + + public class TestUserControlWithWhenActivated : UserControl, IActivatable + { + public bool Active { get; private set; } + + public TestUserControlWithWhenActivated() + { + this.WhenActivated(disposables => { + Active = true; + Disposable + .Create(() => Active = false) + .DisposeWith(disposables); + }); + } + } + + [Fact] + public void VisualElementIsActivatedAndDeactivated() + { + var userControl = new TestUserControl(); + var activationForViewFetcher = new AvaloniaActivationForViewFetcher(); + + activationForViewFetcher + .GetActivationForView(userControl) + .ToObservableChangeSet(scheduler: ImmediateScheduler.Instance) + .Bind(out var activated) + .Subscribe(); + + var fakeRenderedDecorator = new FakeRenderDecorator(); + fakeRenderedDecorator.Child = userControl; + Assert.True(activated[0]); + Assert.Equal(1, activated.Count); + + fakeRenderedDecorator.Child = null; + Assert.True(activated[0]); + Assert.False(activated[1]); + Assert.Equal(2, activated.Count); + } + + [Fact] + public void ActivationForViewFetcherShouldSupportWhenActivated() + { + var locator = new ModernDependencyResolver(); + locator.InitializeSplat(); + locator.InitializeReactiveUI(); + locator.RegisterConstant(new AvaloniaActivationForViewFetcher(), typeof(IActivationForViewFetcher)); + using (locator.WithResolver()) + { + var userControl = new TestUserControlWithWhenActivated(); + Assert.False(userControl.Active); + + var fakeRenderedDecorator = new FakeRenderDecorator(); + fakeRenderedDecorator.Child = userControl; + Assert.True(userControl.Active); + + fakeRenderedDecorator.Child = null; + Assert.False(userControl.Active); + } + } + } +} \ No newline at end of file From accdd502b8ea73f616ed6d8042448be0b925c2d5 Mon Sep 17 00:00:00 2001 From: artyom Date: Sun, 7 Oct 2018 11:48:24 +0300 Subject: [PATCH 3/6] Reference ReactiveUI the Avalonia way --- build/ReactiveUI.props | 2 +- src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/build/ReactiveUI.props b/build/ReactiveUI.props index acdfdd215a..1208be34b8 100644 --- a/build/ReactiveUI.props +++ b/build/ReactiveUI.props @@ -1,5 +1,5 @@ - + diff --git a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj index 83dbfa41f9..ae1ef60464 100644 --- a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj +++ b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj @@ -12,7 +12,6 @@ - - - + + \ No newline at end of file From 0153c769979986220f22c0177235a339ecd2aeed Mon Sep 17 00:00:00 2001 From: artyom Date: Sun, 7 Oct 2018 12:01:30 +0300 Subject: [PATCH 4/6] Add affinity tests --- .../AvaloniaActivationForViewFetcherTest.cs | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs index a3c6b1a44a..f07378fe0c 100644 --- a/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs +++ b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs @@ -71,25 +71,35 @@ namespace Avalonia Assert.Equal(2, activated.Count); } + [Fact] + public void GetAffinityForViewShouldReturnNonZeroForVisualElements() + { + var userControl = new TestUserControl(); + var activationForViewFetcher = new AvaloniaActivationForViewFetcher(); + + var forUserControl = activationForViewFetcher.GetAffinityForView(userControl.GetType()); + var forNonUserControl = activationForViewFetcher.GetAffinityForView(typeof(object)); + + Assert.NotEqual(0, forUserControl); + Assert.Equal(0, forNonUserControl); + } + [Fact] public void ActivationForViewFetcherShouldSupportWhenActivated() { - var locator = new ModernDependencyResolver(); - locator.InitializeSplat(); - locator.InitializeReactiveUI(); - locator.RegisterConstant(new AvaloniaActivationForViewFetcher(), typeof(IActivationForViewFetcher)); - using (locator.WithResolver()) - { - var userControl = new TestUserControlWithWhenActivated(); - Assert.False(userControl.Active); + Locator.CurrentMutable.RegisterConstant( + new AvaloniaActivationForViewFetcher(), + typeof(IActivationForViewFetcher)); - var fakeRenderedDecorator = new FakeRenderDecorator(); - fakeRenderedDecorator.Child = userControl; - Assert.True(userControl.Active); + var userControl = new TestUserControlWithWhenActivated(); + Assert.False(userControl.Active); - fakeRenderedDecorator.Child = null; - Assert.False(userControl.Active); - } + var fakeRenderedDecorator = new FakeRenderDecorator(); + fakeRenderedDecorator.Child = userControl; + Assert.True(userControl.Active); + + fakeRenderedDecorator.Child = null; + Assert.False(userControl.Active); } } } \ No newline at end of file From cdc6033ed2a5eb54cee9d72b67332246a6f621f1 Mon Sep 17 00:00:00 2001 From: artyom Date: Sun, 7 Oct 2018 12:02:53 +0300 Subject: [PATCH 5/6] Use Avalonia code style for test methods names --- .../AvaloniaActivationForViewFetcherTest.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs index f07378fe0c..694d75f5b7 100644 --- a/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs +++ b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs @@ -49,7 +49,7 @@ namespace Avalonia } [Fact] - public void VisualElementIsActivatedAndDeactivated() + public void Visual_Element_Is_Activated_And_Deactivated() { var userControl = new TestUserControl(); var activationForViewFetcher = new AvaloniaActivationForViewFetcher(); @@ -72,7 +72,7 @@ namespace Avalonia } [Fact] - public void GetAffinityForViewShouldReturnNonZeroForVisualElements() + public void Get_Affinity_For_View_Should_Return_Non_Zero_For_Visual_Elements() { var userControl = new TestUserControl(); var activationForViewFetcher = new AvaloniaActivationForViewFetcher(); @@ -85,7 +85,7 @@ namespace Avalonia } [Fact] - public void ActivationForViewFetcherShouldSupportWhenActivated() + public void Activation_For_View_Fetcher_Should_Support_When_Activated() { Locator.CurrentMutable.RegisterConstant( new AvaloniaActivationForViewFetcher(), From ebcbe99c4e4d45204891dfcfdf4cc45cc04e76e4 Mon Sep 17 00:00:00 2001 From: artyom Date: Sun, 7 Oct 2018 14:03:38 +0300 Subject: [PATCH 6/6] Add tests for ReactiveUI 9.0.1 --- build.cake | 1 + .../ViewModels/MainWindowViewModel.cs | 1 + .../Avalonia.ReactiveUI.UnitTests.csproj | 3 ++- .../AvaloniaActivationForViewFetcherTest.cs | 22 +++---------------- 4 files changed, 7 insertions(+), 20 deletions(-) diff --git a/build.cake b/build.cake index 24f529ee4a..56653109ae 100644 --- a/build.cake +++ b/build.cake @@ -170,6 +170,7 @@ Task("Run-Unit-Tests-Impl") RunCoreTest("./tests/Avalonia.Styling.UnitTests", data.Parameters, false); RunCoreTest("./tests/Avalonia.Visuals.UnitTests", data.Parameters, false); RunCoreTest("./tests/Avalonia.Skia.UnitTests", data.Parameters, false); + RunCoreTest("./tests/Avalonia.ReactiveUI.UnitTests", data.Parameters, false); if (data.Parameters.IsRunningOnWindows) { RunCoreTest("./tests/Avalonia.Direct2D1.UnitTests", data.Parameters, false); diff --git a/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs b/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs index eb08ef9656..eba17f92e4 100644 --- a/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs +++ b/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs @@ -7,6 +7,7 @@ using System.Linq; using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Controls.Primitives; +using ReactiveUI.Legacy; using ReactiveUI; namespace VirtualizationDemo.ViewModels diff --git a/tests/Avalonia.ReactiveUI.UnitTests/Avalonia.ReactiveUI.UnitTests.csproj b/tests/Avalonia.ReactiveUI.UnitTests/Avalonia.ReactiveUI.UnitTests.csproj index 88f4e08886..7c0ff79183 100644 --- a/tests/Avalonia.ReactiveUI.UnitTests/Avalonia.ReactiveUI.UnitTests.csproj +++ b/tests/Avalonia.ReactiveUI.UnitTests/Avalonia.ReactiveUI.UnitTests.csproj @@ -8,6 +8,7 @@ - + + diff --git a/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs index 694d75f5b7..97701f8437 100644 --- a/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs +++ b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs @@ -4,6 +4,7 @@ using System.Reactive.Disposables; using Avalonia.Controls; using Avalonia.Rendering; using Avalonia.Platform; +using Avalonia.UnitTests; using Avalonia; using ReactiveUI; using DynamicData; @@ -16,23 +17,6 @@ namespace Avalonia { public class TestUserControl : UserControl, IActivatable { } - public class FakeRenderDecorator : Decorator, IRenderRoot - { - public Size ClientSize => new Size(100, 100); - - public IRenderer Renderer { get; } - - public double RenderScaling => 1; - - public IRenderTarget CreateRenderTarget() => null; - - public void Invalidate(Rect rect) { } - - public Point PointToClient(Point point) => point; - - public Point PointToScreen(Point point) => point; - } - public class TestUserControlWithWhenActivated : UserControl, IActivatable { public bool Active { get; private set; } @@ -60,7 +44,7 @@ namespace Avalonia .Bind(out var activated) .Subscribe(); - var fakeRenderedDecorator = new FakeRenderDecorator(); + var fakeRenderedDecorator = new TestRoot(); fakeRenderedDecorator.Child = userControl; Assert.True(activated[0]); Assert.Equal(1, activated.Count); @@ -94,7 +78,7 @@ namespace Avalonia var userControl = new TestUserControlWithWhenActivated(); Assert.False(userControl.Active); - var fakeRenderedDecorator = new FakeRenderDecorator(); + var fakeRenderedDecorator = new TestRoot(); fakeRenderedDecorator.Child = userControl; Assert.True(userControl.Active);