using System; using System.ComponentModel; using System.Linq; using System.Reactive.Disposables; using System.Reactive.Linq; using Avalonia.Automation.Peers; 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,e) => x.IsVisibleChanged(e)); 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) { Screens = new Screens(impl.Screen); impl.Activated = HandleActivated; impl.Deactivated = HandleDeactivated; impl.PositionChanged = HandlePositionChanged; } /// /// 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; 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); } } public Screens Screens { get; private set; } [Obsolete("No longer used. Always returns false.")] protected bool AutoSizing => false; /// /// Gets or sets the owner of the window. /// public WindowBase? Owner { get { return _owner; } protected 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 window. /// public virtual void Show() { _ignoreVisibilityChange = true; try { EnsureInitialized(); IsVisible = true; if (!_hasExecutedInitialLayoutPass) { LayoutManager.ExecuteInitialLayoutPass(); _hasExecutedInitialLayoutPass = true; } PlatformImpl?.Show(true, false); Renderer?.Start(); OnOpened(EventArgs.Empty); } finally { _ignoreVisibilityChange = false; } } [Obsolete("No longer used. Has no effect.")] protected IDisposable BeginAutoSizing() => Disposable.Empty; /// /// Ensures that the window is initialized. /// protected void EnsureInitialized() { if (!IsInitialized) { var init = (ISupportInitialize)this; init.BeginInit(); init.EndInit(); } } /// protected override void OnClosed(EventArgs e) { // Window must manually raise Loaded/Unloaded events as it is a visual root and // does not raise OnAttachedToVisualTreeCore/OnDetachedFromVisualTreeCore events OnUnloadedCore(); base.OnClosed(e); } /// protected override void OnOpened(EventArgs e) { // Window must manually raise Loaded/Unloaded events as it is a visual root and // does not raise OnAttachedToVisualTreeCore/OnDetachedFromVisualTreeCore events ScheduleOnLoadedCore(); base.OnOpened(e); } protected override void HandleClosed() { _ignoreVisibilityChange = true; try { IsVisible = false; if (this is IFocusScope scope) { FocusManager.Instance?.RemoveFocusScope(scope); } base.HandleClosed(); } finally { _ignoreVisibilityChange = false; } } [Obsolete("Use HandleResized(Size, PlatformResizeReason)")] protected override void HandleResized(Size clientSize) => HandleResized(clientSize, PlatformResizeReason.Unspecified); /// /// Handles a resize notification from . /// /// The new client size. /// The reason for the resize. protected override void HandleResized(Size clientSize, PlatformResizeReason reason) { ClientSize = clientSize; FrameSize = PlatformImpl?.FrameSize; LayoutManager.ExecuteLayoutPass(); Renderer?.Resized(clientSize); } /// /// Overrides the core measure logic for windows. /// /// The available size. /// The measured size. /// /// The layout logic for top-level windows is different than for other controls because /// they don't have a parent, meaning that many layout properties handled by the default /// MeasureCore (such as margins and alignment) make no sense. /// protected override Size MeasureCore(Size availableSize) { ApplyStyling(); ApplyTemplate(); var constraint = LayoutHelper.ApplyLayoutConstraints(this, availableSize); return MeasureOverride(constraint); } /// /// Overrides the core arrange logic for windows. /// /// The final arrange rect. /// /// The layout logic for top-level windows is different than for other controls because /// they don't have a parent, meaning that many layout properties handled by the default /// ArrangeCore (such as margins and alignment) make no sense. /// protected override void ArrangeCore(Rect finalRect) { var constraint = ArrangeSetBounds(finalRect.Size); var arrangeSize = ArrangeOverride(constraint); Bounds = new Rect(arrangeSize); } /// /// Called during the arrange pass to set the size of the window. /// /// The requested size of the window. /// The actual size of the window. protected virtual Size ArrangeSetBounds(Size size) => size; /// /// Handles a window position change notification from /// . /// /// The window position. private void HandlePositionChanged(PixelPoint pos) { PositionChanged?.Invoke(this, new PixelPointEventArgs(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(); } } } } }