optimize<*>: add a static method CreateMenuIcon to App. Restore working copy changes' selection state after refreshed

This commit is contained in:
leo 2024-02-27 18:26:05 +08:00
parent 9c3294c285
commit 2aacec75b4
7 changed files with 217 additions and 292 deletions

View file

@ -120,6 +120,15 @@ namespace SourceGit {
return string.Format(fmt, args);
}
public static Avalonia.Controls.Shapes.Path CreateMenuIcon(string key) {
var icon = new Avalonia.Controls.Shapes.Path();
icon.Width = 12;
icon.Height = 12;
icon.Stretch = Stretch.Uniform;
icon.Data = Current.FindResource(key) as StreamGeometry;
return icon;
}
public static TopLevel GetTopLevel() {
if (Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) {
return desktop.MainWindow;

View file

@ -1,6 +1,4 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Controls;
using Avalonia.Platform.Storage;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
@ -151,7 +149,7 @@ namespace SourceGit.ViewModels {
if (change.Index != Models.ChangeState.Deleted) {
var history = new MenuItem();
history.Header = App.Text("FileHistory");
history.Icon = CreateMenuIcon("Icons.Histories");
history.Icon = App.CreateMenuIcon("Icons.Histories");
history.Click += (_, ev) => {
var window = new Views.FileHistories() { DataContext = new FileHistories(_repo, change.Path) };
window.Show();
@ -160,7 +158,7 @@ namespace SourceGit.ViewModels {
var blame = new MenuItem();
blame.Header = App.Text("Blame");
blame.Icon = CreateMenuIcon("Icons.Blame");
blame.Icon = App.CreateMenuIcon("Icons.Blame");
blame.Click += (o, ev) => {
var window = new Views.Blame() { DataContext = new Blame(_repo, change.Path, _commit.SHA) };
window.Show();
@ -170,7 +168,7 @@ namespace SourceGit.ViewModels {
var full = Path.GetFullPath(Path.Combine(_repo, change.Path));
var explore = new MenuItem();
explore.Header = App.Text("RevealFile");
explore.Icon = CreateMenuIcon("Icons.Folder.Open");
explore.Icon = App.CreateMenuIcon("Icons.Folder.Open");
explore.IsEnabled = File.Exists(full);
explore.Click += (_, ev) => {
Native.OS.OpenInFileManager(full, true);
@ -184,7 +182,7 @@ namespace SourceGit.ViewModels {
var copyPath = new MenuItem();
copyPath.Header = App.Text("CopyPath");
copyPath.Icon = CreateMenuIcon("Icons.Copy");
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
copyPath.Click += (_, ev) => {
App.CopyText(change.Path);
ev.Handled = true;
@ -197,7 +195,7 @@ namespace SourceGit.ViewModels {
public ContextMenu CreateRevisionFileContextMenu(Models.Object file) {
var history = new MenuItem();
history.Header = App.Text("FileHistory");
history.Icon = CreateMenuIcon("Icons.Histories");
history.Icon = App.CreateMenuIcon("Icons.Histories");
history.Click += (_, ev) => {
var window = new Views.FileHistories() { DataContext = new FileHistories(_repo, file.Path) };
window.Show();
@ -206,7 +204,7 @@ namespace SourceGit.ViewModels {
var blame = new MenuItem();
blame.Header = App.Text("Blame");
blame.Icon = CreateMenuIcon("Icons.Blame");
blame.Icon = App.CreateMenuIcon("Icons.Blame");
blame.Click += (o, ev) => {
var window = new Views.Blame() { DataContext = new Blame(_repo, file.Path, _commit.SHA) };
window.Show();
@ -216,7 +214,7 @@ namespace SourceGit.ViewModels {
var full = Path.GetFullPath(Path.Combine(_repo, file.Path));
var explore = new MenuItem();
explore.Header = App.Text("RevealFile");
explore.Icon = CreateMenuIcon("Icons.Folder.Open");
explore.Icon = App.CreateMenuIcon("Icons.Folder.Open");
explore.Click += (_, ev) => {
Native.OS.OpenInFileManager(full, file.Type == Models.ObjectType.Blob);
ev.Handled = true;
@ -224,7 +222,7 @@ namespace SourceGit.ViewModels {
var saveAs = new MenuItem();
saveAs.Header = App.Text("SaveAs");
saveAs.Icon = CreateMenuIcon("Icons.Save");
saveAs.Icon = App.CreateMenuIcon("Icons.Save");
saveAs.IsEnabled = file.Type == Models.ObjectType.Blob;
saveAs.Click += async (_, ev) => {
var topLevel = App.GetTopLevel();
@ -242,7 +240,7 @@ namespace SourceGit.ViewModels {
var copyPath = new MenuItem();
copyPath.Header = App.Text("CopyPath");
copyPath.Icon = CreateMenuIcon("Icons.Copy");
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
copyPath.Click += (_, ev) => {
App.CopyText(file.Path);
ev.Handled = true;
@ -397,15 +395,6 @@ namespace SourceGit.ViewModels {
}
}
private Avalonia.Controls.Shapes.Path CreateMenuIcon(string key) {
var icon = new Avalonia.Controls.Shapes.Path();
icon.Width = 12;
icon.Height = 12;
icon.Stretch = Stretch.Uniform;
icon.Data = App.Current?.FindResource(key) as StreamGeometry;
return icon;
}
private string _repo = string.Empty;
private int _activePageIndex = 0;
private Models.Commit _commit = null;

View file

@ -1,5 +1,4 @@
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Platform.Storage;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
@ -148,7 +147,7 @@ namespace SourceGit.ViewModels {
if (current.Head != commit.SHA) {
var reset = new MenuItem();
reset.Header = CreateHighlightLabel("CommitCM.Reset", current.Name);
reset.Icon = CreateMenuIcon("Icons.Reset");
reset.Icon = App.CreateMenuIcon("Icons.Reset");
reset.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Reset(_repo, current, commit));
e.Handled = true;
@ -157,7 +156,7 @@ namespace SourceGit.ViewModels {
} else {
var reword = new MenuItem();
reword.Header = App.Text("CommitCM.Reword");
reword.Icon = CreateMenuIcon("Icons.Edit");
reword.Icon = App.CreateMenuIcon("Icons.Edit");
reword.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Reword(_repo, commit));
e.Handled = true;
@ -166,7 +165,7 @@ namespace SourceGit.ViewModels {
var squash = new MenuItem();
squash.Header = App.Text("CommitCM.Squash");
squash.Icon = CreateMenuIcon("Icons.SquashIntoParent");
squash.Icon = App.CreateMenuIcon("Icons.SquashIntoParent");
squash.IsEnabled = commit.Parents.Count == 1;
squash.Click += (o, e) => {
if (commit.Parents.Count == 1) {
@ -182,7 +181,7 @@ namespace SourceGit.ViewModels {
if (!commit.IsMerged) {
var rebase = new MenuItem();
rebase.Header = CreateHighlightLabel("CommitCM.Rebase", current.Name);
rebase.Icon = CreateMenuIcon("Icons.Rebase");
rebase.Icon = App.CreateMenuIcon("Icons.Rebase");
rebase.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Rebase(_repo, current, commit));
e.Handled = true;
@ -191,7 +190,7 @@ namespace SourceGit.ViewModels {
var cherryPick = new MenuItem();
cherryPick.Header = App.Text("CommitCM.CherryPick");
cherryPick.Icon = CreateMenuIcon("Icons.CherryPick");
cherryPick.Icon = App.CreateMenuIcon("Icons.CherryPick");
cherryPick.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CherryPick(_repo, commit));
e.Handled = true;
@ -200,7 +199,7 @@ namespace SourceGit.ViewModels {
} else {
var revert = new MenuItem();
revert.Header = App.Text("CommitCM.Revert");
revert.Icon = CreateMenuIcon("Icons.Undo");
revert.Icon = App.CreateMenuIcon("Icons.Undo");
revert.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Revert(_repo, commit));
e.Handled = true;
@ -211,7 +210,7 @@ namespace SourceGit.ViewModels {
menu.Items.Add(new MenuItem() { Header = "-" });
var createBranch = new MenuItem();
createBranch.Icon = CreateMenuIcon("Icons.Branch.Add");
createBranch.Icon = App.CreateMenuIcon("Icons.Branch.Add");
createBranch.Header = App.Text("CreateBranch");
createBranch.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateBranch(_repo, commit));
@ -220,7 +219,7 @@ namespace SourceGit.ViewModels {
menu.Items.Add(createBranch);
var createTag = new MenuItem();
createTag.Icon = CreateMenuIcon("Icons.Tag.Add");
createTag.Icon = App.CreateMenuIcon("Icons.Tag.Add");
createTag.Header = App.Text("CreateTag");
createTag.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateTag(_repo, commit));
@ -230,7 +229,7 @@ namespace SourceGit.ViewModels {
menu.Items.Add(new MenuItem() { Header = "-" });
var saveToPatch = new MenuItem();
saveToPatch.Icon = CreateMenuIcon("Icons.Diff");
saveToPatch.Icon = App.CreateMenuIcon("Icons.Diff");
saveToPatch.Header = App.Text("CommitCM.SaveAsPatch");
saveToPatch.Click += async (_, e) => {
var topLevel = App.GetTopLevel();
@ -248,7 +247,7 @@ namespace SourceGit.ViewModels {
menu.Items.Add(saveToPatch);
var archive = new MenuItem();
archive.Icon = CreateMenuIcon("Icons.Archive");
archive.Icon = App.CreateMenuIcon("Icons.Archive");
archive.Header = App.Text("Archive");
archive.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Archive(_repo, commit));
@ -259,7 +258,7 @@ namespace SourceGit.ViewModels {
var copySHA = new MenuItem();
copySHA.Header = App.Text("CommitCM.CopySHA");
copySHA.Icon = CreateMenuIcon("Icons.Copy");
copySHA.Icon = App.CreateMenuIcon("Icons.Copy");
copySHA.Click += (o, e) => {
App.CopyText(commit.SHA);
e.Handled = true;
@ -270,7 +269,7 @@ namespace SourceGit.ViewModels {
private void FillCurrentBranchMenu(ContextMenu menu, Models.Branch current) {
var submenu = new MenuItem();
submenu.Icon = CreateMenuIcon("Icons.Branch");
submenu.Icon = App.CreateMenuIcon("Icons.Branch");
submenu.Header = current.Name;
var dirty = !string.IsNullOrEmpty(current.UpstreamTrackStatus);
@ -279,7 +278,7 @@ namespace SourceGit.ViewModels {
var fastForward = new MenuItem();
fastForward.Header = CreateHighlightLabel("BranchCM.FastForward", upstream);
fastForward.Icon = CreateMenuIcon("Icons.FastForward");
fastForward.Icon = App.CreateMenuIcon("Icons.FastForward");
fastForward.IsEnabled = dirty;
fastForward.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowAndStartPopup(new Merge(_repo, upstream, current.Name));
@ -289,7 +288,7 @@ namespace SourceGit.ViewModels {
var pull = new MenuItem();
pull.Header = CreateHighlightLabel("BranchCM.Pull", upstream);
pull.Icon = CreateMenuIcon("Icons.Pull");
pull.Icon = App.CreateMenuIcon("Icons.Pull");
pull.IsEnabled = dirty;
pull.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Pull(_repo, null));
@ -300,7 +299,7 @@ namespace SourceGit.ViewModels {
var push = new MenuItem();
push.Header = CreateHighlightLabel("BranchCM.Push", current.Name);
push.Icon = CreateMenuIcon("Icons.Push");
push.Icon = App.CreateMenuIcon("Icons.Push");
push.IsEnabled = _repo.Remotes.Count > 0 && dirty;
push.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Push(_repo, current));
@ -313,7 +312,7 @@ namespace SourceGit.ViewModels {
if (type != Models.GitFlowBranchType.None) {
var finish = new MenuItem();
finish.Header = CreateHighlightLabel("BranchCM.Finish", current.Name);
finish.Icon = CreateMenuIcon("Icons.Flow");
finish.Icon = App.CreateMenuIcon("Icons.Flow");
finish.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new GitFlowFinish(_repo, current, type));
e.Handled = true;
@ -324,7 +323,7 @@ namespace SourceGit.ViewModels {
var rename = new MenuItem();
rename.Header = CreateHighlightLabel("BranchCM.Rename", current.Name);
rename.Icon = CreateMenuIcon("Icons.Rename");
rename.Icon = App.CreateMenuIcon("Icons.Rename");
rename.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new RenameBranch(_repo, current));
e.Handled = true;
@ -336,12 +335,12 @@ namespace SourceGit.ViewModels {
private void FillOtherLocalBranchMenu(ContextMenu menu, Models.Branch branch, Models.Branch current, bool merged) {
var submenu = new MenuItem();
submenu.Icon = CreateMenuIcon("Icons.Branch");
submenu.Icon = App.CreateMenuIcon("Icons.Branch");
submenu.Header = branch.Name;
var checkout = new MenuItem();
checkout.Header = CreateHighlightLabel("BranchCM.Checkout", branch.Name);
checkout.Icon = CreateMenuIcon("Icons.Check");
checkout.Icon = App.CreateMenuIcon("Icons.Check");
checkout.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowAndStartPopup(new Checkout(_repo, branch.Name));
e.Handled = true;
@ -350,7 +349,7 @@ namespace SourceGit.ViewModels {
var merge = new MenuItem();
merge.Header = CreateHighlightLabel("BranchCM.Merge", branch.Name, current.Name);
merge.Icon = CreateMenuIcon("Icons.Merge");
merge.Icon = App.CreateMenuIcon("Icons.Merge");
merge.IsEnabled = !merged;
merge.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Merge(_repo, branch.Name, current.Name));
@ -363,7 +362,7 @@ namespace SourceGit.ViewModels {
if (type != Models.GitFlowBranchType.None) {
var finish = new MenuItem();
finish.Header = CreateHighlightLabel("BranchCM.Finish", branch.Name);
finish.Icon = CreateMenuIcon("Icons.Flow");
finish.Icon = App.CreateMenuIcon("Icons.Flow");
finish.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new GitFlowFinish(_repo, branch, type));
e.Handled = true;
@ -374,7 +373,7 @@ namespace SourceGit.ViewModels {
var rename = new MenuItem();
rename.Header = CreateHighlightLabel("BranchCM.Rename", branch.Name);
rename.Icon = CreateMenuIcon("Icons.Rename");
rename.Icon = App.CreateMenuIcon("Icons.Rename");
rename.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new RenameBranch(_repo, branch));
e.Handled = true;
@ -383,7 +382,7 @@ namespace SourceGit.ViewModels {
var delete = new MenuItem();
delete.Header = CreateHighlightLabel("BranchCM.Delete", branch.Name);
delete.Icon = CreateMenuIcon("Icons.Clear");
delete.Icon = App.CreateMenuIcon("Icons.Clear");
delete.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteBranch(_repo, branch));
e.Handled = true;
@ -397,12 +396,12 @@ namespace SourceGit.ViewModels {
var name = $"{branch.Remote}/{branch.Name}";
var submenu = new MenuItem();
submenu.Icon = CreateMenuIcon("Icons.Branch");
submenu.Icon = App.CreateMenuIcon("Icons.Branch");
submenu.Header = name;
var checkout = new MenuItem();
checkout.Header = CreateHighlightLabel("BranchCM.Checkout", name);
checkout.Icon = CreateMenuIcon("Icons.Check");
checkout.Icon = App.CreateMenuIcon("Icons.Check");
checkout.Click += (o, e) => {
foreach (var b in _repo.Branches) {
if (b.IsLocal && b.Upstream == branch.FullName) {
@ -419,7 +418,7 @@ namespace SourceGit.ViewModels {
var merge = new MenuItem();
merge.Header = CreateHighlightLabel("BranchCM.Merge", name, current.Name);
merge.Icon = CreateMenuIcon("Icons.Merge");
merge.Icon = App.CreateMenuIcon("Icons.Merge");
merge.IsEnabled = !merged;
merge.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Merge(_repo, name, current.Name));
@ -431,7 +430,7 @@ namespace SourceGit.ViewModels {
var delete = new MenuItem();
delete.Header = CreateHighlightLabel("BranchCM.Delete", name);
delete.Icon = CreateMenuIcon("Icons.Clear");
delete.Icon = App.CreateMenuIcon("Icons.Clear");
delete.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteBranch(_repo, branch));
e.Handled = true;
@ -444,12 +443,12 @@ namespace SourceGit.ViewModels {
private void FillTagMenu(ContextMenu menu, Models.Tag tag) {
var submenu = new MenuItem();
submenu.Header = tag.Name;
submenu.Icon = CreateMenuIcon("Icons.Tag");
submenu.Icon = App.CreateMenuIcon("Icons.Tag");
submenu.MinWidth = 200;
var push = new MenuItem();
push.Header = CreateHighlightLabel("TagCM.Push", tag.Name);
push.Icon = CreateMenuIcon("Icons.Push");
push.Icon = App.CreateMenuIcon("Icons.Push");
push.IsEnabled = _repo.Remotes.Count > 0;
push.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new PushTag(_repo, tag));
@ -459,7 +458,7 @@ namespace SourceGit.ViewModels {
var delete = new MenuItem();
delete.Header = CreateHighlightLabel("TagCM.Delete", tag.Name);
delete.Icon = CreateMenuIcon("Icons.Clear");
delete.Icon = App.CreateMenuIcon("Icons.Clear");
delete.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteTag(_repo, tag));
e.Handled = true;
@ -476,15 +475,6 @@ namespace SourceGit.ViewModels {
return label;
}
private Avalonia.Controls.Shapes.Path CreateMenuIcon(string key) {
var icon = new Avalonia.Controls.Shapes.Path();
icon.Width = 12;
icon.Height = 12;
icon.Stretch = Stretch.Uniform;
icon.Data = App.Current?.FindResource(key) as StreamGeometry;
return icon;
}
private Repository _repo = null;
private double _dataGridRowHeight = 28;
private bool _isLoading = true;

View file

@ -1,6 +1,5 @@
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using System;
@ -580,7 +579,7 @@ namespace SourceGit.ViewModels {
var push = new MenuItem();
push.Header = CreateHighlightLabel("BranchCM.Push", branch.Name);
push.Icon = CreateMenuIcon("Icons.Push");
push.Icon = App.CreateMenuIcon("Icons.Push");
push.IsEnabled = Remotes.Count > 0;
push.Click += (_, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Push(this, branch));
@ -590,7 +589,7 @@ namespace SourceGit.ViewModels {
if (branch.IsCurrent) {
var discard = new MenuItem();
discard.Header = App.Text("BranchCM.DiscardAll");
discard.Icon = CreateMenuIcon("Icons.Undo");
discard.Icon = App.CreateMenuIcon("Icons.Undo");
discard.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Discard(this));
e.Handled = true;
@ -603,7 +602,7 @@ namespace SourceGit.ViewModels {
var upstream = branch.Upstream.Substring(13);
var fastForward = new MenuItem();
fastForward.Header = CreateHighlightLabel("BranchCM.FastForward", upstream);
fastForward.Icon = CreateMenuIcon("Icons.FastForward");
fastForward.Icon = App.CreateMenuIcon("Icons.FastForward");
fastForward.IsEnabled = !string.IsNullOrEmpty(branch.UpstreamTrackStatus) && branch.UpstreamTrackStatus.IndexOf('↑') < 0;
fastForward.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowAndStartPopup(new Merge(this, upstream, branch.Name));
@ -612,7 +611,7 @@ namespace SourceGit.ViewModels {
var pull = new MenuItem();
pull.Header = CreateHighlightLabel("BranchCM.Pull", upstream);
pull.Icon = CreateMenuIcon("Icons.Pull");
pull.Icon = App.CreateMenuIcon("Icons.Pull");
pull.IsEnabled = !string.IsNullOrEmpty(branch.UpstreamTrackStatus);
pull.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Pull(this, null));
@ -629,7 +628,7 @@ namespace SourceGit.ViewModels {
var checkout = new MenuItem();
checkout.Header = CreateHighlightLabel("BranchCM.Checkout", branch.Name);
checkout.Icon = CreateMenuIcon("Icons.Check");
checkout.Icon = App.CreateMenuIcon("Icons.Check");
checkout.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowAndStartPopup(new Checkout(this, branch.Name));
e.Handled = true;
@ -640,7 +639,7 @@ namespace SourceGit.ViewModels {
if (upstream != null) {
var fastForward = new MenuItem();
fastForward.Header = CreateHighlightLabel("BranchCM.FastForward", $"{upstream.Remote}/{upstream.Name}");
fastForward.Icon = CreateMenuIcon("Icons.FastForward");
fastForward.Icon = App.CreateMenuIcon("Icons.FastForward");
fastForward.IsEnabled = !string.IsNullOrEmpty(branch.UpstreamTrackStatus) && branch.UpstreamTrackStatus.IndexOf('↑') < 0;
fastForward.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowAndStartPopup(new FastForwardWithoutCheckout(this, branch, upstream));
@ -656,7 +655,7 @@ namespace SourceGit.ViewModels {
var merge = new MenuItem();
merge.Header = CreateHighlightLabel("BranchCM.Merge", branch.Name, current.Name);
merge.Icon = CreateMenuIcon("Icons.Merge");
merge.Icon = App.CreateMenuIcon("Icons.Merge");
merge.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Merge(this, branch.Name, current.Name));
e.Handled = true;
@ -664,7 +663,7 @@ namespace SourceGit.ViewModels {
var rebase = new MenuItem();
rebase.Header = CreateHighlightLabel("BranchCM.Rebase", current.Name, branch.Name);
rebase.Icon = CreateMenuIcon("Icons.Rebase");
rebase.Icon = App.CreateMenuIcon("Icons.Rebase");
rebase.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Rebase(this, current, branch));
e.Handled = true;
@ -678,7 +677,7 @@ namespace SourceGit.ViewModels {
if (type != Models.GitFlowBranchType.None) {
var finish = new MenuItem();
finish.Header = CreateHighlightLabel("BranchCM.Finish", branch.Name);
finish.Icon = CreateMenuIcon("Icons.Flow");
finish.Icon = App.CreateMenuIcon("Icons.Flow");
finish.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new GitFlowFinish(this, branch, type));
e.Handled = true;
@ -689,7 +688,7 @@ namespace SourceGit.ViewModels {
var rename = new MenuItem();
rename.Header = CreateHighlightLabel("BranchCM.Rename", branch.Name);
rename.Icon = CreateMenuIcon("Icons.Rename");
rename.Icon = App.CreateMenuIcon("Icons.Rename");
rename.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new RenameBranch(this, branch));
e.Handled = true;
@ -697,7 +696,7 @@ namespace SourceGit.ViewModels {
var delete = new MenuItem();
delete.Header = CreateHighlightLabel("BranchCM.Delete", branch.Name);
delete.Icon = CreateMenuIcon("Icons.Clear");
delete.Icon = App.CreateMenuIcon("Icons.Clear");
delete.IsEnabled = !branch.IsCurrent;
delete.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteBranch(this, branch));
@ -705,7 +704,7 @@ namespace SourceGit.ViewModels {
};
var createBranch = new MenuItem();
createBranch.Icon = CreateMenuIcon("Icons.Branch.Add");
createBranch.Icon = App.CreateMenuIcon("Icons.Branch.Add");
createBranch.Header = App.Text("CreateBranch");
createBranch.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateBranch(this, branch));
@ -713,7 +712,7 @@ namespace SourceGit.ViewModels {
};
var createTag = new MenuItem();
createTag.Icon = CreateMenuIcon("Icons.Tag.Add");
createTag.Icon = App.CreateMenuIcon("Icons.Tag.Add");
createTag.Header = App.Text("CreateTag");
createTag.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateTag(this, branch));
@ -736,13 +735,13 @@ namespace SourceGit.ViewModels {
if (remoteBranches.Count > 0) {
var tracking = new MenuItem();
tracking.Header = App.Text("BranchCM.Tracking");
tracking.Icon = CreateMenuIcon("Icons.Branch");
tracking.Icon = App.CreateMenuIcon("Icons.Branch");
foreach (var b in remoteBranches) {
var upstream = b.FullName.Replace("refs/remotes/", "");
var target = new MenuItem();
target.Header = upstream;
if (branch.Upstream == b.FullName) target.Icon = CreateMenuIcon("Icons.Check");
if (branch.Upstream == b.FullName) target.Icon = App.CreateMenuIcon("Icons.Check");
target.Click += (o, e) => {
if (Commands.Branch.SetUpstream(_fullpath, branch.Name, upstream)) {
@ -769,7 +768,7 @@ namespace SourceGit.ViewModels {
}
var archive = new MenuItem();
archive.Icon = CreateMenuIcon("Icons.Archive");
archive.Icon = App.CreateMenuIcon("Icons.Archive");
archive.Header = App.Text("Archive");
archive.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Archive(this, branch));
@ -780,7 +779,7 @@ namespace SourceGit.ViewModels {
var copy = new MenuItem();
copy.Header = App.Text("BranchCM.CopyName");
copy.Icon = CreateMenuIcon("Icons.Copy");
copy.Icon = App.CreateMenuIcon("Icons.Copy");
copy.Click += (o, e) => {
App.CopyText(branch.Name);
e.Handled = true;
@ -795,7 +794,7 @@ namespace SourceGit.ViewModels {
var fetch = new MenuItem();
fetch.Header = App.Text("RemoteCM.Fetch");
fetch.Icon = CreateMenuIcon("Icons.Fetch");
fetch.Icon = App.CreateMenuIcon("Icons.Fetch");
fetch.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowAndStartPopup(new Fetch(this, remote));
e.Handled = true;
@ -803,7 +802,7 @@ namespace SourceGit.ViewModels {
var prune = new MenuItem();
prune.Header = App.Text("RemoteCM.Prune");
prune.Icon = CreateMenuIcon("Icons.Clear2");
prune.Icon = App.CreateMenuIcon("Icons.Clear2");
prune.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowAndStartPopup(new PruneRemote(this, remote));
e.Handled = true;
@ -811,7 +810,7 @@ namespace SourceGit.ViewModels {
var edit = new MenuItem();
edit.Header = App.Text("RemoteCM.Edit");
edit.Icon = CreateMenuIcon("Icons.Edit");
edit.Icon = App.CreateMenuIcon("Icons.Edit");
edit.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new EditRemote(this, remote));
e.Handled = true;
@ -819,7 +818,7 @@ namespace SourceGit.ViewModels {
var delete = new MenuItem();
delete.Header = App.Text("RemoteCM.Delete");
delete.Icon = CreateMenuIcon("Icons.Clear");
delete.Icon = App.CreateMenuIcon("Icons.Clear");
delete.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteRemote(this, remote));
e.Handled = true;
@ -827,7 +826,7 @@ namespace SourceGit.ViewModels {
var copy = new MenuItem();
copy.Header = App.Text("RemoteCM.CopyURL");
copy.Icon = CreateMenuIcon("Icons.Copy");
copy.Icon = App.CreateMenuIcon("Icons.Copy");
copy.Click += (o, e) => {
App.CopyText(remote.URL);
e.Handled = true;
@ -849,7 +848,7 @@ namespace SourceGit.ViewModels {
var checkout = new MenuItem();
checkout.Header = CreateHighlightLabel("BranchCM.Checkout", $"{branch.Remote}/{branch.Name}");
checkout.Icon = CreateMenuIcon("Icons.Check");
checkout.Icon = App.CreateMenuIcon("Icons.Check");
checkout.Click += (o, e) => {
foreach (var b in Branches) {
if (b.IsLocal && b.Upstream == branch.FullName) {
@ -868,7 +867,7 @@ namespace SourceGit.ViewModels {
if (current != null) {
var pull = new MenuItem();
pull.Header = CreateHighlightLabel("BranchCM.PullInto", $"{branch.Remote}/{branch.Name}", current.Name);
pull.Icon = CreateMenuIcon("Icons.Pull");
pull.Icon = App.CreateMenuIcon("Icons.Pull");
pull.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Pull(this, branch));
e.Handled = true;
@ -876,7 +875,7 @@ namespace SourceGit.ViewModels {
var merge = new MenuItem();
merge.Header = CreateHighlightLabel("BranchCM.Merge", $"{branch.Remote}/{branch.Name}", current.Name);
merge.Icon = CreateMenuIcon("Icons.Merge");
merge.Icon = App.CreateMenuIcon("Icons.Merge");
merge.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Merge(this, $"{branch.Remote}/{branch.Name}", current.Name));
e.Handled = true;
@ -884,7 +883,7 @@ namespace SourceGit.ViewModels {
var rebase = new MenuItem();
rebase.Header = CreateHighlightLabel("BranchCM.Rebase", current.Name, $"{branch.Remote}/{branch.Name}");
rebase.Icon = CreateMenuIcon("Icons.Rebase");
rebase.Icon = App.CreateMenuIcon("Icons.Rebase");
rebase.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Rebase(this, current, branch));
e.Handled = true;
@ -898,14 +897,14 @@ namespace SourceGit.ViewModels {
var delete = new MenuItem();
delete.Header = CreateHighlightLabel("BranchCM.Delete", $"{branch.Remote}/{branch.Name}");
delete.Icon = CreateMenuIcon("Icons.Clear");
delete.Icon = App.CreateMenuIcon("Icons.Clear");
delete.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteBranch(this, branch));
e.Handled = true;
};
var createBranch = new MenuItem();
createBranch.Icon = CreateMenuIcon("Icons.Branch.Add");
createBranch.Icon = App.CreateMenuIcon("Icons.Branch.Add");
createBranch.Header = App.Text("CreateBranch");
createBranch.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateBranch(this, branch));
@ -913,7 +912,7 @@ namespace SourceGit.ViewModels {
};
var createTag = new MenuItem();
createTag.Icon = CreateMenuIcon("Icons.Tag.Add");
createTag.Icon = App.CreateMenuIcon("Icons.Tag.Add");
createTag.Header = App.Text("CreateTag");
createTag.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateTag(this, branch));
@ -921,7 +920,7 @@ namespace SourceGit.ViewModels {
};
var archive = new MenuItem();
archive.Icon = CreateMenuIcon("Icons.Archive");
archive.Icon = App.CreateMenuIcon("Icons.Archive");
archive.Header = App.Text("Archive");
archive.Click += (o, e) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Archive(this, branch));
@ -930,7 +929,7 @@ namespace SourceGit.ViewModels {
var copy = new MenuItem();
copy.Header = App.Text("BranchCM.CopyName");
copy.Icon = CreateMenuIcon("Icons.Copy");
copy.Icon = App.CreateMenuIcon("Icons.Copy");
copy.Click += (o, e) => {
App.CopyText(branch.Remote + "/" + branch.Name);
e.Handled = true;
@ -949,7 +948,7 @@ namespace SourceGit.ViewModels {
public ContextMenu CreateContextMenuForTag(Models.Tag tag) {
var createBranch = new MenuItem();
createBranch.Icon = CreateMenuIcon("Icons.Branch.Add");
createBranch.Icon = App.CreateMenuIcon("Icons.Branch.Add");
createBranch.Header = App.Text("CreateBranch");
createBranch.Click += (o, ev) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateBranch(this, tag));
@ -958,7 +957,7 @@ namespace SourceGit.ViewModels {
var pushTag = new MenuItem();
pushTag.Header = CreateHighlightLabel("TagCM.Push", tag.Name);
pushTag.Icon = CreateMenuIcon("Icons.Push");
pushTag.Icon = App.CreateMenuIcon("Icons.Push");
pushTag.IsEnabled = Remotes.Count > 0;
pushTag.Click += (o, ev) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new PushTag(this, tag));
@ -967,14 +966,14 @@ namespace SourceGit.ViewModels {
var deleteTag = new MenuItem();
deleteTag.Header = CreateHighlightLabel("TagCM.Delete", tag.Name);
deleteTag.Icon = CreateMenuIcon("Icons.Clear");
deleteTag.Icon = App.CreateMenuIcon("Icons.Clear");
deleteTag.Click += (o, ev) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteTag(this, tag));
ev.Handled = true;
};
var archive = new MenuItem();
archive.Icon = CreateMenuIcon("Icons.Archive");
archive.Icon = App.CreateMenuIcon("Icons.Archive");
archive.Header = App.Text("Archive");
archive.Click += (o, ev) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Archive(this, tag));
@ -983,7 +982,7 @@ namespace SourceGit.ViewModels {
var copy = new MenuItem();
copy.Header = App.Text("TagCM.Copy");
copy.Icon = CreateMenuIcon("Icons.Copy");
copy.Icon = App.CreateMenuIcon("Icons.Copy");
copy.Click += (o, ev) => {
App.CopyText(tag.Name);
ev.Handled = true;
@ -1004,7 +1003,7 @@ namespace SourceGit.ViewModels {
public ContextMenu CreateContextMenuForSubmodule(string submodule) {
var open = new MenuItem();
open.Header = App.Text("Submodule.Open");
open.Icon = CreateMenuIcon("Icons.Folder.Open");
open.Icon = App.CreateMenuIcon("Icons.Folder.Open");
open.Click += (o, ev) => {
var root = Path.GetFullPath(Path.Combine(_fullpath, submodule));
var gitDir = new Commands.QueryGitDir(root).Result();
@ -1026,7 +1025,7 @@ namespace SourceGit.ViewModels {
var copy = new MenuItem();
copy.Header = App.Text("Submodule.CopyPath");
copy.Icon = CreateMenuIcon("Icons.Copy");
copy.Icon = App.CreateMenuIcon("Icons.Copy");
copy.Click += (o, ev) => {
App.CopyText(submodule);
ev.Handled = true;
@ -1034,7 +1033,7 @@ namespace SourceGit.ViewModels {
var rm = new MenuItem();
rm.Header = App.Text("Submodule.Remove");
rm.Icon = CreateMenuIcon("Icons.Clear");
rm.Icon = App.CreateMenuIcon("Icons.Clear");
rm.Click += (o, ev) => {
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteSubmodule(this, submodule));
ev.Handled = true;
@ -1054,15 +1053,6 @@ namespace SourceGit.ViewModels {
return label;
}
private Avalonia.Controls.Shapes.Path CreateMenuIcon(string key) {
var icon = new Avalonia.Controls.Shapes.Path();
icon.Width = 12;
icon.Height = 12;
icon.Stretch = Stretch.Uniform;
icon.Data = App.Current?.FindResource(key) as StreamGeometry;
return icon;
}
private string _fullpath = string.Empty;
private string _gitDir = string.Empty;
private Models.GitFlow _gitflow = new Models.GitFlow();

View file

@ -1,6 +1,4 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Controls;
using Avalonia.Platform.Storage;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
@ -14,11 +12,6 @@ namespace SourceGit.ViewModels {
public Models.Change Change { get; set; }
}
public class ViewChangeDetailContext {
public string FilePath { get; set; } = string.Empty;
public bool IsUnstaged { get; set; } = false;
}
public class WorkingCopy : ObservableObject {
public bool IsStaging {
get => _isStaging;
@ -54,6 +47,28 @@ namespace SourceGit.ViewModels {
get => _count;
}
public Models.Change SelectedUnstagedChange {
get => _selectedUnstagedChange;
set {
if (SetProperty(ref _selectedUnstagedChange, value) && value != null) {
SelectedStagedChange = null;
SelectedStagedTreeNode = null;
SetDetail(value, true);
}
}
}
public Models.Change SelectedStagedChange {
get => _selectedStagedChange;
set {
if (SetProperty(ref _selectedStagedChange, value) && value != null) {
SelectedUnstagedChange = null;
SelectedUnstagedTreeNode = null;
SetDetail(value, false);
}
}
}
public List<FileTreeNode> UnstagedTree {
get => _unstagedTree;
private set => SetProperty(ref _unstagedTree, value);
@ -64,6 +79,44 @@ namespace SourceGit.ViewModels {
private set => SetProperty(ref _stagedTree, value);
}
public FileTreeNode SelectedUnstagedTreeNode {
get => _selectedUnstagedTreeNode;
set {
if (SetProperty(ref _selectedUnstagedTreeNode, value)) {
if (value == null) {
SelectedUnstagedChange = null;
} else {
SelectedUnstagedChange = value.Backend as Models.Change;
SelectedStagedTreeNode = null;
SelectedStagedChange = null;
if (value.IsFolder) {
SetDetail(null, true);
}
}
}
}
}
public FileTreeNode SelectedStagedTreeNode {
get => _selectedStagedTreeNode;
set {
if (SetProperty(ref _selectedStagedTreeNode, value)) {
if (value == null) {
SelectedStagedChange = null;
} else {
SelectedStagedChange = value.Backend as Models.Change;
SelectedUnstagedTreeNode = null;
SelectedUnstagedChange = null;
if (value.IsFolder) {
SetDetail(null, false);
}
}
}
}
}
public object DetailContext {
get => _detailContext;
private set => SetProperty(ref _detailContext, value);
@ -84,7 +137,10 @@ namespace SourceGit.ViewModels {
if (_staged != null) _staged.Clear();
if (_unstagedTree != null) _unstagedTree.Clear();
if (_stagedTree != null) _stagedTree.Clear();
_lastViewChange = null;
_selectedUnstagedChange = null;
_selectedStagedChange = null;
_selectedUnstagedTreeNode = null;
_selectedStagedTreeNode = null;
_detailContext = null;
_commitMessage = string.Empty;
}
@ -93,24 +149,34 @@ namespace SourceGit.ViewModels {
var unstaged = new List<Models.Change>();
var staged = new List<Models.Change>();
var viewFile = _lastViewChange == null ? string.Empty : _lastViewChange.FilePath;
var viewFile = string.Empty;
var lastSelectedIsUnstaged = false;
if (_selectedUnstagedChange != null) {
viewFile = _selectedUnstagedChange.Path;
lastSelectedIsUnstaged = true;
} else if (_selectedStagedChange != null) {
viewFile = _selectedStagedChange.Path;
}
var viewChange = null as Models.Change;
var hasConflict = false;
foreach (var c in changes) {
if (c.Path == viewFile) {
viewChange = c;
}
if (c.Index == Models.ChangeState.Modified
|| c.Index == Models.ChangeState.Added
|| c.Index == Models.ChangeState.Deleted
|| c.Index == Models.ChangeState.Renamed) {
staged.Add(c);
if (!lastSelectedIsUnstaged && c.Path == viewFile) {
viewChange = c;
}
}
if (c.WorkTree != Models.ChangeState.None) {
unstaged.Add(c);
hasConflict |= c.IsConflit;
if (lastSelectedIsUnstaged && c.Path == viewFile) {
viewChange = c;
}
}
}
@ -125,7 +191,23 @@ namespace SourceGit.ViewModels {
UnstagedTree = unstagedTree;
StagedTree = stagedTree;
_isLoadingData = false;
SetDetail(viewChange, _lastViewChange == null || _lastViewChange.IsUnstaged);
// Restore last selection states.
if (viewChange != null) {
if (lastSelectedIsUnstaged) {
SelectedUnstagedChange = viewChange;
SelectedUnstagedTreeNode = FileTreeNode.SelectByPath(_unstagedTree, viewFile);
} else {
SelectedStagedChange = viewChange;
SelectedStagedTreeNode = FileTreeNode.SelectByPath(_stagedTree, viewFile);
}
} else {
SelectedUnstagedChange = null;
SelectedUnstagedTreeNode = null;
SelectedStagedChange = null;
SelectedStagedTreeNode = null;
SetDetail(null, false);
}
});
return hasConflict;
@ -135,13 +217,10 @@ namespace SourceGit.ViewModels {
if (_isLoadingData) return;
if (change == null) {
_lastViewChange = null;
DetailContext = null;
} else if (change.IsConflit) {
_lastViewChange = new ViewChangeDetailContext() { FilePath = change.Path, IsUnstaged = isUnstaged };
DetailContext = new ConflictContext() { Change = change };
} else {
_lastViewChange = new ViewChangeDetailContext() { FilePath = change.Path, IsUnstaged = isUnstaged };
DetailContext = new DiffContext(_repo.FullPath, new Models.DiffOption(change, isUnstaged));
}
}
@ -284,7 +363,7 @@ namespace SourceGit.ViewModels {
var explore = new MenuItem();
explore.Header = App.Text("RevealFile");
explore.Icon = CreateMenuIcon("Icons.Folder.Open");
explore.Icon = App.CreateMenuIcon("Icons.Folder.Open");
explore.IsEnabled = File.Exists(path) || Directory.Exists(path);
explore.Click += (_, e) => {
Native.OS.OpenInFileManager(path, true);
@ -293,7 +372,7 @@ namespace SourceGit.ViewModels {
var openWith = new MenuItem();
openWith.Header = App.Text("OpenWith");
openWith.Icon = CreateMenuIcon("Icons.OpenWith");
openWith.Icon = App.CreateMenuIcon("Icons.OpenWith");
openWith.IsEnabled = File.Exists(path);
openWith.Click += (_, e) => {
Native.OS.OpenWithDefaultEditor(path);
@ -302,7 +381,7 @@ namespace SourceGit.ViewModels {
var stage = new MenuItem();
stage.Header = App.Text("FileCM.Stage");
stage.Icon = CreateMenuIcon("Icons.File.Add");
stage.Icon = App.CreateMenuIcon("Icons.File.Add");
stage.Click += (_, e) => {
StageChanges(changes);
e.Handled = true;
@ -310,7 +389,7 @@ namespace SourceGit.ViewModels {
var discard = new MenuItem();
discard.Header = App.Text("FileCM.Discard");
discard.Icon = CreateMenuIcon("Icons.Undo");
discard.Icon = App.CreateMenuIcon("Icons.Undo");
discard.Click += (_, e) => {
Discard(changes);
e.Handled = true;
@ -318,7 +397,7 @@ namespace SourceGit.ViewModels {
var stash = new MenuItem();
stash.Header = App.Text("FileCM.Stash");
stash.Icon = CreateMenuIcon("Icons.Stashes");
stash.Icon = App.CreateMenuIcon("Icons.Stashes");
stash.Click += (_, e) => {
if (PopupHost.CanCreatePopup()) {
PopupHost.ShowPopup(new StashChanges(_repo, changes, false));
@ -328,7 +407,7 @@ namespace SourceGit.ViewModels {
var patch = new MenuItem();
patch.Header = App.Text("FileCM.SaveAsPatch");
patch.Icon = CreateMenuIcon("Icons.Diff");
patch.Icon = App.CreateMenuIcon("Icons.Diff");
patch.Click += async (_, e) => {
var topLevel = App.GetTopLevel();
if (topLevel == null) return;
@ -349,7 +428,7 @@ namespace SourceGit.ViewModels {
var history = new MenuItem();
history.Header = App.Text("FileHistory");
history.Icon = CreateMenuIcon("Icons.Histories");
history.Icon = App.CreateMenuIcon("Icons.Histories");
history.Click += (_, e) => {
var window = new Views.FileHistories() { DataContext = new FileHistories(_repo.FullPath, change.Path) };
window.Show();
@ -358,7 +437,7 @@ namespace SourceGit.ViewModels {
var assumeUnchanged = new MenuItem();
assumeUnchanged.Header = App.Text("FileCM.AssumeUnchanged");
assumeUnchanged.Icon = CreateMenuIcon("Icons.File.Ignore");
assumeUnchanged.Icon = App.CreateMenuIcon("Icons.File.Ignore");
assumeUnchanged.IsEnabled = change.WorkTree != Models.ChangeState.Untracked;
assumeUnchanged.Click += (_, e) => {
new Commands.AssumeUnchanged(_repo.FullPath).Add(change.Path);
@ -367,7 +446,7 @@ namespace SourceGit.ViewModels {
var copy = new MenuItem();
copy.Header = App.Text("CopyPath");
copy.Icon = CreateMenuIcon("Icons.Copy");
copy.Icon = App.CreateMenuIcon("Icons.Copy");
copy.Click += (_, e) => {
App.CopyText(change.Path);
e.Handled = true;
@ -388,7 +467,7 @@ namespace SourceGit.ViewModels {
} else {
var stage = new MenuItem();
stage.Header = App.Text("FileCM.StageMulti", changes.Count);
stage.Icon = CreateMenuIcon("Icons.File.Add");
stage.Icon = App.CreateMenuIcon("Icons.File.Add");
stage.Click += (_, e) => {
StageChanges(changes);
e.Handled = true;
@ -396,7 +475,7 @@ namespace SourceGit.ViewModels {
var discard = new MenuItem();
discard.Header = App.Text("FileCM.DiscardMulti", changes.Count);
discard.Icon = CreateMenuIcon("Icons.Undo");
discard.Icon = App.CreateMenuIcon("Icons.Undo");
discard.Click += (_, e) => {
Discard(changes);
e.Handled = true;
@ -404,7 +483,7 @@ namespace SourceGit.ViewModels {
var stash = new MenuItem();
stash.Header = App.Text("FileCM.StashMulti", changes.Count);
stash.Icon = CreateMenuIcon("Icons.Stashes");
stash.Icon = App.CreateMenuIcon("Icons.Stashes");
stash.Click += (_, e) => {
if (PopupHost.CanCreatePopup()) {
PopupHost.ShowPopup(new StashChanges(_repo, changes, false));
@ -414,7 +493,7 @@ namespace SourceGit.ViewModels {
var patch = new MenuItem();
patch.Header = App.Text("FileCM.SaveAsPatch");
patch.Icon = CreateMenuIcon("Icons.Diff");
patch.Icon = App.CreateMenuIcon("Icons.Diff");
patch.Click += async (o, e) => {
var topLevel = App.GetTopLevel();
if (topLevel == null) return;
@ -453,7 +532,7 @@ namespace SourceGit.ViewModels {
var explore = new MenuItem();
explore.IsEnabled = File.Exists(path) || Directory.Exists(path);
explore.Header = App.Text("RevealFile");
explore.Icon = CreateMenuIcon("Icons.Folder.Open");
explore.Icon = App.CreateMenuIcon("Icons.Folder.Open");
explore.Click += (o, e) => {
Native.OS.OpenInFileManager(path, true);
e.Handled = true;
@ -461,7 +540,7 @@ namespace SourceGit.ViewModels {
var openWith = new MenuItem();
openWith.Header = App.Text("OpenWith");
openWith.Icon = CreateMenuIcon("Icons.OpenWith");
openWith.Icon = App.CreateMenuIcon("Icons.OpenWith");
openWith.IsEnabled = File.Exists(path);
openWith.Click += (_, e) => {
Native.OS.OpenWithDefaultEditor(path);
@ -470,7 +549,7 @@ namespace SourceGit.ViewModels {
var unstage = new MenuItem();
unstage.Header = App.Text("FileCM.Unstage");
unstage.Icon = CreateMenuIcon("Icons.File.Remove");
unstage.Icon = App.CreateMenuIcon("Icons.File.Remove");
unstage.Click += (o, e) => {
UnstageChanges(changes);
e.Handled = true;
@ -478,7 +557,7 @@ namespace SourceGit.ViewModels {
var stash = new MenuItem();
stash.Header = App.Text("FileCM.Stash");
stash.Icon = CreateMenuIcon("Icons.Stashes");
stash.Icon = App.CreateMenuIcon("Icons.Stashes");
stash.Click += (_, e) => {
if (PopupHost.CanCreatePopup()) {
PopupHost.ShowPopup(new StashChanges(_repo, changes, false));
@ -488,7 +567,7 @@ namespace SourceGit.ViewModels {
var patch = new MenuItem();
patch.Header = App.Text("FileCM.SaveAsPatch");
patch.Icon = CreateMenuIcon("Icons.Diff");
patch.Icon = App.CreateMenuIcon("Icons.Diff");
patch.Click += async (o, e) => {
var topLevel = App.GetTopLevel();
if (topLevel == null) return;
@ -509,7 +588,7 @@ namespace SourceGit.ViewModels {
var copyPath = new MenuItem();
copyPath.Header = App.Text("CopyPath");
copyPath.Icon = CreateMenuIcon("Icons.Copy");
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
copyPath.Click += (o, e) => {
App.CopyText(change.Path);
e.Handled = true;
@ -526,7 +605,7 @@ namespace SourceGit.ViewModels {
} else {
var unstage = new MenuItem();
unstage.Header = App.Text("FileCM.UnstageMulti", changes.Count);
unstage.Icon = CreateMenuIcon("Icons.File.Remove");
unstage.Icon = App.CreateMenuIcon("Icons.File.Remove");
unstage.Click += (o, e) => {
UnstageChanges(changes);
e.Handled = true;
@ -534,7 +613,7 @@ namespace SourceGit.ViewModels {
var stash = new MenuItem();
stash.Header = App.Text("FileCM.StashMulti", changes.Count);
stash.Icon = CreateMenuIcon("Icons.Stashes");
stash.Icon = App.CreateMenuIcon("Icons.Stashes");
stash.Click += (_, e) => {
if (PopupHost.CanCreatePopup()) {
PopupHost.ShowPopup(new StashChanges(_repo, changes, false));
@ -544,7 +623,7 @@ namespace SourceGit.ViewModels {
var patch = new MenuItem();
patch.Header = App.Text("FileCM.SaveAsPatch");
patch.Icon = CreateMenuIcon("Icons.Diff");
patch.Icon = App.CreateMenuIcon("Icons.Diff");
patch.Click += async (_, e) => {
var topLevel = App.GetTopLevel();
if (topLevel == null) return;
@ -603,15 +682,6 @@ namespace SourceGit.ViewModels {
return menu;
}
private Avalonia.Controls.Shapes.Path CreateMenuIcon(string key) {
var icon = new Avalonia.Controls.Shapes.Path();
icon.Width = 12;
icon.Height = 12;
icon.Stretch = Stretch.Uniform;
icon.Data = App.Current?.FindResource(key) as StreamGeometry;
return icon;
}
private void PushCommitMessage() {
var existIdx = _repo.CommitMessages.IndexOf(CommitMessage);
if (existIdx == 0) {
@ -636,10 +706,13 @@ namespace SourceGit.ViewModels {
private bool _useAmend = false;
private List<Models.Change> _unstaged = null;
private List<Models.Change> _staged = null;
private Models.Change _selectedUnstagedChange = null;
private Models.Change _selectedStagedChange = null;
private int _count = 0;
private List<FileTreeNode> _unstagedTree = null;
private List<FileTreeNode> _stagedTree = null;
private ViewChangeDetailContext _lastViewChange = null;
private FileTreeNode _selectedUnstagedTreeNode = null;
private FileTreeNode _selectedStagedTreeNode = null;
private object _detailContext = null;
private string _commitMessage = string.Empty;
}

View file

@ -60,6 +60,7 @@
<DataGrid x:Name="unstagedList"
Background="Transparent"
ItemsSource="{Binding Unstaged}"
SelectedItem="{Binding SelectedUnstagedChange, Mode=TwoWay}"
SelectionMode="Extended"
CanUserReorderColumns="False"
CanUserResizeColumns="False"
@ -70,7 +71,6 @@
RowHeight="26"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"
SelectionChanged="OnUnstagedListSelectionChanged"
KeyDown="OnUnstagedListKeyDown"
ContextRequested="OnUnstagedListContextRequested"
IsVisible="{Binding Source={x:Static vm:Preference.Instance}, Path=UnstagedChangeViewMode, Converter={x:Static c:ChangeViewModeConverters.IsList}}">
@ -96,6 +96,7 @@
<DataGrid x:Name="unstagedGrid"
Background="Transparent"
ItemsSource="{Binding Unstaged}"
SelectedItem="{Binding SelectedUnstagedChange, Mode=TwoWay}"
SelectionMode="Extended"
CanUserReorderColumns="False"
CanUserResizeColumns="False"
@ -106,7 +107,6 @@
RowHeight="26"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"
SelectionChanged="OnUnstagedListSelectionChanged"
KeyDown="OnUnstagedListKeyDown"
ContextRequested="OnUnstagedListContextRequested"
IsVisible="{Binding Source={x:Static vm:Preference.Instance}, Path=UnstagedChangeViewMode, Converter={x:Static c:ChangeViewModeConverters.IsGrid}}">
@ -139,11 +139,11 @@
<TreeView x:Name="unstagedTree"
ItemsSource="{Binding UnstagedTree}"
SelectedItem="{Binding SelectedUnstagedTreeNode, Mode=TwoWay}"
SelectionMode="Multiple"
AutoScrollToSelectedItem="True"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto"
SelectionChanged="OnUnstagedTreeViewSelectionChanged"
KeyDown="OnUnstagedTreeViewKeyDown"
ContextRequested="OnUnstagedTreeViewContextRequested"
IsVisible="{Binding Source={x:Static vm:Preference.Instance}, Path=UnstagedChangeViewMode, Converter={x:Static c:ChangeViewModeConverters.IsTree}}">
@ -185,6 +185,7 @@
<DataGrid x:Name="stagedList"
Background="Transparent"
ItemsSource="{Binding Staged}"
SelectedItem="{Binding SelectedStagedChange, Mode=TwoWay}"
SelectionMode="Extended"
CanUserReorderColumns="False"
CanUserResizeColumns="False"
@ -195,7 +196,6 @@
RowHeight="26"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"
SelectionChanged="OnStagedListSelectionChanged"
KeyDown="OnStagedListKeyDown"
ContextRequested="OnStagedListContextRequested"
IsVisible="{Binding Source={x:Static vm:Preference.Instance}, Path=StagedChangeViewMode, Converter={x:Static c:ChangeViewModeConverters.IsList}}">
@ -221,6 +221,7 @@
<DataGrid x:Name="stagedGrid"
Background="Transparent"
ItemsSource="{Binding Staged}"
SelectedItem="{Binding SelectedStagedChange, Mode=TwoWay}"
SelectionMode="Extended"
CanUserReorderColumns="False"
CanUserResizeColumns="False"
@ -231,7 +232,6 @@
RowHeight="26"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"
SelectionChanged="OnStagedListSelectionChanged"
KeyDown="OnStagedListKeyDown"
ContextRequested="OnStagedListContextRequested"
IsVisible="{Binding Source={x:Static vm:Preference.Instance}, Path=StagedChangeViewMode, Converter={x:Static c:ChangeViewModeConverters.IsGrid}}">
@ -264,11 +264,11 @@
<TreeView x:Name="stagedTree"
ItemsSource="{Binding StagedTree}"
SelectedItem="{Binding SelectedStagedTreeNode, Mode=TwoWay}"
SelectionMode="Multiple"
AutoScrollToSelectedItem="True"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto"
SelectionChanged="OnStagedTreeViewSelectionChanged"
KeyDown="OnStagedTreeViewKeyDown"
ContextRequested="OnStagedTreeViewContextRequested"
IsVisible="{Binding Source={x:Static vm:Preference.Instance}, Path=StagedChangeViewMode, Converter={x:Static c:ChangeViewModeConverters.IsTree}}">

View file

@ -19,132 +19,6 @@ namespace SourceGit.Views {
GC.Collect();
}
private void OnUnstagedListSelectionChanged(object sender, SelectionChangedEventArgs e) {
var vm = DataContext as ViewModels.WorkingCopy;
if (vm == null) return;
var datagrid = sender as DataGrid;
if (datagrid.SelectedItems.Count == 0) {
if (stagedList.SelectedItem == null &&
stagedGrid.SelectedItem == null &&
stagedTree.SelectedItem == null) {
vm.SetDetail(null, true);
}
e.Handled = true;
return;
}
stagedList.SelectedItems.Clear();
stagedGrid.SelectedItems.Clear();
stagedTree.SelectedItems.Clear();
if (datagrid.SelectedItems.Count == 1) {
vm.SetDetail(datagrid.SelectedItem as Models.Change, true);
} else {
vm.SetDetail(null, true);
}
e.Handled = true;
}
private void OnUnstagedTreeViewSelectionChanged(object sender, SelectionChangedEventArgs e) {
var vm = DataContext as ViewModels.WorkingCopy;
if (vm == null) return;
var tree = sender as TreeView;
if (tree.SelectedItems.Count == 0) {
if (stagedList.SelectedItem == null &&
stagedGrid.SelectedItem == null &&
stagedTree.SelectedItem == null) {
vm.SetDetail(null, true);
}
e.Handled = true;
return;
}
stagedList.SelectedItems.Clear();
stagedGrid.SelectedItems.Clear();
stagedTree.SelectedItems.Clear();
if (tree.SelectedItems.Count == 1) {
var node = tree.SelectedItem as ViewModels.FileTreeNode;
if (node != null && !node.IsFolder) {
vm.SetDetail(node.Backend as Models.Change, true);
} else {
vm.SetDetail(null, true);
}
} else {
vm.SetDetail(null, true);
}
e.Handled = true;
}
private void OnStagedListSelectionChanged(object sender, SelectionChangedEventArgs e) {
var vm = DataContext as ViewModels.WorkingCopy;
if (vm == null) return;
var datagrid = sender as DataGrid;
if (datagrid.SelectedItems.Count == 0) {
if (unstagedList.SelectedItem == null &&
unstagedGrid.SelectedItem == null &&
unstagedTree.SelectedItem == null) {
vm.SetDetail(null, false);
}
e.Handled = true;
return;
}
unstagedList.SelectedItems.Clear();
unstagedGrid.SelectedItems.Clear();
unstagedTree.SelectedItems.Clear();
if (datagrid.SelectedItems.Count == 1) {
vm.SetDetail(datagrid.SelectedItem as Models.Change, false);
} else {
vm.SetDetail(null, false);
}
e.Handled = true;
}
private void OnStagedTreeViewSelectionChanged(object sender, SelectionChangedEventArgs e) {
var vm = DataContext as ViewModels.WorkingCopy;
if (vm == null) return;
var tree = sender as TreeView;
if (tree.SelectedItems.Count == 0) {
if (unstagedList.SelectedItem == null &&
unstagedGrid.SelectedItem == null &&
unstagedTree.SelectedItem == null) {
vm.SetDetail(null, false);
}
e.Handled = true;
return;
}
unstagedList.SelectedItems.Clear();
unstagedGrid.SelectedItems.Clear();
unstagedTree.SelectedItems.Clear();
if (tree.SelectedItems.Count == 1) {
var node = tree.SelectedItem as ViewModels.FileTreeNode;
if (node != null && !node.IsFolder) {
vm.SetDetail(node.Backend as Models.Change, false);
} else {
vm.SetDetail(null, false);
}
} else {
vm.SetDetail(null, false);
}
e.Handled = true;
}
private void ViewAssumeUnchanged(object sender, RoutedEventArgs e) {
var repoPage = this.FindAncestorOfType<Repository>();
if (repoPage != null) {