Browse Source

Merge branch 'master' into feature/immediate-drawing-context

pull/9718/head
Max Katz 4 years ago
committed by GitHub
parent
commit
0345089fb3
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs
  2. 2
      src/Avalonia.Controls/Platform/IWindowImpl.cs
  3. 37
      src/Avalonia.Controls/Window.cs
  4. 57
      src/Avalonia.Controls/WindowClosingEventArgs.cs
  5. 2
      src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs
  6. 2
      src/Avalonia.DesignerSupport/Remote/Stubs.cs
  7. 2
      src/Avalonia.Headless/HeadlessWindowImpl.cs
  8. 4
      src/Avalonia.Native/WindowImpl.cs
  9. 4
      src/Avalonia.X11/X11Window.cs
  10. 2
      src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
  11. 2
      src/Windows/Avalonia.Win32/WindowImpl.cs
  12. 8
      tests/Avalonia.Controls.UnitTests/WindowTests.cs

14
src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs

@ -83,12 +83,12 @@ namespace Avalonia.Controls.ApplicationLifetimes
public void Shutdown(int exitCode = 0) public void Shutdown(int exitCode = 0)
{ {
DoShutdown(new ShutdownRequestedEventArgs(), true, exitCode); DoShutdown(new ShutdownRequestedEventArgs(), true, true, exitCode);
} }
public bool TryShutdown(int exitCode = 0) public bool TryShutdown(int exitCode = 0)
{ {
return DoShutdown(new ShutdownRequestedEventArgs(), false, exitCode); return DoShutdown(new ShutdownRequestedEventArgs(), true, false, exitCode);
} }
public int Start(string[] args) public int Start(string[] args)
@ -134,7 +134,11 @@ namespace Avalonia.Controls.ApplicationLifetimes
_activeLifetime = null; _activeLifetime = null;
} }
private bool DoShutdown(ShutdownRequestedEventArgs e, bool force = false, int exitCode = 0) private bool DoShutdown(
ShutdownRequestedEventArgs e,
bool isProgrammatic,
bool force = false,
int exitCode = 0)
{ {
if (!force) if (!force)
{ {
@ -159,7 +163,7 @@ namespace Avalonia.Controls.ApplicationLifetimes
{ {
if (w.Owner is null) if (w.Owner is null)
{ {
w.Close(); w.CloseCore(WindowCloseReason.ApplicationShutdown, isProgrammatic);
} }
} }
@ -183,7 +187,7 @@ namespace Avalonia.Controls.ApplicationLifetimes
return true; return true;
} }
private void OnShutdownRequested(object? sender, ShutdownRequestedEventArgs e) => DoShutdown(e); private void OnShutdownRequested(object? sender, ShutdownRequestedEventArgs e) => DoShutdown(e, false);
} }
public class ClassicDesktopStyleApplicationLifetimeOptions public class ClassicDesktopStyleApplicationLifetimeOptions

2
src/Avalonia.Controls/Platform/IWindowImpl.cs

@ -68,7 +68,7 @@ namespace Avalonia.Platform
/// Gets or sets a method called before the underlying implementation is destroyed. /// Gets or sets a method called before the underlying implementation is destroyed.
/// Return true to prevent the underlying implementation from closing. /// Return true to prevent the underlying implementation from closing.
/// </summary> /// </summary>
Func<bool> Closing { get; set; } Func<WindowCloseReason, bool> Closing { get; set; }
/// <summary> /// <summary>
/// Gets a value to indicate if the platform was able to extend client area to non-client area. /// Gets a value to indicate if the platform was able to extend client area to non-client area.

37
src/Avalonia.Controls/Window.cs

