Fix #23: KeyDown/KeyUp firing sequence; Prevent duplicate launch when pressing Enter key

This commit is contained in:
Paddy Xu
2017-06-08 21:07:27 +03:00
parent 50081abb62
commit 00a6100166
4 changed files with 91 additions and 70 deletions

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.Windows.Forms; using System.Windows.Forms;
using System.Windows.Threading; using System.Windows.Threading;
using QuickLook.Helpers;
namespace QuickLook namespace QuickLook
{ {
@@ -9,10 +10,11 @@ namespace QuickLook
private static BackgroundListener _instance; private static BackgroundListener _instance;
private GlobalKeyboardHook _hook; private GlobalKeyboardHook _hook;
private bool _isKeyDownInDesktopOrShell;
protected BackgroundListener() protected BackgroundListener()
{ {
InstallKeyHook(HotkeyEventHandler); InstallKeyHook(KeyDownEventHandler, KeyUpEventHandler);
} }
public void Dispose() public void Dispose()
@@ -21,29 +23,55 @@ namespace QuickLook
_hook = null; _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) if (e.Modifiers != Keys.None)
return; return;
Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() => ViewWindowManager.GetInstance().InvokeRoutine(e)), // set variable only when KeyDown
DispatcherPriority.ApplicationIdle); 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<bool>(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 = GlobalKeyboardHook.GetInstance();
_hook.HookedDownKeys.Add(Keys.Enter); _hook.HookedKeys.Add(Keys.Enter);
_hook.KeyDown += handler; _hook.HookedKeys.Add(Keys.Space);
_hook.HookedKeys.Add(Keys.Escape);
_hook.HookedUpKeys.Add(Keys.Space); _hook.HookedKeys.Add(Keys.Up);
_hook.HookedUpKeys.Add(Keys.Escape); _hook.HookedKeys.Add(Keys.Down);
_hook.HookedUpKeys.Add(Keys.Up); _hook.HookedKeys.Add(Keys.Left);
_hook.HookedUpKeys.Add(Keys.Down); _hook.HookedKeys.Add(Keys.Right);
_hook.HookedUpKeys.Add(Keys.Left); _hook.KeyDown += downHandler;
_hook.HookedUpKeys.Add(Keys.Right); _hook.KeyUp += upHandler;
_hook.KeyUp += handler;
} }
internal static BackgroundListener GetInstance() internal static BackgroundListener GetInstance()

View File

