From 5557829950b81a892818c76b765f19caf919322d Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Wed, 10 Apr 2019 18:57:35 +0200 Subject: [PATCH] Initial --- samples/ControlCatalog.Desktop/Program.cs | 8 +- samples/ControlCatalog.NetCore/Program.cs | 2 +- samples/ControlCatalog/App.xaml.cs | 9 ++ src/Avalonia.Controls/AppBuilderBase.cs | 17 ++- src/Avalonia.Controls/Application.cs | 112 +++++++++--------- .../IApplicationLifecycle.cs | 9 +- .../{ExitMode.cs => ShutdownMode.cs} | 8 +- src/Avalonia.Controls/TopLevel.cs | 4 +- src/Avalonia.Controls/Window.cs | 18 ++- src/Avalonia.Controls/WindowCollection.cs | 12 +- .../ApplicationTests.cs | 35 ++++-- .../TopLevelTests.cs | 2 +- 12 files changed, 144 insertions(+), 92 deletions(-) rename src/Avalonia.Controls/{ExitMode.cs => ShutdownMode.cs} (88%) diff --git a/samples/ControlCatalog.Desktop/Program.cs b/samples/ControlCatalog.Desktop/Program.cs index 329b2ab5a3..b809cc8c94 100644 --- a/samples/ControlCatalog.Desktop/Program.cs +++ b/samples/ControlCatalog.Desktop/Program.cs @@ -4,7 +4,6 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Logging.Serilog; using Avalonia.Platform; -using Serilog; namespace ControlCatalog { @@ -15,7 +14,12 @@ namespace ControlCatalog { // TODO: Make this work with GTK/Skia/Cairo depending on command-line args // again. - BuildAvaloniaApp().Start(); + BuildAvaloniaApp().Start(AppMain, args); + } + + private static void AppMain(Application app, string[] args) + { + app.Run(); } /// diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs index 57c8b700df..ee4c3fe350 100644 --- a/samples/ControlCatalog.NetCore/Program.cs +++ b/samples/ControlCatalog.NetCore/Program.cs @@ -36,7 +36,7 @@ namespace ControlCatalog.NetCore static void AppMain(Application app, string[] args) { - app.Run(new MainWindow()); + app.Run(); } /// diff --git a/samples/ControlCatalog/App.xaml.cs b/samples/ControlCatalog/App.xaml.cs index d862749132..6fbcecfd6e 100644 --- a/samples/ControlCatalog/App.xaml.cs +++ b/samples/ControlCatalog/App.xaml.cs @@ -9,5 +9,14 @@ namespace ControlCatalog { AvaloniaXamlLoader.Load(this); } + + protected override void OnStartup() + { + base.OnStartup(); + + var mainWindow = new MainWindow(); + + mainWindow.Show(); + } } } diff --git a/src/Avalonia.Controls/AppBuilderBase.cs b/src/Avalonia.Controls/AppBuilderBase.cs index 1f6870d60d..2dfc04132a 100644 --- a/src/Avalonia.Controls/AppBuilderBase.cs +++ b/src/Avalonia.Controls/AppBuilderBase.cs @@ -147,13 +147,20 @@ namespace Avalonia.Controls public delegate void AppMainDelegate(Application app, string[] args); + public void Start() + { + Setup(); + BeforeStartCallback(Self); + Instance.Run(); + } + public void Start(AppMainDelegate main, string[] args) { Setup(); BeforeStartCallback(Self); main(Instance, args); } - + /// /// Sets up the platform-specific services for the application, but does not run it. /// @@ -219,13 +226,13 @@ namespace Avalonia.Controls /// /// Sets the shutdown mode of the application. /// - /// The shutdown mode. + /// The shutdown mode. /// - public TAppBuilder SetExitMode(ExitMode exitMode) + public TAppBuilder SetExitMode(ShutdownMode shutdownMode) { - Instance.ExitMode = exitMode; + Instance.ShutdownMode = shutdownMode; return Self; - } + } protected virtual bool CheckSetup => true; diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index 1d4e4cbeaa..383b994e70 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -52,10 +52,14 @@ namespace Avalonia public Application() { Windows = new WindowCollection(this); - - OnExit += OnExiting; } + /// + public event EventHandler Startup; + + /// + public event EventHandler Exit; + /// public event EventHandler ResourcesChanged; @@ -164,14 +168,14 @@ namespace Avalonia IResourceNode IResourceNode.ResourceParent => null; /// - /// Gets or sets the . This property indicates whether the application exits explicitly or implicitly. - /// If is set to OnExplicitExit the application is only closes if Exit is called. + /// Gets or sets the . This property indicates whether the application is shutdown explicitly or implicitly. + /// If is set to OnExplicitShutdown the application is only closes if Shutdown is called. /// The default is OnLastWindowClose /// /// /// The shutdown mode. /// - public ExitMode ExitMode { get; set; } + public ShutdownMode ShutdownMode { get; set; } /// /// Gets or sets the main window of the application. @@ -190,12 +194,12 @@ namespace Avalonia public WindowCollection Windows { get; } /// - /// Gets or sets a value indicating whether this instance is existing. + /// Gets or sets a value indicating whether this instance is shutting down. /// /// - /// true if this instance is existing; otherwise, false. + /// true if this instance is shutting down; otherwise, false. /// - internal bool IsExiting { get; set; } + internal bool IsShuttingDown { get; set; } /// /// Initializes the application by loading XAML etc. @@ -204,6 +208,35 @@ namespace Avalonia { } + public void Run() + { + if (_mainLoopCancellationTokenSource != null) + { + throw new Exception("Run should only called once"); + } + + _mainLoopCancellationTokenSource = new CancellationTokenSource(); + + Dispatcher.UIThread.Post(OnStartup, DispatcherPriority.Send); + + Run(_mainLoopCancellationTokenSource.Token); + } + + /// + /// Runs the application's main loop until the is canceled. + /// + /// The token to track + public void Run(CancellationToken token) + { + Dispatcher.UIThread.MainLoop(token); + + // Make sure we call OnExit in case an error happened and OnExit() wasn't called explicitly + if (!IsShuttingDown) + { + OnExit(); + } + } + /// /// Runs the application's main loop until the is closed. /// @@ -215,17 +248,11 @@ namespace Avalonia throw new Exception("Run should only called once"); } - closable.Closed += (s, e) => Exit(); + closable.Closed += (s, e) => OnExit(); _mainLoopCancellationTokenSource = new CancellationTokenSource(); - Dispatcher.UIThread.MainLoop(_mainLoopCancellationTokenSource.Token); - - // Make sure we call OnExit in case an error happened and Exit() wasn't called explicitly - if (!IsExiting) - { - OnExit?.Invoke(this, EventArgs.Empty); - } + Run(_mainLoopCancellationTokenSource.Token); } /// @@ -252,46 +279,31 @@ namespace Avalonia { mainWindow.Show(); } - - MainWindow = mainWindow; - } - - Dispatcher.UIThread.MainLoop(_mainLoopCancellationTokenSource.Token); - - // Make sure we call OnExit in case an error happened and Exit() wasn't called explicitly - if (!IsExiting) - { - OnExit?.Invoke(this, EventArgs.Empty); } + + Run(_mainLoopCancellationTokenSource.Token); } - /// - /// Runs the application's main loop until the is canceled. - /// - /// The token to track - public void Run(CancellationToken token) + protected virtual void OnStartup() { - Dispatcher.UIThread.MainLoop(token); + Startup?.Invoke(this, EventArgs.Empty); + } - // Make sure we call OnExit in case an error happened and Exit() wasn't called explicitly - if (!IsExiting) - { - OnExit?.Invoke(this, EventArgs.Empty); - } + protected virtual void OnExit() + { + Exit?.Invoke(this, EventArgs.Empty); } - /// - /// Exits the application - /// - public void Exit() + /// + public void Shutdown() { - IsExiting = true; + IsShuttingDown = true; Windows.Clear(); - OnExit?.Invoke(this, EventArgs.Empty); - _mainLoopCancellationTokenSource?.Cancel(); + + OnExit(); } /// @@ -302,19 +314,7 @@ namespace Avalonia Styles.TryGetResource(key, out value); } - /// - /// Sent when the application is exiting. - /// - public event EventHandler OnExit; - /// - /// Called when the application is exiting. - /// - /// - /// - protected virtual void OnExiting(object sender, EventArgs e) - { - } /// /// Register's the services needed by Avalonia. diff --git a/src/Avalonia.Controls/IApplicationLifecycle.cs b/src/Avalonia.Controls/IApplicationLifecycle.cs index 51f554c078..c088ceb0eb 100644 --- a/src/Avalonia.Controls/IApplicationLifecycle.cs +++ b/src/Avalonia.Controls/IApplicationLifecycle.cs @@ -7,14 +7,19 @@ namespace Avalonia.Controls /// public interface IApplicationLifecycle { + /// + /// Sent when the application is starting up. + /// + event EventHandler Startup; + /// /// Sent when the application is exiting. /// - event EventHandler OnExit; + event EventHandler Exit; /// /// Exits the application. /// - void Exit(); + void Shutdown(); } } diff --git a/src/Avalonia.Controls/ExitMode.cs b/src/Avalonia.Controls/ShutdownMode.cs similarity index 88% rename from src/Avalonia.Controls/ExitMode.cs rename to src/Avalonia.Controls/ShutdownMode.cs index b73fe4a963..8c52d4b70e 100644 --- a/src/Avalonia.Controls/ExitMode.cs +++ b/src/Avalonia.Controls/ShutdownMode.cs @@ -6,7 +6,7 @@ namespace Avalonia /// /// Enum for ExitMode /// - public enum ExitMode + public enum ShutdownMode { /// /// Indicates an implicit call to Application.Exit when the last window closes. @@ -19,8 +19,8 @@ namespace Avalonia OnMainWindowClose, /// - /// Indicates that the application only exits on an explicit call to Application.Exit. + /// Indicates that the application only exits on an explicit call to Application.Shutdown. /// - OnExplicitExit + OnExplicitShutdown } -} \ No newline at end of file +} diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 32c40847c5..d627785129 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -124,7 +124,7 @@ namespace Avalonia.Controls if (_applicationLifecycle != null) { - _applicationLifecycle.OnExit += OnApplicationExiting; + _applicationLifecycle.Exit += OnApplicationExiting; } if (((IStyleHost)this).StylingParent is IResourceProvider applicationResources) @@ -278,7 +278,7 @@ namespace Avalonia.Controls Closed?.Invoke(this, EventArgs.Empty); Renderer?.Dispose(); Renderer = null; - _applicationLifecycle.OnExit -= OnApplicationExiting; + _applicationLifecycle.Exit -= OnApplicationExiting; } /// diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index f5af6774b5..648256e667 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -49,6 +49,8 @@ namespace Avalonia.Controls /// public class Window : WindowBase, IStyleable, IFocusScope, ILayoutRoot, INameScope { + private static bool s_hasAddedFirstWindow; + /// /// Defines the property. /// @@ -250,7 +252,7 @@ namespace Avalonia.Controls /// /// Fired before a window is closed. /// - public event EventHandler Closing; + public event EventHandler Closing; private static void AddWindow(Window window) { @@ -260,6 +262,18 @@ namespace Avalonia.Controls } Application.Current.Windows.Add(window); + + if (s_hasAddedFirstWindow) + { + return; + } + + s_hasAddedFirstWindow = true; + + if (Application.Current.MainWindow == null) + { + Application.Current.MainWindow = window; + } } private static void RemoveWindow(Window window) @@ -428,7 +442,7 @@ namespace Avalonia.Controls /// public Task ShowDialog(IWindowImpl owner) { - if(owner == null) + if (owner == null) throw new ArgumentNullException(nameof(owner)); if (IsVisible) diff --git a/src/Avalonia.Controls/WindowCollection.cs b/src/Avalonia.Controls/WindowCollection.cs index df79c3e3c8..328bb9f147 100644 --- a/src/Avalonia.Controls/WindowCollection.cs +++ b/src/Avalonia.Controls/WindowCollection.cs @@ -107,24 +107,24 @@ namespace Avalonia return; } - if (_application.IsExiting) + if (_application.IsShuttingDown) { return; } - switch (_application.ExitMode) + switch (_application.ShutdownMode) { - case ExitMode.OnLastWindowClose: + case ShutdownMode.OnLastWindowClose: if (Count == 0) { - _application.Exit(); + _application.Shutdown(); } break; - case ExitMode.OnMainWindowClose: + case ShutdownMode.OnMainWindowClose: if (window == _application.MainWindow) { - _application.Exit(); + _application.Shutdown(); } break; diff --git a/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs b/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs index df14c808db..7cedd9b69a 100644 --- a/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs @@ -15,7 +15,7 @@ namespace Avalonia.Controls.UnitTests { using (UnitTestApplication.Start(TestServices.StyledWindow)) { - Application.Current.ExitMode = ExitMode.OnMainWindowClose; + Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose; var mainWindow = new Window(); @@ -29,7 +29,7 @@ namespace Avalonia.Controls.UnitTests mainWindow.Close(); - Assert.True(Application.Current.IsExiting); + Assert.True(Application.Current.IsShuttingDown); } } @@ -38,7 +38,7 @@ namespace Avalonia.Controls.UnitTests { using (UnitTestApplication.Start(TestServices.StyledWindow)) { - Application.Current.ExitMode = ExitMode.OnLastWindowClose; + Application.Current.ShutdownMode = ShutdownMode.OnLastWindowClose; var windowA = new Window(); @@ -50,11 +50,11 @@ namespace Avalonia.Controls.UnitTests windowA.Close(); - Assert.False(Application.Current.IsExiting); + Assert.False(Application.Current.IsShuttingDown); windowB.Close(); - Assert.True(Application.Current.IsExiting); + Assert.True(Application.Current.IsShuttingDown); } } @@ -63,7 +63,7 @@ namespace Avalonia.Controls.UnitTests { using (UnitTestApplication.Start(TestServices.StyledWindow)) { - Application.Current.ExitMode = ExitMode.OnExplicitExit; + Application.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown; var windowA = new Window(); @@ -75,15 +75,15 @@ namespace Avalonia.Controls.UnitTests windowA.Close(); - Assert.False(Application.Current.IsExiting); + Assert.False(Application.Current.IsShuttingDown); windowB.Close(); - Assert.False(Application.Current.IsExiting); + Assert.False(Application.Current.IsShuttingDown); - Application.Current.Exit(); + Application.Current.Shutdown(); - Assert.True(Application.Current.IsExiting); + Assert.True(Application.Current.IsShuttingDown); } } @@ -99,7 +99,7 @@ namespace Avalonia.Controls.UnitTests window.Show(); } - Application.Current.Exit(); + Application.Current.Shutdown(); Assert.Empty(Application.Current.Windows); } @@ -129,5 +129,18 @@ namespace Avalonia.Controls.UnitTests Assert.True(raised); } } + + [Fact] + public void Should_Have_MainWindow_After_First_Window_Shown() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var mainWindow = new Window(); + + mainWindow.Show(); + + Assert.Equal(mainWindow, Application.Current.MainWindow); + } + } } } diff --git a/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs b/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs index 014bf458ea..aa99d31cff 100644 --- a/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs @@ -215,7 +215,7 @@ namespace Avalonia.Controls.UnitTests var impl = new Mock(); impl.SetupAllProperties(); var target = new TestTopLevel(impl.Object); - UnitTestApplication.Current.Exit(); + UnitTestApplication.Current.Shutdown(); Assert.True(target.IsClosed); } }