From 196b454ae8751f5b158c4aa5e96d2836eb012c0f Mon Sep 17 00:00:00 2001 From: Nathan Baulch Date: Wed, 11 Jun 2025 17:35:43 +1000 Subject: [PATCH] feature: support delete key everywhere (#1412) --- src/ViewModels/Repository.cs | 18 ++++++++++++++++ src/ViewModels/StashesPage.cs | 6 ++++++ src/Views/BranchTree.axaml | 1 + src/Views/BranchTree.axaml.cs | 38 ++++++++++++++++++++++++++++++++++ src/Views/StashesPage.axaml | 1 + src/Views/StashesPage.axaml.cs | 16 ++++++++++++++ src/Views/TagsView.axaml | 2 ++ src/Views/TagsView.axaml.cs | 12 +++++++++++ src/Views/ViewLogs.axaml | 1 + src/Views/ViewLogs.axaml.cs | 13 ++++++++++++ src/Views/Welcome.axaml.cs | 30 +++++++++++++++++---------- 11 files changed, 127 insertions(+), 11 deletions(-) diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index 44fbffc5..7f76dc2a 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -1359,6 +1359,18 @@ namespace SourceGit.ViewModels } } + public void DeleteBranch(Models.Branch branch) + { + if (CanCreatePopup()) + ShowPopup(new DeleteBranch(this, branch)); + } + + public void DeleteRemote(Models.Remote remote) + { + if (CanCreatePopup()) + ShowPopup(new DeleteRemote(this, remote)); + } + public void DeleteMultipleBranches(List branches, bool isLocal) { if (CanCreatePopup()) @@ -1383,6 +1395,12 @@ namespace SourceGit.ViewModels ShowPopup(new CreateTag(this, _currentBranch)); } + public void DeleteTag(Models.Tag tag) + { + if (CanCreatePopup()) + ShowPopup(new DeleteTag(this, tag)); + } + public void AddRemote() { if (CanCreatePopup()) diff --git a/src/ViewModels/StashesPage.cs b/src/ViewModels/StashesPage.cs index eba8ac28..6cdf9f45 100644 --- a/src/ViewModels/StashesPage.cs +++ b/src/ViewModels/StashesPage.cs @@ -314,6 +314,12 @@ namespace SourceGit.ViewModels } } + public void Drop(Models.Stash stash) + { + if (_repo.CanCreatePopup()) + _repo.ShowPopup(new DropStash(_repo, stash)); + } + private Repository _repo = null; private List _stashes = []; private List _visibleStashes = []; diff --git a/src/Views/BranchTree.axaml b/src/Views/BranchTree.axaml index dad4f820..151b41bf 100644 --- a/src/Views/BranchTree.axaml +++ b/src/Views/BranchTree.axaml @@ -13,6 +13,7 @@ ItemsSource="{Binding #ThisControl.Rows}" SelectionMode="Multiple" SelectionChanged="OnNodesSelectionChanged" + KeyDown="OnListKeyDown" ContextRequested="OnTreeContextRequested"> diff --git a/src/Views/BranchTree.axaml.cs b/src/Views/BranchTree.axaml.cs index d8fe7903..45cc8608 100644 --- a/src/Views/BranchTree.axaml.cs +++ b/src/Views/BranchTree.axaml.cs @@ -450,6 +450,44 @@ namespace SourceGit.Views } } + private void OnListKeyDown(object _, KeyEventArgs e) + { + if (e.Key is not (Key.Delete or Key.Back)) + return; + + var repo = DataContext as ViewModels.Repository; + if (repo?.Settings == null) + return; + + var selected = BranchesPresenter.SelectedItems; + if (selected == null || selected.Count == 0) + return; + + if (selected is [ViewModels.BranchTreeNode { Backend: Models.Remote remote }]) + { + repo.DeleteRemote(remote); + e.Handled = true; + return; + } + + var branches = new List(); + foreach (var item in selected) + { + if (item is ViewModels.BranchTreeNode node) + CollectBranchesInNode(branches, node); + } + + if (branches.Find(x => x.IsCurrent) != null) + return; + + if (branches.Count == 1) + repo.DeleteBranch(branches[0]); + else + repo.DeleteMultipleBranches(branches, branches[0].IsLocal); + + e.Handled = true; + } + private void OnDoubleTappedBranchNode(object sender, TappedEventArgs _) { if (sender is Grid { DataContext: ViewModels.BranchTreeNode node }) diff --git a/src/Views/StashesPage.axaml b/src/Views/StashesPage.axaml index 15427b93..34913973 100644 --- a/src/Views/StashesPage.axaml +++ b/src/Views/StashesPage.axaml @@ -65,6 +65,7 @@ ItemsSource="{Binding VisibleStashes}" SelectedItem="{Binding SelectedStash, Mode=TwoWay}" SelectionMode="Single" + KeyDown="OnStashKeyDown" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto"> diff --git a/src/Views/StashesPage.axaml.cs b/src/Views/StashesPage.axaml.cs index 461f9d5d..f95d0b98 100644 --- a/src/Views/StashesPage.axaml.cs +++ b/src/Views/StashesPage.axaml.cs @@ -1,4 +1,5 @@ using Avalonia.Controls; +using Avalonia.Input; namespace SourceGit.Views { @@ -33,6 +34,21 @@ namespace SourceGit.Views e.Handled = true; } + private void OnStashKeyDown(object sender, KeyEventArgs e) + { + if (e.Key is not (Key.Delete or Key.Back)) + return; + + if (DataContext is not ViewModels.StashesPage vm) + return; + + if (sender is not ListBox { SelectedValue: Models.Stash stash }) + return; + + vm.Drop(stash); + e.Handled = true; + } + private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e) { if (DataContext is ViewModels.StashesPage vm && sender is Grid grid) diff --git a/src/Views/TagsView.axaml b/src/Views/TagsView.axaml index 0dbe6dae..7dadbaad 100644 --- a/src/Views/TagsView.axaml +++ b/src/Views/TagsView.axaml @@ -23,6 +23,7 @@ @@ -88,6 +89,7 @@ Margin="8,0,0,0" ItemsSource="{Binding Tags}" SelectionMode="Single" + KeyDown="OnKeyDown" SelectionChanged="OnSelectionChanged"> diff --git a/src/Views/TagsView.axaml.cs b/src/Views/TagsView.axaml.cs index 6764c7ea..6bd312f3 100644 --- a/src/Views/TagsView.axaml.cs +++ b/src/Views/TagsView.axaml.cs @@ -214,6 +214,18 @@ namespace SourceGit.Views if (selectedTag != null) RaiseEvent(new RoutedEventArgs(SelectionChangedEvent)); } + + private void OnKeyDown(object sender, KeyEventArgs e) + { + if (sender is not ListBox { SelectedValue: Models.Tag tag }) + return; + + if (DataContext is not ViewModels.Repository repo) + return; + + repo.DeleteTag(tag); + e.Handled = true; + } } } diff --git a/src/Views/ViewLogs.axaml b/src/Views/ViewLogs.axaml index 1fb44d66..07d0048e 100644 --- a/src/Views/ViewLogs.axaml +++ b/src/Views/ViewLogs.axaml @@ -53,6 +53,7 @@ ItemsSource="{Binding Logs}" SelectedItem="{Binding SelectedLog, Mode=TwoWay}" SelectionMode="Single" + KeyDown="OnLogKeyDown" Grid.IsSharedSizeScope="True" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto"> diff --git a/src/Views/ViewLogs.axaml.cs b/src/Views/ViewLogs.axaml.cs index f0a27884..0ed24449 100644 --- a/src/Views/ViewLogs.axaml.cs +++ b/src/Views/ViewLogs.axaml.cs @@ -1,4 +1,5 @@ using Avalonia.Controls; +using Avalonia.Input; namespace SourceGit.Views { @@ -39,5 +40,17 @@ namespace SourceGit.Views e.Handled = true; } + + private void OnLogKeyDown(object _, KeyEventArgs e) + { + if (e.Key is not (Key.Delete or Key.Back)) + return; + + if (DataContext is not ViewModels.ViewLogs vm) + return; + + vm.Logs.Remove(vm.SelectedLog); + e.Handled = true; + } } } diff --git a/src/Views/Welcome.axaml.cs b/src/Views/Welcome.axaml.cs index 3ab11782..3a69247a 100644 --- a/src/Views/Welcome.axaml.cs +++ b/src/Views/Welcome.axaml.cs @@ -94,20 +94,28 @@ namespace SourceGit.Views private void OnTreeViewKeyDown(object _, KeyEventArgs e) { - if (TreeContainer.SelectedItem is ViewModels.RepositoryNode node && e.Key == Key.Enter) + if (TreeContainer.SelectedItem is ViewModels.RepositoryNode node) { - if (node.IsRepository) + if (e.Key == Key.Enter) { - var parent = this.FindAncestorOfType(); - if (parent is { DataContext: ViewModels.Launcher launcher }) - launcher.OpenRepositoryInTab(node, null); - } - else - { - ViewModels.Welcome.Instance.ToggleNodeIsExpanded(node); - } + if (node.IsRepository) + { + var parent = this.FindAncestorOfType(); + if (parent is { DataContext: ViewModels.Launcher launcher }) + launcher.OpenRepositoryInTab(node, null); + } + else + { + ViewModels.Welcome.Instance.ToggleNodeIsExpanded(node); + } - e.Handled = true; + e.Handled = true; + } + else if (e.Key is Key.Delete or Key.Back) + { + node.Delete(); + e.Handled = true; + } } }