@@ -14,8 +14,7 @@ namespace QuickLook
private User32.KeyboardHookProc _callback; private User32.KeyboardHookProc _callback;
private IntPtr _hhook = IntPtr.Zero; private IntPtr _hhook = IntPtr.Zero;
internal List<Keys> HookedDownKeys = new List<Keys>(); internal List<Keys> HookedKeys = new List<Keys>();
internal List<Keys> HookedUpKeys = new List<Keys>();
protected GlobalKeyboardHook() protected GlobalKeyboardHook()
{ {
@@ -64,21 +63,13 @@ namespace QuickLook
if (code >= 0) if (code >= 0)
{ {
var key = (Keys) lParam.vkCode; var key = (Keys) lParam.vkCode;
if (HookedDownKeys.Contains(key)) if (HookedKeys.Contains(key))
{ {
key = AddModifiers(key); key = AddModifiers(key);
var kea = new KeyEventArgs(key); var kea = new KeyEventArgs(key);
if (wParam == User32.WM_KEYDOWN || wParam == User32.WM_SYSKEYDOWN) if (wParam == User32.WM_KEYDOWN || wParam == User32.WM_SYSKEYDOWN)
KeyDown?.Invoke(this, kea); 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) if (wParam == User32.WM_KEYUP || wParam == User32.WM_SYSKEYUP)
KeyUp?.Invoke(this, kea); KeyUp?.Invoke(this, kea);
if (kea.Handled) if (kea.Handled)

View File

@@ -8,19 +8,19 @@ namespace QuickLook.NativeMethods
{ {
[DllImport("QuickLook.Native.Shell32.dll", EntryPoint = "GetFocusedWindowType", [DllImport("QuickLook.Native.Shell32.dll", EntryPoint = "GetFocusedWindowType",
CallingConvention = CallingConvention.Cdecl)] CallingConvention = CallingConvention.Cdecl)]
internal static extern FocusedWindowType GetFocusedWindowTypeNative_32(); private static extern FocusedWindowType GetFocusedWindowTypeNative_32();
[DllImport("QuickLook.Native.Shell32.dll", EntryPoint = "GetCurrentSelection", [DllImport("QuickLook.Native.Shell32.dll", EntryPoint = "GetCurrentSelection",
CallingConvention = CallingConvention.Cdecl)] 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", [DllImport("QuickLook.Native.Shell32.x64.dll", EntryPoint = "GetFocusedWindowType",
CallingConvention = CallingConvention.Cdecl)] CallingConvention = CallingConvention.Cdecl)]
internal static extern FocusedWindowType GetFocusedWindowTypeNative_64(); private static extern FocusedWindowType GetFocusedWindowTypeNative_64();
[DllImport("QuickLook.Native.Shell32.x64.dll", EntryPoint = "GetCurrentSelection", [DllImport("QuickLook.Native.Shell32.x64.dll", EntryPoint = "GetCurrentSelection",
CallingConvention = CallingConvention.Cdecl)] 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() internal static FocusedWindowType GetFocusedWindowType()
{ {

View File

@@ -34,51 +34,62 @@ namespace QuickLook
StopFocusMonitor(); StopFocusMonitor();
} }
internal void InvokeRoutine(KeyEventArgs kea) internal void InvokeRoutine(KeyEventArgs kea, bool isKeyDown)
{ {
switch (kea.KeyCode) Debug.WriteLine($"InvokeRoutine: key={kea.KeyCode},down={isKeyDown}");
{
case Keys.Up:
case Keys.Down: if (isKeyDown)
case Keys.Left: switch (kea.KeyCode)
case Keys.Right: {
SwitchPreview(); case Keys.Enter:
break; RunAndClosePreview();
case Keys.Space: break;
TogglePreview(); }
break; else
case Keys.Escape: switch (kea.KeyCode)
ClosePreview(); {
break; case Keys.Up:
case Keys.Enter: case Keys.Down:
RunAndClosePreview(); case Keys.Left:
break; case Keys.Right:
default: SwitchPreview();
break; break;
} case Keys.Space:
TogglePreview();
break;
case Keys.Escape:
ClosePreview();
break;
}
} }
internal void RunAndClosePreview() internal void RunAndClosePreview()
{ {
if (NativeMethods.QuickLook.GetFocusedWindowType() ==
NativeMethods.QuickLook.FocusedWindowType.Invalid)
if (!WindowHelper.IsForegroundWindowBelongToSelf())
return;
if (_currentMainWindow.Visibility != Visibility.Visible) if (_currentMainWindow.Visibility != Visibility.Visible)
return; 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(); StopFocusMonitor();
_currentMainWindow.RunAndHide(); _currentMainWindow.RunAndHide();
} }
internal void ClosePreview() internal void ClosePreview()
{ {
if (NativeMethods.QuickLook.GetFocusedWindowType() ==
NativeMethods.QuickLook.FocusedWindowType.Invalid)
if (!WindowHelper.IsForegroundWindowBelongToSelf())
return;
if (_currentMainWindow.Visibility != Visibility.Visible) if (_currentMainWindow.Visibility != Visibility.Visible)
return; return;
@@ -90,11 +101,6 @@ namespace QuickLook
{ {
_lastSwitchTick = DateTime.Now.Ticks; _lastSwitchTick = DateTime.Now.Ticks;
if (NativeMethods.QuickLook.GetFocusedWindowType() ==
NativeMethods.QuickLook.FocusedWindowType.Invalid)
if (!WindowHelper.IsForegroundWindowBelongToSelf())
return;
if (_currentMainWindow.Visibility == Visibility.Visible) if (_currentMainWindow.Visibility == Visibility.Visible)
{ {
ClosePreview(); ClosePreview();
@@ -113,10 +119,6 @@ namespace QuickLook
_lastSwitchTick = DateTime.Now.Ticks; _lastSwitchTick = DateTime.Now.Ticks;
if (NativeMethods.QuickLook.GetFocusedWindowType() ==
NativeMethods.QuickLook.FocusedWindowType.Invalid)
return;
_path = NativeMethods.QuickLook.GetCurrentSelection(); _path = NativeMethods.QuickLook.GetCurrentSelection();
InvokeViewer(); InvokeViewer();