using System; using System.ComponentModel; using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.Layout; using Avalonia.Platform; 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 int _ignoreVisibilityChanges; private WindowBase? _owner; protected bool IgnoreVisibilityChanges => _ignoreVisibilityChanges > 0; 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; } protected IDisposable FreezeVisibilityChangeHandling() { return new IgnoreVisibilityChangesDisposable(this); } /// /// 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; } /// /// 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() { using (FreezeVisibilityChangeHandling()) { Renderer.Stop(); PlatformImpl?.Hide(); IsVisible = false; } } /// /// Shows the window. /// public virtual void Show() { using (FreezeVisibilityChangeHandling()) { EnsureInitialized(); ApplyStyling(); IsVisible = true; if (!_hasExecutedInitialLayoutPass) { LayoutManager.ExecuteInitialLayoutPass(); _hasExecutedInitialLayoutPass = true; } PlatformImpl?.Show(true, false); Renderer.Start(); OnOpened(EventArgs.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() { using (FreezeVisibilityChangeHandling()) { IsVisible = false; if (this is IFocusScope scope) { FocusManager.Instance?.RemoveFocusScope(scope); } base.HandleClosed(); } } /// /// Handles a resize notification from . /// /// The new client size. /// The reason for the resize. protected override void HandleResized(Size clientSize, PlatformResizeReason reason) { FrameSize = PlatformImpl?.FrameSize; if (ClientSize != clientSize) { ClientSize = clientSize; 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); } protected virtual void IsVisibleChanged(AvaloniaPropertyChangedEventArgs e) { if (_ignoreVisibilityChanges == 0) { if ((bool)e.NewValue!) { Show(); } else { Hide(); } } } private readonly struct IgnoreVisibilityChangesDisposable : IDisposable { private readonly WindowBase _windowBase; public IgnoreVisibilityChangesDisposable(WindowBase windowBase) { _windowBase = windowBase; _windowBase._ignoreVisibilityChanges++; } public void Dispose() { _windowBase._ignoreVisibilityChanges--; } } } }