mirror of
https://fastgit.cc/github.com/sourcegit-scm/sourcegit
synced 2026-04-30 22:01:10 +08:00
feature: enable multiple-selection for changes collection view in Changes Detail/Stash Changes/Branch Compare/Revision Compare (#1826)
Signed-off-by: leo <longshuang@msn.cn>
This commit is contained in:
@@ -72,5 +72,20 @@ namespace SourceGit.Commands
|
||||
Args = $"checkout --no-overlay {revision} -- {file.Quoted()}";
|
||||
return await ExecAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<bool> MultipleFilesWithRevisionAsync(List<string> files, string revision)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
builder
|
||||
.Append("checkout --no-overlay ")
|
||||
.Append(revision)
|
||||
.Append(" --");
|
||||
|
||||
foreach (var f in files)
|
||||
builder.Append(' ').Append(f.Quoted());
|
||||
|
||||
Args = builder.ToString();
|
||||
return await ExecAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,6 +128,13 @@ namespace SourceGit.ViewModels
|
||||
return Native.OS.GetAbsPath(_repo, path);
|
||||
}
|
||||
|
||||
public async Task SaveChangesAsPatchAsync(List<Models.Change> changes, string saveTo)
|
||||
{
|
||||
var succ = await Commands.SaveChangesAsPatch.ProcessRevisionCompareChangesAsync(_repo, changes, _based.Head, _to.Head, saveTo);
|
||||
if (succ)
|
||||
App.SendNotification(_repo, App.Text("SaveAsPatchSuccess"));
|
||||
}
|
||||
|
||||
private void Refresh()
|
||||
{
|
||||
IsLoading = true;
|
||||
|
||||
@@ -239,7 +239,7 @@ namespace SourceGit.ViewModels
|
||||
public async Task ResetToThisRevisionAsync(string path)
|
||||
{
|
||||
var log = _repo.CreateLog($"Reset File to '{_commit.SHA}'");
|
||||
await new Commands.Checkout(_repo.FullPath).Use(log).FileWithRevisionAsync(path, $"{_commit.SHA}");
|
||||
await new Commands.Checkout(_repo.FullPath).Use(log).FileWithRevisionAsync(path, _commit.SHA);
|
||||
log.Complete();
|
||||
}
|
||||
|
||||
@@ -254,6 +254,41 @@ namespace SourceGit.ViewModels
|
||||
log.Complete();
|
||||
}
|
||||
|
||||
public async Task ResetMultipleToThisRevisionAsync(List<Models.Change> changes)
|
||||
{
|
||||
var files = new List<string>();
|
||||
foreach (var c in changes)
|
||||
files.Add(c.Path);
|
||||
|
||||
var log = _repo.CreateLog($"Reset Files to '{_commit.SHA}'");
|
||||
await new Commands.Checkout(_repo.FullPath).Use(log).MultipleFilesWithRevisionAsync(files, _commit.SHA);
|
||||
log.Complete();
|
||||
}
|
||||
|
||||
public async Task ResetMultipleToParentRevisionAsync(List<Models.Change> changes)
|
||||
{
|
||||
var renamed = new List<string>();
|
||||
var modified = new List<string>();
|
||||
|
||||
foreach (var c in changes)
|
||||
{
|
||||
if (c.Index == Models.ChangeState.Renamed)
|
||||
renamed.Add(c.OriginalPath);
|
||||
else
|
||||
modified.Add(c.Path);
|
||||
}
|
||||
|
||||
var log = _repo.CreateLog($"Reset Files to '{_commit.SHA}~1'");
|
||||
|
||||
if (modified.Count > 0)
|
||||
await new Commands.Checkout(_repo.FullPath).Use(log).MultipleFilesWithRevisionAsync(modified, $"{_commit.SHA}~1");
|
||||
|
||||
if (renamed.Count > 0)
|
||||
await new Commands.Checkout(_repo.FullPath).Use(log).MultipleFilesWithRevisionAsync(renamed, $"{_commit.SHA}~1");
|
||||
|
||||
log.Complete();
|
||||
}
|
||||
|
||||
public async Task<List<Models.Object>> GetRevisionFilesUnderFolderAsync(string parentFolder)
|
||||
{
|
||||
return await new Commands.QueryRevisionObjects(_repo.FullPath, _commit.SHA, parentFolder)
|
||||
|
||||
@@ -127,14 +127,11 @@ namespace SourceGit.ViewModels
|
||||
return Native.OS.GetAbsPath(_repo, path);
|
||||
}
|
||||
|
||||
public void SaveAsPatch(string saveTo)
|
||||
public async Task SaveChangesAsPatchAsync(List<Models.Change> changes, string saveTo)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
var succ = await Commands.SaveChangesAsPatch.ProcessRevisionCompareChangesAsync(_repo, _changes, GetSHA(_startPoint), GetSHA(_endPoint), saveTo);
|
||||
if (succ)
|
||||
App.SendNotification(_repo, App.Text("SaveAsPatchSuccess"));
|
||||
});
|
||||
var succ = await Commands.SaveChangesAsPatch.ProcessRevisionCompareChangesAsync(_repo, changes ?? _changes, GetSHA(_startPoint), GetSHA(_endPoint), saveTo);
|
||||
if (succ)
|
||||
App.SendNotification(_repo, App.Text("SaveAsPatchSuccess"));
|
||||
}
|
||||
|
||||
public void ClearSearchFilter()
|
||||
|
||||
@@ -158,7 +158,7 @@ namespace SourceGit.ViewModels
|
||||
_repo.ShowPopup(new DropStash(_repo, stash));
|
||||
}
|
||||
|
||||
public async Task SaveStashAsPathAsync(Models.Stash stash, string saveTo)
|
||||
public async Task SaveStashAsPatchAsync(Models.Stash stash, string saveTo)
|
||||
{
|
||||
var opts = new List<Models.DiffOption>();
|
||||
var changes = await new Commands.CompareRevisions(_repo.FullPath, $"{stash.SHA}^", stash.SHA)
|
||||
@@ -211,6 +211,42 @@ namespace SourceGit.ViewModels
|
||||
log.Complete();
|
||||
}
|
||||
|
||||
public async Task CheckoutMultipleFileAsync(List<Models.Change> changes)
|
||||
{
|
||||
var untracked = new List<string>();
|
||||
var added = new List<string>();
|
||||
var modified = new List<string>();
|
||||
|
||||
foreach (var c in changes)
|
||||
{
|
||||
if (_untracked.Contains(c) && _selectedStash.Parents.Count == 3)
|
||||
untracked.Add(c.Path);
|
||||
else if (c.Index == Models.ChangeState.Added && _selectedStash.Parents.Count > 1)
|
||||
added.Add(c.Path);
|
||||
else
|
||||
modified.Add(c.Path);
|
||||
}
|
||||
|
||||
var log = _repo.CreateLog($"Reset File to '{_selectedStash.Name}'");
|
||||
|
||||
if (untracked.Count > 0)
|
||||
await new Commands.Checkout(_repo.FullPath)
|
||||
.Use(log)
|
||||
.MultipleFilesWithRevisionAsync(untracked, _selectedStash.Parents[2]);
|
||||
|
||||
if (added.Count > 0)
|
||||
await new Commands.Checkout(_repo.FullPath)
|
||||
.Use(log)
|
||||
.MultipleFilesWithRevisionAsync(added, _selectedStash.Parents[1]);
|
||||
|
||||
if (modified.Count > 0)
|
||||
await new Commands.Checkout(_repo.FullPath)
|
||||
.Use(log)
|
||||
.MultipleFilesWithRevisionAsync(modified, _selectedStash.SHA);
|
||||
|
||||
log.Complete();
|
||||
}
|
||||
|
||||
private void RefreshVisible()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_searchFilter))
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Platform.Storage;
|
||||
|
||||
namespace SourceGit.Views
|
||||
{
|
||||
@@ -15,61 +17,127 @@ namespace SourceGit.Views
|
||||
|
||||
private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e)
|
||||
{
|
||||
if (DataContext is ViewModels.BranchCompare { SelectedChanges: { Count: 1 } selected } vm &&
|
||||
if (DataContext is ViewModels.BranchCompare { SelectedChanges: { Count: > 0 } selected } vm &&
|
||||
sender is ChangeCollectionView view)
|
||||
{
|
||||
var repo = vm.RepositoryPath;
|
||||
var change = selected[0];
|
||||
var menu = new ContextMenu();
|
||||
var repo = vm.RepositoryPath;
|
||||
|
||||
var openWithMerger = new MenuItem();
|
||||
openWithMerger.Header = App.Text("OpenInExternalMergeTool");
|
||||
openWithMerger.Icon = App.CreateMenuIcon("Icons.OpenWith");
|
||||
openWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D";
|
||||
openWithMerger.Click += (_, ev) =>
|
||||
var patch = new MenuItem();
|
||||
patch.Header = App.Text("FileCM.SaveAsPatch");
|
||||
patch.Icon = App.CreateMenuIcon("Icons.Diff");
|
||||
patch.Click += async (_, e) =>
|
||||
{
|
||||
new Commands.DiffTool(repo, new Models.DiffOption(vm.Base.Head, vm.To.Head, change)).Open();
|
||||
ev.Handled = true;
|
||||
};
|
||||
menu.Items.Add(openWithMerger);
|
||||
var storageProvider = this.StorageProvider;
|
||||
if (storageProvider == null)
|
||||
return;
|
||||
|
||||
if (change.Index != Models.ChangeState.Deleted)
|
||||
{
|
||||
var full = Path.GetFullPath(Path.Combine(repo, change.Path));
|
||||
var explore = new MenuItem();
|
||||
explore.Header = App.Text("RevealFile");
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Explore");
|
||||
explore.IsEnabled = File.Exists(full);
|
||||
explore.Click += (_, ev) =>
|
||||
var options = new FilePickerSaveOptions();
|
||||
options.Title = App.Text("FileCM.SaveAsPatch");
|
||||
options.DefaultExtension = ".patch";
|
||||
options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }];
|
||||
|
||||
var storageFile = await storageProvider.SaveFilePickerAsync(options);
|
||||
if (storageFile != null)
|
||||
{
|
||||
Native.OS.OpenInFileManager(full, true);
|
||||
var saveTo = storageFile.Path.LocalPath;
|
||||
await vm.SaveChangesAsPatchAsync(selected, saveTo);
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
if (selected.Count == 1)
|
||||
{
|
||||
var change = selected[0];
|
||||
var openWithMerger = new MenuItem();
|
||||
openWithMerger.Header = App.Text("OpenInExternalMergeTool");
|
||||
openWithMerger.Icon = App.CreateMenuIcon("Icons.OpenWith");
|
||||
openWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D";
|
||||
openWithMerger.Click += (_, ev) =>
|
||||
{
|
||||
new Commands.DiffTool(repo, new Models.DiffOption(vm.Base.Head, vm.To.Head, change)).Open();
|
||||
ev.Handled = true;
|
||||
};
|
||||
menu.Items.Add(explore);
|
||||
menu.Items.Add(openWithMerger);
|
||||
|
||||
if (change.Index != Models.ChangeState.Deleted)
|
||||
{
|
||||
var full = Path.GetFullPath(Path.Combine(repo, change.Path));
|
||||
var explore = new MenuItem();
|
||||
explore.Header = App.Text("RevealFile");
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Explore");
|
||||
explore.IsEnabled = File.Exists(full);
|
||||
explore.Click += (_, ev) =>
|
||||
{
|
||||
Native.OS.OpenInFileManager(full, true);
|
||||
ev.Handled = true;
|
||||
};
|
||||
menu.Items.Add(explore);
|
||||
}
|
||||
|
||||
var copyPath = new MenuItem();
|
||||
copyPath.Header = App.Text("CopyPath");
|
||||
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C";
|
||||
copyPath.Click += async (_, ev) =>
|
||||
{
|
||||
await App.CopyTextAsync(change.Path);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var copyFullPath = new MenuItem();
|
||||
copyFullPath.Header = App.Text("CopyFullPath");
|
||||
copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C";
|
||||
copyFullPath.Click += async (_, ev) =>
|
||||
{
|
||||
await App.CopyTextAsync(Native.OS.GetAbsPath(repo, change.Path));
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
menu.Items.Add(patch);
|
||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
menu.Items.Add(copyPath);
|
||||
menu.Items.Add(copyFullPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
var copyPath = new MenuItem();
|
||||
copyPath.Header = App.Text("CopyPath");
|
||||
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C";
|
||||
copyPath.Click += async (_, ev) =>
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
foreach (var c in selected)
|
||||
builder.AppendLine(c.Path);
|
||||
|
||||
await App.CopyTextAsync(builder.ToString());
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var copyFullPath = new MenuItem();
|
||||
copyFullPath.Header = App.Text("CopyFullPath");
|
||||
copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C";
|
||||
copyFullPath.Click += async (_, ev) =>
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
foreach (var c in selected)
|
||||
builder.AppendLine(Native.OS.GetAbsPath(repo, c.Path));
|
||||
|
||||
await App.CopyTextAsync(builder.ToString());
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
menu.Items.Add(patch);
|
||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
menu.Items.Add(copyPath);
|
||||
menu.Items.Add(copyFullPath);
|
||||
}
|
||||
|
||||
var copyPath = new MenuItem();
|
||||
copyPath.Header = App.Text("CopyPath");
|
||||
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C";
|
||||
copyPath.Click += async (_, ev) =>
|
||||
{
|
||||
await App.CopyTextAsync(change.Path);
|
||||
ev.Handled = true;
|
||||
};
|
||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
menu.Items.Add(copyPath);
|
||||
|
||||
var copyFullPath = new MenuItem();
|
||||
copyFullPath.Header = App.Text("CopyFullPath");
|
||||
copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C";
|
||||
copyFullPath.Click += async (_, ev) =>
|
||||
{
|
||||
await App.CopyTextAsync(Native.OS.GetAbsPath(repo, change.Path));
|
||||
ev.Handled = true;
|
||||
};
|
||||
menu.Items.Add(copyFullPath);
|
||||
menu.Open(view);
|
||||
}
|
||||
|
||||
@@ -89,17 +157,24 @@ namespace SourceGit.Views
|
||||
if (DataContext is not ViewModels.BranchCompare vm)
|
||||
return;
|
||||
|
||||
if (sender is not ChangeCollectionView { SelectedChanges: { Count: 1 } selectedChanges })
|
||||
if (sender is not ChangeCollectionView { SelectedChanges: { Count: > 0 } 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));
|
||||
var builder = new StringBuilder();
|
||||
var copyAbsPath = e.KeyModifiers.HasFlag(KeyModifiers.Shift);
|
||||
if (selectedChanges.Count == 1)
|
||||
{
|
||||
builder.Append(copyAbsPath ? vm.GetAbsPath(selectedChanges[0].Path) : selectedChanges[0].Path);
|
||||
}
|
||||
else
|
||||
await App.CopyTextAsync(change.Path);
|
||||
{
|
||||
foreach (var c in selectedChanges)
|
||||
builder.AppendLine(copyAbsPath ? vm.GetAbsPath(c.Path) : c.Path);
|
||||
}
|
||||
|
||||
await App.CopyTextAsync(builder.ToString());
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
<v:ChangeCollectionContainer Focusable="True"
|
||||
ItemsSource="{Binding Rows}"
|
||||
SelectedItems="{Binding SelectedRows, Mode=TwoWay}"
|
||||
SelectionMode="{Binding #ThisControl.SelectionMode}"
|
||||
SelectionMode="Multiple"
|
||||
SelectionChanged="OnRowSelectionChanged">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate DataType="vm:ChangeTreeNode">
|
||||
@@ -78,7 +78,7 @@
|
||||
<v:ChangeCollectionContainer Focusable="True"
|
||||
ItemsSource="{Binding Changes}"
|
||||
SelectedItems="{Binding SelectedChanges, Mode=TwoWay}"
|
||||
SelectionMode="{Binding #ThisControl.SelectionMode}"
|
||||
SelectionMode="Multiple"
|
||||
SelectionChanged="OnRowSelectionChanged">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate DataType="m:Change">
|
||||
@@ -110,7 +110,7 @@
|
||||
<v:ChangeCollectionContainer Focusable="True"
|
||||
ItemsSource="{Binding Changes}"
|
||||
SelectedItems="{Binding SelectedChanges, Mode=TwoWay}"
|
||||
SelectionMode="{Binding #ThisControl.SelectionMode}"
|
||||
SelectionMode="Multiple"
|
||||
SelectionChanged="OnRowSelectionChanged">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate DataType="m:Change">
|
||||
|
||||
@@ -61,15 +61,6 @@ namespace SourceGit.Views
|
||||
set => SetValue(IsUnstagedChangeProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<SelectionMode> SelectionModeProperty =
|
||||
AvaloniaProperty.Register<ChangeCollectionView, SelectionMode>(nameof(SelectionMode));
|
||||
|
||||
public SelectionMode SelectionMode
|
||||
{
|
||||
get => GetValue(SelectionModeProperty);
|
||||
set => SetValue(SelectionModeProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<Models.ChangeViewMode> ViewModeProperty =
|
||||
AvaloniaProperty.Register<ChangeCollectionView, Models.ChangeViewMode>(nameof(ViewMode), Models.ChangeViewMode.Tree);
|
||||
|
||||
|
||||
@@ -45,8 +45,7 @@
|
||||
|
||||
<!-- Changes -->
|
||||
<Border Grid.Row="1" Margin="0,4,0,0" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}">
|
||||
<v:ChangeCollectionView SelectionMode="Single"
|
||||
ViewMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=CommitChangeViewMode}"
|
||||
<v:ChangeCollectionView ViewMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=CommitChangeViewMode}"
|
||||
EnableCompactFolders="{Binding Source={x:Static vm:Preferences.Instance}, Path=EnableCompactFoldersInChangesTree}"
|
||||
Changes="{Binding VisibleChanges}"
|
||||
SelectedChanges="{Binding SelectedChanges, Mode=TwoWay}"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
@@ -17,27 +18,20 @@ namespace SourceGit.Views
|
||||
{
|
||||
e.Handled = true;
|
||||
|
||||
if (sender is not ChangeCollectionView view)
|
||||
if (sender is not ChangeCollectionView { SelectedChanges: { Count: > 0 } changes } view)
|
||||
return;
|
||||
|
||||
var detailView = this.FindAncestorOfType<CommitDetail>();
|
||||
if (detailView == null)
|
||||
return;
|
||||
|
||||
var changes = view.SelectedChanges ?? [];
|
||||
var container = view.FindDescendantOfType<ChangeCollectionContainer>();
|
||||
if (container is { SelectedItems.Count: 1, SelectedItem: ViewModels.ChangeTreeNode { IsFolder: true } node })
|
||||
{
|
||||
var menu = detailView.CreateChangeContextMenuByFolder(node, changes);
|
||||
menu.Open(view);
|
||||
return;
|
||||
}
|
||||
|
||||
if (changes.Count == 1)
|
||||
{
|
||||
var menu = detailView.CreateChangeContextMenu(changes[0]);
|
||||
menu.Open(view);
|
||||
}
|
||||
detailView.CreateChangeContextMenuByFolder(node, changes)?.Open(view);
|
||||
else if (changes.Count > 1)
|
||||
detailView.CreateMultipleChangesContextMenu(changes)?.Open(view);
|
||||
else
|
||||
detailView.CreateChangeContextMenu(changes[0])?.Open(view);
|
||||
}
|
||||
|
||||
private async void OnChangeCollectionViewKeyDown(object sender, KeyEventArgs e)
|
||||
@@ -45,18 +39,30 @@ namespace SourceGit.Views
|
||||
if (DataContext is not ViewModels.CommitDetail vm)
|
||||
return;
|
||||
|
||||
if (sender is not ChangeCollectionView { SelectedChanges: { Count: 1 } selectedChanges })
|
||||
if (sender is not ChangeCollectionView { SelectedChanges: { Count: > 0 } selectedChanges } view)
|
||||
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));
|
||||
var builder = new StringBuilder();
|
||||
var copyAbsPath = e.KeyModifiers.HasFlag(KeyModifiers.Shift);
|
||||
var container = view.FindDescendantOfType<ChangeCollectionContainer>();
|
||||
if (container is { SelectedItems.Count: 1, SelectedItem: ViewModels.ChangeTreeNode { IsFolder: true } node })
|
||||
{
|
||||
builder.Append(copyAbsPath ? vm.GetAbsPath(node.FullPath) : node.FullPath);
|
||||
}
|
||||
else if (selectedChanges.Count == 1)
|
||||
{
|
||||
builder.Append(copyAbsPath ? vm.GetAbsPath(selectedChanges[0].Path) : selectedChanges[0].Path);
|
||||
}
|
||||
else
|
||||
await App.CopyTextAsync(change.Path);
|
||||
{
|
||||
foreach (var c in selectedChanges)
|
||||
builder.AppendLine(copyAbsPath ? vm.GetAbsPath(c.Path) : c.Path);
|
||||
}
|
||||
|
||||
await App.CopyTextAsync(builder.ToString());
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
@@ -97,6 +98,98 @@ namespace SourceGit.Views
|
||||
return menu;
|
||||
}
|
||||
|
||||
public ContextMenu CreateMultipleChangesContextMenu(List<Models.Change> changes)
|
||||
{
|
||||
if (DataContext is not ViewModels.CommitDetail { Repository: { } repo, Commit: { } commit } vm)
|
||||
return null;
|
||||
|
||||
var patch = new MenuItem();
|
||||
patch.Header = App.Text("FileCM.SaveAsPatch");
|
||||
patch.Icon = App.CreateMenuIcon("Icons.Diff");
|
||||
patch.Click += async (_, e) =>
|
||||
{
|
||||
var storageProvider = TopLevel.GetTopLevel(this)?.StorageProvider;
|
||||
if (storageProvider == null)
|
||||
return;
|
||||
|
||||
var options = new FilePickerSaveOptions();
|
||||
options.Title = App.Text("FileCM.SaveAsPatch");
|
||||
options.DefaultExtension = ".patch";
|
||||
options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }];
|
||||
|
||||
var storageFile = await storageProvider.SaveFilePickerAsync(options);
|
||||
if (storageFile != null)
|
||||
{
|
||||
var saveTo = storageFile.Path.LocalPath;
|
||||
await vm.SaveChangesAsPatchAsync(changes, saveTo);
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
var menu = new ContextMenu();
|
||||
menu.Items.Add(patch);
|
||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
|
||||
if (!repo.IsBare)
|
||||
{
|
||||
var resetToThisRevision = new MenuItem();
|
||||
resetToThisRevision.Header = App.Text("ChangeCM.CheckoutThisRevision");
|
||||
resetToThisRevision.Icon = App.CreateMenuIcon("Icons.File.Checkout");
|
||||
resetToThisRevision.Click += async (_, ev) =>
|
||||
{
|
||||
await vm.ResetMultipleToThisRevisionAsync(changes);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var resetToFirstParent = new MenuItem();
|
||||
resetToFirstParent.Header = App.Text("ChangeCM.CheckoutFirstParentRevision");
|
||||
resetToFirstParent.Icon = App.CreateMenuIcon("Icons.File.Checkout");
|
||||
resetToFirstParent.IsEnabled = commit.Parents.Count > 0;
|
||||
resetToFirstParent.Click += async (_, ev) =>
|
||||
{
|
||||
await vm.ResetMultipleToParentRevisionAsync(changes);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
menu.Items.Add(resetToThisRevision);
|
||||
menu.Items.Add(resetToFirstParent);
|
||||
menu.Items.Add(new MenuItem { Header = "-" });
|
||||
}
|
||||
|
||||
var copyPath = new MenuItem();
|
||||
copyPath.Header = App.Text("CopyPath");
|
||||
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C";
|
||||
copyPath.Click += async (_, ev) =>
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
foreach (var c in changes)
|
||||
builder.AppendLine(c.Path);
|
||||
|
||||
await App.CopyTextAsync(builder.ToString());
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var copyFullPath = new MenuItem();
|
||||
copyFullPath.Header = App.Text("CopyFullPath");
|
||||
copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C";
|
||||
copyFullPath.Click += async (_, e) =>
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
foreach (var c in changes)
|
||||
builder.AppendLine(Native.OS.GetAbsPath(repo.FullPath, c.Path));
|
||||
|
||||
await App.CopyTextAsync(builder.ToString());
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
menu.Items.Add(copyPath);
|
||||
menu.Items.Add(copyFullPath);
|
||||
return menu;
|
||||
}
|
||||
|
||||
public ContextMenu CreateChangeContextMenu(Models.Change change)
|
||||
{
|
||||
if (DataContext is not ViewModels.CommitDetail { Repository: { } repo, Commit: { } commit } vm)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
@@ -17,59 +18,126 @@ namespace SourceGit.Views
|
||||
|
||||
private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e)
|
||||
{
|
||||
if (DataContext is ViewModels.RevisionCompare { SelectedChanges: { Count: 1 } selected } vm &&
|
||||
if (DataContext is ViewModels.RevisionCompare { SelectedChanges: { Count: > 0 } selected } vm &&
|
||||
sender is ChangeCollectionView view)
|
||||
{
|
||||
var change = selected[0];
|
||||
var changeFullPath = vm.GetAbsPath(change.Path);
|
||||
var menu = new ContextMenu();
|
||||
|
||||
var openWithMerger = new MenuItem();
|
||||
openWithMerger.Header = App.Text("OpenInExternalMergeTool");
|
||||
openWithMerger.Icon = App.CreateMenuIcon("Icons.OpenWith");
|
||||
openWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D";
|
||||
openWithMerger.Click += (_, ev) =>
|
||||
var patch = new MenuItem();
|
||||
patch.Header = App.Text("FileCM.SaveAsPatch");
|
||||
patch.Icon = App.CreateMenuIcon("Icons.Diff");
|
||||
patch.Click += async (_, e) =>
|
||||
{
|
||||
vm.OpenChangeWithExternalDiffTool(change);
|
||||
ev.Handled = true;
|
||||
};
|
||||
menu.Items.Add(openWithMerger);
|
||||
var storageProvider = TopLevel.GetTopLevel(this)?.StorageProvider;
|
||||
if (storageProvider == null)
|
||||
return;
|
||||
|
||||
if (change.Index != Models.ChangeState.Deleted)
|
||||
{
|
||||
var explore = new MenuItem();
|
||||
explore.Header = App.Text("RevealFile");
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Explore");
|
||||
explore.IsEnabled = File.Exists(changeFullPath);
|
||||
explore.Click += (_, ev) =>
|
||||
var options = new FilePickerSaveOptions();
|
||||
options.Title = App.Text("FileCM.SaveAsPatch");
|
||||
options.DefaultExtension = ".patch";
|
||||
options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }];
|
||||
|
||||
var storageFile = await storageProvider.SaveFilePickerAsync(options);
|
||||
if (storageFile != null)
|
||||
{
|
||||
Native.OS.OpenInFileManager(changeFullPath, true);
|
||||
var saveTo = storageFile.Path.LocalPath;
|
||||
await vm.SaveChangesAsPatchAsync(selected, saveTo);
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
var menu = new ContextMenu();
|
||||
if (selected.Count == 1)
|
||||
{
|
||||
var change = selected[0];
|
||||
var changeFullPath = vm.GetAbsPath(change.Path);
|
||||
|
||||
var openWithMerger = new MenuItem();
|
||||
openWithMerger.Header = App.Text("OpenInExternalMergeTool");
|
||||
openWithMerger.Icon = App.CreateMenuIcon("Icons.OpenWith");
|
||||
openWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D";
|
||||
openWithMerger.Click += (_, ev) =>
|
||||
{
|
||||
vm.OpenChangeWithExternalDiffTool(change);
|
||||
ev.Handled = true;
|
||||
};
|
||||
menu.Items.Add(explore);
|
||||
menu.Items.Add(openWithMerger);
|
||||
|
||||
if (change.Index != Models.ChangeState.Deleted)
|
||||
{
|
||||
var explore = new MenuItem();
|
||||
explore.Header = App.Text("RevealFile");
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Explore");
|
||||
explore.IsEnabled = File.Exists(changeFullPath);
|
||||
explore.Click += (_, ev) =>
|
||||
{
|
||||
Native.OS.OpenInFileManager(changeFullPath, true);
|
||||
ev.Handled = true;
|
||||
};
|
||||
menu.Items.Add(explore);
|
||||
}
|
||||
|
||||
var copyPath = new MenuItem();
|
||||
copyPath.Header = App.Text("CopyPath");
|
||||
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C";
|
||||
copyPath.Click += async (_, ev) =>
|
||||
{
|
||||
await App.CopyTextAsync(change.Path);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var copyFullPath = new MenuItem();
|
||||
copyFullPath.Header = App.Text("CopyFullPath");
|
||||
copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C";
|
||||
copyFullPath.Click += async (_, ev) =>
|
||||
{
|
||||
await App.CopyTextAsync(changeFullPath);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
menu.Items.Add(patch);
|
||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
menu.Items.Add(copyPath);
|
||||
menu.Items.Add(copyFullPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
var copyPath = new MenuItem();
|
||||
copyPath.Header = App.Text("CopyPath");
|
||||
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C";
|
||||
copyPath.Click += async (_, ev) =>
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
foreach (var c in selected)
|
||||
builder.AppendLine(c.Path);
|
||||
|
||||
await App.CopyTextAsync(builder.ToString());
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var copyFullPath = new MenuItem();
|
||||
copyFullPath.Header = App.Text("CopyFullPath");
|
||||
copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C";
|
||||
copyFullPath.Click += async (_, ev) =>
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
foreach (var c in selected)
|
||||
builder.AppendLine(vm.GetAbsPath(c.Path));
|
||||
|
||||
await App.CopyTextAsync(builder.ToString());
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
menu.Items.Add(patch);
|
||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
menu.Items.Add(copyPath);
|
||||
menu.Items.Add(copyFullPath);
|
||||
}
|
||||
|
||||
var copyPath = new MenuItem();
|
||||
copyPath.Header = App.Text("CopyPath");
|
||||
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C";
|
||||
copyPath.Click += async (_, ev) =>
|
||||
{
|
||||
await App.CopyTextAsync(change.Path);
|
||||
ev.Handled = true;
|
||||
};
|
||||
menu.Items.Add(copyPath);
|
||||
|
||||
var copyFullPath = new MenuItem();
|
||||
copyFullPath.Header = App.Text("CopyFullPath");
|
||||
copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C";
|
||||
copyFullPath.Click += async (_, ev) =>
|
||||
{
|
||||
await App.CopyTextAsync(changeFullPath);
|
||||
ev.Handled = true;
|
||||
};
|
||||
menu.Items.Add(copyFullPath);
|
||||
menu.Open(view);
|
||||
}
|
||||
|
||||
@@ -86,8 +154,8 @@ namespace SourceGit.Views
|
||||
|
||||
private async void OnSaveAsPatch(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var topLevel = TopLevel.GetTopLevel(this);
|
||||
if (topLevel == null)
|
||||
var storage = TopLevel.GetTopLevel(this)?.StorageProvider;
|
||||
if (storage == null)
|
||||
return;
|
||||
|
||||
if (DataContext is not ViewModels.RevisionCompare vm)
|
||||
@@ -98,9 +166,9 @@ namespace SourceGit.Views
|
||||
options.DefaultExtension = ".patch";
|
||||
options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }];
|
||||
|
||||
var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options);
|
||||
var storageFile = await storage.SaveFilePickerAsync(options);
|
||||
if (storageFile != null)
|
||||
vm.SaveAsPatch(storageFile.Path.LocalPath);
|
||||
await vm.SaveChangesAsPatchAsync(null, storageFile.Path.LocalPath);
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
@@ -110,17 +178,24 @@ namespace SourceGit.Views
|
||||
if (DataContext is not ViewModels.RevisionCompare vm)
|
||||
return;
|
||||
|
||||
if (sender is not ChangeCollectionView { SelectedChanges: { Count: 1 } selectedChanges })
|
||||
if (sender is not ChangeCollectionView { SelectedChanges: { Count: > 0 } 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));
|
||||
var builder = new StringBuilder();
|
||||
var copyAbsPath = e.KeyModifiers.HasFlag(KeyModifiers.Shift);
|
||||
if (selectedChanges.Count == 1)
|
||||
{
|
||||
builder.Append(copyAbsPath ? vm.GetAbsPath(selectedChanges[0].Path) : selectedChanges[0].Path);
|
||||
}
|
||||
else
|
||||
await App.CopyTextAsync(change.Path);
|
||||
{
|
||||
foreach (var c in selectedChanges)
|
||||
builder.AppendLine(copyAbsPath ? vm.GetAbsPath(c.Path) : c.Path);
|
||||
}
|
||||
|
||||
await App.CopyTextAsync(builder.ToString());
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
@@ -84,7 +85,7 @@ namespace SourceGit.Views
|
||||
|
||||
var storageFile = await storageProvider.SaveFilePickerAsync(options);
|
||||
if (storageFile != null)
|
||||
await vm.SaveStashAsPathAsync(stash, storageFile.Path.LocalPath);
|
||||
await vm.SaveStashAsPatchAsync(stash, storageFile.Path.LocalPath);
|
||||
|
||||
ev.Handled = true;
|
||||
};
|
||||
@@ -123,70 +124,119 @@ namespace SourceGit.Views
|
||||
|
||||
private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e)
|
||||
{
|
||||
if (DataContext is ViewModels.StashesPage { SelectedChanges: { Count: 1 } selected } vm &&
|
||||
if (DataContext is ViewModels.StashesPage { SelectedChanges: { Count: > 0 } selected } vm &&
|
||||
sender is ChangeCollectionView view)
|
||||
{
|
||||
var change = selected[0];
|
||||
var fullPath = vm.GetAbsPath(change.Path);
|
||||
|
||||
var openWithMerger = new MenuItem();
|
||||
openWithMerger.Header = App.Text("OpenInExternalMergeTool");
|
||||
openWithMerger.Icon = App.CreateMenuIcon("Icons.OpenWith");
|
||||
openWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D";
|
||||
openWithMerger.Click += (_, ev) =>
|
||||
if (selected.Count == 1)
|
||||
{
|
||||
vm.OpenChangeWithExternalDiffTool(change);
|
||||
ev.Handled = true;
|
||||
};
|
||||
var change = selected[0];
|
||||
var fullPath = vm.GetAbsPath(change.Path);
|
||||
|
||||
var explore = new MenuItem();
|
||||
explore.Header = App.Text("RevealFile");
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Explore");
|
||||
explore.IsEnabled = File.Exists(fullPath);
|
||||
explore.Click += (_, ev) =>
|
||||
var openWithMerger = new MenuItem();
|
||||
openWithMerger.Header = App.Text("OpenInExternalMergeTool");
|
||||
openWithMerger.Icon = App.CreateMenuIcon("Icons.OpenWith");
|
||||
openWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D";
|
||||
openWithMerger.Click += (_, ev) =>
|
||||
{
|
||||
vm.OpenChangeWithExternalDiffTool(change);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var explore = new MenuItem();
|
||||
explore.Header = App.Text("RevealFile");
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Explore");
|
||||
explore.IsEnabled = File.Exists(fullPath);
|
||||
explore.Click += (_, ev) =>
|
||||
{
|
||||
Native.OS.OpenInFileManager(fullPath, true);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var resetToThisRevision = new MenuItem();
|
||||
resetToThisRevision.Header = App.Text("ChangeCM.CheckoutThisRevision");
|
||||
resetToThisRevision.Icon = App.CreateMenuIcon("Icons.File.Checkout");
|
||||
resetToThisRevision.Click += async (_, ev) =>
|
||||
{
|
||||
await vm.CheckoutSingleFileAsync(change);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var copyPath = new MenuItem();
|
||||
copyPath.Header = App.Text("CopyPath");
|
||||
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C";
|
||||
copyPath.Click += async (_, ev) =>
|
||||
{
|
||||
await App.CopyTextAsync(change.Path);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var copyFullPath = new MenuItem();
|
||||
copyFullPath.Header = App.Text("CopyFullPath");
|
||||
copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C";
|
||||
copyFullPath.Click += async (_, ev) =>
|
||||
{
|
||||
await App.CopyTextAsync(fullPath);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var menu = new ContextMenu();
|
||||
menu.Items.Add(openWithMerger);
|
||||
menu.Items.Add(explore);
|
||||
menu.Items.Add(new MenuItem { Header = "-" });
|
||||
menu.Items.Add(resetToThisRevision);
|
||||
menu.Items.Add(new MenuItem { Header = "-" });
|
||||
menu.Items.Add(copyPath);
|
||||
menu.Items.Add(copyFullPath);
|
||||
menu.Open(view);
|
||||
}
|
||||
else
|
||||
{
|
||||
Native.OS.OpenInFileManager(fullPath, true);
|
||||
ev.Handled = true;
|
||||
};
|
||||
var resetToThisRevision = new MenuItem();
|
||||
resetToThisRevision.Header = App.Text("ChangeCM.CheckoutThisRevision");
|
||||
resetToThisRevision.Icon = App.CreateMenuIcon("Icons.File.Checkout");
|
||||
resetToThisRevision.Click += async (_, ev) =>
|
||||
{
|
||||
await vm.CheckoutMultipleFileAsync(selected);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var resetToThisRevision = new MenuItem();
|
||||
resetToThisRevision.Header = App.Text("ChangeCM.CheckoutThisRevision");
|
||||
resetToThisRevision.Icon = App.CreateMenuIcon("Icons.File.Checkout");
|
||||
resetToThisRevision.Click += async (_, ev) =>
|
||||
{
|
||||
await vm.CheckoutSingleFileAsync(change);
|
||||
ev.Handled = true;
|
||||
};
|
||||
var copyPath = new MenuItem();
|
||||
copyPath.Header = App.Text("CopyPath");
|
||||
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C";
|
||||
copyPath.Click += async (_, ev) =>
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
foreach (var c in selected)
|
||||
builder.AppendLine(c.Path);
|
||||
|
||||
var copyPath = new MenuItem();
|
||||
copyPath.Header = App.Text("CopyPath");
|
||||
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C";
|
||||
copyPath.Click += async (_, ev) =>
|
||||
{
|
||||
await App.CopyTextAsync(change.Path);
|
||||
ev.Handled = true;
|
||||
};
|
||||
await App.CopyTextAsync(builder.ToString());
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var copyFullPath = new MenuItem();
|
||||
copyFullPath.Header = App.Text("CopyFullPath");
|
||||
copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C";
|
||||
copyFullPath.Click += async (_, ev) =>
|
||||
{
|
||||
await App.CopyTextAsync(fullPath);
|
||||
ev.Handled = true;
|
||||
};
|
||||
var copyFullPath = new MenuItem();
|
||||
copyFullPath.Header = App.Text("CopyFullPath");
|
||||
copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C";
|
||||
copyFullPath.Click += async (_, ev) =>
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
foreach (var c in selected)
|
||||
builder.AppendLine(vm.GetAbsPath(c.Path));
|
||||
|
||||
var menu = new ContextMenu();
|
||||
menu.Items.Add(openWithMerger);
|
||||
menu.Items.Add(explore);
|
||||
menu.Items.Add(new MenuItem { Header = "-" });
|
||||
menu.Items.Add(resetToThisRevision);
|
||||
menu.Items.Add(new MenuItem { Header = "-" });
|
||||
menu.Items.Add(copyPath);
|
||||
menu.Items.Add(copyFullPath);
|
||||
menu.Open(view);
|
||||
await App.CopyTextAsync(builder.ToString());
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var menu = new ContextMenu();
|
||||
menu.Items.Add(resetToThisRevision);
|
||||
menu.Items.Add(new MenuItem { Header = "-" });
|
||||
menu.Items.Add(copyPath);
|
||||
menu.Items.Add(copyFullPath);
|
||||
menu.Open(view);
|
||||
}
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
@@ -197,17 +247,24 @@ namespace SourceGit.Views
|
||||
if (DataContext is not ViewModels.StashesPage vm)
|
||||
return;
|
||||
|
||||
if (sender is not ChangeCollectionView { SelectedChanges: { Count: 1 } selectedChanges })
|
||||
if (sender is not ChangeCollectionView { SelectedChanges: { Count: > 0 } 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));
|
||||
var builder = new StringBuilder();
|
||||
var copyAbsPath = e.KeyModifiers.HasFlag(KeyModifiers.Shift);
|
||||
if (selectedChanges.Count == 1)
|
||||
{
|
||||
builder.Append(copyAbsPath ? vm.GetAbsPath(selectedChanges[0].Path) : selectedChanges[0].Path);
|
||||
}
|
||||
else
|
||||
await App.CopyTextAsync(change.Path);
|
||||
{
|
||||
foreach (var c in selectedChanges)
|
||||
builder.AppendLine(copyAbsPath ? vm.GetAbsPath(c.Path) : c.Path);
|
||||
}
|
||||
|
||||
await App.CopyTextAsync(builder.ToString());
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +126,6 @@
|
||||
<v:ChangeCollectionView Grid.Row="1"
|
||||
x:Name="UnstagedChangesView"
|
||||
IsUnstagedChange="True"
|
||||
SelectionMode="Multiple"
|
||||
Background="{DynamicResource Brush.Contents}"
|
||||
ViewMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=UnstagedChangeViewMode}"
|
||||
EnableCompactFolders="{Binding Source={x:Static vm:Preferences.Instance}, Path=EnableCompactFoldersInChangesTree}"
|
||||
@@ -181,7 +180,6 @@
|
||||
<v:ChangeCollectionView Grid.Row="1"
|
||||
x:Name="StagedChangesView"
|
||||
IsUnstagedChange="False"
|
||||
SelectionMode="Multiple"
|
||||
Background="{DynamicResource Brush.Contents}"
|
||||
ViewMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=StagedChangeViewMode}"
|
||||
EnableCompactFolders="{Binding Source={x:Static vm:Preferences.Instance}, Path=EnableCompactFoldersInChangesTree}"
|
||||
|
||||
Reference in New Issue
Block a user