Make drag-and-drop follow Windows Explorer and refresh the tree after folder operations.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-23 16:33:49 +02:00
parent 09a8cfafa3
commit d79605cde9
15 changed files with 879 additions and 58 deletions

View File

@@ -75,12 +75,17 @@ public sealed class NavigationTreeViewModel
Roots.Add(thisPc);
var sources = await _sources.RefreshOnlineStateAsync(cancellationToken).ConfigureAwait(true);
foreach (var source in sources.Where(s => !s.Kind.IsNetwork()))
foreach (var source in sources.Where(s => !s.Kind.IsNetwork())
.OrderBy(s => PathRules.DriveLetterSortKey(s.LastRootPath))
.ThenBy(s => s.DisplayName, StringComparer.CurrentCultureIgnoreCase))
{
thisPc.Children.Add(CreateSourceNode(source, _sources.CanForget(source)));
}
var network = sources.Where(s => s.Kind.IsNetwork()).ToList();
var network = sources.Where(s => s.Kind.IsNetwork())
.OrderBy(s => PathRules.DriveLetterSortKey(s.LastRootPath))
.ThenBy(s => s.DisplayName, StringComparer.CurrentCultureIgnoreCase)
.ToList();
if (prefs.GroupNetworkPlaces && network.Count > 0)
{
var group = new NavNodeViewModel
@@ -174,7 +179,7 @@ public sealed class NavigationTreeViewModel
{
var child = new NavNodeViewModel
{
Label = dir.Name,
Label = dir.DisplayName ?? dir.Name,
Path = dir.FullPath
};
AddPlaceholder(child);
@@ -184,6 +189,78 @@ public sealed class NavigationTreeViewModel
node.ChildrenLoaded = true;
}
public async Task RefreshAfterChangesAsync(IEnumerable<string> directories)
{
var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var raw in directories)
{
if (string.IsNullOrWhiteSpace(raw) || LocationRoots.IsVirtual(raw))
{
continue;
}
var dir = PathRules.EnsureDirectoryTrailingSlashIfRoot(PathRules.FromExtended(raw).TrimEnd('\\'));
if (!seen.Add(dir))
{
continue;
}
var node = FindByPath(Roots, dir);
if (node is null || !node.ChildrenLoaded)
{
continue;
}
await SyncChildrenAsync(node).ConfigureAwait(true);
}
}
private async Task SyncChildrenAsync(NavNodeViewModel node)
{
if (node.IsPlaceholder || node.IsGroup || LocationRoots.IsVirtual(node.Path))
{
return;
}
var listing = await _browse.ListAsync(node.Path).ConfigureAwait(true);
var dirs = listing.Items
.Where(i => i.IsDirectory)
.OrderBy(i => i.Name, StringComparer.CurrentCultureIgnoreCase)
.Take(200)
.ToList();
var previous = node.Children.Where(c => !c.IsPlaceholder).ToList();
var next = new List<NavNodeViewModel>(dirs.Count);
foreach (var dir in dirs)
{
var match = previous.FirstOrDefault(c => PathsEqual(c.Path, dir.FullPath));
if (match is null)
{
match = new NavNodeViewModel
{
Label = dir.DisplayName ?? dir.Name,
Path = dir.FullPath
};
AddPlaceholder(match);
}
else
{
match.Label = dir.DisplayName ?? dir.Name;
match.Path = dir.FullPath;
}
next.Add(match);
}
node.Children.Clear();
foreach (var child in next)
{
node.Children.Add(child);
}
node.ChildrenLoaded = true;
}
public async Task RevealPathAsync(string path)
{
if (string.IsNullOrWhiteSpace(path) || Roots.Count == 0)