From 2f5a4f63b6069983fdd84ec653c8a8918f9d9bab Mon Sep 17 00:00:00 2001 From: qianlifeng Date: Sat, 22 Feb 2014 11:55:48 +0800 Subject: [PATCH] Add hotkey setting --- Wox.Infrastructure/CommonStorage.cs | 1 + .../GloablHotkey.cs | 35 +--- .../UserSettings/UserSetting.cs | 1 + Wox.Infrastructure/Wox.Infrastructure.csproj | 2 + Wox/HotkeyControl.xaml | 10 +- Wox/HotkeyControl.xaml.cs | 112 +++++++++--- Wox/MainWindow.xaml.cs | 161 ++++++++++-------- Wox/ResultPanel.xaml.cs | 1 + Wox/SettingWindow.xaml | 2 +- Wox/SettingWindow.xaml.cs | 12 ++ Wox/Wox.csproj | 1 - 11 files changed, 208 insertions(+), 130 deletions(-) rename {Wox/Helper => Wox.Infrastructure}/GloablHotkey.cs (86%) diff --git a/Wox.Infrastructure/CommonStorage.cs b/Wox.Infrastructure/CommonStorage.cs index 3a4dfa88c9..19e6776736 100644 --- a/Wox.Infrastructure/CommonStorage.cs +++ b/Wox.Infrastructure/CommonStorage.cs @@ -62,6 +62,7 @@ namespace Wox.Infrastructure Instance.UserSetting.Theme = "Default"; Instance.UserSetting.ReplaceWinR = true; Instance.UserSetting.WebSearches = Instance.UserSetting.LoadDefaultWebSearches(); + Instance.UserSetting.Hotkey = "Win + W"; } public static CommonStorage Instance diff --git a/Wox/Helper/GloablHotkey.cs b/Wox.Infrastructure/GloablHotkey.cs similarity index 86% rename from Wox/Helper/GloablHotkey.cs rename to Wox.Infrastructure/GloablHotkey.cs index 7871633fee..22602d27d3 100644 --- a/Wox/Helper/GloablHotkey.cs +++ b/Wox.Infrastructure/GloablHotkey.cs @@ -4,7 +4,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Wox.Plugin; -namespace Wox.Helper +namespace Wox.Infrastructure { public enum KeyEvent : int { @@ -29,8 +29,6 @@ namespace Wox.Helper WM_SYSKEYDOWN = 260 } - - /// /// Listens keyboard globally. /// Uses WH_KEYBOARD_LL. @@ -153,37 +151,6 @@ namespace Wox.Helper internal static extern uint SendInput(uint nInputs, [MarshalAs(UnmanagedType.LPArray), In] INPUT[] pInputs, int cbSize); } - public enum InputType - { - INPUT_MOUSE = 0, - INPUT_KEYBOARD = 1, - INPUT_HARDWARE = 2, - } - [Flags()] - public enum MOUSEEVENTF - { - MOVE = 0x0001, //mouse move - LEFTDOWN = 0x0002, //left button down - LEFTUP = 0x0004, //left button up - RIGHTDOWN = 0x0008, //right button down - RIGHTUP = 0x0010, //right button up - MIDDLEDOWN = 0x0020, //middle button down - MIDDLEUP = 0x0040, //middle button up - XDOWN = 0x0080, //x button down - XUP = 0x0100, //x button down - WHEEL = 0x0800, //wheel button rolled - VIRTUALDESK = 0x4000, //map to entire virtual desktop - ABSOLUTE = 0x8000, //absolute move - } - - [Flags()] - public enum KEYEVENTF - { - EXTENDEDKEY = 0x0001, - KEYUP = 0x0002, - UNICODE = 0x0004, - SCANCODE = 0x0008, - } [StructLayout(LayoutKind.Explicit)] public struct INPUT { diff --git a/Wox.Infrastructure/UserSettings/UserSetting.cs b/Wox.Infrastructure/UserSettings/UserSetting.cs index 2ee4f7bf5d..c766378ec2 100644 --- a/Wox.Infrastructure/UserSettings/UserSetting.cs +++ b/Wox.Infrastructure/UserSettings/UserSetting.cs @@ -5,6 +5,7 @@ namespace Wox.Infrastructure.UserSettings { public class UserSetting { + public string Hotkey { get; set; } public string Theme { get; set; } public bool ReplaceWinR { get; set; } public List WebSearches { get; set; } diff --git a/Wox.Infrastructure/Wox.Infrastructure.csproj b/Wox.Infrastructure/Wox.Infrastructure.csproj index 4aa8966552..504e8dc655 100644 --- a/Wox.Infrastructure/Wox.Infrastructure.csproj +++ b/Wox.Infrastructure/Wox.Infrastructure.csproj @@ -50,6 +50,8 @@ + + diff --git a/Wox/HotkeyControl.xaml b/Wox/HotkeyControl.xaml index 0a73439bc7..df03049126 100644 --- a/Wox/HotkeyControl.xaml +++ b/Wox/HotkeyControl.xaml @@ -4,10 +4,14 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" - Height="50" - Width="300" + Height="24" d:DesignHeight="300" d:DesignWidth="300"> - + + + + + + diff --git a/Wox/HotkeyControl.xaml.cs b/Wox/HotkeyControl.xaml.cs index e5b2044064..5e66ec17a2 100644 --- a/Wox/HotkeyControl.xaml.cs +++ b/Wox/HotkeyControl.xaml.cs @@ -1,17 +1,12 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.Drawing; using System.Windows; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Forms; using System.Windows.Input; using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; +using NHotkey; +using NHotkey.Wpf; using Wox.Helper; +using Wox.Infrastructure; using Wox.Plugin; using KeyEventArgs = System.Windows.Input.KeyEventArgs; using UserControl = System.Windows.Controls.UserControl; @@ -20,13 +15,34 @@ namespace Wox { public partial class HotkeyControl : UserControl { + public HotkeyModel CurrentHotkey { get; private set; } + public bool CurrentHotkeyAvailable { get; private set; } + + public event EventHandler OnHotkeyChanged; + + protected virtual void OnOnHotkeyChanged() + { + EventHandler handler = OnHotkeyChanged; + if (handler != null) handler(this, EventArgs.Empty); + } + public HotkeyControl() { InitializeComponent(); } + public void SetHotkey(HotkeyModel model) + { + if (model != null) + { + SetHotkey(model.ToString()); + } + } + private void TbHotkey_OnPreviewKeyDown(object sender, KeyEventArgs e) { + tbMsg.Visibility = Visibility.Hidden; + //when alt is pressed, the real key should be e.SystemKey Key key = (e.Key == Key.System ? e.SystemKey : e.Key); @@ -34,35 +50,87 @@ namespace Wox SpecialKeyState specialKeyState = new GloablHotkey().CheckModifiers(); if (specialKeyState.AltPressed) { - text += "Alt "; + text += "Alt"; } if (specialKeyState.CtrlPressed) { - text += "Ctrl "; + text += string.IsNullOrEmpty(text) ? "Ctrl" : " + Ctrl"; } if (specialKeyState.ShiftPressed) { - text += "Shift "; + text += string.IsNullOrEmpty(text) ? "Shift" : " + Shift"; } if (specialKeyState.WinPressed) { - text += "Win "; + text += string.IsNullOrEmpty(text) ? "Win" : " + Win"; } - if(IsKeyAChar(key)) + if (string.IsNullOrEmpty(text)) { - text += e.Key.ToString(); - } - if (key == Key.Space) - { - text += "Space"; + text += "Ctrl + Alt"; } - tbHotkey.Text = text; + if (IsKeyACharOrNumber(key)) + { + text += " + " + key; + } + else if (key == Key.Space) + { + text += " + Space"; + } + + e.Handled = true; + + Dispatcher.DelayInvoke("HotkeyAvailableTest", o => SetHotkey(text), TimeSpan.FromMilliseconds(300)); } - private static bool IsKeyAChar(Key key) + public void SetHotkey(string keyStr) { - return key >= Key.A && key <= Key.Z; + tbHotkey.Text = keyStr; + tbHotkey.Select(tbHotkey.Text.Length, 0); + + CurrentHotkey = new HotkeyModel(keyStr); + CurrentHotkeyAvailable = CheckHotAvailabel(CurrentHotkey); + tbMsg.Visibility = Visibility.Visible; + if (!CurrentHotkeyAvailable) + { + tbMsg.Foreground = new SolidColorBrush(Colors.Red); + tbMsg.Text = "hotkey unavailable"; + } + else + { + tbMsg.Foreground = new SolidColorBrush(Colors.Green); + tbMsg.Text = "hotkey available"; + } + OnOnHotkeyChanged(); + } + + private bool CheckHotAvailabel(HotkeyModel hotkey) + { + try + { + HotkeyManager.Current.AddOrReplace("HotkeyAvailableTest", hotkey.CharKey, hotkey.ModifierKeys, OnHotkey); + + return true; + } + catch + { + } + finally + { + HotkeyManager.Current.Remove("HotkeyAvailableTest"); + } + + return false; + } + + private void OnHotkey(object sender, HotkeyEventArgs e) + { + + } + + private static bool IsKeyACharOrNumber(Key key) + { + return (key >= Key.A && key <= Key.Z) || (key >= Key.D0 && key <= Key.D9); } } } diff --git a/Wox/MainWindow.xaml.cs b/Wox/MainWindow.xaml.cs index 872d993158..2d4b54daa0 100644 --- a/Wox/MainWindow.xaml.cs +++ b/Wox/MainWindow.xaml.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.IO; using System.Linq; using System.Threading; @@ -8,29 +9,33 @@ using System.Windows.Controls; using System.Windows.Forms; using System.Windows.Input; using System.Windows.Media.Animation; +using WindowsInput; +using WindowsInput.Native; using NHotkey; using NHotkey.Wpf; using Wox.Commands; -using Wox.Helper; using Wox.Infrastructure; using Wox.Plugin; using Wox.PluginLoader; using Application = System.Windows.Application; +using ContextMenu = System.Windows.Forms.ContextMenu; using KeyEventArgs = System.Windows.Input.KeyEventArgs; +using MenuItem = System.Windows.Forms.MenuItem; +using MessageBox = System.Windows.MessageBox; +using Timer = System.Timers.Timer; namespace Wox { public partial class MainWindow { + private static readonly object locker = new object(); + private readonly GloablHotkey globalHotkey = new GloablHotkey(); + + private readonly KeyboardSimulator keyboardSimulator = new KeyboardSimulator(new InputSimulator()); + private readonly Storyboard progressBarStoryboard = new Storyboard(); + private bool WinRStroked; private NotifyIcon notifyIcon; - Storyboard progressBarStoryboard = new Storyboard(); - private bool queryHasReturn = false; - - private GloablHotkey globalHotkey = new GloablHotkey(); - private bool WinRStroked = false; - private static object locker = new object(); - - private WindowsInput.KeyboardSimulator keyboardSimulator = new WindowsInput.KeyboardSimulator(new WindowsInput.InputSimulator()); + private bool queryHasReturn; public MainWindow() { @@ -49,8 +54,22 @@ namespace Wox SetTheme(CommonStorage.Instance.UserSetting.Theme = "Default"); } - HotkeyManager.Current.AddOrReplace("ShowHideWox", Key.W, ModifierKeys.Windows, OnHotkey); - this.Closing += MainWindow_Closing; + + Closing += MainWindow_Closing; + } + + public void SetHotkey(string hotkeyStr) + { + HotkeyModel hotkey = new HotkeyModel(hotkeyStr); + try + { + HotkeyManager.Current.AddOrReplace("ShowHideWox", hotkey.CharKey, hotkey.ModifierKeys, OnHotkey); + } + catch (Exception) + { + MessageBox.Show("Registe hotkey: " + CommonStorage.Instance.UserSetting.Hotkey + " failed."); + } + } private void OnHotkey(object sender, HotkeyEventArgs e) @@ -66,7 +85,7 @@ namespace Wox e.Handled = true; } - void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e) + private void MainWindow_Closing(object sender, CancelEventArgs e) { e.Cancel = true; } @@ -77,25 +96,25 @@ namespace Wox //This is caused by the Virtual Mermory Page Mechanisam. So, our solution is execute some codes in every min //which may prevent sysetem uninstall memory from RAM to disk. - System.Timers.Timer t = new System.Timers.Timer(1000 * 60 * 5) { AutoReset = true, Enabled = true }; + var t = new Timer(1000*60*5) {AutoReset = true, Enabled = true}; t.Elapsed += (o, e) => Dispatcher.Invoke(new Action(() => + { + if (Visibility != Visibility.Visible) { - if (Visibility != Visibility.Visible) - { - double oldLeft = Left; - Left = 20000; - ShowWox(); - CommandFactory.DispatchCommand(new Query("qq"), false); - HideWox(); - Left = oldLeft; - } - })); + double oldLeft = Left; + Left = 20000; + ShowWox(); + CommandFactory.DispatchCommand(new Query("qq"), false); + HideWox(); + Left = oldLeft; + } + })); } private void InitProgressbarAnimation() { - DoubleAnimation da = new DoubleAnimation(progressBar.X2, Width + 100, new Duration(new TimeSpan(0, 0, 0, 0, 1600))); - DoubleAnimation da1 = new DoubleAnimation(progressBar.X1, Width, new Duration(new TimeSpan(0, 0, 0, 0, 1600))); + var da = new DoubleAnimation(progressBar.X2, Width + 100, new Duration(new TimeSpan(0, 0, 0, 0, 1600))); + var da1 = new DoubleAnimation(progressBar.X1, Width, new Duration(new TimeSpan(0, 0, 0, 0, 1600))); Storyboard.SetTargetProperty(da, new PropertyPath("(Line.X2)")); Storyboard.SetTargetProperty(da1, new PropertyPath("(Line.X1)")); progressBarStoryboard.Children.Add(da); @@ -107,50 +126,51 @@ namespace Wox private void InitialTray() { - notifyIcon = new NotifyIcon { Text = "Wox", Icon = Properties.Resources.app, Visible = true }; + notifyIcon = new NotifyIcon {Text = "Wox", Icon = Properties.Resources.app, Visible = true}; notifyIcon.Click += (o, e) => ShowWox(); - System.Windows.Forms.MenuItem open = new System.Windows.Forms.MenuItem("Open"); + var open = new MenuItem("Open"); open.Click += (o, e) => ShowWox(); - System.Windows.Forms.MenuItem exit = new System.Windows.Forms.MenuItem("Exit"); + var exit = new MenuItem("Exit"); exit.Click += (o, e) => CloseApp(); - System.Windows.Forms.MenuItem[] childen = { open, exit }; - notifyIcon.ContextMenu = new System.Windows.Forms.ContextMenu(childen); + MenuItem[] childen = {open, exit}; + notifyIcon.ContextMenu = new ContextMenu(childen); } private void resultCtrl_resultItemChangedEvent() { - resultCtrl.Margin = resultCtrl.GetCurrentResultCount() > 0 ? new Thickness { Top = grid.Margin.Top } : new Thickness { Top = 0 }; + resultCtrl.Margin = resultCtrl.GetCurrentResultCount() > 0 + ? new Thickness {Top = grid.Margin.Top} + : new Thickness {Top = 0}; } private void TextBoxBase_OnTextChanged(object sender, TextChangedEventArgs e) { resultCtrl.Dirty = true; Dispatcher.DelayInvoke("UpdateSearch", - o => - { - Dispatcher.DelayInvoke("ClearResults", i => - { - // first try to use clear method inside resultCtrl, which is more closer to the add new results - // and this will not bring splash issues.After waiting 30ms, if there still no results added, we - // must clear the result. otherwise, it will be confused why the query changed, but the results - // didn't. - if (resultCtrl.Dirty) resultCtrl.Clear(); - }, TimeSpan.FromMilliseconds(30), null); - var q = new Query(tbQuery.Text); - CommandFactory.DispatchCommand(q); - queryHasReturn = false; - if (Plugins.HitThirdpartyKeyword(q)) - { - Dispatcher.DelayInvoke("ShowProgressbar", originQuery => - { - if (!queryHasReturn && originQuery == tbQuery.Text) - { - StartProgress(); - } - }, TimeSpan.FromSeconds(1), tbQuery.Text); - } - - }, TimeSpan.FromMilliseconds(150)); + o => + { + Dispatcher.DelayInvoke("ClearResults", i => + { + // first try to use clear method inside resultCtrl, which is more closer to the add new results + // and this will not bring splash issues.After waiting 30ms, if there still no results added, we + // must clear the result. otherwise, it will be confused why the query changed, but the results + // didn't. + if (resultCtrl.Dirty) resultCtrl.Clear(); + }, TimeSpan.FromMilliseconds(30), null); + var q = new Query(tbQuery.Text); + CommandFactory.DispatchCommand(q); + queryHasReturn = false; + if (Plugins.HitThirdpartyKeyword(q)) + { + Dispatcher.DelayInvoke("ShowProgressbar", originQuery => + { + if (!queryHasReturn && originQuery == tbQuery.Text) + { + StartProgress(); + } + }, TimeSpan.FromSeconds(1), tbQuery.Text); + } + }, TimeSpan.FromMilliseconds(150)); } private void StartProgress() @@ -205,9 +225,10 @@ namespace Wox private void MainWindow_OnLoaded(object sender, RoutedEventArgs e) { - Left = (SystemParameters.PrimaryScreenWidth - ActualWidth) / 2; - Top = (SystemParameters.PrimaryScreenHeight - ActualHeight) / 3; + Left = (SystemParameters.PrimaryScreenWidth - ActualWidth)/2; + Top = (SystemParameters.PrimaryScreenHeight - ActualHeight)/3; + SetHotkey(CommonStorage.Instance.UserSetting.Hotkey); WakeupApp(); Plugins.Init(); @@ -219,16 +240,16 @@ namespace Wox if (CommonStorage.Instance.UserSetting.ReplaceWinR) { //todo:need refatoring. move those codes to CMD file or expose events - if (keyevent == KeyEvent.WM_KEYDOWN && vkcode == (int)Keys.R && state.WinPressed) + if (keyevent == KeyEvent.WM_KEYDOWN && vkcode == (int) Keys.R && state.WinPressed) { WinRStroked = true; Dispatcher.BeginInvoke(new Action(OnWinRPressed)); return false; } - if (keyevent == KeyEvent.WM_KEYUP && WinRStroked && vkcode == (int)Keys.LWin) + if (keyevent == KeyEvent.WM_KEYUP && WinRStroked && vkcode == (int) Keys.LWin) { WinRStroked = false; - keyboardSimulator.ModifiedKeyStroke(WindowsInput.Native.VirtualKeyCode.LWIN, WindowsInput.Native.VirtualKeyCode.CONTROL); + keyboardSimulator.ModifiedKeyStroke(VirtualKeyCode.LWIN, VirtualKeyCode.CONTROL); return false; } } @@ -294,15 +315,17 @@ namespace Wox if (list.Count > 0) { //todo:this should be opened to users, it's their choise to use it or not in thier workflows - list.ForEach(o => - { - if (o.AutoAjustScore) o.Score += CommonStorage.Instance.UserSelectedRecords.GetSelectedCount(o); - }); + list.ForEach( + o => + { + if (o.AutoAjustScore) o.Score += CommonStorage.Instance.UserSelectedRecords.GetSelectedCount(o); + }); lock (locker) { resultCtrl.Dispatcher.Invoke(new Action(() => { - List l = list.Where(o => o.OriginQuery != null && o.OriginQuery.RawQuery == tbQuery.Text).ToList(); + List l = + list.Where(o => o.OriginQuery != null && o.OriginQuery.RawQuery == tbQuery.Text).ToList(); resultCtrl.AddResults(list); })); } @@ -311,7 +334,7 @@ namespace Wox public void SetTheme(string themeName) { - ResourceDictionary dict = new ResourceDictionary + var dict = new ResourceDictionary { Source = new Uri("pack://application:,,,/Themes/" + themeName + ".xaml") }; @@ -348,13 +371,13 @@ namespace Wox public void ShowMsg(string title, string subTitle, string iconPath) { - Msg m = new Msg { Owner = GetWindow(this) }; + var m = new Msg {Owner = GetWindow(this)}; m.Show(title, subTitle, iconPath); } public void OpenSettingDialog() { - SettingWidow s = new SettingWidow(this); + var s = new SettingWidow(this); s.Show(); } diff --git a/Wox/ResultPanel.xaml.cs b/Wox/ResultPanel.xaml.cs index 8d6e7cf1ed..d7189847c3 100644 --- a/Wox/ResultPanel.xaml.cs +++ b/Wox/ResultPanel.xaml.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Windows; using System.Windows.Controls; using Wox.Helper; +using Wox.Infrastructure; using Wox.Plugin; namespace Wox diff --git a/Wox/SettingWindow.xaml b/Wox/SettingWindow.xaml index 37247497dc..ba04be28f2 100644 --- a/Wox/SettingWindow.xaml +++ b/Wox/SettingWindow.xaml @@ -24,7 +24,7 @@ - + diff --git a/Wox/SettingWindow.xaml.cs b/Wox/SettingWindow.xaml.cs index f704e05924..04fbdcf262 100644 --- a/Wox/SettingWindow.xaml.cs +++ b/Wox/SettingWindow.xaml.cs @@ -31,6 +31,8 @@ namespace Wox this.mainWindow = mainWindow; InitializeComponent(); Loaded += Setting_Loaded; + ctlHotkey.OnHotkeyChanged += ctlHotkey_OnHotkeyChanged; + ctlHotkey.SetHotkey(CommonStorage.Instance.UserSetting.Hotkey); cbReplaceWinR.Checked += (o, e) => { CommonStorage.Instance.UserSetting.ReplaceWinR = true; @@ -43,6 +45,16 @@ namespace Wox }; } + void ctlHotkey_OnHotkeyChanged(object sender, System.EventArgs e) + { + if (ctlHotkey.CurrentHotkeyAvailable) + { + mainWindow.SetHotkey(ctlHotkey.CurrentHotkey.ToString()); + CommonStorage.Instance.UserSetting.Hotkey = ctlHotkey.CurrentHotkey.ToString(); + CommonStorage.Instance.Save(); + } + } + private void Setting_Loaded(object sender, RoutedEventArgs e) { foreach (string theme in LoadAvailableThemes()) diff --git a/Wox/Wox.csproj b/Wox/Wox.csproj index cffd524546..50b0e60845 100644 --- a/Wox/Wox.csproj +++ b/Wox/Wox.csproj @@ -129,7 +129,6 @@ - HotkeyControl.xaml