From fe0153be088d8f82877d7ea06b46364a556c68f5 Mon Sep 17 00:00:00 2001 From: Garulf <535299+Garulf@users.noreply.github.com> Date: Wed, 15 Dec 2021 05:21:40 -0500 Subject: [PATCH 01/79] Initial commit --- Flow.Launcher/Flow.Launcher.csproj | 33 ++++++++++++ Flow.Launcher/ViewModel/MainViewModel.cs | 65 +++++++++++++++++++++++- 2 files changed, 97 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index f431504c2..117dececf 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -77,6 +77,24 @@ Designer PreserveNewest + + 1 + 1 + eab22ac0-30c1-11cf-a7eb-0000c05bae0b + 0 + tlbimp + false + true + + + 0 + 1 + 50a7e9b0-70ef-11d1-b75a-00a0c90564fe + 0 + tlbimp + false + true + PreserveNewest @@ -110,6 +128,21 @@ + + + True + True + Settings.settings + + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index abf3a1d14..cbe35e0f5 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -20,7 +20,9 @@ using Flow.Launcher.Infrastructure.Logger; using Microsoft.VisualStudio.Threading; using System.Threading.Channels; using ISavable = Flow.Launcher.Plugin.ISavable; - +using System.Runtime.InteropServices; +using System.Text; +using SHDocVw; namespace Flow.Launcher.ViewModel { @@ -720,6 +722,62 @@ namespace Flow.Launcher.ViewModel OpenResultCommandModifiers = _settings.OpenResultModifiers ?? DefaultOpenResultModifiers; } + private static string GetActiveExplorerPath() + { + // get the active window + IntPtr handle = GetForegroundWindow(); + + // Required ref: SHDocVw (Microsoft Internet Controls COM Object) - C:\Windows\system32\ShDocVw.dll + ShellWindows shellWindows = new SHDocVw.ShellWindows(); + + // loop through all windows + foreach (InternetExplorer window in shellWindows) + { + // match active window + if (window.HWND == (int)handle) + { + // Required ref: Shell32 - C:\Windows\system32\Shell32.dll + var shellWindow = window.Document as Shell32.IShellFolderViewDual2; + + // will be null if you are in Internet Explorer for example + if (shellWindow != null) + { + // Item without an index returns the current object + var currentFolder = shellWindow.Folder.Items().Item(); + + // special folder - use window title + // for some reason on "Desktop" gives null + if (currentFolder == null || currentFolder.Path.StartsWith("::")) + { + // Get window title instead + const int nChars = 256; + StringBuilder Buff = new StringBuilder(nChars); + if (GetWindowText(handle, Buff, nChars) > 0) + { + return Buff.ToString(); + } + } + else + { + return currentFolder.Path; + } + } + + break; + } + } + + return null; + } + + // COM Imports + + [DllImport("user32.dll")] + private static extern IntPtr GetForegroundWindow(); + + [DllImport("user32.dll")] + static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count); + public void ToggleFlowLauncher() { if (!MainWindowVisibilityStatus) @@ -734,6 +792,9 @@ namespace Flow.Launcher.ViewModel public void Show() { + string _explorerPath = GetActiveExplorerPath(); + + ChangeQueryText($"{_explorerPath}\\>"); if (_settings.UseSound) { MediaPlayer media = new MediaPlayer(); @@ -749,6 +810,8 @@ namespace Flow.Launcher.ViewModel ((MainWindow)Application.Current.MainWindow).WindowAnimator(); MainWindowOpacity = 1; + + } public async void Hide() From 79a6fda289dfa641e011588f500d98b22425b3d2 Mon Sep 17 00:00:00 2001 From: Garulf <535299+Garulf@users.noreply.github.com> Date: Wed, 15 Dec 2021 05:24:31 -0500 Subject: [PATCH 02/79] Only insert when explorer is active --- Flow.Launcher/ViewModel/MainViewModel.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index cbe35e0f5..7a48d159f 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -793,8 +793,11 @@ namespace Flow.Launcher.ViewModel public void Show() { string _explorerPath = GetActiveExplorerPath(); - - ChangeQueryText($"{_explorerPath}\\>"); + if (_explorerPath != null) + { + ChangeQueryText($"{_explorerPath}\\>"); + } + if (_settings.UseSound) { MediaPlayer media = new MediaPlayer(); From 1fdaec2648f14994b9b15fc86c732629796f6f13 Mon Sep 17 00:00:00 2001 From: Garulf <535299+Garulf@users.noreply.github.com> Date: Wed, 15 Dec 2021 06:15:05 -0500 Subject: [PATCH 03/79] ChangeQuery after window is visible --- Flow.Launcher/ViewModel/MainViewModel.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 7a48d159f..b525ac342 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -731,7 +731,7 @@ namespace Flow.Launcher.ViewModel ShellWindows shellWindows = new SHDocVw.ShellWindows(); // loop through all windows - foreach (InternetExplorer window in shellWindows) + foreach (SHDocVw.InternetExplorer window in shellWindows) { // match active window if (window.HWND == (int)handle) @@ -793,10 +793,7 @@ namespace Flow.Launcher.ViewModel public void Show() { string _explorerPath = GetActiveExplorerPath(); - if (_explorerPath != null) - { - ChangeQueryText($"{_explorerPath}\\>"); - } + if (_settings.UseSound) { @@ -813,7 +810,10 @@ namespace Flow.Launcher.ViewModel ((MainWindow)Application.Current.MainWindow).WindowAnimator(); MainWindowOpacity = 1; - + if (_explorerPath != null && _explorerPath != "File Explorer") + { + ChangeQueryText($"{_explorerPath}\\>"); + } } From 1109f9887e78ddcfda62f0e775893f6161344ec2 Mon Sep 17 00:00:00 2001 From: Garulf <535299+Garulf@users.noreply.github.com> Date: Thu, 16 Dec 2021 02:03:22 -0500 Subject: [PATCH 04/79] Remove unnecessary changes --- Flow.Launcher/Flow.Launcher.csproj | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index 117dececf..9d4042700 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -128,21 +128,6 @@ - - - True - True - Settings.settings - - - - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - From aa5ca76b180b94680ee4f1f677581eb35bbf7424 Mon Sep 17 00:00:00 2001 From: Garulf <535299+Garulf@users.noreply.github.com> Date: Mon, 20 Dec 2021 03:34:41 -0500 Subject: [PATCH 05/79] Check window is InternetExplorer type --- Flow.Launcher/ViewModel/MainViewModel.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index b525ac342..412af09fd 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -731,13 +731,19 @@ namespace Flow.Launcher.ViewModel ShellWindows shellWindows = new SHDocVw.ShellWindows(); // loop through all windows - foreach (SHDocVw.InternetExplorer window in shellWindows) + foreach (var window in shellWindows) { + if (window is not SHDocVw.InternetExplorer) + { + continue; + } + + var explorerWindow = (SHDocVw.InternetExplorer)window; // match active window - if (window.HWND == (int)handle) + if (explorerWindow.HWND == (int)handle) { // Required ref: Shell32 - C:\Windows\system32\Shell32.dll - var shellWindow = window.Document as Shell32.IShellFolderViewDual2; + var shellWindow = explorerWindow.Document as Shell32.IShellFolderViewDual2; // will be null if you are in Internet Explorer for example if (shellWindow != null) From c039b17d93c66717dbdaf4a14843ce19a224f5c8 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Sat, 16 Jul 2022 11:16:00 +0200 Subject: [PATCH 06/79] Refactor file explorer path getting --- Flow.Launcher/Helper/FileExplorerHelper.cs | 70 ++++++++++++++++++++++ Flow.Launcher/ViewModel/MainViewModel.cs | 66 +------------------- 2 files changed, 72 insertions(+), 64 deletions(-) create mode 100644 Flow.Launcher/Helper/FileExplorerHelper.cs diff --git a/Flow.Launcher/Helper/FileExplorerHelper.cs b/Flow.Launcher/Helper/FileExplorerHelper.cs new file mode 100644 index 000000000..16152e830 --- /dev/null +++ b/Flow.Launcher/Helper/FileExplorerHelper.cs @@ -0,0 +1,70 @@ +using System; +using System.Text; +using System.Runtime.InteropServices; +using System.IO; + +namespace Flow.Launcher.Helper +{ + public class FileExplorerHelper + { + + /// + /// Gets the path of the file explorer that is currently in the foreground + /// + public static string GetActiveExplorerPath() + { + var explorerWindow = GetActiveExplorer(); + string locationUrl = explorerWindow.LocationURL; + if (!string.IsNullOrEmpty(locationUrl)) + { + return new Uri(locationUrl).LocalPath; + } + else + { + return null; + } + } + + /// + /// Gets the file explorer that is currently in the foreground + /// + private static SHDocVw.InternetExplorer GetActiveExplorer() + { + // get the active window + IntPtr handle = GetForegroundWindow(); + + // Required ref: SHDocVw (Microsoft Internet Controls COM Object) - C:\Windows\system32\ShDocVw.dll + var shellWindows = new SHDocVw.ShellWindows(); + + // loop through all windows + foreach (var window in shellWindows) + { + if (window is SHDocVw.InternetExplorer explorerWindow && new IntPtr(explorerWindow.HWND) == handle) + { + // we have found the desired window, now let's make sure that it is indeed a file explorer + // we don't want the Internet Explorer or the classic control panel + if (explorerWindow.Document is not Shell32.IShellFolderViewDual2) + { + return null; + } + if (Path.GetFileName(explorerWindow.FullName) != "explorer.exe") + { + return null; + } + + return explorerWindow; + } + } + + return null; + } + + // COM Imports + + [DllImport("user32.dll")] + private static extern IntPtr GetForegroundWindow(); + + [DllImport("user32.dll")] + static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count); + } +} diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 412af09fd..ff0c88339 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -722,68 +722,6 @@ namespace Flow.Launcher.ViewModel OpenResultCommandModifiers = _settings.OpenResultModifiers ?? DefaultOpenResultModifiers; } - private static string GetActiveExplorerPath() - { - // get the active window - IntPtr handle = GetForegroundWindow(); - - // Required ref: SHDocVw (Microsoft Internet Controls COM Object) - C:\Windows\system32\ShDocVw.dll - ShellWindows shellWindows = new SHDocVw.ShellWindows(); - - // loop through all windows - foreach (var window in shellWindows) - { - if (window is not SHDocVw.InternetExplorer) - { - continue; - } - - var explorerWindow = (SHDocVw.InternetExplorer)window; - // match active window - if (explorerWindow.HWND == (int)handle) - { - // Required ref: Shell32 - C:\Windows\system32\Shell32.dll - var shellWindow = explorerWindow.Document as Shell32.IShellFolderViewDual2; - - // will be null if you are in Internet Explorer for example - if (shellWindow != null) - { - // Item without an index returns the current object - var currentFolder = shellWindow.Folder.Items().Item(); - - // special folder - use window title - // for some reason on "Desktop" gives null - if (currentFolder == null || currentFolder.Path.StartsWith("::")) - { - // Get window title instead - const int nChars = 256; - StringBuilder Buff = new StringBuilder(nChars); - if (GetWindowText(handle, Buff, nChars) > 0) - { - return Buff.ToString(); - } - } - else - { - return currentFolder.Path; - } - } - - break; - } - } - - return null; - } - - // COM Imports - - [DllImport("user32.dll")] - private static extern IntPtr GetForegroundWindow(); - - [DllImport("user32.dll")] - static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count); - public void ToggleFlowLauncher() { if (!MainWindowVisibilityStatus) @@ -798,7 +736,7 @@ namespace Flow.Launcher.ViewModel public void Show() { - string _explorerPath = GetActiveExplorerPath(); + string _explorerPath = FileExplorerHelper.GetActiveExplorerPath(); if (_settings.UseSound) @@ -816,7 +754,7 @@ namespace Flow.Launcher.ViewModel ((MainWindow)Application.Current.MainWindow).WindowAnimator(); MainWindowOpacity = 1; - if (_explorerPath != null && _explorerPath != "File Explorer") + if (_explorerPath != null) { ChangeQueryText($"{_explorerPath}\\>"); } From 81bacdb9a72e5482788d2f90933e46659fd4ce42 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Sat, 16 Jul 2022 11:20:36 +0200 Subject: [PATCH 07/79] Remove unused function --- Flow.Launcher/Helper/FileExplorerHelper.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Flow.Launcher/Helper/FileExplorerHelper.cs b/Flow.Launcher/Helper/FileExplorerHelper.cs index 16152e830..3b24b7414 100644 --- a/Flow.Launcher/Helper/FileExplorerHelper.cs +++ b/Flow.Launcher/Helper/FileExplorerHelper.cs @@ -63,8 +63,5 @@ namespace Flow.Launcher.Helper [DllImport("user32.dll")] private static extern IntPtr GetForegroundWindow(); - - [DllImport("user32.dll")] - static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count); } } From ab306d90cc1ef0187b3227c9174d303e846c38fa Mon Sep 17 00:00:00 2001 From: stefnotch Date: Sat, 16 Jul 2022 11:22:31 +0200 Subject: [PATCH 08/79] Remove unused imports --- Flow.Launcher/ViewModel/MainViewModel.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index ff0c88339..d785249a6 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -20,9 +20,6 @@ using Flow.Launcher.Infrastructure.Logger; using Microsoft.VisualStudio.Threading; using System.Threading.Channels; using ISavable = Flow.Launcher.Plugin.ISavable; -using System.Runtime.InteropServices; -using System.Text; -using SHDocVw; namespace Flow.Launcher.ViewModel { From 1606908d28484c1cc63410be0bbd2ef499730e25 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Mon, 8 Aug 2022 19:07:18 +0200 Subject: [PATCH 09/79] Move to dynamic --- Flow.Launcher/Flow.Launcher.csproj | 18 ------------ Flow.Launcher/Helper/FileExplorerHelper.cs | 34 +++++++++------------- 2 files changed, 14 insertions(+), 38 deletions(-) diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index 9d4042700..f431504c2 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -77,24 +77,6 @@ Designer PreserveNewest - - 1 - 1 - eab22ac0-30c1-11cf-a7eb-0000c05bae0b - 0 - tlbimp - false - true - - - 0 - 1 - 50a7e9b0-70ef-11d1-b75a-00a0c90564fe - 0 - tlbimp - false - true - PreserveNewest diff --git a/Flow.Launcher/Helper/FileExplorerHelper.cs b/Flow.Launcher/Helper/FileExplorerHelper.cs index 3b24b7414..22c3de63f 100644 --- a/Flow.Launcher/Helper/FileExplorerHelper.cs +++ b/Flow.Launcher/Helper/FileExplorerHelper.cs @@ -14,7 +14,7 @@ namespace Flow.Launcher.Helper public static string GetActiveExplorerPath() { var explorerWindow = GetActiveExplorer(); - string locationUrl = explorerWindow.LocationURL; + string locationUrl = explorerWindow?.LocationURL; if (!string.IsNullOrEmpty(locationUrl)) { return new Uri(locationUrl).LocalPath; @@ -28,31 +28,25 @@ namespace Flow.Launcher.Helper /// /// Gets the file explorer that is currently in the foreground /// - private static SHDocVw.InternetExplorer GetActiveExplorer() + private static dynamic GetActiveExplorer() { // get the active window IntPtr handle = GetForegroundWindow(); - // Required ref: SHDocVw (Microsoft Internet Controls COM Object) - C:\Windows\system32\ShDocVw.dll - var shellWindows = new SHDocVw.ShellWindows(); - - // loop through all windows - foreach (var window in shellWindows) + Type type = Type.GetTypeFromProgID("Shell.Application"); + if (type == null) return null; + dynamic shell = Activator.CreateInstance(type); + var openWindows = shell.Windows(); + for (int i = 0; i < openWindows.Count; i++) { - if (window is SHDocVw.InternetExplorer explorerWindow && new IntPtr(explorerWindow.HWND) == handle) - { - // we have found the desired window, now let's make sure that it is indeed a file explorer - // we don't want the Internet Explorer or the classic control panel - if (explorerWindow.Document is not Shell32.IShellFolderViewDual2) - { - return null; - } - if (Path.GetFileName(explorerWindow.FullName) != "explorer.exe") - { - return null; - } + var window = openWindows.Item(i); + if (window == null) continue; - return explorerWindow; + // find the desired window and make sure that it is indeed a file explorer + // we don't want the Internet Explorer or the classic control panel + if (Path.GetFileName((string)window.FullName) == "explorer.exe" && new IntPtr(window.HWND) == handle) + { + return window; } } From 0a062c310a1386d9e7c9bbe4ad59e3927981ea96 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Mon, 8 Aug 2022 19:19:05 +0200 Subject: [PATCH 10/79] Fix querying of explorer window --- Flow.Launcher/Helper/FileExplorerHelper.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher/Helper/FileExplorerHelper.cs b/Flow.Launcher/Helper/FileExplorerHelper.cs index 22c3de63f..c6d50f514 100644 --- a/Flow.Launcher/Helper/FileExplorerHelper.cs +++ b/Flow.Launcher/Helper/FileExplorerHelper.cs @@ -44,7 +44,8 @@ namespace Flow.Launcher.Helper // find the desired window and make sure that it is indeed a file explorer // we don't want the Internet Explorer or the classic control panel - if (Path.GetFileName((string)window.FullName) == "explorer.exe" && new IntPtr(window.HWND) == handle) + // ToLower() is needed, because Windows can report the path as "C:\\Windows\\Explorer.EXE" + if (Path.GetFileName((string)window.FullName).ToLower() == "explorer.exe" && new IntPtr(window.HWND) == handle) { return window; } @@ -53,8 +54,6 @@ namespace Flow.Launcher.Helper return null; } - // COM Imports - [DllImport("user32.dll")] private static extern IntPtr GetForegroundWindow(); } From 55dee4f471641651583208f4ffbdc4b07299a4ab Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Tue, 3 Jan 2023 23:05:35 -0500 Subject: [PATCH 11/79] Use the explorer window with the lowest z-index to allow active explorer path be a built-in shortcut --- .../FileExplorerHelper.cs | 89 +++++++++++++++++++ .../UserSettings/Settings.cs | 6 +- Flow.Launcher/Helper/FileExplorerHelper.cs | 60 ------------- Flow.Launcher/Languages/en.xaml | 1 + 4 files changed, 94 insertions(+), 62 deletions(-) create mode 100644 Flow.Launcher.Infrastructure/FileExplorerHelper.cs delete mode 100644 Flow.Launcher/Helper/FileExplorerHelper.cs diff --git a/Flow.Launcher.Infrastructure/FileExplorerHelper.cs b/Flow.Launcher.Infrastructure/FileExplorerHelper.cs new file mode 100644 index 000000000..a3b1bd6b7 --- /dev/null +++ b/Flow.Launcher.Infrastructure/FileExplorerHelper.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; + +namespace Flow.Launcher.Infrastructure +{ + public static class FileExplorerHelper + { + /// + /// Gets the path of the file explorer that is currently in the foreground + /// + public static string GetActiveExplorerPath() + { + var explorerWindow = GetActiveExplorer(); + string locationUrl = explorerWindow?.LocationURL; + return !string.IsNullOrEmpty(locationUrl) ? new Uri(locationUrl).LocalPath : null; + } + + /// + /// Gets the file explorer that is currently in the foreground + /// + private static dynamic GetActiveExplorer() + { + Type type = Type.GetTypeFromProgID("Shell.Application"); + if (type == null) return null; + dynamic shell = Activator.CreateInstance(type); + if (shell == null) + { + return null; + } + + var explorerWindows = new List(); + var openWindows = shell.Windows(); + for (int i = 0; i < openWindows.Count; i++) + { + var window = openWindows.Item(i); + if (window == null) continue; + + // find the desired window and make sure that it is indeed a file explorer + // we don't want the Internet Explorer or the classic control panel + // ToLower() is needed, because Windows can report the path as "C:\\Windows\\Explorer.EXE" + if (Path.GetFileName((string)window.FullName)?.ToLower() == "explorer.exe") + { + explorerWindows.Add(window); + } + } + + if (explorerWindows.Count == 0) return null; + + var zOrders = GetZOrder(explorerWindows); + + return explorerWindows.Zip(zOrders).MinBy(x => x.Second).First; + } + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam); + + private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); + + /// + /// Gets the z-order for one or more windows atomically with respect to each other. In Windows, smaller z-order is higher. If the window is not top level, the z order is returned as -1. + /// + private static IEnumerable GetZOrder(List hWnds) + { + var z = new int[hWnds.Count]; + for (var i = 0; i < hWnds.Count; i++) z[i] = -1; + + var index = 0; + var numRemaining = hWnds.Count; + EnumWindows((wnd, _) => + { + var searchIndex = hWnds.FindIndex(x => x.HWND == wnd.ToInt32()); + if (searchIndex != -1) + { + z[searchIndex] = index; + numRemaining--; + if (numRemaining == 0) return false; + } + index++; + return true; + }, IntPtr.Zero); + + return z; + } + } +} diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index a184520d0..753334e23 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -192,8 +192,10 @@ namespace Flow.Launcher.Infrastructure.UserSettings public ObservableCollection CustomShortcuts { get; set; } = new ObservableCollection(); [JsonIgnore] - public ObservableCollection BuiltinShortcuts { get; set; } = new ObservableCollection() { - new BuiltinShortcutModel("{clipboard}", "shortcut_clipboard_description", Clipboard.GetText) + public ObservableCollection BuiltinShortcuts { get; set; } = new() + { + new BuiltinShortcutModel("{clipboard}", "shortcut_clipboard_description", Clipboard.GetText), + new BuiltinShortcutModel("{active_explorer_path}", "shortcut_active_explorer_path", FileExplorerHelper.GetActiveExplorerPath) }; public bool DontPromptUpdateMsg { get; set; } diff --git a/Flow.Launcher/Helper/FileExplorerHelper.cs b/Flow.Launcher/Helper/FileExplorerHelper.cs deleted file mode 100644 index c6d50f514..000000000 --- a/Flow.Launcher/Helper/FileExplorerHelper.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using System.Text; -using System.Runtime.InteropServices; -using System.IO; - -namespace Flow.Launcher.Helper -{ - public class FileExplorerHelper - { - - /// - /// Gets the path of the file explorer that is currently in the foreground - /// - public static string GetActiveExplorerPath() - { - var explorerWindow = GetActiveExplorer(); - string locationUrl = explorerWindow?.LocationURL; - if (!string.IsNullOrEmpty(locationUrl)) - { - return new Uri(locationUrl).LocalPath; - } - else - { - return null; - } - } - - /// - /// Gets the file explorer that is currently in the foreground - /// - private static dynamic GetActiveExplorer() - { - // get the active window - IntPtr handle = GetForegroundWindow(); - - Type type = Type.GetTypeFromProgID("Shell.Application"); - if (type == null) return null; - dynamic shell = Activator.CreateInstance(type); - var openWindows = shell.Windows(); - for (int i = 0; i < openWindows.Count; i++) - { - var window = openWindows.Item(i); - if (window == null) continue; - - // find the desired window and make sure that it is indeed a file explorer - // we don't want the Internet Explorer or the classic control panel - // ToLower() is needed, because Windows can report the path as "C:\\Windows\\Explorer.EXE" - if (Path.GetFileName((string)window.FullName).ToLower() == "explorer.exe" && new IntPtr(window.HWND) == handle) - { - return window; - } - } - - return null; - } - - [DllImport("user32.dll")] - private static extern IntPtr GetForegroundWindow(); - } -} diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index b192617cb..49c603dc8 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -176,6 +176,7 @@ Are you sure you want to delete {0} plugin hotkey? Are you sure you want to delete shortcut: {0} with expansion {1}? Get text from clipboard. + Get path from active explorer. Query window shadow effect Shadow effect has a substantial usage of GPU. Not recommended if your computer performance is limited. Window Width Size From f5d3acbe233bc9603a1b36f129148c400cf2fd3d Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Tue, 3 Jan 2023 23:23:56 -0500 Subject: [PATCH 12/79] Catch General Exception when checking update, and use PeriodicTimer to avoid error to crash the main thread --- Flow.Launcher.Core/Updater.cs | 10 +++++++--- Flow.Launcher/App.xaml.cs | 20 +++++++++----------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs index 91de8298c..3f64b273e 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -33,7 +33,7 @@ namespace Flow.Launcher.Core public async Task UpdateAppAsync(IPublicAPI api, bool silentUpdate = true) { - await UpdateLock.WaitAsync(); + await UpdateLock.WaitAsync().ConfigureAwait(false); try { if (!silentUpdate) @@ -88,9 +88,13 @@ namespace Flow.Launcher.Core UpdateManager.RestartApp(Constant.ApplicationFileName); } } - catch (Exception e) when (e is HttpRequestException or WebException or SocketException || e.InnerException is TimeoutException) + catch (Exception e) { - Log.Exception($"|Updater.UpdateApp|Check your connection and proxy settings to github-cloud.s3.amazonaws.com.", e); + if ((e is HttpRequestException or WebException or SocketException || e.InnerException is TimeoutException)) + Log.Exception($"|Updater.UpdateApp|Check your connection and proxy settings to github-cloud.s3.amazonaws.com.", e); + else + Log.Exception($"|Updater.UpdateApp|Error Occurred", e); + if (!silentUpdate) api.ShowMsg(api.GetTranslation("update_flowlauncher_fail"), api.GetTranslation("update_flowlauncher_check_connection")); diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 43fa0eddb..9df936e76 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using System.Text; +using System.Threading; using System.Threading.Tasks; using System.Timers; using System.Windows; @@ -84,7 +85,7 @@ namespace Flow.Launcher Current.MainWindow = window; Current.MainWindow.Title = Constant.FlowLauncher; - + HotKeyMapper.Initialize(_mainVM); // happlebao todo temp fix for instance code logic @@ -130,20 +131,17 @@ namespace Flow.Launcher //[Conditional("RELEASE")] private void AutoUpdates() { - Task.Run(async () => + _ = Task.Run(async () => { if (_settings.AutoUpdates) { - // check udpate every 5 hours - var timer = new Timer(1000 * 60 * 60 * 5); - timer.Elapsed += async (s, e) => - { - await _updater.UpdateAppAsync(API); - }; - timer.Start(); - - // check updates on startup + // check update every 5 hours + var timer = new PeriodicTimer(TimeSpan.FromHours(5)); await _updater.UpdateAppAsync(API); + + while (await timer.WaitForNextTickAsync()) + // check updates on startup + await _updater.UpdateAppAsync(API); } }); } From c2137011e8ae13781207b67ddbdc73e8c09bebc0 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sat, 7 Jan 2023 22:15:57 +1100 Subject: [PATCH 13/79] Update comment --- Flow.Launcher/App.xaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 9df936e76..4ae754fcd 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -88,7 +88,7 @@ namespace Flow.Launcher HotKeyMapper.Initialize(_mainVM); - // happlebao todo temp fix for instance code logic + // todo temp fix for instance code logic // load plugin before change language, because plugin language also needs be changed InternationalizationManager.Instance.Settings = _settings; InternationalizationManager.Instance.ChangeLanguage(_settings.Language); From dfe2c01fb5745381561724e417899c1686e53196 Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Sat, 7 Jan 2023 09:14:18 -0600 Subject: [PATCH 14/79] fix spelling --- Flow.Launcher/App.xaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 4ae754fcd..1d398276d 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -92,7 +92,7 @@ namespace Flow.Launcher // load plugin before change language, because plugin language also needs be changed InternationalizationManager.Instance.Settings = _settings; InternationalizationManager.Instance.ChangeLanguage(_settings.Language); - // main windows needs initialized before theme change because of blur settigns + // main windows needs initialized before theme change because of blur settings ThemeManager.Instance.Settings = _settings; ThemeManager.Instance.ChangeTheme(_settings.Theme); From a92ebf5756ba6f937189f3c30becbd88e3104040 Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Sun, 8 Jan 2023 16:31:37 -0600 Subject: [PATCH 15/79] Dynamic Height and Dynamic Icon Size --- .../DiameterToCenterPointConverter.cs | 25 +++ .../Converters/IconRadiusConverter.cs | 27 +++ Flow.Launcher/ResultListBox.xaml | 155 +++++++++++------- Flow.Launcher/Themes/Base.xaml | 8 + Flow.Launcher/ViewModel/ResultsViewModel.cs | 2 +- 5 files changed, 156 insertions(+), 61 deletions(-) create mode 100644 Flow.Launcher/Converters/DiameterToCenterPointConverter.cs create mode 100644 Flow.Launcher/Converters/IconRadiusConverter.cs diff --git a/Flow.Launcher/Converters/DiameterToCenterPointConverter.cs b/Flow.Launcher/Converters/DiameterToCenterPointConverter.cs new file mode 100644 index 000000000..e81bb2507 --- /dev/null +++ b/Flow.Launcher/Converters/DiameterToCenterPointConverter.cs @@ -0,0 +1,25 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; + +namespace Flow.Launcher.Converters +{ + public class DiameterToCenterPointConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is double d) + { + return new Point(d / 2, d / 2); + } + + return new Point(0, 0); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotSupportedException(); + } + } +} diff --git a/Flow.Launcher/Converters/IconRadiusConverter.cs b/Flow.Launcher/Converters/IconRadiusConverter.cs new file mode 100644 index 000000000..51129cfb8 --- /dev/null +++ b/Flow.Launcher/Converters/IconRadiusConverter.cs @@ -0,0 +1,27 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using Windows.Devices.PointOfService; + +namespace Flow.Launcher.Converters +{ + public class IconRadiusConverter : IMultiValueConverter + { + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + if (values.Length != 2) + throw new ArgumentException("IconRadiusConverter must have 2 parameters"); + + return values[1] switch + { + true => (double)values[0] / 2, + false => (double)values[0], + _ => throw new ArgumentException("The second argument should be boolean", nameof(values)) + }; + } + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + throw new NotSupportedException(); + } + } +} diff --git a/Flow.Launcher/ResultListBox.xaml b/Flow.Launcher/ResultListBox.xaml index 8f3238303..b2ab4a287 100644 --- a/Flow.Launcher/ResultListBox.xaml +++ b/Flow.Launcher/ResultListBox.xaml @@ -1,5 +1,4 @@ - + + + +