Add Git overlay, operation tools, and virtualized preview so large folders stay responsive.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -15,6 +15,7 @@ namespace Explorer.App;
|
||||
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
private DocumentationWindow? _docs;
|
||||
private Point _dragStart;
|
||||
private bool _dragPending;
|
||||
private MouseButton _dragButton;
|
||||
@@ -46,6 +47,7 @@ public partial class MainWindow : Window
|
||||
_wiredVm.InlineRenameRequested += OnInlineRenameRequested;
|
||||
_wiredVm.PropertyChanged += OnViewModelPropertyChanged;
|
||||
RestoreLayout(_wiredVm);
|
||||
_ = _wiredVm.RefreshUndoRenameAsync();
|
||||
}
|
||||
};
|
||||
Closing += (_, _) => PersistLayout();
|
||||
@@ -274,6 +276,37 @@ public partial class MainWindow : Window
|
||||
await Vm.ForgetSourceAsync(path).ConfigureAwait(true);
|
||||
}
|
||||
|
||||
private async void OnOpenRecycleBin(object sender, RoutedEventArgs e)
|
||||
=> await Vm.ActivePane.NavigateAsync(LocationRoots.RecycleBin).ConfigureAwait(true);
|
||||
|
||||
private async void OnEmptyRecycleBin(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (MessageBox.Show(
|
||||
this,
|
||||
"Empty Recycle Bin?\n\nItems will be permanently deleted. This is queued and can be cancelled until it starts.",
|
||||
"Empty Recycle Bin",
|
||||
MessageBoxButton.YesNo,
|
||||
MessageBoxImage.Warning) != MessageBoxResult.Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await Vm.EmptyRecycleBinAsync().ConfigureAwait(true);
|
||||
}
|
||||
|
||||
private async void OnImportWindowsLocation(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var item = Vm.ActivePane.SelectedItems.FirstOrDefault();
|
||||
if (item is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await Vm.ActivePane.OpenItemAsync(item).ConfigureAwait(true);
|
||||
await Vm.Tree.ReloadAsync(item.FullPath).ConfigureAwait(true);
|
||||
Vm.Footer = "Added Windows location. Indexing is optional.";
|
||||
}
|
||||
|
||||
private static IEnumerable<NavNodeViewModel> FlattenTree(NavNodeViewModel node)
|
||||
{
|
||||
yield return node;
|
||||
@@ -584,6 +617,7 @@ public partial class MainWindow : Window
|
||||
if (sender is FrameworkElement { ContextMenu: { } menu })
|
||||
{
|
||||
menu.DataContext = DataContext;
|
||||
_ = FillRunProfileMenuAsync(menu);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1045,7 +1079,6 @@ public partial class MainWindow : Window
|
||||
|
||||
private void OnCloseSearch(object sender, RoutedEventArgs e) => Vm.Search.IsOpen = false;
|
||||
private void OnCloseAnalysis(object sender, RoutedEventArgs e) => Vm.Analysis.Close();
|
||||
private void OnCloseDuplicates(object sender, RoutedEventArgs e) => Vm.Duplicates.IsOpen = false;
|
||||
|
||||
private void OnBuildIndex(object sender, RoutedEventArgs e) => Vm.BuildIndexCommand.Execute(null);
|
||||
|
||||
@@ -1084,6 +1117,12 @@ public partial class MainWindow : Window
|
||||
|
||||
private void OnCtxRename(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (Vm.ActivePane.SelectedItems.Count >= 2)
|
||||
{
|
||||
OnBatchRename(sender, e);
|
||||
return;
|
||||
}
|
||||
|
||||
var item = Vm.ActivePane.SelectedItems.FirstOrDefault();
|
||||
if (item is not null)
|
||||
{
|
||||
@@ -1091,6 +1130,143 @@ public partial class MainWindow : Window
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnBatchRename(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var vm = Vm.CreateBatchRenameViewModel();
|
||||
if (vm is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var dlg = new BatchRenameWindow(vm) { Owner = this };
|
||||
if (dlg.ShowDialog() == true)
|
||||
{
|
||||
Vm.Footer = "Rename queued.";
|
||||
await Vm.RefreshUndoRenameAsync().ConfigureAwait(true);
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnExtractHere(object sender, RoutedEventArgs e)
|
||||
=> await Vm.ExtractSelectedAsync(null).ConfigureAwait(true);
|
||||
|
||||
private async void OnExtractTo(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var picker = new Microsoft.Win32.OpenFolderDialog
|
||||
{
|
||||
Title = "Extract to",
|
||||
Multiselect = false
|
||||
};
|
||||
if (picker.ShowDialog(this) != true || string.IsNullOrWhiteSpace(picker.FolderName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await Vm.ExtractSelectedAsync(picker.FolderName).ConfigureAwait(true);
|
||||
}
|
||||
|
||||
private async void OnCompressZip(object sender, RoutedEventArgs e)
|
||||
=> await Vm.CompressSelectedAsync(ArchiveFormat.Zip).ConfigureAwait(true);
|
||||
|
||||
private async void OnCompressSevenZip(object sender, RoutedEventArgs e)
|
||||
=> await Vm.CompressSelectedAsync(ArchiveFormat.SevenZip).ConfigureAwait(true);
|
||||
|
||||
private async void OnAddToArchive(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var dlg = new Microsoft.Win32.OpenFileDialog
|
||||
{
|
||||
Title = "Add to archive",
|
||||
Filter = "Archives|*.zip;*.7z;*.zipx;*.cbz;*.cb7|All files|*.*",
|
||||
CheckFileExists = true
|
||||
};
|
||||
if (dlg.ShowDialog(this) != true || string.IsNullOrWhiteSpace(dlg.FileName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await Vm.AddSelectedToArchiveAsync(dlg.FileName).ConfigureAwait(true);
|
||||
}
|
||||
|
||||
private async void OnVerifyArchive(object sender, RoutedEventArgs e)
|
||||
=> await Vm.VerifySelectedAsync().ConfigureAwait(true);
|
||||
|
||||
private async void OnFolderSync(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var vm = Vm.CreateFolderSyncViewModel();
|
||||
await vm.LoadAsync().ConfigureAwait(true);
|
||||
var dlg = new FolderSyncWindow(vm) { Owner = this };
|
||||
dlg.ShowDialog();
|
||||
}
|
||||
|
||||
private async void OnOperationProfiles(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var vm = Vm.CreateOperationProfilesViewModel();
|
||||
await vm.LoadAsync().ConfigureAwait(true);
|
||||
var dlg = new OperationProfilesWindow(vm) { Owner = this };
|
||||
dlg.ShowDialog();
|
||||
}
|
||||
|
||||
private void OnOrganizeFolder(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var vm = Vm.CreateReorganizeViewModel();
|
||||
var dlg = new ReorganizeWindow(vm) { Owner = this };
|
||||
dlg.ShowDialog();
|
||||
}
|
||||
|
||||
private async Task FillRunProfileMenuAsync(ContextMenu menu)
|
||||
{
|
||||
var host = menu.Items.OfType<MenuItem>().FirstOrDefault(i => Equals(i.Tag, "RunProfileMenu"));
|
||||
if (host is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
host.Items.Clear();
|
||||
IReadOnlyList<OperationProfile> profiles;
|
||||
try
|
||||
{
|
||||
profiles = await Vm.ListOperationProfilesAsync().ConfigureAwait(true);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
host.Items.Add(new MenuItem { Header = "Could not load profiles", IsEnabled = false });
|
||||
return;
|
||||
}
|
||||
|
||||
if (profiles.Count == 0)
|
||||
{
|
||||
host.Items.Add(new MenuItem { Header = "No profiles yet", IsEnabled = false });
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var profile in profiles)
|
||||
{
|
||||
var item = new MenuItem { Header = profile.Name, Tag = profile.Id };
|
||||
item.Click += OnRunProfile;
|
||||
host.Items.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnRunProfile(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is not MenuItem { Tag: long id })
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var sources = Vm.SelectedRealPaths();
|
||||
if (sources.Count == 0)
|
||||
{
|
||||
Vm.Footer = "Select files or folders to run a profile.";
|
||||
return;
|
||||
}
|
||||
|
||||
var vm = Vm.CreateOperationProfilesViewModel();
|
||||
await vm.LoadAsync().ConfigureAwait(true);
|
||||
await vm.RunOnAsync(id, sources).ConfigureAwait(true);
|
||||
var dlg = new OperationProfilesWindow(vm) { Owner = this };
|
||||
dlg.ShowDialog();
|
||||
}
|
||||
|
||||
private void BeginInlineRenameForPath(string path)
|
||||
{
|
||||
var item = Vm.ActivePane.Items.FirstOrDefault(i => NavigationTreeViewModel.PathsEqual(i.FullPath, path));
|
||||
@@ -1299,6 +1475,24 @@ public partial class MainWindow : Window
|
||||
await Vm.RefreshForgetActionAsync().ConfigureAwait(true);
|
||||
}
|
||||
|
||||
private void OnDocumentation(object sender, RoutedEventArgs e) => ShowDocumentation();
|
||||
|
||||
private void OnAbout(object sender, RoutedEventArgs e)
|
||||
=> new AboutWindow { Owner = this }.ShowDialog();
|
||||
|
||||
private void ShowDocumentation()
|
||||
{
|
||||
if (_docs is { IsVisible: true })
|
||||
{
|
||||
_docs.Activate();
|
||||
return;
|
||||
}
|
||||
|
||||
_docs = new DocumentationWindow { Owner = this };
|
||||
_docs.Closed += (_, _) => _docs = null;
|
||||
_docs.Show();
|
||||
}
|
||||
|
||||
private void OnAddNetwork(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var path = PromptWindow.Ask(this, "Add network location", "Network path (\\\\server\\share):", @"\\");
|
||||
@@ -1350,6 +1544,13 @@ public partial class MainWindow : Window
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.Key == Key.F1)
|
||||
{
|
||||
ShowDocumentation();
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.Key == Key.F5)
|
||||
{
|
||||
await Vm.RefreshAsync().ConfigureAwait(true);
|
||||
|
||||
Reference in New Issue
Block a user