Skip to content

Feature: Enable drag and drop reordering for Pinned Sidebar items#18320

Open
kalmix wants to merge 8 commits into
files-community:mainfrom
kalmix:feature/sidebar-drag-reorder
Open

Feature: Enable drag and drop reordering for Pinned Sidebar items#18320
kalmix wants to merge 8 commits into
files-community:mainfrom
kalmix:feature/sidebar-drag-reorder

Conversation

@kalmix
Copy link
Copy Markdown
Contributor

@kalmix kalmix commented Mar 29, 2026

Resolved / Related Issues

Code Changes

  • Implemented Inline Drag-and-Drop Reordering: Removed the standalone ReorderSidebarItemsDialog entirely and replaced it with native inline drag-and-drop directly within the sidebar.
  • Added Visual Drop Indicators: Updated SidebarItem UI and logic to calculate insertion positions (top, bottom, or center) and display the appropriate visual feedback when hovering during a drag operation.
  • Resolved OS-Level Race Conditions: Implemented a robust FileSystemWatcher in WindowsQuickAccessService to monitor the hidden automaticDestinations-ms file. This guarantees that the Windows Shell has finished asynchronously saving the new pin order before the app triggers a UI refresh, preventing snapping and stuttering.
  • Optimized Widget Rendering: Updated QuickAccessWidgetViewModel to use Items.Move() when diffing and updating reordered items, rather than destroying and rebuilding the UI collection. This eliminates screen flickering during reorders.
  • Refined Exception Handling: Added safe interception for stale OLE payload crashes locally in the sidebar drag-and-drop event handlers. This uses the native SafetyExtensions.IgnoreExceptions helper, strictly targeting COMExceptions to ensure the app doesn't crash during drag race conditions.

Before:

before.mp4

After:

after.mp4

Steps used to test these changes

  1. Opened Files
  2. Pinned multiple folders to the sidebar
  3. Dragged a pinned folder to a different position in the sidebar
  4. Verified the folder drops and stays in the new position naturally
  5. Checked the Home Page Quick Access widget to confirm it matches the new order without flickering or reloading
  6. Opened Windows File Explorer in the background
  7. Started dragging a pinned folder in the sidebar to reorder it, and then quickly grabbed another sidebar item while the first drag was resolving
  8. Confirmed the app securely handles the underlying COMException and no longer crashes
  9. Restarted the app to verify the new pinned folder order persists

@kalmix kalmix force-pushed the feature/sidebar-drag-reorder branch 3 times, most recently from abd09bc to 7456ba8 Compare March 30, 2026 03:12
@yair100
Copy link
Copy Markdown
Member

yair100 commented Apr 12, 2026

@kalmix I haven't reviewed the code yet, but I tested the changes. It works pretty well but I noticed that I can't drag Recycle Bin to a different position. I'm not sure if this is specific to Recycle Bin or if it's because it's the last item in the list.

As a side note, can you remove the old content dialog since it's no longer needed?

@yair100 yair100 requested a review from marcelwgn April 12, 2026 15:59
@yair100 yair100 added the ready for review Pull requests that are ready for review label Apr 12, 2026
@kalmix
Copy link
Copy Markdown
Contributor Author

kalmix commented Apr 12, 2026

@yair100 Sorry, I missed Shell: paths like Recycle Bin. I fixed it now; I also removed the old dialog.

@kalmix kalmix force-pushed the feature/sidebar-drag-reorder branch from 176a3c0 to fff0cd5 Compare April 12, 2026 19:29
@kalmix
Copy link
Copy Markdown
Contributor Author

kalmix commented Apr 13, 2026

I've also updated the "drop on center" behavior to better align with the Explorer. For pinned items, dropping on the center now performs a Create Link action instead of a move operation, while non-pinned items continue to be moved as before. In addition, I added support for drag-and-drop pinning.

@Josh65-2201
Copy link
Copy Markdown
Member

Seems to be no way to drag an item to the first position. I have to drag the item to the second from top and then the first one down.

Also has issues keeping Gallery pined when the list is updated

\SHELL\OgAfAOTpNjks2e5OqFq8FtXqCBkmAAEAJgDvvhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAA\_AAAAPYAycAeDOsAAQAAVgAAADFTUFOmamMoPZXSEbXWAMBP2RjQKQAAACAAAAAAERAAABYAAAAUAB9B6mWI6BwOIE6apu3NAhLIfAAAAAARAAAAGQAAAAATAAAAAAAAIAAAAABgAAAAMVNQU_TuyDAyqOJBqzLjw8oo_SkRAAAAEwAAAAATAAAAAAAAABEAAAAEAAAAAAsAAAD__wAAEQAAAAMAAAAAEwAAAAUAAAARAAAAFAAAAAALAAAAAAAAAAAAAAAxAAAAMVNQU35WhU_w__VNsdmYsxT_BykVAAAACAAAAAAfAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAA=

…ss sync

