From 8621fe2e3cd58bee75b31d040023210c2dada734 Mon Sep 17 00:00:00 2001 From: Colin Liu Date: Thu, 18 Feb 2016 19:30:36 +0800 Subject: [PATCH 01/18] Refactor MainWindow with MVVM - Add MVVM structure(BaseViewModel) - Create ViewModel for MainWindow - Refactor MainWindow.xaml to support MVVM - Move PublicAPI implementation from MainViewModel to PublicAPIInstance --- Wox/Converters/VisibilityConverter.cs | 30 + Wox/MainWindow.xaml | 29 +- Wox/MainWindow.xaml.cs | 1010 +++++++------------------ Wox/PublicAPIInstance.cs | 309 ++++++++ Wox/ViewModel/BaseViewModel.cs | 50 ++ Wox/ViewModel/MainViewModel.cs | 449 +++++++++++ less.exe.stackdump | 15 + 7 files changed, 1153 insertions(+), 739 deletions(-) create mode 100644 Wox/Converters/VisibilityConverter.cs create mode 100644 Wox/PublicAPIInstance.cs create mode 100644 Wox/ViewModel/BaseViewModel.cs create mode 100644 Wox/ViewModel/MainViewModel.cs create mode 100644 less.exe.stackdump diff --git a/Wox/Converters/VisibilityConverter.cs b/Wox/Converters/VisibilityConverter.cs new file mode 100644 index 0000000000..46488bde37 --- /dev/null +++ b/Wox/Converters/VisibilityConverter.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Data; + +namespace Wox.Converters +{ + class VisibilityConverter : ConvertorBase + { + public override object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value == null || value == DependencyProperty.UnsetValue) + { + return null; + } + + return bool.Parse(value.ToString()) ? Visibility.Visible : Visibility.Collapsed; + } + + public override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return null; + } + } + +} diff --git a/Wox/MainWindow.xaml b/Wox/MainWindow.xaml index e4873a677a..2ba87ff018 100644 --- a/Wox/MainWindow.xaml +++ b/Wox/MainWindow.xaml @@ -1,6 +1,7 @@  + + + + + + + + + - - - - + + + + + + + + + + \ No newline at end of file diff --git a/Wox/MainWindow.xaml.cs b/Wox/MainWindow.xaml.cs index aa802a3ad6..4d5ea4744b 100644 --- a/Wox/MainWindow.xaml.cs +++ b/Wox/MainWindow.xaml.cs @@ -34,219 +34,54 @@ using MenuItem = System.Windows.Forms.MenuItem; using MessageBox = System.Windows.MessageBox; using Stopwatch = Wox.Infrastructure.Stopwatch; using ToolTip = System.Windows.Controls.ToolTip; +using Wox.ViewModel; namespace Wox { - public partial class MainWindow : IPublicAPI + public partial class MainWindow { #region Properties private readonly Storyboard progressBarStoryboard = new Storyboard(); private NotifyIcon notifyIcon; - private bool _queryHasReturn; - private Query _lastQuery = new Query(); - private ToolTip toolTip = new ToolTip(); - - private bool _ignoreTextChange; - private List CurrentContextMenus = new List(); - private string textBeforeEnterContextMenuMode; - - #endregion - - #region Public API - - public void ChangeQuery(string query, bool requery = false) - { - Dispatcher.Invoke(() => - { - tbQuery.Text = query; - tbQuery.CaretIndex = tbQuery.Text.Length; - if (requery) - { - TbQuery_OnTextChanged(null, null); - } - }); - } - - public void ChangeQueryText(string query, bool selectAll = false) - { - Dispatcher.Invoke(() => - { - _ignoreTextChange = true; - tbQuery.Text = query; - tbQuery.CaretIndex = tbQuery.Text.Length; - if (selectAll) - { - tbQuery.SelectAll(); - } - }); - } - - public void CloseApp() - { - notifyIcon.Visible = false; - Application.Current.Shutdown(); - } - - public void RestarApp() - { - ProcessStartInfo info = new ProcessStartInfo - { - FileName = Application.ResourceAssembly.Location, - Arguments = SingleInstance.Restart - }; - Process.Start(info); - } - - public void HideApp() - { - Dispatcher.Invoke(HideWox); - } - - public void ShowApp() - { - Dispatcher.Invoke(() => ShowWox()); - } - - public void ShowMsg(string title, string subTitle, string iconPath) - { - Dispatcher.Invoke(() => - { - var m = new Msg { Owner = GetWindow(this) }; - m.Show(title, subTitle, iconPath); - }); - } - - public void OpenSettingDialog(string tabName = "general") - { - Dispatcher.Invoke(() => - { - SettingWindow sw = SingletonWindowOpener.Open(this); - sw.SwitchTo(tabName); - }); - } - - public void StartLoadingBar() - { - Dispatcher.Invoke(StartProgress); - } - - public void StopLoadingBar() - { - Dispatcher.Invoke(StopProgress); - } - - public void InstallPlugin(string path) - { - Dispatcher.Invoke(() => PluginManager.InstallPlugin(path)); - } - - public void ReloadPlugins() - { - Dispatcher.Invoke(() => PluginManager.Init(this)); - } - - public string GetTranslation(string key) - { - return InternationalizationManager.Instance.GetTranslation(key); - } - - public List GetAllPlugins() - { - return PluginManager.AllPlugins.ToList(); - } - - public event WoxKeyDownEventHandler BackKeyDownEvent; - public event WoxGlobalKeyboardEventHandler GlobalKeyboardEvent; - public event ResultItemDropEventHandler ResultItemDropEvent; - - public void PushResults(Query query, PluginMetadata plugin, List results) - { - results.ForEach(o => - { - o.PluginDirectory = plugin.PluginDirectory; - o.PluginID = plugin.ID; - o.OriginQuery = query; - }); - UpdateResultView(results, plugin, query); - } - - public void ShowContextMenu(PluginMetadata plugin, List results) - { - if (results != null && results.Count > 0) - { - results.ForEach(o => - { - o.PluginDirectory = plugin.PluginDirectory; - o.PluginID = plugin.ID; - o.ContextMenu = null; - }); - pnlContextMenu.Clear(); - pnlContextMenu.AddResults(results, plugin.ID); - pnlContextMenu.Visibility = Visibility.Visible; - pnlResult.Visibility = Visibility.Collapsed; - } - } + + #endregion + public MainWindow() { InitializeComponent(); - ThreadPool.SetMaxThreads(30, 10); - ThreadPool.SetMinThreads(10, 5); - - WebRequest.RegisterPrefix("data", new DataWebRequestFactory()); - GlobalHotkey.Instance.hookedKeyboardCallback += KListener_hookedKeyboardCallback; - progressBar.ToolTip = toolTip; - pnlResult.LeftMouseClickEvent += SelectResult; - pnlResult.ItemDropEvent += pnlResult_ItemDropEvent; - pnlContextMenu.LeftMouseClickEvent += SelectResult; - pnlResult.RightMouseClickEvent += pnlResult_RightMouseClickEvent; - Closing += MainWindow_Closing; - - - SetHotkey(UserSettingStorage.Instance.Hotkey, OnHotkey); - SetCustomPluginHotkey(); + InitialTray(); + + //pnlResult.ItemDropEvent += pnlResult_ItemDropEvent; + Closing += MainWindow_Closing; + } - void pnlResult_ItemDropEvent(Result result, IDataObject dropDataObject, DragEventArgs args) - { - PluginPair pluginPair = PluginManager.AllPlugins.FirstOrDefault(o => o.Metadata.ID == result.PluginID); - if (ResultItemDropEvent != null && pluginPair != null) - { - foreach (var delegateHandler in ResultItemDropEvent.GetInvocationList()) - { - if (delegateHandler.Target == pluginPair.Plugin) - { - delegateHandler.DynamicInvoke(result, dropDataObject, args); - } - } - } - } - - private bool KListener_hookedKeyboardCallback(KeyEvent keyevent, int vkcode, SpecialKeyState state) - { - if (GlobalKeyboardEvent != null) - { - return GlobalKeyboardEvent((int)keyevent, vkcode, state); - } - return true; - } - - void pnlResult_RightMouseClickEvent(Result result) - { - ShowContextMenu(result); - } + //void pnlResult_ItemDropEvent(Result result, IDataObject dropDataObject, DragEventArgs args) + //{ + // PluginPair pluginPair = PluginManager.AllPlugins.FirstOrDefault(o => o.Metadata.ID == result.PluginID); + // if (ResultItemDropEvent != null && pluginPair != null) + // { + // foreach (var delegateHandler in ResultItemDropEvent.GetInvocationList()) + // { + // if (delegateHandler.Target == pluginPair.Plugin) + // { + // delegateHandler.DynamicInvoke(result, dropDataObject, args); + // } + // } + // } + //} void MainWindow_Closing(object sender, CancelEventArgs e) { UserSettingStorage.Instance.WindowLeft = Left; UserSettingStorage.Instance.WindowTop = Top; UserSettingStorage.Instance.Save(); - HideWox(); e.Cancel = true; } @@ -304,82 +139,6 @@ namespace Wox }); } - public void SetHotkey(string hotkeyStr, EventHandler action) - { - var hotkey = new HotkeyModel(hotkeyStr); - SetHotkey(hotkey, action); - } - - public void SetHotkey(HotkeyModel hotkey, EventHandler action) - { - string hotkeyStr = hotkey.ToString(); - try - { - HotkeyManager.Current.AddOrReplace(hotkeyStr, hotkey.CharKey, hotkey.ModifierKeys, action); - } - catch (Exception) - { - string errorMsg = string.Format(InternationalizationManager.Instance.GetTranslation("registerHotkeyFailed"), hotkeyStr); - MessageBox.Show(errorMsg); - } - } - - public void RemoveHotkey(string hotkeyStr) - { - if (!string.IsNullOrEmpty(hotkeyStr)) - { - HotkeyManager.Current.Remove(hotkeyStr); - } - } - - /// - /// Checks if Wox should ignore any hotkeys - /// - /// - private bool ShouldIgnoreHotkeys() - { - //double if to omit calling win32 function - if (UserSettingStorage.Instance.IgnoreHotkeysOnFullscreen) - if (WindowIntelopHelper.IsWindowFullscreen()) - return true; - - return false; - } - - private void SetCustomPluginHotkey() - { - if (UserSettingStorage.Instance.CustomPluginHotkeys == null) return; - foreach (CustomPluginHotkey hotkey in UserSettingStorage.Instance.CustomPluginHotkeys) - { - CustomPluginHotkey hotkey1 = hotkey; - SetHotkey(hotkey.Hotkey, delegate - { - if (ShouldIgnoreHotkeys()) return; - ShowApp(); - ChangeQuery(hotkey1.ActionKeyword, true); - }); - } - } - - private void OnHotkey(object sender, HotkeyEventArgs e) - { - if (ShouldIgnoreHotkeys()) return; - ToggleWox(); - e.Handled = true; - } - - public void ToggleWox() - { - if (!IsVisible) - { - ShowWox(); - } - else - { - HideWox(); - } - } - private void InitProgressbarAnimation() { var da = new DoubleAnimation(progressBar.X2, ActualWidth + 100, new Duration(new TimeSpan(0, 0, 0, 0, 1600))); @@ -395,119 +154,18 @@ namespace Wox private void InitialTray() { - notifyIcon = new NotifyIcon { Text = "Wox", Icon = Properties.Resources.app, Visible = true }; - notifyIcon.Click += (o, e) => ShowWox(); - var open = new MenuItem(GetTranslation("iconTrayOpen")); - open.Click += (o, e) => ShowWox(); - var setting = new MenuItem(GetTranslation("iconTraySettings")); - setting.Click += (o, e) => OpenSettingDialog(); - var about = new MenuItem(GetTranslation("iconTrayAbout")); - about.Click += (o, e) => OpenSettingDialog("about"); - var exit = new MenuItem(GetTranslation("iconTrayExit")); - exit.Click += (o, e) => CloseApp(); - MenuItem[] childen = { open, setting, about, exit }; - notifyIcon.ContextMenu = new ContextMenu(childen); - } - - private void QueryContextMenu() - { - var contextMenuId = "Context Menu Id"; - pnlContextMenu.Clear(); - var query = tbQuery.Text.ToLower(); - if (string.IsNullOrEmpty(query)) - { - pnlContextMenu.AddResults(CurrentContextMenus, contextMenuId); - } - else - { - List filterResults = new List(); - foreach (Result contextMenu in CurrentContextMenus) - { - if (StringMatcher.IsMatch(contextMenu.Title, query) - || StringMatcher.IsMatch(contextMenu.SubTitle, query)) - { - filterResults.Add(contextMenu); - } - } - pnlContextMenu.AddResults(filterResults, contextMenuId); - } - } - - private void TbQuery_OnTextChanged(object sender, TextChangedEventArgs e) - { - if (_ignoreTextChange) { _ignoreTextChange = false; return; } - - toolTip.IsOpen = false; - if (IsInContextMenuMode) - { - QueryContextMenu(); - return; - } - - string query = tbQuery.Text.Trim(); - if (!string.IsNullOrEmpty(query)) - { - Query(query); - //reset query history index after user start new query - ResetQueryHistoryIndex(); - } - else - { - pnlResult.Clear(); - } - } - - private void ResetQueryHistoryIndex() - { - pnlResult.RemoveResultsFor(QueryHistoryStorage.MetaData); - QueryHistoryStorage.Instance.Reset(); - } - private void Query(string text) - { - _queryHasReturn = false; - var query = PluginManager.QueryInit(text); - if (query != null) - { - // handle the exclusiveness of plugin using action keyword - string lastKeyword = _lastQuery.ActionKeyword; - string keyword = query.ActionKeyword; - if (string.IsNullOrEmpty(lastKeyword)) - { - if (!string.IsNullOrEmpty(keyword)) - { - pnlResult.RemoveResultsExcept(PluginManager.NonGlobalPlugins[keyword].Metadata); - } - } - else - { - if (string.IsNullOrEmpty(keyword)) - { - pnlResult.RemoveResultsFor(PluginManager.NonGlobalPlugins[lastKeyword].Metadata); - } - else if (lastKeyword != keyword) - { - pnlResult.RemoveResultsExcept(PluginManager.NonGlobalPlugins[keyword].Metadata); - } - } - _lastQuery = query; - Dispatcher.InvokeAsync(async () => - { - await Task.Delay(150); - if (!string.IsNullOrEmpty(query.RawQuery) && query.RawQuery == _lastQuery.RawQuery && !_queryHasReturn) - { - StartProgress(); - } - }); - PluginManager.QueryForAllPlugins(query); - } - StopProgress(); - } - - private void BackToResultMode() - { - ChangeQueryText(textBeforeEnterContextMenuMode); - pnlResult.Visibility = Visibility.Visible; - pnlContextMenu.Visibility = Visibility.Collapsed; + //notifyIcon = new NotifyIcon { Text = "Wox", Icon = Properties.Resources.app, Visible = true }; + //notifyIcon.Click += (o, e) => ShowWox(); + //var open = new MenuItem(InternationalizationManager.Instance.GetTranslation("iconTrayOpen")); + //open.Click += (o, e) => ShowWox(); + //var setting = new MenuItem(InternationalizationManager.Instance.GetTranslation("iconTraySettings")); + //setting.Click += (o, e) => OpenSettingDialog(); + //var about = new MenuItem(InternationalizationManager.Instance.GetTranslation("iconTrayAbout")); + //about.Click += (o, e) => OpenSettingDialog("about"); + //var exit = new MenuItem(InternationalizationManager.Instance.GetTranslation("iconTrayExit")); + //exit.Click += (o, e) => CloseApp(); + //MenuItem[] childen = { open, setting, about, exit }; + //notifyIcon.ContextMenu = new ContextMenu(childen); } private void Border_OnMouseDown(object sender, MouseButtonEventArgs e) @@ -515,401 +173,285 @@ namespace Wox if (e.ChangedButton == MouseButton.Left) DragMove(); } - private void StartProgress() - { - progressBar.Visibility = Visibility.Visible; - } - - private void StopProgress() - { - progressBar.Visibility = Visibility.Hidden; - } - - private void HideWox() - { - UserSettingStorage.Instance.WindowLeft = Left; - UserSettingStorage.Instance.WindowTop = Top; - if (IsInContextMenuMode) - { - BackToResultMode(); - } - Hide(); - } - private void ShowWox(bool selectAll = true) { - UserSettingStorage.Instance.IncreaseActivateTimes(); - Left = GetWindowsLeft(); - Top = GetWindowsTop(); + //UserSettingStorage.Instance.IncreaseActivateTimes(); + //Left = GetWindowsLeft(); + //Top = GetWindowsTop(); - Show(); - Activate(); - Focus(); - tbQuery.Focus(); - ResetQueryHistoryIndex(); - if (selectAll) tbQuery.SelectAll(); + //Show(); + //Activate(); + //Focus(); + //tbQuery.Focus(); + //ResetQueryHistoryIndex(); + //if (selectAll) tbQuery.SelectAll(); } private void MainWindow_OnDeactivated(object sender, EventArgs e) { if (UserSettingStorage.Instance.HideWhenDeactive) { - HideWox(); + //TODO:Hide the window when deactivated + //HideWox(); } } - private void TbQuery_OnPreviewKeyDown(object sender, KeyEventArgs e) - { - //when alt is pressed, the real key should be e.SystemKey - Key key = (e.Key == Key.System ? e.SystemKey : e.Key); - switch (key) - { - case Key.Escape: - if (IsInContextMenuMode) - { - BackToResultMode(); - } - else - { - HideWox(); - } - e.Handled = true; - break; + //private void TbQuery_OnPreviewKeyDown(object sender, KeyEventArgs e) + //{ + // //when alt is pressed, the real key should be e.SystemKey + // Key key = (e.Key == Key.System ? e.SystemKey : e.Key); + // switch (key) + // { + // case Key.Escape: + // if (IsInContextMenuMode) + // { + // BackToResultMode(); + // } + // else + // { + // HideWox(); + // } + // e.Handled = true; + // break; - case Key.Tab: - if (GlobalHotkey.Instance.CheckModifiers().ShiftPressed) - { - SelectPrevItem(); - } - else - { - SelectNextItem(); - } - e.Handled = true; - break; + // case Key.Tab: + // if (GlobalHotkey.Instance.CheckModifiers().ShiftPressed) + // { + // SelectPrevItem(); + // } + // else + // { + // SelectNextItem(); + // } + // e.Handled = true; + // break; - case Key.N: - case Key.J: - if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) - { - SelectNextItem(); - } - break; + // case Key.N: + // case Key.J: + // if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) + // { + // SelectNextItem(); + // } + // break; - case Key.P: - case Key.K: - if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) - { - SelectPrevItem(); - } - break; + // case Key.P: + // case Key.K: + // if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) + // { + // SelectPrevItem(); + // } + // break; - case Key.O: - if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) - { - if (IsInContextMenuMode) - { - BackToResultMode(); - } - else - { - ShowContextMenu(GetActiveResult()); - } - } - break; + // case Key.O: + // if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) + // { + // if (IsInContextMenuMode) + // { + // BackToResultMode(); + // } + // else + // { + // ShowContextMenu(GetActiveResult()); + // } + // } + // break; - case Key.Down: - if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) - { - DisplayNextQuery(); - } - else - { - SelectNextItem(); - } - e.Handled = true; - break; + // case Key.Down: + // if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) + // { + // DisplayNextQuery(); + // } + // else + // { + // SelectNextItem(); + // } + // e.Handled = true; + // break; - case Key.Up: - if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) - { - DisplayPrevQuery(); - } - else - { - SelectPrevItem(); - } - e.Handled = true; - break; + // case Key.Up: + // if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) + // { + // DisplayPrevQuery(); + // } + // else + // { + // SelectPrevItem(); + // } + // e.Handled = true; + // break; - case Key.D: - if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) - { - pnlResult.SelectNextPage(); - } - break; + // case Key.D: + // if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) + // { + // pnlResult.SelectNextPage(); + // } + // break; - case Key.PageDown: - pnlResult.SelectNextPage(); - e.Handled = true; - break; + // case Key.PageDown: + // pnlResult.SelectNextPage(); + // e.Handled = true; + // break; - case Key.U: - if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) - { - pnlResult.SelectPrevPage(); - } - break; + // case Key.U: + // if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) + // { + // pnlResult.SelectPrevPage(); + // } + // break; - case Key.PageUp: - pnlResult.SelectPrevPage(); - e.Handled = true; - break; + // case Key.PageUp: + // pnlResult.SelectPrevPage(); + // e.Handled = true; + // break; - case Key.Back: - if (BackKeyDownEvent != null) - { - BackKeyDownEvent(new WoxKeyDownEventArgs - { - Query = tbQuery.Text, - keyEventArgs = e - }); - } - break; + // case Key.Back: + // if (BackKeyDownEvent != null) + // { + // BackKeyDownEvent(new WoxKeyDownEventArgs + // { + // Query = tbQuery.Text, + // keyEventArgs = e + // }); + // } + // break; - case Key.F1: - Process.Start("http://doc.getwox.com"); - break; + // case Key.F1: + // Process.Start("http://doc.getwox.com"); + // break; - case Key.Enter: - Result activeResult = GetActiveResult(); - if (GlobalHotkey.Instance.CheckModifiers().ShiftPressed) - { - ShowContextMenu(activeResult); - } - else - { - SelectResult(activeResult); - } - e.Handled = true; - break; + // case Key.Enter: + // Result activeResult = GetActiveResult(); + // if (GlobalHotkey.Instance.CheckModifiers().ShiftPressed) + // { + // ShowContextMenu(activeResult); + // } + // else + // { + // SelectResult(activeResult); + // } + // e.Handled = true; + // break; - case Key.D1: - SelectItem(1); - break; + // case Key.D1: + // SelectItem(1); + // break; - case Key.D2: - SelectItem(2); - break; + // case Key.D2: + // SelectItem(2); + // break; - case Key.D3: - SelectItem(3); - break; + // case Key.D3: + // SelectItem(3); + // break; - case Key.D4: - SelectItem(4); - break; + // case Key.D4: + // SelectItem(4); + // break; - case Key.D5: - SelectItem(5); - break; - case Key.D6: - SelectItem(6); - break; + // case Key.D5: + // SelectItem(5); + // break; + // case Key.D6: + // SelectItem(6); + // break; - } - } + // } + //} - private void DisplayPrevQuery() - { - var prev = QueryHistoryStorage.Instance.Previous(); - DisplayQueryHistory(prev); - } + //private void DisplayPrevQuery() + //{ + // var prev = QueryHistoryStorage.Instance.Previous(); + // DisplayQueryHistory(prev); + //} - private void DisplayNextQuery() - { - var nextQuery = QueryHistoryStorage.Instance.Next(); - DisplayQueryHistory(nextQuery); - } + //private void DisplayNextQuery() + //{ + // var nextQuery = QueryHistoryStorage.Instance.Next(); + // DisplayQueryHistory(nextQuery); + //} - private void DisplayQueryHistory(HistoryItem history) - { - if (history != null) - { - var historyMetadata = QueryHistoryStorage.MetaData; - ChangeQueryText(history.Query, true); - var executeQueryHistoryTitle = GetTranslation("executeQuery"); - var lastExecuteTime = GetTranslation("lastExecuteTime"); - pnlResult.RemoveResultsExcept(historyMetadata); - UpdateResultViewInternal(new List - { - new Result - { - Title = string.Format(executeQueryHistoryTitle,history.Query), - SubTitle = string.Format(lastExecuteTime,history.ExecutedDateTime), - IcoPath = "Images\\history.png", - PluginDirectory = WoxDirectroy.Executable, - Action = _ =>{ - ChangeQuery(history.Query,true); - return false; - } - } - }, historyMetadata); - } - } + //private void DisplayQueryHistory(HistoryItem history) + //{ + // if (history != null) + // { + // var historyMetadata = QueryHistoryStorage.MetaData; + // ChangeQueryText(history.Query, true); + // var executeQueryHistoryTitle = GetTranslation("executeQuery"); + // var lastExecuteTime = GetTranslation("lastExecuteTime"); + // pnlResult.RemoveResultsExcept(historyMetadata); + // UpdateResultViewInternal(new List + // { + // new Result + // { + // Title = string.Format(executeQueryHistoryTitle,history.Query), + // SubTitle = string.Format(lastExecuteTime,history.ExecutedDateTime), + // IcoPath = "Images\\history.png", + // PluginDirectory = WoxDirectroy.Executable, + // Action = _ =>{ + // ChangeQuery(history.Query,true); + // return false; + // } + // } + // }, historyMetadata); + // } + //} - private void SelectItem(int index) - { - int zeroBasedIndex = index - 1; - SpecialKeyState keyState = GlobalHotkey.Instance.CheckModifiers(); - if (keyState.AltPressed || keyState.CtrlPressed) - { - List visibleResults = pnlResult.GetVisibleResults(); - if (zeroBasedIndex < visibleResults.Count) - { - SelectResult(visibleResults[zeroBasedIndex]); - } - } - } + //private void SelectItem(int index) + //{ + // int zeroBasedIndex = index - 1; + // SpecialKeyState keyState = GlobalHotkey.Instance.CheckModifiers(); + // if (keyState.AltPressed || keyState.CtrlPressed) + // { + // List visibleResults = pnlResult.GetVisibleResults(); + // if (zeroBasedIndex < visibleResults.Count) + // { + // SelectResult(visibleResults[zeroBasedIndex]); + // } + // } + //} - private bool IsInContextMenuMode - { - get { return pnlContextMenu.Visibility == Visibility.Visible; } - } + //private bool IsInContextMenuMode + //{ + // get { return pnlContextMenu.Visibility == Visibility.Visible; } + //} - private Result GetActiveResult() - { - if (IsInContextMenuMode) - { - return pnlContextMenu.GetActiveResult(); - } - else - { - return pnlResult.GetActiveResult(); - } - } + //private Result GetActiveResult() + //{ + // if (IsInContextMenuMode) + // { + // return pnlContextMenu.GetActiveResult(); + // } + // else + // { + // return pnlResult.GetActiveResult(); + // } + //} - private void SelectPrevItem() - { - if (IsInContextMenuMode) - { - pnlContextMenu.SelectPrev(); - } - else - { - pnlResult.SelectPrev(); - } - toolTip.IsOpen = false; - } + //private void SelectPrevItem() + //{ + // if (IsInContextMenuMode) + // { + // pnlContextMenu.SelectPrev(); + // } + // else + // { + // pnlResult.SelectPrev(); + // } + // toolTip.IsOpen = false; + //} - private void SelectNextItem() - { - if (IsInContextMenuMode) - { - pnlContextMenu.SelectNext(); - } - else - { - pnlResult.SelectNext(); - } - toolTip.IsOpen = false; - } - - private void SelectResult(Result result) - { - if (result != null) - { - if (result.Action != null) - { - bool hideWindow = result.Action(new ActionContext - { - SpecialKeyState = GlobalHotkey.Instance.CheckModifiers() - }); - if (hideWindow) - { - HideWox(); - } - UserSelectedRecordStorage.Instance.Add(result); - QueryHistoryStorage.Instance.Add(tbQuery.Text); - } - } - } - - private void UpdateResultView(List list, PluginMetadata metadata, Query originQuery) - { - _queryHasReturn = true; - progressBar.Dispatcher.Invoke(StopProgress); - - list.ForEach(o => - { - o.Score += UserSelectedRecordStorage.Instance.GetSelectedCount(o) * 5; - }); - if (originQuery.RawQuery == _lastQuery.RawQuery) - { - UpdateResultViewInternal(list, metadata); - } - } - - private void UpdateResultViewInternal(List list, PluginMetadata metadata) - { - Dispatcher.Invoke(() => - { - Stopwatch.Normal($"UI update cost for {metadata.Name}", - () => { pnlResult.AddResults(list, metadata.ID); }); - }); - } - - private Result GetTopMostContextMenu(Result result) - { - if (TopMostRecordStorage.Instance.IsTopMost(result)) - { - return new Result(GetTranslation("cancelTopMostInThisQuery"), "Images\\down.png") - { - PluginDirectory = WoxDirectroy.Executable, - Action = _ => - { - TopMostRecordStorage.Instance.Remove(result); - ShowMsg("Succeed", "", ""); - return false; - } - }; - } - else - { - return new Result(GetTranslation("setAsTopMostInThisQuery"), "Images\\up.png") - { - PluginDirectory = WoxDirectroy.Executable, - Action = _ => - { - TopMostRecordStorage.Instance.AddOrUpdate(result); - ShowMsg("Succeed", "", ""); - return false; - } - }; - } - } - - private void ShowContextMenu(Result result) - { - if (result == null) return; - List results = PluginManager.GetContextMenusForPlugin(result); - results.ForEach(o => - { - o.PluginDirectory = PluginManager.GetPluginForId(result.PluginID).Metadata.PluginDirectory; - o.PluginID = result.PluginID; - o.OriginQuery = result.OriginQuery; - }); - - results.Add(GetTopMostContextMenu(result)); - - textBeforeEnterContextMenuMode = tbQuery.Text; - ChangeQueryText(""); - pnlContextMenu.Clear(); - pnlContextMenu.AddResults(results, result.PluginID); - CurrentContextMenus = results; - pnlContextMenu.Visibility = Visibility.Visible; - pnlResult.Visibility = Visibility.Collapsed; - } + //private void SelectNextItem() + //{ + // if (IsInContextMenuMode) + // { + // pnlContextMenu.SelectNext(); + // } + // else + // { + // pnlResult.SelectNext(); + // } + // toolTip.IsOpen = false; + //} private void MainWindow_OnDrop(object sender, DragEventArgs e) { diff --git a/Wox/PublicAPIInstance.cs b/Wox/PublicAPIInstance.cs new file mode 100644 index 0000000000..da5d23ee7f --- /dev/null +++ b/Wox/PublicAPIInstance.cs @@ -0,0 +1,309 @@ +using NHotkey; +using NHotkey.Wpf; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Threading; +using Wox.Core.Plugin; +using Wox.Core.Resource; +using Wox.Core.UserSettings; +using Wox.Helper; +using Wox.Infrastructure.Hotkey; +using Wox.Plugin; +using Wox.ViewModel; + +namespace Wox +{ + public class PublicAPIInstance : IPublicAPI + { + + #region Constructor + + public PublicAPIInstance(MainViewModel mainVM) + { + this.MainVM = mainVM; + + ThreadPool.SetMaxThreads(30, 10); + ThreadPool.SetMinThreads(10, 5); + GlobalHotkey.Instance.hookedKeyboardCallback += KListener_hookedKeyboardCallback; + WebRequest.RegisterPrefix("data", new DataWebRequestFactory()); + + SetHotkey(UserSettingStorage.Instance.Hotkey, OnHotkey); + SetCustomPluginHotkey(); + } + + #endregion + + #region Properties + + private MainViewModel MainVM + { + get; + set; + } + + #endregion + + #region Public API + + public void ChangeQuery(string query, bool requery = false) + { + this.MainVM.QueryText = query; + + //Application.Current.Dispatcher.Invoke(() => + //{ + + // tbQuery.CaretIndex = this.MainVM.QueryText.Length; + // if (requery) + // { + // TbQuery_OnTextChanged(null, null); + // } + //}); + } + + public void ChangeQueryText(string query, bool selectAll = false) + { + this.MainVM.QueryText = query; + + //Application.Current.Dispatcher.Invoke(() => + //{ + + // tbQuery.CaretIndex = tbQuery.Text.Length; + // if (selectAll) + // { + // tbQuery.SelectAll(); + // } + //}); + } + + public void CloseApp() + { + //notifyIcon.Visible = false; + Application.Current.Shutdown(); + } + + public void RestarApp() + { + ProcessStartInfo info = new ProcessStartInfo + { + FileName = Application.ResourceAssembly.Location, + Arguments = SingleInstance.Restart + }; + Process.Start(info); + } + + public void HideApp() + { + HideWox(); + } + + public void ShowApp() + { + ShowWox(); + } + + public void ShowMsg(string title, string subTitle, string iconPath) + { + Application.Current.Dispatcher.Invoke(() => + { + var m = new Msg { Owner = Application.Current.MainWindow }; + m.Show(title, subTitle, iconPath); + }); + } + + public void OpenSettingDialog(string tabName = "general") + { + Application.Current.Dispatcher.Invoke(() => + { + SettingWindow sw = SingletonWindowOpener.Open(this); + sw.SwitchTo(tabName); + }); + } + + public void StartLoadingBar() + { + this.MainVM.IsProgressBarVisible = true; + } + + public void StopLoadingBar() + { + this.MainVM.IsProgressBarVisible = false; + } + + public void InstallPlugin(string path) + { + Application.Current.Dispatcher.Invoke(() => PluginManager.InstallPlugin(path)); + } + + public void ReloadPlugins() + { + Application.Current.Dispatcher.Invoke(() => PluginManager.Init(this)); + } + + public string GetTranslation(string key) + { + return InternationalizationManager.Instance.GetTranslation(key); + } + + public List GetAllPlugins() + { + return PluginManager.AllPlugins.ToList(); + } + + public event WoxKeyDownEventHandler BackKeyDownEvent; + public event WoxGlobalKeyboardEventHandler GlobalKeyboardEvent; + public event ResultItemDropEventHandler ResultItemDropEvent; + + public void PushResults(Query query, PluginMetadata plugin, List results) + { + results.ForEach(o => + { + o.PluginDirectory = plugin.PluginDirectory; + o.PluginID = plugin.ID; + o.OriginQuery = query; + }); + this.MainVM.UpdateResultView(results, plugin, query); + } + + public void ShowContextMenu(PluginMetadata plugin, List results) + { + if (results != null && results.Count > 0) + { + results.ForEach(o => + { + o.PluginDirectory = plugin.PluginDirectory; + o.PluginID = plugin.ID; + }); + + this.MainVM.ActionPanel.Clear(); + + //TODO:Show Action Panel accordingly + //pnlContextMenu.Clear(); + //pnlContextMenu.AddResults(results, plugin.ID); + //pnlContextMenu.Visibility = Visibility.Visible; + //pnlResult.Visibility = Visibility.Collapsed; + } + } + + #endregion + + #region Private Methods + + private bool KListener_hookedKeyboardCallback(KeyEvent keyevent, int vkcode, SpecialKeyState state) + { + if (GlobalKeyboardEvent != null) + { + return GlobalKeyboardEvent((int)keyevent, vkcode, state); + } + return true; + } + + private void HideWox() + { + UserSettingStorage.Instance.WindowLeft = this.MainVM.Left; + UserSettingStorage.Instance.WindowTop = this.MainVM.Top; + this.MainVM.IsVisible = false; + } + + private void ShowWox(bool selectAll = true) + { + UserSettingStorage.Instance.IncreaseActivateTimes(); + this.MainVM.IsVisible = true; + + //TODO:Adjust window properties + //Left = GetWindowsLeft(); + //Top = GetWindowsTop(); + + //Show(); + //Activate(); + //Focus(); + //tbQuery.Focus(); + //ResetQueryHistoryIndex(); + //if (selectAll) tbQuery.SelectAll(); + } + + public void SetHotkey(string hotkeyStr, EventHandler action) + { + var hotkey = new HotkeyModel(hotkeyStr); + SetHotkey(hotkey, action); + } + + public void SetHotkey(HotkeyModel hotkey, EventHandler action) + { + string hotkeyStr = hotkey.ToString(); + try + { + HotkeyManager.Current.AddOrReplace(hotkeyStr, hotkey.CharKey, hotkey.ModifierKeys, action); + } + catch (Exception) + { + string errorMsg = string.Format(InternationalizationManager.Instance.GetTranslation("registerHotkeyFailed"), hotkeyStr); + MessageBox.Show(errorMsg); + } + } + + public void RemoveHotkey(string hotkeyStr) + { + if (!string.IsNullOrEmpty(hotkeyStr)) + { + HotkeyManager.Current.Remove(hotkeyStr); + } + } + + /// + /// Checks if Wox should ignore any hotkeys + /// + /// + private bool ShouldIgnoreHotkeys() + { + //double if to omit calling win32 function + if (UserSettingStorage.Instance.IgnoreHotkeysOnFullscreen) + if (WindowIntelopHelper.IsWindowFullscreen()) + return true; + + return false; + } + + private void SetCustomPluginHotkey() + { + if (UserSettingStorage.Instance.CustomPluginHotkeys == null) return; + foreach (CustomPluginHotkey hotkey in UserSettingStorage.Instance.CustomPluginHotkeys) + { + CustomPluginHotkey hotkey1 = hotkey; + SetHotkey(hotkey.Hotkey, delegate + { + if (ShouldIgnoreHotkeys()) return; + ShowApp(); + ChangeQuery(hotkey1.ActionKeyword, true); + }); + } + } + + private void OnHotkey(object sender, HotkeyEventArgs e) + { + if (ShouldIgnoreHotkeys()) return; + ToggleWox(); + e.Handled = true; + } + + private void ToggleWox() + { + if (!MainVM.IsVisible) + { + ShowWox(); + } + else + { + HideWox(); + } + } + + #endregion + } +} diff --git a/Wox/ViewModel/BaseViewModel.cs b/Wox/ViewModel/BaseViewModel.cs new file mode 100644 index 0000000000..c720dfcdf0 --- /dev/null +++ b/Wox/ViewModel/BaseViewModel.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Input; + +namespace Wox.ViewModel +{ + public class BaseViewModel : INotifyPropertyChanged + { + + protected virtual void OnPropertyChanged(string propertyName) + { + if (null != this.PropertyChanged) + { + this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); + } + } + + public event PropertyChangedEventHandler PropertyChanged; + } + + public class RelayCommand : ICommand + { + + private Action _action; + + public RelayCommand(Action action) + { + this._action = action; + } + + public virtual bool CanExecute(object parameter) + { + return true; + } + + public event EventHandler CanExecuteChanged; + + public virtual void Execute(object parameter) + { + if (null != this._action) + { + this._action(); + } + } + } +} diff --git a/Wox/ViewModel/MainViewModel.cs b/Wox/ViewModel/MainViewModel.cs new file mode 100644 index 0000000000..b79ca98dc9 --- /dev/null +++ b/Wox/ViewModel/MainViewModel.cs @@ -0,0 +1,449 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Input; +using Wox.Core.Plugin; +using Wox.Core.Resource; +using Wox.Infrastructure; +using Wox.Infrastructure.Hotkey; +using Wox.Plugin; +using Wox.Storage; + +namespace Wox.ViewModel +{ + public class MainViewModel : BaseViewModel + { + #region Private Fields + + private ResultPanelViewModel _searchResultPanel; + private ResultPanelViewModel _actionPanel; + private string _queryText; + private bool _isVisible; + private bool _isSearchResultPanelVisible; + private bool _isActionPanelVisible; + private bool _isProgressBarVisible; + private bool _isProgressBarTooltipVisible; + private double _left; + private double _top; + + private bool _queryHasReturn; + private Query _lastQuery = new Query(); + private bool _ignoreTextChange; + private List CurrentContextMenus = new List(); + private string _textBeforeEnterContextMenuMode; + + #endregion + + #region Constructor + + public MainViewModel() + { + this.InitializeResultPanel(); + this.InitializeActionPanel(); + this.InitializeKeyCommands(); + + this._queryHasReturn = false; + } + + #endregion + + #region ViewModel Properties + + public ResultPanelViewModel SearchResultPanel + { + get + { + return this._searchResultPanel; + } + } + + public ResultPanelViewModel ActionPanel + { + get + { + return this._actionPanel; + } + } + + public string QueryText + { + get + { + return this._queryText; + } + set + { + this._queryText = value; + OnPropertyChanged("QueryText"); + + this.HandleQueryTextUpdated(); + } + } + + public bool IsVisible + { + get + { + return this._isVisible; + } + set + { + this._isVisible = value; + OnPropertyChanged("IsVisible"); + + if (!value && this.IsActionPanelVisible) + { + this.BackToSearchMode(); + } + } + } + + public bool IsSearchResultPanelVisible + { + get + { + return this._isSearchResultPanelVisible; + } + set + { + this._isSearchResultPanelVisible = value; + OnPropertyChanged("IsSearchResultPanelVisible"); + } + } + + public bool IsActionPanelVisible + { + get + { + return this._isActionPanelVisible; + } + set + { + this._isActionPanelVisible = value; + OnPropertyChanged("IsActionPanelVisible"); + } + } + + public bool IsProgressBarVisible + { + get + { + return this._isProgressBarVisible; + } + set + { + this._isProgressBarVisible = value; + OnPropertyChanged("IsProgressBarVisible"); + } + } + + public bool IsProgressBarTooltipVisible + { + get + { + return this._isProgressBarTooltipVisible; + } + set + { + this._isProgressBarTooltipVisible = value; + OnPropertyChanged("IsProgressBarTooltipVisible"); + } + } + + public double Left + { + get + { + return this._left; + } + set + { + this._left = value; + OnPropertyChanged("Left"); + } + } + + public double Top + { + get + { + return this._top; + } + set + { + this._top = value; + OnPropertyChanged("Top"); + } + } + + public ICommand EscCommand + { + get; + set; + } + + #endregion + + #region Private Methods + + private void InitializeKeyCommands() + { + this.EscCommand = new RelayCommand(() => { + + if (this.IsActionPanelVisible) + { + this.BackToSearchMode(); + } + else + { + this.IsVisible = false; + } + + }); + } + + private void InitializeResultPanel() + { + this._searchResultPanel = new ResultPanelViewModel(); + this.IsSearchResultPanelVisible = false; + this._searchResultPanel.ResultOpenedInPanel += (o, e) => + { + if (e.HideWindow) + { + this.IsVisible = false; + } + UserSelectedRecordStorage.Instance.Add(e.Result.RawResult); + QueryHistoryStorage.Instance.Add(this.QueryText); + }; + + this._searchResultPanel.ResultActionPanelOpenedInPanel += (o, e) => + { + this.ShowActionPanel(e.Result.RawResult); + }; + } + + private void ShowActionPanel(Result result) + { + if (result == null) return; + List results = PluginManager.GetContextMenusForPlugin(result); + results.ForEach(o => + { + o.PluginDirectory = PluginManager.GetPluginForId(result.PluginID).Metadata.PluginDirectory; + o.PluginID = result.PluginID; + o.OriginQuery = result.OriginQuery; + }); + + results.Add(GetTopMostContextMenu(result)); + + _textBeforeEnterContextMenuMode = this.QueryText; + + this._actionPanel.Clear(); + this._actionPanel.AddResults(results, result.PluginID); + CurrentContextMenus = results; + + this.IsActionPanelVisible = true; + this.IsSearchResultPanelVisible = false; + + this.QueryText = ""; + + } + + private Result GetTopMostContextMenu(Result result) + { + if (TopMostRecordStorage.Instance.IsTopMost(result)) + { + return new Result(InternationalizationManager.Instance.GetTranslation("cancelTopMostInThisQuery"), "Images\\down.png") + { + PluginDirectory = WoxDirectroy.Executable, + Action = _ => + { + TopMostRecordStorage.Instance.Remove(result); + //TODO:Modify the way showing this message + //ShowMsg("Succeed", "", ""); + return false; + } + }; + } + else + { + return new Result(InternationalizationManager.Instance.GetTranslation("setAsTopMostInThisQuery"), "Images\\up.png") + { + PluginDirectory = WoxDirectroy.Executable, + Action = _ => + { + TopMostRecordStorage.Instance.AddOrUpdate(result); + //TODO:Modify the way showing this message + //ShowMsg("Succeed", "", ""); + return false; + } + }; + } + } + + private void InitializeActionPanel() + { + this._actionPanel = new ResultPanelViewModel(); + this.IsActionPanelVisible = false; + this._actionPanel.ResultOpenedInPanel += (o, e) => + { + if (e.HideWindow) + { + this.IsVisible = false; + } + }; + } + + private void HandleQueryTextUpdated() + { + if (_ignoreTextChange) { _ignoreTextChange = false; return; } + + this.IsProgressBarTooltipVisible = false; + if (this.IsActionPanelVisible) + { + QueryActionPanel(); + } + else + { + string query = this.QueryText.Trim(); + if (!string.IsNullOrEmpty(query)) + { + Query(query); + //reset query history index after user start new query + ResetQueryHistoryIndex(); + } + else + { + this._searchResultPanel.Clear(); + } + } + } + + private void QueryActionPanel() + { + var contextMenuId = "Context Menu Id"; + this._actionPanel.Clear(); + var query = this.QueryText.ToLower(); + if (string.IsNullOrEmpty(query)) + { + this._actionPanel.AddResults(CurrentContextMenus, contextMenuId); + } + else + { + List filterResults = new List(); + foreach (Result contextMenu in CurrentContextMenus) + { + if (StringMatcher.IsMatch(contextMenu.Title, query) + || StringMatcher.IsMatch(contextMenu.SubTitle, query)) + { + filterResults.Add(contextMenu); + } + } + this._actionPanel.AddResults(filterResults, contextMenuId); + } + } + + private void Query(string text) + { + _queryHasReturn = false; + var query = PluginManager.QueryInit(text); + if (query != null) + { + // handle the exclusiveness of plugin using action keyword + string lastKeyword = _lastQuery.ActionKeyword; + string keyword = query.ActionKeyword; + if (string.IsNullOrEmpty(lastKeyword)) + { + if (!string.IsNullOrEmpty(keyword)) + { + this._searchResultPanel.RemoveResultsExcept(PluginManager.NonGlobalPlugins[keyword].Metadata); + } + } + else + { + if (string.IsNullOrEmpty(keyword)) + { + this._searchResultPanel.RemoveResultsFor(PluginManager.NonGlobalPlugins[lastKeyword].Metadata); + } + else if (lastKeyword != keyword) + { + this._searchResultPanel.RemoveResultsExcept(PluginManager.NonGlobalPlugins[keyword].Metadata); + } + } + _lastQuery = query; + + Action action = new Action(async () => + { + await Task.Delay(150); + if (!string.IsNullOrEmpty(query.RawQuery) && query.RawQuery == _lastQuery.RawQuery && !_queryHasReturn) + { + this.IsProgressBarTooltipVisible = true; + } + }); + action.Invoke(); + + //Application.Current.Dispatcher.InvokeAsync(async () => + //{ + // await Task.Delay(150); + // if (!string.IsNullOrEmpty(query.RawQuery) && query.RawQuery == _lastQuery.RawQuery && !_queryHasReturn) + // { + // StartProgress(); + // } + //}); + PluginManager.QueryForAllPlugins(query); + } + + this.IsProgressBarTooltipVisible = false; + } + + private void ResetQueryHistoryIndex() + { + this._searchResultPanel.RemoveResultsFor(QueryHistoryStorage.MetaData); + QueryHistoryStorage.Instance.Reset(); + } + + private void UpdateResultViewInternal(List list, PluginMetadata metadata) + { + Stopwatch.Normal($"UI update cost for {metadata.Name}", + () => { this._searchResultPanel.AddResults(list, metadata.ID); }); + } + + private void BackToSearchMode() + { + this.QueryText = _textBeforeEnterContextMenuMode; + this.IsActionPanelVisible = false; + this.IsSearchResultPanelVisible = true; + } + + #endregion + + #region Public Methods + + public void UpdateResultView(List list, PluginMetadata metadata, Query originQuery) + { + _queryHasReturn = true; + this.IsProgressBarTooltipVisible = false; + + list.ForEach(o => + { + o.Score += UserSelectedRecordStorage.Instance.GetSelectedCount(o) * 5; + }); + if (originQuery.RawQuery == _lastQuery.RawQuery) + { + Application.Current.Dispatcher.Invoke(() => { + UpdateResultViewInternal(list, metadata); + }); + } + + if(list.Count > 0) + { + this.IsSearchResultPanelVisible = true; + } + } + + #endregion + + } +} diff --git a/less.exe.stackdump b/less.exe.stackdump new file mode 100644 index 0000000000..ec6e43c4a0 --- /dev/null +++ b/less.exe.stackdump @@ -0,0 +1,15 @@ +Stack trace: +Frame Function Args +00000010000 0018007208E (0018024F7D0, 00180215E59, 00000010000, 0000022B940) +00000010000 00180046DF2 (0000022C9A8, 001803253F8, 00000000001, 001803253F8) +00000010000 00180046E32 (00000000001, 00180325608, 00000010000, 00000000002) +00000010000 0018006C65D (001800CDED2, 00000000000, 00000000000, 00000000000) +0000022CBC0 0018006C6EE (00000000020, 00000000023, 00180047805, 00000000000) +0000022CBC0 001800475B7 (000002B6150, 00002080014, 0000022CDB0, 00000000000) +00000000000 0018004602C (00000000000, 00000000000, 00000000000, 00000000000) +00000000000 001800460C4 (00000000000, 00000000000, 00000000000, 00000000000) +00000000000 00100414FC1 (00000000000, 00000000000, 00000000000, 00000000000) +00000000000 00100401010 (00000000000, 00000000000, 00000000000, 00000000000) +00000000000 000779A59CD (00000000000, 00000000000, 00000000000, 00000000000) +00000000000 00077ADB981 (00000000000, 00000000000, 00000000000, 00000000000) +End of stack trace From 934a41e414df80c03bd5036d524d6db84300897b Mon Sep 17 00:00:00 2001 From: Colin Liu Date: Thu, 18 Feb 2016 19:31:15 +0800 Subject: [PATCH 02/18] Refactor ResultPanel/ResultItem with MVVM --- Wox/ResultPanel.xaml | 73 ++--- Wox/ResultPanel.xaml.cs | 177 ++++-------- Wox/ViewModel/ResultItemViewModel.cs | 127 +++++++++ Wox/ViewModel/ResultPanelViewModel.cs | 378 ++++++++++++++++++++++++++ 4 files changed, 598 insertions(+), 157 deletions(-) create mode 100644 Wox/ViewModel/ResultItemViewModel.cs create mode 100644 Wox/ViewModel/ResultPanelViewModel.cs diff --git a/Wox/ResultPanel.xaml b/Wox/ResultPanel.xaml index 6abd51faec..21c1e890f2 100644 --- a/Wox/ResultPanel.xaml +++ b/Wox/ResultPanel.xaml @@ -6,48 +6,59 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:converters="clr-namespace:Wox.Converters" mc:Ignorable="d" d:DesignWidth="100" d:DesignHeight="100"> - - - - + + + + + + + + + + - - - - - - - - - + + + + + + + + - - - - - - + + + + + - + + - - - + Grid.Row="1" x:Name="tbSubTitle" Text="{Binding SubTitle}" > + + + + + + - + - + + + diff --git a/Wox/ResultPanel.xaml.cs b/Wox/ResultPanel.xaml.cs index 4a622815de..a797bda857 100644 --- a/Wox/ResultPanel.xaml.cs +++ b/Wox/ResultPanel.xaml.cs @@ -10,123 +10,72 @@ using Wox.Core.UserSettings; using Wox.Helper; using Wox.Plugin; using Wox.Storage; +using Wox.ViewModel; namespace Wox { [Synchronization] public partial class ResultPanel : UserControl { - public event Action LeftMouseClickEvent; - public event Action RightMouseClickEvent; public event Action ItemDropEvent; - private readonly ListBoxItems _results; private readonly object _resultsUpdateLock = new object(); - protected virtual void OnRightMouseClick(Result result) - { - Action handler = RightMouseClickEvent; - if (handler != null) handler(result); - } - - protected virtual void OnLeftMouseClick(Result result) - { - Action handler = LeftMouseClickEvent; - if (handler != null) handler(result); - } - - - public int MaxResultsToShow { get { return UserSettingStorage.Instance.MaxResultsToShow * 50; } } - - internal void RemoveResultsFor(PluginMetadata metadata) - { - lock (_resultsUpdateLock) - { - _results.RemoveAll(r => r.PluginID == metadata.ID); - } - } - - internal void RemoveResultsExcept(PluginMetadata metadata) - { - lock (_resultsUpdateLock) - { - _results.RemoveAll(r => r.PluginID != metadata.ID); - } - } - public void AddResults(List newResults, string resultId) { - lock (_resultsUpdateLock) - { - // todo use async to do new result calculation - var resultsCopy = _results.ToList(); - var oldResults = resultsCopy.Where(r => r.PluginID == resultId).ToList(); - // intersection of A (old results) and B (new newResults) - var intersection = oldResults.Intersect(newResults).ToList(); - // remove result of relative complement of B in A - foreach (var result in oldResults.Except(intersection)) - { - resultsCopy.Remove(result); - } + //lock (_resultsUpdateLock) + //{ + // // todo use async to do new result calculation + // var resultsCopy = _results.ToList(); + // var oldResults = resultsCopy.Where(r => r.PluginID == resultId).ToList(); + // // intersection of A (old results) and B (new newResults) + // var intersection = oldResults.Intersect(newResults).ToList(); + // // remove result of relative complement of B in A + // foreach (var result in oldResults.Except(intersection)) + // { + // resultsCopy.Remove(result); + // } - // update scores - foreach (var result in newResults) - { - if (IsTopMostResult(result)) - { - result.Score = int.MaxValue; - } - } + // // update scores + // foreach (var result in newResults) + // { + // if (IsTopMostResult(result)) + // { + // result.Score = int.MaxValue; + // } + // } - // update index for result in intersection of A and B - foreach (var commonResult in intersection) - { - int oldIndex = resultsCopy.IndexOf(commonResult); - int oldScore = resultsCopy[oldIndex].Score; - int newScore = newResults[newResults.IndexOf(commonResult)].Score; - if (newScore != oldScore) - { - var oldResult = resultsCopy[oldIndex]; - oldResult.Score = newScore; - resultsCopy.RemoveAt(oldIndex); - int newIndex = InsertIndexOf(newScore, resultsCopy); - resultsCopy.Insert(newIndex, oldResult); + // // update index for result in intersection of A and B + // foreach (var commonResult in intersection) + // { + // int oldIndex = resultsCopy.IndexOf(commonResult); + // int oldScore = resultsCopy[oldIndex].Score; + // int newScore = newResults[newResults.IndexOf(commonResult)].Score; + // if (newScore != oldScore) + // { + // var oldResult = resultsCopy[oldIndex]; + // oldResult.Score = newScore; + // resultsCopy.RemoveAt(oldIndex); + // int newIndex = InsertIndexOf(newScore, resultsCopy); + // resultsCopy.Insert(newIndex, oldResult); - } - } + // } + // } - // insert result in relative complement of A in B - foreach (var result in newResults.Except(intersection)) - { - int newIndex = InsertIndexOf(result.Score, resultsCopy); - resultsCopy.Insert(newIndex, result); - } + // // insert result in relative complement of A in B + // foreach (var result in newResults.Except(intersection)) + // { + // int newIndex = InsertIndexOf(result.Score, resultsCopy); + // resultsCopy.Insert(newIndex, result); + // } - // update UI in one run, so it can avoid UI flickering - _results.Update(resultsCopy); + // // update UI in one run, so it can avoid UI flickering + // _results.Update(resultsCopy); - lbResults.Margin = lbResults.Items.Count > 0 ? new Thickness { Top = 8 } : new Thickness { Top = 0 }; - SelectFirst(); - } - } - - private bool IsTopMostResult(Result result) - { - return TopMostRecordStorage.Instance.IsTopMost(result); - } - - private int InsertIndexOf(int newScore, IList list) - { - int index = 0; - for (; index < list.Count; index++) - { - var result = list[index]; - if (newScore > result.Score) - { - break; - } - } - return index; + // lbResults.Margin = lbResults.Items.Count > 0 ? new Thickness { Top = 8 } : new Thickness { Top = 0 }; + // SelectFirst(); + //} } + public void SelectNext() { @@ -245,17 +194,6 @@ namespace Wox public ResultPanel() { InitializeComponent(); - _results = new ListBoxItems(); - lbResults.ItemsSource = _results; - } - - public void Clear() - { - lock (_resultsUpdateLock) - { - _results.Clear(); - lbResults.Margin = new Thickness { Top = 0 }; - } } private void lbResults_SelectionChanged(object sender, SelectionChangedEventArgs e) @@ -270,19 +208,6 @@ namespace Wox } } - private void LbResults_OnPreviewMouseDown(object sender, MouseButtonEventArgs e) - { - var item = ItemsControl.ContainerFromElement(lbResults, e.OriginalSource as DependencyObject) as ListBoxItem; - if (item != null && e.ChangedButton == MouseButton.Left) - { - OnLeftMouseClick(item.DataContext as Result); - } - if (item != null && e.ChangedButton == MouseButton.Right) - { - OnRightMouseClick(item.DataContext as Result); - } - } - public void SelectNextPage() { int index = lbResults.SelectedIndex; @@ -310,14 +235,14 @@ namespace Wox var item = ItemsControl.ContainerFromElement(lbResults, e.OriginalSource as DependencyObject) as ListBoxItem; if (item != null) { - OnItemDropEvent(item.DataContext as Result, e.Data, e); + OnItemDropEvent(item.DataContext as ResultItemViewModel, e.Data, e); } } - protected virtual void OnItemDropEvent(Result obj, IDataObject data, DragEventArgs e) + protected virtual void OnItemDropEvent(ResultItemViewModel obj, IDataObject data, DragEventArgs e) { var handler = ItemDropEvent; - if (handler != null) handler(obj, data, e); + if (handler != null) handler(obj.RawResult, data, e); } } } \ No newline at end of file diff --git a/Wox/ViewModel/ResultItemViewModel.cs b/Wox/ViewModel/ResultItemViewModel.cs new file mode 100644 index 0000000000..3af615d387 --- /dev/null +++ b/Wox/ViewModel/ResultItemViewModel.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Wox.Infrastructure.Hotkey; +using Wox.Plugin; +using Wox.Storage; + +namespace Wox.ViewModel +{ + public class ResultItemViewModel : BaseViewModel + { + #region Private Fields + + private Result _result; + + #endregion + + #region Constructor + + public ResultItemViewModel(Result result) + { + if(null!= result) + { + this._result = result; + + this.OpenResultCommand = new RelayCommand(() => { + + bool hideWindow = result.Action(new ActionContext + { + SpecialKeyState = GlobalHotkey.Instance.CheckModifiers() + }); + + if (null != this.ResultOpened) + { + this.ResultOpened(this, new ResultOpenedEventArgs(hideWindow)); + } + }); + + this.OpenResultActionPanelCommand = new RelayCommand(()=> { + + if(null!= ResultActionPanelOpened) + { + this.ResultActionPanelOpened(this, new EventArgs()); + } + + }); + } + } + + #endregion + + #region ViewModel Properties + + public string Title + { + get + { + return this._result.Title; + } + } + + public string SubTitle + { + get + { + return this._result.SubTitle; + } + } + + public string FullIcoPath + { + get + { + //TODO: Some of the properties in Result class may be moved to this class + return this._result.FullIcoPath; + } + } + + public RelayCommand OpenResultCommand + { + get; + set; + } + + public RelayCommand OpenResultActionPanelCommand + { + get; + set; + } + + #endregion + + #region Properties + + public Result RawResult + { + get + { + return this._result; + } + } + + #endregion + + public event EventHandler ResultOpened; + + public event EventHandler ResultActionPanelOpened; + + } + + public class ResultOpenedEventArgs : EventArgs + { + + public bool HideWindow + { + get; + private set; + } + + public ResultOpenedEventArgs(bool hideWindow) + { + this.HideWindow = hideWindow; + } + } +} diff --git a/Wox/ViewModel/ResultPanelViewModel.cs b/Wox/ViewModel/ResultPanelViewModel.cs new file mode 100644 index 0000000000..052fd01378 --- /dev/null +++ b/Wox/ViewModel/ResultPanelViewModel.cs @@ -0,0 +1,378 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using Wox.Core.UserSettings; +using Wox.Plugin; +using Wox.Storage; + +namespace Wox.ViewModel +{ + public class ResultPanelViewModel : BaseViewModel + { + #region Private Fields + + private ResultItemViewModel _selectedResult; + private ResultCollection _results; + private bool _isVisible; + private Thickness _margin; + + private readonly object _resultsUpdateLock = new object(); + + #endregion + + #region Constructor + + public ResultPanelViewModel() + { + this._results = new ResultCollection( + + (o, e)=> { + + if(null != ResultOpenedInPanel) + { + this.ResultOpenedInPanel(this, new ResultOpenedInPanelEventArgs(o as ResultItemViewModel, e.HideWindow)); + } + }, + + (o, e) => { + + if(null != ResultActionPanelOpenedInPanel) + { + this.ResultActionPanelOpenedInPanel(this, new ResultActionPanelOpenedInPanelEventArgs(o as ResultItemViewModel)); + } + + } + ); + } + + #endregion + + #region ViewModel Properties + + public int MaxHeight + { + get + { + return UserSettingStorage.Instance.MaxResultsToShow * 50; + } + } + + public ResultCollection Results + { + get + { + return this._results; + } + } + + public ResultItemViewModel SelectedResult + { + get + { + return this._selectedResult; + } + set + { + this._selectedResult = value; + OnPropertyChanged("SelectedResult"); + } + } + + public Thickness Margin + { + get + { + return this._margin; + } + set + { + this._margin = value; + OnPropertyChanged("Margin"); + } + } + + #endregion + + #region Private Methods + + private bool IsTopMostResult(Result result) + { + return TopMostRecordStorage.Instance.IsTopMost(result); + } + + private int InsertIndexOf(int newScore, IList list) + { + int index = 0; + for (; index < list.Count; index++) + { + var result = list[index]; + if (newScore > result.RawResult.Score) + { + break; + } + } + return index; + } + + #endregion + + #region Public Methods + + public void Clear() + { + this._results.Clear(); + } + + public void RemoveResultsExcept(PluginMetadata metadata) + { + lock (_resultsUpdateLock) + { + _results.RemoveAll(r => r.RawResult.PluginID != metadata.ID); + } + } + + public void RemoveResultsFor(PluginMetadata metadata) + { + lock (_resultsUpdateLock) + { + _results.RemoveAll(r => r.RawResult.PluginID == metadata.ID); + } + } + + public void AddResults(List newRawResults, string resultId) + { + lock (_resultsUpdateLock) + { + var newResults = new List(); + newRawResults.ForEach((re) => { newResults.Add(new ResultItemViewModel(re)); }); + // todo use async to do new result calculation + var resultsCopy = _results.ToList(); + var oldResults = resultsCopy.Where(r => r.RawResult.PluginID == resultId).ToList(); + // intersection of A (old results) and B (new newResults) + var intersection = oldResults.Intersect(newResults).ToList(); + // remove result of relative complement of B in A + foreach (var result in oldResults.Except(intersection)) + { + resultsCopy.Remove(result); + } + + // update scores + foreach (var result in newResults) + { + if (IsTopMostResult(result.RawResult)) + { + result.RawResult.Score = int.MaxValue; + } + } + + // update index for result in intersection of A and B + foreach (var commonResult in intersection) + { + int oldIndex = resultsCopy.IndexOf(commonResult); + int oldScore = resultsCopy[oldIndex].RawResult.Score; + int newScore = newResults[newResults.IndexOf(commonResult)].RawResult.Score; + if (newScore != oldScore) + { + var oldResult = resultsCopy[oldIndex]; + oldResult.RawResult.Score = newScore; + resultsCopy.RemoveAt(oldIndex); + int newIndex = InsertIndexOf(newScore, resultsCopy); + resultsCopy.Insert(newIndex, oldResult); + + } + } + + // insert result in relative complement of A in B + foreach (var result in newResults.Except(intersection)) + { + int newIndex = InsertIndexOf(result.RawResult.Score, resultsCopy); + resultsCopy.Insert(newIndex, result); + } + + // update UI in one run, so it can avoid UI flickering + _results.Update(resultsCopy); + + if(this._results.Count > 0) + { + this.Margin = new Thickness { Top = 8 }; + this.SelectedResult = this._results[0]; + } + else + { + this.Margin = new Thickness { Top = 0 }; + } + } + } + + + #endregion + + public event EventHandler ResultOpenedInPanel; + + public event EventHandler ResultActionPanelOpenedInPanel; + + public class ResultCollection : ObservableCollection + // todo implement custom moveItem,removeItem,insertItem for better performance + { + + private EventHandler _resultOpenedHandler; + private EventHandler _resultActionPanelOpenedHandler; + + public ResultCollection(EventHandler resultOpenedHandler, + EventHandler resultActionPanelOpenedHandler) + { + this._resultOpenedHandler = resultOpenedHandler; + this._resultActionPanelOpenedHandler = resultActionPanelOpenedHandler; + } + + protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) + { + base.OnCollectionChanged(e); + + if(e.Action == NotifyCollectionChangedAction.Add) + { + foreach(var item in e.NewItems) + { + var resultVM = item as ResultItemViewModel; + resultVM.ResultOpened += this._resultOpenedHandler; + resultVM.ResultActionPanelOpened += this._resultActionPanelOpenedHandler; + } + } + + if(e.Action == NotifyCollectionChangedAction.Remove) + { + foreach (var item in e.OldItems) + { + var resultVM = item as ResultItemViewModel; + resultVM.ResultOpened -= this._resultOpenedHandler; + resultVM.ResultActionPanelOpened -= this._resultActionPanelOpenedHandler; + } + } + + if(e.Action == NotifyCollectionChangedAction.Replace) + { + foreach (var item in e.NewItems) + { + var resultVM = item as ResultItemViewModel; + resultVM.ResultOpened += this._resultOpenedHandler; + resultVM.ResultActionPanelOpened += this._resultActionPanelOpenedHandler; + } + + foreach (var item in e.OldItems) + { + var resultVM = item as ResultItemViewModel; + resultVM.ResultOpened -= this._resultOpenedHandler; + resultVM.ResultActionPanelOpened -= this._resultActionPanelOpenedHandler; + } + } + } + + public void RemoveAll(Predicate predicate) + { + CheckReentrancy(); + + List itemsToRemove = Items.Where(x => predicate(x)).ToList(); + if (itemsToRemove.Count > 0) + { + + itemsToRemove.ForEach(item => { + + Items.Remove(item); + + }); + + OnPropertyChanged(new PropertyChangedEventArgs("Count")); + OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); + // fuck ms + // http://blogs.msdn.com/b/nathannesbit/archive/2009/04/20/addrange-and-observablecollection.aspx + // http://geekswithblogs.net/NewThingsILearned/archive/2008/01/16/listcollectionviewcollectionview-doesnt-support-notifycollectionchanged-with-multiple-items.aspx + // PS: don't use Reset for other data updates, it will cause UI flickering + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + + + } + + public void Update(List newItems) + { + int newCount = newItems.Count; + int oldCount = Items.Count; + int location = newCount > oldCount ? oldCount : newCount; + for (int i = 0; i < location; i++) + { + ResultItemViewModel oldItem = Items[i]; + ResultItemViewModel newItem = newItems[i]; + if (!oldItem.Equals(newItem)) + { + this[i] = newItem; + } + else if (oldItem.RawResult.Score != newItem.RawResult.Score) + { + this[i].RawResult.Score = newItem.RawResult.Score; + } + } + + if (newCount > oldCount) + { + for (int i = oldCount; i < newCount; i++) + { + Add(newItems[i]); + } + } + else + { + int removeIndex = newCount; + for (int i = newCount; i < oldCount; i++) + { + RemoveAt(removeIndex); + } + } + + } + } + + } + + public class ResultOpenedInPanelEventArgs : EventArgs + { + + public bool HideWindow + { + get; + private set; + } + + public ResultItemViewModel Result + { + get; + private set; + } + + public ResultOpenedInPanelEventArgs(ResultItemViewModel result, bool hideWindow) + { + this.HideWindow = hideWindow; + this.Result = result; + } + } + + public class ResultActionPanelOpenedInPanelEventArgs : EventArgs + { + + public ResultItemViewModel Result + { + get; + private set; + } + + public ResultActionPanelOpenedInPanelEventArgs(ResultItemViewModel result) + { + this.Result = result; + } + } +} From f16804c0f8a0f86126c99cae9a7420fd687ecab4 Mon Sep 17 00:00:00 2001 From: Colin Liu Date: Thu, 18 Feb 2016 19:32:48 +0800 Subject: [PATCH 03/18] Comment out code for later update --- Wox/CommandArgs/CommandArgsFactory.cs | 2 +- Wox/CommandArgs/QueryCommandArg.cs | 4 ++-- Wox/CommandArgs/ReloadPluginCommandArg.cs | 2 +- Wox/CommandArgs/ToggleCommandArg.cs | 3 ++- Wox/CustomQueryHotkeySetting.xaml.cs | 26 ++++++++++----------- Wox/SettingWindow.xaml.cs | 28 +++++++++++------------ 6 files changed, 33 insertions(+), 32 deletions(-) diff --git a/Wox/CommandArgs/CommandArgsFactory.cs b/Wox/CommandArgs/CommandArgsFactory.cs index e6e4a91b74..14a3f99a72 100644 --- a/Wox/CommandArgs/CommandArgsFactory.cs +++ b/Wox/CommandArgs/CommandArgsFactory.cs @@ -34,7 +34,7 @@ namespace Wox.CommandArgs } else { - App.Window.ShowApp(); + App.API.ShowApp(); } } } diff --git a/Wox/CommandArgs/QueryCommandArg.cs b/Wox/CommandArgs/QueryCommandArg.cs index 22f0443528..d1df8e0a5b 100644 --- a/Wox/CommandArgs/QueryCommandArg.cs +++ b/Wox/CommandArgs/QueryCommandArg.cs @@ -16,9 +16,9 @@ namespace Wox.CommandArgs if (args.Count > 0) { string query = args[0]; - App.Window.ChangeQuery(query); + App.API.ChangeQuery(query); } - App.Window.ShowApp(); + App.API.ShowApp(); } } } diff --git a/Wox/CommandArgs/ReloadPluginCommandArg.cs b/Wox/CommandArgs/ReloadPluginCommandArg.cs index aec5724c11..a866446d2d 100644 --- a/Wox/CommandArgs/ReloadPluginCommandArg.cs +++ b/Wox/CommandArgs/ReloadPluginCommandArg.cs @@ -12,7 +12,7 @@ namespace Wox.CommandArgs public void Execute(IList args) { - PluginManager.Init(App.Window); + PluginManager.Init(App.API); } } } diff --git a/Wox/CommandArgs/ToggleCommandArg.cs b/Wox/CommandArgs/ToggleCommandArg.cs index 623648e311..7c2cbefb12 100644 --- a/Wox/CommandArgs/ToggleCommandArg.cs +++ b/Wox/CommandArgs/ToggleCommandArg.cs @@ -11,7 +11,8 @@ namespace Wox.CommandArgs public void Execute(IList args) { - App.Window.ToggleWox(); + //TODO: Add ToggleWox Method + //App.API.ToggleWox(); } } } diff --git a/Wox/CustomQueryHotkeySetting.xaml.cs b/Wox/CustomQueryHotkeySetting.xaml.cs index 7c58babe1c..d7136398fd 100644 --- a/Wox/CustomQueryHotkeySetting.xaml.cs +++ b/Wox/CustomQueryHotkeySetting.xaml.cs @@ -44,11 +44,11 @@ namespace Wox ActionKeyword = tbAction.Text }; UserSettingStorage.Instance.CustomPluginHotkeys.Add(pluginHotkey); - settingWidow.MainWindow.SetHotkey(ctlHotkey.CurrentHotkey, delegate - { - settingWidow.MainWindow.ChangeQuery(pluginHotkey.ActionKeyword); - settingWidow.MainWindow.ShowApp(); - }); + //settingWidow.MainWindow.SetHotkey(ctlHotkey.CurrentHotkey, delegate + //{ + // settingWidow.MainWindow.ChangeQuery(pluginHotkey.ActionKeyword); + // settingWidow.MainWindow.ShowApp(); + //}); MessageBox.Show(InternationalizationManager.Instance.GetTranslation("succeed")); } else @@ -62,12 +62,12 @@ namespace Wox updateCustomHotkey.ActionKeyword = tbAction.Text; updateCustomHotkey.Hotkey = ctlHotkey.CurrentHotkey.ToString(); //remove origin hotkey - settingWidow.MainWindow.RemoveHotkey(oldHotkey); - settingWidow.MainWindow.SetHotkey(updateCustomHotkey.Hotkey, delegate - { - settingWidow.MainWindow.ShowApp(); - settingWidow.MainWindow.ChangeQuery(updateCustomHotkey.ActionKeyword); - }); + //settingWidow.MainWindow.RemoveHotkey(oldHotkey); + //settingWidow.MainWindow.SetHotkey(updateCustomHotkey.Hotkey, delegate + //{ + // settingWidow.MainWindow.ShowApp(); + // settingWidow.MainWindow.ChangeQuery(updateCustomHotkey.ActionKeyword); + //}); MessageBox.Show(InternationalizationManager.Instance.GetTranslation("succeed")); } @@ -94,8 +94,8 @@ namespace Wox private void BtnTestActionKeyword_OnClick(object sender, RoutedEventArgs e) { - settingWidow.MainWindow.ShowApp(); - settingWidow.MainWindow.ChangeQuery(tbAction.Text); + //settingWidow.MainWindow.ShowApp(); + //settingWidow.MainWindow.ChangeQuery(tbAction.Text); } } } diff --git a/Wox/SettingWindow.xaml.cs b/Wox/SettingWindow.xaml.cs index effdddb618..bd181ed0c6 100644 --- a/Wox/SettingWindow.xaml.cs +++ b/Wox/SettingWindow.xaml.cs @@ -94,7 +94,7 @@ namespace Wox { UserSettingStorage.Instance.MaxResultsToShow = (int)comboMaxResultsToShow.SelectedItem; UserSettingStorage.Instance.Save(); - MainWindow.pnlResult.lbResults.GetBindingExpression(MaxHeightProperty).UpdateTarget(); + //MainWindow.pnlResult.lbResults.GetBindingExpression(MaxHeightProperty).UpdateTarget(); }; cbHideWhenDeactive.IsChecked = UserSettingStorage.Instance.HideWhenDeactive; @@ -250,18 +250,18 @@ namespace Wox { if (ctlHotkey.CurrentHotkeyAvailable) { - MainWindow.SetHotkey(ctlHotkey.CurrentHotkey, delegate - { - if (!MainWindow.IsVisible) - { - MainWindow.ShowApp(); - } - else - { - MainWindow.HideApp(); - } - }); - MainWindow.RemoveHotkey(UserSettingStorage.Instance.Hotkey); + //MainWindow.SetHotkey(ctlHotkey.CurrentHotkey, delegate + //{ + // if (!MainWindow.IsVisible) + // { + // MainWindow.ShowApp(); + // } + // else + // { + // MainWindow.HideApp(); + // } + //}); + //MainWindow.RemoveHotkey(UserSettingStorage.Instance.Hotkey); UserSettingStorage.Instance.Hotkey = ctlHotkey.CurrentHotkey.ToString(); UserSettingStorage.Instance.Save(); } @@ -289,7 +289,7 @@ namespace Wox UserSettingStorage.Instance.CustomPluginHotkeys.Remove(item); lvCustomHotkey.Items.Refresh(); UserSettingStorage.Instance.Save(); - MainWindow.RemoveHotkey(item.Hotkey); + //MainWindow.RemoveHotkey(item.Hotkey); } } From e15baeb4a2047ea06b5e947129a468bb7db6c8b9 Mon Sep 17 00:00:00 2001 From: Colin Liu Date: Thu, 18 Feb 2016 19:35:17 +0800 Subject: [PATCH 04/18] Remove unused code and add required code for MVVM --- Wox/App.xaml.cs | 14 ++++++-- Wox/Helper/ListBoxItems.cs | 71 -------------------------------------- Wox/Wox.csproj | 7 +++- 3 files changed, 18 insertions(+), 74 deletions(-) delete mode 100644 Wox/Helper/ListBoxItems.cs diff --git a/Wox/App.xaml.cs b/Wox/App.xaml.cs index 19ff2c2f17..2b9ed56c32 100644 --- a/Wox/App.xaml.cs +++ b/Wox/App.xaml.cs @@ -10,6 +10,8 @@ using Wox.CommandArgs; using Wox.Core.Plugin; using Wox.Helper; using Wox.Infrastructure; +using Wox.Plugin; +using Wox.ViewModel; using Stopwatch = Wox.Infrastructure.Stopwatch; @@ -20,6 +22,8 @@ namespace Wox private const string Unique = "Wox_Unique_Application_Mutex"; public static MainWindow Window { get; private set; } + public static IPublicAPI API { get; private set; } + [STAThread] public static void Main() { @@ -40,8 +44,14 @@ namespace Wox WoxDirectroy.Executable = Directory.GetParent(Assembly.GetExecutingAssembly().Location).ToString(); RegisterUnhandledException(); ThreadPool.QueueUserWorkItem(o => { ImageLoader.ImageLoader.PreloadImages(); }); + + + MainViewModel mainVM = new MainViewModel(); + API = new PublicAPIInstance(mainVM); Window = new MainWindow(); - PluginManager.Init(Window); + Window.DataContext = mainVM; + + PluginManager.Init(API); CommandArgsFactory.Execute(e.Args.ToList()); }); @@ -59,7 +69,7 @@ namespace Wox { if (args.Count > 0 && args[0] == SingleInstance.Restart) { - Window.CloseApp(); + API.CloseApp(); } else { diff --git a/Wox/Helper/ListBoxItems.cs b/Wox/Helper/ListBoxItems.cs deleted file mode 100644 index bb2bdc2a2e..0000000000 --- a/Wox/Helper/ListBoxItems.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Collections.Specialized; -using System.ComponentModel; -using System.Linq; -using Wox.Plugin; - -namespace Wox.Helper -{ - class ListBoxItems : ObservableCollection - // todo implement custom moveItem,removeItem,insertItem for better performance - { - public void RemoveAll(Predicate predicate) - { - CheckReentrancy(); - - List itemsToRemove = Items.Where(x => predicate(x)).ToList(); - if (itemsToRemove.Count > 0) - { - - itemsToRemove.ForEach(item => Items.Remove(item)); - - OnPropertyChanged(new PropertyChangedEventArgs("Count")); - OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); - // fuck ms - // http://blogs.msdn.com/b/nathannesbit/archive/2009/04/20/addrange-and-observablecollection.aspx - // http://geekswithblogs.net/NewThingsILearned/archive/2008/01/16/listcollectionviewcollectionview-doesnt-support-notifycollectionchanged-with-multiple-items.aspx - // PS: don't use Reset for other data updates, it will cause UI flickering - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); - } - } - - public void Update(List newItems) - { - int newCount = newItems.Count; - int oldCount = Items.Count; - int location = newCount > oldCount ? oldCount : newCount; - for (int i = 0; i < location; i++) - { - Result oldItem = Items[i]; - Result newItem = newItems[i]; - if (!oldItem.Equals(newItem)) - { - this[i] = newItem; - } - else if (oldItem.Score != newItem.Score) - { - this[i].Score = newItem.Score; - } - } - - if (newCount > oldCount) - { - for (int i = oldCount; i < newCount; i++) - { - Add(newItems[i]); - } - } - else - { - int removeIndex = newCount; - for (int i = newCount; i < oldCount; i++) - { - RemoveAt(removeIndex); - } - } - - } - } -} diff --git a/Wox/Wox.csproj b/Wox/Wox.csproj index d05cf6ffd1..e0e17c5cf2 100644 --- a/Wox/Wox.csproj +++ b/Wox/Wox.csproj @@ -123,12 +123,17 @@ - + + + + + + WoxUpdate.xaml From df85543337c5a4cdce4e764f0983118f0d9e2fdc Mon Sep 17 00:00:00 2001 From: Colin Liu Date: Fri, 12 Feb 2016 14:21:12 +0800 Subject: [PATCH 05/18] Implement the support for shortcut key --- Wox/MainWindow.xaml | 28 +++ Wox/MainWindow.xaml.cs | 322 ++++---------------------- Wox/PublicAPIInstance.cs | 4 +- Wox/ResultPanel.xaml | 2 +- Wox/ResultPanel.xaml.cs | 116 ---------- Wox/ViewModel/BaseViewModel.cs | 6 +- Wox/ViewModel/MainViewModel.cs | 188 ++++++++++++++- Wox/ViewModel/ResultItemViewModel.cs | 4 +- Wox/ViewModel/ResultPanelViewModel.cs | 64 +++++ 9 files changed, 330 insertions(+), 404 deletions(-) diff --git a/Wox/MainWindow.xaml b/Wox/MainWindow.xaml index 2ba87ff018..9299032397 100644 --- a/Wox/MainWindow.xaml +++ b/Wox/MainWindow.xaml @@ -17,9 +17,37 @@ Icon="Images\app.png" AllowsTransparency="True" Visibility="{Binding IsVisible,Converter={converters:VisibilityConverter}}" + PreviewKeyDown="Window_PreviewKeyDown" > + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wox/MainWindow.xaml.cs b/Wox/MainWindow.xaml.cs index 4d5ea4744b..b74752d32f 100644 --- a/Wox/MainWindow.xaml.cs +++ b/Wox/MainWindow.xaml.cs @@ -1,40 +1,21 @@ using System; -using System.Collections.Generic; using System.ComponentModel; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Net; -using System.Reflection; -using System.Threading; -using System.Threading.Tasks; using System.Windows; -using System.Windows.Controls; using System.Windows.Forms; using System.Windows.Input; using System.Windows.Media.Animation; -using NHotkey; -using NHotkey.Wpf; using Wox.Core.Plugin; using Wox.Core.Resource; using Wox.Core.Updater; using Wox.Core.UserSettings; using Wox.Helper; -using Wox.Infrastructure; using Wox.Infrastructure.Hotkey; -using Wox.Plugin; -using Wox.Storage; -using Application = System.Windows.Application; -using ContextMenu = System.Windows.Forms.ContextMenu; using DataFormats = System.Windows.DataFormats; using DragEventArgs = System.Windows.DragEventArgs; -using IDataObject = System.Windows.IDataObject; using KeyEventArgs = System.Windows.Input.KeyEventArgs; -using MenuItem = System.Windows.Forms.MenuItem; using MessageBox = System.Windows.MessageBox; -using Stopwatch = Wox.Infrastructure.Stopwatch; -using ToolTip = System.Windows.Controls.ToolTip; using Wox.ViewModel; +using Wox.Plugin; namespace Wox { @@ -59,7 +40,6 @@ namespace Wox //pnlResult.ItemDropEvent += pnlResult_ItemDropEvent; Closing += MainWindow_Closing; - } //void pnlResult_ItemDropEvent(Result result, IDataObject dropDataObject, DragEventArgs args) @@ -173,20 +153,6 @@ namespace Wox if (e.ChangedButton == MouseButton.Left) DragMove(); } - private void ShowWox(bool selectAll = true) - { - //UserSettingStorage.Instance.IncreaseActivateTimes(); - //Left = GetWindowsLeft(); - //Top = GetWindowsTop(); - - //Show(); - //Activate(); - //Focus(); - //tbQuery.Focus(); - //ResetQueryHistoryIndex(); - //if (selectAll) tbQuery.SelectAll(); - } - private void MainWindow_OnDeactivated(object sender, EventArgs e) { if (UserSettingStorage.Instance.HideWhenDeactive) @@ -196,114 +162,49 @@ namespace Wox } } - //private void TbQuery_OnPreviewKeyDown(object sender, KeyEventArgs e) - //{ - // //when alt is pressed, the real key should be e.SystemKey - // Key key = (e.Key == Key.System ? e.SystemKey : e.Key); - // switch (key) - // { - // case Key.Escape: - // if (IsInContextMenuMode) - // { - // BackToResultMode(); - // } - // else - // { - // HideWox(); - // } - // e.Handled = true; - // break; + private void Window_PreviewKeyDown(object sender, KeyEventArgs e) + { + //The code here is to supress the conflict of Window.InputBinding and ListBox native Down/Up Key handle + var vm = this.DataContext as MainViewModel; + //when alt is pressed, the real key should be e.SystemKey + Key key = (e.Key == Key.System ? e.SystemKey : e.Key); + switch (key) + { + case Key.Down: + if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) + { + vm.DisplayNextQueryCommand.Execute(null); + } + else + { + vm.SelectNextItemCommand.Execute(null); + } + e.Handled = true; + break; - // case Key.Tab: - // if (GlobalHotkey.Instance.CheckModifiers().ShiftPressed) - // { - // SelectPrevItem(); - // } - // else - // { - // SelectNextItem(); - // } - // e.Handled = true; - // break; - - // case Key.N: - // case Key.J: - // if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) - // { - // SelectNextItem(); - // } - // break; - - // case Key.P: - // case Key.K: - // if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) - // { - // SelectPrevItem(); - // } - // break; - - // case Key.O: - // if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) - // { - // if (IsInContextMenuMode) - // { - // BackToResultMode(); - // } - // else - // { - // ShowContextMenu(GetActiveResult()); - // } - // } - // break; - - // case Key.Down: - // if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) - // { - // DisplayNextQuery(); - // } - // else - // { - // SelectNextItem(); - // } - // e.Handled = true; - // break; - - // case Key.Up: - // if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) - // { - // DisplayPrevQuery(); - // } - // else - // { - // SelectPrevItem(); - // } - // e.Handled = true; - // break; - - // case Key.D: - // if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) - // { - // pnlResult.SelectNextPage(); - // } - // break; - - // case Key.PageDown: - // pnlResult.SelectNextPage(); - // e.Handled = true; - // break; - - // case Key.U: - // if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) - // { - // pnlResult.SelectPrevPage(); - // } - // break; - - // case Key.PageUp: - // pnlResult.SelectPrevPage(); - // e.Handled = true; - // break; + case Key.Up: + if (GlobalHotkey.Instance.CheckModifiers().CtrlPressed) + { + vm.DisplayPrevQueryCommand.Execute(null); + } + else + { + vm.SelectPrevItemCommand.Execute(null); + } + e.Handled = true; + break; + case Key.PageDown: + vm.SelectNextPageCommand.Execute(null); + e.Handled = true; + break; + case Key.PageUp: + vm.SelectPrevPageCommand.Execute(null); + e.Handled = true; + break; + } + } + //TODO: Colin - Figure out how to raise BackKeyDownEvent? // case Key.Back: // if (BackKeyDownEvent != null) // { @@ -315,144 +216,6 @@ namespace Wox // } // break; - // case Key.F1: - // Process.Start("http://doc.getwox.com"); - // break; - - // case Key.Enter: - // Result activeResult = GetActiveResult(); - // if (GlobalHotkey.Instance.CheckModifiers().ShiftPressed) - // { - // ShowContextMenu(activeResult); - // } - // else - // { - // SelectResult(activeResult); - // } - // e.Handled = true; - // break; - - // case Key.D1: - // SelectItem(1); - // break; - - // case Key.D2: - // SelectItem(2); - // break; - - // case Key.D3: - // SelectItem(3); - // break; - - // case Key.D4: - // SelectItem(4); - // break; - - // case Key.D5: - // SelectItem(5); - // break; - // case Key.D6: - // SelectItem(6); - // break; - - // } - //} - - //private void DisplayPrevQuery() - //{ - // var prev = QueryHistoryStorage.Instance.Previous(); - // DisplayQueryHistory(prev); - //} - - //private void DisplayNextQuery() - //{ - // var nextQuery = QueryHistoryStorage.Instance.Next(); - // DisplayQueryHistory(nextQuery); - //} - - //private void DisplayQueryHistory(HistoryItem history) - //{ - // if (history != null) - // { - // var historyMetadata = QueryHistoryStorage.MetaData; - // ChangeQueryText(history.Query, true); - // var executeQueryHistoryTitle = GetTranslation("executeQuery"); - // var lastExecuteTime = GetTranslation("lastExecuteTime"); - // pnlResult.RemoveResultsExcept(historyMetadata); - // UpdateResultViewInternal(new List - // { - // new Result - // { - // Title = string.Format(executeQueryHistoryTitle,history.Query), - // SubTitle = string.Format(lastExecuteTime,history.ExecutedDateTime), - // IcoPath = "Images\\history.png", - // PluginDirectory = WoxDirectroy.Executable, - // Action = _ =>{ - // ChangeQuery(history.Query,true); - // return false; - // } - // } - // }, historyMetadata); - // } - //} - - //private void SelectItem(int index) - //{ - // int zeroBasedIndex = index - 1; - // SpecialKeyState keyState = GlobalHotkey.Instance.CheckModifiers(); - // if (keyState.AltPressed || keyState.CtrlPressed) - // { - // List visibleResults = pnlResult.GetVisibleResults(); - // if (zeroBasedIndex < visibleResults.Count) - // { - // SelectResult(visibleResults[zeroBasedIndex]); - // } - // } - //} - - //private bool IsInContextMenuMode - //{ - // get { return pnlContextMenu.Visibility == Visibility.Visible; } - //} - - //private Result GetActiveResult() - //{ - // if (IsInContextMenuMode) - // { - // return pnlContextMenu.GetActiveResult(); - // } - // else - // { - // return pnlResult.GetActiveResult(); - // } - //} - - //private void SelectPrevItem() - //{ - // if (IsInContextMenuMode) - // { - // pnlContextMenu.SelectPrev(); - // } - // else - // { - // pnlResult.SelectPrev(); - // } - // toolTip.IsOpen = false; - //} - - //private void SelectNextItem() - //{ - // if (IsInContextMenuMode) - // { - // pnlContextMenu.SelectNext(); - // } - // else - // { - // pnlResult.SelectNext(); - // } - // toolTip.IsOpen = false; - //} - private void MainWindow_OnDrop(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.FileDrop)) @@ -468,6 +231,7 @@ namespace Wox MessageBox.Show(InternationalizationManager.Instance.GetTranslation("invalidWoxPluginFileFormat")); } } + e.Handled = false; } private void TbQuery_OnPreviewDragOver(object sender, DragEventArgs e) diff --git a/Wox/PublicAPIInstance.cs b/Wox/PublicAPIInstance.cs index da5d23ee7f..f3170050e9 100644 --- a/Wox/PublicAPIInstance.cs +++ b/Wox/PublicAPIInstance.cs @@ -56,6 +56,7 @@ namespace Wox { this.MainVM.QueryText = query; + //TODO: Colin - Adjust CaretIndext //Application.Current.Dispatcher.Invoke(() => //{ @@ -71,6 +72,7 @@ namespace Wox { this.MainVM.QueryText = query; + //TODO: Colin - Select all text //Application.Current.Dispatcher.Invoke(() => //{ @@ -216,7 +218,7 @@ namespace Wox UserSettingStorage.Instance.IncreaseActivateTimes(); this.MainVM.IsVisible = true; - //TODO:Adjust window properties + //TODO: Colin - Adjust window properties //Left = GetWindowsLeft(); //Top = GetWindowsTop(); diff --git a/Wox/ResultPanel.xaml b/Wox/ResultPanel.xaml index 21c1e890f2..eede9f3b87 100644 --- a/Wox/ResultPanel.xaml +++ b/Wox/ResultPanel.xaml @@ -9,7 +9,7 @@ diff --git a/Wox/ResultPanel.xaml.cs b/Wox/ResultPanel.xaml.cs index a797bda857..d20d0390c2 100644 --- a/Wox/ResultPanel.xaml.cs +++ b/Wox/ResultPanel.xaml.cs @@ -18,7 +18,6 @@ namespace Wox public partial class ResultPanel : UserControl { public event Action ItemDropEvent; - private readonly object _resultsUpdateLock = new object(); public void AddResults(List newResults, string resultId) { @@ -77,54 +76,6 @@ namespace Wox } - public void SelectNext() - { - int index = lbResults.SelectedIndex; - if (index == lbResults.Items.Count - 1) - { - index = -1; - } - Select(index + 1); - } - - public void SelectPrev() - { - int index = lbResults.SelectedIndex; - if (index == 0) - { - index = lbResults.Items.Count; - } - Select(index - 1); - } - - private void SelectFirst() - { - Select(0); - } - - private void Select(int index) - { - if (index >= 0 && index < lbResults.Items.Count) - { - lbResults.SelectedItem = lbResults.Items.GetItemAt(index); - } - } - - public List GetVisibleResults() - { - List visibleElements = new List(); - VirtualizingStackPanel virtualizingStackPanel = GetInnerStackPanel(lbResults); - for (int i = (int)virtualizingStackPanel.VerticalOffset; i <= virtualizingStackPanel.VerticalOffset + virtualizingStackPanel.ViewportHeight; i++) - { - ListBoxItem item = lbResults.ItemContainerGenerator.ContainerFromIndex(i) as ListBoxItem; - if (item != null) - { - visibleElements.Add(item.DataContext as Result); - } - } - return visibleElements; - } - private void UpdateItemNumber() { //VirtualizingStackPanel virtualizingStackPanel = GetInnerStackPanel(lbResults); @@ -146,51 +97,6 @@ namespace Wox //} } - private childItem FindVisualChild(DependencyObject obj) where childItem : DependencyObject - { - for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) - { - DependencyObject child = VisualTreeHelper.GetChild(obj, i); - if (child != null && child is childItem) - return (childItem)child; - else - { - childItem childOfChild = FindVisualChild(child); - if (childOfChild != null) - return childOfChild; - } - } - return null; - } - - private VirtualizingStackPanel GetInnerStackPanel(FrameworkElement element) - { - for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++) - { - var child = VisualTreeHelper.GetChild(element, i) as FrameworkElement; - - if (child == null) continue; - - if (child is VirtualizingStackPanel) return child as VirtualizingStackPanel; - - var panel = GetInnerStackPanel(child); - - if (panel != null) - return panel; - } - - return null; - - } - - public Result GetActiveResult() - { - int index = lbResults.SelectedIndex; - if (index < 0) return null; - - return lbResults.Items[index] as Result; - } - public ResultPanel() { InitializeComponent(); @@ -208,28 +114,6 @@ namespace Wox } } - public void SelectNextPage() - { - int index = lbResults.SelectedIndex; - index += 5; - if (index >= lbResults.Items.Count) - { - index = lbResults.Items.Count - 1; - } - Select(index); - } - - public void SelectPrevPage() - { - int index = lbResults.SelectedIndex; - index -= 5; - if (index < 0) - { - index = 0; - } - Select(index); - } - private void ListBoxItem_OnDrop(object sender, DragEventArgs e) { var item = ItemsControl.ContainerFromElement(lbResults, e.OriginalSource as DependencyObject) as ListBoxItem; diff --git a/Wox/ViewModel/BaseViewModel.cs b/Wox/ViewModel/BaseViewModel.cs index c720dfcdf0..2635df6f4a 100644 --- a/Wox/ViewModel/BaseViewModel.cs +++ b/Wox/ViewModel/BaseViewModel.cs @@ -25,9 +25,9 @@ namespace Wox.ViewModel public class RelayCommand : ICommand { - private Action _action; + private Action _action; - public RelayCommand(Action action) + public RelayCommand(Action action) { this._action = action; } @@ -43,7 +43,7 @@ namespace Wox.ViewModel { if (null != this._action) { - this._action(); + this._action(parameter); } } } diff --git a/Wox/ViewModel/MainViewModel.cs b/Wox/ViewModel/MainViewModel.cs index b79ca98dc9..6cd9ad1307 100644 --- a/Wox/ViewModel/MainViewModel.cs +++ b/Wox/ViewModel/MainViewModel.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -185,13 +186,73 @@ namespace Wox.ViewModel set; } + public ICommand SelectNextItemCommand + { + get; + set; + } + + public ICommand SelectPrevItemCommand + { + get; + set; + } + + public ICommand CtrlOCommand + { + get; + set; + } + + public ICommand DisplayNextQueryCommand + { + get; + set; + } + + public ICommand DisplayPrevQueryCommand + { + get; + set; + } + + public ICommand SelectNextPageCommand + { + get; + set; + } + + public ICommand SelectPrevPageCommand + { + get; + set; + } + + public ICommand StartHelpCommand + { + get; + set; + } + + public ICommand ShiftEnterCommand + { + get; + set; + } + + public ICommand OpenResultCommand + { + get; + set; + } + #endregion #region Private Methods private void InitializeKeyCommands() { - this.EscCommand = new RelayCommand(() => { + this.EscCommand = new RelayCommand((parameter) => { if (this.IsActionPanelVisible) { @@ -203,6 +264,97 @@ namespace Wox.ViewModel } }); + + this.SelectNextItemCommand = new RelayCommand((parameter) => { + + if (this.IsActionPanelVisible) + { + this._actionPanel.SelectNextResult(); + } + else + { + this._searchResultPanel.SelectNextResult(); + } + + }); + + this.SelectPrevItemCommand = new RelayCommand((parameter) => { + + if (this.IsActionPanelVisible) + { + this._actionPanel.SelectPrevResult(); + } + else + { + this._searchResultPanel.SelectPrevResult(); + } + + }); + + this.CtrlOCommand = new RelayCommand((parameter) => { + + if (this.IsActionPanelVisible) + { + BackToSearchMode(); + } + else + { + ShowActionPanel(this._searchResultPanel.SelectedResult.RawResult); + } + }); + + this.DisplayNextQueryCommand = new RelayCommand((parameter) => { + + var nextQuery = QueryHistoryStorage.Instance.Next(); + DisplayQueryHistory(nextQuery); + + }); + + this.DisplayPrevQueryCommand = new RelayCommand((parameter) => { + + var prev = QueryHistoryStorage.Instance.Previous(); + DisplayQueryHistory(prev); + + }); + + this.SelectNextPageCommand = new RelayCommand((parameter) => { + + this._searchResultPanel.SelectNextPage(); + + }); + + this.SelectPrevPageCommand = new RelayCommand((parameter) => { + + this._searchResultPanel.SelectPrevPage(); + + }); + + this.StartHelpCommand = new RelayCommand((parameter) => { + Process.Start("http://doc.getwox.com"); + }); + + this.ShiftEnterCommand = new RelayCommand((parameter) => { + + if (!this.IsActionPanelVisible && null != this._searchResultPanel.SelectedResult) + { + this.ShowActionPanel(this._searchResultPanel.SelectedResult.RawResult); + } + + }); + + this.OpenResultCommand = new RelayCommand((parameter) => { + + if(null != parameter) + { + var index = int.Parse(parameter.ToString()); + this._searchResultPanel.SelectResult(index); + } + + if (null != this._searchResultPanel.SelectedResult) + { + this._searchResultPanel.SelectedResult.OpenResultCommand.Execute(null); + } + }); } private void InitializeResultPanel() @@ -406,7 +558,7 @@ namespace Wox.ViewModel private void UpdateResultViewInternal(List list, PluginMetadata metadata) { - Stopwatch.Normal($"UI update cost for {metadata.Name}", + Infrastructure.Stopwatch.Normal($"UI update cost for {metadata.Name}", () => { this._searchResultPanel.AddResults(list, metadata.ID); }); } @@ -417,6 +569,38 @@ namespace Wox.ViewModel this.IsSearchResultPanelVisible = true; } + private void DisplayQueryHistory(HistoryItem history) + { + if (history != null) + { + var historyMetadata = QueryHistoryStorage.MetaData; + + this.QueryText = history.Query; + //TODO: Need to select all text + + var executeQueryHistoryTitle = InternationalizationManager.Instance.GetTranslation("executeQuery"); + var lastExecuteTime = InternationalizationManager.Instance.GetTranslation("lastExecuteTime"); + this._searchResultPanel.RemoveResultsExcept(historyMetadata); + UpdateResultViewInternal(new List + { + new Result + { + Title = string.Format(executeQueryHistoryTitle,history.Query), + SubTitle = string.Format(lastExecuteTime,history.ExecutedDateTime), + IcoPath = "Images\\history.png", + PluginDirectory = WoxDirectroy.Executable, + Action = _ =>{ + + this.QueryText = history.Query; + //TODO: Need to select all text + + return false; + } + } + }, historyMetadata); + } + } + #endregion #region Public Methods diff --git a/Wox/ViewModel/ResultItemViewModel.cs b/Wox/ViewModel/ResultItemViewModel.cs index 3af615d387..55b0306130 100644 --- a/Wox/ViewModel/ResultItemViewModel.cs +++ b/Wox/ViewModel/ResultItemViewModel.cs @@ -25,7 +25,7 @@ namespace Wox.ViewModel { this._result = result; - this.OpenResultCommand = new RelayCommand(() => { + this.OpenResultCommand = new RelayCommand((parameter) => { bool hideWindow = result.Action(new ActionContext { @@ -38,7 +38,7 @@ namespace Wox.ViewModel } }); - this.OpenResultActionPanelCommand = new RelayCommand(()=> { + this.OpenResultActionPanelCommand = new RelayCommand((parameter) => { if(null!= ResultActionPanelOpened) { diff --git a/Wox/ViewModel/ResultPanelViewModel.cs b/Wox/ViewModel/ResultPanelViewModel.cs index 052fd01378..08b6b29909 100644 --- a/Wox/ViewModel/ResultPanelViewModel.cs +++ b/Wox/ViewModel/ResultPanelViewModel.cs @@ -124,6 +124,70 @@ namespace Wox.ViewModel #region Public Methods + public void SelectResult(int index) + { + if(index <= this.Results.Count - 1) + { + this.SelectedResult = this.Results[index]; + } + } + + public void SelectNextResult() + { + if (null != this.SelectedResult) + { + var index = this.Results.IndexOf(this.SelectedResult); + if(index == this.Results.Count - 1) + { + index = -1; + } + this.SelectedResult = this.Results.ElementAt(index + 1); + } + } + + public void SelectPrevResult() + { + if (null != this.SelectedResult) + { + var index = this.Results.IndexOf(this.SelectedResult); + if (index == 0) + { + index = this.Results.Count; + } + this.SelectedResult = this.Results.ElementAt(index - 1); + } + } + + public void SelectNextPage() + { + var index = 0; + if (null != this.SelectedResult) + { + index = this.Results.IndexOf(this.SelectedResult); + } + index += 5; + if (index > this.Results.Count - 1) + { + index = this.Results.Count - 1; + } + this.SelectedResult = this.Results.ElementAt(index); + } + + public void SelectPrevPage() + { + var index = 0; + if (null != this.SelectedResult) + { + index = this.Results.IndexOf(this.SelectedResult); + } + index -= 5; + if (index < 0) + { + index = 0; + } + this.SelectedResult = this.Results.ElementAt(index); + } + public void Clear() { this._results.Clear(); From 2d4d7b80c1d525ee7ab3ced74683b381bc1db222 Mon Sep 17 00:00:00 2001 From: Colin Liu Date: Fri, 12 Feb 2016 15:22:01 +0800 Subject: [PATCH 06/18] Support for Sys Tray Icon --- Wox/App.xaml.cs | 3 +- Wox/Helper/SingletonWindowOpener.cs | 2 +- Wox/MainWindow.xaml.cs | 24 +----------- Wox/NotifyIconManager.cs | 40 ++++++++++++++++++++ Wox/SettingWindow.xaml.cs | 57 +++++++++++++++++++++-------- Wox/Wox.csproj | 1 + 6 files changed, 86 insertions(+), 41 deletions(-) create mode 100644 Wox/NotifyIconManager.cs diff --git a/Wox/App.xaml.cs b/Wox/App.xaml.cs index 2b9ed56c32..b5847ac164 100644 --- a/Wox/App.xaml.cs +++ b/Wox/App.xaml.cs @@ -45,12 +45,13 @@ namespace Wox RegisterUnhandledException(); ThreadPool.QueueUserWorkItem(o => { ImageLoader.ImageLoader.PreloadImages(); }); - MainViewModel mainVM = new MainViewModel(); API = new PublicAPIInstance(mainVM); Window = new MainWindow(); Window.DataContext = mainVM; + NotifyIconManager notifyIconManager = new NotifyIconManager(API); + PluginManager.Init(API); CommandArgsFactory.Execute(e.Args.ToList()); }); diff --git a/Wox/Helper/SingletonWindowOpener.cs b/Wox/Helper/SingletonWindowOpener.cs index 2b49503981..f7786ea04b 100644 --- a/Wox/Helper/SingletonWindowOpener.cs +++ b/Wox/Helper/SingletonWindowOpener.cs @@ -10,7 +10,7 @@ namespace Wox.Helper { var window = Application.Current.Windows.OfType().FirstOrDefault(x => x.GetType() == typeof(T)) ?? (T)Activator.CreateInstance(typeof(T), args); - Application.Current.MainWindow.Hide(); + App.API.HideApp(); window.Show(); window.Focus(); diff --git a/Wox/MainWindow.xaml.cs b/Wox/MainWindow.xaml.cs index b74752d32f..63bc3014f6 100644 --- a/Wox/MainWindow.xaml.cs +++ b/Wox/MainWindow.xaml.cs @@ -24,20 +24,14 @@ namespace Wox #region Properties - private readonly Storyboard progressBarStoryboard = new Storyboard(); - private NotifyIcon notifyIcon; - - + private readonly Storyboard progressBarStoryboard = new Storyboard(); #endregion - public MainWindow() { InitializeComponent(); - InitialTray(); - //pnlResult.ItemDropEvent += pnlResult_ItemDropEvent; Closing += MainWindow_Closing; } @@ -132,22 +126,6 @@ namespace Wox progressBar.BeginStoryboard(progressBarStoryboard); } - private void InitialTray() - { - //notifyIcon = new NotifyIcon { Text = "Wox", Icon = Properties.Resources.app, Visible = true }; - //notifyIcon.Click += (o, e) => ShowWox(); - //var open = new MenuItem(InternationalizationManager.Instance.GetTranslation("iconTrayOpen")); - //open.Click += (o, e) => ShowWox(); - //var setting = new MenuItem(InternationalizationManager.Instance.GetTranslation("iconTraySettings")); - //setting.Click += (o, e) => OpenSettingDialog(); - //var about = new MenuItem(InternationalizationManager.Instance.GetTranslation("iconTrayAbout")); - //about.Click += (o, e) => OpenSettingDialog("about"); - //var exit = new MenuItem(InternationalizationManager.Instance.GetTranslation("iconTrayExit")); - //exit.Click += (o, e) => CloseApp(); - //MenuItem[] childen = { open, setting, about, exit }; - //notifyIcon.ContextMenu = new ContextMenu(childen); - } - private void Border_OnMouseDown(object sender, MouseButtonEventArgs e) { if (e.ChangedButton == MouseButton.Left) DragMove(); diff --git a/Wox/NotifyIconManager.cs b/Wox/NotifyIconManager.cs new file mode 100644 index 0000000000..7b16fcbd16 --- /dev/null +++ b/Wox/NotifyIconManager.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using Wox.Core.Resource; +using Wox.Plugin; + +namespace Wox +{ + public class NotifyIconManager + { + + private NotifyIcon notifyIcon; + private IPublicAPI _api; + + public NotifyIconManager(IPublicAPI api) + { + this.InitialTray(); + this._api = api; + } + + private void InitialTray() + { + notifyIcon = new NotifyIcon { Text = "Wox", Icon = Properties.Resources.app, Visible = true }; + notifyIcon.Click += (o, e) => this._api.ShowApp(); + var open = new MenuItem(InternationalizationManager.Instance.GetTranslation("iconTrayOpen")); + open.Click += (o, e) => this._api.ShowApp(); + var setting = new MenuItem(InternationalizationManager.Instance.GetTranslation("iconTraySettings")); + setting.Click += (o, e) => this._api.OpenSettingDialog(); + var about = new MenuItem(InternationalizationManager.Instance.GetTranslation("iconTrayAbout")); + about.Click += (o, e) => this._api.OpenSettingDialog("about"); + var exit = new MenuItem(InternationalizationManager.Instance.GetTranslation("iconTrayExit")); + exit.Click += (o, e) => this._api.HideApp(); + MenuItem[] childen = { open, setting, about, exit }; + notifyIcon.ContextMenu = new ContextMenu(childen); + } + } +} diff --git a/Wox/SettingWindow.xaml.cs b/Wox/SettingWindow.xaml.cs index bd181ed0c6..173668ea13 100644 --- a/Wox/SettingWindow.xaml.cs +++ b/Wox/SettingWindow.xaml.cs @@ -19,19 +19,22 @@ using Wox.Helper; using Wox.Plugin; using Application = System.Windows.Forms.Application; using Stopwatch = Wox.Infrastructure.Stopwatch; +using Wox.Infrastructure.Hotkey; +using NHotkey.Wpf; +using NHotkey; namespace Wox { public partial class SettingWindow : Window { - public readonly MainWindow MainWindow; + public readonly IPublicAPI _api; bool settingsLoaded; private Dictionary featureControls = new Dictionary(); private bool themeTabLoaded; - public SettingWindow(MainWindow mainWindow) + public SettingWindow(IPublicAPI api) { - MainWindow = mainWindow; + this._api = api; InitializeComponent(); Loaded += Setting_Loaded; } @@ -250,23 +253,45 @@ namespace Wox { if (ctlHotkey.CurrentHotkeyAvailable) { - //MainWindow.SetHotkey(ctlHotkey.CurrentHotkey, delegate - //{ - // if (!MainWindow.IsVisible) - // { - // MainWindow.ShowApp(); - // } - // else - // { - // MainWindow.HideApp(); - // } - //}); - //MainWindow.RemoveHotkey(UserSettingStorage.Instance.Hotkey); + SetHotkey(ctlHotkey.CurrentHotkey, delegate + { + if (!App.Window.IsVisible) + { + this._api.ShowApp(); + } + else + { + this._api.HideApp(); + } + }); + RemoveHotkey(UserSettingStorage.Instance.Hotkey); UserSettingStorage.Instance.Hotkey = ctlHotkey.CurrentHotkey.ToString(); UserSettingStorage.Instance.Save(); } } + void SetHotkey(HotkeyModel hotkey, EventHandler action) + { + string hotkeyStr = hotkey.ToString(); + try + { + HotkeyManager.Current.AddOrReplace(hotkeyStr, hotkey.CharKey, hotkey.ModifierKeys, action); + } + catch (Exception) + { + string errorMsg = string.Format(InternationalizationManager.Instance.GetTranslation("registerHotkeyFailed"), hotkeyStr); + MessageBox.Show(errorMsg); + } + } + + void RemoveHotkey(string hotkeyStr) + { + if (!string.IsNullOrEmpty(hotkeyStr)) + { + HotkeyManager.Current.Remove(hotkeyStr); + } + } + private void OnHotkeyTabSelected() { ctlHotkey.HotkeyChanged += ctlHotkey_OnHotkeyChanged; @@ -289,7 +314,7 @@ namespace Wox UserSettingStorage.Instance.CustomPluginHotkeys.Remove(item); lvCustomHotkey.Items.Refresh(); UserSettingStorage.Instance.Save(); - //MainWindow.RemoveHotkey(item.Hotkey); + RemoveHotkey(item.Hotkey); } } diff --git a/Wox/Wox.csproj b/Wox/Wox.csproj index e0e17c5cf2..c9256918c9 100644 --- a/Wox/Wox.csproj +++ b/Wox/Wox.csproj @@ -126,6 +126,7 @@ + From ca3bedc9a1d5b37dc22a61937e56f2bccdc854df Mon Sep 17 00:00:00 2001 From: Colin Liu Date: Fri, 12 Feb 2016 17:20:46 +0800 Subject: [PATCH 07/18] Fix dependency bugs --- Wox/CommandArgs/ToggleCommandArg.cs | 10 +- Wox/MainWindow.xaml | 2 + Wox/MainWindow.xaml.cs | 60 ++++++++---- Wox/NotifyIconManager.cs | 2 +- Wox/PublicAPIInstance.cs | 60 ++++-------- Wox/ResultPanel.xaml.cs | 55 +---------- Wox/SettingWindow.xaml.cs | 2 + Wox/ViewModel/MainViewModel.cs | 138 ++++++++++++++++++++++----- Wox/ViewModel/ResultItemViewModel.cs | 1 - 9 files changed, 191 insertions(+), 139 deletions(-) diff --git a/Wox/CommandArgs/ToggleCommandArg.cs b/Wox/CommandArgs/ToggleCommandArg.cs index 7c2cbefb12..e509316291 100644 --- a/Wox/CommandArgs/ToggleCommandArg.cs +++ b/Wox/CommandArgs/ToggleCommandArg.cs @@ -11,8 +11,14 @@ namespace Wox.CommandArgs public void Execute(IList args) { - //TODO: Add ToggleWox Method - //App.API.ToggleWox(); + if (App.Window.IsVisible) + { + App.API.HideApp(); + } + else + { + App.API.ShowApp(); + } } } } diff --git a/Wox/MainWindow.xaml b/Wox/MainWindow.xaml index 9299032397..554ff7499e 100644 --- a/Wox/MainWindow.xaml +++ b/Wox/MainWindow.xaml @@ -48,6 +48,8 @@ + + diff --git a/Wox/MainWindow.xaml.cs b/Wox/MainWindow.xaml.cs index 63bc3014f6..a15082f1b5 100644 --- a/Wox/MainWindow.xaml.cs +++ b/Wox/MainWindow.xaml.cs @@ -64,12 +64,46 @@ namespace Wox ThemeManager.Theme.ChangeTheme(UserSettingStorage.Instance.Theme); InternationalizationManager.Instance.ChangeLanguage(UserSettingStorage.Instance.Language); - Left = GetWindowsLeft(); - Top = GetWindowsTop(); - InitProgressbarAnimation(); WindowIntelopHelper.DisableControlBox(this); CheckUpdate(); + + var vm = this.DataContext as MainViewModel; + vm.PropertyChanged += (o, eve) => + { + if(eve.PropertyName == "SelectAllText") + { + if (vm.SelectAllText) + { + this.tbQuery.SelectAll(); + } + } + else if(eve.PropertyName == "CaretIndex") + { + this.tbQuery.CaretIndex = vm.CaretIndex; + } + else if(eve.PropertyName == "Left") + { + this.Left = vm.Left; + } + else if(eve.PropertyName == "Top") + { + this.Top = vm.Top; + } + else if(eve.PropertyName == "IsVisible") + { + if (vm.IsVisible) + { + this.tbQuery.Focus(); + } + } + }; + + vm.Left = GetWindowsLeft(); + vm.Top = GetWindowsTop(); + this.Activate(); + this.Focus(); + this.tbQuery.Focus(); } private double GetWindowsLeft() @@ -135,14 +169,13 @@ namespace Wox { if (UserSettingStorage.Instance.HideWhenDeactive) { - //TODO:Hide the window when deactivated - //HideWox(); + App.API.HideApp(); } } private void Window_PreviewKeyDown(object sender, KeyEventArgs e) { - //The code here is to supress the conflict of Window.InputBinding and ListBox native Down/Up Key handle + //The code here is to supress the conflict of Window.InputBinding and ListBox/TextBox native Key handle var vm = this.DataContext as MainViewModel; //when alt is pressed, the real key should be e.SystemKey Key key = (e.Key == Key.System ? e.SystemKey : e.Key); @@ -179,21 +212,12 @@ namespace Wox vm.SelectPrevPageCommand.Execute(null); e.Handled = true; break; + case Key.Back: + vm.BackCommand.Execute(e); + break; } } - //TODO: Colin - Figure out how to raise BackKeyDownEvent? - // case Key.Back: - // if (BackKeyDownEvent != null) - // { - // BackKeyDownEvent(new WoxKeyDownEventArgs - // { - // Query = tbQuery.Text, - // keyEventArgs = e - // }); - // } - // break; - private void MainWindow_OnDrop(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.FileDrop)) diff --git a/Wox/NotifyIconManager.cs b/Wox/NotifyIconManager.cs index 7b16fcbd16..28ab61ad86 100644 --- a/Wox/NotifyIconManager.cs +++ b/Wox/NotifyIconManager.cs @@ -32,7 +32,7 @@ namespace Wox var about = new MenuItem(InternationalizationManager.Instance.GetTranslation("iconTrayAbout")); about.Click += (o, e) => this._api.OpenSettingDialog("about"); var exit = new MenuItem(InternationalizationManager.Instance.GetTranslation("iconTrayExit")); - exit.Click += (o, e) => this._api.HideApp(); + exit.Click += (o, e) => this._api.CloseApp(); MenuItem[] childen = { open, setting, about, exit }; notifyIcon.ContextMenu = new ContextMenu(childen); } diff --git a/Wox/PublicAPIInstance.cs b/Wox/PublicAPIInstance.cs index f3170050e9..7cb7f296f4 100644 --- a/Wox/PublicAPIInstance.cs +++ b/Wox/PublicAPIInstance.cs @@ -9,6 +9,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; +using System.Windows.Input; using System.Windows.Threading; using Wox.Core.Plugin; using Wox.Core.Resource; @@ -36,6 +37,22 @@ namespace Wox SetHotkey(UserSettingStorage.Instance.Hotkey, OnHotkey); SetCustomPluginHotkey(); + + this.MainVM.ListeningKeyPressed += (o, e) => { + + if(e.KeyEventArgs.Key == Key.Back) + { + if (null != this.BackKeyDownEvent) + { + BackKeyDownEvent(new WoxKeyDownEventArgs + { + Query = this.MainVM.QueryText, + keyEventArgs = e.KeyEventArgs + }); + } + } + + }; } #endregion @@ -55,33 +72,14 @@ namespace Wox public void ChangeQuery(string query, bool requery = false) { this.MainVM.QueryText = query; + this.MainVM.CaretIndex = this.MainVM.QueryText.Length; - //TODO: Colin - Adjust CaretIndext - //Application.Current.Dispatcher.Invoke(() => - //{ - - // tbQuery.CaretIndex = this.MainVM.QueryText.Length; - // if (requery) - // { - // TbQuery_OnTextChanged(null, null); - // } - //}); } public void ChangeQueryText(string query, bool selectAll = false) { this.MainVM.QueryText = query; - - //TODO: Colin - Select all text - //Application.Current.Dispatcher.Invoke(() => - //{ - - // tbQuery.CaretIndex = tbQuery.Text.Length; - // if (selectAll) - // { - // tbQuery.SelectAll(); - // } - //}); + this.MainVM.SelectAllText = true; } public void CloseApp() @@ -183,13 +181,7 @@ namespace Wox o.PluginID = plugin.ID; }); - this.MainVM.ActionPanel.Clear(); - - //TODO:Show Action Panel accordingly - //pnlContextMenu.Clear(); - //pnlContextMenu.AddResults(results, plugin.ID); - //pnlContextMenu.Visibility = Visibility.Visible; - //pnlResult.Visibility = Visibility.Collapsed; + this.MainVM.ShowActionPanel(results, plugin.ID); } } @@ -217,17 +209,7 @@ namespace Wox { UserSettingStorage.Instance.IncreaseActivateTimes(); this.MainVM.IsVisible = true; - - //TODO: Colin - Adjust window properties - //Left = GetWindowsLeft(); - //Top = GetWindowsTop(); - - //Show(); - //Activate(); - //Focus(); - //tbQuery.Focus(); - //ResetQueryHistoryIndex(); - //if (selectAll) tbQuery.SelectAll(); + this.MainVM.SelectAllText = true; } public void SetHotkey(string hotkeyStr, EventHandler action) diff --git a/Wox/ResultPanel.xaml.cs b/Wox/ResultPanel.xaml.cs index d20d0390c2..d6640467f5 100644 --- a/Wox/ResultPanel.xaml.cs +++ b/Wox/ResultPanel.xaml.cs @@ -17,62 +17,13 @@ namespace Wox [Synchronization] public partial class ResultPanel : UserControl { + //TODO: Refactor this event public event Action ItemDropEvent; public void AddResults(List newResults, string resultId) { - //lock (_resultsUpdateLock) - //{ - // // todo use async to do new result calculation - // var resultsCopy = _results.ToList(); - // var oldResults = resultsCopy.Where(r => r.PluginID == resultId).ToList(); - // // intersection of A (old results) and B (new newResults) - // var intersection = oldResults.Intersect(newResults).ToList(); - // // remove result of relative complement of B in A - // foreach (var result in oldResults.Except(intersection)) - // { - // resultsCopy.Remove(result); - // } - - // // update scores - // foreach (var result in newResults) - // { - // if (IsTopMostResult(result)) - // { - // result.Score = int.MaxValue; - // } - // } - - // // update index for result in intersection of A and B - // foreach (var commonResult in intersection) - // { - // int oldIndex = resultsCopy.IndexOf(commonResult); - // int oldScore = resultsCopy[oldIndex].Score; - // int newScore = newResults[newResults.IndexOf(commonResult)].Score; - // if (newScore != oldScore) - // { - // var oldResult = resultsCopy[oldIndex]; - // oldResult.Score = newScore; - // resultsCopy.RemoveAt(oldIndex); - // int newIndex = InsertIndexOf(newScore, resultsCopy); - // resultsCopy.Insert(newIndex, oldResult); - - // } - // } - - // // insert result in relative complement of A in B - // foreach (var result in newResults.Except(intersection)) - // { - // int newIndex = InsertIndexOf(result.Score, resultsCopy); - // resultsCopy.Insert(newIndex, result); - // } - - // // update UI in one run, so it can avoid UI flickering - // _results.Update(resultsCopy); - - // lbResults.Margin = lbResults.Items.Count > 0 ? new Thickness { Top = 8 } : new Thickness { Top = 0 }; - // SelectFirst(); - //} + var vm = this.DataContext as ResultPanelViewModel; + vm.AddResults(newResults, resultId); } diff --git a/Wox/SettingWindow.xaml.cs b/Wox/SettingWindow.xaml.cs index 173668ea13..f47bbbde3b 100644 --- a/Wox/SettingWindow.xaml.cs +++ b/Wox/SettingWindow.xaml.cs @@ -22,6 +22,7 @@ using Stopwatch = Wox.Infrastructure.Stopwatch; using Wox.Infrastructure.Hotkey; using NHotkey.Wpf; using NHotkey; +using Wox.ViewModel; namespace Wox { @@ -36,6 +37,7 @@ namespace Wox { this._api = api; InitializeComponent(); + this.resultPanelPreview.DataContext = new ResultPanelViewModel(); Loaded += Setting_Loaded; } diff --git a/Wox/ViewModel/MainViewModel.cs b/Wox/ViewModel/MainViewModel.cs index 6cd9ad1307..25f085b010 100644 --- a/Wox/ViewModel/MainViewModel.cs +++ b/Wox/ViewModel/MainViewModel.cs @@ -5,9 +5,11 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; +using System.Windows.Forms; using System.Windows.Input; using Wox.Core.Plugin; using Wox.Core.Resource; +using Wox.Core.UserSettings; using Wox.Infrastructure; using Wox.Infrastructure.Hotkey; using Wox.Plugin; @@ -27,6 +29,8 @@ namespace Wox.ViewModel private bool _isActionPanelVisible; private bool _isProgressBarVisible; private bool _isProgressBarTooltipVisible; + private bool _selectAllText; + private int _caretIndex; private double _left; private double _top; @@ -84,6 +88,32 @@ namespace Wox.ViewModel } } + public bool SelectAllText + { + get + { + return this._selectAllText; + } + set + { + this._selectAllText = value; + OnPropertyChanged("SelectAllText"); + } + } + + public int CaretIndex + { + get + { + return this._caretIndex; + } + set + { + this._caretIndex = value; + OnPropertyChanged("CaretIndex"); + } + } + public bool IsVisible { get @@ -246,13 +276,20 @@ namespace Wox.ViewModel set; } + public ICommand BackCommand + { + get; + set; + } + #endregion #region Private Methods private void InitializeKeyCommands() { - this.EscCommand = new RelayCommand((parameter) => { + this.EscCommand = new RelayCommand((parameter) => + { if (this.IsActionPanelVisible) { @@ -265,7 +302,8 @@ namespace Wox.ViewModel }); - this.SelectNextItemCommand = new RelayCommand((parameter) => { + this.SelectNextItemCommand = new RelayCommand((parameter) => + { if (this.IsActionPanelVisible) { @@ -278,7 +316,8 @@ namespace Wox.ViewModel }); - this.SelectPrevItemCommand = new RelayCommand((parameter) => { + this.SelectPrevItemCommand = new RelayCommand((parameter) => + { if (this.IsActionPanelVisible) { @@ -291,7 +330,8 @@ namespace Wox.ViewModel }); - this.CtrlOCommand = new RelayCommand((parameter) => { + this.CtrlOCommand = new RelayCommand((parameter) => + { if (this.IsActionPanelVisible) { @@ -303,37 +343,43 @@ namespace Wox.ViewModel } }); - this.DisplayNextQueryCommand = new RelayCommand((parameter) => { + this.DisplayNextQueryCommand = new RelayCommand((parameter) => + { var nextQuery = QueryHistoryStorage.Instance.Next(); DisplayQueryHistory(nextQuery); }); - this.DisplayPrevQueryCommand = new RelayCommand((parameter) => { + this.DisplayPrevQueryCommand = new RelayCommand((parameter) => + { var prev = QueryHistoryStorage.Instance.Previous(); DisplayQueryHistory(prev); }); - this.SelectNextPageCommand = new RelayCommand((parameter) => { + this.SelectNextPageCommand = new RelayCommand((parameter) => + { this._searchResultPanel.SelectNextPage(); }); - this.SelectPrevPageCommand = new RelayCommand((parameter) => { + this.SelectPrevPageCommand = new RelayCommand((parameter) => + { this._searchResultPanel.SelectPrevPage(); }); - this.StartHelpCommand = new RelayCommand((parameter) => { + this.StartHelpCommand = new RelayCommand((parameter) => + { Process.Start("http://doc.getwox.com"); }); - this.ShiftEnterCommand = new RelayCommand((parameter) => { + this.ShiftEnterCommand = new RelayCommand((parameter) => + { if (!this.IsActionPanelVisible && null != this._searchResultPanel.SelectedResult) { @@ -342,9 +388,10 @@ namespace Wox.ViewModel }); - this.OpenResultCommand = new RelayCommand((parameter) => { + this.OpenResultCommand = new RelayCommand((parameter) => + { - if(null != parameter) + if (null != parameter) { var index = int.Parse(parameter.ToString()); this._searchResultPanel.SelectResult(index); @@ -355,6 +402,15 @@ namespace Wox.ViewModel this._searchResultPanel.SelectedResult.OpenResultCommand.Execute(null); } }); + + this.BackCommand = new RelayCommand((parameter) => + { + if (null != ListeningKeyPressed) + { + this.ListeningKeyPressed(this, new ListeningKeyPressedEventArgs(parameter as System.Windows.Input.KeyEventArgs)); + } + + }); } private void InitializeResultPanel() @@ -380,27 +436,35 @@ namespace Wox.ViewModel private void ShowActionPanel(Result result) { if (result == null) return; - List results = PluginManager.GetContextMenusForPlugin(result); - results.ForEach(o => + this.ShowActionPanel(result, PluginManager.GetContextMenusForPlugin(result)); + } + + private void ShowActionPanel(Result result, List actions) + { + actions.ForEach(o => { o.PluginDirectory = PluginManager.GetPluginForId(result.PluginID).Metadata.PluginDirectory; o.PluginID = result.PluginID; o.OriginQuery = result.OriginQuery; }); - results.Add(GetTopMostContextMenu(result)); + actions.Add(GetTopMostContextMenu(result)); + this.DisplayActionPanel(actions, result.PluginID); + } + + private void DisplayActionPanel(List actions, string pluginID) + { _textBeforeEnterContextMenuMode = this.QueryText; this._actionPanel.Clear(); - this._actionPanel.AddResults(results, result.PluginID); - CurrentContextMenus = results; + this._actionPanel.AddResults(actions, pluginID); + CurrentContextMenus = actions; this.IsActionPanelVisible = true; this.IsSearchResultPanelVisible = false; this.QueryText = ""; - } private Result GetTopMostContextMenu(Result result) @@ -413,8 +477,7 @@ namespace Wox.ViewModel Action = _ => { TopMostRecordStorage.Instance.Remove(result); - //TODO:Modify the way showing this message - //ShowMsg("Succeed", "", ""); + App.API.ShowMsg("Succeed", "", ""); return false; } }; @@ -427,8 +490,7 @@ namespace Wox.ViewModel Action = _ => { TopMostRecordStorage.Instance.AddOrUpdate(result); - //TODO:Modify the way showing this message - //ShowMsg("Succeed", "", ""); + App.API.ShowMsg("Succeed", "", ""); return false; } }; @@ -576,7 +638,7 @@ namespace Wox.ViewModel var historyMetadata = QueryHistoryStorage.MetaData; this.QueryText = history.Query; - //TODO: Need to select all text + this.SelectAllText = true; var executeQueryHistoryTitle = InternationalizationManager.Instance.GetTranslation("executeQuery"); var lastExecuteTime = InternationalizationManager.Instance.GetTranslation("lastExecuteTime"); @@ -592,7 +654,7 @@ namespace Wox.ViewModel Action = _ =>{ this.QueryText = history.Query; - //TODO: Need to select all text + this.SelectAllText = true; return false; } @@ -616,18 +678,42 @@ namespace Wox.ViewModel }); if (originQuery.RawQuery == _lastQuery.RawQuery) { - Application.Current.Dispatcher.Invoke(() => { + System.Windows.Application.Current.Dispatcher.Invoke(() => + { UpdateResultViewInternal(list, metadata); }); } - if(list.Count > 0) + if (list.Count > 0) { this.IsSearchResultPanelVisible = true; } } + public void ShowActionPanel(List actions, string pluginID) + { + this.DisplayActionPanel(actions, pluginID); + } + #endregion + public event EventHandler ListeningKeyPressed; + + } + + public class ListeningKeyPressedEventArgs : EventArgs + { + + public System.Windows.Input.KeyEventArgs KeyEventArgs + { + get; + private set; + } + + public ListeningKeyPressedEventArgs(System.Windows.Input.KeyEventArgs keyEventArgs) + { + this.KeyEventArgs = keyEventArgs; + } + } } diff --git a/Wox/ViewModel/ResultItemViewModel.cs b/Wox/ViewModel/ResultItemViewModel.cs index 55b0306130..32aac3c430 100644 --- a/Wox/ViewModel/ResultItemViewModel.cs +++ b/Wox/ViewModel/ResultItemViewModel.cs @@ -73,7 +73,6 @@ namespace Wox.ViewModel { get { - //TODO: Some of the properties in Result class may be moved to this class return this._result.FullIcoPath; } } From 2846eea9566bef65c7aba4ad00eab17c313c120d Mon Sep 17 00:00:00 2001 From: Colin Liu Date: Fri, 12 Feb 2016 17:29:02 +0800 Subject: [PATCH 08/18] Remove unused code --- Wox/ResultPanel.xaml.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Wox/ResultPanel.xaml.cs b/Wox/ResultPanel.xaml.cs index d6640467f5..677853ea7b 100644 --- a/Wox/ResultPanel.xaml.cs +++ b/Wox/ResultPanel.xaml.cs @@ -17,7 +17,6 @@ namespace Wox [Synchronization] public partial class ResultPanel : UserControl { - //TODO: Refactor this event public event Action ItemDropEvent; public void AddResults(List newResults, string resultId) From 3ce0b8bc977f77ce53297a4b2c25948951b9143f Mon Sep 17 00:00:00 2001 From: Colin Liu Date: Thu, 18 Feb 2016 20:29:51 +0800 Subject: [PATCH 09/18] Remove unused code - ListBoxItem_OnDrop Event Handler - ItemDropEvent Handler - UpdateItemNumber method --- Wox/MainWindow.xaml.cs | 16 ---------------- Wox/ResultPanel.xaml | 2 +- Wox/ResultPanel.xaml.cs | 41 ----------------------------------------- 3 files changed, 1 insertion(+), 58 deletions(-) diff --git a/Wox/MainWindow.xaml.cs b/Wox/MainWindow.xaml.cs index a15082f1b5..0fd9210bb8 100644 --- a/Wox/MainWindow.xaml.cs +++ b/Wox/MainWindow.xaml.cs @@ -32,25 +32,9 @@ namespace Wox { InitializeComponent(); - //pnlResult.ItemDropEvent += pnlResult_ItemDropEvent; Closing += MainWindow_Closing; } - //void pnlResult_ItemDropEvent(Result result, IDataObject dropDataObject, DragEventArgs args) - //{ - // PluginPair pluginPair = PluginManager.AllPlugins.FirstOrDefault(o => o.Metadata.ID == result.PluginID); - // if (ResultItemDropEvent != null && pluginPair != null) - // { - // foreach (var delegateHandler in ResultItemDropEvent.GetInvocationList()) - // { - // if (delegateHandler.Target == pluginPair.Plugin) - // { - // delegateHandler.DynamicInvoke(result, dropDataObject, args); - // } - // } - // } - //} - void MainWindow_Closing(object sender, CancelEventArgs e) { UserSettingStorage.Instance.WindowLeft = Left; diff --git a/Wox/ResultPanel.xaml b/Wox/ResultPanel.xaml index eede9f3b87..21c1e890f2 100644 --- a/Wox/ResultPanel.xaml +++ b/Wox/ResultPanel.xaml @@ -9,7 +9,7 @@ diff --git a/Wox/ResultPanel.xaml.cs b/Wox/ResultPanel.xaml.cs index 677853ea7b..05f3870f7b 100644 --- a/Wox/ResultPanel.xaml.cs +++ b/Wox/ResultPanel.xaml.cs @@ -17,8 +17,6 @@ namespace Wox [Synchronization] public partial class ResultPanel : UserControl { - public event Action ItemDropEvent; - public void AddResults(List newResults, string resultId) { var vm = this.DataContext as ResultPanelViewModel; @@ -26,27 +24,6 @@ namespace Wox } - private void UpdateItemNumber() - { - //VirtualizingStackPanel virtualizingStackPanel = GetInnerStackPanel(lbResults); - //int index = 0; - //for (int i = (int)virtualizingStackPanel.VerticalOffset; i <= virtualizingStackPanel.VerticalOffset + virtualizingStackPanel.ViewportHeight; i++) - //{ - // index++; - // ListBoxItem item = lbResults.ItemContainerGenerator.ContainerFromIndex(i) as ListBoxItem; - // if (item != null) - // { - // ContentPresenter myContentPresenter = FindVisualChild(item); - // if (myContentPresenter != null) - // { - // DataTemplate dataTemplate = myContentPresenter.ContentTemplate; - // TextBlock tbItemNumber = (TextBlock)dataTemplate.FindName("tbItemNumber", myContentPresenter); - // tbItemNumber.Text = index.ToString(); - // } - // } - //} - } - public ResultPanel() { InitializeComponent(); @@ -57,26 +34,8 @@ namespace Wox if (e.AddedItems.Count > 0 && e.AddedItems[0] != null) { lbResults.ScrollIntoView(e.AddedItems[0]); - //Dispatcher.DelayInvoke("UpdateItemNumber", () => - //{ - //UpdateItemNumber(); - //}, TimeSpan.FromMilliseconds(3)); } } - private void ListBoxItem_OnDrop(object sender, DragEventArgs e) - { - var item = ItemsControl.ContainerFromElement(lbResults, e.OriginalSource as DependencyObject) as ListBoxItem; - if (item != null) - { - OnItemDropEvent(item.DataContext as ResultItemViewModel, e.Data, e); - } - } - - protected virtual void OnItemDropEvent(ResultItemViewModel obj, IDataObject data, DragEventArgs e) - { - var handler = ItemDropEvent; - if (handler != null) handler(obj.RawResult, data, e); - } } } \ No newline at end of file From c3fd732243a78b0f25a445ae0b3028233f22044b Mon Sep 17 00:00:00 2001 From: Colin Liu Date: Thu, 18 Feb 2016 20:56:53 +0800 Subject: [PATCH 10/18] Refactor according to MVVM implementation --- Wox/CustomQueryHotkeySetting.xaml.cs | 55 +++++++++++++++++++++------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/Wox/CustomQueryHotkeySetting.xaml.cs b/Wox/CustomQueryHotkeySetting.xaml.cs index d7136398fd..8c29324509 100644 --- a/Wox/CustomQueryHotkeySetting.xaml.cs +++ b/Wox/CustomQueryHotkeySetting.xaml.cs @@ -1,8 +1,12 @@ -using System.Collections.Generic; +using NHotkey; +using NHotkey.Wpf; +using System; +using System.Collections.Generic; using System.Linq; using System.Windows; using Wox.Core.Resource; using Wox.Core.UserSettings; +using Wox.Infrastructure.Hotkey; namespace Wox { @@ -44,11 +48,12 @@ namespace Wox ActionKeyword = tbAction.Text }; UserSettingStorage.Instance.CustomPluginHotkeys.Add(pluginHotkey); - //settingWidow.MainWindow.SetHotkey(ctlHotkey.CurrentHotkey, delegate - //{ - // settingWidow.MainWindow.ChangeQuery(pluginHotkey.ActionKeyword); - // settingWidow.MainWindow.ShowApp(); - //}); + + SetHotkey(ctlHotkey.CurrentHotkey, delegate + { + App.API.ChangeQuery(pluginHotkey.ActionKeyword); + App.API.ShowApp(); + }); MessageBox.Show(InternationalizationManager.Instance.GetTranslation("succeed")); } else @@ -62,12 +67,12 @@ namespace Wox updateCustomHotkey.ActionKeyword = tbAction.Text; updateCustomHotkey.Hotkey = ctlHotkey.CurrentHotkey.ToString(); //remove origin hotkey - //settingWidow.MainWindow.RemoveHotkey(oldHotkey); - //settingWidow.MainWindow.SetHotkey(updateCustomHotkey.Hotkey, delegate - //{ - // settingWidow.MainWindow.ShowApp(); - // settingWidow.MainWindow.ChangeQuery(updateCustomHotkey.ActionKeyword); - //}); + RemoveHotkey(oldHotkey); + SetHotkey(new HotkeyModel(updateCustomHotkey.Hotkey), delegate + { + App.API.ShowApp(); + App.API.ChangeQuery(updateCustomHotkey.ActionKeyword); + }); MessageBox.Show(InternationalizationManager.Instance.GetTranslation("succeed")); } @@ -94,8 +99,30 @@ namespace Wox private void BtnTestActionKeyword_OnClick(object sender, RoutedEventArgs e) { - //settingWidow.MainWindow.ShowApp(); - //settingWidow.MainWindow.ChangeQuery(tbAction.Text); + App.API.ShowApp(); + App.API.ChangeQuery(tbAction.Text); + } + + private void RemoveHotkey(string hotkeyStr) + { + if (!string.IsNullOrEmpty(hotkeyStr)) + { + HotkeyManager.Current.Remove(hotkeyStr); + } + } + + private void SetHotkey(HotkeyModel hotkey, EventHandler action) + { + string hotkeyStr = hotkey.ToString(); + try + { + HotkeyManager.Current.AddOrReplace(hotkeyStr, hotkey.CharKey, hotkey.ModifierKeys, action); + } + catch (Exception) + { + string errorMsg = string.Format(InternationalizationManager.Instance.GetTranslation("registerHotkeyFailed"), hotkeyStr); + MessageBox.Show(errorMsg); + } } } } From 8de26af24607defe1a0f180a59c099750c9e36cf Mon Sep 17 00:00:00 2001 From: Colin Liu Date: Thu, 18 Feb 2016 22:53:41 +0800 Subject: [PATCH 11/18] Fix UI Flickering issue --- Wox/ViewModel/ResultItemViewModel.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Wox/ViewModel/ResultItemViewModel.cs b/Wox/ViewModel/ResultItemViewModel.cs index 32aac3c430..e7652f1752 100644 --- a/Wox/ViewModel/ResultItemViewModel.cs +++ b/Wox/ViewModel/ResultItemViewModel.cs @@ -103,6 +103,21 @@ namespace Wox.ViewModel #endregion + public override bool Equals(object obj) + { + ResultItemViewModel r = obj as ResultItemViewModel; + if (r != null) + { + var equality = string.Equals(r.Title, Title) && + string.Equals(r.SubTitle, SubTitle); + return equality; + } + else + { + return false; + } + } + public event EventHandler ResultOpened; public event EventHandler ResultActionPanelOpened; From 1b6a43ae15461af1e764ef709e7ef10bdf4264f8 Mon Sep 17 00:00:00 2001 From: Colin Liu Date: Fri, 19 Feb 2016 21:25:55 +0800 Subject: [PATCH 12/18] Add support for design time DataContext binding Add this support to suppress the warning from Resharper --- Wox/MainWindow.xaml | 5 +++-- Wox/ResultPanel.xaml | 6 +++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Wox/MainWindow.xaml b/Wox/MainWindow.xaml index 554ff7499e..f6405e5607 100644 --- a/Wox/MainWindow.xaml +++ b/Wox/MainWindow.xaml @@ -2,6 +2,8 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:wox="clr-namespace:Wox" xmlns:vm="clr-namespace:Wox.ViewModel" xmlns:converters="clr-namespace:Wox.Converters" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" Title="Wox" Topmost="True" Loaded="MainWindow_OnLoaded" @@ -17,8 +19,7 @@ Icon="Images\app.png" AllowsTransparency="True" Visibility="{Binding IsVisible,Converter={converters:VisibilityConverter}}" - PreviewKeyDown="Window_PreviewKeyDown" - > + PreviewKeyDown="Window_PreviewKeyDown" d:DataContext="{d:DesignInstance vm:MainViewModel}"> diff --git a/Wox/ResultPanel.xaml b/Wox/ResultPanel.xaml index 21c1e890f2..d4e088186e 100644 --- a/Wox/ResultPanel.xaml +++ b/Wox/ResultPanel.xaml @@ -5,7 +5,8 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:converters="clr-namespace:Wox.Converters" - mc:Ignorable="d" d:DesignWidth="100" d:DesignHeight="100"> + xmlns:vm ="clr-namespace:Wox.ViewModel" + mc:Ignorable="d" d:DesignWidth="100" d:DesignHeight="100" d:DataContext="{d:DesignInstance vm:ResultPanelViewModel}"> + + +