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

Fix key handling for windows on Mac/Wpf #2757

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
6 changes: 5 additions & 1 deletion src/Eto.Mac/Forms/MacViewTextInput.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Eto.Mac.Forms
{
class MacViewTextInput
static class MacViewTextInput
{
internal static IntPtr HasMarkedText_Selector = Selector.GetHandle("hasMarkedText");
internal static MarshalDelegates.Func_IntPtr_IntPtr_bool HasMarkedText_Delegate = HasMarkedText;
Expand Down Expand Up @@ -57,6 +57,10 @@ static CGRect FirstRectForCharacterRange(IntPtr sender, IntPtr sel, NSRange rang
internal static MarshalDelegates.Action_IntPtr_IntPtr_IntPtr DoCommandBySelector_Delegate = DoCommandBySelector;
static void DoCommandBySelector(IntPtr sender, IntPtr sel, IntPtr selector)
{
var obj = Runtime.GetNSObject(sender);

if (obj != null && ObjCExtensions.SuperClassInstancesRespondsToSelector(obj, sel))
Messaging.void_objc_msgSendSuper_IntPtr(obj.SuperHandle, sel, selector);
}

internal static IntPtr NSTextInputClientProtocol_Handle = ObjCExtensions.GetProtocolHandle("NSTextInputClient");
Expand Down
12 changes: 7 additions & 5 deletions src/Eto.Wpf/Forms/WpfFrameworkElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,8 @@ public virtual void ResumeLayout()

protected virtual sw.FrameworkElement FocusControl => Control;

protected virtual sw.FrameworkElement KeyEventControl => Control;

public virtual void Focus()
{
if (FocusControl.IsLoaded)
Expand Down Expand Up @@ -461,20 +463,20 @@ public override void AttachEvent(string id)
case Eto.Forms.Control.KeyDownEvent:
if (UseKeyPreview)
{
Control.PreviewKeyDown += HandleKeyDown;
Control.PreviewTextInput += HandleTextInput;
KeyEventControl.PreviewKeyDown += HandleKeyDown;
KeyEventControl.PreviewTextInput += HandleTextInput;
}
else
{
Control.KeyDown += HandleKeyDown;
Control.TextInput += HandleTextInput;
KeyEventControl.KeyDown += HandleKeyDown;
KeyEventControl.TextInput += HandleTextInput;
}
break;
case Eto.Forms.Control.TextInputEvent:
HandleEvent(Eto.Forms.Control.KeyDownEvent);
break;
case Eto.Forms.Control.KeyUpEvent:
Control.KeyUp += HandleKeyUp;
KeyEventControl.KeyUp += HandleKeyUp;
break;
case Eto.Forms.Control.ShownEvent:
ContainerControl.IsVisibleChanged += HandleIsVisibleChanged;
Expand Down
2 changes: 2 additions & 0 deletions src/Eto.Wpf/Forms/WpfWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ public override IntPtr NativeHandle

public swc.DockPanel MainPanel { get { return main; } }

protected override sw.FrameworkElement KeyEventControl => main;

public bool MovableByWindowBackground
{
get => Widget.Properties.Get<bool>(WpfWindow.MovableByWindowBackground_Key);
Expand Down
20 changes: 19 additions & 1 deletion test/Eto.Test/UnitTests/Forms/MenuItemTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,25 @@ public void TextBoxShouldAcceptInputEvenWhenShortcutDefined(bool enabled, Keys k
[ManualTest]
public void DrawableShouldAcceptInputEvenWhenShortcutDefined(bool enabled, Keys key, bool handleKey, bool shouldBeIntrinsic)
{
ControlShouldAcceptInputEvenWhenShortcutDefined<Drawable>(enabled, key, handleKey, shouldBeIntrinsic, d => d.CanFocus = true);
ControlShouldAcceptInputEvenWhenShortcutDefined<Drawable>(enabled, key, handleKey, shouldBeIntrinsic, d =>
{
d.CanFocus = true;
d.BackgroundColor = Colors.Blue;
d.KeyDown += (sender, e) =>
{
if (e.KeyData == key)
{
d.BackgroundColor = Colors.Green;
}
};
d.KeyUp += (sender, e) =>
{
if (e.KeyData == key)
{
d.BackgroundColor = Colors.Blue;
}
};
});
}

void ControlShouldAcceptInputEvenWhenShortcutDefined<T>(bool enabled, Keys key, bool handleKey, bool shouldBeIntrinsic, Action<T> initialize = null)
Expand Down
98 changes: 93 additions & 5 deletions test/Eto.Test/UnitTests/Forms/WindowTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,14 +200,14 @@ public void WindowFromPointShouldReturnWindowUnderPoint()
var timer = new UITimer { Interval = 0.5 };
timer.Elapsed += (sender, e) =>
{
var window = Window.FromPoint(Mouse.Position);
content.Content = $"Window: {window?.Title}";
};
var window = Window.FromPoint(Mouse.Position);
content.Content = $"Window: {window?.Title}";
};
timer.Start();
form.Closed += (sender, e) =>
{
timer.Stop();
};
timer.Stop();
};
form.Title = "Test Form";
return content;
}
Expand Down Expand Up @@ -395,4 +395,92 @@ public void WindowShouldNotBeShowingDuringLoadComplete() => Async(-1, async () =
Assert.That(loadComplete, Is.True, "#1.1 - Window should be visible during LoadComplete");
Assert.That(shown, Is.True, "#1.2 - Window should be visible during Shown");
});

[Test]
[ManualTest]
public void HandlingTextInputOnWindowShouldNotPreventShortcuts()
{
bool? copyCalled = null;
bool? keyDownInControl = null;
bool? keyDownInWindow = null;
ManualTest(Platform.Instance.IsMac ? "Press CMD+C" : "Press Ctrl+C", form =>
{
// var ctl = new Drawable { CanFocus = true, Size = new Size(200, 200), BackgroundColor = Colors.Blue };
var ctl = new WebView { Size = new Size(200, 200) };
ctl.KeyDown += (sender, e) =>
{
if (e.KeyData == (Application.Instance.CommonModifier | Keys.C))
{
keyDownInControl = true;
}
};
ctl.TextInput += (sender, e) =>
{
Log.Write(ctl, "TextInput called!");
};

form.KeyDown += (sender, e) =>
{
if (e.KeyData == (Application.Instance.CommonModifier | Keys.C))
{
keyDownInWindow = true;
}
};
form.TextInput += (sender, e) =>
{
Log.Write(form, "TextInput called!");
};

void Close(object sender, EventArgs e)
{
form.Close();
}

void Copy(object sender, EventArgs e)
{
Log.Write(null, "Copy called!");
copyCalled = true;
form.Close();
}

ctl.DocumentLoaded += (sender, e) =>
{
ctl.Focus();
};
ctl.LoadHtml("<html><body><p>Test</p></body></html>");

var bar = new MenuBar
{
IncludeSystemItems = MenuBarSystemItems.Quit,
Items = {
new SubMenuItem { Text = "Close", Items = {
new ButtonMenuItem(Close) { Text = "Close", Shortcut = Application.Instance.CommonModifier | Keys.W }
}
},
new SubMenuItem { Text = "Edit", Items = {
new ButtonMenuItem(Copy) { Text = "Copy", Shortcut = Application.Instance.CommonModifier | Keys.C }
}
}
}
};

form.Menu = bar;


return ctl;
});
Assert.That(copyCalled, Is.True, "#1 - Copy menuItem was not called");
if (Platform.Instance.IsMac || Platform.Instance.IsGtk)
{
// in case this gets "fixed" later to behave like other platforms.
// Gtk and Mac prioritize menu shortcuts over control key events
Assert.That(keyDownInControl, Is.Null, "#2 - KeyDown is not called on control on Mac or Gtk with a menu shortcut");
Assert.That(keyDownInWindow, Is.Null, "#3 - KeyDown is not called on Window on Mac or Gtk with a menu shortcut");
}
else
{
Assert.That(keyDownInControl, Is.True, "#2 - KeyDown was not called in control");
Assert.That(keyDownInWindow, Is.True, "#3 - KeyDown was not called in window");
}
}
}
Loading