Skip to content

Commit bb20878

Browse files
committed
Support enable & disable quick switch
1 parent 9a241e2 commit bb20878

File tree

9 files changed

+210
-108
lines changed

9 files changed

+210
-108
lines changed

Flow.Launcher.Infrastructure/QuickSwitch/QuickSwitch.cs

Lines changed: 129 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -67,91 +67,137 @@ public static class QuickSwitch
6767
private static HWND _hookedDialogWindowHandle = HWND.Null;
6868
private static readonly object _hookedDialogWindowHandleLock = new();*/
6969

70-
private static bool _isInitialized = false;
70+
private static bool _initialized = false;
71+
private static bool _enabled = false;
7172

7273
#endregion
7374

74-
#region Initialization
75+
#region Initialize & Setup
7576

76-
public static void Initialize()
77+
public static void InitializeQuickSwitch()
7778
{
78-
if (_isInitialized) return;
79+
if (_initialized) return;
7980

80-
// Check all foreground windows and check if there are explorer windows
81-
lock (_lastExplorerViewLock)
81+
// Initialize main window handle
82+
_mainWindowHandle = Win32Helper.GetMainWindowHandle();
83+
84+
// Initialize timer
85+
_dragMoveTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(10) };
86+
_dragMoveTimer.Tick += (s, e) => InvokeUpdateQuickSwitchWindow();
87+
88+
_initialized = true;
89+
}
90+
91+
public static void SetupQuickSwitch(bool enabled)
92+
{
93+
if (enabled == _enabled) return;
94+
95+
if (enabled)
8296
{
83-
var explorerInitialized = false;
84-
EnumerateShellWindows((shellWindow) =>
97+
// Check all foreground windows and check if there are explorer windows
98+
lock (_lastExplorerViewLock)
8599
{
86-
if (shellWindow is not IWebBrowser2 explorer)
100+
var explorerInitialized = false;
101+
EnumerateShellWindows((shellWindow) =>
87102
{
88-
return;
89-
}
103+
if (shellWindow is not IWebBrowser2 explorer)
104+
{
105+
return;
106+
}
90107

91-
// Initialize one explorer window even if it is not foreground
92-
if (!explorerInitialized)
93-
{
94-
_lastExplorerView = explorer;
108+
// Initialize one explorer window even if it is not foreground
109+
if (!explorerInitialized)
110+
{
111+
_lastExplorerView = explorer;
95112

96-
Log.Debug(ClassName, $"Explorer Window: {explorer.HWND.Value}");
97-
}
98-
// Force update explorer window if it is foreground
99-
else if (Win32Helper.IsForegroundWindow(explorer.HWND.Value))
100-
{
101-
_lastExplorerView = explorer;
113+
Log.Debug(ClassName, $"Explorer Window: {explorer.HWND.Value}");
114+
}
102115

103-
Log.Debug(ClassName, $"Explorer Window: {explorer.HWND.Value}");
104-
}
105-
});
106-
}
116+
// Force update explorer window if it is foreground
117+
else if (Win32Helper.IsForegroundWindow(explorer.HWND.Value))
118+
{
119+
_lastExplorerView = explorer;
107120

108-
// Call ForegroundChange when the foreground window changes
109-
_foregroundChangeHook = PInvoke.SetWinEventHook(
110-
PInvoke.EVENT_SYSTEM_FOREGROUND,
111-
PInvoke.EVENT_SYSTEM_FOREGROUND,
112-
PInvoke.GetModuleHandle((PCWSTR)null),
113-
ForegroundChangeCallback,
114-
0,
115-
0,
116-
PInvoke.WINEVENT_OUTOFCONTEXT);
117-
118-
// Call LocationChange when the location of the window changes
119-
_locationChangeHook = PInvoke.SetWinEventHook(
120-
PInvoke.EVENT_OBJECT_LOCATIONCHANGE,
121-
PInvoke.EVENT_OBJECT_LOCATIONCHANGE,
122-
PInvoke.GetModuleHandle((PCWSTR)null),
123-
LocationChangeCallback,
124-
0,
125-
0,
126-
PInvoke.WINEVENT_OUTOFCONTEXT);
127-
128-
// Call DestroyChange when the window is destroyed
129-
_destroyChangeHook = PInvoke.SetWinEventHook(
130-
PInvoke.EVENT_OBJECT_DESTROY,
131-
PInvoke.EVENT_OBJECT_DESTROY,
132-
PInvoke.GetModuleHandle((PCWSTR)null),
133-
DestroyChangeCallback,
134-
0,
135-
0,
136-
PInvoke.WINEVENT_OUTOFCONTEXT);
137-
138-
if (_foregroundChangeHook.IsNull ||
139-
_locationChangeHook.IsNull ||
140-
_destroyChangeHook.IsNull)
141-
{
142-
Log.Error(ClassName, "Failed to initialize QuickSwitch");
143-
return;
144-
}
121+
Log.Debug(ClassName, $"Explorer Window: {explorer.HWND.Value}");
122+
}
123+
});
124+
}
145125

