From 163e8cc0a4f70c9d760b3cfb751d2791f512c65d Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 4 Nov 2024 15:31:55 +0800 Subject: [PATCH] feature: add context menu for issue link in commit details panel (#651) Signed-off-by: leo --- src/Models/CustomAction.cs | 2 +- src/Native/Windows.cs | 4 +-- src/Resources/Locales/en_US.axaml | 2 ++ src/Views/Blame.axaml.cs | 2 +- src/Views/BranchCompare.axaml.cs | 2 +- src/Views/BranchTree.axaml.cs | 6 ++--- src/Views/CommitBaseInfo.axaml.cs | 4 +-- src/Views/CommitChanges.axaml.cs | 2 +- src/Views/CommitDetail.axaml.cs | 2 +- src/Views/CommitMessagePresenter.cs | 36 ++++++++++++++++++++++++- src/Views/ContextMenuExtension.cs | 26 ------------------ src/Views/Histories.axaml.cs | 2 +- src/Views/Launcher.axaml.cs | 2 +- src/Views/LauncherTabBar.axaml.cs | 2 +- src/Views/Repository.axaml.cs | 4 +-- src/Views/RepositoryToolbar.axaml.cs | 14 +++++----- src/Views/RevisionCompare.axaml.cs | 2 +- src/Views/RevisionFileTreeView.axaml.cs | 2 +- src/Views/RevisionFiles.axaml.cs | 2 +- src/Views/StashesPage.axaml.cs | 4 +-- src/Views/TagsView.axaml.cs | 2 +- src/Views/TextDiffView.axaml.cs | 2 +- src/Views/Welcome.axaml.cs | 2 +- src/Views/WorkingCopy.axaml.cs | 14 +++++----- 24 files changed, 76 insertions(+), 66 deletions(-) delete mode 100644 src/Views/ContextMenuExtension.cs diff --git a/src/Models/CustomAction.cs b/src/Models/CustomAction.cs index 2a400b02..8452a42d 100644 --- a/src/Models/CustomAction.cs +++ b/src/Models/CustomAction.cs @@ -30,7 +30,7 @@ namespace SourceGit.Models public string Arguments { - get => _arguments; + get => _arguments; set => SetProperty(ref _arguments, value); } diff --git a/src/Native/Windows.cs b/src/Native/Windows.cs index 3c56edd3..48fbb287 100644 --- a/src/Native/Windows.cs +++ b/src/Native/Windows.cs @@ -325,8 +325,8 @@ namespace SourceGit.Native if (localMachine.OpenSubKey(@"SOFTWARE\Classes\VisualStudio.Launcher.sln\CLSID") is Microsoft.Win32.RegistryKey launcher) { // Get actual path to the executable - if (launcher.GetValue(string.Empty) is string CLSID && - localMachine.OpenSubKey(@$"SOFTWARE\Classes\CLSID\{CLSID}\LocalServer32") is Microsoft.Win32.RegistryKey devenv && + if (launcher.GetValue(string.Empty) is string CLSID && + localMachine.OpenSubKey(@$"SOFTWARE\Classes\CLSID\{CLSID}\LocalServer32") is Microsoft.Win32.RegistryKey devenv && devenv.GetValue(string.Empty) is string localServer32) { return localServer32!.Trim('\"'); diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index b9371f2e..15c8b6ee 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -387,6 +387,8 @@ Interactive Rebase Target Branch: On: + Open in Browser + Copy Link ERROR NOTICE Merge Branch diff --git a/src/Views/Blame.axaml.cs b/src/Views/Blame.axaml.cs index 164b89de..d32e4370 100644 --- a/src/Views/Blame.axaml.cs +++ b/src/Views/Blame.axaml.cs @@ -407,8 +407,8 @@ namespace SourceGit.Views var menu = new ContextMenu(); menu.Items.Add(copy); + menu.Open(TextArea.TextView); - TextArea.TextView.OpenContextMenu(menu); e.Handled = true; } diff --git a/src/Views/BranchCompare.axaml.cs b/src/Views/BranchCompare.axaml.cs index 90ec1af5..ca90a180 100644 --- a/src/Views/BranchCompare.axaml.cs +++ b/src/Views/BranchCompare.axaml.cs @@ -15,7 +15,7 @@ namespace SourceGit.Views if (DataContext is ViewModels.BranchCompare vm && sender is ChangeCollectionView view) { var menu = vm.CreateChangeContextMenu(); - view.OpenContextMenu(menu); + menu?.Open(view); } e.Handled = true; diff --git a/src/Views/BranchTree.axaml.cs b/src/Views/BranchTree.axaml.cs index 081160d0..e96b2594 100644 --- a/src/Views/BranchTree.axaml.cs +++ b/src/Views/BranchTree.axaml.cs @@ -374,7 +374,7 @@ namespace SourceGit.Views if (selected.Count == 1 && selected[0] is ViewModels.BranchTreeNode { Backend: Models.Remote remote }) { var menu = repo.CreateContextMenuForRemote(remote); - this.OpenContextMenu(menu); + menu?.Open(this); return; } @@ -391,7 +391,7 @@ namespace SourceGit.Views var menu = branch.IsLocal ? repo.CreateContextMenuForLocalBranch(branch) : repo.CreateContextMenuForRemoteBranch(branch); - this.OpenContextMenu(menu); + menu?.Open(this); } else if (branches.Find(x => x.IsCurrent) == null) { @@ -405,7 +405,7 @@ namespace SourceGit.Views ev.Handled = true; }; menu.Items.Add(deleteMulti); - this.OpenContextMenu(menu); + menu?.Open(this); } } diff --git a/src/Views/CommitBaseInfo.axaml.cs b/src/Views/CommitBaseInfo.axaml.cs index e31ddfba..7992b40d 100644 --- a/src/Views/CommitBaseInfo.axaml.cs +++ b/src/Views/CommitBaseInfo.axaml.cs @@ -68,7 +68,7 @@ namespace SourceGit.Views private void OnOpenWebLink(object sender, RoutedEventArgs e) { - if (DataContext is ViewModels.CommitDetail detail) + if (DataContext is ViewModels.CommitDetail detail && sender is Control control) { var links = WebLinks; if (links.Count > 1) @@ -88,7 +88,7 @@ namespace SourceGit.Views menu.Items.Add(item); } - (sender as Control)?.OpenContextMenu(menu); + menu?.Open(control); } else if (links.Count == 1) { diff --git a/src/Views/CommitChanges.axaml.cs b/src/Views/CommitChanges.axaml.cs index f197bdd5..c3d30018 100644 --- a/src/Views/CommitChanges.axaml.cs +++ b/src/Views/CommitChanges.axaml.cs @@ -16,7 +16,7 @@ namespace SourceGit.Views DataContext is ViewModels.CommitDetail vm) { var menu = vm.CreateChangeContextMenu(selected[0]); - view.OpenContextMenu(menu); + menu?.Open(view); } e.Handled = true; diff --git a/src/Views/CommitDetail.axaml.cs b/src/Views/CommitDetail.axaml.cs index 999d1c07..f0599c66 100644 --- a/src/Views/CommitDetail.axaml.cs +++ b/src/Views/CommitDetail.axaml.cs @@ -26,7 +26,7 @@ namespace SourceGit.Views if (DataContext is ViewModels.CommitDetail detail && sender is Grid grid && grid.DataContext is Models.Change change) { var menu = detail.CreateChangeContextMenu(change); - grid.OpenContextMenu(menu); + menu?.Open(grid); } e.Handled = true; diff --git a/src/Views/CommitMessagePresenter.cs b/src/Views/CommitMessagePresenter.cs index 55e1dfb1..112c1f57 100644 --- a/src/Views/CommitMessagePresenter.cs +++ b/src/Views/CommitMessagePresenter.cs @@ -176,7 +176,41 @@ namespace SourceGit.Views } else { - Native.OS.OpenBrowser(_lastHover.Link); + var point = e.GetCurrentPoint(this); + var link = _lastHover.Link; + + if (point.Properties.IsLeftButtonPressed) + { + Native.OS.OpenBrowser(link); + } + else if (point.Properties.IsRightButtonPressed) + { + var open = new MenuItem(); + open.Header = App.Text("IssueLinkCM.OpenInBrowser"); + open.Icon = App.CreateMenuIcon("Icons.OpenWith"); + open.Click += (_, ev) => + { + ev.Handled = true; + + var parentView = this.FindAncestorOfType(); + if (parentView is { DataContext: ViewModels.CommitDetail detail }) + detail.NavigateTo(link); + }; + + var copy = new MenuItem(); + copy.Header = App.Text("IssueLinkCM.CopyLink"); + copy.Icon = App.CreateMenuIcon("Icons.Copy"); + copy.Click += (_, ev) => + { + App.CopyText(link); + ev.Handled = true; + }; + + var menu = new ContextMenu(); + menu.Items.Add(open); + menu.Items.Add(copy); + menu.Open(this); + } } e.Handled = true; diff --git a/src/Views/ContextMenuExtension.cs b/src/Views/ContextMenuExtension.cs deleted file mode 100644 index 2abcf2b9..00000000 --- a/src/Views/ContextMenuExtension.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.ComponentModel; -using Avalonia.Controls; - -namespace SourceGit.Views -{ - public static class ContextMenuExtension - { - public static void OpenContextMenu(this Control control, ContextMenu menu) - { - if (menu == null) - return; - - menu.PlacementTarget = control; - menu.Closing += OnContextMenuClosing; // Clear context menu because it is dynamic. - - control.ContextMenu = menu; - control.ContextMenu?.Open(); - } - - private static void OnContextMenuClosing(object sender, CancelEventArgs e) - { - if (sender is ContextMenu menu && menu.PlacementTarget != null) - menu.PlacementTarget.ContextMenu = null; - } - } -} diff --git a/src/Views/Histories.axaml.cs b/src/Views/Histories.axaml.cs index 137fd298..43258dd7 100644 --- a/src/Views/Histories.axaml.cs +++ b/src/Views/Histories.axaml.cs @@ -706,7 +706,7 @@ namespace SourceGit.Views if (DataContext is ViewModels.Histories histories && sender is ListBox { SelectedItems: { Count: > 0 } } list) { var menu = histories.MakeContextMenu(list); - list.OpenContextMenu(menu); + menu?.Open(list); } e.Handled = true; } diff --git a/src/Views/Launcher.axaml.cs b/src/Views/Launcher.axaml.cs index b4a44868..99916da3 100644 --- a/src/Views/Launcher.axaml.cs +++ b/src/Views/Launcher.axaml.cs @@ -250,7 +250,7 @@ namespace SourceGit.Views if (sender is Button btn && DataContext is ViewModels.Launcher launcher) { var menu = launcher.CreateContextForWorkspace(); - btn.OpenContextMenu(menu); + menu?.Open(btn); } e.Handled = true; diff --git a/src/Views/LauncherTabBar.axaml.cs b/src/Views/LauncherTabBar.axaml.cs index 3258a09c..f8c9107c 100644 --- a/src/Views/LauncherTabBar.axaml.cs +++ b/src/Views/LauncherTabBar.axaml.cs @@ -234,7 +234,7 @@ namespace SourceGit.Views if (sender is Border border && DataContext is ViewModels.Launcher vm) { var menu = vm.CreateContextForPageTab(border.DataContext as ViewModels.LauncherPage); - border.OpenContextMenu(menu); + menu?.Open(border); } e.Handled = true; diff --git a/src/Views/Repository.axaml.cs b/src/Views/Repository.axaml.cs index 499f5e62..dec3d447 100644 --- a/src/Views/Repository.axaml.cs +++ b/src/Views/Repository.axaml.cs @@ -189,7 +189,7 @@ namespace SourceGit.Views if (sender is ListBox { SelectedItem: Models.Submodule submodule } grid && DataContext is ViewModels.Repository repo) { var menu = repo.CreateContextMenuForSubmodule(submodule.Path); - grid.OpenContextMenu(menu); + menu?.Open(grid); } e.Handled = true; @@ -210,7 +210,7 @@ namespace SourceGit.Views if (sender is ListBox { SelectedItem: Models.Worktree worktree } grid && DataContext is ViewModels.Repository repo) { var menu = repo.CreateContextMenuForWorktree(worktree); - grid.OpenContextMenu(menu); + menu?.Open(grid); } e.Handled = true; diff --git a/src/Views/RepositoryToolbar.axaml.cs b/src/Views/RepositoryToolbar.axaml.cs index 55132620..a4a05dc4 100644 --- a/src/Views/RepositoryToolbar.axaml.cs +++ b/src/Views/RepositoryToolbar.axaml.cs @@ -17,7 +17,7 @@ namespace SourceGit.Views if (sender is Button button && DataContext is ViewModels.Repository repo) { var menu = repo.CreateContextMenuForExternalTools(); - button.OpenContextMenu(menu); + menu?.Open(button); e.Handled = true; } } @@ -72,10 +72,10 @@ namespace SourceGit.Views private void OpenGitFlowMenu(object sender, RoutedEventArgs e) { - if (DataContext is ViewModels.Repository repo) + if (DataContext is ViewModels.Repository repo && sender is Control control) { var menu = repo.CreateContextMenuForGitFlow(); - (sender as Control)?.OpenContextMenu(menu); + menu?.Open(control); } e.Handled = true; @@ -83,10 +83,10 @@ namespace SourceGit.Views private void OpenGitLFSMenu(object sender, RoutedEventArgs e) { - if (DataContext is ViewModels.Repository repo) + if (DataContext is ViewModels.Repository repo && sender is Control control) { var menu = repo.CreateContextMenuForGitLFS(); - (sender as Control)?.OpenContextMenu(menu); + menu?.Open(control); } e.Handled = true; @@ -94,10 +94,10 @@ namespace SourceGit.Views private void OpenCustomActionMenu(object sender, RoutedEventArgs e) { - if (DataContext is ViewModels.Repository repo) + if (DataContext is ViewModels.Repository repo && sender is Control control) { var menu = repo.CreateContextMenuForCustomAction(); - (sender as Control)?.OpenContextMenu(menu); + menu?.Open(control); } e.Handled = true; diff --git a/src/Views/RevisionCompare.axaml.cs b/src/Views/RevisionCompare.axaml.cs index e3ecb2b7..b484b78f 100644 --- a/src/Views/RevisionCompare.axaml.cs +++ b/src/Views/RevisionCompare.axaml.cs @@ -15,7 +15,7 @@ namespace SourceGit.Views if (DataContext is ViewModels.RevisionCompare vm && sender is ChangeCollectionView view) { var menu = vm.CreateChangeContextMenu(); - view.OpenContextMenu(menu); + menu?.Open(view); } e.Handled = true; diff --git a/src/Views/RevisionFileTreeView.axaml.cs b/src/Views/RevisionFileTreeView.axaml.cs index e198f6f0..af9beb7d 100644 --- a/src/Views/RevisionFileTreeView.axaml.cs +++ b/src/Views/RevisionFileTreeView.axaml.cs @@ -229,7 +229,7 @@ namespace SourceGit.Views if (obj.Type != Models.ObjectType.Tree) { var menu = vm.CreateRevisionFileContextMenu(obj); - grid.OpenContextMenu(menu); + menu?.Open(grid); } } diff --git a/src/Views/RevisionFiles.axaml.cs b/src/Views/RevisionFiles.axaml.cs index b76e1360..53c36b1c 100644 --- a/src/Views/RevisionFiles.axaml.cs +++ b/src/Views/RevisionFiles.axaml.cs @@ -95,8 +95,8 @@ namespace SourceGit.Views var menu = new ContextMenu(); menu.Items.Add(copy); + menu.Open(TextArea.TextView); - TextArea.TextView.OpenContextMenu(menu); e.Handled = true; } diff --git a/src/Views/StashesPage.axaml.cs b/src/Views/StashesPage.axaml.cs index f3048889..af32cb2c 100644 --- a/src/Views/StashesPage.axaml.cs +++ b/src/Views/StashesPage.axaml.cs @@ -28,7 +28,7 @@ namespace SourceGit.Views if (DataContext is ViewModels.StashesPage vm && sender is Border border) { var menu = vm.MakeContextMenu(border.DataContext as Models.Stash); - border.OpenContextMenu(menu); + menu?.Open(border); } e.Handled = true; } @@ -38,7 +38,7 @@ namespace SourceGit.Views if (DataContext is ViewModels.StashesPage vm && sender is Grid grid) { var menu = vm.MakeContextMenuForChange(grid.DataContext as Models.Change); - grid.OpenContextMenu(menu); + menu?.Open(grid); } e.Handled = true; } diff --git a/src/Views/TagsView.axaml.cs b/src/Views/TagsView.axaml.cs index 29b591fb..8d4168b2 100644 --- a/src/Views/TagsView.axaml.cs +++ b/src/Views/TagsView.axaml.cs @@ -225,7 +225,7 @@ namespace SourceGit.Views if (selected != null && DataContext is ViewModels.Repository repo) { var menu = repo.CreateContextMenuForTag(selected); - control.OpenContextMenu(menu); + menu?.Open(control); } e.Handled = true; diff --git a/src/Views/TextDiffView.axaml.cs b/src/Views/TextDiffView.axaml.cs index 63833fc1..99e499b0 100644 --- a/src/Views/TextDiffView.axaml.cs +++ b/src/Views/TextDiffView.axaml.cs @@ -589,8 +589,8 @@ namespace SourceGit.Views var menu = new ContextMenu(); menu.Items.Add(copy); + menu.Open(TextArea.TextView); - TextArea.TextView.OpenContextMenu(menu); e.Handled = true; } diff --git a/src/Views/Welcome.axaml.cs b/src/Views/Welcome.axaml.cs index a8045aa9..a292a6ef 100644 --- a/src/Views/Welcome.axaml.cs +++ b/src/Views/Welcome.axaml.cs @@ -117,7 +117,7 @@ namespace SourceGit.Views if (sender is Grid { DataContext: ViewModels.RepositoryNode node } grid) { var menu = ViewModels.Welcome.Instance.CreateContextMenu(node); - grid.OpenContextMenu(menu); + menu?.Open(grid); e.Handled = true; } } diff --git a/src/Views/WorkingCopy.axaml.cs b/src/Views/WorkingCopy.axaml.cs index f64e1a30..df45a7f1 100644 --- a/src/Views/WorkingCopy.axaml.cs +++ b/src/Views/WorkingCopy.axaml.cs @@ -31,27 +31,27 @@ namespace SourceGit.Views { var menu = vm.CreateContextMenuForCommitMessages(); menu.Placement = PlacementMode.TopEdgeAlignedLeft; - button.OpenContextMenu(menu); + menu?.Open(button); e.Handled = true; } } private void OnUnstagedContextRequested(object sender, ContextRequestedEventArgs e) { - if (DataContext is ViewModels.WorkingCopy vm) + if (DataContext is ViewModels.WorkingCopy vm && sender is Control control) { var menu = vm.CreateContextMenuForUnstagedChanges(); - (sender as Control)?.OpenContextMenu(menu); + menu?.Open(control); e.Handled = true; } } private void OnStagedContextRequested(object sender, ContextRequestedEventArgs e) { - if (DataContext is ViewModels.WorkingCopy vm) + if (DataContext is ViewModels.WorkingCopy vm && sender is Control control) { var menu = vm.CreateContextMenuForStagedChanges(); - (sender as Control)?.OpenContextMenu(menu); + menu?.Open(control); e.Handled = true; } } @@ -136,10 +136,10 @@ namespace SourceGit.Views private void OnOpenOpenAIHelper(object sender, RoutedEventArgs e) { - if (DataContext is ViewModels.WorkingCopy vm) + if (DataContext is ViewModels.WorkingCopy vm && sender is Control control) { var menu = vm.CreateContextForOpenAI(); - (sender as Button)?.OpenContextMenu(menu); + menu?.Open(control); } e.Handled = true;