diff --git a/Fluent.Ribbon/Fluent.Ribbon.NET 4.0.csproj b/Fluent.Ribbon/Fluent.Ribbon.NET 4.0.csproj index efa71ee8c..ce29a67c1 100644 --- a/Fluent.Ribbon/Fluent.Ribbon.NET 4.0.csproj +++ b/Fluent.Ribbon/Fluent.Ribbon.NET 4.0.csproj @@ -94,6 +94,7 @@ + diff --git a/Fluent.Ribbon/Fluent.Ribbon.NET 4.5.csproj b/Fluent.Ribbon/Fluent.Ribbon.NET 4.5.csproj index 2749a737c..e69c5c5fa 100644 --- a/Fluent.Ribbon/Fluent.Ribbon.NET 4.5.csproj +++ b/Fluent.Ribbon/Fluent.Ribbon.NET 4.5.csproj @@ -94,6 +94,7 @@ + diff --git a/Fluent.Ribbon/Internal/KeyEventUtility.cs b/Fluent.Ribbon/Internal/KeyEventUtility.cs new file mode 100644 index 000000000..e84e023c5 --- /dev/null +++ b/Fluent.Ribbon/Internal/KeyEventUtility.cs @@ -0,0 +1,34 @@ +namespace Fluent.Internal +{ + using System.Windows.Input; + + internal static class KeyEventUtility + { + public static string GetStringFromKey(Key key) + { + var keyboardState = new byte[256]; + if (NativeMethods.GetKeyboardState(keyboardState) == false) + { + return null; + } + + var virtualKey = KeyInterop.VirtualKeyFromKey(key); + var scanCode = NativeMethods.MapVirtualKey((uint)virtualKey, NativeMethods.MapType.MAPVK_VK_TO_VSC); + var chars = new char[1]; + + var result = NativeMethods.ToUnicode((uint)virtualKey, scanCode, keyboardState, chars, chars.Length, 0); + switch (result) + { + case -1: + case 0: + return null; + + case 1: + return chars[0].ToString(); + + default: + return null; + } + } + } +} \ No newline at end of file diff --git a/Fluent.Ribbon/Internal/NativeMethods.cs b/Fluent.Ribbon/Internal/NativeMethods.cs index 0187f8485..b99c006f8 100644 --- a/Fluent.Ribbon/Internal/NativeMethods.cs +++ b/Fluent.Ribbon/Internal/NativeMethods.cs @@ -236,6 +236,25 @@ public static bool _ModifyStyle(this IntPtr _hwnd, WS removeStyle, WS addStyle) return true; } + [DllImport("user32.dll", CharSet = CharSet.Unicode)] + internal static extern int ToUnicode(uint virtualKey, uint scanCode, byte[] keyStates, [MarshalAs(UnmanagedType.LPArray)] [Out] char[] chars, int charMaxCount, uint flags); + + [DllImport("user32.dll")] + internal static extern bool GetKeyboardState(byte[] lpKeyState); + + [DllImport("user32.dll")] + internal static extern uint MapVirtualKey(uint uCode, MapType uMapType); + + // ReSharper disable InconsistentNaming + internal enum MapType : uint + { + MAPVK_VK_TO_VSC = 0x0, + MAPVK_VSC_TO_VK = 0x1, + MAPVK_VK_TO_CHAR = 0x2, + MAPVK_VSC_TO_VK_EX = 0x3, + } + // ReSharper restore InconsistentNaming + /// /// GetWindowLong values, GWL_* /// diff --git a/Fluent.Ribbon/Services/KeyTipService.cs b/Fluent.Ribbon/Services/KeyTipService.cs index d175c8b06..16005528e 100644 --- a/Fluent.Ribbon/Services/KeyTipService.cs +++ b/Fluent.Ribbon/Services/KeyTipService.cs @@ -36,7 +36,6 @@ internal class KeyTipService // Attached HWND source private HwndSource attachedHwndSource; - private static readonly KeyConverter keyConverter = new KeyConverter(); private string currentUserInput; /// @@ -171,13 +170,15 @@ private IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, re private void OnWindowPreviewKeyDown(object sender, KeyEventArgs e) { - if (e.IsRepeat) + if (e.IsRepeat + || e.Handled) { return; } if (this.ribbon.IsCollapsed - || this.ribbon.IsEnabled == false) + || this.ribbon.IsEnabled == false + || this.window.IsActive == false) { return; } @@ -224,7 +225,9 @@ private void OnWindowPreviewKeyDown(object sender, KeyEventArgs e) } var actualKey = e.Key == Key.System ? e.SystemKey : e.Key; - var isKeyRealInput = ((actualKey >= Key.A && actualKey <= Key.Z) || (actualKey >= Key.D0 && actualKey <= Key.D9) || (actualKey >= Key.NumPad0 && actualKey <= Key.NumPad9)); + // we need to get the real string input for the key because of keys like ä,ö,ü #258 + var key = KeyEventUtility.GetStringFromKey(actualKey); + var isKeyRealInput = string.IsNullOrEmpty(key) == false; // Don't do anything and let WPF handle the rest if (isKeyRealInput == false) @@ -240,12 +243,15 @@ private void OnWindowPreviewKeyDown(object sender, KeyEventArgs e) return; } + var shownImmediately = false; + // Should we show the keytips and immediately react to key? if (this.activeAdornerChain == null || this.activeAdornerChain.IsAdornerChainAlive == false || this.activeAdornerChain.AreAnyKeyTipsVisible == false) { this.ShowImmediatly(); + shownImmediately = true; } if (this.activeAdornerChain == null) @@ -254,11 +260,18 @@ private void OnWindowPreviewKeyDown(object sender, KeyEventArgs e) } var previousInput = this.currentUserInput; - this.currentUserInput += keyConverter.ConvertToString(actualKey); + this.currentUserInput += key; - // If no key tips match the current input, continue with the previously entered and still correct keys. if (this.activeAdornerChain.ActiveKeyTipAdorner.ContainsKeyTipStartingWith(this.currentUserInput) == false) { + // Handles access-keys #258 + if (shownImmediately) + { + this.activeAdornerChain?.Terminate(); + return; + } + + // If no key tips match the current input, continue with the previously entered and still correct keys. this.currentUserInput = previousInput; System.Media.SystemSounds.Beep.Play(); e.Handled = true; @@ -282,8 +295,10 @@ private void OnWindowPreviewKeyDown(object sender, KeyEventArgs e) private void OnWindowKeyUp(object sender, KeyEventArgs e) { if (this.ribbon.IsCollapsed - || this.ribbon.IsEnabled == false) + || this.ribbon.IsEnabled == false + || this.window.IsActive == false) { + this.activeAdornerChain?.Terminate(); return; }