diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index 37796ff9ba..1d4e4cbeaa 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -122,11 +122,11 @@ namespace Avalonia if (_resources != null) { hadResources = _resources.Count > 0; - _resources.ResourcesChanged -= ResourcesChanged; + _resources.ResourcesChanged -= ThisResourcesChanged; } _resources = value; - _resources.ResourcesChanged += ResourcesChanged; + _resources.ResourcesChanged += ThisResourcesChanged; if (hadResources || _resources.Count > 0) { @@ -343,5 +343,10 @@ namespace Avalonia .Bind().ToConstant(clock) .GetService()?.Add(clock); } + + private void ThisResourcesChanged(object sender, ResourcesChangedEventArgs e) + { + ResourcesChanged?.Invoke(this, e); + } } } diff --git a/src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs b/src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs index 828d8024e6..cf386a235e 100644 --- a/src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs +++ b/src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs @@ -6,6 +6,7 @@ using System.Reflection; using System.Reactive.Linq; using Avalonia; using Avalonia.VisualTree; +using Avalonia.Controls; using ReactiveUI; namespace Avalonia @@ -20,18 +21,41 @@ namespace Avalonia public IObservable GetActivationForView(IActivatable view) { if (!(view is IVisual visual)) return Observable.Return(false); - var viewLoaded = Observable + if (view is WindowBase window) return GetActivationForWindowBase(window); + return GetActivationForVisual(visual); + } + + private IObservable GetActivationForWindowBase(WindowBase window) + { + var windowLoaded = Observable + .FromEventPattern( + x => window.Initialized += x, + x => window.Initialized -= x) + .Select(args => true); + var windowUnloaded = Observable + .FromEventPattern( + x => window.Closed += x, + x => window.Closed -= x) + .Select(args => false); + return windowLoaded + .Merge(windowUnloaded) + .DistinctUntilChanged(); + } + + private IObservable GetActivationForVisual(IVisual visual) + { + var visualLoaded = Observable .FromEventPattern( x => visual.AttachedToVisualTree += x, - x => visual.DetachedFromVisualTree -= x) + x => visual.AttachedToVisualTree -= x) .Select(args => true); - var viewUnloaded = Observable + var visualUnloaded = Observable .FromEventPattern( x => visual.DetachedFromVisualTree += x, x => visual.DetachedFromVisualTree -= x) .Select(args => false); - return viewLoaded - .Merge(viewUnloaded) + return visualLoaded + .Merge(visualUnloaded) .DistinctUntilChanged(); } } diff --git a/src/Gtk/Avalonia.Gtk3/KeyTransform.cs b/src/Gtk/Avalonia.Gtk3/KeyTransform.cs index 29ee6aaeb5..ae85d618d1 100644 --- a/src/Gtk/Avalonia.Gtk3/KeyTransform.cs +++ b/src/Gtk/Avalonia.Gtk3/KeyTransform.cs @@ -15,7 +15,7 @@ namespace Avalonia.Gtk.Common { GdkKey.Clear, Key.Clear }, { GdkKey.Return, Key.Return }, { GdkKey.Pause, Key.Pause }, - //{ GdkKey.?, Key.CapsLock } + { GdkKey.Caps_Lock, Key.CapsLock }, //{ GdkKey.?, Key.HangulMode } //{ GdkKey.?, Key.JunjaMode } //{ GdkKey.?, Key.FinalMode } @@ -27,7 +27,7 @@ namespace Avalonia.Gtk.Common //{ GdkKey.?, Key.ImeModeChange } { GdkKey.space, Key.Space }, { GdkKey.Prior, Key.Prior }, - //{ GdkKey.?, Key.PageDown } + { GdkKey.Page_Down, Key.PageDown }, { GdkKey.End, Key.End }, { GdkKey.KP_End, Key.End }, { GdkKey.Home, Key.Home }, @@ -114,16 +114,16 @@ namespace Avalonia.Gtk.Common //{ GdkKey.?, Key.RWin } //{ GdkKey.?, Key.Apps } //{ GdkKey.?, Key.Sleep } - //{ GdkKey.?, Key.NumPad0 } - //{ GdkKey.?, Key.NumPad1 } - //{ GdkKey.?, Key.NumPad2 } - //{ GdkKey.?, Key.NumPad3 } - //{ GdkKey.?, Key.NumPad4 } - //{ GdkKey.?, Key.NumPad5 } - //{ GdkKey.?, Key.NumPad6 } - //{ GdkKey.?, Key.NumPad7 } - //{ GdkKey.?, Key.NumPad8 } - //{ GdkKey.?, Key.NumPad9 } + { GdkKey.KP_0, Key.NumPad0 }, + { GdkKey.KP_1, Key.NumPad1 }, + { GdkKey.KP_2, Key.NumPad2 }, + { GdkKey.KP_3, Key.NumPad3 }, + { GdkKey.KP_4, Key.NumPad4 }, + { GdkKey.KP_5, Key.NumPad5 }, + { GdkKey.KP_6, Key.NumPad6 }, + { GdkKey.KP_7, Key.NumPad7 }, + { GdkKey.KP_8, Key.NumPad8 }, + { GdkKey.KP_9, Key.NumPad9 }, { GdkKey.multiply, Key.Multiply }, //{ GdkKey.?, Key.Add } //{ GdkKey.?, Key.Separator } @@ -156,12 +156,12 @@ namespace Avalonia.Gtk.Common { GdkKey.R4, Key.F24 }, { GdkKey.Num_Lock, Key.NumLock }, { GdkKey.Scroll_Lock, Key.Scroll }, - //{ GdkKey.?, Key.LeftShift } - //{ GdkKey.?, Key.RightShift } - //{ GdkKey.?, Key.LeftCtrl } - //{ GdkKey.?, Key.RightCtrl } - //{ GdkKey.?, Key.LeftAlt } - //{ GdkKey.?, Key.RightAlt } + { GdkKey.Shift_L, Key.LeftShift }, + { GdkKey.Shift_R, Key.RightShift }, + { GdkKey.Control_L, Key.LeftCtrl }, + { GdkKey.Control_R, Key.RightCtrl }, + { GdkKey.Alt_L, Key.LeftAlt }, + { GdkKey.Alt_R, Key.RightAlt }, //{ GdkKey.?, Key.BrowserBack } //{ GdkKey.?, Key.BrowserForward } //{ GdkKey.?, Key.BrowserRefresh } diff --git a/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs b/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs index 694a6d2278..df14c808db 100644 --- a/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs @@ -113,5 +113,21 @@ namespace Avalonia.Controls.UnitTests Assert.Throws(() => { Application.Current.Run(null); }); } } + + [Fact] + public void Raises_ResourcesChanged_When_Event_Handler_Added_After_Resources_Has_Been_Accessed() + { + // Test for #1765. + using (UnitTestApplication.Start()) + { + var resources = Application.Current.Resources; + var raised = false; + + Application.Current.ResourcesChanged += (s, e) => raised = true; + resources["foo"] = "bar"; + + Assert.True(raised); + } + } } -} \ No newline at end of file +} diff --git a/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs index 97701f8437..bda55276a8 100644 --- a/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs +++ b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs @@ -32,6 +32,21 @@ namespace Avalonia } } + public class TestWindowWithWhenActivated : Window, IActivatable + { + public bool Active { get; private set; } + + public TestWindowWithWhenActivated() + { + this.WhenActivated(disposables => { + Active = true; + Disposable + .Create(() => Active = false) + .DisposeWith(disposables); + }); + } + } + [Fact] public void Visual_Element_Is_Activated_And_Deactivated() { @@ -85,5 +100,25 @@ namespace Avalonia fakeRenderedDecorator.Child = null; Assert.False(userControl.Active); } + + [Fact] + public void Activation_For_View_Fetcher_Should_Support_Windows() + { + Locator.CurrentMutable.RegisterConstant( + new AvaloniaActivationForViewFetcher(), + typeof(IActivationForViewFetcher)); + + using (var application = UnitTestApplication.Start(TestServices.MockWindowingPlatform)) + { + var window = new TestWindowWithWhenActivated(); + Assert.False(window.Active); + + window.Show(); + Assert.True(window.Active); + + window.Close(); + Assert.False(window.Active); + } + } } } \ No newline at end of file