- Fix sidebar drag-to-top missing item UI virtualization bug by replacing .Move() with a data reset Manager_DataChanged, which allows drag reordering to the very first position.
- Resolve Gallery unpinning failures by explicitly normalizing Windows Virtual Folder PIDL paths (stripping \\SHELL\) and implementing a fallback remove COM verb for virtual shell folders that ignore the standard unpinfromhome verb.
- Eliminate race conditions over Quick Access pinning by introducing a FileSystemWatcher in WaitUntilAsync on the OS automaticDestinations-ms registry.
- Update LocationItem to correctly display friendly hover tooltips for virtual folders instead of exposing their raw path.
@kalmix
Copy link
Copy Markdown
Contributor Author

kalmix commented Apr 18, 2026

@Josh65-2201 Yes, sorry I blocked users from draging to the top because I was running into a UI virtualization bug that left a blank space and also because the file explorer doesn't allow you to do that either but now i've fixed it using Manager_DataChanged() instead of Move(), and since we're already "reseting" the pinned section we can get around that.

Regarding the Gallery issue, thanks for pointing that out, in this case Gallery is an outlier that also causes problem with the current state of Files (try it on the prod app), I gave my best shot at fixing this by normalizing the PIDL paths and also adding a fallback remove COM verb and then to keep everything in sync I added a FileSystemWatcher that tracks the Quick Access registry.

@Josh65-2201
Copy link
Copy Markdown
Member

Working perfectly now.

Comment thread src/Files.App/ViewModels/UserControls/SidebarViewModel.cs Outdated
Copy link
Copy Markdown
Contributor

@marcelwgn marcelwgn left a comment

Choose a reason for hiding this comment

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

As a general thought, why are we now introducing a bunch of try catches for functions that should REALLY not throw and why not leverage the SafetyExtensions for this?

Comment thread src/Files.App/Views/MainPage.xaml.cs Outdated
Comment thread src/Files.App/Utils/Storage/Operations/FilesystemHelpers.cs Outdated
Comment thread src/Files.App.Controls/Sidebar/SidebarView.xaml.cs Outdated
Comment thread src/Files.App.Controls/Sidebar/ISidebarViewModel.cs Outdated
Comment thread src/Files.App.Controls/Sidebar/SidebarView.xaml.cs Outdated
Comment thread src/Files.App/Services/Windows/WindowsQuickAccessService.cs Outdated
Comment thread src/Files.App/ViewModels/UserControls/SidebarViewModel.cs Outdated

case NotifyCollectionChangedAction.Move:
{
if (e.OldItems?.Count == 1 && e.NewItems?.Count == 1)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If this is not the case, our view now just stays outdated?

Comment thread src/Files.App/ViewModels/UserControls/SidebarViewModel.cs Outdated
@kalmix kalmix requested a review from marcelwgn April 27, 2026 00:44
Copy link
Copy Markdown
Contributor

@marcelwgn marcelwgn left a comment

Choose a reason for hiding this comment

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

I think we should only try/catch what we expect to fail, a lot of the try catches catch a more than is necessary/expected to throw, e.g. SideBarItem.cs:440 or SideBarItem.cs:499.

Also regarding all of these try catches, what is the need for the try catches based on? I couldnt find any documentation saying that those are expected to throw and the fact that you introduced them now in a lot of places that we didnt have them before is a bit suspicious to me.

Adding to that, what do you mean by this part in the PR description:

Replaced the previous app-level COM exception workaround [...]

@yair100
Copy link
Copy Markdown
Member

yair100 commented May 31, 2026

@kalmix

@kalmix
Copy link
Copy Markdown
Contributor Author

kalmix commented Jun 2, 2026

I think we should only try/catch what we expect to fail, a lot of the try catches catch a more than is necessary/expected to throw, e.g. SideBarItem.cs:440 or SideBarItem.cs:499.

Also regarding all of these try catches, what is the need for the try catches based on? I couldnt find any documentation saying that those are expected to throw and the fact that you introduced them now in a lot of places that we didnt have them before is a bit suspicious to me.

Adding to that, what do you mean by this part in the PR description:

Replaced the previous app-level COM exception workaround [...]

@marcelwgn You make a very fair point. I just pushed a new commit that completely cleans up all of those broad try-catches.

To answer your question on why those exceptions were happening at all, during drag-and-drop operations (especially when reordering pinned items while the background Quick Access syncs are running) Windows occasionally invalidates the OLE drag payload mid-flight. If the UI tries to read a property like e.GetDeferral() from that stale payload, WinUI throws a hard COMException that crashes the app. Since WinUI doesn't provide a way to check if an external drag object is still valid, catching that specific COMException when reading the payload is the only way to avoid a crash when that race condition triggers. I've added comments in the code to document this so it's clear for anyone reading it in the future.

You were totally right that catching broad Exceptions or wrapping huge blocks of code was the wrong approach. I've switched everything over to use the existing SafetyExtensions.IgnoreExceptions. Now, it only wraps the exact single-line property read that fails, and it strictly targets typeof(COMException) so we aren't swallowing actual bugs anymore. I also removed the broad try-catches that were hiding in the background sync logic.

Let me know what you think of the latest commit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready for review Pull requests that are ready for review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: Replace content dialog with sidebar drag and drop for rearranging pinned items

4 participants