feature: support to open selected file with default editor in CHANGES tab (#1750)

Signed-off-by: leo <longshuang@msn.cn>
This commit is contained in:
leo
2025-08-22 17:04:36 +08:00
parent 85c49b75cd
commit dbedf4bc7b
13 changed files with 161 additions and 64 deletions

View File

@@ -8,11 +8,6 @@ namespace SourceGit.ViewModels
{
public class RevisionCompare : ObservableObject, IDisposable
{
public string RepositoryPath
{
get => _repo;
}
public bool IsLoading
{
get => _isLoading;
@@ -127,6 +122,11 @@ namespace SourceGit.ViewModels
Refresh();
}
public string GetAbsPath(string path)
{
return Native.OS.GetAbsPath(_repo, path);
}
public void SaveAsPatch(string saveTo)
{
Task.Run(async () =>

View File

@@ -9,11 +9,6 @@ namespace SourceGit.ViewModels
{
public class StashesPage : ObservableObject, IDisposable
{
public string RepositoryPath
{
get => _repo.FullPath;
}
public List<Models.Stash> Stashes
{
get => _stashes;
@@ -146,6 +141,11 @@ namespace SourceGit.ViewModels
SearchFilter = string.Empty;
}
public string GetAbsPath(string path)
{
return Native.OS.GetAbsPath(_repo.FullPath, path);
}
public void Apply(Models.Stash stash)
{
if (_repo.CanCreatePopup())

View File

@@ -128,7 +128,8 @@
EnableCompactFolders="{Binding Source={x:Static vm:Preferences.Instance}, Path=EnableCompactFoldersInChangesTree}"
Changes="{Binding VisibleChanges}"
SelectedChanges="{Binding SelectedChanges, Mode=TwoWay}"
ContextRequested="OnChangeContextRequested"/>
ContextRequested="OnChangeContextRequested"
KeyDown="OnChangeCollectionViewKeyDown"/>
</Border>
<!-- Loading Status Icon -->

View File

@@ -83,5 +83,25 @@ namespace SourceGit.Views
e.Handled = true;
}
private async void OnChangeCollectionViewKeyDown(object sender, KeyEventArgs e)
{
if (DataContext is not ViewModels.BranchCompare vm)
return;
if (sender is not ChangeCollectionView { SelectedChanges: { Count: 1 } selectedChanges })
return;
var change = selectedChanges[0];
if (e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control) && e.Key == Key.C)
{
if (e.KeyModifiers.HasFlag(KeyModifiers.Shift))
await App.CopyTextAsync(vm.GetAbsPath(change.Path));
else
await App.CopyTextAsync(change.Path);
e.Handled = true;
}
}
}
}

View File

@@ -33,7 +33,7 @@ namespace SourceGit.Views
{
protected override Type StyleKeyOverride => typeof(ListBox);
protected override async void OnKeyDown(KeyEventArgs e)
protected override void OnKeyDown(KeyEventArgs e)
{
if (SelectedItems is [ViewModels.ChangeTreeNode node])
{
@@ -43,36 +43,6 @@ namespace SourceGit.Views
this.FindAncestorOfType<ChangeCollectionView>()?.ToggleNodeIsExpanded(node);
e.Handled = true;
}
else if (e.Key == Key.C &&
e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control))
{
var path = node.FullPath;
if (e.KeyModifiers.HasFlag(KeyModifiers.Shift))
{
do
{
var repoView = this.FindAncestorOfType<Repository>();
if (repoView is { DataContext: ViewModels.Repository repo })
{
path = Native.OS.GetAbsPath(repo.FullPath, path);
break;
}
var branchCompareView = this.FindAncestorOfType<BranchCompare>();
if (branchCompareView is { DataContext: ViewModels.BranchCompare branchCompare })
{
path = branchCompare.GetAbsPath(path);
break;
}
// NOTE: if there is another window uses ChangeCollectionView, add it here!
} while (false);
}
await App.CopyTextAsync(path);
e.Handled = true;
}
}
if (!e.Handled && e.Key != Key.Space && e.Key != Key.Enter)

View File

@@ -50,7 +50,8 @@
EnableCompactFolders="{Binding Source={x:Static vm:Preferences.Instance}, Path=EnableCompactFoldersInChangesTree}"
Changes="{Binding VisibleChanges}"
SelectedChanges="{Binding SelectedChanges, Mode=TwoWay}"
ContextRequested="OnChangeContextRequested"/>
ContextRequested="OnChangeContextRequested"
KeyDown="OnChangeCollectionViewKeyDown"/>
</Border>
<!-- Summary -->

View File

@@ -1,4 +1,7 @@
using System;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.VisualTree;
namespace SourceGit.Views
@@ -36,5 +39,26 @@ namespace SourceGit.Views
menu.Open(view);
}
}
private async void OnChangeCollectionViewKeyDown(object sender, KeyEventArgs e)
{
if (DataContext is not ViewModels.CommitDetail vm)
return;
if (sender is not ChangeCollectionView { SelectedChanges: { Count: 1 } selectedChanges })
return;
var change = selectedChanges[0];
if (e.Key == Key.C &&
e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control))
{
if (e.KeyModifiers.HasFlag(KeyModifiers.Shift))
await App.CopyTextAsync(vm.GetAbsPath(change.Path));
else
await App.CopyTextAsync(change.Path);
e.Handled = true;
}
}
}
}

