Browse Source

Initial

pull/2442/head
Benedikt Schroeder 8 years ago
parent
commit
5557829950
  1. 8
      samples/ControlCatalog.Desktop/Program.cs
  2. 2
      samples/ControlCatalog.NetCore/Program.cs
  3. 9
      samples/ControlCatalog/App.xaml.cs
  4. 17
      src/Avalonia.Controls/AppBuilderBase.cs
  5. 112
      src/Avalonia.Controls/Application.cs
  6. 9
      src/Avalonia.Controls/IApplicationLifecycle.cs
  7. 8
      src/Avalonia.Controls/ShutdownMode.cs
  8. 4
      src/Avalonia.Controls/TopLevel.cs
  9. 18
      src/Avalonia.Controls/Window.cs
  10. 12
      src/Avalonia.Controls/WindowCollection.cs
  11. 35
      tests/Avalonia.Controls.UnitTests/ApplicationTests.cs
  12. 2
      tests/Avalonia.Controls.UnitTests/TopLevelTests.cs

8
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<MainWindow>();
BuildAvaloniaApp().Start(AppMain, args);
}
private static void AppMain(Application app, string[] args)
{
app.Run();
}
/// <summary>

2
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();
}
/// <summary>

9
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();
}
}
}

17
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);
}
/// <summary>
/// Sets up the platform-specific services for the application, but does not run it.
/// </summary>
@ -219,13 +226,13 @@ namespace Avalonia.Controls
/// <summary>
/// Sets the shutdown mode of the application.
/// </summary>
/// <param name="exitMode">The shutdown mode.</param>
/// <param name="shutdownMode">The shutdown mode.</param>
/// <returns></returns>
public TAppBuilder SetExitMode(ExitMode exitMode)
public TAppBuilder SetExitMode(ShutdownMode shutdownMode)
{
Instance.ExitMode = exitMode;
Instance.ShutdownMode = shutdownMode;
return Self;
}
}
protected virtual bool CheckSetup => true;

112
src/Avalonia.Controls/Application.cs

@ -52,10 +52,14 @@ namespace Avalonia
public Application()
{
Windows = new WindowCollection(this);
OnExit += OnExiting;
}
/// <inheritdoc/>
public event EventHandler Startup;
/// <inheritdoc/>
public event EventHandler Exit;
/// <inheritdoc/>
public event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
@ -164,14 +168,14 @@ namespace Avalonia
IResourceNode IResourceNode.ResourceParent => null;
/// <summary>
/// Gets or sets the <see cref="ExitMode"/>. This property indicates whether the application exits explicitly or implicitly.
/// If <see cref="ExitMode"/> is set to OnExplicitExit the application is only closes if Exit is called.
/// Gets or sets the <see cref="ShutdownMode"/>. This property indicates whether the application is shutdown explicitly or implicitly.
/// If <see cref="ShutdownMode"/> is set to OnExplicitShutdown the application is only closes if Shutdown is called.
/// The default is OnLastWindowClose
/// </summary>
/// <value>
/// The shutdown mode.
/// </value>
public ExitMode ExitMode { get; set; }
public ShutdownMode ShutdownMode { get; set; }
/// <summary>
/// Gets or sets the main window of the application.
@ -190,12 +194,12 @@ namespace Avalonia
public WindowCollection Windows { get; }
/// <summary>
/// Gets or sets a value indicating whether this instance is existing.
/// Gets or sets a value indicating whether this instance is shutting down.
/// </summary>
/// <value>
/// <c>true</c> if this instance is existing; otherwise, <c>false</c>.
/// <c>true</c> if this instance is shutting down; otherwise, <c>false</c>.
/// </value>
internal bool IsExiting { get; set; }
internal bool IsShuttingDown { get; set; }
/// <summary>
/// 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);
}
/// <summary>
/// Runs the application's main loop until the <see cref="CancellationToken"/> is canceled.
/// </summary>
/// <param name="token">The token to track</param>
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();
}
}
/// <summary>
/// Runs the application's main loop until the <see cref="ICloseable"/> is closed.
/// </summary>
@ -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);
}
/// <summary>
@ -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);
}
/// <summary>
/// Runs the application's main loop until the <see cref="CancellationToken"/> is canceled.
/// </summary>
/// <param name="token">The token to track</param>
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);
}
/// <summary>
/// Exits the application
/// </summary>
public void Exit()
/// <inheritdoc/>
public void Shutdown()
{
IsExiting = true;
IsShuttingDown = true;
Windows.Clear();
OnExit?.Invoke(this, EventArgs.Empty);
_mainLoopCancellationTokenSource?.Cancel();
OnExit();
}
/// <inheritdoc/>
@ -302,19 +314,7 @@ namespace Avalonia
Styles.TryGetResource(key, out value);
}
/// <summary>
/// Sent when the application is exiting.
/// </summary>
public event EventHandler OnExit;
/// <summary>
/// Called when the application is exiting.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected virtual void OnExiting(object sender, EventArgs e)
{
}
/// <summary>
/// Register's the services needed by Avalonia.

9
src/Avalonia.Controls/IApplicationLifecycle.cs

@ -7,14 +7,19 @@ namespace Avalonia.Controls
/// </summary>
public interface IApplicationLifecycle
{
/// <summary>
/// Sent when the application is starting up.
/// </summary>
event EventHandler Startup;
/// <summary>
/// Sent when the application is exiting.
/// </summary>
event EventHandler OnExit;
event EventHandler Exit;
/// <summary>
/// Exits the application.
/// </summary>
void Exit();
void Shutdown();
}
}

8
src/Avalonia.Controls/ExitMode.cs → src/Avalonia.Controls/ShutdownMode.cs

@ -6,7 +6,7 @@ namespace Avalonia
/// <summary>
/// Enum for ExitMode
/// </summary>
public enum ExitMode
public enum ShutdownMode
{
/// <summary>
/// Indicates an implicit call to Application.Exit when the last window closes.
@ -19,8 +19,8 @@ namespace Avalonia
OnMainWindowClose,
/// <summary>
/// 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.
/// </summary>
OnExplicitExit
OnExplicitShutdown
}
}
}

4
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;
}
/// <summary>

18
src/Avalonia.Controls/Window.cs

@ -49,6 +49,8 @@ namespace Avalonia.Controls
/// </summary>
public class Window : WindowBase, IStyleable, IFocusScope, ILayoutRoot, INameScope
{
private static bool s_hasAddedFirstWindow;
/// <summary>
/// Defines the <see cref="SizeToContent"/> property.
/// </summary>
@ -250,7 +252,7 @@ namespace Avalonia.Controls
/// <summary>
/// Fired before a window is closed.
/// </summary>
public event EventHandler<CancelEventArgs> Closing;
public event EventHandler<CancelEventArgs> 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
/// </returns>
public Task<TResult> ShowDialog<TResult>(IWindowImpl owner)
{
if(owner == null)
if (owner == null)
throw new ArgumentNullException(nameof(owner));
if (IsVisible)

12
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;

35
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);
}
}
}
}

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

@ -215,7 +215,7 @@ namespace Avalonia.Controls.UnitTests
var impl = new Mock<ITopLevelImpl>();
impl.SetupAllProperties();
var target = new TestTopLevel(impl.Object);
UnitTestApplication.Current.Exit();
UnitTestApplication.Current.Shutdown();
Assert.True(target.IsClosed);
}
}

Loading…
Cancel
Save