From 00a6100166ae9ff1649c77f6d54303b4df7edadf Mon Sep 17 00:00:00 2001 From: Paddy Xu Date: Thu, 8 Jun 2017 21:07:27 +0300 Subject: [PATCH] Fix #23: KeyDown/KeyUp firing sequence; Prevent duplicate launch when pressing Enter key --- QuickLook/BackgroundListener.cs | 58 +++++++++++++++----- QuickLook/GlobalKeyboardHook.cs | 13 +---- QuickLook/NativeMethods/QuickLook.cs | 8 +-- QuickLook/ViewWindowManager.cs | 82 ++++++++++++++-------------- 4 files changed, 91 insertions(+), 70 deletions(-) diff --git a/QuickLook/BackgroundListener.cs b/QuickLook/BackgroundListener.cs index 46d79dd..3085d8d 100644 --- a/QuickLook/BackgroundListener.cs +++ b/QuickLook/BackgroundListener.cs @@ -1,6 +1,7 @@ using System; using System.Windows.Forms; using System.Windows.Threading; +using QuickLook.Helpers; namespace QuickLook { @@ -9,10 +10,11 @@ namespace QuickLook private static BackgroundListener _instance; private GlobalKeyboardHook _hook; + private bool _isKeyDownInDesktopOrShell; protected BackgroundListener() { - InstallKeyHook(HotkeyEventHandler); + InstallKeyHook(KeyDownEventHandler, KeyUpEventHandler); } public void Dispose() @@ -21,29 +23,55 @@ namespace QuickLook _hook = null; } - private void HotkeyEventHandler(object sender, KeyEventArgs e) + private void KeyDownEventHandler(object sender, KeyEventArgs e) + { + CallViewWindowManagerInvokeRoutine(e, true); + } + + private void KeyUpEventHandler(object sender, KeyEventArgs e) + { + CallViewWindowManagerInvokeRoutine(e, false); + } + + private void CallViewWindowManagerInvokeRoutine(KeyEventArgs e, bool isKeyDown) { if (e.Modifiers != Keys.None) return; - Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() => ViewWindowManager.GetInstance().InvokeRoutine(e)), - DispatcherPriority.ApplicationIdle); + // set variable only when KeyDown + if (isKeyDown) + { + _isKeyDownInDesktopOrShell = NativeMethods.QuickLook.GetFocusedWindowType() != + NativeMethods.QuickLook.FocusedWindowType.Invalid; + _isKeyDownInDesktopOrShell |= WindowHelper.IsForegroundWindowBelongToSelf(); + } + + // call InvokeRoutine only when the KeyDown is valid + if (_isKeyDownInDesktopOrShell) + Dispatcher.CurrentDispatcher.BeginInvoke( + new Action(down => + ViewWindowManager.GetInstance().InvokeRoutine(e, down)), + DispatcherPriority.ApplicationIdle, + isKeyDown); + + // reset variable only when KeyUp + if (!isKeyDown) + _isKeyDownInDesktopOrShell = false; } - private void InstallKeyHook(KeyEventHandler handler) + private void InstallKeyHook(KeyEventHandler downHandler, KeyEventHandler upHandler) { _hook = GlobalKeyboardHook.GetInstance(); - _hook.HookedDownKeys.Add(Keys.Enter); - _hook.KeyDown += handler; - - _hook.HookedUpKeys.Add(Keys.Space); - _hook.HookedUpKeys.Add(Keys.Escape); - _hook.HookedUpKeys.Add(Keys.Up); - _hook.HookedUpKeys.Add(Keys.Down); - _hook.HookedUpKeys.Add(Keys.Left); - _hook.HookedUpKeys.Add(Keys.Right); - _hook.KeyUp += handler; + _hook.HookedKeys.Add(Keys.Enter); + _hook.HookedKeys.Add(Keys.Space); + _hook.HookedKeys.Add(Keys.Escape); + _hook.HookedKeys.Add(Keys.Up); + _hook.HookedKeys.Add(Keys.Down); + _hook.HookedKeys.Add(Keys.Left); + _hook.HookedKeys.Add(Keys.Right); + _hook.KeyDown += downHandler; + _hook.KeyUp += upHandler; } internal static BackgroundListener GetInstance() diff --git a/QuickLook/GlobalKeyboardHook.cs b/QuickLook/GlobalKeyboardHook.cs index 6ec8855..eac0c2f 100644 --- a/QuickLook/GlobalKeyboardHook.cs +++ b/QuickLook/GlobalKeyboardHook.cs @@ -14,8 +14,7 @@ namespace QuickLook private User32.KeyboardHookProc _callback; private IntPtr _hhook = IntPtr.Zero; - internal List HookedDownKeys = new List(); - internal List HookedUpKeys = new List(); + internal List HookedKeys = new List(); protected GlobalKeyboardHook() { @@ -64,21 +63,13 @@ namespace QuickLook if (code >= 0) { var key = (Keys) lParam.vkCode; - if (HookedDownKeys.Contains(key)) + if (HookedKeys.Contains(key)) { key = AddModifiers(key); var kea = new KeyEventArgs(key); if (wParam == User32.WM_KEYDOWN || wParam == User32.WM_SYSKEYDOWN) KeyDown?.Invoke(this, kea); - if (kea.Handled) - return 1; - } - else if (HookedUpKeys.Contains(key)) - { - key = AddModifiers(key); - - var kea = new KeyEventArgs(key); if (wParam == User32.WM_KEYUP || wParam == User32.WM_SYSKEYUP) KeyUp?.Invoke(this, kea); if (kea.Handled) diff --git a/QuickLook/NativeMethods/QuickLook.cs b/QuickLook/NativeMethods/QuickLook.cs index e4b0c12..8464c22 100644 --- a/QuickLook/NativeMethods/QuickLook.cs +++ b/QuickLook/NativeMethods/QuickLook.cs @@ -8,19 +8,19 @@ namespace QuickLook.NativeMethods { [DllImport("QuickLook.Native.Shell32.dll", EntryPoint = "GetFocusedWindowType", CallingConvention = CallingConvention.Cdecl)] - internal static extern FocusedWindowType GetFocusedWindowTypeNative_32(); + private static extern FocusedWindowType GetFocusedWindowTypeNative_32(); [DllImport("QuickLook.Native.Shell32.dll", EntryPoint = "GetCurrentSelection", CallingConvention = CallingConvention.Cdecl)] - internal static extern void GetCurrentSelectionNative_32([MarshalAs(UnmanagedType.LPWStr)] StringBuilder sb); + private static extern void GetCurrentSelectionNative_32([MarshalAs(UnmanagedType.LPWStr)] StringBuilder sb); [DllImport("QuickLook.Native.Shell32.x64.dll", EntryPoint = "GetFocusedWindowType", CallingConvention = CallingConvention.Cdecl)] - internal static extern FocusedWindowType GetFocusedWindowTypeNative_64(); + private static extern FocusedWindowType GetFocusedWindowTypeNative_64(); [DllImport("QuickLook.Native.Shell32.x64.dll", EntryPoint = "GetCurrentSelection", CallingConvention = CallingConvention.Cdecl)] - internal static extern void GetCurrentSelectionNative_64([MarshalAs(UnmanagedType.LPWStr)] StringBuilder sb); + private static extern void GetCurrentSelectionNative_64([MarshalAs(UnmanagedType.LPWStr)] StringBuilder sb); internal static FocusedWindowType GetFocusedWindowType() { diff --git a/QuickLook/ViewWindowManager.cs b/QuickLook/ViewWindowManager.cs index a94c9dc..d88db26 100644 --- a/QuickLook/ViewWindowManager.cs +++ b/QuickLook/ViewWindowManager.cs @@ -34,51 +34,62 @@ namespace QuickLook StopFocusMonitor(); } - internal void InvokeRoutine(KeyEventArgs kea) + internal void InvokeRoutine(KeyEventArgs kea, bool isKeyDown) { - switch (kea.KeyCode) - { - case Keys.Up: - case Keys.Down: - case Keys.Left: - case Keys.Right: - SwitchPreview(); - break; - case Keys.Space: - TogglePreview(); - break; - case Keys.Escape: - ClosePreview(); - break; - case Keys.Enter: - RunAndClosePreview(); - break; - default: - break; - } + Debug.WriteLine($"InvokeRoutine: key={kea.KeyCode},down={isKeyDown}"); + + + if (isKeyDown) + switch (kea.KeyCode) + { + case Keys.Enter: + RunAndClosePreview(); + break; + } + else + switch (kea.KeyCode) + { + case Keys.Up: + case Keys.Down: + case Keys.Left: + case Keys.Right: + SwitchPreview(); + break; + case Keys.Space: + TogglePreview(); + break; + case Keys.Escape: + ClosePreview(); + break; + } } internal void RunAndClosePreview() { - if (NativeMethods.QuickLook.GetFocusedWindowType() == - NativeMethods.QuickLook.FocusedWindowType.Invalid) - if (!WindowHelper.IsForegroundWindowBelongToSelf()) - return; - if (_currentMainWindow.Visibility != Visibility.Visible) return; + // if the current focus is in Desktop or explorer windows, just close the preview window and leave the task to System. + var focus = NativeMethods.QuickLook.GetFocusedWindowType(); + if (focus == NativeMethods.QuickLook.FocusedWindowType.Desktop || + focus == NativeMethods.QuickLook.FocusedWindowType.Explorer) + if (_path == NativeMethods.QuickLook.GetCurrentSelection()) + { + StopFocusMonitor(); + _currentMainWindow.BeginHide(); + return; + } + + // if the focus is in the preview window, run it + if (!WindowHelper.IsForegroundWindowBelongToSelf()) + return; + StopFocusMonitor(); _currentMainWindow.RunAndHide(); } internal void ClosePreview() { - if (NativeMethods.QuickLook.GetFocusedWindowType() == - NativeMethods.QuickLook.FocusedWindowType.Invalid) - if (!WindowHelper.IsForegroundWindowBelongToSelf()) - return; - if (_currentMainWindow.Visibility != Visibility.Visible) return; @@ -90,11 +101,6 @@ namespace QuickLook { _lastSwitchTick = DateTime.Now.Ticks; - if (NativeMethods.QuickLook.GetFocusedWindowType() == - NativeMethods.QuickLook.FocusedWindowType.Invalid) - if (!WindowHelper.IsForegroundWindowBelongToSelf()) - return; - if (_currentMainWindow.Visibility == Visibility.Visible) { ClosePreview(); @@ -113,10 +119,6 @@ namespace QuickLook _lastSwitchTick = DateTime.Now.Ticks; - if (NativeMethods.QuickLook.GetFocusedWindowType() == - NativeMethods.QuickLook.FocusedWindowType.Invalid) - return; - _path = NativeMethods.QuickLook.GetCurrentSelection(); InvokeViewer();