View File

@@ -111,6 +111,16 @@ namespace SourceGit.Views
ev.Handled = true;
};
var openWith = new MenuItem();
openWith.Header = App.Text("OpenWith");
openWith.Icon = App.CreateMenuIcon("Icons.OpenWith");
openWith.IsEnabled = change.Index != Models.ChangeState.Deleted;
openWith.Click += async (_, ev) =>
{
await vm.OpenRevisionFileWithDefaultEditorAsync(change.Path);
ev.Handled = true;
};
var fullPath = Native.OS.GetAbsPath(repo.FullPath, change.Path);
var explore = new MenuItem();
explore.Header = App.Text("RevealFile");
@@ -167,6 +177,7 @@ namespace SourceGit.Views
var menu = new ContextMenu();
menu.Items.Add(openWithMerger);
menu.Items.Add(openWith);
menu.Items.Add(explore);
menu.Items.Add(new MenuItem { Header = "-" });
menu.Items.Add(history);
@@ -293,24 +304,30 @@ namespace SourceGit.Views
private async void OnCommitListKeyDown(object sender, KeyEventArgs e)
{
if (DataContext is ViewModels.CommitDetail detail &&
sender is ListBox { SelectedItem: Models.Change change } &&
if (DataContext is not ViewModels.CommitDetail vm)
return;
if (sender is not ListBox { SelectedItem: Models.Change change })
return;
if (e.Key == Key.C &&
e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control))
{
if (e.Key == Key.C)
{
var path = change.Path;
if (e.KeyModifiers.HasFlag(KeyModifiers.Shift))
path = detail.GetAbsPath(path);
if (e.KeyModifiers.HasFlag(KeyModifiers.Shift))
await App.CopyTextAsync(vm.GetAbsPath(change.Path));
else
await App.CopyTextAsync(change.Path);
await App.CopyTextAsync(path);
e.Handled = true;
}
else if (e.Key == Key.D && e.KeyModifiers.HasFlag(KeyModifiers.Shift))
{
detail.OpenChangeInMergeTool(change);
e.Handled = true;
}
e.Handled = true;
return;
}
if (e.Key == Key.D &&
e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control) &&
e.KeyModifiers.HasFlag(KeyModifiers.Shift))
{
vm.OpenChangeInMergeTool(change);
e.Handled = true;
}
}

View File

@@ -100,7 +100,8 @@
EnableCompactFolders="{Binding Source={x:Static vm:Preferences.Instance}, Path=EnableCompactFoldersInChangesTree}"
Changes="{Binding VisibleChanges}"
SelectedChanges="{Binding SelectedChanges, Mode=TwoWay}"
ContextRequested="OnChangeContextRequested"/>
ContextRequested="OnChangeContextRequested"
KeyDown="OnChangeCollectionViewKeyDown"/>
</Border>
<!-- Loading Status Icon -->

View File

