diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 28c631d11..1689fa59d 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -57,7 +57,7 @@ namespace Flow.Launcher DataContext = mainVM; _viewModel = mainVM; _settings = settings; - + InitializeComponent(); InitializePosition(); animationSound.Open(new Uri(AppDomain.CurrentDomain.BaseDirectory + "Resources\\open.wav")); @@ -67,7 +67,7 @@ namespace Flow.Launcher { InitializeComponent(); } - + private void OnCopy(object sender, ExecutedRoutedEventArgs e) { if (QueryTextBox.SelectionLength == 0) @@ -115,6 +115,8 @@ namespace Flow.Launcher switch (e.PropertyName) { case nameof(MainViewModel.MainWindowVisibilityStatus): + { + Dispatcher.Invoke(() => { if (_viewModel.MainWindowVisibilityStatus) { @@ -140,7 +142,7 @@ namespace Flow.Launcher isProgressBarStoryboardPaused = false; } - if(_settings.UseAnimation) + if (_settings.UseAnimation) WindowAnimator(); } else if (!isProgressBarStoryboardPaused) @@ -148,27 +150,27 @@ namespace Flow.Launcher _progressBarStoryboard.Stop(ProgressBar); isProgressBarStoryboardPaused = true; } - - break; - } + }); + break; + } case nameof(MainViewModel.ProgressBarVisibility): + { + Dispatcher.Invoke(() => { - Dispatcher.Invoke(() => + if (_viewModel.ProgressBarVisibility == Visibility.Hidden && !isProgressBarStoryboardPaused) { - if (_viewModel.ProgressBarVisibility == Visibility.Hidden && !isProgressBarStoryboardPaused) - { - _progressBarStoryboard.Stop(ProgressBar); - isProgressBarStoryboardPaused = true; - } - else if (_viewModel.MainWindowVisibilityStatus && - isProgressBarStoryboardPaused) - { - _progressBarStoryboard.Begin(ProgressBar, true); - isProgressBarStoryboardPaused = false; - } - }); - break; - } + _progressBarStoryboard.Stop(ProgressBar); + isProgressBarStoryboardPaused = true; + } + else if (_viewModel.MainWindowVisibilityStatus && + isProgressBarStoryboardPaused) + { + _progressBarStoryboard.Begin(ProgressBar, true); + isProgressBarStoryboardPaused = false; + } + }); + break; + } case nameof(MainViewModel.QueryTextCursorMovedToEnd): if (_viewModel.QueryTextCursorMovedToEnd) { @@ -251,35 +253,45 @@ namespace Flow.Launcher contextMenu = new ContextMenu(); - var openIcon = new FontIcon { Glyph = "\ue71e" }; + var openIcon = new FontIcon + { + Glyph = "\ue71e" + }; var open = new MenuItem { - Header = InternationalizationManager.Instance.GetTranslation("iconTrayOpen") + " (" + _settings.Hotkey + ")", - Icon = openIcon + Header = InternationalizationManager.Instance.GetTranslation("iconTrayOpen") + " (" + _settings.Hotkey + ")", Icon = openIcon + }; + var gamemodeIcon = new FontIcon + { + Glyph = "\ue7fc" }; - var gamemodeIcon = new FontIcon { Glyph = "\ue7fc" }; var gamemode = new MenuItem { - Header = InternationalizationManager.Instance.GetTranslation("GameMode"), - Icon = gamemodeIcon + Header = InternationalizationManager.Instance.GetTranslation("GameMode"), Icon = gamemodeIcon + }; + var positionresetIcon = new FontIcon + { + Glyph = "\ue73f" }; - var positionresetIcon = new FontIcon { Glyph = "\ue73f" }; var positionreset = new MenuItem { - Header = InternationalizationManager.Instance.GetTranslation("PositionReset"), - Icon = positionresetIcon + Header = InternationalizationManager.Instance.GetTranslation("PositionReset"), Icon = positionresetIcon + }; + var settingsIcon = new FontIcon + { + Glyph = "\ue713" }; - var settingsIcon = new FontIcon { Glyph = "\ue713" }; var settings = new MenuItem { - Header = InternationalizationManager.Instance.GetTranslation("iconTraySettings"), - Icon = settingsIcon + Header = InternationalizationManager.Instance.GetTranslation("iconTraySettings"), Icon = settingsIcon + }; + var exitIcon = new FontIcon + { + Glyph = "\ue7e8" }; - var exitIcon = new FontIcon { Glyph = "\ue7e8" }; var exit = new MenuItem { - Header = InternationalizationManager.Instance.GetTranslation("iconTrayExit"), - Icon = exitIcon + Header = InternationalizationManager.Instance.GetTranslation("iconTrayExit"), Icon = exitIcon }; open.Click += (o, e) => _viewModel.ToggleFlowLauncher(); @@ -342,15 +354,15 @@ namespace Flow.Launcher } private async void PositionReset() { - _viewModel.Show(); - await Task.Delay(300); // If don't give a time, Positioning will be weird. - Left = HorizonCenter(); - Top = VerticalCenter(); + _viewModel.Show(); + await Task.Delay(300); // If don't give a time, Positioning will be weird. + Left = HorizonCenter(); + Top = VerticalCenter(); } private void InitProgressbarAnimation() { var da = new DoubleAnimation(ProgressBar.X2, ActualWidth + 150, - new Duration(new TimeSpan(0, 0, 0, 0, 1600))); + new Duration(new TimeSpan(0, 0, 0, 0, 1600))); var da1 = new DoubleAnimation(ProgressBar.X1, ActualWidth + 50, new Duration(new TimeSpan(0, 0, 0, 0, 1600))); Storyboard.SetTargetProperty(da, new PropertyPath("(Line.X2)")); Storyboard.SetTargetProperty(da1, new PropertyPath("(Line.X1)")); @@ -392,11 +404,11 @@ namespace Flow.Launcher }; var IconMotion = new DoubleAnimation { - From = 12, - To = 0, - EasingFunction = easing, - Duration = TimeSpan.FromSeconds(0.36), - FillBehavior = FillBehavior.Stop + From = 12, + To = 0, + EasingFunction = easing, + Duration = TimeSpan.FromSeconds(0.36), + FillBehavior = FillBehavior.Stop }; var ClockOpacity = new DoubleAnimation @@ -468,10 +480,10 @@ namespace Flow.Launcher private async void OnContextMenusForSettingsClick(object sender, RoutedEventArgs e) { _viewModel.Hide(); - - if(_settings.UseAnimation) + + if (_settings.UseAnimation) await Task.Delay(100); - + App.API.OpenSettingDialog(); } @@ -489,7 +501,7 @@ namespace Flow.Launcher // and always after Settings window is closed. if (_settings.UseAnimation) await Task.Delay(100); - + if (_settings.HideWhenDeactive) { _viewModel.Hide(); @@ -527,7 +539,7 @@ namespace Flow.Launcher _viewModel.Show(); } } - + public double HorizonCenter() { var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position); @@ -609,9 +621,9 @@ namespace Flow.Launcher && QueryTextBox.Text.Length > 0 && QueryTextBox.CaretIndex == QueryTextBox.Text.Length) { - var queryWithoutActionKeyword = + var queryWithoutActionKeyword = QueryBuilder.Build(QueryTextBox.Text.Trim(), PluginManager.NonGlobalPlugins)?.Search; - + if (FilesFolders.IsLocationPathString(queryWithoutActionKeyword)) { _viewModel.BackspaceCommand.Execute(null); @@ -663,7 +675,7 @@ namespace Flow.Launcher { // QueryTextBox seems to be update with a DispatcherPriority as low as ContextIdle. // To ensure QueryTextBox is up to date with QueryText from the View, we need to Dispatch with such a priority - Dispatcher.Invoke(() => QueryTextBox.CaretIndex = QueryTextBox.Text.Length, System.Windows.Threading.DispatcherPriority.ContextIdle); + Dispatcher.Invoke(() => QueryTextBox.CaretIndex = QueryTextBox.Text.Length); } public void InitializeColorScheme() @@ -680,7 +692,7 @@ namespace Flow.Launcher private void QueryTextBox_KeyUp(object sender, KeyEventArgs e) { - if(_viewModel.QueryText != QueryTextBox.Text) + if (_viewModel.QueryText != QueryTextBox.Text) { BindingExpression be = QueryTextBox.GetBindingExpression(System.Windows.Controls.TextBox.TextProperty); be.UpdateSource(); diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index d1f8132da..e05e47041 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -24,6 +24,7 @@ using System.IO; using System.Collections.Specialized; using CommunityToolkit.Mvvm.Input; using System.Globalization; +using System.Windows.Threading; namespace Flow.Launcher.ViewModel { @@ -65,7 +66,8 @@ namespace Flow.Launcher.ViewModel Settings = settings; Settings.PropertyChanged += (_, args) => { - switch (args.PropertyName) { + switch (args.PropertyName) + { case nameof(Settings.WindowSize): OnPropertyChanged(nameof(MainWindowWidth)); break; @@ -365,6 +367,7 @@ namespace Flow.Launcher.ViewModel set { _queryText = value; + OnPropertyChanged(); Query(); } } @@ -426,19 +429,24 @@ namespace Flow.Launcher.ViewModel /// Force query even when Query Text doesn't change public void ChangeQueryText(string queryText, bool reQuery = false) { - if (QueryText != queryText) + Application.Current.Dispatcher.Invoke(() => { - // re-query is done in QueryText's setter method - QueryText = queryText; - // set to false so the subsequent set true triggers - // PropertyChanged and MoveQueryTextToEnd is called - QueryTextCursorMovedToEnd = false; - } - else if (reQuery) - { - Query(); - } - QueryTextCursorMovedToEnd = true; + if (QueryText != queryText) + { + + // re-query is done in QueryText's setter method + QueryText = queryText; + // set to false so the subsequent set true triggers + // PropertyChanged and MoveQueryTextToEnd is called + QueryTextCursorMovedToEnd = false; + + } + else if (reQuery) + { + Query(); + } + QueryTextCursorMovedToEnd = true; + }); } public bool LastQuerySelected { get; set; } @@ -752,11 +760,14 @@ namespace Flow.Launcher.ViewModel queryBuilder.Replace('@' + shortcut.Key, shortcut.Expand()); } - foreach (var shortcut in builtInShortcuts) + Application.Current.Dispatcher.Invoke(() => { - queryBuilder.Replace(shortcut.Key, shortcut.Expand()); - queryBuilderTmp.Replace(shortcut.Key, shortcut.Expand()); - } + foreach (var shortcut in builtInShortcuts) + { + queryBuilder.Replace(shortcut.Key, shortcut.Expand()); + queryBuilderTmp.Replace(shortcut.Key, shortcut.Expand()); + } + }); // show expanded builtin shortcuts // use private field to avoid infinite recursion @@ -879,11 +890,14 @@ namespace Flow.Launcher.ViewModel public void Show() { - MainWindowVisibility = Visibility.Visible; + Application.Current.Dispatcher.Invoke(() => + { + MainWindowVisibility = Visibility.Visible; - MainWindowVisibilityStatus = true; + MainWindowOpacity = 1; - MainWindowOpacity = 1; + MainWindowVisibilityStatus = true; + }); } public async void Hide() diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Main.cs b/Plugins/Flow.Launcher.Plugin.Shell/Main.cs index 351f0b2d8..7ce597b96 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Shell/Main.cs @@ -61,15 +61,15 @@ namespace Flow.Launcher.Plugin.Shell if (basedir != null) { - var autocomplete = + var autocomplete = Directory.GetFileSystemEntries(basedir) .Select(o => dir + Path.GetFileName(o)) .Where(o => o.StartsWith(cmd, StringComparison.OrdinalIgnoreCase) && - !results.Any(p => o.Equals(p.Title, StringComparison.OrdinalIgnoreCase)) && - !results.Any(p => o.Equals(p.Title, StringComparison.OrdinalIgnoreCase))).ToList(); - + !results.Any(p => o.Equals(p.Title, StringComparison.OrdinalIgnoreCase)) && + !results.Any(p => o.Equals(p.Title, StringComparison.OrdinalIgnoreCase))).ToList(); + autocomplete.Sort(); - + results.AddRange(autocomplete.ConvertAll(m => new Result { Title = m, @@ -194,72 +194,74 @@ namespace Flow.Launcher.Plugin.Shell ProcessStartInfo info = new() { - Verb = runAsAdministratorArg, - WorkingDirectory = workingDirectory, + Verb = runAsAdministratorArg, WorkingDirectory = workingDirectory, }; switch (_settings.Shell) { case Shell.Cmd: - { - info.FileName = "cmd.exe"; - info.Arguments = $"{(_settings.LeaveShellOpen ? "/k" : "/c")} {command}"; + { + info.FileName = "cmd.exe"; + info.Arguments = $"{(_settings.LeaveShellOpen ? "/k" : "/c")} {command}"; - //// Use info.Arguments instead of info.ArgumentList to enable users better control over the arguments they are writing. - //// Previous code using ArgumentList, commands needed to be seperated correctly: - //// Incorrect: - // info.ArgumentList.Add(_settings.LeaveShellOpen ? "/k" : "/c"); - // info.ArgumentList.Add(command); //<== info.ArgumentList.Add("mkdir \"c:\\test new\""); + //// Use info.Arguments instead of info.ArgumentList to enable users better control over the arguments they are writing. + //// Previous code using ArgumentList, commands needed to be seperated correctly: + //// Incorrect: + // info.ArgumentList.Add(_settings.LeaveShellOpen ? "/k" : "/c"); + // info.ArgumentList.Add(command); //<== info.ArgumentList.Add("mkdir \"c:\\test new\""); - //// Correct version should be: - //info.ArgumentList.Add(_settings.LeaveShellOpen ? "/k" : "/c"); - //info.ArgumentList.Add("mkdir"); - //info.ArgumentList.Add(@"c:\test new"); + //// Correct version should be: + //info.ArgumentList.Add(_settings.LeaveShellOpen ? "/k" : "/c"); + //info.ArgumentList.Add("mkdir"); + //info.ArgumentList.Add(@"c:\test new"); - //https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.processstartinfo.argumentlist?view=net-6.0#remarks + //https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.processstartinfo.argumentlist?view=net-6.0#remarks - break; - } + break; + } case Shell.Powershell: + { + info.FileName = "powershell.exe"; + if (_settings.LeaveShellOpen) { - info.FileName = "powershell.exe"; - if (_settings.LeaveShellOpen) - { - info.ArgumentList.Add("-NoExit"); - info.ArgumentList.Add(command); - } - else - { - info.ArgumentList.Add("-Command"); - info.ArgumentList.Add(command); - } - break; + info.ArgumentList.Add("-NoExit"); + info.ArgumentList.Add(command); } + else + { + info.ArgumentList.Add("-Command"); + info.ArgumentList.Add(command); + } + break; + } case Shell.RunCommand: + { + var parts = command.Split(new[] { - var parts = command.Split(new[] { ' ' }, 2); - if (parts.Length == 2) + ' ' + }, 2); + if (parts.Length == 2) + { + var filename = parts[0]; + if (ExistInPath(filename)) { - var filename = parts[0]; - if (ExistInPath(filename)) - { - var arguments = parts[1]; - info.FileName = filename; - info.ArgumentList.Add(arguments); - } - else - { - info.FileName = command; - } + var arguments = parts[1]; + info.FileName = filename; + info.ArgumentList.Add(arguments); } else { info.FileName = command; } - - break; } + else + { + info.FileName = command; + } + + break; + } default: throw new NotImplementedException(); } @@ -350,8 +352,12 @@ namespace Flow.Launcher.Plugin.Shell private void OnWinRPressed() { // show the main window and set focus to the query box - context.API.ShowMainWindow(); - context.API.ChangeQuery($"{context.CurrentPluginMetadata.ActionKeywords[0]}{Plugin.Query.TermSeparator}"); + Task.Run(() => + { + context.API.ShowMainWindow(); + context.API.ChangeQuery($"{context.CurrentPluginMetadata.ActionKeywords[0]}{Plugin.Query.TermSeparator}"); + }); + } public Control CreateSettingPanel()