146-
// Initialize main window handle
147-
_mainWindowHandle = Win32Helper.GetMainWindowHandle();
126+
// Unhook events
127+
if (!_foregroundChangeHook.IsNull)
128+
{
129+
PInvoke.UnhookWinEvent(_foregroundChangeHook);
130+
_foregroundChangeHook = HWINEVENTHOOK.Null;
131+
}
132+
if (!_locationChangeHook.IsNull)
133+
{
134+
PInvoke.UnhookWinEvent(_locationChangeHook);
135+
_locationChangeHook = HWINEVENTHOOK.Null;
136+
}
137+
if (!_destroyChangeHook.IsNull)
138+
{
139+
PInvoke.UnhookWinEvent(_destroyChangeHook);
140+
_destroyChangeHook = HWINEVENTHOOK.Null;
141+
}
148142

149-
// Initialize timer
150-
_dragMoveTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(10) };
151-
_dragMoveTimer.Tick += (s, e) => InvokeUpdateQuickSwitchWindow();
143+
// Hook events
144+
_foregroundChangeHook = PInvoke.SetWinEventHook(
145+
PInvoke.EVENT_SYSTEM_FOREGROUND,
146+
PInvoke.EVENT_SYSTEM_FOREGROUND,
147+
PInvoke.GetModuleHandle((PCWSTR)null),
148+
ForegroundChangeCallback,
149+
0,
150+
0,
151+
PInvoke.WINEVENT_OUTOFCONTEXT);
152+
_locationChangeHook = PInvoke.SetWinEventHook(
153+
PInvoke.EVENT_OBJECT_LOCATIONCHANGE,
154+
PInvoke.EVENT_OBJECT_LOCATIONCHANGE,
155+
PInvoke.GetModuleHandle((PCWSTR)null),
156+
LocationChangeCallback,
157+
0,
158+
0,
159+
PInvoke.WINEVENT_OUTOFCONTEXT);
160+
_destroyChangeHook = PInvoke.SetWinEventHook(
161+
PInvoke.EVENT_OBJECT_DESTROY,
162+
PInvoke.EVENT_OBJECT_DESTROY,
163+
PInvoke.GetModuleHandle((PCWSTR)null),
164+
DestroyChangeCallback,
165+
0,
166+
0,
167+
PInvoke.WINEVENT_OUTOFCONTEXT);
168+
169+
if (_foregroundChangeHook.IsNull ||
170+
_locationChangeHook.IsNull ||
171+
_destroyChangeHook.IsNull)
172+
{
173+
Log.Error(ClassName, "Failed to enable QuickSwitch");
174+
return;
175+
}
176+
}
177+
else
178+
{
179+
// Unhook events
180+
if (!_foregroundChangeHook.IsNull)
181+
{
182+
PInvoke.UnhookWinEvent(_foregroundChangeHook);
183+
_foregroundChangeHook = HWINEVENTHOOK.Null;
184+
}
185+
if (!_locationChangeHook.IsNull)
186+
{
187+
PInvoke.UnhookWinEvent(_locationChangeHook);
188+
_locationChangeHook = HWINEVENTHOOK.Null;
189+
}
190+
if (!_destroyChangeHook.IsNull)
191+
{
192+
PInvoke.UnhookWinEvent(_destroyChangeHook);
193+
_destroyChangeHook = HWINEVENTHOOK.Null;
194+
}
152195

153-
_isInitialized = true;
154-
return;
196+
// Stop drag move timer
197+
_dragMoveTimer?.Stop();
198+
}
199+
200+
_enabled = enabled;
155201
}
156202

