using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Avalonia.Automation.Peers;
using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Reactive;
using Avalonia.Styling;
using Avalonia.Utilities;
namespace Avalonia.Controls
{
///
/// Determines how a will size itself to fit its content.
///
[Flags]
public enum SizeToContent
{
///
/// The window will not automatically size itself to fit its content.
///
Manual = 0,
///
/// The window will size itself horizontally to fit its content.
///
Width = 1,
///
/// The window will size itself vertically to fit its content.
///
Height = 2,
///
/// The window will size itself horizontally and vertically to fit its content.
///
WidthAndHeight = 3,
}
///
/// Determines system decorations (title bar, border, etc) for a
///
public enum SystemDecorations
{
///
/// No decorations
///
None = 0,
///
/// Window border without titlebar
///
BorderOnly = 1,
///
/// Fully decorated (default)
///
Full = 2
}
///
/// Describes how the event behaves in the presence of child windows.
///
public enum WindowClosingBehavior
{
///
/// When the owner window is closed, the child windows' event
/// will be raised, followed by the owner window's events. A child
/// canceling the close will result in the owner Window's close being cancelled.
///
OwnerAndChildWindows,
///
/// When the owner window is closed, only the owner window's event
/// will be raised. This behavior is the same as WPF's.
///
OwnerWindowOnly,
}
///
/// A top-level window.
///
public class Window : WindowBase, IFocusScope
{
private static readonly Lazy s_defaultIcon = new(LoadDefaultIcon);
private readonly List<(Window child, bool isDialog)> _children = new List<(Window, bool)>();
private bool _isExtendedIntoWindowDecorations;
private Thickness _windowDecorationMargin;
private Thickness _offScreenMargin;
private bool _canHandleResized = false;
private Size _arrangeBounds;
///
/// Defines the property.
///
public static readonly StyledProperty SizeToContentProperty =
AvaloniaProperty.Register(nameof(SizeToContent));
///
/// Defines the property.
///
public static readonly StyledProperty ExtendClientAreaToDecorationsHintProperty =
AvaloniaProperty.Register(nameof(ExtendClientAreaToDecorationsHint), false);
public static readonly StyledProperty ExtendClientAreaChromeHintsProperty =
AvaloniaProperty.Register(nameof(ExtendClientAreaChromeHints), ExtendClientAreaChromeHints.Default);
public static readonly StyledProperty ExtendClientAreaTitleBarHeightHintProperty =
AvaloniaProperty.Register(nameof(ExtendClientAreaTitleBarHeightHint), -1);
///
/// Defines the property.
///
public static readonly DirectProperty IsExtendedIntoWindowDecorationsProperty =
AvaloniaProperty.RegisterDirect(nameof(IsExtendedIntoWindowDecorations),
o => o.IsExtendedIntoWindowDecorations,
unsetValue: false);
///
/// Defines the property.
///
public static readonly DirectProperty WindowDecorationMarginProperty =
AvaloniaProperty.RegisterDirect(nameof(WindowDecorationMargin),
o => o.WindowDecorationMargin);
public static readonly DirectProperty OffScreenMarginProperty =
AvaloniaProperty.RegisterDirect(nameof(OffScreenMargin),
o => o.OffScreenMargin);
///
/// Defines the property.
///
public static readonly StyledProperty SystemDecorationsProperty =
AvaloniaProperty.Register(nameof(SystemDecorations), SystemDecorations.Full);
///
/// Defines the property.
///
public static readonly StyledProperty ShowActivatedProperty =
AvaloniaProperty.Register(nameof(ShowActivated), true);
///
/// Enables or disables the taskbar icon
///
public static readonly StyledProperty ShowInTaskbarProperty =
AvaloniaProperty.Register(nameof(ShowInTaskbar), true);
///
/// Defines the property.
///
public static readonly StyledProperty ClosingBehaviorProperty =
AvaloniaProperty.Register(nameof(ClosingBehavior));
///
/// Represents the current window state (normal, minimized, maximized)
///
public static readonly StyledProperty WindowStateProperty =
AvaloniaProperty.Register(nameof(WindowState));
///
/// Defines the property.
///
public static readonly StyledProperty TitleProperty =
AvaloniaProperty.Register(nameof(Title), "Window");
///
/// Defines the property.
///
public static readonly StyledProperty IconProperty =
AvaloniaProperty.Register(nameof(Icon));
///
/// Defines the property.
///
public static readonly StyledProperty WindowStartupLocationProperty =
AvaloniaProperty.Register(nameof(WindowStartupLocation));
///
/// Defines the property.
///
public static readonly StyledProperty CanResizeProperty =
AvaloniaProperty.Register(nameof(CanResize), true);
///
/// Defines the property.
///
public static readonly StyledProperty CanMinimizeProperty =
AvaloniaProperty.Register(nameof(CanMinimize), true);
///
/// Defines the property.
///
public static readonly StyledProperty CanMaximizeProperty =
AvaloniaProperty.Register(nameof(CanMaximize), true, coerce: CoerceCanMaximize);
///
/// Routed event that can be used for global tracking of window destruction
///
public static readonly RoutedEvent WindowClosedEvent =
RoutedEvent.Register("WindowClosed", RoutingStrategies.Direct);
///
/// Routed event that can be used for global tracking of opening windows
///
public static readonly RoutedEvent WindowOpenedEvent =
RoutedEvent.Register("WindowOpened", RoutingStrategies.Direct);
private object? _dialogResult;
private readonly Size _maxPlatformClientSize;
private bool _shown;
private bool _showingAsDialog;
private bool _positionWasSet;
private bool _wasShownBefore;
private IDisposable? _modalSubscription;
///
/// Initializes static members of the class.
///
static Window()
{
BackgroundProperty.OverrideDefaultValue(typeof(Window), Brushes.White);
}
///
/// Initializes a new instance of the class.
///
public Window()
: this(PlatformManager.CreateWindow())
{
}
///
/// Initializes a new instance of the class.
///
/// The window implementation.
public Window(IWindowImpl impl)
: base(impl)
{
impl.Closing = HandleClosing;
impl.GotInputWhenDisabled = OnGotInputWhenDisabled;
impl.WindowStateChanged = HandleWindowStateChanged;
_maxPlatformClientSize = PlatformImpl?.MaxAutoSizeHint ?? default(Size);
impl.ExtendClientAreaToDecorationsChanged = ExtendClientAreaToDecorationsChanged;
this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl?.Resize(x, WindowResizeReason.Application));
CreatePlatformImplBinding(TitleProperty, title => PlatformImpl!.SetTitle(title));
CreatePlatformImplBinding(IconProperty, icon => PlatformImpl!.SetIcon((icon ?? s_defaultIcon.Value)?.PlatformImpl));
CreatePlatformImplBinding(CanResizeProperty, canResize => PlatformImpl!.CanResize(canResize));
CreatePlatformImplBinding(CanMinimizeProperty, canMinimize => PlatformImpl!.SetCanMinimize(canMinimize));
CreatePlatformImplBinding(CanMaximizeProperty, canMaximize => PlatformImpl!.SetCanMaximize(canMaximize));
CreatePlatformImplBinding(ShowInTaskbarProperty, show => PlatformImpl!.ShowTaskbarIcon(show));
CreatePlatformImplBinding(WindowStateProperty, state => PlatformImpl!.WindowState = state);
CreatePlatformImplBinding(ExtendClientAreaToDecorationsHintProperty, hint => PlatformImpl!.SetExtendClientAreaToDecorationsHint(hint));
CreatePlatformImplBinding(ExtendClientAreaChromeHintsProperty, hint => PlatformImpl!.SetExtendClientAreaChromeHints(hint));
CreatePlatformImplBinding(ExtendClientAreaTitleBarHeightHintProperty, height => PlatformImpl!.SetExtendClientAreaTitleBarHeightHint(height));
CreatePlatformImplBinding(MinWidthProperty, UpdateMinMaxSize);
CreatePlatformImplBinding(MaxWidthProperty, UpdateMinMaxSize);
CreatePlatformImplBinding(MinHeightProperty, UpdateMinMaxSize);
CreatePlatformImplBinding(MaxHeightProperty, UpdateMinMaxSize);
void UpdateMinMaxSize(double _) => PlatformImpl!.SetMinMaxSize(new Size(MinWidth, MinHeight), new Size(MaxWidth, MaxHeight));
}
///
/// Gets the platform-specific window implementation.
///
public new IWindowImpl? PlatformImpl => (IWindowImpl?)base.PlatformImpl;
///
/// Gets a collection of child windows owned by this window.
///
public IReadOnlyList OwnedWindows => _children.Select(x => x.child).ToArray();
///
/// Gets or sets a value indicating how the window will size itself to fit its content.
///
///
/// If has a value other than ,
/// is automatically set to
/// if a user resizes the window by using the resize grip or dragging the border.
///
/// NOTE: Because of a limitation of X11, will be reset on X11 to
/// on any resize - including the resize that happens when
/// the window is first shown. This is because X11 resize notifications are asynchronous and
/// there is no way to know whether a resize came from the user or the layout system. To avoid
/// this, consider setting to false, which will disable user resizing
/// of the window.
///
public SizeToContent SizeToContent
{
get => GetValue(SizeToContentProperty);
set => SetValue(SizeToContentProperty, value);
}
///
/// Gets or sets the title of the window.
///
public string? Title
{
get => GetValue(TitleProperty);
set => SetValue(TitleProperty, value);
}
///
/// Gets or sets if the ClientArea is Extended into the Window Decorations (chrome or border).
///
public bool ExtendClientAreaToDecorationsHint
{
get => GetValue(ExtendClientAreaToDecorationsHintProperty);
set => SetValue(ExtendClientAreaToDecorationsHintProperty, value);
}
///
/// Gets or Sets the that control
/// how the chrome looks when the client area is extended.
///
public ExtendClientAreaChromeHints ExtendClientAreaChromeHints
{
get => GetValue(ExtendClientAreaChromeHintsProperty);
set => SetValue(ExtendClientAreaChromeHintsProperty, value);
}
///
/// Gets or Sets the TitlebarHeightHint for when the client area is extended.
/// A value of -1 will cause the titlebar to be auto sized to the OS default.
/// Any other positive value will cause the titlebar to assume that height.
///
public double ExtendClientAreaTitleBarHeightHint
{
get => GetValue(ExtendClientAreaTitleBarHeightHintProperty);
set => SetValue(ExtendClientAreaTitleBarHeightHintProperty, value);
}
///
/// Gets if the ClientArea is Extended into the Window Decorations.
///
public bool IsExtendedIntoWindowDecorations
{
get => _isExtendedIntoWindowDecorations;
private set => SetAndRaise(IsExtendedIntoWindowDecorationsProperty, ref _isExtendedIntoWindowDecorations, value);
}
///
/// Gets the WindowDecorationMargin.
/// This tells you the thickness around the window that is used by borders and the titlebar.
///
public Thickness WindowDecorationMargin
{
get => _windowDecorationMargin;
private set => SetAndRaise(WindowDecorationMarginProperty, ref _windowDecorationMargin, value);
}
///
/// Gets the window margin that is hidden off the screen area.
/// This is generally only the case on Windows when in Maximized where the window border
/// is hidden off the screen. This Margin may be used to ensure user content doesnt overlap this space.
///
public Thickness OffScreenMargin
{
get => _offScreenMargin;
private set => SetAndRaise(OffScreenMarginProperty, ref _offScreenMargin, value);
}
///
/// Sets the system decorations (title bar, border, etc)
///
public SystemDecorations SystemDecorations
{
get => GetValue(SystemDecorationsProperty);
set => SetValue(SystemDecorationsProperty, value);
}
///
/// Gets or sets a value that indicates whether a window is activated when first shown.
///
public bool ShowActivated
{
get => GetValue(ShowActivatedProperty);
set => SetValue(ShowActivatedProperty, value);
}
///
/// Enables or disables the taskbar icon
///
///
public bool ShowInTaskbar
{
get => GetValue(ShowInTaskbarProperty);
set => SetValue(ShowInTaskbarProperty, value);
}
///
/// Gets or sets a value indicating how the event behaves in the presence
/// of child windows.
///
public WindowClosingBehavior ClosingBehavior
{
get => GetValue(ClosingBehaviorProperty);
set => SetValue(ClosingBehaviorProperty, value);
}
///
/// Gets or sets the minimized/maximized state of the window.
///
public WindowState WindowState
{
get => GetValue(WindowStateProperty);
set => SetValue(WindowStateProperty, value);
}
///
/// Enables or disables resizing of the window.
///
public bool CanResize
{
get => GetValue(CanResizeProperty);
set => SetValue(CanResizeProperty, value);
}
///
/// Enables or disables minimizing the window.
///
///
/// This property might be ignored by some window managers on Linux.
///
public bool CanMinimize
{
get => GetValue(CanMinimizeProperty);
set => SetValue(CanMinimizeProperty, value);
}
///
/// Enables or disables maximizing the window.
///
///
/// When is false, this property is always false.
/// On macOS, setting this property to false also disables the full screen mode.
/// This property might be ignored by some window managers on Linux.
///
public bool CanMaximize
{
get => GetValue(CanMaximizeProperty);
set => SetValue(CanMaximizeProperty, value);
}
///
/// Gets or sets the icon of the window.
///
public WindowIcon? Icon
{
get => GetValue(IconProperty);
set => SetValue(IconProperty, value);
}
///
/// Gets or sets the startup location of the window.
///
public WindowStartupLocation WindowStartupLocation
{
get => GetValue(WindowStartupLocationProperty);
set => SetValue(WindowStartupLocationProperty, value);
}
///
/// Gets or sets the window position in screen coordinates.
///
public PixelPoint Position
{
get => PlatformImpl?.Position ?? PixelPoint.Origin;
set
{
PlatformImpl?.Move(value);
_positionWasSet = true;
}
}
///
/// Gets whether this window was opened as a dialog
///
public bool IsDialog => _showingAsDialog;
///
/// Starts moving a window with left button being held. Should be called from left mouse button press event handler
///
public void BeginMoveDrag(PointerPressedEventArgs e) => PlatformImpl?.BeginMoveDrag(e);
///
/// Starts resizing a window. This function is used if an application has window resizing controls.
/// Should be called from left mouse button press event handler
///
public void BeginResizeDrag(WindowEdge edge, PointerPressedEventArgs e) => PlatformImpl?.BeginResizeDrag(edge, e);
///
protected override Type StyleKeyOverride => typeof(Window);
///
/// Fired before a window is closed.
///
public event EventHandler? Closing;
///
/// Closes the window.
///
public void Close()
{
CloseCore(WindowCloseReason.WindowClosing, true, false);
}
///
/// Closes a dialog window with the specified result.
///
/// The dialog result.
///
/// When the window is shown with the
/// or method, the
/// resulting task will produce the value when the window
/// is closed.
///
public void Close(object? dialogResult)
{
_dialogResult = dialogResult;
CloseCore(WindowCloseReason.WindowClosing, true, false);
}
internal void CloseCore(WindowCloseReason reason, bool isProgrammatic, bool ignoreCancel)
{
bool close = true;
try
{
if (ShouldCancelClose(new WindowClosingEventArgs(reason, isProgrammatic)))
{
close = false;
}
}
finally
{
if (close || ignoreCancel)
{
CloseInternal();
}
}
}
///
/// Handles a closing notification from .
/// true if closing is cancelled. Otherwise false.
///
/// The reason the window is closing.
private protected virtual bool HandleClosing(WindowCloseReason reason)
{
if (!ShouldCancelClose(new WindowClosingEventArgs(reason, false)))
{
CloseInternal();
return false;
}
return true;
}
private void CloseInternal()
{
foreach (var (child, _) in _children.ToArray())
{
child.CloseInternal();
}
PlatformImpl?.Dispose();
_showingAsDialog = false;
Owner = null;
}
private bool ShouldCancelClose(WindowClosingEventArgs args)
{
switch (ClosingBehavior)
{
case WindowClosingBehavior.OwnerAndChildWindows:
bool canClose = true;
if (_children.Count > 0)
{
var childArgs = args.CloseReason == WindowCloseReason.WindowClosing ?
new WindowClosingEventArgs(WindowCloseReason.OwnerWindowClosing, args.IsProgrammatic) :
args;
foreach (var (child, _) in _children.ToArray())
{
if (child.ShouldCancelClose(childArgs))
{
canClose = false;
}
}
}
if (canClose)
{
OnClosing(args);
return args.Cancel;
}
return true;
case WindowClosingBehavior.OwnerWindowOnly:
OnClosing(args);
return args.Cancel;
}
return false;
}
private void HandleWindowStateChanged(WindowState state)
{
WindowState = state;
if (state == WindowState.Minimized)
{
StopRendering();
}
else
{
StartRendering();
}
}
protected virtual void ExtendClientAreaToDecorationsChanged(bool isExtended)
{
IsExtendedIntoWindowDecorations = isExtended;
WindowDecorationMargin = PlatformImpl?.ExtendedMargins ?? default;
OffScreenMargin = PlatformImpl?.OffScreenMargin ?? default;
}
///
/// Hides the window but does not close it.
///
public override void Hide()
{
using (FreezeVisibilityChangeHandling())
{
if (!_shown)
{
return;
}
StopRendering();
if (_children.Count > 0)
{
foreach (var child in _children.ToArray())
{
child.child.Hide();
}
}
Owner = null;
PlatformImpl?.Hide();
IsVisible = false;
_modalSubscription?.Dispose();
_shown = false;
}
}
///
/// Shows the window.
///
///
/// The window has already been closed.
///
public override void Show()
{
ShowCore