Skip to content

Dialog Jump - Quickly navigate the Open/Save As dialog window #1018

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 282 commits into from
Jul 20, 2025
Merged

Conversation

taooceros
Copy link
Member

@taooceros taooceros commented Feb 10, 2022

Features:

  1. Quickly navigate to the current path of the file manager in the Open/Save As dialog window using customisable Alt+G keyboard shortcut
  2. Display the search window when the Open/Save As dialog window is shown to quickly navigate to file/folder locations
  3. Select position for search window when activated
    a. Floating
    b. Under dialog window
  4. Select behaviour to navigate Open/Save As dialog window to the selected result path
    a. Use left click or Enter key
    b. Use right click
  5. Select behaviour to navigate Open/Save As dialog window when the result is a file path
    a. Fill full path in file name box
    b. Fill full path in file name box and open
    c. Fill directory in path box

API

New API interfaces to enable development by plugin for third party explorers & dialogs. This is for dotnet plugins only.

 public interface IAsyncDialogJump : IFeatures
    {
        /// <summary>
        /// Asynchronous querying for Dialog Jump window
        /// </summary>
        Task<List<DialogJumpResult>> QueryDialogJumpAsync(Query query, CancellationToken token);
    }
public interface IDialogJump : IAsyncDialogJump
    {
        /// <summary>
        /// Querying for Dialog Jump window
        List<DialogJumpResult> QueryDialogJump(Query query);

        Task<List<DialogJumpResult>> IAsyncDialogJump.QueryDialogJumpAsync(Query query, CancellationToken token) => Task.Run(() => QueryDialogJump(query), token);
    }
/// <summary>
/// Describes a result of a <see cref="Query"/> executed by a plugin in Dialog Jump window
/// </summary>
public class DialogJumpResult : Result
    {
        /// <summary>
        /// This holds the path which can be provided by plugin to be navigated to the
        /// file dialog when records in Dialog Jump window is right clicked on a result.
        /// </summary>
        public required string DialogJumpPath { get; init; }

        // ...
    }

Additionally, Explorer plugin already supports Dialog Jump.

Test

  • Dialog Jump hotkey can change and apply.
  • Automatically Dialog Jump can work under most cases (since it is an experimental feature)
  • Show Dialog Jump window
  • Dialog Jump window position can change properly.
  • Dialog Jump window action can change properly.
  • Explorer path will select the active tab.