157203
#endregion
@@ -243,10 +289,7 @@ private static void InvokeHideQuickSwitchWindow()
243289

244290
public static void OnToggleHotkey(object sender, HotkeyEventArgs args)
245291
{
246-
if (_isInitialized)
247-
{
248-
NavigateDialogPath(Win32Helper.GetForegroundWindowHWND());
249-
}
292+
NavigateDialogPath(Win32Helper.GetForegroundWindowHWND());
250293
}
251294

252295
#endregion
@@ -620,10 +663,11 @@ private static unsafe void EnumerateShellWindows(Action<object> action)
620663

621664
public static void Dispose()
622665
{
623-
// Reset initialize flag
624-
_isInitialized = false;
666+
// Reset flags
667+
_enabled = false;
668+
_initialized = false;
625669

626-
// Dispose handle
670+
// Unhook events
627671
if (!_foregroundChangeHook.IsNull)
628672
{
629673
PInvoke.UnhookWinEvent(_foregroundChangeHook);
@@ -646,9 +690,16 @@ public static void Dispose()
646690
}
647691

648692
// Release ComObjects
649-
if (_lastExplorerView != null)
693+
try
694+
{
695+
if (_lastExplorerView != null)
696+
{
697+
Marshal.ReleaseComObject(_lastExplorerView);
698+
_lastExplorerView = null;
699+
}
700+
}
701+
catch (COMException)
650702
{
651-
Marshal.ReleaseComObject(_lastExplorerView);
652703
_lastExplorerView = null;
653704
}
654705

Flow.Launcher.Infrastructure/UserSettings/Settings.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ public CustomBrowserViewModel CustomBrowser
232232

233233
public bool EnableQuickSwitch { get; set; } = true;
234234

235-
// TODO: Due to many issues, this option is removed from FL
235+
// TODO: TODO: Due to many issues, this option is removed from FL (see https://github.com/Flow-Launcher/Flow.Launcher/pull/1018)
236236
public bool AutoQuickSwitch { get; set; } = false;
237237

238238
public bool ShowQuickSwitchWindow { get; set; } = true;

Flow.Launcher/App.xaml.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,8 @@ await API.StopwatchLogInfoAsync(ClassName, "Startup cost", async () =>
185185
// main windows needs initialized before theme change because of blur settings
186186
Ioc.Default.GetRequiredService<Theme>().ChangeTheme();
187187

188-
QuickSwitch.Initialize();
188+
QuickSwitch.InitializeQuickSwitch();
189+
QuickSwitch.SetupQuickSwitch(_settings.EnableQuickSwitch);
189190

190191
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
191192

Flow.Launcher/Helper/HotKeyMapper.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ internal static void Initialize()
2323
_settings = Ioc.Default.GetService<Settings>();
2424

2525
SetHotkey(_settings.Hotkey, OnToggleHotkey);
26-
SetHotkey(_settings.QuickSwitchHotkey, QuickSwitch.OnToggleHotkey);
26+
if (_settings.EnableQuickSwitch)
27+
{
28+
SetHotkey(_settings.QuickSwitchHotkey, QuickSwitch.OnToggleHotkey);
29+
}
2730
LoadCustomPluginHotkey();
2831
}
2932

Flow.Launcher/Languages/en.xaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,8 @@
308308
<system:String x:Key="showBadgesGlobalOnlyToolTip">Show badges for global query results only</system:String>
309309
<system:String x:Key="quickSwitchHotkey">Quick Switch</system:String>
310310
<system:String x:Key="quickSwitchHotkeyToolTip">Enter shortcut to quickly navigate the path of a file dialog to the path of the current Explorer.</system:String>
311+
<system:String x:Key="quickSwitch">Quick Switch</system:String>
312+
<system:String x:Key="quickSwitchToolTip">Quickly navigate to the path of the current Explorer when a file dialog is opened.</system:String>
311313
<system:String x:Key="autoQuickSwitch">Quick Switch Automatically</system:String>
312314
<system:String x:Key="autoQuickSwitchToolTip">Quick switch automatically navigate to the path of the current Explorer when a file dialog is opened.</system:String>
313315
<system:String x:Key="showQuickSwitchWindow">Show Quick Switch Window</system:String>

Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using Flow.Launcher.Core.Resource;
99
using Flow.Launcher.Helper;
1010
using Flow.Launcher.Infrastructure;
11+
using Flow.Launcher.Infrastructure.QuickSwitch;
1112
using Flow.Launcher.Infrastructure.UserSettings;
1213
using Flow.Launcher.Plugin;
1314
using Flow.Launcher.Plugin.SharedModels;
@@ -144,6 +145,27 @@ public bool PortableMode
144145
public List<LastQueryModeData> LastQueryModes { get; } =
145146
DropdownDataGeneric<LastQueryMode>.GetValues<LastQueryModeData>("LastQuery");
146147

148+
public bool EnableQuickSwitch
149+
{
150+
get => Settings.EnableQuickSwitch;
151+
set
152+
{
153+
if (Settings.EnableQuickSwitch != value)
154+
{
155+
Settings.EnableQuickSwitch = value;
156+
QuickSwitch.SetupQuickSwitch(value);
157+
if (Settings.EnableQuickSwitch)
158+
{
159+
HotKeyMapper.SetHotkey(new(Settings.QuickSwitchHotkey), QuickSwitch.OnToggleHotkey);
160+
}
161+
else
162+
{
163+
HotKeyMapper.RemoveHotkey(Settings.QuickSwitchHotkey);
164+
}
165+
}
166+
}
167+
}
168+
147169
public int SearchDelayTimeValue
148170
{
149171
get => Settings.SearchDelayTime;

Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@ private void SetTogglingHotkey(HotkeyModel hotkey)
3838
[RelayCommand]
3939
private void SetQuickSwitchHotkey(HotkeyModel hotkey)
4040
{
41-
HotKeyMapper.SetHotkey(hotkey, QuickSwitch.OnToggleHotkey);
41+
if (Settings.EnableQuickSwitch)
42+
{
43+
HotKeyMapper.SetHotkey(hotkey, QuickSwitch.OnToggleHotkey);
44+
}
4245
}
4346

4447
[RelayCommand]

Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,40 @@
202202
</cc:Card>
203203
</cc:CardGroup>
204204

205+
<cc:ExCard
206+
Title="{DynamicResource quickSwitch}"
207+
Margin="0 14 0 0"
208+
Icon="&#xE8AB;"
209+
Sub="{DynamicResource quickSwitchToolTip}">
210+
<cc:ExCard.SideContent>
211+
<ui:ToggleSwitch
212+
IsOn="{Binding EnableQuickSwitch}"
213+
OffContent="{DynamicResource disable}"
214+
OnContent="{DynamicResource enable}" />
215+
</cc:ExCard.SideContent>
216+
217+
<!-- TODO: Due to many issues, this option is removed from FL (see https://github.com/Flow-Launcher/Flow.Launcher/pull/1018) -->
218+
<!--<cc:Card
219+
Title="{DynamicResource autoQuickSwitch}"
220+
Sub="{DynamicResource autoQuickSwitchToolTip}"
221+
Type="InsideFit">
222+
<ui:ToggleSwitch
223+
IsOn="{Binding Settings.AutoQuickSwitch}"
224+
OffContent="{DynamicResource disable}"
225+
OnContent="{DynamicResource enable}" />
226+
</cc:Card>-->
227+
228+
<cc:Card
229+
Title="{DynamicResource showQuickSwitchWindow}"
230+
Sub="{DynamicResource showQuickSwitchWindowToolTip}"
231+
Type="InsideFit">
232+
<ui:ToggleSwitch
233+
IsOn="{Binding Settings.ShowQuickSwitchWindow}"
234+
OffContent="{DynamicResource disable}"
235+
OnContent="{DynamicResource enable}" />
236+
</cc:Card>
237+
</cc:ExCard>
238+
205239
<cc:ExCard
206240
Title="{DynamicResource searchDelay}"
207241
Margin="0 14 0 0"

0 commit comments

Comments
 (0)