@ -430,14 +430,14 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Fired before a window is closed. /// Fired before a window is closed.
/// </summary> /// </summary>
public event EventHandler<CancelEventArgs>? Closing; public event EventHandler<WindowClosingEventArgs>? Closing;
/// <summary> /// <summary>
/// Closes the window. /// Closes the window.
/// </summary> /// </summary>
public void Close() public void Close()
{ {
Close(false); CloseCore(WindowCloseReason.WindowClosing, true);
} }
/// <summary> /// <summary>
@ -453,16 +453,16 @@ namespace Avalonia.Controls
public void Close(object dialogResult) public void Close(object dialogResult)
{ {
_dialogResult = dialogResult; _dialogResult = dialogResult;
Close(false); CloseCore(WindowCloseReason.WindowClosing, true);
} }
internal void Close(bool ignoreCancel) internal void CloseCore(WindowCloseReason reason, bool isProgrammatic)
{ {
bool close = true; bool close = true;
try try
{ {
if (!ignoreCancel && ShouldCancelClose()) if (ShouldCancelClose(new WindowClosingEventArgs(reason, isProgrammatic)))
{ {
close = false; close = false;
} }
@ -480,9 +480,10 @@ namespace Avalonia.Controls
/// Handles a closing notification from <see cref="IWindowImpl.Closing"/>. /// Handles a closing notification from <see cref="IWindowImpl.Closing"/>.
/// <returns>true if closing is cancelled. Otherwise false.</returns> /// <returns>true if closing is cancelled. Otherwise false.</returns>
/// </summary> /// </summary>
protected virtual bool HandleClosing() /// <param name="reason">The reason the window is closing.</param>
private protected virtual bool HandleClosing(WindowCloseReason reason)
{ {
if (!ShouldCancelClose()) if (!ShouldCancelClose(new WindowClosingEventArgs(reason, false)))
{ {
CloseInternal(); CloseInternal();
return false; return false;
@ -510,20 +511,22 @@ namespace Avalonia.Controls
_showingAsDialog = false; _showingAsDialog = false;
} }
private bool ShouldCancelClose(CancelEventArgs? args = null) private bool ShouldCancelClose(WindowClosingEventArgs args)
{ {
if (args is null)
{
args = new CancelEventArgs();
}
bool canClose = true; bool canClose = true;
foreach (var (child, _) in _children.ToArray()) if (_children.Count > 0)
{ {
if (child.ShouldCancelClose(args)) var childArgs = args.CloseReason == WindowCloseReason.WindowClosing ?
new WindowClosingEventArgs(WindowCloseReason.OwnerWindowClosing, args.IsProgrammatic) :
args;
foreach (var (child, _) in _children.ToArray())
{ {
canClose = false; if (child.ShouldCancelClose(childArgs))
{
canClose = false;
}
} }
} }
@ -1033,7 +1036,7 @@ namespace Avalonia.Controls
/// overridden method must call <see cref="OnClosing"/> on the base class if the /// overridden method must call <see cref="OnClosing"/> on the base class if the
/// <see cref="Closing"/> event needs to be raised. /// <see cref="Closing"/> event needs to be raised.
/// </remarks> /// </remarks>
protected virtual void OnClosing(CancelEventArgs e) => Closing?.Invoke(this, e); protected virtual void OnClosing(WindowClosingEventArgs e) => Closing?.Invoke(this, e);
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {

57
src/Avalonia.Controls/WindowClosingEventArgs.cs

@ -0,0 +1,57 @@
using System.ComponentModel;
namespace Avalonia.Controls
{
/// <summary>
/// Specifies the reason that a window was closed.
/// </summary>
public enum WindowCloseReason
{
/// <summary>
/// The cause of the closure was not provided by the underlying platform.
/// </summary>
Undefined,
/// <summary>
/// The window itself was requested to close.
/// </summary>
WindowClosing,
/// <summary>
/// The window is closing due to a parent/owner window closing.
/// </summary>
OwnerWindowClosing,
/// <summary>
/// The window is closing due to the application shutting down.
/// </summary>
ApplicationShutdown,
/// <summary>
/// The window is closing due to the operating system shutting down.
/// </summary>
OSShutdown,
}
/// <summary>
/// Provides data for the <see cref="Window.Closing"/> event.
/// </summary>
public class WindowClosingEventArgs : CancelEventArgs
{
internal WindowClosingEventArgs(WindowCloseReason reason, bool isProgrammatic)
{
CloseReason = reason;
IsProgrammatic = isProgrammatic;
}
/// <summary>
/// Gets a value that indicates why the window is being closed.
/// </summary>
public WindowCloseReason CloseReason { get; }
/// <summary>
/// Gets a value indicating whether the window is being closed programmatically.
/// </summary>
public bool IsProgrammatic { get; }
}
}

2
src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs

@ -42,7 +42,7 @@ namespace Avalonia.DesignerSupport.Remote
public Action<PixelPoint> PositionChanged { get; set; } public Action<PixelPoint> PositionChanged { get; set; }
public Action Deactivated { get; set; } public Action Deactivated { get; set; }
public Action Activated { get; set; } public Action Activated { get; set; }
public Func<bool> Closing { get; set; } public Func<WindowCloseReason, bool> Closing { get; set; }
public IPlatformHandle Handle { get; } public IPlatformHandle Handle { get; }
public WindowState WindowState { get; set; } public WindowState WindowState { get; set; }
public Action<WindowState> WindowStateChanged { get; set; } public Action<WindowState> WindowStateChanged { get; set; }

2
src/Avalonia.DesignerSupport/Remote/Stubs.cs

@ -32,7 +32,7 @@ namespace Avalonia.DesignerSupport.Remote
public Action<Rect> Paint { get; set; } public Action<Rect> Paint { get; set; }
public Action<Size, PlatformResizeReason> Resized { get; set; } public Action<Size, PlatformResizeReason> Resized { get; set; }
public Action<double> ScalingChanged { get; set; } public Action<double> ScalingChanged { get; set; }
public Func<bool> Closing { get; set; } public Func<WindowCloseReason, bool> Closing { get; set; }
public Action Closed { get; set; } public Action Closed { get; set; }
public Action LostFocus { get; set; } public Action LostFocus { get; set; }
public IMouseDevice MouseDevice { get; } = new MouseDevice(); public IMouseDevice MouseDevice { get; } = new MouseDevice();

2
src/Avalonia.Headless/HeadlessWindowImpl.cs

@ -175,7 +175,7 @@ namespace Avalonia.Headless
} }
public Func<bool> Closing { get; set; } public Func<WindowCloseReason, bool> Closing { get; set; }
class FramebufferProxy : ILockedFramebuffer class FramebufferProxy : ILockedFramebuffer
{ {

4
src/Avalonia.Native/WindowImpl.cs

@ -48,7 +48,7 @@ namespace Avalonia.Native
{ {
if (_parent.Closing != null) if (_parent.Closing != null)
{ {
return _parent.Closing().AsComBool(); return _parent.Closing(WindowCloseReason.WindowClosing).AsComBool();
} }
return true.AsComBool(); return true.AsComBool();
@ -207,7 +207,7 @@ namespace Avalonia.Native
// NO OP on OSX // NO OP on OSX
} }
public Func<bool> Closing { get; set; } public Func<WindowCloseReason, bool> Closing { get; set; }
public ITopLevelNativeMenuExporter NativeMenuExporter { get; } public ITopLevelNativeMenuExporter NativeMenuExporter { get; }

4
src/Avalonia.X11/X11Window.cs

@ -354,7 +354,7 @@ namespace Avalonia.X11
public Action<double> ScalingChanged { get; set; } public Action<double> ScalingChanged { get; set; }
public Action Deactivated { get; set; } public Action Deactivated { get; set; }
public Action Activated { get; set; } public Action Activated { get; set; }
public Func<bool> Closing { get; set; } public Func<WindowCloseReason, bool> Closing { get; set; }
public Action<WindowState> WindowStateChanged { get; set; } public Action<WindowState> WindowStateChanged { get; set; }
public Action<WindowTransparencyLevel> TransparencyLevelChanged public Action<WindowTransparencyLevel> TransparencyLevelChanged
@ -542,7 +542,7 @@ namespace Avalonia.X11
{ {
if (ev.ClientMessageEvent.ptr1 == _x11.Atoms.WM_DELETE_WINDOW) if (ev.ClientMessageEvent.ptr1 == _x11.Atoms.WM_DELETE_WINDOW)
{ {
if (Closing?.Invoke() != true) if (Closing?.Invoke(WindowCloseReason.WindowClosing) != true)
Dispose(); Dispose();
} }
else if (ev.ClientMessageEvent.ptr1 == _x11.Atoms._NET_WM_SYNC_REQUEST) else if (ev.ClientMessageEvent.ptr1 == _x11.Atoms._NET_WM_SYNC_REQUEST)

2
src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs

@ -70,7 +70,7 @@ namespace Avalonia.Win32
case WindowsMessage.WM_CLOSE: case WindowsMessage.WM_CLOSE:
{ {
bool? preventClosing = Closing?.Invoke(); bool? preventClosing = Closing?.Invoke(WindowCloseReason.WindowClosing);
if (preventClosing == true) if (preventClosing == true)
{ {
return IntPtr.Zero; return IntPtr.Zero;

2
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -191,7 +191,7 @@ namespace Avalonia.Win32
public Action Activated { get; set; } public Action Activated { get; set; }
public Func<bool> Closing { get; set; } public Func<WindowCloseReason, bool> Closing { get; set; }
public Action Closed { get; set; } public Action Closed { get; set; }

8
tests/Avalonia.Controls.UnitTests/WindowTests.cs

@ -155,12 +155,16 @@ namespace Avalonia.Controls.UnitTests
window.Closing += (sender, e) => window.Closing += (sender, e) =>
{ {
Assert.Equal(WindowCloseReason.WindowClosing, e.CloseReason);
Assert.Equal(programaticClose, e.IsProgrammatic);
count++; count++;
windowClosing = count; windowClosing = count;
}; };
child.Closing += (sender, e) => child.Closing += (sender, e) =>
{ {
Assert.Equal(WindowCloseReason.OwnerWindowClosing, e.CloseReason);
Assert.Equal(programaticClose, e.IsProgrammatic);
count++; count++;
childClosing = count; childClosing = count;
}; };
@ -186,7 +190,7 @@ namespace Avalonia.Controls.UnitTests
} }
else else
{ {
var cancel = window.PlatformImpl.Closing(); var cancel = window.PlatformImpl.Closing(WindowCloseReason.WindowClosing);
Assert.Equal(false, cancel); Assert.Equal(false, cancel);
} }
@ -248,7 +252,7 @@ namespace Avalonia.Controls.UnitTests
} }
else else
{ {
var cancel = window.PlatformImpl.Closing(); var cancel = window.PlatformImpl.Closing(WindowCloseReason.WindowClosing);
Assert.Equal(true, cancel); Assert.Equal(true, cancel);
} }

Loading…
Cancel
Save