Skip to content

Commit

Permalink
Merge pull request #2238 from cwensley/curtis/mac-repsect-topmost-on-…
Browse files Browse the repository at this point in the history
…form

Mac: Respect Window.Topmost even when Owner is set
  • Loading branch information
cwensley authored Jun 3, 2022
2 parents 9490e28 + 57544a3 commit 11caf99
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 18 deletions.
33 changes: 21 additions & 12 deletions src/Eto.Mac/Forms/FloatingFormHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<bool>(Topmost_Key, true);
var wantsTopmost = WantsTopmost;
var owner = Widget.Owner;
var needsLevelAdjust = wantsTopmost && WindowStyle != WindowStyle.Utility && owner != null;

Expand Down Expand Up @@ -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;
}
}
Expand All @@ -94,19 +91,19 @@ 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
{
get => base.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();
}
}
Expand Down Expand Up @@ -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()
Expand All @@ -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();
}
}
}
22 changes: 21 additions & 1 deletion src/Eto.Mac/Forms/MacWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<TControl, TWidget, TCallback> : MacPanel<TControl, TWidget, TCallback>, Window.IHandler, IMacWindow
Expand Down Expand Up @@ -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
{
Expand Down Expand Up @@ -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
{
Expand All @@ -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;
}
}
10 changes: 5 additions & 5 deletions src/Eto.Mac/Forms/NativeFormHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 11caf99

Please sign in to comment.