Todos

  • https://github.com/listary/Listary.FileAppPlugin/blob/master/docs%2FGetting%20Started.md
  • Support opening file path.
  • Dialog Jump automatically issue for save as file dialog from @onesounds: For the Open dialog, the path changes correctly at the time the dialog is opened, even without switching focus to File Explorer.
    However, for the Save As dialog, the path does not apply immediately when the dialog is opened. It only works after switching focus to File Explorer and then returning.
  • Third party explorer.
  • Third party dialog.
  • In auto mode, the target program still closes when using "Open." (Previously reported issue) on some devices & apps from @onesounds.
  • Dialogs pop up on many other dialog windows.
  • When the bottom bar mode is active, if a dialog is closed and Flow is exited without being reopened, the window position is saved based on the bottom bar mode location. On the next launch, Flow opens at the last position used by the bottom bar mode.
  • In the "Save As" dialog, Flow automatically inputs the entire file name, which causes unintended saving.
  • Check if plugin is modified before querying (Check if plugin is modified before querying #3791)

Attribution

We would like to thank @idkidknow, whose plugin Flow.Launcher.Plugin.DirQuickJump also helped inspire and contribute to the development of this feature.

Future

  • Add support for automatic navigation (automatically navigate the Open/Save As dialog window to the path of the last explorer without needing to trigger the Dialog Jump hotkey), this feature can cause some problems and it is hidden from setting panel in this PR.

@taooceros
Copy link
Member Author

Some threading issues seem appearing. Not sure the detailed reason.

@stefnotch
Copy link
Contributor

Okay, my main questions would be

  • What's the intended use-case for this? As in, when does a Flow plugin need to navigate in the actual file explorer, instead of opening a new one with the correct path.
  • Would opening a new file explorer window with the desired path, and closing the old one work? Or does that not handle certain cases? (e.g. the file browser/chooser )

@taooceros
Copy link
Member Author

Okay, my main questions would be

* What's the intended use-case for this? As in, when does a Flow plugin need to navigate in the actual file explorer, instead of opening a new one with the correct path.

I think the major use case is to sync the path of an opened explorer to a open file dialog to select a file easily.

Would opening a new file explorer window with the desired path, and closing the old one work? Or does that not handle certain cases? (e.g. the file browser/chooser )

Sry I don't get the idea.

@stefnotch
Copy link
Contributor

Okay, I understand what this is used for now. I'd have to dig a lot deeper into what the IUIAutomation can do to be able to improve this.

I think the rule of thumb is to avoid sending keyboard events, and instead always use an API if one exists. Keyboard events can be delayed and whatnot.

@taooceros
Copy link
Member Author

Okay, I understand what this is used for now. I'd have to dig a lot deeper into what the IUIAutomation can do to be able to improve this.

I think the rule of thumb is to avoid sending keyboard events, and instead always use an API if one exists. Keyboard events can be delayed and whatnot.

Yeah that's what I would like to see. It is possible to use PInvoke directly without IUIAutomation though, so it will be cool if you are familiar with that as well.

Another thing is the original listary seems implement this feature without changing the textbox and sending an enter signal, so I wonder whether you may have some clues about that.

@stefnotch
Copy link
Contributor

I tried searching for what I could, but that's apparently quite tricky to hook into. So I don't really have a better solution at the moment.

@taooceros
Copy link
Member Author

I tried searching for what I could, but that's apparently quite tricky to hook into. So I don't really have a better solution at the moment.

okay thanks🤣

@stefnotch
Copy link
Contributor

stefnotch commented Aug 17, 2022

There might be a alternate design:

So the file manager has the "quick access" sidebar. Flow could add its own entry there, and that entry always redirects to the currently open folder. An additional advantage might be that it's easier to discover this, compared to a keyboard shortcut.

Screenshot for context:

image

(Note: I have no idea how hard that would be to efficiently pull that off.)

@taooceros
Copy link
Member Author

So you mean to add a entry that redirect to the most recent opened explorer path?🤔Interesting

@stefnotch
Copy link
Contributor

Yep, spot-on.

@taooceros
Copy link
Member Author

Yep, spot-on.

If that's the case, we may be able to create a plugin for it.

@taooceros
Copy link
Member Author

Do you have any docs for that?

@stefnotch
Copy link
Contributor

stefnotch commented Aug 17, 2022

@taooceros I haven't looked into this all that much (just a few cursory google searches)

Programmatic access

Apparently there's a way of programmatically adding folders to the quick access area.

https://stackoverflow.com/questions/30051634/is-it-possible-programmatically-add-folders-to-the-windows-10-quick-access-panel

Special Links folder

https://blogs.msmvps.com/kenlin/2017/06/14/537/

Steps:

  1. Enable a special, built-in folder by setting a value in the system registry. Anything in this folder will land in the "quick access".
  2. Put a shortcut in that folder. (A .lnk shortcut)
  3. And then always update the shortcut's path to point at the currently open file explorer.

Symbolic links or Hardlink

I bet there's some trickery that could be done with those

Extra harddrive

We could add an in-memory harddrive, mount it and provide a single shortcut in there.
This might be a tad tricky though, depending on whether there's an easy API/wrapper or not...

@mcthesw
Copy link

mcthesw commented Oct 15, 2022

Could this be done? I really love this feature.

@VictoriousRaptor VictoriousRaptor linked an issue Nov 2, 2022 that may be closed by this pull request
@VictoriousRaptor VictoriousRaptor linked an issue Nov 27, 2022 that may be closed by this pull request
@stefnotch
Copy link
Contributor

Yet another option would be to add a "switch to" context menu entry

Sort of like how 7zip has a dynamic context menu, except that we'd populate it with the titles of other explorer windows.
image

@stefnotch
Copy link
Contributor

stefnotch commented Jan 8, 2023

Apparently Windows 11 can add files to quick access. That might let us pin a program to quick access

Such a program could then update the list of files in the quick access window.

@rexhibition
Copy link

Really hope we can get the quick switch function :( the Ctrl+G in Listary is so useful

@Jack251970 Jack251970 requested a review from Copilot July 20, 2025 10:37

This comment has been minimized.

@Jack251970
Copy link
Member

@coderabbitai full review

Copy link
Contributor

coderabbitai bot commented Jul 20, 2025

✅ Actions performed

Full review triggered.

Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements a comprehensive "Dialog Jump" feature that allows users to quickly navigate Open/Save As dialogs to file manager paths. The feature provides both manual navigation via customizable hotkeys and automatic search window functionality.

Key changes include:

  • New Dialog Jump hotkey (Alt+G) to navigate dialogs to current file manager path
  • Search window that appears when Open/Save As dialogs are shown for quick navigation
  • Configurable window positioning (under dialog or default position) and interaction behaviors
  • Plugin API for third-party dialog and explorer support

Reviewed Changes

Copilot reviewed 29 out of 29 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
Flow.Launcher.Plugin/Interfaces/IDialogJump*.cs New plugin interfaces for dialog jump functionality
Flow.Launcher.Plugin/DialogJumpResult.cs New result type for dialog jump operations
Flow.Launcher.Infrastructure/DialogJump/ Core dialog jump implementation and Windows-specific handlers
Flow.Launcher/SettingPages/ UI settings for dialog jump configuration
Flow.Launcher/ViewModel/MainViewModel.cs Integration of dialog jump with main application logic
Plugins/Flow.Launcher.Plugin.Explorer/ Explorer plugin dialog jump support

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
Flow.Launcher.Infrastructure/DialogJump/Models/WindowsExplorer.cs (1)

22-23: LGTM! String comparison issue already addressed.

The culture-invariant string comparison using StringComparison.OrdinalIgnoreCase is correctly implemented, addressing the previous review comment about culture-sensitive comparisons.

Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs (1)

467-606: Exception handling is properly implemented in async void method.

The ForegroundChangeCallback method correctly includes comprehensive exception handling with try-catch-finally blocks and proper semaphore release. This addresses the previous review comment about missing exception handling in async void methods.

🧹 Nitpick comments (2)
Flow.Launcher.Infrastructure/DialogJump/Models/WindowsExplorer.cs (2)

47-73: Consider adding COM object cleanup in the enumeration method.

The EnumerateShellWindows method creates COM objects but doesn't explicitly release them. While .NET garbage collection will eventually handle this, explicit cleanup is a best practice for COM interop.

 internal static unsafe void EnumerateShellWindows(Func<object, bool> action)
 {
     // Create an instance of ShellWindows
     var clsidShellWindows = new Guid("9BA05972-F6A8-11CF-A442-00A0C90A8F39"); // ShellWindowsClass
     var iidIShellWindows = typeof(IShellWindows).GUID; // IShellWindows

     var result = PInvoke.CoCreateInstance(
         &clsidShellWindows,
         null,
         CLSCTX.CLSCTX_ALL,
         &iidIShellWindows,
         out var shellWindowsObj);

     if (result.Failed) return;

     var shellWindows = (IShellWindows)shellWindowsObj;

-    // Enumerate the shell windows
-    var count = shellWindows.Count;
-    for (var i = 0; i < count; i++)
+    try
     {
-        if (!action(shellWindows.Item(i)))
+        // Enumerate the shell windows
+        var count = shellWindows.Count;
+        for (var i = 0; i < count; i++)
         {
-            return;
+            if (!action(shellWindows.Item(i)))
+            {
+                return;
+            }
         }
     }
+    finally
+    {
+        Marshal.ReleaseComObject(shellWindows);
+    }
 }

173-187: Consider adding timeout and cancellation support to STA thread execution.

The StartSTAThread method uses thread.Join() without a timeout, which could potentially cause the calling thread to hang indefinitely if the STA thread encounters issues.

-private static bool? StartSTAThread(Func<bool> action)
+private static bool? StartSTAThread(Func<bool> action, int timeoutMs = 5000)
 {
     bool? result = null;
     var thread = new Thread(() =>
     {
         result = action();
     })
     {
         IsBackground = true
     };
     thread.SetApartmentState(ApartmentState.STA);
     thread.Start();
-    thread.Join();
+    if (!thread.Join(timeoutMs))
+    {
+        // Thread didn't complete within timeout
+        return null;
+    }
     return result;
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6084493 and 42c5f9e.

📒 Files selected for processing (5)
  • Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs (1 hunks)
  • Flow.Launcher.Infrastructure/DialogJump/Models/WindowsExplorer.cs (1 hunks)
  • Flow.Launcher.Plugin/Interfaces/IDialogJump.cs (1 hunks)
  • Flow.Launcher.Plugin/Interfaces/IPlugin.cs (1 hunks)
  • Flow.Launcher/ViewModel/MainViewModel.cs (15 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • Flow.Launcher.Plugin/Interfaces/IDialogJump.cs
  • Flow.Launcher/ViewModel/MainViewModel.cs
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: Yusyuriv
PR: Flow-Launcher/Flow.Launcher#3057
File: Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs:0-0
Timestamp: 2024-11-03T07:40:11.014Z
Learning: In Flow Launcher, when using Windows Forms dialogs (e.g., in `JsonRPCPluginSettings.cs`), path validation is enabled by default in `OpenFileDialog` and `FolderBrowserDialog`, preventing users from selecting invalid paths, but it's possible to opt out of this validation on individual dialogs.
Flow.Launcher.Plugin/Interfaces/IPlugin.cs (2)
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3791
File: Flow.Launcher.Core/Plugin/PluginManager.cs:293-295
Timestamp: 2025-07-01T05:46:13.251Z
Learning: In Flow.Launcher.Core/Plugin/PluginManager.cs, when checking if a plugin is modified within the PluginManager class itself, prefer using the internal static PluginModified(string id) method directly rather than going through API.PluginModified() for better performance and architectural design.
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3572
File: Flow.Launcher/App.xaml.cs:214-216
Timestamp: 2025-07-06T12:21:37.947Z
Learning: In Flow Launcher, the UpdatePluginManifestAsync method in PluginsManifest.cs already has comprehensive internal try-catch handling that logs exceptions and returns false on failure rather than throwing, making external try-catch wrappers unnecessary.
Flow.Launcher.Infrastructure/DialogJump/Models/WindowsExplorer.cs (3)
Learnt from: Yusyuriv
PR: Flow-Launcher/Flow.Launcher#3057
File: Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs:0-0
Timestamp: 2024-11-03T07:40:11.014Z
Learning: In Flow Launcher, when using Windows Forms dialogs (e.g., in `JsonRPCPluginSettings.cs`), path validation is enabled by default in `OpenFileDialog` and `FolderBrowserDialog`, preventing users from selecting invalid paths, but it's possible to opt out of this validation on individual dialogs.
Learnt from: Koisu-unavailable
PR: Flow-Launcher/Flow.Launcher#3770
File: Flow.Launcher/ViewModel/MainViewModel.cs:0-0
Timestamp: 2025-06-24T19:06:48.344Z
Learning: In Flow.Launcher's Explorer plugin results, the SubTitle property always contains the directory containing the file. For file results, Title contains the filename and SubTitle contains the parent directory. For directory results, SubTitle contains the directory path itself.
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3672
File: Flow.Launcher/MainWindow.xaml.cs:244-247
Timestamp: 2025-06-08T14:12:21.348Z
Learning: In Flow.Launcher, App.NotifyIcon is created before MainWindow creation, so null checks for App.NotifyIcon are not necessary when accessing it from MainWindow code.
Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs (3)
Learnt from: Yusyuriv
PR: Flow-Launcher/Flow.Launcher#3057
File: Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs:0-0
Timestamp: 2024-11-03T07:40:11.014Z
Learning: In Flow Launcher, when using Windows Forms dialogs (e.g., in `JsonRPCPluginSettings.cs`), path validation is enabled by default in `OpenFileDialog` and `FolderBrowserDialog`, preventing users from selecting invalid paths, but it's possible to opt out of this validation on individual dialogs.
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3572
File: Flow.Launcher/App.xaml.cs:214-216
Timestamp: 2025-07-06T12:21:37.947Z
Learning: In Flow Launcher, the UpdatePluginManifestAsync method in PluginsManifest.cs already has comprehensive internal try-catch handling that logs exceptions and returns false on failure rather than throwing, making external try-catch wrappers unnecessary.
Learnt from: Yusyuriv
PR: Flow-Launcher/Flow.Launcher#3057
File: Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs:0-0
Timestamp: 2024-11-03T07:34:24.926Z
Learning: In Windows Forms dialogs, Windows handles invalid paths and prevents the user from clicking "Ok" if the path is incorrect, so additional path validation is unnecessary.
🧬 Code Graph Analysis (1)
Flow.Launcher.Plugin/Interfaces/IPlugin.cs (1)
Flow.Launcher.Plugin/Interfaces/IDialogJump.cs (2)
  • List (25-25)
  • Task (27-27)
🪛 GitHub Check: Check Spelling
Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs

[warning] 170-170:
PInvoke is not a recognized word. (unrecognized-spelling)


[warning] 165-165:
PInvoke is not a recognized word. (unrecognized-spelling)


[warning] 160-160:
PInvoke is not a recognized word. (unrecognized-spelling)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (11)
Flow.Launcher.Plugin/Interfaces/IPlugin.cs (1)

35-35: LGTM! Proper cancellation token support added.

The addition of the token parameter to Task.Run correctly enables cancellation support for the wrapped synchronous Query method. This change aligns with the async programming best practices and supports the Dialog Jump feature's need for cancellable operations.

This pattern is consistent with the similar implementation in IDialogJump.cs where QueryDialogJumpAsync also passes the cancellation token to Task.Run.

Flow.Launcher.Infrastructure/DialogJump/Models/WindowsExplorer.cs (4)

131-160: Excellent COM object cleanup in STA thread execution.

The code properly handles COM object cleanup with try-finally blocks and Marshal.ReleaseComObject(). This is a best practice for COM interop to prevent memory leaks.


200-208: Dynamic COM access is well-documented and necessary.

The use of dynamic types for accessing COM properties that aren't available in the CSWin32 bindings is well-documented and appears to be the appropriate solution for this scenario.


218-232: Robust path normalization with proper error handling.

The NormalizeLocation method handles various path formats (environment variables, shell paths, GUID paths) and includes proper trimming and normalization. The logic appears comprehensive for the use case.


241-257: COM interface declarations follow proper conventions.

The COM interface declarations use appropriate attributes (InterfaceType, Guid, ComImport) and method signatures. The PreserveSig attribute is correctly used for methods that need to return HRESULT values directly.

Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs (6)

108-136: Solid initialization pattern with proper state management.

The initialization method properly checks for duplicate initialization, sets up collections, and initializes necessary components. The state tracking with the _initialized flag prevents double initialization.


138-305: Comprehensive setup and teardown logic with proper resource management.

The SetupDialogJump method handles both enabling and disabling scenarios properly:

  • Correctly unhooks existing event hooks before setting up new ones
  • Properly clears state when disabling
  • Includes error checking for hook creation failures
  • Uses proper locking mechanisms for thread safety

803-829: Robust path validation with comprehensive format support.

The CheckPath method properly handles various path formats including shell paths, file URIs, and regular file system paths. The exception handling ensures graceful degradation when path validation fails.


892-950: Well-implemented navigation logic with proper synchronization.

The JumpToPathAsync method includes:

  • Timeout handling for foreground window detection
  • Proper lock usage to prevent concurrent navigation
  • Comprehensive error handling with logging
  • Correct state management for auto-switched dialogs

984-1054: Comprehensive disposal pattern ensuring proper cleanup.

The Dispose method properly:

  • Unhooks all event hooks
  • Disposes all explorer and dialog instances
  • Clears collections and resets state
  • Disposes synchronization primitives
  • Stops timers

This is a well-implemented disposal pattern for a complex static class.


98-99: Appropriate use of semaphores for async synchronization.

The use of SemaphoreSlim for both foreground change handling and navigation operations provides proper async-friendly synchronization to prevent race conditions in concurrent operations.

This comment has been minimized.

Copy link
Contributor

coderabbitai bot commented Jul 20, 2025

✅ Actions performed

Full review triggered.

@Flow-Launcher Flow-Launcher deleted a comment from coderabbitai bot Jul 20, 2025
Copy link

@check-spelling-bot Report

🔴 Please review

See the 📂 files view, the 📜action log, or 📝 job summary for details.

❌ Errors Count
❌ forbidden-pattern 3

See ❌ Event descriptions for more information.

Forbidden patterns 🙅 (2)

In order to address this, you could change the content to not match the forbidden patterns (comments before forbidden patterns may help explain why they're forbidden), add patterns for acceptable instances, or adjust the forbidden patterns themselves.

These forbidden patterns matched content:

s.b. workaround(s)

\bwork[- ]arounds?\b

Reject duplicate words

\s([A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})\s\g{-1}\s
If the flagged items are 🤯 false positives

If items relate to a ...

  • binary file (or some other file you wouldn't want to check at all).

    Please add a file path to the excludes.txt file matching the containing file.

    File paths are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your files.

    ^ refers to the file's path from the root of the repository, so ^README\.md$ would exclude README.md (on whichever branch you're using).

  • well-formed pattern.

    If you can write a pattern that would match it,
    try adding it to the patterns.txt file.

    Patterns are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your lines.

    Note that patterns can't match multiline strings.

@jjw24
Copy link
Member

jjw24 commented Jul 20, 2025

@Jack251970 ready to merge?

@Jack251970
Copy link
Member

@Jack251970 ready to merge?

Yeah, all reviews are resolved.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

♻️ Duplicate comments (10)
Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs (1)

287-287: Remove unnecessary null-coalescing operator.

The null-coalescing operation is redundant. Path.GetFileName() can only return null if the input path is null, but if filePath were null, Path.GetExtension() on line 286 would have already thrown an exception.

-            var title = Path.GetFileName(filePath) ?? string.Empty;
+            var title = Path.GetFileName(filePath);
Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml (1)

277-286: Auto Dialog Jump feature is hidden but still present in the UI.

The Auto Dialog Jump card has Visibility="Collapsed" but remains in the XAML markup. If this feature is not ready for production, consider removing this code entirely rather than keeping hidden UI elements.

Flow.Launcher.Infrastructure/DialogJump/Models/WindowsExplorer.cs (2)

75-78: Empty Dispose implementation needs attention.

The empty Dispose method should either contain cleanup logic or be removed if the interface doesn't require it. If COM objects need cleanup, implement proper disposal.

If no cleanup is needed, consider documenting why:

 public void Dispose()
 {
-    
+    // No resources to dispose - COM objects are managed per enumeration
 }

106-109: Empty Dispose implementation.

Similar to the WindowsExplorer class, this empty Dispose method should either implement cleanup logic or document why no cleanup is needed.

Flow.Launcher.Infrastructure/DialogJump/Models/WindowsDialog.cs (2)

36-39: Empty Dispose implementation pattern continues.

This is the third empty Dispose method in the DialogJump models. Consider implementing a base class or documenting the pattern.


110-110: Static InputSimulator field poses thread safety risk.

The static _inputSimulator field is shared across all instances, which could cause thread safety issues if multiple dialog tabs are handled concurrently.

Flow.Launcher/MainWindow.xaml.cs (1)

825-825: Replace forbidden pattern in comment.

The phrase "work around" matches a forbidden pattern in the static analysis rules.

-            // Initialize call twice to work around multi-display alignment issue- https://github.com/Flow-Launcher/Flow.Launcher/issues/2910
+            // Initialize call twice to resolve multi-display alignment issue- https://github.com/Flow-Launcher/Flow.Launcher/issues/2910
Flow.Launcher/Languages/en.xaml (1)

371-392: Consider incorporating previous review suggestions for clarity.

The Dialog Jump localization strings are comprehensive. However, several previous review suggestions for improved clarity haven't been incorporated:

  • Line 372: Consider changing to "Enter shortcut to quickly navigate the Open/Save As dialogue to the path of the current file manager."
  • Line 374: Consider changing to "When Open/Save As dialog opens, quickly navigate to the current path of the file manager."
  • Line 378: Consider changing to "Display quick switch search window when the open/save dialog is shown to quickly navigate to file/folder locations."
Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs (2)

477-477: Suppression attribute has incomplete justification

The justification should explain why async void is necessary here (required for Windows event handling).

-[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD100:Avoid async void methods", Justification = "<Pending>")]
+[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD100:Avoid async void methods", Justification = "Required for Windows event handling. ForegroundChangeCallback is an event handler, and the Windows API mandates the use of this signature.")]

547-558: Remove unnecessary Task.Run wrapper

The Task.Run wrapper around NavigateDialogPathAsync is unnecessary since it's already an async method. The nested try-catch is also redundant given the outer exception handling.

-if (!await Task.Run(async () =>
-{
-    try
-    {
-        return await NavigateDialogPathAsync(hwnd, true);
-    }
-    catch (System.Exception ex)
-    {
-        Log.Exception(ClassName, "Failed to navigate dialog path", ex);
-        return false;
-    }
-}))
+if (!await NavigateDialogPathAsync(hwnd, true))
🧹 Nitpick comments (9)
Flow.Launcher/App.xaml.cs (1)

419-419: Ensure DialogJump disposal is exception-safe.

The DialogJump disposal is correctly placed in the application disposal method. However, consider wrapping it in a try-catch block to prevent disposal exceptions from affecting the overall application shutdown process.

                    _mainWindow?.Dispatcher.Invoke(_mainWindow.Dispose);
                    _mainVM?.Dispose();
-                   DialogJump.Dispose();
+                   try
+                   {
+                       DialogJump.Dispose();
+                   }
+                   catch (Exception ex)
+                   {
+                       API.LogException(ClassName, "Failed to dispose DialogJump", ex);
+                   }
Flow.Launcher.Plugin/DialogJumpResult.cs (2)

17-51: Consider refactoring property copying for maintainability.

The Clone method manually copies all properties from the base Result class. While functionally correct, this approach is error-prone if new properties are added to the Result class in the future.

Consider using reflection or implementing a base CopyFrom method in the Result class to reduce maintenance overhead:

public new DialogJumpResult Clone()
{
-    return new DialogJumpResult
-    {
-        Title = Title,
-        SubTitle = SubTitle,
-        // ... all other properties
-        DialogJumpPath = DialogJumpPath
-    };
+    var cloned = (DialogJumpResult)this.MemberwiseClone();
+    return cloned with { }; // or implement a more sophisticated copy mechanism
}

56-90: Property copying duplication between Clone and From methods.

Both Clone and From methods contain identical property copying logic. This duplication increases maintenance overhead and the risk of inconsistencies.

Consider extracting the property copying logic into a shared method or using a more maintainable approach to avoid duplication.

Flow.Launcher.Infrastructure/DialogJump/DialogJumpPair.cs (2)

7-9: Consider marking properties as required.

Since these properties are used in Equals and GetHashCode, they should be marked as required to prevent null reference issues and ensure proper dictionary key behavior.

-    public IDialogJumpExplorer Plugin { get; init; }
-    public PluginMetadata Metadata { get; init; }
+    public required IDialogJumpExplorer Plugin { get; init; }
+    public required PluginMetadata Metadata { get; init; }

16-32: Implement IEquatable for better performance.

Consider implementing IEquatable<DialogJumpExplorerPair> to improve performance when used as dictionary keys and enable more efficient equality comparisons.

-public class DialogJumpExplorerPair
+public class DialogJumpExplorerPair : IEquatable<DialogJumpExplorerPair>
{
    // ... properties ...

+    public bool Equals(DialogJumpExplorerPair? other)
+    {
+        return other is not null && string.Equals(other.Metadata.ID, Metadata.ID);
+    }

    public override bool Equals(object obj)
    {
-        if (obj is DialogJumpExplorerPair r)
-        {
-            return string.Equals(r.Metadata.ID, Metadata.ID);
-        }
-        else
-        {
-            return false;
-        }
+        return Equals(obj as DialogJumpExplorerPair);
    }
}
Flow.Launcher/MainWindow.xaml.cs (1)

596-596: Fix typo in comments.

The comments have a typo: "Do do handle" should be "Do not handle".

-                    // Do do handle size move event for dialog jump window
+                    // Do not handle size move event for dialog jump window
-                    // Do do handle size move event for Dialog Jump window
+                    // Do not handle size move event for Dialog Jump window

Also applies to: 607-607

Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs (2)

129-130: Consider increasing the timer interval for better performance

A 10ms timer interval means the timer fires 100 times per second, which could impact performance. Consider increasing this to 50-100ms unless the high frequency is specifically required for smooth UI updates.

-_dragMoveTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(10) };
+_dragMoveTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(50) };

695-695: Fix typo in log message

There's a typo in the log message: "Destory" should be "Destroy".

-Log.Debug(ClassName, $"Destory dialog: {hwnd}");
+Log.Debug(ClassName, $"Destroy dialog: {hwnd}");
Flow.Launcher/ViewModel/MainViewModel.cs (1)

1827-2027: Consider extracting Dialog Jump state management in future refactoring.

The Dialog Jump functionality adds significant complexity to MainViewModel with multiple state variables and complex window management logic. For improved maintainability and testability, consider extracting this into a dedicated DialogJumpStateManager service in a future refactoring. This would:

  • Centralize Dialog Jump state management
  • Simplify MainViewModel responsibilities
  • Make the feature easier to unit test
  • Reduce coupling between Dialog Jump and other MainViewModel concerns

@jjw24 jjw24 disabled auto-merge July 20, 2025 11:06
@jjw24 jjw24 enabled auto-merge (squash) July 20, 2025 11:07
@Jack251970
Copy link
Member

All thins are good to go

@jjw24 jjw24 removed bug Something isn't working review in progress Indicates that a review is in progress for this PR listary from listary 20 min review labels Jul 20, 2025
Copy link
Member

@jjw24 jjw24 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯

@jjw24 jjw24 merged commit 4f269d3 into dev Jul 20, 2025
6 checks passed
@jjw24 jjw24 deleted the quickswitch branch July 20, 2025 11:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request kind/ux related to user experience
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Quick Switch like Listary output search result to file selection dialog?
8 participants