Add settings, cloud places, and optional archive-content indexing.

Keep official clients in charge of sync while Explorer can group locations, persist UI prefs, and list zip/rar/7z members without extracting them.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-23 12:16:09 +02:00
parent e9aba73552
commit 09a8cfafa3
57 changed files with 3130 additions and 119 deletions

View File

@@ -3,6 +3,7 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using Explorer.Domain;
using Explorer.Presentation;
using Explorer.Presentation.ViewModels;
@@ -129,6 +130,70 @@ public partial class MainWindow : Window
}
}
private void OnTreeContextOpening(object sender, ContextMenuEventArgs e)
{
var node = FindTreeNode(e.OriginalSource as DependencyObject);
if (node is null || !node.CanRemove)
{
e.Handled = true;
return;
}
node.IsSelected = true;
RemoveLocationMenu.Tag = node.Path;
}
private async void OnRemoveLocation(object sender, RoutedEventArgs e)
{
var path = (sender as FrameworkElement)?.Tag as string
?? RemoveLocationMenu.Tag as string
?? Vm.ActivePane.SelectedItems.FirstOrDefault()?.FullPath
?? (Vm.Tree.Roots.SelectMany(FlattenTree).FirstOrDefault(n => n.IsSelected && n.CanRemove)?.Path);
if (string.IsNullOrWhiteSpace(path))
{
return;
}
var label = Vm.Tree.Roots.SelectMany(FlattenTree).FirstOrDefault(n => NavigationTreeViewModel.PathsEqual(n.Path, path))?.Label
?? path;
var confirm = MessageBox.Show(
this,
$"Remove “{label}” from Explorer?\n\nThe link and its index data will be deleted. Files on disk or the server are not touched.\n\nIf Windows still has this location, it will appear again.",
"Remove from Explorer",
MessageBoxButton.YesNo,
MessageBoxImage.Question);
if (confirm != MessageBoxResult.Yes)
{
return;
}
await Vm.ForgetSourceAsync(path).ConfigureAwait(true);
}
private static IEnumerable<NavNodeViewModel> FlattenTree(NavNodeViewModel node)
{
yield return node;
foreach (var child in node.Children.Where(c => !c.IsPlaceholder).SelectMany(FlattenTree))
{
yield return child;
}
}
private static NavNodeViewModel? FindTreeNode(DependencyObject? origin)
{
while (origin is not null)
{
if (origin is TreeViewItem { DataContext: NavNodeViewModel node })
{
return node;
}
origin = origin is Visual ? VisualTreeHelper.GetParent(origin) : LogicalTreeHelper.GetParent(origin);
}
return null;
}
private async void OnTreeExpanded(object sender, RoutedEventArgs e)
{
if (e.OriginalSource is TreeViewItem { DataContext: NavNodeViewModel node })
@@ -442,6 +507,13 @@ public partial class MainWindow : Window
}
}
private async void OnOpenSettings(object sender, RoutedEventArgs e)
{
var dlg = new SettingsWindow(Vm) { Owner = this };
dlg.ShowDialog();
await Vm.RefreshForgetActionAsync().ConfigureAwait(true);
}
private void OnAddNetwork(object sender, RoutedEventArgs e)
{
var path = PromptWindow.Ask(this, "Add network location", "Network path (\\\\server\\share):", @"\\");
@@ -453,10 +525,19 @@ public partial class MainWindow : Window
}
private async void OnAddOneDrive(object sender, RoutedEventArgs e)
=> await AddCloudFolderAsync("Add OneDrive folder", MainViewModel.OneDriveProviderId).ConfigureAwait(true);
private async void OnAddGoogleDrive(object sender, RoutedEventArgs e)
=> await AddCloudFolderAsync("Add Google Drive folder", MainViewModel.GoogleDriveProviderId).ConfigureAwait(true);
private async void OnAddNextcloud(object sender, RoutedEventArgs e)
=> await AddCloudFolderAsync("Add Nextcloud folder", MainViewModel.NextcloudProviderId).ConfigureAwait(true);
private async Task AddCloudFolderAsync(string title, string providerId)
{
var picker = new Microsoft.Win32.OpenFolderDialog
{
Title = "Add OneDrive folder",
Title = title,
Multiselect = false
};
if (picker.ShowDialog(this) != true || string.IsNullOrWhiteSpace(picker.FolderName))
@@ -464,7 +545,7 @@ public partial class MainWindow : Window
return;
}
await Vm.AddCloudFolderAsync(picker.FolderName).ConfigureAwait(true);
await Vm.AddCloudFolderAsync(picker.FolderName, providerId).ConfigureAwait(true);
}
private async void OnPreviewKeyDown(object sender, KeyEventArgs e)