// Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; using System.Reactive.Linq; using System.Threading.Tasks; using Avalonia.Controls.Platform; using Avalonia.Input; using Avalonia.Layout; using Avalonia.Media; using Avalonia.Platform; using Avalonia.Styling; using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; using System.ComponentModel; 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, } /// /// A top-level window. /// public class Window : WindowBase, IStyleable, IFocusScope, ILayoutRoot, INameScope { /// Defines the property. /// public static readonly StyledProperty SizeToContentProperty = AvaloniaProperty.Register(nameof(SizeToContent)); /// /// Enables or disables system window decorations (title bar, buttons, etc) /// public static readonly StyledProperty HasSystemDecorationsProperty = AvaloniaProperty.Register(nameof(HasSystemDecorations), true); /// /// Enables or disables the taskbar icon /// public static readonly StyledProperty ShowInTaskbarProperty = AvaloniaProperty.Register(nameof(ShowInTaskbar), true); /// /// 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 proeprty. /// public static readonly DirectProperty WindowStartupLocationProperty = AvaloniaProperty.RegisterDirect( nameof(WindowStartupLocation), o => o.WindowStartupLocation, (o, v) => o.WindowStartupLocation = v); public static readonly StyledProperty CanResizeProperty = AvaloniaProperty.Register(nameof(CanResize), true); private readonly NameScope _nameScope = new NameScope(); private object _dialogResult; private readonly Size _maxPlatformClientSize; private WindowStartupLocation _windowStartupLoction; /// /// Initializes static members of the class. /// static Window() { BackgroundProperty.OverrideDefaultValue(typeof(Window), Brushes.White); TitleProperty.Changed.AddClassHandler((s, e) => s.PlatformImpl?.SetTitle((string)e.NewValue)); HasSystemDecorationsProperty.Changed.AddClassHandler( (s, e) => s.PlatformImpl?.SetSystemDecorations((bool)e.NewValue)); ShowInTaskbarProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.ShowTaskbarIcon((bool)e.NewValue)); IconProperty.Changed.AddClassHandler((s, e) => s.PlatformImpl?.SetIcon(((WindowIcon)e.NewValue).PlatformImpl)); CanResizeProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.CanResize((bool)e.NewValue)); WindowStateProperty.Changed.AddClassHandler( (w, e) => { if (w.PlatformImpl != null) w.PlatformImpl.WindowState = (WindowState)e.NewValue; }); } /// /// 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.WindowStateChanged = HandleWindowStateChanged; _maxPlatformClientSize = PlatformImpl?.MaxClientSize ?? default(Size); Screens = new Screens(PlatformImpl?.Screen); } /// event EventHandler INameScope.Registered { add { _nameScope.Registered += value; } remove { _nameScope.Registered -= value; } } /// event EventHandler INameScope.Unregistered { add { _nameScope.Unregistered += value; } remove { _nameScope.Unregistered -= value; } } public Screens Screens { get; private set; } /// /// Gets the platform-specific window implementation. /// [CanBeNull] public new IWindowImpl PlatformImpl => (IWindowImpl)base.PlatformImpl; /// /// Gets or sets a value indicating how the window will size itself to fit its content. /// public SizeToContent SizeToContent { get { return GetValue(SizeToContentProperty); } set { SetValue(SizeToContentProperty, value); } } /// /// Gets or sets the title of the window. /// public string Title { get { return GetValue(TitleProperty); } set { SetValue(TitleProperty, value); } } /// /// Enables or disables system window decorations (title bar, buttons, etc) /// /// public bool HasSystemDecorations { get { return GetValue(HasSystemDecorationsProperty); } set { SetValue(HasSystemDecorationsProperty, value); } } /// /// Enables or disables the taskbar icon /// /// public bool ShowInTaskbar { get { return GetValue(ShowInTaskbarProperty); } set { SetValue(ShowInTaskbarProperty, value); } } /// /// Gets or sets the minimized/maximized state of the window. /// public WindowState WindowState { get { return GetValue(WindowStateProperty); } set { SetValue(WindowStateProperty, value); } } /// /// Enables or disables resizing of the window. /// Note that if is set to False then this property /// has no effect and should be treated as a recommendation for the user setting HasSystemDecorations. /// public bool CanResize { get { return GetValue(CanResizeProperty); } set { SetValue(CanResizeProperty, value); } } /// /// Gets or sets the icon of the window. /// public WindowIcon Icon { get { return GetValue(IconProperty); } set { SetValue(IconProperty, value); } } /// /// Gets or sets the startup location of the window. /// public WindowStartupLocation WindowStartupLocation { get { return _windowStartupLoction; } set { SetAndRaise(WindowStartupLocationProperty, ref _windowStartupLoction, value); } } /// Size ILayoutRoot.MaxClientSize => _maxPlatformClientSize; /// Type IStyleable.StyleKey => typeof(Window); /// /// Fired before a window is closed. /// public event EventHandler Closing; private static void AddWindow(Window window) { if (Application.Current == null) { return; } Application.Current.Windows.Add(window); } private static void RemoveWindow(Window window) { if (Application.Current == null) { return; } Application.Current.Windows.Remove(window); } /// /// Closes the window. /// public void Close() { Close(false); } protected override void HandleApplicationExiting() { base.HandleApplicationExiting(); Close(true); } /// /// Closes a dialog window with the specified result. /// /// The dialog result. /// /// When the window is shown with the method, the /// resulting task will produce the value when the window /// is closed. /// public void Close(object dialogResult) { _dialogResult = dialogResult; Close(false); } internal void Close(bool ignoreCancel) { try { if (!ignoreCancel && HandleClosing()) { return; } } finally { PlatformImpl?.Dispose(); HandleClosed(); } } /// /// Handles a closing notification from . /// protected virtual bool HandleClosing() { var args = new CancelEventArgs(); Closing?.Invoke(this, args); return args.Cancel; } protected virtual void HandleWindowStateChanged(WindowState state) { WindowState = state; if (state == WindowState.Minimized) { Renderer.Stop(); } else { Renderer.Start(); } } /// /// Hides the window but does not close it. /// public override void Hide() { if (!IsVisible) { return; } using (BeginAutoSizing()) { Renderer?.Stop(); PlatformImpl?.Hide(); } IsVisible = false; } /// /// Shows the window. /// public override void Show() { if (IsVisible) { return; } AddWindow(this); EnsureInitialized(); IsVisible = true; LayoutManager.Instance.ExecuteInitialLayoutPass(this); using (BeginAutoSizing()) { PlatformImpl?.Show(); Renderer?.Start(); } SetWindowStartupLocation(); } /// /// Shows the window as a dialog. /// /// /// A task that can be used to track the lifetime of the dialog. /// public Task ShowDialog() { return ShowDialog(); } /// /// Shows the window as a dialog. /// /// /// The type of the result produced by the dialog. /// /// . /// A task that can be used to retrive the result of the dialog when it closes. /// public Task ShowDialog() { if (IsVisible) { throw new InvalidOperationException("The window is already being shown."); } AddWindow(this); EnsureInitialized(); SetWindowStartupLocation(); IsVisible = true; LayoutManager.Instance.ExecuteInitialLayoutPass(this); using (BeginAutoSizing()) { var affectedWindows = Application.Current.Windows.Where(w => w.IsEnabled && w != this).ToList(); var activated = affectedWindows.Where(w => w.IsActive).FirstOrDefault(); SetIsEnabled(affectedWindows, false); var modal = PlatformImpl?.ShowDialog(); var result = new TaskCompletionSource(); Renderer?.Start(); Observable.FromEventPattern( x => this.Closed += x, x => this.Closed -= x) .Take(1) .Subscribe(_ => { modal?.Dispose(); SetIsEnabled(affectedWindows, true); activated?.Activate(); result.SetResult((TResult)(_dialogResult ?? default(TResult))); }); return result.Task; } } void SetIsEnabled(IEnumerable windows, bool isEnabled) { foreach (var window in windows) { window.IsEnabled = isEnabled; } } void SetWindowStartupLocation() { if (WindowStartupLocation == WindowStartupLocation.CenterScreen) { var screen = Screens.ScreenFromPoint(Bounds.Position); if (screen != null) Position = screen.WorkingArea.CenterRect(new Rect(ClientSize)).Position; } else if (WindowStartupLocation == WindowStartupLocation.CenterOwner) { if (Owner != null) { var positionAsSize = Owner.ClientSize / 2 - ClientSize / 2; Position = Owner.Position + new Point(positionAsSize.Width, positionAsSize.Height); } } } /// void INameScope.Register(string name, object element) { _nameScope.Register(name, element); } /// object INameScope.Find(string name) { return _nameScope.Find(name); } /// void INameScope.Unregister(string name) { _nameScope.Unregister(name); } /// protected override Size MeasureOverride(Size availableSize) { var sizeToContent = SizeToContent; var clientSize = ClientSize; Size constraint = clientSize; if ((sizeToContent & SizeToContent.Width) != 0) { constraint = constraint.WithWidth(double.PositiveInfinity); } if ((sizeToContent & SizeToContent.Height) != 0) { constraint = constraint.WithHeight(double.PositiveInfinity); } var result = base.MeasureOverride(constraint); if ((sizeToContent & SizeToContent.Width) == 0) { result = result.WithWidth(clientSize.Width); } if ((sizeToContent & SizeToContent.Height) == 0) { result = result.WithHeight(clientSize.Height); } return result; } protected override void HandleClosed() { RemoveWindow(this); base.HandleClosed(); } /// protected override void HandleResized(Size clientSize) { if (!AutoSizing) { SizeToContent = SizeToContent.Manual; } base.HandleResized(clientSize); } } } namespace Avalonia { public static class WindowApplicationExtensions { public static void RunWithMainWindow(this Application app) where TWindow : Avalonia.Controls.Window, new() { var window = new TWindow(); window.Show(); app.Run(window); } } }