using System; using System.Collections.Generic; using System.Linq; using System.Reactive.Disposables; using System.Reactive.Linq; using System.Text; using System.Threading.Tasks; using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.Layout; using Avalonia.Platform; using JetBrains.Annotations; namespace Avalonia.Controls { /// /// Base class for top-level windows. /// /// /// This class acts as a base for top level windows such as and /// . It handles scheduling layout, styling and rendering as well as /// tracking the window and state. /// public class WindowBase : TopLevel { /// /// Defines the property. /// public static readonly DirectProperty IsActiveProperty = AvaloniaProperty.RegisterDirect(nameof(IsActive), o => o.IsActive); /// /// Defines the property. /// public static readonly DirectProperty OwnerProperty = AvaloniaProperty.RegisterDirect( nameof(Owner), o => o.Owner, (o, v) => o.Owner = v); public static readonly StyledProperty TopmostProperty = AvaloniaProperty.Register(nameof(Topmost)); private bool _hasExecutedInitialLayoutPass; private bool _isActive; private bool _ignoreVisibilityChange; private WindowBase _owner; static WindowBase() { IsVisibleProperty.OverrideDefaultValue(false); IsVisibleProperty.Changed.AddClassHandler(x => x.IsVisibleChanged); MinWidthProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size((double)e.NewValue, w.MinHeight), new Size(w.MaxWidth, w.MaxHeight))); MinHeightProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size(w.MinWidth, (double)e.NewValue), new Size(w.MaxWidth, w.MaxHeight))); MaxWidthProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size(w.MinWidth, w.MinHeight), new Size((double)e.NewValue, w.MaxHeight))); MaxHeightProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size(w.MinWidth, w.MinHeight), new Size(w.MaxWidth, (double)e.NewValue))); TopmostProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.SetTopmost((bool)e.NewValue)); } public WindowBase(IWindowBaseImpl impl) : this(impl, AvaloniaLocator.Current) { } public WindowBase(IWindowBaseImpl impl, IAvaloniaDependencyResolver dependencyResolver) : base(impl, dependencyResolver) { impl.Activated = HandleActivated; impl.Deactivated = HandleDeactivated; impl.PositionChanged = HandlePositionChanged; this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl?.Resize(x)); } /// /// Fired when the window is activated. /// public event EventHandler Activated; /// /// Fired when the window is deactivated. /// public event EventHandler Deactivated; /// /// Fired when the window position is changed. /// public event EventHandler PositionChanged; [CanBeNull] public new IWindowBaseImpl PlatformImpl => (IWindowBaseImpl) base.PlatformImpl; /// /// Gets a value that indicates whether the window is active. /// public bool IsActive { get { return _isActive; } private set { SetAndRaise(IsActiveProperty, ref _isActive, value); } } /// /// Gets or sets the window position in screen coordinates. /// public Point Position { get { return PlatformImpl?.Position ?? default(Point); } set { if (PlatformImpl is IWindowBaseImpl impl) impl.Position = value; } } /// /// Whether an auto-size operation is in progress. /// protected bool AutoSizing { get; private set; } /// /// Gets or sets the owner of the window. /// public WindowBase Owner { get { return _owner; } set { SetAndRaise(OwnerProperty, ref _owner, value); } } /// /// Gets or sets whether this window appears on top of all other windows /// public bool Topmost { get { return GetValue(TopmostProperty); } set { SetValue(TopmostProperty, value); } } /// /// Activates the window. /// public void Activate() { PlatformImpl?.Activate(); } /// /// Hides the popup. /// public virtual void Hide() { _ignoreVisibilityChange = true; try { Renderer?.Stop(); PlatformImpl?.Hide(); IsVisible = false; } finally { _ignoreVisibilityChange = false; } } /// /// Shows the popup. /// public virtual void Show() { _ignoreVisibilityChange = true; try { EnsureInitialized(); IsVisible = true; if (!_hasExecutedInitialLayoutPass) { LayoutManager.ExecuteInitialLayoutPass(this); _hasExecutedInitialLayoutPass = true; } PlatformImpl?.Show(); Renderer?.Start(); } finally { _ignoreVisibilityChange = false; } } /// /// Begins an auto-resize operation. /// /// A disposable used to finish the operation. /// /// When an auto-resize operation is in progress any resize events received will not be /// cause the new size to be written to the and /// properties. /// protected IDisposable BeginAutoSizing() { AutoSizing = true; return Disposable.Create(() => AutoSizing = false); } /// /// Carries out the arrange pass of the window. /// /// The final window size. /// The parameter unchanged. protected override Size ArrangeOverride(Size finalSize) { using (BeginAutoSizing()) { PlatformImpl?.Resize(finalSize); } return base.ArrangeOverride(PlatformImpl?.ClientSize ?? default(Size)); } /// /// Ensures that the window is initialized. /// protected void EnsureInitialized() { if (!IsInitialized) { var init = (ISupportInitialize)this; init.BeginInit(); init.EndInit(); } } protected override void HandleClosed() { _ignoreVisibilityChange = true; try { IsVisible = false; base.HandleClosed(); } finally { _ignoreVisibilityChange = false; } } /// /// Handles a resize notification from . /// /// The new client size. protected override void HandleResized(Size clientSize) { if (!AutoSizing) { Width = clientSize.Width; Height = clientSize.Height; } ClientSize = clientSize; LayoutManager.ExecuteLayoutPass(); Renderer?.Resized(clientSize); } /// /// Handles a window position change notification from /// . /// /// The window position. private void HandlePositionChanged(Point pos) { PositionChanged?.Invoke(this, new PointEventArgs(pos)); } /// /// Handles an activated notification from . /// private void HandleActivated() { Activated?.Invoke(this, EventArgs.Empty); var scope = this as IFocusScope; if (scope != null) { FocusManager.Instance.SetFocusScope(scope); } IsActive = true; } /// /// Handles a deactivated notification from . /// private void HandleDeactivated() { IsActive = false; Deactivated?.Invoke(this, EventArgs.Empty); } private void IsVisibleChanged(AvaloniaPropertyChangedEventArgs e) { if (!_ignoreVisibilityChange) { if ((bool)e.NewValue) { Show(); } else { Hide(); } } } /// /// Starts moving a window with left button being held. Should be called from left mouse button press event handler /// public void BeginMoveDrag() => PlatformImpl?.BeginMoveDrag(); /// /// 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) => PlatformImpl?.BeginResizeDrag(edge); } }