@@ -20,9 +20,8 @@ namespace SourceGit.Views
if (DataContext is ViewModels.RevisionCompare { SelectedChanges: { Count: 1 } selected } vm &&
sender is ChangeCollectionView view)
{
var repo = vm.RepositoryPath;
var change = selected[0];
var changeFullPath = Native.OS.GetAbsPath(repo, change.Path);
var changeFullPath = vm.GetAbsPath(change.Path);
var menu = new ContextMenu();
var openWithMerger = new MenuItem();
@@ -105,5 +104,25 @@ namespace SourceGit.Views
e.Handled = true;
}
private async void OnChangeCollectionViewKeyDown(object sender, KeyEventArgs e)
{
if (DataContext is not ViewModels.RevisionCompare vm)
return;
if (sender is not ChangeCollectionView { SelectedChanges: { Count: 1 } selectedChanges })
return;
var change = selectedChanges[0];
if (e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control) && e.Key == Key.C)
{
if (e.KeyModifiers.HasFlag(KeyModifiers.Shift))
await App.CopyTextAsync(vm.GetAbsPath(change.Path));
else
await App.CopyTextAsync(change.Path);
e.Handled = true;
}
}
}
}

View File

@@ -131,7 +131,8 @@
EnableCompactFolders="{Binding Source={x:Static vm:Preferences.Instance}, Path=EnableCompactFoldersInChangesTree}"
Changes="{Binding Changes}"
SelectedChanges="{Binding SelectedChanges, Mode=TwoWay}"
ContextRequested="OnChangeContextRequested"/>
ContextRequested="OnChangeContextRequested"
KeyDown="OnChangeCollectionViewKeyDown"/>
</Border>
</Grid>

View File

@@ -126,8 +126,8 @@ namespace SourceGit.Views
if (DataContext is ViewModels.StashesPage { SelectedChanges: { Count: 1 } selected } vm &&
sender is ChangeCollectionView view)
{
var repo = vm.RepositoryPath;
var change = selected[0];
var fullPath = vm.GetAbsPath(change.Path);
var openWithMerger = new MenuItem();
openWithMerger.Header = App.Text("OpenInExternalMergeTool");
@@ -139,7 +139,6 @@ namespace SourceGit.Views
ev.Handled = true;
};
var fullPath = Native.OS.GetAbsPath(repo, change.Path);
var explore = new MenuItem();
explore.Header = App.Text("RevealFile");
explore.Icon = App.CreateMenuIcon("Icons.Explore");
@@ -192,5 +191,25 @@ namespace SourceGit.Views
e.Handled = true;
}
private async void OnChangeCollectionViewKeyDown(object sender, KeyEventArgs e)
{
if (DataContext is not ViewModels.StashesPage vm)
return;
if (sender is not ChangeCollectionView { SelectedChanges: { Count: 1 } selectedChanges })
return;
var change = selectedChanges[0];
if (e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control) && e.Key == Key.C)
{
if (e.KeyModifiers.HasFlag(KeyModifiers.Shift))
await App.CopyTextAsync(vm.GetAbsPath(change.Path));
else
await App.CopyTextAsync(change.Path);
e.Handled = true;
}
}
}
}

View File

@@ -113,6 +113,18 @@ namespace SourceGit.Views
vm.OpenWithDefaultEditor(vm.SelectedUnstaged[0]);
e.Handled = true;
}
else if (e.Key is Key.C &&
e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control) &&
vm.SelectedUnstaged is { Count: 1 })
{
var change = vm.SelectedUnstaged[0];
if (e.KeyModifiers.HasFlag(KeyModifiers.Shift))
await App.CopyTextAsync(Native.OS.GetAbsPath(vm.Repository.FullPath, change.Path));
else
await App.CopyTextAsync(change.Path);
e.Handled = true;
}
}
}
@@ -134,6 +146,18 @@ namespace SourceGit.Views
vm.OpenWithDefaultEditor(vm.SelectedStaged[0]);
e.Handled = true;
}
else if (e.Key is Key.C &&
e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control) &&
vm.SelectedStaged is { Count: 1 })
{
var change = vm.SelectedStaged[0];
if (e.KeyModifiers.HasFlag(KeyModifiers.Shift))
await App.CopyTextAsync(Native.OS.GetAbsPath(vm.Repository.FullPath, change.Path));
else
await App.CopyTextAsync(change.Path);
e.Handled = true;
}
}
}