diff --git a/src/Eto.Mac/Forms/FloatingFormHandler.cs b/src/Eto.Mac/Forms/FloatingFormHandler.cs index c562031f62..a19bebf48a 100644 --- a/src/Eto.Mac/Forms/FloatingFormHandler.cs +++ b/src/Eto.Mac/Forms/FloatingFormHandler.cs @@ -32,17 +32,10 @@ protected override void Initialize() protected override NSWindowLevel TopmostWindowLevel => NSWindowLevel.Floating; - public override void SetOwner(Window owner) - { - base.SetOwner(owner); - - SetLevelAdjustment(); - } - void SetLevelAdjustment() { // only need to adjust level when window style is not utility and we actually want it to be topmost (default for FloatingForm). - var wantsTopmost = Widget.Properties.Get(Topmost_Key, true); + var wantsTopmost = WantsTopmost; var owner = Widget.Owner; var needsLevelAdjust = wantsTopmost && WindowStyle != WindowStyle.Utility && owner != null; @@ -72,12 +65,16 @@ void SetLevelAdjustment() { lastOwner.GotFocus -= Owner_GotFocus; lastOwner.LostFocus -= Owner_LostFocus; + Widget.GotFocus -= Owner_GotFocus; + Widget.LostFocus -= Owner_LostFocus; Widget.Closed -= Widget_Closed; } if (owner != null) { owner.GotFocus += Owner_GotFocus; owner.LostFocus += Owner_LostFocus; + Widget.GotFocus += Owner_GotFocus; + Widget.LostFocus += Owner_LostFocus; Widget.Closed += Widget_Closed; } } @@ -94,10 +91,12 @@ private void Widget_Closed(object sender, EventArgs e) // when closed we need to disconnect from owner to prevent leaks lastOwner.GotFocus -= Owner_GotFocus; lastOwner.LostFocus -= Owner_LostFocus; + Widget.GotFocus -= Owner_GotFocus; + Widget.LostFocus -= Owner_LostFocus; } } - static readonly object Topmost_Key = new object(); + internal override bool DefaultTopmost => true; public override bool Topmost { @@ -105,8 +104,6 @@ public override bool Topmost set { base.Topmost = value; - // need to remember the preferred state as it can be changed on us when setting the owner - Widget.Properties.Set(Topmost_Key, value, true); SetLevelAdjustment(); } } @@ -134,9 +131,15 @@ void SetAsTopmost() private void Owner_LostFocus(object sender, EventArgs e) { + if (HasFocus || Widget.Owner.HasFocus) + return; Control.Level = NSWindowLevel.Normal; + + // Window will still be topmost until we order the window explicitly. + // If there are multiple app windows, we use this instead of OrderFront otherwise + // the original parent window steals focus again. if (Control.IsVisible) - Control.OrderFront(Control); + Control.OrderWindow(NSWindowOrderingMode.Above, Control.ParentWindow?.WindowNumber ?? 0); } protected override NSPanel CreateControl() @@ -151,5 +154,11 @@ protected override NSPanel CreateControl() return panel; } + + internal override void OnSetAsChildWindow() + { + // Don't call base, we do our own window level logic for FloatingForm. + SetLevelAdjustment(); + } } } diff --git a/src/Eto.Mac/Forms/MacWindow.cs b/src/Eto.Mac/Forms/MacWindow.cs index 011801b1a9..5db0e808fe 100644 --- a/src/Eto.Mac/Forms/MacWindow.cs +++ b/src/Eto.Mac/Forms/MacWindow.cs @@ -196,6 +196,7 @@ static class MacWindow internal static readonly IntPtr selIsVisible_Handle = Selector.GetHandle("isVisible"); internal static readonly object AnimateSizeChanges_Key = new object(); internal static readonly object DisableAutoSize_Key = new object(); + internal static readonly object Topmost_Key = new object(); } public abstract class MacWindow : MacPanel, Window.IHandler, IMacWindow @@ -604,12 +605,22 @@ public virtual bool Topmost get => Control.Level >= NSWindowLevel.Floating; set { - if (Topmost != value) + // need to remember the preferred state as it can be changed on us when setting the owner + if (WantsTopmost != value) { + WantsTopmost = value; Control.Level = value ? TopmostWindowLevel : NSWindowLevel.Normal; } } } + + internal virtual bool DefaultTopmost => false; + + internal bool WantsTopmost + { + get => Widget.Properties.Get(MacWindow.Topmost_Key, DefaultTopmost); + set => Widget.Properties.Set(MacWindow.Topmost_Key, value, DefaultTopmost); + } public override Size Size { @@ -1212,7 +1223,10 @@ public virtual void SetOwner(Window owner) { var macWindow = owner.Handler as IMacWindow; if (macWindow != null) + { macWindow.Control.AddChildWindow(Control, NSWindowOrderingMode.Above); + OnSetAsChildWindow(); + } } else { @@ -1223,6 +1237,12 @@ public virtual void SetOwner(Window owner) } } + internal virtual void OnSetAsChildWindow() + { + if (WantsTopmost && Control.Level != TopmostWindowLevel) + Control.Level = TopmostWindowLevel; + } + public float LogicalPixelSize => Screen?.LogicalPixelSize ?? 1f; } } diff --git a/src/Eto.Mac/Forms/NativeFormHandler.cs b/src/Eto.Mac/Forms/NativeFormHandler.cs index 313318e858..fe49d9ae7a 100644 --- a/src/Eto.Mac/Forms/NativeFormHandler.cs +++ b/src/Eto.Mac/Forms/NativeFormHandler.cs @@ -17,20 +17,20 @@ public NativeFormHandler(NSWindowController windowController) public override void AttachEvent(string id) { - // native window, so attach notifications instead of using the delegate so we don't clobber existing functionality + // native window, so attach observers instead of using the delegate so we don't clobber existing functionality switch (id) { case Window.ClosedEvent: - NSNotificationCenter.DefaultCenter.AddObserver(NSWindow.WillCloseNotification, n => Callback.OnClosed(Widget, EventArgs.Empty)); + AddObserver(NSWindow.WillCloseNotification, n => Callback.OnClosed(Widget, EventArgs.Empty)); break; case Window.SizeChangedEvent: - NSNotificationCenter.DefaultCenter.AddObserver(NSWindow.DidResizeNotification, n => Callback.OnSizeChanged(Widget, EventArgs.Empty)); + AddObserver(NSWindow.DidResizeNotification, n => Callback.OnSizeChanged(Widget, EventArgs.Empty)); break; case Window.GotFocusEvent: - NSNotificationCenter.DefaultCenter.AddObserver(NSWindow.DidBecomeKeyNotification, n => Callback.OnGotFocus(Widget, EventArgs.Empty)); + AddObserver(NSWindow.DidBecomeKeyNotification, n => Callback.OnGotFocus(Widget, EventArgs.Empty)); break; case Window.LostFocusEvent: - NSNotificationCenter.DefaultCenter.AddObserver(NSWindow.DidResignKeyNotification, n => Callback.OnLostFocus(Widget, EventArgs.Empty)); + AddObserver(NSWindow.DidResignKeyNotification, n => Callback.OnLostFocus(Widget, EventArgs.Empty)); break; } return;