mirror of
https://fastgit.cc/github.com/sourcegit-scm/sourcegit
synced 2026-04-23 10:22:13 +08:00
feature: supports show commit histories under selected folder (#1470)
Signed-off-by: leo <longshuang@msn.cn>
This commit is contained in:
@@ -303,6 +303,7 @@
|
||||
<x:String x:Key="Text.Diff.VisualLines.Incr" xml:space="preserve">Increase Number of Visible Lines</x:String>
|
||||
<x:String x:Key="Text.Diff.Welcome" xml:space="preserve">SELECT FILE TO VIEW CHANGES</x:String>
|
||||
<x:String x:Key="Text.DiffWithMerger" xml:space="preserve">Open in Merge Tool</x:String>
|
||||
<x:String x:Key="Text.DirHistories" xml:space="preserve">Dir Histories</x:String>
|
||||
<x:String x:Key="Text.Discard" xml:space="preserve">Discard Changes</x:String>
|
||||
<x:String x:Key="Text.Discard.All" xml:space="preserve">All local changes in working copy.</x:String>
|
||||
<x:String x:Key="Text.Discard.Changes" xml:space="preserve">Changes:</x:String>
|
||||
|
||||
@@ -307,6 +307,7 @@
|
||||
<x:String x:Key="Text.Diff.VisualLines.Incr" xml:space="preserve">增加可见的行数</x:String>
|
||||
<x:String x:Key="Text.Diff.Welcome" xml:space="preserve">请选择需要对比的文件</x:String>
|
||||
<x:String x:Key="Text.DiffWithMerger" xml:space="preserve">使用外部比对工具查看</x:String>
|
||||
<x:String x:Key="Text.DirHistories" xml:space="preserve">目录内容变更历史</x:String>
|
||||
<x:String x:Key="Text.Discard" xml:space="preserve">放弃更改确认</x:String>
|
||||
<x:String x:Key="Text.Discard.All" xml:space="preserve">所有本地址未提交的修改。</x:String>
|
||||
<x:String x:Key="Text.Discard.Changes" xml:space="preserve">变更 :</x:String>
|
||||
|
||||
@@ -307,6 +307,7 @@
|
||||
<x:String x:Key="Text.Diff.VisualLines.Incr" xml:space="preserve">增加可見的行數</x:String>
|
||||
<x:String x:Key="Text.Diff.Welcome" xml:space="preserve">請選擇需要對比的檔案</x:String>
|
||||
<x:String x:Key="Text.DiffWithMerger" xml:space="preserve">使用外部比對工具檢視</x:String>
|
||||
<x:String x:Key="Text.DirHistories" xml:space="preserve">目錄内容變更歷史</x:String>
|
||||
<x:String x:Key="Text.Discard" xml:space="preserve">捨棄變更</x:String>
|
||||
<x:String x:Key="Text.Discard.All" xml:space="preserve">所有本機未提交的變更。</x:String>
|
||||
<x:String x:Key="Text.Discard.Changes" xml:space="preserve">變更:</x:String>
|
||||
|
||||
@@ -296,6 +296,85 @@ namespace SourceGit.ViewModels
|
||||
});
|
||||
}
|
||||
|
||||
public ContextMenu CreateChangeContextMenuByFolder(ChangeTreeNode node, List<Models.Change> changes)
|
||||
{
|
||||
var fullPath = Native.OS.GetAbsPath(_repo.FullPath, node.FullPath);
|
||||
var explore = new MenuItem();
|
||||
explore.Header = App.Text("RevealFile");
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Explore");
|
||||
explore.IsEnabled = Directory.Exists(fullPath);
|
||||
explore.Click += (_, ev) =>
|
||||
{
|
||||
Native.OS.OpenInFileManager(fullPath, true);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var history = new MenuItem();
|
||||
history.Header = App.Text("DirHistories");
|
||||
history.Icon = App.CreateMenuIcon("Icons.Histories");
|
||||
history.Click += (_, ev) =>
|
||||
{
|
||||
App.ShowWindow(new DirHistories(_repo, node.FullPath, _commit.SHA), false);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var patch = new MenuItem();
|
||||
patch.Header = App.Text("FileCM.SaveAsPatch");
|
||||
patch.Icon = App.CreateMenuIcon("Icons.Diff");
|
||||
patch.Click += async (_, e) =>
|
||||
{
|
||||
var storageProvider = App.GetStorageProvider();
|
||||
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 baseRevision = _commit.Parents.Count == 0 ? Models.Commit.EmptyTreeSHA1 : _commit.Parents[0];
|
||||
var storageFile = await storageProvider.SaveFilePickerAsync(options);
|
||||
if (storageFile != null)
|
||||
{
|
||||
var saveTo = storageFile.Path.LocalPath;
|
||||
var succ = await Task.Run(() => Commands.SaveChangesAsPatch.ProcessRevisionCompareChanges(_repo.FullPath, changes, baseRevision, _commit.SHA, saveTo));
|
||||
if (succ)
|
||||
App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
var copyPath = new MenuItem();
|
||||
copyPath.Header = App.Text("CopyPath");
|
||||
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyPath.Click += (_, ev) =>
|
||||
{
|
||||
App.CopyText(node.FullPath);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var copyFullPath = new MenuItem();
|
||||
copyFullPath.Header = App.Text("CopyFullPath");
|
||||
copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyFullPath.Click += (_, e) =>
|
||||
{
|
||||
App.CopyText(fullPath);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
var menu = new ContextMenu();
|
||||
menu.Items.Add(explore);
|
||||
menu.Items.Add(new MenuItem { Header = "-" });
|
||||
menu.Items.Add(history);
|
||||
menu.Items.Add(patch);
|
||||
menu.Items.Add(new MenuItem { Header = "-" });
|
||||
menu.Items.Add(copyPath);
|
||||
menu.Items.Add(copyFullPath);
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
public ContextMenu CreateChangeContextMenu(Models.Change change)
|
||||
{
|
||||
var diffWithMerger = new MenuItem();
|
||||
@@ -428,8 +507,61 @@ namespace SourceGit.ViewModels
|
||||
return menu;
|
||||
}
|
||||
|
||||
public ContextMenu CreateRevisionFileContextMenuByFolder(string path)
|
||||
{
|
||||
var fullPath = Native.OS.GetAbsPath(_repo.FullPath, path);
|
||||
var explore = new MenuItem();
|
||||
explore.Header = App.Text("RevealFile");
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Explore");
|
||||
explore.IsEnabled = Directory.Exists(fullPath);
|
||||
explore.Click += (_, ev) =>
|
||||
{
|
||||
Native.OS.OpenInFileManager(fullPath, true);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var history = new MenuItem();
|
||||
history.Header = App.Text("DirHistories");
|
||||
history.Icon = App.CreateMenuIcon("Icons.Histories");
|
||||
history.Click += (_, ev) =>
|
||||
{
|
||||
App.ShowWindow(new DirHistories(_repo, path, _commit.SHA), false);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var copyPath = new MenuItem();
|
||||
copyPath.Header = App.Text("CopyPath");
|
||||
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyPath.Click += (_, ev) =>
|
||||
{
|
||||
App.CopyText(path);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
||||
var copyFullPath = new MenuItem();
|
||||
copyFullPath.Header = App.Text("CopyFullPath");
|
||||
copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyFullPath.Click += (_, e) =>
|
||||
{
|
||||
App.CopyText(fullPath);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
var menu = new ContextMenu();
|
||||
menu.Items.Add(explore);
|
||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
menu.Items.Add(history);
|
||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
menu.Items.Add(copyPath);
|
||||
menu.Items.Add(copyFullPath);
|
||||
return menu;
|
||||
}
|
||||
|
||||
public ContextMenu CreateRevisionFileContextMenu(Models.Object file)
|
||||
{
|
||||
if (file.Type == Models.ObjectType.Tree)
|
||||
return CreateRevisionFileContextMenuByFolder(file.Path);
|
||||
|
||||
var menu = new ContextMenu();
|
||||
var fullPath = Native.OS.GetAbsPath(_repo.FullPath, file.Path);
|
||||
var explore = new MenuItem();
|
||||
|
||||
90
src/ViewModels/DirHistories.cs
Normal file
90
src/ViewModels/DirHistories.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class DirHistories : ObservableObject
|
||||
{
|
||||
public string Title
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public bool IsLoading
|
||||
{
|
||||
get => _isLoading;
|
||||
private set => SetProperty(ref _isLoading, value);
|
||||
}
|
||||
|
||||
public List<Models.Commit> Commits
|
||||
{
|
||||
get => _commits;
|
||||
private set => SetProperty(ref _commits, value);
|
||||
}
|
||||
|
||||
public Models.Commit SelectedCommit
|
||||
{
|
||||
get => _selectedCommit;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _selectedCommit, value))
|
||||
Detail.Commit = value;
|
||||
}
|
||||
}
|
||||
|
||||
public CommitDetail Detail
|
||||
{
|
||||
get => _detail;
|
||||
private set => SetProperty(ref _detail, value);
|
||||
}
|
||||
|
||||
public DirHistories(Repository repo, string dir, string revision = null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(revision))
|
||||
Title = $"{dir} @ {revision}";
|
||||
else
|
||||
Title = dir;
|
||||
|
||||
_repo = repo;
|
||||
_detail = new CommitDetail(repo);
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
var commits = new Commands.QueryCommits(_repo.FullPath, $"--date-order -n 10000 {revision??string.Empty} -- \"{dir}\"", false).Result();
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
Commits = commits;
|
||||
IsLoading = false;
|
||||
|
||||
if (commits.Count > 0)
|
||||
SelectedCommit = commits[0];
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public void NavigateToCommit(Models.Commit commit)
|
||||
{
|
||||
_repo.NavigateToCommit(commit.SHA);
|
||||
}
|
||||
|
||||
public string GetCommitFullMessage(Models.Commit commit)
|
||||
{
|
||||
var sha = commit.SHA;
|
||||
if (_cachedCommitFullMessage.TryGetValue(sha, out var msg))
|
||||
return msg;
|
||||
|
||||
msg = new Commands.QueryCommitFullMessage(_repo.FullPath, sha).Result();
|
||||
_cachedCommitFullMessage[sha] = msg;
|
||||
return msg;
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private bool _isLoading = true;
|
||||
private List<Models.Commit> _commits = [];
|
||||
private Models.Commit _selectedCommit = null;
|
||||
private CommitDetail _detail = null;
|
||||
private Dictionary<string, string> _cachedCommitFullMessage = new();
|
||||
}
|
||||
}
|
||||
@@ -238,6 +238,11 @@ namespace SourceGit.ViewModels
|
||||
|
||||
public class FileHistories : ObservableObject
|
||||
{
|
||||
public string Title
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public bool IsLoading
|
||||
{
|
||||
get => _isLoading;
|
||||
@@ -264,6 +269,11 @@ namespace SourceGit.ViewModels
|
||||
|
||||
public FileHistories(Repository repo, string file, string commit = null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(commit))
|
||||
Title = $"{file} @ {commit}";
|
||||
else
|
||||
Title = file;
|
||||
|
||||
_repo = repo;
|
||||
|
||||
Task.Run(() =>
|
||||
|
||||
@@ -590,6 +590,7 @@ namespace SourceGit.ViewModels
|
||||
if (_selectedUnstaged == null || _selectedUnstaged.Count == 0)
|
||||
return null;
|
||||
|
||||
var hasSelectedFolder = !string.IsNullOrEmpty(selectedSingleFolder);
|
||||
var menu = new ContextMenu();
|
||||
if (_selectedUnstaged.Count == 1)
|
||||
{
|
||||
@@ -602,11 +603,8 @@ namespace SourceGit.ViewModels
|
||||
explore.IsEnabled = File.Exists(path) || Directory.Exists(path);
|
||||
explore.Click += (_, e) =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(selectedSingleFolder))
|
||||
Native.OS.OpenInFileManager(path, true);
|
||||
else
|
||||
Native.OS.OpenInFileManager(Native.OS.GetAbsPath(_repo.FullPath, selectedSingleFolder), true);
|
||||
|
||||
var target = hasSelectedFolder ? Native.OS.GetAbsPath(_repo.FullPath, selectedSingleFolder) : path;
|
||||
Native.OS.OpenInFileManager(target, true);
|
||||
e.Handled = true;
|
||||
};
|
||||
menu.Items.Add(explore);
|
||||
@@ -747,23 +745,12 @@ namespace SourceGit.ViewModels
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
var history = new MenuItem();
|
||||
history.Header = App.Text("FileHistory");
|
||||
history.Icon = App.CreateMenuIcon("Icons.Histories");
|
||||
history.Click += (_, e) =>
|
||||
{
|
||||
App.ShowWindow(new FileHistories(_repo, change.Path), false);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
menu.Items.Add(stage);
|
||||
menu.Items.Add(discard);
|
||||
menu.Items.Add(stash);
|
||||
menu.Items.Add(patch);
|
||||
menu.Items.Add(assumeUnchanged);
|
||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
menu.Items.Add(history);
|
||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
|
||||
var extension = Path.GetExtension(change.Path);
|
||||
var hasExtra = false;
|
||||
@@ -773,7 +760,7 @@ namespace SourceGit.ViewModels
|
||||
addToIgnore.Header = App.Text("WorkingCopy.AddToGitIgnore");
|
||||
addToIgnore.Icon = App.CreateMenuIcon("Icons.GitIgnore");
|
||||
|
||||
if (!string.IsNullOrEmpty(selectedSingleFolder))
|
||||
if (hasSelectedFolder)
|
||||
{
|
||||
var ignoreFolder = new MenuItem();
|
||||
ignoreFolder.Header = App.Text("WorkingCopy.AddToGitIgnore.InFolder");
|
||||
@@ -827,24 +814,21 @@ namespace SourceGit.ViewModels
|
||||
menu.Items.Add(addToIgnore);
|
||||
hasExtra = true;
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(selectedSingleFolder))
|
||||
else if (hasSelectedFolder)
|
||||
{
|
||||
var addToIgnore = new MenuItem();
|
||||
addToIgnore.Header = App.Text("WorkingCopy.AddToGitIgnore");
|
||||
addToIgnore.Icon = App.CreateMenuIcon("Icons.GitIgnore");
|
||||
|
||||
if (!string.IsNullOrEmpty(selectedSingleFolder))
|
||||
var ignoreFolder = new MenuItem();
|
||||
ignoreFolder.Header = App.Text("WorkingCopy.AddToGitIgnore.InFolder");
|
||||
ignoreFolder.Click += (_, e) =>
|
||||
{
|
||||
var ignoreFolder = new MenuItem();
|
||||
ignoreFolder.Header = App.Text("WorkingCopy.AddToGitIgnore.InFolder");
|
||||
ignoreFolder.Click += (_, e) =>
|
||||
{
|
||||
if (_repo.CanCreatePopup())
|
||||
_repo.ShowPopup(new AddToIgnore(_repo, $"{selectedSingleFolder}/"));
|
||||
e.Handled = true;
|
||||
};
|
||||
addToIgnore.Items.Add(ignoreFolder);
|
||||
}
|
||||
if (_repo.CanCreatePopup())
|
||||
_repo.ShowPopup(new AddToIgnore(_repo, $"{selectedSingleFolder}/"));
|
||||
e.Handled = true;
|
||||
};
|
||||
addToIgnore.Items.Add(ignoreFolder);
|
||||
|
||||
menu.Items.Add(addToIgnore);
|
||||
hasExtra = true;
|
||||
@@ -981,32 +965,40 @@ namespace SourceGit.ViewModels
|
||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
}
|
||||
|
||||
var history = new MenuItem();
|
||||
history.Header = App.Text(hasSelectedFolder ? "DirHistories" : "FileHistory");
|
||||
history.Icon = App.CreateMenuIcon("Icons.Histories");
|
||||
history.Click += (_, e) =>
|
||||
{
|
||||
if (hasSelectedFolder)
|
||||
App.ShowWindow(new DirHistories(_repo, selectedSingleFolder), false);
|
||||
else
|
||||
App.ShowWindow(new FileHistories(_repo, change.Path), false);
|
||||
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
var copy = new MenuItem();
|
||||
copy.Header = App.Text("CopyPath");
|
||||
copy.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copy.Click += (_, e) =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(selectedSingleFolder))
|
||||
App.CopyText(change.Path);
|
||||
else
|
||||
App.CopyText(selectedSingleFolder);
|
||||
|
||||
App.CopyText(hasSelectedFolder ? selectedSingleFolder : change.Path);
|
||||
e.Handled = true;
|
||||
};
|
||||
menu.Items.Add(copy);
|
||||
|
||||
var copyFullPath = new MenuItem();
|
||||
copyFullPath.Header = App.Text("CopyFullPath");
|
||||
copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyFullPath.Click += (_, e) =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(selectedSingleFolder))
|
||||
App.CopyText(path);
|
||||
else
|
||||
App.CopyText(Native.OS.GetAbsPath(_repo.FullPath, selectedSingleFolder));
|
||||
|
||||
App.CopyText(hasSelectedFolder ? Native.OS.GetAbsPath(_repo.FullPath, selectedSingleFolder) : path);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
menu.Items.Add(history);
|
||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
menu.Items.Add(copy);
|
||||
menu.Items.Add(copyFullPath);
|
||||
}
|
||||
else
|
||||
@@ -1073,7 +1065,7 @@ namespace SourceGit.ViewModels
|
||||
return menu;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(selectedSingleFolder))
|
||||
if (hasSelectedFolder)
|
||||
{
|
||||
var dir = Path.Combine(_repo.FullPath, selectedSingleFolder);
|
||||
var explore = new MenuItem();
|
||||
@@ -1148,7 +1140,7 @@ namespace SourceGit.ViewModels
|
||||
menu.Items.Add(stash);
|
||||
menu.Items.Add(patch);
|
||||
|
||||
if (!string.IsNullOrEmpty(selectedSingleFolder))
|
||||
if (hasSelectedFolder)
|
||||
{
|
||||
var ignoreFolder = new MenuItem();
|
||||
ignoreFolder.Header = App.Text("WorkingCopy.AddToGitIgnore.InFolder");
|
||||
@@ -1167,6 +1159,17 @@ namespace SourceGit.ViewModels
|
||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
menu.Items.Add(addToIgnore);
|
||||
|
||||
var history = new MenuItem();
|
||||
history.Header = App.Text("DirHistories");
|
||||
history.Icon = App.CreateMenuIcon("Icons.Histories");
|
||||
history.Click += (_, e) =>
|
||||
{
|
||||
App.ShowWindow(new DirHistories(_repo, selectedSingleFolder), false);
|
||||
e.Handled = true;
|
||||
};
|
||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
menu.Items.Add(history);
|
||||
|
||||
var copy = new MenuItem();
|
||||
copy.Header = App.Text("CopyPath");
|
||||
copy.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
@@ -1235,6 +1238,7 @@ namespace SourceGit.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
var hasSelectedFolder = !string.IsNullOrEmpty(selectedSingleFolder);
|
||||
if (_selectedStaged.Count == 1)
|
||||
{
|
||||
var change = _selectedStaged[0];
|
||||
@@ -1246,11 +1250,8 @@ namespace SourceGit.ViewModels
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Explore");
|
||||
explore.Click += (_, e) =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(selectedSingleFolder))
|
||||
Native.OS.OpenInFileManager(path, true);
|
||||
else
|
||||
Native.OS.OpenInFileManager(Native.OS.GetAbsPath(_repo.FullPath, selectedSingleFolder), true);
|
||||
|
||||
var target = hasSelectedFolder ? Native.OS.GetAbsPath(_repo.FullPath, selectedSingleFolder) : path;
|
||||
Native.OS.OpenInFileManager(target, true);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
@@ -1309,15 +1310,6 @@ namespace SourceGit.ViewModels
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
var history = new MenuItem();
|
||||
history.Header = App.Text("FileHistory");
|
||||
history.Icon = App.CreateMenuIcon("Icons.Histories");
|
||||
history.Click += (_, e) =>
|
||||
{
|
||||
App.ShowWindow(new FileHistories(_repo, change.Path), false);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
menu.Items.Add(explore);
|
||||
menu.Items.Add(openWith);
|
||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
@@ -1325,8 +1317,6 @@ namespace SourceGit.ViewModels
|
||||
menu.Items.Add(stash);
|
||||
menu.Items.Add(patch);
|
||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
menu.Items.Add(history);
|
||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
|
||||
var lfsEnabled = new Commands.LFS(_repo.FullPath).IsEnabled();
|
||||
if (lfsEnabled)
|
||||
@@ -1423,16 +1413,24 @@ namespace SourceGit.ViewModels
|
||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
}
|
||||
|
||||
var history = new MenuItem();
|
||||
history.Header = App.Text(hasSelectedFolder ? "DirHistories" : "FileHistory");
|
||||
history.Icon = App.CreateMenuIcon("Icons.Histories");
|
||||
history.Click += (_, e) =>
|
||||
{
|
||||
if (hasSelectedFolder)
|
||||
App.ShowWindow(new DirHistories(_repo, selectedSingleFolder), false);
|
||||
else
|
||||
App.ShowWindow(new FileHistories(_repo, change.Path), false);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
var copyPath = new MenuItem();
|
||||
copyPath.Header = App.Text("CopyPath");
|
||||
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyPath.Click += (_, e) =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(selectedSingleFolder))
|
||||
App.CopyText(change.Path);
|
||||
else
|
||||
App.CopyText(selectedSingleFolder);
|
||||
|
||||
App.CopyText(hasSelectedFolder ? selectedSingleFolder : change.Path);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
@@ -1441,20 +1439,19 @@ namespace SourceGit.ViewModels
|
||||
copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyFullPath.Click += (_, e) =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(selectedSingleFolder))
|
||||
App.CopyText(path);
|
||||
else
|
||||
App.CopyText(Native.OS.GetAbsPath(_repo.FullPath, selectedSingleFolder));
|
||||
|
||||
var target = hasSelectedFolder ? Native.OS.GetAbsPath(_repo.FullPath, selectedSingleFolder) : path;
|
||||
App.CopyText(target);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
menu.Items.Add(history);
|
||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
menu.Items.Add(copyPath);
|
||||
menu.Items.Add(copyFullPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrEmpty(selectedSingleFolder))
|
||||
if (hasSelectedFolder)
|
||||
{
|
||||
var dir = Path.Combine(_repo.FullPath, selectedSingleFolder);
|
||||
var explore = new MenuItem();
|
||||
@@ -1526,8 +1523,17 @@ namespace SourceGit.ViewModels
|
||||
menu.Items.Add(ai);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(selectedSingleFolder))
|
||||
if (hasSelectedFolder)
|
||||
{
|
||||
var history = new MenuItem();
|
||||
history.Header = App.Text(hasSelectedFolder ? "DirHistories" : "FileHistory");
|
||||
history.Icon = App.CreateMenuIcon("Icons.Histories");
|
||||
history.Click += (_, e) =>
|
||||
{
|
||||
App.ShowWindow(new DirHistories(_repo, selectedSingleFolder), false);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
var copyPath = new MenuItem();
|
||||
copyPath.Header = App.Text("CopyPath");
|
||||
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
@@ -1546,6 +1552,8 @@ namespace SourceGit.ViewModels
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
menu.Items.Add(history);
|
||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
menu.Items.Add(copyPath);
|
||||
menu.Items.Add(copyFullPath);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.VisualTree;
|
||||
|
||||
namespace SourceGit.Views
|
||||
{
|
||||
@@ -11,15 +12,25 @@ namespace SourceGit.Views
|
||||
|
||||
private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e)
|
||||
{
|
||||
if (sender is ChangeCollectionView { SelectedChanges: { } selected } view &&
|
||||
selected.Count == 1 &&
|
||||
DataContext is ViewModels.CommitDetail vm)
|
||||
e.Handled = true;
|
||||
|
||||
if (sender is not ChangeCollectionView view || DataContext is not ViewModels.CommitDetail vm)
|
||||
return;
|
||||
|
||||
var changes = view.SelectedChanges ?? [];
|
||||
var container = view.FindDescendantOfType<ChangeCollectionContainer>();
|
||||
if (container is { SelectedItems.Count: 1, SelectedItem: ViewModels.ChangeTreeNode { IsFolder: true } node })
|
||||
{
|
||||
var menu = vm.CreateChangeContextMenu(selected[0]);
|
||||
menu?.Open(view);
|
||||
var menu = vm.CreateChangeContextMenuByFolder(node, changes);
|
||||
menu.Open(view);
|
||||
return;
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
if (changes.Count == 1)
|
||||
{
|
||||
var menu = vm.CreateChangeContextMenu(changes[0]);
|
||||
menu.Open(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
129
src/Views/DirHistories.axaml
Normal file
129
src/Views/DirHistories.axaml
Normal file
@@ -0,0 +1,129 @@
|
||||
<v:ChromelessWindow xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:m="using:SourceGit.Models"
|
||||
xmlns:vm="using:SourceGit.ViewModels"
|
||||
xmlns:v="using:SourceGit.Views"
|
||||
xmlns:c="using:SourceGit.Converters"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="SourceGit.Views.DirHistories"
|
||||
x:DataType="vm:DirHistories"
|
||||
x:Name="ThisControl"
|
||||
Icon="/App.ico"
|
||||
Title="{DynamicResource Text.DirHistories}"
|
||||
MinWidth="1280" MinHeight="720">
|
||||
<Grid RowDefinitions="Auto,28,1,*">
|
||||
<!-- TitleBar -->
|
||||
<Grid Grid.Row="0" Height="28" IsVisible="{Binding !#ThisControl.UseSystemWindowFrame}">
|
||||
<!-- Bottom border -->
|
||||
<Border Background="{DynamicResource Brush.TitleBar}"
|
||||
BorderThickness="0,0,0,1" BorderBrush="{DynamicResource Brush.Border0}"
|
||||
DoubleTapped="MaximizeOrRestoreWindow"
|
||||
PointerPressed="BeginMoveWindow"/>
|
||||
|
||||
<Path Width="12" Height="12"
|
||||
Margin="10,0,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
Data="{StaticResource Icons.Histories}"
|
||||
IsVisible="{OnPlatform True, macOS=False}"/>
|
||||
|
||||
<TextBlock Classes="bold"
|
||||
Text="{DynamicResource Text.DirHistories}"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
IsHitTestVisible="False"/>
|
||||
|
||||
<!-- Caption Buttons (Windows/Linux) -->
|
||||
<v:CaptionButtons HorizontalAlignment="Right" IsVisible="{OnPlatform True, macOS=False}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- Info -->
|
||||
<Grid Grid.Row="1" ColumnDefinitions="Auto,*" Background="{DynamicResource Brush.Popup}">
|
||||
<Path Grid.Column="0" Width="14" Height="14" Margin="8,0,6,0" Data="{StaticResource Icons.Folder.Open}"/>
|
||||
<TextBlock Grid.Column="1" Text="{Binding Title, Mode=OneWay}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- Line -->
|
||||
<Rectangle Grid.Row="2" Height="0.8" HorizontalAlignment="Stretch" VerticalAlignment="Top" Fill="{DynamicResource Brush.Border0}"/>
|
||||
|
||||
<!-- Body -->
|
||||
<Grid Grid.Row="3">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="300" MinWidth="300" MaxWidth="600"/>
|
||||
<ColumnDefinition Width="4"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Commits -->
|
||||
<ListBox Grid.Column="0"
|
||||
Background="{DynamicResource Brush.Contents}"
|
||||
BorderThickness="1"
|
||||
Margin="8,4,4,8"
|
||||
BorderBrush="{DynamicResource Brush.Border2}"
|
||||
ItemsSource="{Binding Commits}"
|
||||
SelectedItem="{Binding SelectedCommit, Mode=TwoWay}"
|
||||
SelectionMode="Single"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto">
|
||||
<ListBox.Styles>
|
||||
<Style Selector="ListBoxItem">
|
||||
<Setter Property="Margin" Value="0"/>
|
||||
<Setter Property="Padding" Value="0"/>
|
||||
<Setter Property="Height" Value="50"/>
|
||||
</Style>
|
||||
</ListBox.Styles>
|
||||
|
||||
<ListBox.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<VirtualizingStackPanel Orientation="Vertical"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ListBox.ItemsPanel>
|
||||
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate DataType="m:Commit">
|
||||
<Border BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="0,0,0,1" Padding="4">
|
||||
<Grid RowDefinitions="Auto,*">
|
||||
<Grid Grid.Row="0" ColumnDefinitions="Auto,*,Auto,96">
|
||||
<v:Avatar Grid.Column="0" Width="16" Height="16" VerticalAlignment="Center" IsHitTestVisible="False" User="{Binding Author}"/>
|
||||
<TextBlock Grid.Column="1" Classes="primary" Text="{Binding Author.Name}" Margin="8,0,0,0" TextTrimming="CharacterEllipsis"/>
|
||||
<TextBlock Grid.Column="2"
|
||||
Classes="primary"
|
||||
Text="{Binding SHA, Converter={x:Static c:StringConverters.ToShortSHA}}"
|
||||
Cursor="Hand"
|
||||
Background="Transparent"
|
||||
Foreground="DarkOrange"
|
||||
TextDecorations="Underline"
|
||||
Margin="8,0,0,0"
|
||||
PointerPressed="OnPressCommitSHA"/>
|
||||
<TextBlock Grid.Column="3" Classes="primary" Text="{Binding AuthorTimeShortStr}" Foreground="{DynamicResource Brush.FG2}" HorizontalAlignment="Right"/>
|
||||
</Grid>
|
||||
|
||||
<Border Grid.Row="1" Background="Transparent" DataContextChanged="OnCommitSubjectDataContextChanged" PointerMoved="OnCommitSubjectPointerMoved">
|
||||
<TextBlock Classes="primary" Text="{Binding Subject}" VerticalAlignment="Bottom"/>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
|
||||
<GridSplitter Grid.Column="1"
|
||||
MinWidth="1"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
|
||||
Background="Transparent"
|
||||
BorderThickness="1,0,0,0"
|
||||
BorderBrush="{DynamicResource Brush.Border0}"/>
|
||||
|
||||
<!-- Commit Detail -->
|
||||
<Border Grid.Column="2" Padding="0,4,4,8">
|
||||
<ContentControl Content="{Binding Detail, Mode=OneWay}">
|
||||
<ContentControl.DataTemplates>
|
||||
<DataTemplate DataType="vm:CommitDetail">
|
||||
<v:CommitDetail/>
|
||||
</DataTemplate>
|
||||
</ContentControl.DataTemplates>
|
||||
</ContentControl>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</v:ChromelessWindow>
|
||||
44
src/Views/DirHistories.axaml.cs
Normal file
44
src/Views/DirHistories.axaml.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
|
||||
namespace SourceGit.Views
|
||||
{
|
||||
public partial class DirHistories : ChromelessWindow
|
||||
{
|
||||
public DirHistories()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void OnPressCommitSHA(object sender, PointerPressedEventArgs e)
|
||||
{
|
||||
if (sender is TextBlock { DataContext: Models.Commit commit } &&
|
||||
DataContext is ViewModels.DirHistories vm)
|
||||
{
|
||||
vm.NavigateToCommit(commit);
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void OnCommitSubjectDataContextChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (sender is Border border)
|
||||
ToolTip.SetTip(border, null);
|
||||
}
|
||||
|
||||
private void OnCommitSubjectPointerMoved(object sender, PointerEventArgs e)
|
||||
{
|
||||
if (sender is Border { DataContext: Models.Commit commit } border &&
|
||||
DataContext is ViewModels.DirHistories vm)
|
||||
{
|
||||
var tooltip = ToolTip.GetTip(border);
|
||||
if (tooltip == null)
|
||||
ToolTip.SetTip(border, vm.GetCommitFullMessage(commit));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
Icon="/App.ico"
|
||||
Title="{DynamicResource Text.FileHistory}"
|
||||
MinWidth="1280" MinHeight="720">
|
||||
<Grid RowDefinitions="Auto,*">
|
||||
<Grid RowDefinitions="Auto,28,1,*">
|
||||
<!-- TitleBar -->
|
||||
<Grid Grid.Row="0" Height="28" IsVisible="{Binding !#ThisControl.UseSystemWindowFrame}">
|
||||
<!-- Bottom border -->
|
||||
@@ -37,8 +37,17 @@
|
||||
<v:CaptionButtons HorizontalAlignment="Right" IsVisible="{OnPlatform True, macOS=False}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- Info -->
|
||||
<Grid Grid.Row="1" ColumnDefinitions="Auto,*" Background="{DynamicResource Brush.Popup}">
|
||||
<Path Grid.Column="0" Width="14" Height="14" Margin="8,0,6,0" Data="{StaticResource Icons.File}"/>
|
||||
<TextBlock Grid.Column="1" Text="{Binding Title, Mode=OneWay}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- Line -->
|
||||
<Rectangle Grid.Row="2" Height="0.8" HorizontalAlignment="Stretch" VerticalAlignment="Top" Fill="{DynamicResource Brush.Border0}"/>
|
||||
|
||||
<!-- Body -->
|
||||
<Grid Grid.Row="1">
|
||||
<Grid Grid.Row="3">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="300" MinWidth="300" MaxWidth="600"/>
|
||||
<ColumnDefinition Width="4"/>
|
||||
|
||||
@@ -281,11 +281,8 @@ namespace SourceGit.Views
|
||||
if (DataContext is ViewModels.CommitDetail vm &&
|
||||
sender is Grid { DataContext: ViewModels.RevisionFileTreeNode { Backend: { } obj } } grid)
|
||||
{
|
||||
if (obj.Type != Models.ObjectType.Tree)
|
||||
{
|
||||
var menu = vm.CreateRevisionFileContextMenu(obj);
|
||||
menu?.Open(grid);
|
||||
}
|
||||
var menu = vm.CreateRevisionFileContextMenu(obj);
|
||||
menu.Open(grid);
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
|
||||
Reference in New Issue
Block a user