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

Prevent accelerator keys to work when typing text #2056

Merged
merged 16 commits into from
Jan 18, 2018
Merged
Changes from 6 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
122 changes: 33 additions & 89 deletions CefSharp.Wpf/ChromiumWebBrowser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@
//
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

using CefSharp.Internals;
using CefSharp.Wpf.Internals;
using CefSharp.Wpf.Rendering;
using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
Expand All @@ -17,10 +14,12 @@
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Threading;
using CefSharp.ModelBinding;
using System.Runtime.CompilerServices;
using CefSharp.Internals;
using CefSharp.Wpf.Internals;
using CefSharp.Wpf.Rendering;
using Microsoft.Win32.SafeHandles;

namespace CefSharp.Wpf
namespace CefSharp.Wpf
{
/// <summary>
/// ChromiumWebBrowser is the WPF web browser control
Expand All @@ -35,10 +34,6 @@ public class ChromiumWebBrowser : ContentControl, IRenderWebBrowser, IWpfWebBrow
/// </summary>
private HwndSource source;
/// <summary>
/// The source hook
/// </summary>
private HwndSourceHook sourceHook;
/// <summary>
/// The tooltip timer
/// </summary>
private DispatcherTimer tooltipTimer;
Expand Down Expand Up @@ -552,7 +547,7 @@ protected virtual void Dispose(bool isDisposing)

Cef.RemoveDisposable(this);

RemoveSourceHook();
source = null;
}
}

Expand Down Expand Up @@ -1521,8 +1516,6 @@ private void PresentationSourceChangedHandler(object sender, SourceChangedEventA
var notifyDpiChanged = DpiScaleFactor > 0 && !DpiScaleFactor.Equals(matrix.M11);

DpiScaleFactor = source.CompositionTarget.TransformToDevice.M11;
sourceHook = SourceHook;
source.AddHook(sourceHook);

if (notifyDpiChanged && browser != null)
{
Expand All @@ -1538,8 +1531,6 @@ private void PresentationSourceChangedHandler(object sender, SourceChangedEventA
}
else if (args.OldSource != null)
{
RemoveSourceHook();

var window = args.OldSource.RootVisual as Window;
if (window != null)
{
Expand Down Expand Up @@ -1574,18 +1565,6 @@ private void WindowStateChanged(object sender, EventArgs e)
}
}

/// <summary>
/// Removes the source hook.
/// </summary>
private void RemoveSourceHook()
{
if (source != null && sourceHook != null)
{
source.RemoveHook(sourceHook);
source = null;
}
}

/// <summary>
/// Create the underlying Browser instance, can be overriden to defer control creation
/// The browser will only be created when size &gt; Size(0,0). If you specify a positive
Expand Down Expand Up @@ -1782,59 +1761,6 @@ private Popup CreatePopup()
return newPopup;
}

/// <summary>
/// WindowProc callback interceptor. Handles Windows messages intended for the source hWnd, and passes them to the
/// contained browser as needed.
/// </summary>
/// <param name="hWnd">The source handle.</param>
/// <param name="message">The message.</param>
/// <param name="wParam">Additional message info.</param>
/// <param name="lParam">Even more message info.</param>
/// <param name="handled">if set to <c>true</c>, the event has already been handled by someone else.</param>
/// <returns>IntPtr.</returns>
protected virtual IntPtr SourceHook(IntPtr hWnd, int message, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (handled)
{
return IntPtr.Zero;
}

switch ((WM)message)
{
case WM.SYSCHAR:
case WM.SYSKEYDOWN:
case WM.SYSKEYUP:
case WM.KEYDOWN:
case WM.KEYUP:
case WM.CHAR:
case WM.IME_CHAR:
{
if (!IsKeyboardFocused)
{
break;
}

if (message == (int)WM.SYSKEYDOWN &&
wParam.ToInt32() == KeyInterop.VirtualKeyFromKey(Key.F4))
{
// We don't want CEF to receive this event (and mark it as handled), since that makes it impossible to
// shut down a CefSharp-based app by pressing Alt-F4, which is kind of bad.
return IntPtr.Zero;
}

if (browser != null)
{
browser.GetHost().SendKeyEvent(message, wParam.CastToInt32(), lParam.CastToInt32());
handled = true;
}

break;
}
}

return IntPtr.Zero;
}

/// <summary>
/// Converts a .NET Drag event to a CefSharp MouseEvent
/// </summary>
Expand Down Expand Up @@ -1972,25 +1898,43 @@ protected override void OnPreviewKeyUp(KeyEventArgs e)
/// <param name="e">The <see cref="KeyEventArgs"/> instance containing the event data.</param>
private void OnPreviewKey(KeyEventArgs e)
{
// As KeyDown and KeyUp bubble, it appears they're being handled before they get a chance to
// trigger the appropriate WM_ messages handled by our SourceHook, so we have to handle these extra keys here.
if (browser != null)
{
var modifiers = e.GetModifiers();
var message = (int)(e.IsDown ? WM.KEYDOWN : WM.KEYUP);
var virtualKey = KeyInterop.VirtualKeyFromKey(e.Key);

browser.GetHost().SendKeyEvent(message, virtualKey, (int)modifiers);
}

// Hooking the Tab key like this makes the tab focusing in essence work like
// KeyboardNavigation.TabNavigation="Cycle"; you will never be able to Tab out of the web browser control.
// We also add the condition to allow ctrl+a to work when the web browser control is put inside listbox.
// Prevent keyboard navigation using arrows and home and end keys
if (e.Key == Key.Tab || e.Key == Key.Home || e.Key == Key.End || e.Key == Key.Up
|| e.Key == Key.Down || e.Key == Key.Left || e.Key == Key.Right
|| (e.Key == Key.A && Keyboard.Modifiers == ModifierKeys.Control))
{
var modifiers = e.GetModifiers();
var message = (int)(e.IsDown ? WM.KEYDOWN : WM.KEYUP);
var virtualKey = KeyInterop.VirtualKeyFromKey(e.Key);
e.Handled = true;
}
}

if(browser != null)
/// <summary>
/// Handles the <see cref="E:PreviewTextInput" /> event.
/// </summary>
/// <param name="e">The <see cref="TextCompositionEventArgs"/> instance containing the event data.</param>
protected override void OnPreviewTextInput(TextCompositionEventArgs e)
{
if (browser != null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to check if (!e.Handled)` here, handle the case where an overriding implementation chooses to handle the input.

{
var browserHost = browser.GetHost();
for (int i = 0; i < e.Text.Length; i++)
{
browser.GetHost().SendKeyEvent(message, virtualKey, (int)modifiers);
e.Handled = true;
browserHost.SendKeyEvent((int)WM.CHAR, e.Text[i], 0);
}
e.Handled = true;
}
base.OnTextInput(e);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should base.OnTextInput(e); be base.OnPreviewTextInput(e);?

}

/// <summary>
Expand Down