Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mac: Fix child window focus when there is more than one #2315

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/Eto.Mac/Forms/MacWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1261,15 +1261,32 @@ public virtual void SetOwner(Window owner)
macWindow.Control.AddChildWindow(Control, NSWindowOrderingMode.Above);
OnSetAsChildWindow();
}
Widget.GotFocus += HandleGotFocusAsChild;
}
else
{
Widget.GotFocus -= HandleGotFocusAsChild;
var parentWindow = Control.ParentWindow;
if (parentWindow != null)
parentWindow.RemoveChildWindow(Control);
}
}
}

void HandleGotFocusAsChild(object sender, EventArgs e)
{
// When there are multiple modeless child windows, clicking on one doesn't bring it to front
// so, we remove then re-add the child window to get it to come above again.
var parentWindow = Control.ParentWindow;
var childWindows = parentWindow?.ChildWindows;

// .. only if it isn't already the last child window
if (parentWindow != null && childWindows?.Length > 1 && !Equals(Control.Handle, childWindows[childWindows.Length -1].Handle))
{
parentWindow.RemoveChildWindow(Control);
parentWindow.AddChildWindow(Control, NSWindowOrderingMode.Above);
}
}

internal virtual void OnSetAsChildWindow()
{
Expand Down
50 changes: 50 additions & 0 deletions test/Eto.Test/UnitTests/Forms/WindowTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Eto.Drawing;
using System.Threading;
using System.Threading.Tasks;
using System.ComponentModel;

namespace Eto.Test.UnitTests.Forms
{
Expand Down Expand Up @@ -306,6 +307,55 @@ public void WindowShouldCloseOnLostFocusWithoutHidingParent()
}
);
}

// Hm, this seems useful.. should it be added as an extension method somewhere?
static Task EventAsync<TWidget, TEvent>(TWidget control, Action<TWidget, EventHandler<TEvent>> addHandler, Action<TWidget, EventHandler<TEvent>> removeHandler = null)
where TWidget : Widget
{
var mre = new TaskCompletionSource<bool>();
void EventTriggered(object sender, TEvent e)
{
removeHandler?.Invoke(control, EventTriggered);
mre.TrySetResult(true);
}

addHandler(control, EventTriggered);
return mre.Task;
}

[Test, ManualTest]
public void MultipleChildWindowsShouldGetFocusWhenClicked() => Async(async () =>
{
var form1 = new Form { ClientSize = new Size(200, 200), Location = new Point(300, 300) };
form1.Owner = Application.Instance.MainForm;
form1.Title = "Form1";
form1.Content = new Label
{
VerticalAlignment = VerticalAlignment.Center,
TextAlignment = TextAlignment.Center,
Text = "Click on Form2, it should then get focus and be on top of this form."
};
// var form1ClosedTask = EventTask<EventArgs>(h => form1.Closed += h);
var form1ClosedTask = EventAsync<Form, EventArgs>(form1, (c, h) => c.Closed += h);

var form2 = new Form { ClientSize = new Size(200, 200), Location = new Point(400, 400) };
form2.Owner = Application.Instance.MainForm;
form2.Title = "Form2";
form2.Content = new Label
{
VerticalAlignment = VerticalAlignment.Center,
TextAlignment = TextAlignment.Center,
Text = "Click on Form1, it should then get focus and be on top of this form."
};
var form2ClosedTask = EventAsync<Form, EventArgs>(form2, (c, h) => c.Closed += h);

form1.Show();

form2.Show();

// wait till both forms are closed..
await Task.WhenAll(form1ClosedTask, form2ClosedTask);
});
}
}