mirror of
https://github.com/sourcegit-scm/sourcegit.git
synced 2024-11-01 13:13:21 -07:00
Merge branch 'release/v8.15'
This commit is contained in:
commit
d8fe0e2387
85 changed files with 2736 additions and 1948 deletions
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
8.14
|
8.15
|
|
@ -19,6 +19,7 @@
|
||||||
<Application.Styles>
|
<Application.Styles>
|
||||||
<FluentTheme />
|
<FluentTheme />
|
||||||
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml"/>
|
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml"/>
|
||||||
|
<StyleInclude Source="avares://Avalonia.Controls.TreeDataGrid/Themes/Fluent.axaml"/>
|
||||||
<StyleInclude Source="avares://AvaloniaEdit/Themes/Fluent/AvaloniaEdit.xaml" />
|
<StyleInclude Source="avares://AvaloniaEdit/Themes/Fluent/AvaloniaEdit.xaml" />
|
||||||
<StyleInclude Source="/Resources/Styles.axaml"/>
|
<StyleInclude Source="/Resources/Styles.axaml"/>
|
||||||
</Application.Styles>
|
</Application.Styles>
|
||||||
|
|
|
@ -62,9 +62,7 @@ namespace SourceGit
|
||||||
builder.Append(ex.StackTrace);
|
builder.Append(ex.StackTrace);
|
||||||
|
|
||||||
var time = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
|
var time = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
|
||||||
var file = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
var file = Path.Combine(Native.OS.DataDir, $"crash_{time}.log");
|
||||||
"SourceGit",
|
|
||||||
$"crash_{time}.log");
|
|
||||||
File.WriteAllText(file, builder.ToString());
|
File.WriteAllText(file, builder.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,8 @@ namespace SourceGit.Commands
|
||||||
{
|
{
|
||||||
public class QueryCommits : Command
|
public class QueryCommits : Command
|
||||||
{
|
{
|
||||||
private const string GPGSIG_START = "gpgsig -----BEGIN PGP SIGNATURE-----";
|
private const string GPGSIG_START = "gpgsig -----BEGIN ";
|
||||||
private const string GPGSIG_END = " -----END PGP SIGNATURE-----";
|
private const string GPGSIG_END = " -----END ";
|
||||||
|
|
||||||
private readonly List<Models.Commit> commits = new List<Models.Commit>();
|
private readonly List<Models.Commit> commits = new List<Models.Commit>();
|
||||||
private Models.Commit current = null;
|
private Models.Commit current = null;
|
||||||
|
@ -17,6 +17,7 @@ namespace SourceGit.Commands
|
||||||
public QueryCommits(string repo, string limits, bool needFindHead = true)
|
public QueryCommits(string repo, string limits, bool needFindHead = true)
|
||||||
{
|
{
|
||||||
WorkingDirectory = repo;
|
WorkingDirectory = repo;
|
||||||
|
Context = repo;
|
||||||
Args = "log --date-order --decorate=full --pretty=raw " + limits;
|
Args = "log --date-order --decorate=full --pretty=raw " + limits;
|
||||||
findFirstMerged = needFindHead;
|
findFirstMerged = needFindHead;
|
||||||
}
|
}
|
||||||
|
|
167
src/Commands/QuerySingleCommit.cs
Normal file
167
src/Commands/QuerySingleCommit.cs
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace SourceGit.Commands
|
||||||
|
{
|
||||||
|
public class QuerySingleCommit : Command
|
||||||
|
{
|
||||||
|
private const string GPGSIG_START = "gpgsig -----BEGIN PGP SIGNATURE-----";
|
||||||
|
private const string GPGSIG_END = " -----END PGP SIGNATURE-----";
|
||||||
|
|
||||||
|
public QuerySingleCommit(string repo, string sha) {
|
||||||
|
WorkingDirectory = repo;
|
||||||
|
Context = repo;
|
||||||
|
Args = $"show --pretty=raw --decorate=full -s {sha}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Models.Commit Result()
|
||||||
|
{
|
||||||
|
var succ = Exec();
|
||||||
|
if (!succ)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
_commit.Message.Trim();
|
||||||
|
return _commit;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnReadline(string line)
|
||||||
|
{
|
||||||
|
if (isSkipingGpgsig)
|
||||||
|
{
|
||||||
|
if (line.StartsWith(GPGSIG_END, StringComparison.Ordinal))
|
||||||
|
isSkipingGpgsig = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (line.StartsWith(GPGSIG_START, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
isSkipingGpgsig = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.StartsWith("commit ", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
line = line.Substring(7);
|
||||||
|
|
||||||
|
var decoratorStart = line.IndexOf('(', StringComparison.Ordinal);
|
||||||
|
if (decoratorStart < 0)
|
||||||
|
{
|
||||||
|
_commit.SHA = line.Trim();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_commit.SHA = line.Substring(0, decoratorStart).Trim();
|
||||||
|
ParseDecorators(_commit.Decorators, line.Substring(decoratorStart + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.StartsWith("tree ", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (line.StartsWith("parent ", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
_commit.Parents.Add(line.Substring("parent ".Length));
|
||||||
|
}
|
||||||
|
else if (line.StartsWith("author ", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
Models.User user = Models.User.Invalid;
|
||||||
|
ulong time = 0;
|
||||||
|
Models.Commit.ParseUserAndTime(line.Substring(7), ref user, ref time);
|
||||||
|
_commit.Author = user;
|
||||||
|
_commit.AuthorTime = time;
|
||||||
|
}
|
||||||
|
else if (line.StartsWith("committer ", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
Models.User user = Models.User.Invalid;
|
||||||
|
ulong time = 0;
|
||||||
|
Models.Commit.ParseUserAndTime(line.Substring(10), ref user, ref time);
|
||||||
|
_commit.Committer = user;
|
||||||
|
_commit.CommitterTime = time;
|
||||||
|
}
|
||||||
|
else if (string.IsNullOrEmpty(_commit.Subject))
|
||||||
|
{
|
||||||
|
_commit.Subject = line.Trim();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_commit.Message += (line.Trim() + "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ParseDecorators(List<Models.Decorator> decorators, string data)
|
||||||
|
{
|
||||||
|
bool isHeadOfCurrent = false;
|
||||||
|
|
||||||
|
var subs = data.Split(new char[] { ',', ')', '(' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
foreach (var sub in subs)
|
||||||
|
{
|
||||||
|
var d = sub.Trim();
|
||||||
|
if (d.StartsWith("tag: refs/tags/", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
decorators.Add(new Models.Decorator()
|
||||||
|
{
|
||||||
|
Type = Models.DecoratorType.Tag,
|
||||||
|
Name = d.Substring(15).Trim(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (d.EndsWith("/HEAD", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (d.StartsWith("HEAD -> refs/heads/", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
isHeadOfCurrent = true;
|
||||||
|
decorators.Add(new Models.Decorator()
|
||||||
|
{
|
||||||
|
Type = Models.DecoratorType.CurrentBranchHead,
|
||||||
|
Name = d.Substring(19).Trim(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (d.Equals("HEAD"))
|
||||||
|
{
|
||||||
|
isHeadOfCurrent = true;
|
||||||
|
decorators.Add(new Models.Decorator()
|
||||||
|
{
|
||||||
|
Type = Models.DecoratorType.CurrentCommitHead,
|
||||||
|
Name = d.Trim(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (d.StartsWith("refs/heads/", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
decorators.Add(new Models.Decorator()
|
||||||
|
{
|
||||||
|
Type = Models.DecoratorType.LocalBranchHead,
|
||||||
|
Name = d.Substring(11).Trim(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (d.StartsWith("refs/remotes/", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
decorators.Add(new Models.Decorator()
|
||||||
|
{
|
||||||
|
Type = Models.DecoratorType.RemoteBranchHead,
|
||||||
|
Name = d.Substring(13).Trim(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
decorators.Sort((l, r) =>
|
||||||
|
{
|
||||||
|
if (l.Type != r.Type)
|
||||||
|
{
|
||||||
|
return (int)l.Type - (int)r.Type;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return l.Name.CompareTo(r.Name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return isHeadOfCurrent;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Models.Commit _commit = new Models.Commit();
|
||||||
|
private bool isSkipingGpgsig = false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,18 +1,17 @@
|
||||||
using Avalonia.Controls;
|
using Avalonia.Data.Converters;
|
||||||
using Avalonia.Data.Converters;
|
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
|
|
||||||
namespace SourceGit.Converters
|
namespace SourceGit.Converters
|
||||||
{
|
{
|
||||||
public static class BoolConverters
|
public static class BoolConverters
|
||||||
{
|
{
|
||||||
|
public static readonly FuncValueConverter<bool, double> ToPageTabWidth =
|
||||||
|
new FuncValueConverter<bool, double>(x => x ? 200 : double.NaN);
|
||||||
|
|
||||||
public static readonly FuncValueConverter<bool, double> HalfIfFalse =
|
public static readonly FuncValueConverter<bool, double> HalfIfFalse =
|
||||||
new FuncValueConverter<bool, double>(x => x ? 1 : 0.5);
|
new FuncValueConverter<bool, double>(x => x ? 1 : 0.5);
|
||||||
|
|
||||||
public static readonly FuncValueConverter<bool, FontWeight> BoldIfTrue =
|
public static readonly FuncValueConverter<bool, FontWeight> BoldIfTrue =
|
||||||
new FuncValueConverter<bool, FontWeight>(x => x ? FontWeight.Bold : FontWeight.Regular);
|
new FuncValueConverter<bool, FontWeight>(x => x ? FontWeight.Bold : FontWeight.Regular);
|
||||||
|
|
||||||
public static readonly FuncValueConverter<bool, GridLength> ToStarOrAutoGridLength =
|
|
||||||
new(value => value ? new GridLength(1, GridUnitType.Star) : new GridLength(1, GridUnitType.Auto));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
using Avalonia.Data.Converters;
|
using Avalonia.Data.Converters;
|
||||||
using Avalonia.Styling;
|
using Avalonia.Styling;
|
||||||
|
|
||||||
namespace SourceGit.Converters
|
namespace SourceGit.Converters
|
||||||
{
|
{
|
||||||
public static class StringConverters
|
public static partial class StringConverters
|
||||||
{
|
{
|
||||||
public class ToLocaleConverter : IValueConverter
|
public class ToLocaleConverter : IValueConverter
|
||||||
{
|
{
|
||||||
|
@ -68,6 +69,27 @@ namespace SourceGit.Converters
|
||||||
public static readonly FormatByResourceKeyConverter FormatByResourceKey = new FormatByResourceKeyConverter();
|
public static readonly FormatByResourceKeyConverter FormatByResourceKey = new FormatByResourceKeyConverter();
|
||||||
|
|
||||||
public static readonly FuncValueConverter<string, string> ToShortSHA =
|
public static readonly FuncValueConverter<string, string> ToShortSHA =
|
||||||
new FuncValueConverter<string, string>(v => v.Length > 10 ? v.Substring(0, 10) : v);
|
new FuncValueConverter<string, string>(v => v == null ? string.Empty : (v.Length > 10 ? v.Substring(0, 10) : v));
|
||||||
|
|
||||||
|
public static readonly FuncValueConverter<string, bool> UnderRecommendGitVersion =
|
||||||
|
new(v =>
|
||||||
|
{
|
||||||
|
var match = REG_GIT_VERSION().Match(v ?? "");
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
var major = int.Parse(match.Groups[1].Value);
|
||||||
|
var minor = int.Parse(match.Groups[2].Value);
|
||||||
|
var build = int.Parse(match.Groups[3].Value);
|
||||||
|
|
||||||
|
return new Version(major, minor, build) < MINIMAL_GIT_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
[GeneratedRegex(@"^[\s\w]*(\d+)\.(\d+)[\.\-](\d+).*$")]
|
||||||
|
private static partial Regex REG_GIT_VERSION();
|
||||||
|
|
||||||
|
private static readonly Version MINIMAL_GIT_VERSION = new Version(2, 23, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Data.Converters;
|
using Avalonia.Data.Converters;
|
||||||
using Avalonia.Media;
|
|
||||||
|
|
||||||
namespace SourceGit.Converters
|
namespace SourceGit.Converters
|
||||||
{
|
{
|
||||||
|
@ -39,19 +38,6 @@ namespace SourceGit.Converters
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
public static readonly FuncValueConverter<WindowState, StreamGeometry> ToMaxOrRestoreIcon =
|
|
||||||
new FuncValueConverter<WindowState, StreamGeometry>(state =>
|
|
||||||
{
|
|
||||||
if (state == WindowState.Maximized)
|
|
||||||
{
|
|
||||||
return Application.Current?.FindResource("Icons.Window.Restore") as StreamGeometry;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return Application.Current?.FindResource("Icons.Window.Maximize") as StreamGeometry;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
public static readonly FuncValueConverter<WindowState, bool> IsNormal =
|
public static readonly FuncValueConverter<WindowState, bool> IsNormal =
|
||||||
new FuncValueConverter<WindowState, bool>(state => state == WindowState.Normal);
|
new FuncValueConverter<WindowState, bool>(state => state == WindowState.Normal);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ namespace SourceGit.Models
|
||||||
|
|
||||||
static AvatarManager()
|
static AvatarManager()
|
||||||
{
|
{
|
||||||
_storePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "SourceGit", "avatars");
|
_storePath = Path.Combine(Native.OS.DataDir, "avatars");
|
||||||
if (!Directory.Exists(_storePath))
|
if (!Directory.Exists(_storePath))
|
||||||
Directory.CreateDirectory(_storePath);
|
Directory.CreateDirectory(_storePath);
|
||||||
|
|
||||||
|
|
9
src/Models/DealWithLocalChanges.cs
Normal file
9
src/Models/DealWithLocalChanges.cs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
namespace SourceGit.Models
|
||||||
|
{
|
||||||
|
public enum DealWithLocalChanges
|
||||||
|
{
|
||||||
|
StashAndReaply,
|
||||||
|
Discard,
|
||||||
|
DoNothing,
|
||||||
|
}
|
||||||
|
}
|
|
@ -582,6 +582,12 @@ namespace SourceGit.Models
|
||||||
public string New { get; set; } = string.Empty;
|
public string New { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class SubmoduleDiff
|
||||||
|
{
|
||||||
|
public Commit Old { get; set; } = null;
|
||||||
|
public Commit New { get; set; } = null;
|
||||||
|
}
|
||||||
|
|
||||||
public class DiffResult
|
public class DiffResult
|
||||||
{
|
{
|
||||||
public bool IsBinary { get; set; } = false;
|
public bool IsBinary { get; set; } = false;
|
||||||
|
|
|
@ -1,28 +1,20 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
namespace SourceGit.Models
|
||||||
|
|
||||||
namespace SourceGit.ViewModels
|
|
||||||
{
|
{
|
||||||
public class FileTreeNode : ObservableObject
|
public class FileTreeNode
|
||||||
{
|
{
|
||||||
public string FullPath { get; set; } = string.Empty;
|
public string FullPath { get; set; } = string.Empty;
|
||||||
public bool IsFolder { get; set; } = false;
|
public bool IsFolder { get; set; } = false;
|
||||||
|
public bool IsExpanded { get; set; } = false;
|
||||||
public object Backend { get; set; } = null;
|
public object Backend { get; set; } = null;
|
||||||
public List<FileTreeNode> Children { get; set; } = new List<FileTreeNode>();
|
public List<FileTreeNode> Children { get; set; } = new List<FileTreeNode>();
|
||||||
|
|
||||||
public bool IsExpanded
|
public static List<FileTreeNode> Build(List<Change> changes, bool expanded)
|
||||||
{
|
|
||||||
get => _isExpanded;
|
|
||||||
set => SetProperty(ref _isExpanded, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<FileTreeNode> Build(List<Models.Change> changes)
|
|
||||||
{
|
{
|
||||||
var nodes = new List<FileTreeNode>();
|
var nodes = new List<FileTreeNode>();
|
||||||
var folders = new Dictionary<string, FileTreeNode>();
|
var folders = new Dictionary<string, FileTreeNode>();
|
||||||
var expanded = changes.Count <= 50;
|
|
||||||
|
|
||||||
foreach (var c in changes)
|
foreach (var c in changes)
|
||||||
{
|
{
|
||||||
|
@ -94,11 +86,10 @@ namespace SourceGit.ViewModels
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<FileTreeNode> Build(List<Models.Object> files)
|
public static List<FileTreeNode> Build(List<Object> files, bool expanded)
|
||||||
{
|
{
|
||||||
var nodes = new List<FileTreeNode>();
|
var nodes = new List<FileTreeNode>();
|
||||||
var folders = new Dictionary<string, FileTreeNode>();
|
var folders = new Dictionary<string, FileTreeNode>();
|
||||||
var expanded = files.Count <= 50;
|
|
||||||
|
|
||||||
foreach (var f in files)
|
foreach (var f in files)
|
||||||
{
|
{
|
||||||
|
@ -170,27 +161,6 @@ namespace SourceGit.ViewModels
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FileTreeNode SelectByPath(List<FileTreeNode> nodes, string path)
|
|
||||||
{
|
|
||||||
foreach (var node in nodes)
|
|
||||||
{
|
|
||||||
if (node.FullPath == path)
|
|
||||||
return node;
|
|
||||||
|
|
||||||
if (node.IsFolder && path.StartsWith(node.FullPath + "/", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
var foundInChildren = SelectByPath(node.Children, path);
|
|
||||||
if (foundInChildren != null)
|
|
||||||
{
|
|
||||||
node.IsExpanded = true;
|
|
||||||
}
|
|
||||||
return foundInChildren;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Sort(List<FileTreeNode> nodes)
|
private static void Sort(List<FileTreeNode> nodes)
|
||||||
{
|
{
|
||||||
nodes.Sort((l, r) =>
|
nodes.Sort((l, r) =>
|
||||||
|
@ -211,7 +181,5 @@ namespace SourceGit.ViewModels
|
||||||
Sort(node.Children);
|
Sort(node.Children);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool _isExpanded = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
25
src/Models/GPGFormat.cs
Normal file
25
src/Models/GPGFormat.cs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace SourceGit.Models
|
||||||
|
{
|
||||||
|
public class GPGFormat(string name, string value, string desc)
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = name;
|
||||||
|
public string Value { get; set; } = value;
|
||||||
|
public string Desc { get; set; } = desc;
|
||||||
|
|
||||||
|
public static readonly GPGFormat OPENPGP = new GPGFormat("OPENPGP", "openpgp", "DEFAULT");
|
||||||
|
|
||||||
|
public static readonly GPGFormat SSH = new GPGFormat("SSH", "ssh", "Git >= 2.34.0");
|
||||||
|
|
||||||
|
public static readonly List<GPGFormat> Supported = new List<GPGFormat>() {
|
||||||
|
OPENPGP,
|
||||||
|
SSH,
|
||||||
|
};
|
||||||
|
|
||||||
|
public bool Equals(GPGFormat other)
|
||||||
|
{
|
||||||
|
return Value == other.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,11 +4,11 @@ namespace SourceGit.Models
|
||||||
{
|
{
|
||||||
public partial class Remote
|
public partial class Remote
|
||||||
{
|
{
|
||||||
[GeneratedRegex(@"^http[s]?://([\w\-]+@)?[\w\.\-]+(\:[0-9]+)?/[\w\-/]+/[\w\-\.]+\.git$")]
|
[GeneratedRegex(@"^http[s]?://([\w\-]+@)?[\w\.\-]+(\:[0-9]+)?/[\w\-/]+/[\w\-\.]+(\.git)?$")]
|
||||||
private static partial Regex REG_HTTPS();
|
private static partial Regex REG_HTTPS();
|
||||||
[GeneratedRegex(@"^[\w\-]+@[\w\.\-]+(\:[0-9]+)?:[\w\-/]+/[\w\-\.]+\.git$")]
|
[GeneratedRegex(@"^[\w\-]+@[\w\.\-]+(\:[0-9]+)?:[\w\-/]+/[\w\-\.]+(\.git)?$")]
|
||||||
private static partial Regex REG_SSH1();
|
private static partial Regex REG_SSH1();
|
||||||
[GeneratedRegex(@"^ssh://([\w\-]+@)?[\w\.\-]+(\:[0-9]+)?/[\w\-/]+/[\w\-\.]+\.git$")]
|
[GeneratedRegex(@"^ssh://([\w\-]+@)?[\w\.\-]+(\:[0-9]+)?/[\w\-/]+/[\w\-\.]+(\.git)?$")]
|
||||||
private static partial Regex REG_SSH2();
|
private static partial Regex REG_SSH2();
|
||||||
|
|
||||||
private static readonly Regex[] URL_FORMATS = [
|
private static readonly Regex[] URL_FORMATS = [
|
||||||
|
|
446
src/Models/TreeDataGridSelectionModel.cs
Normal file
446
src/Models/TreeDataGridSelectionModel.cs
Normal file
|
@ -0,0 +1,446 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Models.TreeDataGrid;
|
||||||
|
using Avalonia.Controls.Primitives;
|
||||||
|
using Avalonia.Controls.Selection;
|
||||||
|
using Avalonia.Input;
|
||||||
|
|
||||||
|
namespace SourceGit.Models
|
||||||
|
{
|
||||||
|
public class TreeDataGridSelectionModel<TModel> : TreeSelectionModelBase<TModel>,
|
||||||
|
ITreeDataGridRowSelectionModel<TModel>,
|
||||||
|
ITreeDataGridSelectionInteraction
|
||||||
|
where TModel : class
|
||||||
|
{
|
||||||
|
private static readonly Point s_InvalidPoint = new(double.NegativeInfinity, double.NegativeInfinity);
|
||||||
|
|
||||||
|
private readonly ITreeDataGridSource<TModel> _source;
|
||||||
|
private EventHandler _viewSelectionChanged;
|
||||||
|
private EventHandler _rowDoubleTapped;
|
||||||
|
private Point _pressedPoint = s_InvalidPoint;
|
||||||
|
private bool _raiseViewSelectionChanged;
|
||||||
|
private Func<TModel, IEnumerable<TModel>> _childrenGetter;
|
||||||
|
|
||||||
|
public TreeDataGridSelectionModel(ITreeDataGridSource<TModel> source, Func<TModel, IEnumerable<TModel>> childrenGetter)
|
||||||
|
: base(source.Items)
|
||||||
|
{
|
||||||
|
_source = source;
|
||||||
|
_childrenGetter = childrenGetter;
|
||||||
|
|
||||||
|
SelectionChanged += (s, e) =>
|
||||||
|
{
|
||||||
|
if (!IsSourceCollectionChanging)
|
||||||
|
_viewSelectionChanged?.Invoke(this, e);
|
||||||
|
else
|
||||||
|
_raiseViewSelectionChanged = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Select(IEnumerable<TModel> items)
|
||||||
|
{
|
||||||
|
var sets = new HashSet<TModel>();
|
||||||
|
foreach (var item in items)
|
||||||
|
sets.Add(item);
|
||||||
|
|
||||||
|
using (BatchUpdate())
|
||||||
|
{
|
||||||
|
Clear();
|
||||||
|
|
||||||
|
int num = _source.Rows.Count;
|
||||||
|
for (int i = 0; i < num; ++i)
|
||||||
|
{
|
||||||
|
var m = _source.Rows[i].Model as TModel;
|
||||||
|
if (m != null && sets.Contains(m))
|
||||||
|
{
|
||||||
|
var idx = _source.Rows.RowIndexToModelIndex(i);
|
||||||
|
Select(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event EventHandler ITreeDataGridSelectionInteraction.SelectionChanged
|
||||||
|
{
|
||||||
|
add => _viewSelectionChanged += value;
|
||||||
|
remove => _viewSelectionChanged -= value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler RowDoubleTapped
|
||||||
|
{
|
||||||
|
add => _rowDoubleTapped += value;
|
||||||
|
remove => _rowDoubleTapped -= value;
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable ITreeDataGridSelection.Source
|
||||||
|
{
|
||||||
|
get => Source;
|
||||||
|
set => Source = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ITreeDataGridSelectionInteraction.IsRowSelected(IRow rowModel)
|
||||||
|
{
|
||||||
|
if (rowModel is IModelIndexableRow indexable)
|
||||||
|
return IsSelected(indexable.ModelIndexPath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ITreeDataGridSelectionInteraction.IsRowSelected(int rowIndex)
|
||||||
|
{
|
||||||
|
if (rowIndex >= 0 && rowIndex < _source.Rows.Count)
|
||||||
|
{
|
||||||
|
if (_source.Rows[rowIndex] is IModelIndexableRow indexable)
|
||||||
|
return IsSelected(indexable.ModelIndexPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ITreeDataGridSelectionInteraction.OnKeyDown(TreeDataGrid sender, KeyEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender.RowsPresenter is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!e.Handled)
|
||||||
|
{
|
||||||
|
var ctrl = e.KeyModifiers.HasFlag(KeyModifiers.Control);
|
||||||
|
if (e.Key == Key.A && ctrl && !SingleSelect)
|
||||||
|
{
|
||||||
|
using (BatchUpdate())
|
||||||
|
{
|
||||||
|
Clear();
|
||||||
|
|
||||||
|
int num = _source.Rows.Count;
|
||||||
|
for (int i = 0; i < num; ++i)
|
||||||
|
{
|
||||||
|
var m = _source.Rows.RowIndexToModelIndex(i);
|
||||||
|
Select(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var direction = e.Key.ToNavigationDirection();
|
||||||
|
var shift = e.KeyModifiers.HasFlag(KeyModifiers.Shift);
|
||||||
|
if (direction.HasValue)
|
||||||
|
{
|
||||||
|
var anchorRowIndex = _source.Rows.ModelIndexToRowIndex(AnchorIndex);
|
||||||
|
sender.RowsPresenter.BringIntoView(anchorRowIndex);
|
||||||
|
|
||||||
|
var anchor = sender.TryGetRow(anchorRowIndex);
|
||||||
|
if (anchor is not null && !ctrl)
|
||||||
|
{
|
||||||
|
e.Handled = TryKeyExpandCollapse(sender, direction.Value, anchor);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!e.Handled && (!ctrl || shift))
|
||||||
|
{
|
||||||
|
e.Handled = MoveSelection(sender, direction.Value, shift, anchor);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!e.Handled && direction == NavigationDirection.Left
|
||||||
|
&& anchor?.Rows is HierarchicalRows<TModel> hierarchicalRows && anchorRowIndex > 0)
|
||||||
|
{
|
||||||
|
var newIndex = hierarchicalRows.GetParentRowIndex(AnchorIndex);
|
||||||
|
UpdateSelection(sender, newIndex, true);
|
||||||
|
FocusRow(sender, sender.RowsPresenter.BringIntoView(newIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!e.Handled && direction == NavigationDirection.Right
|
||||||
|
&& anchor?.Rows is HierarchicalRows<TModel> hierarchicalRows2 && hierarchicalRows2[anchorRowIndex].IsExpanded)
|
||||||
|
{
|
||||||
|
var newIndex = anchorRowIndex + 1;
|
||||||
|
UpdateSelection(sender, newIndex, true);
|
||||||
|
sender.RowsPresenter.BringIntoView(newIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ITreeDataGridSelectionInteraction.OnPointerPressed(TreeDataGrid sender, PointerPressedEventArgs e)
|
||||||
|
{
|
||||||
|
if (!e.Handled &&
|
||||||
|
e.Pointer.Type == PointerType.Mouse &&
|
||||||
|
e.Source is Control source &&
|
||||||
|
sender.TryGetRow(source, out var row) &&
|
||||||
|
_source.Rows.RowIndexToModelIndex(row.RowIndex) is { } modelIndex)
|
||||||
|
{
|
||||||
|
if (!IsSelected(modelIndex))
|
||||||
|
{
|
||||||
|
PointerSelect(sender, row, e);
|
||||||
|
_pressedPoint = s_InvalidPoint;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var point = e.GetCurrentPoint(sender);
|
||||||
|
if (point.Properties.IsRightButtonPressed)
|
||||||
|
{
|
||||||
|
_pressedPoint = s_InvalidPoint;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.KeyModifiers == KeyModifiers.Control)
|
||||||
|
{
|
||||||
|
Deselect(modelIndex);
|
||||||
|
}
|
||||||
|
else if (e.ClickCount % 2 == 0)
|
||||||
|
{
|
||||||
|
var focus = _source.Rows[row.RowIndex];
|
||||||
|
if (focus is IExpander expander && HasChildren(focus))
|
||||||
|
expander.IsExpanded = !expander.IsExpanded;
|
||||||
|
else
|
||||||
|
_rowDoubleTapped?.Invoke(this, e);
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
else if (sender.RowSelection.Count > 1)
|
||||||
|
{
|
||||||
|
using (BatchUpdate())
|
||||||
|
{
|
||||||
|
Clear();
|
||||||
|
Select(modelIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_pressedPoint = s_InvalidPoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!sender.TryGetRow(e.Source as Control, out var test))
|
||||||
|
Clear();
|
||||||
|
|
||||||
|
_pressedPoint = e.GetPosition(sender);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ITreeDataGridSelectionInteraction.OnPointerReleased(TreeDataGrid sender, PointerReleasedEventArgs e)
|
||||||
|
{
|
||||||
|
if (!e.Handled &&
|
||||||
|
_pressedPoint != s_InvalidPoint &&
|
||||||
|
e.Source is Control source &&
|
||||||
|
sender.TryGetRow(source, out var row) &&
|
||||||
|
_source.Rows.RowIndexToModelIndex(row.RowIndex) is { } modelIndex)
|
||||||
|
{
|
||||||
|
if (!IsSelected(modelIndex))
|
||||||
|
{
|
||||||
|
var p = e.GetPosition(sender);
|
||||||
|
if (Math.Abs(p.X - _pressedPoint.X) <= 3 || Math.Abs(p.Y - _pressedPoint.Y) <= 3)
|
||||||
|
PointerSelect(sender, row, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnSourceCollectionChangeFinished()
|
||||||
|
{
|
||||||
|
if (_raiseViewSelectionChanged)
|
||||||
|
{
|
||||||
|
_viewSelectionChanged?.Invoke(this, EventArgs.Empty);
|
||||||
|
_raiseViewSelectionChanged = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PointerSelect(TreeDataGrid sender, TreeDataGridRow row, PointerEventArgs e)
|
||||||
|
{
|
||||||
|
var point = e.GetCurrentPoint(sender);
|
||||||
|
|
||||||
|
var commandModifiers = TopLevel.GetTopLevel(sender)?.PlatformSettings?.HotkeyConfiguration.CommandModifiers;
|
||||||
|
var toggleModifier = commandModifiers is not null && e.KeyModifiers.HasFlag(commandModifiers);
|
||||||
|
var isRightButton = point.Properties.PointerUpdateKind is PointerUpdateKind.RightButtonPressed or
|
||||||
|
PointerUpdateKind.RightButtonReleased;
|
||||||
|
|
||||||
|
UpdateSelection(
|
||||||
|
sender,
|
||||||
|
row.RowIndex,
|
||||||
|
select: true,
|
||||||
|
rangeModifier: e.KeyModifiers.HasFlag(KeyModifiers.Shift),
|
||||||
|
toggleModifier: toggleModifier,
|
||||||
|
rightButton: isRightButton);
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateSelection(TreeDataGrid treeDataGrid, int rowIndex, bool select = true, bool rangeModifier = false, bool toggleModifier = false, bool rightButton = false)
|
||||||
|
{
|
||||||
|
var modelIndex = _source.Rows.RowIndexToModelIndex(rowIndex);
|
||||||
|
if (modelIndex == default)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var mode = SingleSelect ? SelectionMode.Single : SelectionMode.Multiple;
|
||||||
|
var multi = (mode & SelectionMode.Multiple) != 0;
|
||||||
|
var toggle = (toggleModifier || (mode & SelectionMode.Toggle) != 0);
|
||||||
|
var range = multi && rangeModifier;
|
||||||
|
|
||||||
|
if (!select)
|
||||||
|
{
|
||||||
|
if (IsSelected(modelIndex) && !treeDataGrid.QueryCancelSelection())
|
||||||
|
Deselect(modelIndex);
|
||||||
|
}
|
||||||
|
else if (rightButton)
|
||||||
|
{
|
||||||
|
if (IsSelected(modelIndex) == false && !treeDataGrid.QueryCancelSelection())
|
||||||
|
SelectedIndex = modelIndex;
|
||||||
|
}
|
||||||
|
else if (range)
|
||||||
|
{
|
||||||
|
if (!treeDataGrid.QueryCancelSelection())
|
||||||
|
{
|
||||||
|
var anchor = RangeAnchorIndex;
|
||||||
|
var i = Math.Max(_source.Rows.ModelIndexToRowIndex(anchor), 0);
|
||||||
|
var step = i < rowIndex ? 1 : -1;
|
||||||
|
|
||||||
|
using (BatchUpdate())
|
||||||
|
{
|
||||||
|
Clear();
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var m = _source.Rows.RowIndexToModelIndex(i);
|
||||||
|
Select(m);
|
||||||
|
anchor = m;
|
||||||
|
if (i == rowIndex)
|
||||||
|
break;
|
||||||
|
i += step;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (multi && toggle)
|
||||||
|
{
|
||||||
|
if (!treeDataGrid.QueryCancelSelection())
|
||||||
|
{
|
||||||
|
if (IsSelected(modelIndex) == true)
|
||||||
|
Deselect(modelIndex);
|
||||||
|
else
|
||||||
|
Select(modelIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (toggle)
|
||||||
|
{
|
||||||
|
if (!treeDataGrid.QueryCancelSelection())
|
||||||
|
SelectedIndex = (SelectedIndex == modelIndex) ? -1 : modelIndex;
|
||||||
|
}
|
||||||
|
else if (SelectedIndex != modelIndex || Count > 1)
|
||||||
|
{
|
||||||
|
if (!treeDataGrid.QueryCancelSelection())
|
||||||
|
SelectedIndex = modelIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryKeyExpandCollapse(TreeDataGrid treeDataGrid, NavigationDirection direction, TreeDataGridRow focused)
|
||||||
|
{
|
||||||
|
if (treeDataGrid.RowsPresenter is null || focused.RowIndex < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var row = _source.Rows[focused.RowIndex];
|
||||||
|
|
||||||
|
if (row is IExpander expander)
|
||||||
|
{
|
||||||
|
if (direction == NavigationDirection.Right && !expander.IsExpanded)
|
||||||
|
{
|
||||||
|
expander.IsExpanded = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (direction == NavigationDirection.Left && expander.IsExpanded)
|
||||||
|
{
|
||||||
|
expander.IsExpanded = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool MoveSelection(TreeDataGrid treeDataGrid, NavigationDirection direction, bool rangeModifier, TreeDataGridRow focused)
|
||||||
|
{
|
||||||
|
if (treeDataGrid.RowsPresenter is null || _source.Columns.Count == 0 || _source.Rows.Count == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var currentRowIndex = focused?.RowIndex ?? _source.Rows.ModelIndexToRowIndex(SelectedIndex);
|
||||||
|
int newRowIndex;
|
||||||
|
|
||||||
|
if (direction == NavigationDirection.First || direction == NavigationDirection.Last)
|
||||||
|
{
|
||||||
|
newRowIndex = direction == NavigationDirection.First ? 0 : _source.Rows.Count - 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
(var x, var y) = direction switch
|
||||||
|
{
|
||||||
|
NavigationDirection.Up => (0, -1),
|
||||||
|
NavigationDirection.Down => (0, 1),
|
||||||
|
NavigationDirection.Left => (-1, 0),
|
||||||
|
NavigationDirection.Right => (1, 0),
|
||||||
|
_ => (0, 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
newRowIndex = Math.Max(0, Math.Min(currentRowIndex + y, _source.Rows.Count - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newRowIndex != currentRowIndex)
|
||||||
|
UpdateSelection(treeDataGrid, newRowIndex, true, rangeModifier);
|
||||||
|
|
||||||
|
if (newRowIndex != currentRowIndex)
|
||||||
|
{
|
||||||
|
treeDataGrid.RowsPresenter?.BringIntoView(newRowIndex);
|
||||||
|
FocusRow(treeDataGrid, treeDataGrid.TryGetRow(newRowIndex));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void FocusRow(TreeDataGrid owner, Control control)
|
||||||
|
{
|
||||||
|
if (!owner.TryGetRow(control, out var row) || row.CellsPresenter is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Get the column index of the currently focused cell if possible: we'll try to focus the
|
||||||
|
// same column in the new row.
|
||||||
|
if (TopLevel.GetTopLevel(owner)?.FocusManager is { } focusManager &&
|
||||||
|
focusManager.GetFocusedElement() is Control currentFocus &&
|
||||||
|
owner.TryGetCell(currentFocus, out var currentCell) &&
|
||||||
|
row.TryGetCell(currentCell.ColumnIndex) is { } newCell &&
|
||||||
|
newCell.Focusable)
|
||||||
|
{
|
||||||
|
newCell.Focus();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Otherwise, just focus the first focusable cell in the row.
|
||||||
|
foreach (var cell in row.CellsPresenter.GetRealizedElements())
|
||||||
|
{
|
||||||
|
if (cell.Focusable)
|
||||||
|
{
|
||||||
|
cell.Focus();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override IEnumerable<TModel> GetChildren(TModel node)
|
||||||
|
{
|
||||||
|
if (node == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return _childrenGetter?.Invoke(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool HasChildren(IRow row)
|
||||||
|
{
|
||||||
|
var children = GetChildren(row.Model as TModel);
|
||||||
|
if (children != null)
|
||||||
|
{
|
||||||
|
foreach (var c in children)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
|
|
||||||
|
@ -20,6 +21,7 @@ namespace SourceGit.Native
|
||||||
void OpenWithDefaultEditor(string file);
|
void OpenWithDefaultEditor(string file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static readonly string DataDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "SourceGit");
|
||||||
public static string GitExecutable { get; set; } = string.Empty;
|
public static string GitExecutable { get; set; } = string.Empty;
|
||||||
public static List<Models.ExternalTool> ExternalTools { get; set; } = new List<Models.ExternalTool>();
|
public static List<Models.ExternalTool> ExternalTools { get; set; } = new List<Models.ExternalTool>();
|
||||||
|
|
||||||
|
@ -72,6 +74,9 @@ namespace SourceGit.Native
|
||||||
|
|
||||||
public static void SetupApp(AppBuilder builder)
|
public static void SetupApp(AppBuilder builder)
|
||||||
{
|
{
|
||||||
|
if (!Directory.Exists(DataDir))
|
||||||
|
Directory.CreateDirectory(DataDir);
|
||||||
|
|
||||||
_backend.SetupApp(builder);
|
_backend.SetupApp(builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,4 +97,5 @@
|
||||||
<StreamGeometry x:Key="Icons.GitFlow.Release">M884 159l-18-18a43 43 0 00-38-12l-235 43a166 166 0 00-101 60L400 349a128 128 0 00-148 47l-120 171a21 21 0 005 29l17 12a128 128 0 00178-32l27-38 124 124-38 27a128 128 0 00-32 178l12 17a21 21 0 0029 5l171-120a128 128 0 0047-148l117-92A166 166 0 00853 431l43-235a43 43 0 00-12-38zm-177 249a64 64 0 110-90 64 64 0 010 90zm-373 312a21 21 0 010 30l-139 139a21 21 0 01-30 0l-30-30a21 21 0 010-30l139-139a21 21 0 0130 0z</StreamGeometry>
|
<StreamGeometry x:Key="Icons.GitFlow.Release">M884 159l-18-18a43 43 0 00-38-12l-235 43a166 166 0 00-101 60L400 349a128 128 0 00-148 47l-120 171a21 21 0 005 29l17 12a128 128 0 00178-32l27-38 124 124-38 27a128 128 0 00-32 178l12 17a21 21 0 0029 5l171-120a128 128 0 0047-148l117-92A166 166 0 00853 431l43-235a43 43 0 00-12-38zm-177 249a64 64 0 110-90 64 64 0 010 90zm-373 312a21 21 0 010 30l-139 139a21 21 0 01-30 0l-30-30a21 21 0 010-30l139-139a21 21 0 0130 0z</StreamGeometry>
|
||||||
<StreamGeometry x:Key="Icons.Lines.Incr">M408 232C408 210 426 192 448 192h416a40 40 0 110 80H448a40 40 0 01-40-40zM408 512c0-22 18-40 40-40h416a40 40 0 110 80H448A40 40 0 01408 512zM448 752A40 40 0 00448 832h416a40 40 0 100-80H448zM32 480l132 0 0-128 64 0 0 128 132 0 0 64-132 0 0 128-64 0 0-128-132 0Z</StreamGeometry>
|
<StreamGeometry x:Key="Icons.Lines.Incr">M408 232C408 210 426 192 448 192h416a40 40 0 110 80H448a40 40 0 01-40-40zM408 512c0-22 18-40 40-40h416a40 40 0 110 80H448A40 40 0 01408 512zM448 752A40 40 0 00448 832h416a40 40 0 100-80H448zM32 480l132 0 0-128 64 0 0 128 132 0 0 64-132 0 0 128-64 0 0-128-132 0Z</StreamGeometry>
|
||||||
<StreamGeometry x:Key="Icons.Lines.Decr">M408 232C408 210 426 192 448 192h416a40 40 0 110 80H448a40 40 0 01-40-40zM408 512c0-22 18-40 40-40h416a40 40 0 110 80H448A40 40 0 01408 512zM448 752A40 40 0 00448 832h416a40 40 0 100-80H448zM32 480l328 0 0 64-328 0Z</StreamGeometry>
|
<StreamGeometry x:Key="Icons.Lines.Decr">M408 232C408 210 426 192 448 192h416a40 40 0 110 80H448a40 40 0 01-40-40zM408 512c0-22 18-40 40-40h416a40 40 0 110 80H448A40 40 0 01408 512zM448 752A40 40 0 00448 832h416a40 40 0 100-80H448zM32 480l328 0 0 64-328 0Z</StreamGeometry>
|
||||||
|
<StreamGeometry x:Key="Icons.Compare">M645 448l64 64 220-221L704 64l-64 64 115 115H128v90h628zM375 576l-64-64-220 224L314 960l64-64-116-115H896v-90H262z</StreamGeometry>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|
|
@ -33,6 +33,8 @@
|
||||||
<x:String x:Key="Text.Blame" xml:space="preserve">Blame</x:String>
|
<x:String x:Key="Text.Blame" xml:space="preserve">Blame</x:String>
|
||||||
<x:String x:Key="Text.BlameTypeNotSupported" xml:space="preserve">BLAME ON THIS FILE IS NOT SUPPORTED!!!</x:String>
|
<x:String x:Key="Text.BlameTypeNotSupported" xml:space="preserve">BLAME ON THIS FILE IS NOT SUPPORTED!!!</x:String>
|
||||||
<x:String x:Key="Text.BranchCM.Checkout" xml:space="preserve">Checkout${0}$</x:String>
|
<x:String x:Key="Text.BranchCM.Checkout" xml:space="preserve">Checkout${0}$</x:String>
|
||||||
|
<x:String x:Key="Text.BranchCM.CompareWithHead" xml:space="preserve">Compare with HEAD</x:String>
|
||||||
|
<x:String x:Key="Text.BranchCM.CompareWithWorktree" xml:space="preserve">Compare with Worktree</x:String>
|
||||||
<x:String x:Key="Text.BranchCM.CopyName" xml:space="preserve">Copy Branch Name</x:String>
|
<x:String x:Key="Text.BranchCM.CopyName" xml:space="preserve">Copy Branch Name</x:String>
|
||||||
<x:String x:Key="Text.BranchCM.Delete" xml:space="preserve">Delete${0}$</x:String>
|
<x:String x:Key="Text.BranchCM.Delete" xml:space="preserve">Delete${0}$</x:String>
|
||||||
<x:String x:Key="Text.BranchCM.DeleteMultiBranches" xml:space="preserve">Delete selected {0} branches</x:String>
|
<x:String x:Key="Text.BranchCM.DeleteMultiBranches" xml:space="preserve">Delete selected {0} branches</x:String>
|
||||||
|
@ -50,17 +52,18 @@
|
||||||
<x:String x:Key="Text.Bytes" xml:space="preserve">Bytes</x:String>
|
<x:String x:Key="Text.Bytes" xml:space="preserve">Bytes</x:String>
|
||||||
<x:String x:Key="Text.Cancel" xml:space="preserve">CANCEL</x:String>
|
<x:String x:Key="Text.Cancel" xml:space="preserve">CANCEL</x:String>
|
||||||
<x:String x:Key="Text.ChangeDisplayMode" xml:space="preserve">CHANGE DISPLAY MODE</x:String>
|
<x:String x:Key="Text.ChangeDisplayMode" xml:space="preserve">CHANGE DISPLAY MODE</x:String>
|
||||||
<x:String x:Key="Text.ChangeDisplayMode.Grid" xml:space="preserve">Show as Grid</x:String>
|
<x:String x:Key="Text.ChangeDisplayMode.Grid" xml:space="preserve">Show as File and Dir List</x:String>
|
||||||
<x:String x:Key="Text.ChangeDisplayMode.List" xml:space="preserve">Show as List</x:String>
|
<x:String x:Key="Text.ChangeDisplayMode.List" xml:space="preserve">Show as Path List</x:String>
|
||||||
<x:String x:Key="Text.ChangeDisplayMode.Tree" xml:space="preserve">Show as Tree</x:String>
|
<x:String x:Key="Text.ChangeDisplayMode.Tree" xml:space="preserve">Show as Filesystem Tree</x:String>
|
||||||
<x:String x:Key="Text.Checkout" xml:space="preserve">Checkout Branch</x:String>
|
<x:String x:Key="Text.Checkout" xml:space="preserve">Checkout Branch</x:String>
|
||||||
<x:String x:Key="Text.Checkout.Commit" xml:space="preserve">Checkout Commit</x:String>
|
<x:String x:Key="Text.Checkout.Commit" xml:space="preserve">Checkout Commit</x:String>
|
||||||
<x:String x:Key="Text.Checkout.Commit.Warning" xml:space="preserve">Warning: By doing a commit checkout, your Head will be detached</x:String>
|
<x:String x:Key="Text.Checkout.Commit.Warning" xml:space="preserve">Warning: By doing a commit checkout, your Head will be detached</x:String>
|
||||||
<x:String x:Key="Text.Checkout.Commit.Target" xml:space="preserve">Commit :</x:String>
|
<x:String x:Key="Text.Checkout.Commit.Target" xml:space="preserve">Commit :</x:String>
|
||||||
<x:String x:Key="Text.Checkout.Target" xml:space="preserve">Branch :</x:String>
|
<x:String x:Key="Text.Checkout.Target" xml:space="preserve">Branch :</x:String>
|
||||||
<x:String x:Key="Text.Checkout.LocalChanges" xml:space="preserve">Local Changes :</x:String>
|
<x:String x:Key="Text.Checkout.LocalChanges" xml:space="preserve">Local Changes :</x:String>
|
||||||
<x:String x:Key="Text.Checkout.LocalChanges.StashAndReply" xml:space="preserve">Stash & Reapply</x:String>
|
|
||||||
<x:String x:Key="Text.Checkout.LocalChanges.Discard" xml:space="preserve">Discard</x:String>
|
<x:String x:Key="Text.Checkout.LocalChanges.Discard" xml:space="preserve">Discard</x:String>
|
||||||
|
<x:String x:Key="Text.Checkout.LocalChanges.DoNothing" xml:space="preserve">Do Nothing</x:String>
|
||||||
|
<x:String x:Key="Text.Checkout.LocalChanges.StashAndReply" xml:space="preserve">Stash & Reapply</x:String>
|
||||||
<x:String x:Key="Text.CherryPick" xml:space="preserve">Cherry-Pick This Commit</x:String>
|
<x:String x:Key="Text.CherryPick" xml:space="preserve">Cherry-Pick This Commit</x:String>
|
||||||
<x:String x:Key="Text.CherryPick.Commit" xml:space="preserve">Commit :</x:String>
|
<x:String x:Key="Text.CherryPick.Commit" xml:space="preserve">Commit :</x:String>
|
||||||
<x:String x:Key="Text.CherryPick.CommitChanges" xml:space="preserve">Commit all changes</x:String>
|
<x:String x:Key="Text.CherryPick.CommitChanges" xml:space="preserve">Commit all changes</x:String>
|
||||||
|
@ -76,8 +79,10 @@
|
||||||
<x:String x:Key="Text.Clone.RemoteURL" xml:space="preserve">Repository URL :</x:String>
|
<x:String x:Key="Text.Clone.RemoteURL" xml:space="preserve">Repository URL :</x:String>
|
||||||
<x:String x:Key="Text.Close" xml:space="preserve">CLOSE</x:String>
|
<x:String x:Key="Text.Close" xml:space="preserve">CLOSE</x:String>
|
||||||
<x:String x:Key="Text.CommitCM.CherryPick" xml:space="preserve">Cherry-Pick This Commit</x:String>
|
<x:String x:Key="Text.CommitCM.CherryPick" xml:space="preserve">Cherry-Pick This Commit</x:String>
|
||||||
<x:String x:Key="Text.CommitCM.CopySHA" xml:space="preserve">Copy SHA</x:String>
|
|
||||||
<x:String x:Key="Text.CommitCM.Checkout" xml:space="preserve">Checkout Commit</x:String>
|
<x:String x:Key="Text.CommitCM.Checkout" xml:space="preserve">Checkout Commit</x:String>
|
||||||
|
<x:String x:Key="Text.CommitCM.CompareWithHead" xml:space="preserve">Compare with HEAD</x:String>
|
||||||
|
<x:String x:Key="Text.CommitCM.CompareWithWorktree" xml:space="preserve">Compare with Worktree</x:String>
|
||||||
|
<x:String x:Key="Text.CommitCM.CopySHA" xml:space="preserve">Copy SHA</x:String>
|
||||||
<x:String x:Key="Text.CommitCM.Rebase" xml:space="preserve">Rebase${0}$to Here</x:String>
|
<x:String x:Key="Text.CommitCM.Rebase" xml:space="preserve">Rebase${0}$to Here</x:String>
|
||||||
<x:String x:Key="Text.CommitCM.Reset" xml:space="preserve">Reset${0}$to Here</x:String>
|
<x:String x:Key="Text.CommitCM.Reset" xml:space="preserve">Reset${0}$to Here</x:String>
|
||||||
<x:String x:Key="Text.CommitCM.Revert" xml:space="preserve">Revert Commit</x:String>
|
<x:String x:Key="Text.CommitCM.Revert" xml:space="preserve">Revert Commit</x:String>
|
||||||
|
@ -109,13 +114,14 @@
|
||||||
<x:String x:Key="Text.Configure.User.Placeholder" xml:space="preserve">User name for this repository</x:String>
|
<x:String x:Key="Text.Configure.User.Placeholder" xml:space="preserve">User name for this repository</x:String>
|
||||||
<x:String x:Key="Text.Copy" xml:space="preserve">Copy</x:String>
|
<x:String x:Key="Text.Copy" xml:space="preserve">Copy</x:String>
|
||||||
<x:String x:Key="Text.CopyPath" xml:space="preserve">Copy Path</x:String>
|
<x:String x:Key="Text.CopyPath" xml:space="preserve">Copy Path</x:String>
|
||||||
|
<x:String x:Key="Text.CopyFileName" xml:space="preserve">Copy File Name</x:String>
|
||||||
<x:String x:Key="Text.CreateBranch" xml:space="preserve">Create Branch</x:String>
|
<x:String x:Key="Text.CreateBranch" xml:space="preserve">Create Branch</x:String>
|
||||||
<x:String x:Key="Text.CreateBranch.BasedOn" xml:space="preserve">Based On :</x:String>
|
<x:String x:Key="Text.CreateBranch.BasedOn" xml:space="preserve">Based On :</x:String>
|
||||||
<x:String x:Key="Text.CreateBranch.Checkout" xml:space="preserve">Check out after created</x:String>
|
<x:String x:Key="Text.CreateBranch.Checkout" xml:space="preserve">Check out after created</x:String>
|
||||||
<x:String x:Key="Text.CreateBranch.LocalChanges" xml:space="preserve">Local Changes :</x:String>
|
<x:String x:Key="Text.CreateBranch.LocalChanges" xml:space="preserve">Local Changes :</x:String>
|
||||||
<x:String x:Key="Text.CreateBranch.LocalChanges.Discard" xml:space="preserve">Discard</x:String>
|
<x:String x:Key="Text.CreateBranch.LocalChanges.Discard" xml:space="preserve">Discard</x:String>
|
||||||
<x:String x:Key="Text.CreateBranch.LocalChanges.StashAndReply" xml:space="preserve">Stash & Reapply</x:String>
|
|
||||||
<x:String x:Key="Text.CreateBranch.LocalChanges.DoNothing" xml:space="preserve">Do Nothing</x:String>
|
<x:String x:Key="Text.CreateBranch.LocalChanges.DoNothing" xml:space="preserve">Do Nothing</x:String>
|
||||||
|
<x:String x:Key="Text.CreateBranch.LocalChanges.StashAndReply" xml:space="preserve">Stash & Reapply</x:String>
|
||||||
<x:String x:Key="Text.CreateBranch.Name" xml:space="preserve">New Branch Name :</x:String>
|
<x:String x:Key="Text.CreateBranch.Name" xml:space="preserve">New Branch Name :</x:String>
|
||||||
<x:String x:Key="Text.CreateBranch.Name.Placeholder" xml:space="preserve">Enter branch name.</x:String>
|
<x:String x:Key="Text.CreateBranch.Name.Placeholder" xml:space="preserve">Enter branch name.</x:String>
|
||||||
<x:String x:Key="Text.CreateBranch.Title" xml:space="preserve">Create Local Branch</x:String>
|
<x:String x:Key="Text.CreateBranch.Title" xml:space="preserve">Create Local Branch</x:String>
|
||||||
|
@ -157,6 +163,8 @@
|
||||||
<x:String x:Key="Text.Diff.NoChange" xml:space="preserve">NO CHANGES OR ONLY EOL CHANGES</x:String>
|
<x:String x:Key="Text.Diff.NoChange" xml:space="preserve">NO CHANGES OR ONLY EOL CHANGES</x:String>
|
||||||
<x:String x:Key="Text.Diff.Prev" xml:space="preserve">Previous Difference</x:String>
|
<x:String x:Key="Text.Diff.Prev" xml:space="preserve">Previous Difference</x:String>
|
||||||
<x:String x:Key="Text.Diff.SideBySide" xml:space="preserve">Side-By-Side Diff</x:String>
|
<x:String x:Key="Text.Diff.SideBySide" xml:space="preserve">Side-By-Side Diff</x:String>
|
||||||
|
<x:String x:Key="Text.Diff.Submodule" xml:space="preserve">SUBMODULE</x:String>
|
||||||
|
<x:String x:Key="Text.Diff.Submodule.New" xml:space="preserve">NEW</x:String>
|
||||||
<x:String x:Key="Text.Diff.SyntaxHighlight" xml:space="preserve">Syntax Highlighting</x:String>
|
<x:String x:Key="Text.Diff.SyntaxHighlight" xml:space="preserve">Syntax Highlighting</x:String>
|
||||||
<x:String x:Key="Text.Diff.UseMerger" xml:space="preserve">Open In Merge Tool</x:String>
|
<x:String x:Key="Text.Diff.UseMerger" xml:space="preserve">Open In Merge Tool</x:String>
|
||||||
<x:String x:Key="Text.Diff.VisualLines.Decr" xml:space="preserve">Decrease Number of Visible Lines</x:String>
|
<x:String x:Key="Text.Diff.VisualLines.Decr" xml:space="preserve">Decrease Number of Visible Lines</x:String>
|
||||||
|
@ -301,8 +309,11 @@
|
||||||
<x:String x:Key="Text.Preference.Git.User" xml:space="preserve">User Name</x:String>
|
<x:String x:Key="Text.Preference.Git.User" xml:space="preserve">User Name</x:String>
|
||||||
<x:String x:Key="Text.Preference.Git.User.Placeholder" xml:space="preserve">Global git user name</x:String>
|
<x:String x:Key="Text.Preference.Git.User.Placeholder" xml:space="preserve">Global git user name</x:String>
|
||||||
<x:String x:Key="Text.Preference.Git.Version" xml:space="preserve">Git version</x:String>
|
<x:String x:Key="Text.Preference.Git.Version" xml:space="preserve">Git version</x:String>
|
||||||
|
<x:String x:Key="Text.Preference.Git.Invalid" xml:space="preserve">Git (>= 2.23.0) is required by this app</x:String>
|
||||||
<x:String x:Key="Text.Preference.GPG" xml:space="preserve">GPG SIGNING</x:String>
|
<x:String x:Key="Text.Preference.GPG" xml:space="preserve">GPG SIGNING</x:String>
|
||||||
<x:String x:Key="Text.Preference.GPG.Enabled" xml:space="preserve">Commit GPG signing</x:String>
|
<x:String x:Key="Text.Preference.GPG.CommitEnabled" xml:space="preserve">Commit GPG signing</x:String>
|
||||||
|
<x:String x:Key="Text.Preference.GPG.TagEnabled" xml:space="preserve">Tag GPG signing</x:String>
|
||||||
|
<x:String x:Key="Text.Preference.GPG.Format" xml:space="preserve">GPG Format</x:String>
|
||||||
<x:String x:Key="Text.Preference.GPG.Path" xml:space="preserve">Install Path</x:String>
|
<x:String x:Key="Text.Preference.GPG.Path" xml:space="preserve">Install Path</x:String>
|
||||||
<x:String x:Key="Text.Preference.GPG.Path.Placeholder" xml:space="preserve">Input path for installed gpg program</x:String>
|
<x:String x:Key="Text.Preference.GPG.Path.Placeholder" xml:space="preserve">Input path for installed gpg program</x:String>
|
||||||
<x:String x:Key="Text.Preference.GPG.UserKey" xml:space="preserve">User Signing Key</x:String>
|
<x:String x:Key="Text.Preference.GPG.UserKey" xml:space="preserve">User Signing Key</x:String>
|
||||||
|
@ -314,9 +325,12 @@
|
||||||
<x:String x:Key="Text.Preference.Merger.Path.Placeholder" xml:space="preserve">Input path for merge tool</x:String>
|
<x:String x:Key="Text.Preference.Merger.Path.Placeholder" xml:space="preserve">Input path for merge tool</x:String>
|
||||||
<x:String x:Key="Text.Preference.Merger.Type" xml:space="preserve">Merger</x:String>
|
<x:String x:Key="Text.Preference.Merger.Type" xml:space="preserve">Merger</x:String>
|
||||||
<x:String x:Key="Text.Pull" xml:space="preserve">Pull</x:String>
|
<x:String x:Key="Text.Pull" xml:space="preserve">Pull</x:String>
|
||||||
<x:String x:Key="Text.Pull.AutoStash" xml:space="preserve">Stash & reapply local changes</x:String>
|
|
||||||
<x:String x:Key="Text.Pull.Branch" xml:space="preserve">Branch :</x:String>
|
<x:String x:Key="Text.Pull.Branch" xml:space="preserve">Branch :</x:String>
|
||||||
<x:String x:Key="Text.Pull.Into" xml:space="preserve">Into :</x:String>
|
<x:String x:Key="Text.Pull.Into" xml:space="preserve">Into :</x:String>
|
||||||
|
<x:String x:Key="Text.Pull.LocalChanges" xml:space="preserve">Local Changes :</x:String>
|
||||||
|
<x:String x:Key="Text.Pull.LocalChanges.Discard" xml:space="preserve">Discard</x:String>
|
||||||
|
<x:String x:Key="Text.Pull.LocalChanges.DoNothing" xml:space="preserve">Do Nothing</x:String>
|
||||||
|
<x:String x:Key="Text.Pull.LocalChanges.StashAndReply" xml:space="preserve">Stash & Reapply</x:String>
|
||||||
<x:String x:Key="Text.Pull.Remote" xml:space="preserve">Remote :</x:String>
|
<x:String x:Key="Text.Pull.Remote" xml:space="preserve">Remote :</x:String>
|
||||||
<x:String x:Key="Text.Pull.Title" xml:space="preserve">Pull (Fetch & Merge)</x:String>
|
<x:String x:Key="Text.Pull.Title" xml:space="preserve">Pull (Fetch & Merge)</x:String>
|
||||||
<x:String x:Key="Text.Pull.UseRebase" xml:space="preserve">Use rebase instead of merge</x:String>
|
<x:String x:Key="Text.Pull.UseRebase" xml:space="preserve">Use rebase instead of merge</x:String>
|
||||||
|
@ -326,7 +340,7 @@
|
||||||
<x:String x:Key="Text.Push.Remote" xml:space="preserve">Remote :</x:String>
|
<x:String x:Key="Text.Push.Remote" xml:space="preserve">Remote :</x:String>
|
||||||
<x:String x:Key="Text.Push.Title" xml:space="preserve">Push Changes To Remote</x:String>
|
<x:String x:Key="Text.Push.Title" xml:space="preserve">Push Changes To Remote</x:String>
|
||||||
<x:String x:Key="Text.Push.To" xml:space="preserve">Remote Branch :</x:String>
|
<x:String x:Key="Text.Push.To" xml:space="preserve">Remote Branch :</x:String>
|
||||||
<x:String x:Key="Text.Push.Tracking" xml:space="preserve">Tracking remote branch(--set-upstream)</x:String>
|
<x:String x:Key="Text.Push.Tracking" xml:space="preserve">Tracking remote branch</x:String>
|
||||||
<x:String x:Key="Text.Push.WithAllTags" xml:space="preserve">Push all tags</x:String>
|
<x:String x:Key="Text.Push.WithAllTags" xml:space="preserve">Push all tags</x:String>
|
||||||
<x:String x:Key="Text.PushTag" xml:space="preserve">Push Tag To Remote</x:String>
|
<x:String x:Key="Text.PushTag" xml:space="preserve">Push Tag To Remote</x:String>
|
||||||
<x:String x:Key="Text.PushTag.PushAllRemotes" xml:space="preserve">Push to all remotes</x:String>
|
<x:String x:Key="Text.PushTag.PushAllRemotes" xml:space="preserve">Push to all remotes</x:String>
|
||||||
|
@ -476,4 +490,5 @@
|
||||||
<x:String x:Key="Text.WorkingCopy.Unstaged.StageAll" xml:space="preserve">STAGE ALL</x:String>
|
<x:String x:Key="Text.WorkingCopy.Unstaged.StageAll" xml:space="preserve">STAGE ALL</x:String>
|
||||||
<x:String x:Key="Text.WorkingCopy.Unstaged.ViewAssumeUnchaged" xml:space="preserve">VIEW ASSUME UNCHANGED</x:String>
|
<x:String x:Key="Text.WorkingCopy.Unstaged.ViewAssumeUnchaged" xml:space="preserve">VIEW ASSUME UNCHANGED</x:String>
|
||||||
<x:String x:Key="Text.WorkingCopy.ResolveTip" xml:space="preserve">Right-click the selected file(s), and make your choice to resolve conflicts.</x:String>
|
<x:String x:Key="Text.WorkingCopy.ResolveTip" xml:space="preserve">Right-click the selected file(s), and make your choice to resolve conflicts.</x:String>
|
||||||
|
<x:String x:Key="Text.Worktree" xml:space="preserve">WORKTREE</x:String>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
<ResourceDictionary xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
<ResourceDictionary xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
<ResourceDictionary.MergedDictionaries>
|
||||||
|
<ResourceInclude Source="en_US.axaml"/>
|
||||||
|
</ResourceDictionary.MergedDictionaries>
|
||||||
<x:String x:Key="Text.About" xml:space="preserve">关于软件</x:String>
|
<x:String x:Key="Text.About" xml:space="preserve">关于软件</x:String>
|
||||||
<x:String x:Key="Text.About.Menu" xml:space="preserve">关于本软件</x:String>
|
<x:String x:Key="Text.About.Menu" xml:space="preserve">关于本软件</x:String>
|
||||||
<x:String x:Key="Text.About.BuildWith" xml:space="preserve">• 项目依赖于 </x:String>
|
<x:String x:Key="Text.About.BuildWith" xml:space="preserve">• 项目依赖于 </x:String>
|
||||||
|
@ -33,6 +36,8 @@
|
||||||
<x:String x:Key="Text.Blame" xml:space="preserve">逐行追溯(blame)</x:String>
|
<x:String x:Key="Text.Blame" xml:space="preserve">逐行追溯(blame)</x:String>
|
||||||
<x:String x:Key="Text.BlameTypeNotSupported" xml:space="preserve">选中文件不支持该操作!!!</x:String>
|
<x:String x:Key="Text.BlameTypeNotSupported" xml:space="preserve">选中文件不支持该操作!!!</x:String>
|
||||||
<x:String x:Key="Text.BranchCM.Checkout" xml:space="preserve">检出(checkout)${0}$</x:String>
|
<x:String x:Key="Text.BranchCM.Checkout" xml:space="preserve">检出(checkout)${0}$</x:String>
|
||||||
|
<x:String x:Key="Text.BranchCM.CompareWithHead" xml:space="preserve">与当前HEAD比较</x:String>
|
||||||
|
<x:String x:Key="Text.BranchCM.CompareWithWorktree" xml:space="preserve">与本地工作树比较</x:String>
|
||||||
<x:String x:Key="Text.BranchCM.CopyName" xml:space="preserve">复制分支名</x:String>
|
<x:String x:Key="Text.BranchCM.CopyName" xml:space="preserve">复制分支名</x:String>
|
||||||
<x:String x:Key="Text.BranchCM.Delete" xml:space="preserve">删除${0}$</x:String>
|
<x:String x:Key="Text.BranchCM.Delete" xml:space="preserve">删除${0}$</x:String>
|
||||||
<x:String x:Key="Text.BranchCM.DeleteMultiBranches" xml:space="preserve">删除选中的 {0} 个分支</x:String>
|
<x:String x:Key="Text.BranchCM.DeleteMultiBranches" xml:space="preserve">删除选中的 {0} 个分支</x:String>
|
||||||
|
@ -50,17 +55,18 @@
|
||||||
<x:String x:Key="Text.Bytes" xml:space="preserve">字节</x:String>
|
<x:String x:Key="Text.Bytes" xml:space="preserve">字节</x:String>
|
||||||
<x:String x:Key="Text.Cancel" xml:space="preserve">取 消</x:String>
|
<x:String x:Key="Text.Cancel" xml:space="preserve">取 消</x:String>
|
||||||
<x:String x:Key="Text.ChangeDisplayMode" xml:space="preserve">切换变更显示模式</x:String>
|
<x:String x:Key="Text.ChangeDisplayMode" xml:space="preserve">切换变更显示模式</x:String>
|
||||||
<x:String x:Key="Text.ChangeDisplayMode.Grid" xml:space="preserve">网格模式</x:String>
|
<x:String x:Key="Text.ChangeDisplayMode.Grid" xml:space="preserve">文件名+路径列表模式</x:String>
|
||||||
<x:String x:Key="Text.ChangeDisplayMode.List" xml:space="preserve">列表模式</x:String>
|
<x:String x:Key="Text.ChangeDisplayMode.List" xml:space="preserve">全路径列表模式</x:String>
|
||||||
<x:String x:Key="Text.ChangeDisplayMode.Tree" xml:space="preserve">树形模式</x:String>
|
<x:String x:Key="Text.ChangeDisplayMode.Tree" xml:space="preserve">文件目录树形结构模式</x:String>
|
||||||
<x:String x:Key="Text.Checkout" xml:space="preserve">检出(checkout)分支</x:String>
|
<x:String x:Key="Text.Checkout" xml:space="preserve">检出(checkout)分支</x:String>
|
||||||
<x:String x:Key="Text.Checkout.Commit" xml:space="preserve">检出(checkout)提交</x:String>
|
<x:String x:Key="Text.Checkout.Commit" xml:space="preserve">检出(checkout)提交</x:String>
|
||||||
<x:String x:Key="Text.Checkout.Commit.Warning" xml:space="preserve">注意:执行该操作后,当前HEAD会变为游离(detached)状态!</x:String>
|
<x:String x:Key="Text.Checkout.Commit.Warning" xml:space="preserve">注意:执行该操作后,当前HEAD会变为游离(detached)状态!</x:String>
|
||||||
<x:String x:Key="Text.Checkout.Commit.Target" xml:space="preserve">提交 :</x:String>
|
<x:String x:Key="Text.Checkout.Commit.Target" xml:space="preserve">提交 :</x:String>
|
||||||
<x:String x:Key="Text.Checkout.Target" xml:space="preserve">目标分支 :</x:String>
|
<x:String x:Key="Text.Checkout.Target" xml:space="preserve">目标分支 :</x:String>
|
||||||
<x:String x:Key="Text.Checkout.LocalChanges" xml:space="preserve">未提交更改 :</x:String>
|
<x:String x:Key="Text.Checkout.LocalChanges" xml:space="preserve">未提交更改 :</x:String>
|
||||||
<x:String x:Key="Text.Checkout.LocalChanges.StashAndReply" xml:space="preserve">贮藏(stash)并自动恢复</x:String>
|
<x:String x:Key="Text.Checkout.LocalChanges.Discard" xml:space="preserve">丢弃更改</x:String>
|
||||||
<x:String x:Key="Text.Checkout.LocalChanges.Discard" xml:space="preserve">忽略</x:String>
|
<x:String x:Key="Text.Checkout.LocalChanges.DoNothing" xml:space="preserve">不做处理</x:String>
|
||||||
|
<x:String x:Key="Text.Checkout.LocalChanges.StashAndReply" xml:space="preserve">贮藏并自动恢复</x:String>
|
||||||
<x:String x:Key="Text.CherryPick" xml:space="preserve">挑选(cherry-pick)此提交</x:String>
|
<x:String x:Key="Text.CherryPick" xml:space="preserve">挑选(cherry-pick)此提交</x:String>
|
||||||
<x:String x:Key="Text.CherryPick.Commit" xml:space="preserve">提交ID :</x:String>
|
<x:String x:Key="Text.CherryPick.Commit" xml:space="preserve">提交ID :</x:String>
|
||||||
<x:String x:Key="Text.CherryPick.CommitChanges" xml:space="preserve">提交变化</x:String>
|
<x:String x:Key="Text.CherryPick.CommitChanges" xml:space="preserve">提交变化</x:String>
|
||||||
|
@ -77,6 +83,8 @@
|
||||||
<x:String x:Key="Text.Close" xml:space="preserve">关闭</x:String>
|
<x:String x:Key="Text.Close" xml:space="preserve">关闭</x:String>
|
||||||
<x:String x:Key="Text.CommitCM.CherryPick" xml:space="preserve">挑选(cherry-pick)此提交</x:String>
|
<x:String x:Key="Text.CommitCM.CherryPick" xml:space="preserve">挑选(cherry-pick)此提交</x:String>
|
||||||
<x:String x:Key="Text.CommitCM.Checkout" xml:space="preserve">检出此提交</x:String>
|
<x:String x:Key="Text.CommitCM.Checkout" xml:space="preserve">检出此提交</x:String>
|
||||||
|
<x:String x:Key="Text.CommitCM.CompareWithHead" xml:space="preserve">与当前HEAD比较</x:String>
|
||||||
|
<x:String x:Key="Text.CommitCM.CompareWithWorktree" xml:space="preserve">与本地工作树比较</x:String>
|
||||||
<x:String x:Key="Text.CommitCM.CopySHA" xml:space="preserve">复制提交指纹</x:String>
|
<x:String x:Key="Text.CommitCM.CopySHA" xml:space="preserve">复制提交指纹</x:String>
|
||||||
<x:String x:Key="Text.CommitCM.Rebase" xml:space="preserve">变基(rebase)${0}$到此处</x:String>
|
<x:String x:Key="Text.CommitCM.Rebase" xml:space="preserve">变基(rebase)${0}$到此处</x:String>
|
||||||
<x:String x:Key="Text.CommitCM.Reset" xml:space="preserve">重置(reset)${0}$到此处</x:String>
|
<x:String x:Key="Text.CommitCM.Reset" xml:space="preserve">重置(reset)${0}$到此处</x:String>
|
||||||
|
@ -109,13 +117,14 @@
|
||||||
<x:String x:Key="Text.Configure.User.Placeholder" xml:space="preserve">应用于本仓库的用户名</x:String>
|
<x:String x:Key="Text.Configure.User.Placeholder" xml:space="preserve">应用于本仓库的用户名</x:String>
|
||||||
<x:String x:Key="Text.Copy" xml:space="preserve">复制</x:String>
|
<x:String x:Key="Text.Copy" xml:space="preserve">复制</x:String>
|
||||||
<x:String x:Key="Text.CopyPath" xml:space="preserve">复制路径</x:String>
|
<x:String x:Key="Text.CopyPath" xml:space="preserve">复制路径</x:String>
|
||||||
|
<x:String x:Key="Text.CopyFileName" xml:space="preserve">复制文件名</x:String>
|
||||||
<x:String x:Key="Text.CreateBranch" xml:space="preserve">新建分支</x:String>
|
<x:String x:Key="Text.CreateBranch" xml:space="preserve">新建分支</x:String>
|
||||||
<x:String x:Key="Text.CreateBranch.BasedOn" xml:space="preserve">新分支基于 :</x:String>
|
<x:String x:Key="Text.CreateBranch.BasedOn" xml:space="preserve">新分支基于 :</x:String>
|
||||||
<x:String x:Key="Text.CreateBranch.Checkout" xml:space="preserve">完成后切换到新分支</x:String>
|
<x:String x:Key="Text.CreateBranch.Checkout" xml:space="preserve">完成后切换到新分支</x:String>
|
||||||
<x:String x:Key="Text.CreateBranch.LocalChanges" xml:space="preserve">未提交更改 :</x:String>
|
<x:String x:Key="Text.CreateBranch.LocalChanges" xml:space="preserve">未提交更改 :</x:String>
|
||||||
<x:String x:Key="Text.CreateBranch.LocalChanges.Discard" xml:space="preserve">放弃所有</x:String>
|
<x:String x:Key="Text.CreateBranch.LocalChanges.Discard" xml:space="preserve">丢弃更改</x:String>
|
||||||
|
<x:String x:Key="Text.CreateBranch.LocalChanges.DoNothing" xml:space="preserve">不做处理</x:String>
|
||||||
<x:String x:Key="Text.CreateBranch.LocalChanges.StashAndReply" xml:space="preserve">贮藏并自动恢复</x:String>
|
<x:String x:Key="Text.CreateBranch.LocalChanges.StashAndReply" xml:space="preserve">贮藏并自动恢复</x:String>
|
||||||
<x:String x:Key="Text.CreateBranch.LocalChanges.DoNothing" xml:space="preserve">GIT默认</x:String>
|
|
||||||
<x:String x:Key="Text.CreateBranch.Name" xml:space="preserve">新分支名 :</x:String>
|
<x:String x:Key="Text.CreateBranch.Name" xml:space="preserve">新分支名 :</x:String>
|
||||||
<x:String x:Key="Text.CreateBranch.Name.Placeholder" xml:space="preserve">填写分支名称。</x:String>
|
<x:String x:Key="Text.CreateBranch.Name.Placeholder" xml:space="preserve">填写分支名称。</x:String>
|
||||||
<x:String x:Key="Text.CreateBranch.Title" xml:space="preserve">创建本地分支</x:String>
|
<x:String x:Key="Text.CreateBranch.Title" xml:space="preserve">创建本地分支</x:String>
|
||||||
|
@ -157,6 +166,8 @@
|
||||||
<x:String x:Key="Text.Diff.NoChange" xml:space="preserve">没有变更或仅有换行符差异</x:String>
|
<x:String x:Key="Text.Diff.NoChange" xml:space="preserve">没有变更或仅有换行符差异</x:String>
|
||||||
<x:String x:Key="Text.Diff.Prev" xml:space="preserve">上一个差异</x:String>
|
<x:String x:Key="Text.Diff.Prev" xml:space="preserve">上一个差异</x:String>
|
||||||
<x:String x:Key="Text.Diff.SideBySide" xml:space="preserve">分列对比</x:String>
|
<x:String x:Key="Text.Diff.SideBySide" xml:space="preserve">分列对比</x:String>
|
||||||
|
<x:String x:Key="Text.Diff.Submodule" xml:space="preserve">子模块</x:String>
|
||||||
|
<x:String x:Key="Text.Diff.Submodule.New" xml:space="preserve">新增</x:String>
|
||||||
<x:String x:Key="Text.Diff.SyntaxHighlight" xml:space="preserve">语法高亮</x:String>
|
<x:String x:Key="Text.Diff.SyntaxHighlight" xml:space="preserve">语法高亮</x:String>
|
||||||
<x:String x:Key="Text.Diff.UseMerger" xml:space="preserve">使用外部合并工具查看</x:String>
|
<x:String x:Key="Text.Diff.UseMerger" xml:space="preserve">使用外部合并工具查看</x:String>
|
||||||
<x:String x:Key="Text.Diff.VisualLines.Decr" xml:space="preserve">减少可见的行数</x:String>
|
<x:String x:Key="Text.Diff.VisualLines.Decr" xml:space="preserve">减少可见的行数</x:String>
|
||||||
|
@ -301,8 +312,11 @@
|
||||||
<x:String x:Key="Text.Preference.Git.User" xml:space="preserve">用户名</x:String>
|
<x:String x:Key="Text.Preference.Git.User" xml:space="preserve">用户名</x:String>
|
||||||
<x:String x:Key="Text.Preference.Git.User.Placeholder" xml:space="preserve">默认GIT用户名</x:String>
|
<x:String x:Key="Text.Preference.Git.User.Placeholder" xml:space="preserve">默认GIT用户名</x:String>
|
||||||
<x:String x:Key="Text.Preference.Git.Version" xml:space="preserve">Git 版本</x:String>
|
<x:String x:Key="Text.Preference.Git.Version" xml:space="preserve">Git 版本</x:String>
|
||||||
|
<x:String x:Key="Text.Preference.Git.Invalid" xml:space="preserve">本软件要求GIT最低版本为2.23.0</x:String>
|
||||||
<x:String x:Key="Text.Preference.GPG" xml:space="preserve">GPG签名</x:String>
|
<x:String x:Key="Text.Preference.GPG" xml:space="preserve">GPG签名</x:String>
|
||||||
<x:String x:Key="Text.Preference.GPG.Enabled" xml:space="preserve">启用提交签名</x:String>
|
<x:String x:Key="Text.Preference.GPG.CommitEnabled" xml:space="preserve">启用提交签名</x:String>
|
||||||
|
<x:String x:Key="Text.Preference.GPG.TagEnabled" xml:space="preserve">启用标签签名</x:String>
|
||||||
|
<x:String x:Key="Text.Preference.GPG.Format" xml:space="preserve">GPG签名格式</x:String>
|
||||||
<x:String x:Key="Text.Preference.GPG.Path" xml:space="preserve">可执行文件位置</x:String>
|
<x:String x:Key="Text.Preference.GPG.Path" xml:space="preserve">可执行文件位置</x:String>
|
||||||
<x:String x:Key="Text.Preference.GPG.Path.Placeholder" xml:space="preserve">gpg.exe所在路径</x:String>
|
<x:String x:Key="Text.Preference.GPG.Path.Placeholder" xml:space="preserve">gpg.exe所在路径</x:String>
|
||||||
<x:String x:Key="Text.Preference.GPG.UserKey" xml:space="preserve">用户签名KEY</x:String>
|
<x:String x:Key="Text.Preference.GPG.UserKey" xml:space="preserve">用户签名KEY</x:String>
|
||||||
|
@ -314,9 +328,12 @@
|
||||||
<x:String x:Key="Text.Preference.Merger.Path.Placeholder" xml:space="preserve">填写工具可执行文件所在位置</x:String>
|
<x:String x:Key="Text.Preference.Merger.Path.Placeholder" xml:space="preserve">填写工具可执行文件所在位置</x:String>
|
||||||
<x:String x:Key="Text.Preference.Merger.Type" xml:space="preserve">工具</x:String>
|
<x:String x:Key="Text.Preference.Merger.Type" xml:space="preserve">工具</x:String>
|
||||||
<x:String x:Key="Text.Pull" xml:space="preserve">拉回(pull)</x:String>
|
<x:String x:Key="Text.Pull" xml:space="preserve">拉回(pull)</x:String>
|
||||||
<x:String x:Key="Text.Pull.AutoStash" xml:space="preserve">自动贮藏并恢复本地变更</x:String>
|
|
||||||
<x:String x:Key="Text.Pull.Branch" xml:space="preserve">拉取分支 :</x:String>
|
<x:String x:Key="Text.Pull.Branch" xml:space="preserve">拉取分支 :</x:String>
|
||||||
<x:String x:Key="Text.Pull.Into" xml:space="preserve">本地分支 :</x:String>
|
<x:String x:Key="Text.Pull.Into" xml:space="preserve">本地分支 :</x:String>
|
||||||
|
<x:String x:Key="Text.Pull.LocalChanges" xml:space="preserve">未提交更改 :</x:String>
|
||||||
|
<x:String x:Key="Text.Pull.LocalChanges.Discard" xml:space="preserve">丢弃更改</x:String>
|
||||||
|
<x:String x:Key="Text.Pull.LocalChanges.DoNothing" xml:space="preserve">不做处理</x:String>
|
||||||
|
<x:String x:Key="Text.Pull.LocalChanges.StashAndReply" xml:space="preserve">贮藏并自动恢复</x:String>
|
||||||
<x:String x:Key="Text.Pull.Remote" xml:space="preserve">远程 :</x:String>
|
<x:String x:Key="Text.Pull.Remote" xml:space="preserve">远程 :</x:String>
|
||||||
<x:String x:Key="Text.Pull.Title" xml:space="preserve">拉回(拉取并合并)</x:String>
|
<x:String x:Key="Text.Pull.Title" xml:space="preserve">拉回(拉取并合并)</x:String>
|
||||||
<x:String x:Key="Text.Pull.UseRebase" xml:space="preserve">使用变基方式合并分支</x:String>
|
<x:String x:Key="Text.Pull.UseRebase" xml:space="preserve">使用变基方式合并分支</x:String>
|
||||||
|
@ -326,7 +343,7 @@
|
||||||
<x:String x:Key="Text.Push.Remote" xml:space="preserve">远程仓库 :</x:String>
|
<x:String x:Key="Text.Push.Remote" xml:space="preserve">远程仓库 :</x:String>
|
||||||
<x:String x:Key="Text.Push.Title" xml:space="preserve">推送到远程仓库</x:String>
|
<x:String x:Key="Text.Push.Title" xml:space="preserve">推送到远程仓库</x:String>
|
||||||
<x:String x:Key="Text.Push.To" xml:space="preserve">远程分支 :</x:String>
|
<x:String x:Key="Text.Push.To" xml:space="preserve">远程分支 :</x:String>
|
||||||
<x:String x:Key="Text.Push.Tracking" xml:space="preserve">跟踪远程分支(--set-upstream)</x:String>
|
<x:String x:Key="Text.Push.Tracking" xml:space="preserve">跟踪远程分支</x:String>
|
||||||
<x:String x:Key="Text.Push.WithAllTags" xml:space="preserve">同时推送标签</x:String>
|
<x:String x:Key="Text.Push.WithAllTags" xml:space="preserve">同时推送标签</x:String>
|
||||||
<x:String x:Key="Text.PushTag" xml:space="preserve">推送标签到远程仓库</x:String>
|
<x:String x:Key="Text.PushTag" xml:space="preserve">推送标签到远程仓库</x:String>
|
||||||
<x:String x:Key="Text.PushTag.PushAllRemotes" xml:space="preserve">推送到所有远程仓库</x:String>
|
<x:String x:Key="Text.PushTag.PushAllRemotes" xml:space="preserve">推送到所有远程仓库</x:String>
|
||||||
|
@ -476,4 +493,5 @@
|
||||||
<x:String x:Key="Text.WorkingCopy.Unstaged.StageAll" xml:space="preserve">暂存所有</x:String>
|
<x:String x:Key="Text.WorkingCopy.Unstaged.StageAll" xml:space="preserve">暂存所有</x:String>
|
||||||
<x:String x:Key="Text.WorkingCopy.Unstaged.ViewAssumeUnchaged" xml:space="preserve">查看忽略变更文件</x:String>
|
<x:String x:Key="Text.WorkingCopy.Unstaged.ViewAssumeUnchaged" xml:space="preserve">查看忽略变更文件</x:String>
|
||||||
<x:String x:Key="Text.WorkingCopy.ResolveTip" xml:space="preserve">请选中冲突文件,打开右键菜单,选择合适的解决方式</x:String>
|
<x:String x:Key="Text.WorkingCopy.ResolveTip" xml:space="preserve">请选中冲突文件,打开右键菜单,选择合适的解决方式</x:String>
|
||||||
|
<x:String x:Key="Text.Worktree" xml:space="preserve">本地工作树</x:String>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|
|
@ -60,6 +60,18 @@
|
||||||
</Animation>
|
</Animation>
|
||||||
</Style.Animations>
|
</Style.Animations>
|
||||||
</Style>
|
</Style>
|
||||||
|
<Style Selector="Path.change_mode_switcher_icon">
|
||||||
|
<Setter Property="Fill" Value="{DynamicResource Brush.FG2}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button:pointerover Path.change_mode_switcher_icon">
|
||||||
|
<Setter Property="Fill" Value="{DynamicResource Brush.Accent}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.max_or_restore_btn Path">
|
||||||
|
<Setter Property="Data" Value="{StaticResource Icons.Window.Maximize}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Window[WindowState=Maximized] Button.max_or_restore_btn Path">
|
||||||
|
<Setter Property="Data" Value="{StaticResource Icons.Window.Restore}"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
<Style Selector="TextBlock">
|
<Style Selector="TextBlock">
|
||||||
<Setter Property="HorizontalAlignment" Value="Left"/>
|
<Setter Property="HorizontalAlignment" Value="Left"/>
|
||||||
|
@ -72,7 +84,7 @@
|
||||||
<Style Selector="TextBlock.italic">
|
<Style Selector="TextBlock.italic">
|
||||||
<Setter Property="FontStyle" Value="Italic"/>
|
<Setter Property="FontStyle" Value="Italic"/>
|
||||||
</Style>
|
</Style>
|
||||||
<Style Selector="TextBlock.monospace">
|
<Style Selector="TextBlock.monospace, SelectableTextBlock.monospace">
|
||||||
<Setter Property="FontFamily" Value="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}"/>
|
<Setter Property="FontFamily" Value="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}"/>
|
||||||
</Style>
|
</Style>
|
||||||
<Style Selector="TextBlock.group_header_label">
|
<Style Selector="TextBlock.group_header_label">
|
||||||
|
@ -86,6 +98,11 @@
|
||||||
<Setter Property="HorizontalAlignment" Value="Right"/>
|
<Setter Property="HorizontalAlignment" Value="Right"/>
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="SelectableTextBlock">
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Left"/>
|
||||||
|
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource Brush.FG1}"/>
|
||||||
|
</Style>
|
||||||
<Style Selector="SelectableTextBlock[IsEnabled=True]">
|
<Style Selector="SelectableTextBlock[IsEnabled=True]">
|
||||||
<Setter Property="ContextFlyout">
|
<Setter Property="ContextFlyout">
|
||||||
<Setter.Value>
|
<Setter.Value>
|
||||||
|
@ -111,6 +128,7 @@
|
||||||
<Setter Property="BorderThickness" Value="1"/>
|
<Setter Property="BorderThickness" Value="1"/>
|
||||||
<Setter Property="BorderBrush" Value="{DynamicResource Brush.Border1}"/>
|
<Setter Property="BorderBrush" Value="{DynamicResource Brush.Border1}"/>
|
||||||
<Setter Property="Background" Value="{DynamicResource Brush.Contents}"/>
|
<Setter Property="Background" Value="{DynamicResource Brush.Contents}"/>
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource Brush.FG1}"/>
|
||||||
<Setter Property="ContextFlyout">
|
<Setter Property="ContextFlyout">
|
||||||
<Setter.Value>
|
<Setter.Value>
|
||||||
<MenuFlyout>
|
<MenuFlyout>
|
||||||
|
@ -344,7 +362,7 @@
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Width="12"
|
Width="12"
|
||||||
Fill="{Binding $parent.Foreground}"/>
|
Fill="{DynamicResource Brush.FG1}"/>
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
<ToggleButton Theme="{StaticResource SearchPanelTextBoxButton}"
|
<ToggleButton Theme="{StaticResource SearchPanelTextBoxButton}"
|
||||||
IsChecked="{Binding $parent[aes:SearchPanel].WholeWords, Mode=TwoWay}"
|
IsChecked="{Binding $parent[aes:SearchPanel].WholeWords, Mode=TwoWay}"
|
||||||
|
@ -356,7 +374,7 @@
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Width="14"
|
Width="14"
|
||||||
Fill="{Binding $parent.Foreground}"/>
|
Fill="{DynamicResource Brush.FG1}"/>
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
<ToggleButton Theme="{StaticResource SearchPanelTextBoxButton}"
|
<ToggleButton Theme="{StaticResource SearchPanelTextBoxButton}"
|
||||||
IsChecked="{Binding $parent[aes:SearchPanel].UseRegex, Mode=TwoWay}"
|
IsChecked="{Binding $parent[aes:SearchPanel].UseRegex, Mode=TwoWay}"
|
||||||
|
@ -368,7 +386,7 @@
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Width="12"
|
Width="12"
|
||||||
Fill="{Binding $parent.Foreground}"/>
|
Fill="{DynamicResource Brush.FG1}"/>
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</TextBox.InnerRightContent>
|
</TextBox.InnerRightContent>
|
||||||
|
@ -1075,4 +1093,62 @@
|
||||||
<Style Selector="TreeViewItem[IsExpanded=False] Path.folder_icon">
|
<Style Selector="TreeViewItem[IsExpanded=False] Path.folder_icon">
|
||||||
<Setter Property="Data" Value="{StaticResource Icons.Folder.Fill}"/>
|
<Setter Property="Data" Value="{StaticResource Icons.Folder.Fill}"/>
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="TreeDataGrid">
|
||||||
|
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<Border x:Name="RootBorder"
|
||||||
|
Background="{TemplateBinding Background}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}">
|
||||||
|
<DockPanel>
|
||||||
|
<ScrollViewer Name="PART_HeaderScrollViewer"
|
||||||
|
DockPanel.Dock="Top"
|
||||||
|
IsVisible="{TemplateBinding ShowColumnHeaders}"
|
||||||
|
HorizontalScrollBarVisibility="Hidden"
|
||||||
|
VerticalScrollBarVisibility="Disabled"
|
||||||
|
BringIntoViewOnFocusChange="{TemplateBinding (ScrollViewer.BringIntoViewOnFocusChange)}">
|
||||||
|
<Border x:Name="ColumnHeadersPresenterBorder">
|
||||||
|
<TreeDataGridColumnHeadersPresenter Name="PART_ColumnHeadersPresenter"
|
||||||
|
ElementFactory="{TemplateBinding ElementFactory}"
|
||||||
|
Items="{TemplateBinding Columns}" />
|
||||||
|
</Border>
|
||||||
|
</ScrollViewer>
|
||||||
|
<ScrollViewer Name="PART_ScrollViewer"
|
||||||
|
HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
|
||||||
|
BringIntoViewOnFocusChange="{TemplateBinding (ScrollViewer.BringIntoViewOnFocusChange)}">
|
||||||
|
<TreeDataGridRowsPresenter Name="PART_RowsPresenter"
|
||||||
|
Columns="{TemplateBinding Columns}"
|
||||||
|
ElementFactory="{TemplateBinding ElementFactory}"
|
||||||
|
Items="{TemplateBinding Rows}" />
|
||||||
|
</ScrollViewer>
|
||||||
|
</DockPanel>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
|
||||||
|
<Style Selector="^/template/ Border#ColumnHeadersPresenterBorder">
|
||||||
|
<Setter Property="BorderThickness" Value="0 0 0 1" />
|
||||||
|
<Setter Property="BorderBrush" Value="{DynamicResource TreeDataGridGridLinesBrush}" />
|
||||||
|
</Style>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="TreeDataGridRow">
|
||||||
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="TreeDataGridColumnHeader">
|
||||||
|
<Setter Property="HorizontalContentAlignment" Value="Center"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="TreeDataGridTextCell TextBlock">
|
||||||
|
<Setter Property="FontFamily" Value="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="TreeDataGridExpanderCell[IsExpanded=True] Path.folder_icon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource Icons.Folder.Open}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="TreeDataGridExpanderCell[IsExpanded=False] Path.folder_icon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource Icons.Folder.Fill}"/>
|
||||||
|
</Style>
|
||||||
</Styles>
|
</Styles>
|
||||||
|
|
|
@ -48,7 +48,7 @@
|
||||||
<Color x:Key="Color.FlatButton.BackgroundHovered">#FF333333</Color>
|
<Color x:Key="Color.FlatButton.BackgroundHovered">#FF333333</Color>
|
||||||
<Color x:Key="Color.FlatButton.PrimaryBackground">#FF3A3A3A</Color>
|
<Color x:Key="Color.FlatButton.PrimaryBackground">#FF3A3A3A</Color>
|
||||||
<Color x:Key="Color.FlatButton.PrimaryBackgroundHovered">#FF404040</Color>
|
<Color x:Key="Color.FlatButton.PrimaryBackgroundHovered">#FF404040</Color>
|
||||||
<Color x:Key="Color.FG1">#FFF1F1F1</Color>
|
<Color x:Key="Color.FG1">#FFDDDDDD</Color>
|
||||||
<Color x:Key="Color.FG2">#40F1F1F1</Color>
|
<Color x:Key="Color.FG2">#40F1F1F1</Color>
|
||||||
<Color x:Key="Color.FG3">#FF252525</Color>
|
<Color x:Key="Color.FG3">#FF252525</Color>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
<PackageReference Include="Avalonia.Desktop" Version="11.0.10" />
|
<PackageReference Include="Avalonia.Desktop" Version="11.0.10" />
|
||||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.10" />
|
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.10" />
|
||||||
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.0.10" />
|
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.0.10" />
|
||||||
|
<PackageReference Include="Avalonia.Controls.TreeDataGrid" Version="11.0.10" />
|
||||||
<PackageReference Include="Avalonia.AvaloniaEdit" Version="11.0.6" />
|
<PackageReference Include="Avalonia.AvaloniaEdit" Version="11.0.6" />
|
||||||
<PackageReference Include="Avalonia.Diagnostics" Version="11.0.10" Condition="'$(Configuration)' == 'Debug'" />
|
<PackageReference Include="Avalonia.Diagnostics" Version="11.0.10" Condition="'$(Configuration)' == 'Debug'" />
|
||||||
<PackageReference Include="AvaloniaEdit.TextMate" Version="11.0.6" />
|
<PackageReference Include="AvaloniaEdit.TextMate" Version="11.0.6" />
|
||||||
|
|
|
@ -24,9 +24,9 @@ namespace SourceGit.ViewModels
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Remove(object param)
|
public void Remove(string file)
|
||||||
{
|
{
|
||||||
if (param is string file)
|
if (!string.IsNullOrEmpty(file))
|
||||||
{
|
{
|
||||||
new Commands.AssumeUnchanged(_repo).Remove(file);
|
new Commands.AssumeUnchanged(_repo).Remove(file);
|
||||||
Files.Remove(file);
|
Files.Remove(file);
|
||||||
|
|
|
@ -10,10 +10,10 @@ namespace SourceGit.ViewModels
|
||||||
private set;
|
private set;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AutoStash
|
public Models.DealWithLocalChanges PreAction
|
||||||
{
|
{
|
||||||
get => _autoStash;
|
get => _preAction;
|
||||||
set => SetProperty(ref _autoStash, value);
|
set => SetProperty(ref _preAction, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Checkout(Repository repo, string branch)
|
public Checkout(Repository repo, string branch)
|
||||||
|
@ -34,7 +34,7 @@ namespace SourceGit.ViewModels
|
||||||
var needPopStash = false;
|
var needPopStash = false;
|
||||||
if (hasLocalChanges)
|
if (hasLocalChanges)
|
||||||
{
|
{
|
||||||
if (AutoStash)
|
if (_preAction == Models.DealWithLocalChanges.StashAndReaply)
|
||||||
{
|
{
|
||||||
SetProgressDescription("Adding untracked changes ...");
|
SetProgressDescription("Adding untracked changes ...");
|
||||||
var succ = new Commands.Add(_repo.FullPath).Exec();
|
var succ = new Commands.Add(_repo.FullPath).Exec();
|
||||||
|
@ -52,7 +52,7 @@ namespace SourceGit.ViewModels
|
||||||
|
|
||||||
needPopStash = true;
|
needPopStash = true;
|
||||||
}
|
}
|
||||||
else
|
else if (_preAction == Models.DealWithLocalChanges.Discard)
|
||||||
{
|
{
|
||||||
SetProgressDescription("Discard local changes ...");
|
SetProgressDescription("Discard local changes ...");
|
||||||
Commands.Discard.All(_repo.FullPath);
|
Commands.Discard.All(_repo.FullPath);
|
||||||
|
@ -78,6 +78,6 @@ namespace SourceGit.ViewModels
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly Repository _repo = null;
|
private readonly Repository _repo = null;
|
||||||
private bool _autoStash = true;
|
private Models.DealWithLocalChanges _preAction = Models.DealWithLocalChanges.StashAndReaply;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,10 +50,10 @@ namespace SourceGit.ViewModels
|
||||||
set => SetProperty(ref _extraArgs, value);
|
set => SetProperty(ref _extraArgs, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Clone(Launcher launcher, LauncherPage page)
|
public Clone(Launcher launcher)
|
||||||
{
|
{
|
||||||
_launcher = launcher;
|
_launcher = launcher;
|
||||||
_page = page;
|
_page = launcher.ActivePage;
|
||||||
|
|
||||||
View = new Views.Clone() { DataContext = this };
|
View = new Views.Clone() { DataContext = this };
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Models.TreeDataGrid;
|
||||||
using Avalonia.Media.Imaging;
|
using Avalonia.Media.Imaging;
|
||||||
using Avalonia.Platform.Storage;
|
using Avalonia.Platform.Storage;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
|
@ -48,48 +49,17 @@ namespace SourceGit.ViewModels
|
||||||
set => SetProperty(ref _visibleChanges, value);
|
set => SetProperty(ref _visibleChanges, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<FileTreeNode> ChangeTree
|
public List<Models.Change> SelectedChanges
|
||||||
{
|
{
|
||||||
get => _changeTree;
|
get => _selectedChanges;
|
||||||
set => SetProperty(ref _changeTree, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Models.Change SelectedChange
|
|
||||||
{
|
|
||||||
get => _selectedChange;
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (SetProperty(ref _selectedChange, value))
|
if (SetProperty(ref _selectedChanges, value))
|
||||||
{
|
{
|
||||||
if (value == null)
|
if (value == null || value.Count != 1)
|
||||||
{
|
|
||||||
SelectedChangeNode = null;
|
|
||||||
DiffContext = null;
|
DiffContext = null;
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
DiffContext = new DiffContext(_repo, new Models.DiffOption(_commit, value[0]), _diffContext);
|
||||||
SelectedChangeNode = FileTreeNode.SelectByPath(_changeTree, value.Path);
|
|
||||||
DiffContext = new DiffContext(_repo, new Models.DiffOption(_commit, value), _diffContext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FileTreeNode SelectedChangeNode
|
|
||||||
{
|
|
||||||
get => _selectedChangeNode;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (SetProperty(ref _selectedChangeNode, value))
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
SelectedChange = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SelectedChange = value.Backend as Models.Change;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,26 +76,10 @@ namespace SourceGit.ViewModels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<FileTreeNode> RevisionFilesTree
|
public HierarchicalTreeDataGridSource<Models.FileTreeNode> RevisionFiles
|
||||||
{
|
{
|
||||||
get => _revisionFilesTree;
|
get => _revisionFiles;
|
||||||
set => SetProperty(ref _revisionFilesTree, value);
|
private set => SetProperty(ref _revisionFiles, value);
|
||||||
}
|
|
||||||
|
|
||||||
public FileTreeNode SelectedRevisionFileNode
|
|
||||||
{
|
|
||||||
get => _selectedRevisionFileNode;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (SetProperty(ref _selectedRevisionFileNode, value) && value != null && !value.IsFolder)
|
|
||||||
{
|
|
||||||
RefreshViewRevisionFile(value.Backend as Models.Object);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ViewRevisionFileContent = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string SearchFileFilter
|
public string SearchFileFilter
|
||||||
|
@ -159,17 +113,14 @@ namespace SourceGit.ViewModels
|
||||||
_changes.Clear();
|
_changes.Clear();
|
||||||
if (_visibleChanges != null)
|
if (_visibleChanges != null)
|
||||||
_visibleChanges.Clear();
|
_visibleChanges.Clear();
|
||||||
if (_changeTree != null)
|
if (_selectedChanges != null)
|
||||||
_changeTree.Clear();
|
_selectedChanges.Clear();
|
||||||
_selectedChange = null;
|
|
||||||
_selectedChangeNode = null;
|
|
||||||
_searchChangeFilter = null;
|
_searchChangeFilter = null;
|
||||||
_diffContext = null;
|
_diffContext = null;
|
||||||
|
if (_revisionFilesBackup != null)
|
||||||
|
_revisionFilesBackup.Clear();
|
||||||
if (_revisionFiles != null)
|
if (_revisionFiles != null)
|
||||||
_revisionFiles.Clear();
|
_revisionFiles.Dispose();
|
||||||
if (_revisionFilesTree != null)
|
|
||||||
_revisionFilesTree.Clear();
|
|
||||||
_selectedRevisionFileNode = null;
|
|
||||||
_searchFileFilter = null;
|
_searchFileFilter = null;
|
||||||
_viewRevisionFileContent = null;
|
_viewRevisionFileContent = null;
|
||||||
_cancelToken = null;
|
_cancelToken = null;
|
||||||
|
@ -268,6 +219,16 @@ namespace SourceGit.ViewModels
|
||||||
};
|
};
|
||||||
menu.Items.Add(copyPath);
|
menu.Items.Add(copyPath);
|
||||||
|
|
||||||
|
var copyFileName = new MenuItem();
|
||||||
|
copyFileName.Header = App.Text("CopyFileName");
|
||||||
|
copyFileName.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||||
|
copyFileName.Click += (_, e) =>
|
||||||
|
{
|
||||||
|
App.CopyText(Path.GetFileName(change.Path));
|
||||||
|
e.Handled = true;
|
||||||
|
};
|
||||||
|
menu.Items.Add(copyFileName);
|
||||||
|
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,12 +294,22 @@ namespace SourceGit.ViewModels
|
||||||
ev.Handled = true;
|
ev.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var copyFileName = new MenuItem();
|
||||||
|
copyFileName.Header = App.Text("CopyFileName");
|
||||||
|
copyFileName.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||||
|
copyFileName.Click += (_, e) =>
|
||||||
|
{
|
||||||
|
App.CopyText(Path.GetFileName(file.Path));
|
||||||
|
e.Handled = true;
|
||||||
|
};
|
||||||
|
|
||||||
var menu = new ContextMenu();
|
var menu = new ContextMenu();
|
||||||
menu.Items.Add(history);
|
menu.Items.Add(history);
|
||||||
menu.Items.Add(blame);
|
menu.Items.Add(blame);
|
||||||
menu.Items.Add(explore);
|
menu.Items.Add(explore);
|
||||||
menu.Items.Add(saveAs);
|
menu.Items.Add(saveAs);
|
||||||
menu.Items.Add(copyPath);
|
menu.Items.Add(copyPath);
|
||||||
|
menu.Items.Add(copyFileName);
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,9 +317,14 @@ namespace SourceGit.ViewModels
|
||||||
{
|
{
|
||||||
_changes = null;
|
_changes = null;
|
||||||
VisibleChanges = null;
|
VisibleChanges = null;
|
||||||
SelectedChange = null;
|
SelectedChanges = null;
|
||||||
RevisionFilesTree = null;
|
|
||||||
SelectedRevisionFileNode = null;
|
if (_revisionFiles != null)
|
||||||
|
{
|
||||||
|
_revisionFiles.Dispose();
|
||||||
|
_revisionFiles = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (_commit == null)
|
if (_commit == null)
|
||||||
return;
|
return;
|
||||||
if (_cancelToken != null)
|
if (_cancelToken != null)
|
||||||
|
@ -379,40 +355,33 @@ namespace SourceGit.ViewModels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var tree = FileTreeNode.Build(visible);
|
|
||||||
Dispatcher.UIThread.Invoke(() =>
|
Dispatcher.UIThread.Invoke(() =>
|
||||||
{
|
{
|
||||||
Changes = changes;
|
Changes = changes;
|
||||||
VisibleChanges = visible;
|
VisibleChanges = visible;
|
||||||
ChangeTree = tree;
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
{
|
{
|
||||||
var files = cmdRevisionFiles.Result();
|
_revisionFilesBackup = cmdRevisionFiles.Result();
|
||||||
if (cmdRevisionFiles.Cancel.Requested)
|
if (cmdRevisionFiles.Cancel.Requested)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var visible = files;
|
var visible = _revisionFilesBackup;
|
||||||
if (!string.IsNullOrWhiteSpace(_searchFileFilter))
|
var isSearching = !string.IsNullOrWhiteSpace(_searchFileFilter);
|
||||||
|
if (isSearching)
|
||||||
{
|
{
|
||||||
visible = new List<Models.Object>();
|
visible = new List<Models.Object>();
|
||||||
foreach (var f in files)
|
foreach (var f in _revisionFilesBackup)
|
||||||
{
|
{
|
||||||
if (f.Path.Contains(_searchFileFilter, StringComparison.OrdinalIgnoreCase))
|
if (f.Path.Contains(_searchFileFilter, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
|
||||||
visible.Add(f);
|
visible.Add(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var tree = FileTreeNode.Build(visible);
|
var tree = Models.FileTreeNode.Build(visible, isSearching || visible.Count <= 100);
|
||||||
Dispatcher.UIThread.Invoke(() =>
|
Dispatcher.UIThread.Invoke(() => BuildRevisionFilesSource(tree));
|
||||||
{
|
|
||||||
_revisionFiles = files;
|
|
||||||
RevisionFilesTree = tree;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -431,15 +400,11 @@ namespace SourceGit.ViewModels
|
||||||
foreach (var c in _changes)
|
foreach (var c in _changes)
|
||||||
{
|
{
|
||||||
if (c.Path.Contains(_searchChangeFilter, StringComparison.OrdinalIgnoreCase))
|
if (c.Path.Contains(_searchChangeFilter, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
|
||||||
visible.Add(c);
|
visible.Add(c);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
VisibleChanges = visible;
|
VisibleChanges = visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
ChangeTree = FileTreeNode.Build(_visibleChanges);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RefreshVisibleFiles()
|
private void RefreshVisibleFiles()
|
||||||
|
@ -447,24 +412,29 @@ namespace SourceGit.ViewModels
|
||||||
if (_revisionFiles == null)
|
if (_revisionFiles == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var visible = _revisionFiles;
|
var visible = _revisionFilesBackup;
|
||||||
if (!string.IsNullOrWhiteSpace(_searchFileFilter))
|
var isSearching = !string.IsNullOrWhiteSpace(_searchFileFilter);
|
||||||
|
if (isSearching)
|
||||||
{
|
{
|
||||||
visible = new List<Models.Object>();
|
visible = new List<Models.Object>();
|
||||||
foreach (var f in _revisionFiles)
|
foreach (var f in _revisionFilesBackup)
|
||||||
{
|
{
|
||||||
if (f.Path.Contains(_searchFileFilter, StringComparison.OrdinalIgnoreCase))
|
if (f.Path.Contains(_searchFileFilter, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
|
||||||
visible.Add(f);
|
visible.Add(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
RevisionFilesTree = FileTreeNode.Build(visible);
|
BuildRevisionFilesSource(Models.FileTreeNode.Build(visible, isSearching || visible.Count < 100));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RefreshViewRevisionFile(Models.Object file)
|
private void RefreshViewRevisionFile(Models.Object file)
|
||||||
{
|
{
|
||||||
|
if (file == null)
|
||||||
|
{
|
||||||
|
ViewRevisionFileContent = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switch (file.Type)
|
switch (file.Type)
|
||||||
{
|
{
|
||||||
case Models.ObjectType.Blob:
|
case Models.ObjectType.Blob:
|
||||||
|
@ -541,6 +511,36 @@ namespace SourceGit.ViewModels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void BuildRevisionFilesSource(List<Models.FileTreeNode> tree)
|
||||||
|
{
|
||||||
|
var source = new HierarchicalTreeDataGridSource<Models.FileTreeNode>(tree)
|
||||||
|
{
|
||||||
|
Columns =
|
||||||
|
{
|
||||||
|
new HierarchicalExpanderColumn<Models.FileTreeNode>(
|
||||||
|
new TemplateColumn<Models.FileTreeNode>("Icon", "FileTreeNodeExpanderTemplate", null, GridLength.Auto),
|
||||||
|
x => x.Children,
|
||||||
|
x => x.Children.Count > 0,
|
||||||
|
x => x.IsExpanded),
|
||||||
|
new TextColumn<Models.FileTreeNode, string>(
|
||||||
|
null,
|
||||||
|
x => string.Empty,
|
||||||
|
GridLength.Star)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var selection = new Models.TreeDataGridSelectionModel<Models.FileTreeNode>(source, x => x.Children);
|
||||||
|
selection.SingleSelect = true;
|
||||||
|
selection.SelectionChanged += (s, _) =>
|
||||||
|
{
|
||||||
|
if (s is Models.TreeDataGridSelectionModel<Models.FileTreeNode> selection)
|
||||||
|
RefreshViewRevisionFile(selection.SelectedItem?.Backend as Models.Object);
|
||||||
|
};
|
||||||
|
|
||||||
|
source.Selection = selection;
|
||||||
|
RevisionFiles = source;
|
||||||
|
}
|
||||||
|
|
||||||
private static readonly HashSet<string> IMG_EXTS = new HashSet<string>()
|
private static readonly HashSet<string> IMG_EXTS = new HashSet<string>()
|
||||||
{
|
{
|
||||||
".ico", ".bmp", ".jpg", ".png", ".jpeg"
|
".ico", ".bmp", ".jpg", ".png", ".jpeg"
|
||||||
|
@ -551,14 +551,11 @@ namespace SourceGit.ViewModels
|
||||||
private Models.Commit _commit = null;
|
private Models.Commit _commit = null;
|
||||||
private List<Models.Change> _changes = null;
|
private List<Models.Change> _changes = null;
|
||||||
private List<Models.Change> _visibleChanges = null;
|
private List<Models.Change> _visibleChanges = null;
|
||||||
private List<FileTreeNode> _changeTree = null;
|
private List<Models.Change> _selectedChanges = null;
|
||||||
private Models.Change _selectedChange = null;
|
|
||||||
private FileTreeNode _selectedChangeNode = null;
|
|
||||||
private string _searchChangeFilter = string.Empty;
|
private string _searchChangeFilter = string.Empty;
|
||||||
private DiffContext _diffContext = null;
|
private DiffContext _diffContext = null;
|
||||||
private List<Models.Object> _revisionFiles = null;
|
private List<Models.Object> _revisionFilesBackup = null;
|
||||||
private List<FileTreeNode> _revisionFilesTree = null;
|
private HierarchicalTreeDataGridSource<Models.FileTreeNode> _revisionFiles = null;
|
||||||
private FileTreeNode _selectedRevisionFileNode = null;
|
|
||||||
private string _searchFileFilter = string.Empty;
|
private string _searchFileFilter = string.Empty;
|
||||||
private object _viewRevisionFileContent = null;
|
private object _viewRevisionFileContent = null;
|
||||||
private Commands.Command.CancelToken _cancelToken = null;
|
private Commands.Command.CancelToken _cancelToken = null;
|
||||||
|
|
|
@ -3,13 +3,6 @@ using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace SourceGit.ViewModels
|
namespace SourceGit.ViewModels
|
||||||
{
|
{
|
||||||
public enum BeforeCreateBranchAction
|
|
||||||
{
|
|
||||||
StashAndReaply,
|
|
||||||
Discard,
|
|
||||||
DoNothing,
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CreateBranch : Popup
|
public class CreateBranch : Popup
|
||||||
{
|
{
|
||||||
[Required(ErrorMessage = "Branch name is required!")]
|
[Required(ErrorMessage = "Branch name is required!")]
|
||||||
|
@ -27,7 +20,7 @@ namespace SourceGit.ViewModels
|
||||||
private set;
|
private set;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BeforeCreateBranchAction PreAction
|
public Models.DealWithLocalChanges PreAction
|
||||||
{
|
{
|
||||||
get => _preAction;
|
get => _preAction;
|
||||||
set => SetProperty(ref _preAction, value);
|
set => SetProperty(ref _preAction, value);
|
||||||
|
@ -97,7 +90,7 @@ namespace SourceGit.ViewModels
|
||||||
bool needPopStash = false;
|
bool needPopStash = false;
|
||||||
if (_repo.WorkingCopyChangesCount > 0)
|
if (_repo.WorkingCopyChangesCount > 0)
|
||||||
{
|
{
|
||||||
if (_preAction == BeforeCreateBranchAction.StashAndReaply)
|
if (_preAction == Models.DealWithLocalChanges.StashAndReaply)
|
||||||
{
|
{
|
||||||
SetProgressDescription("Adding untracked changes...");
|
SetProgressDescription("Adding untracked changes...");
|
||||||
var succ = new Commands.Add(_repo.FullPath).Exec();
|
var succ = new Commands.Add(_repo.FullPath).Exec();
|
||||||
|
@ -115,7 +108,7 @@ namespace SourceGit.ViewModels
|
||||||
|
|
||||||
needPopStash = true;
|
needPopStash = true;
|
||||||
}
|
}
|
||||||
else if (_preAction == BeforeCreateBranchAction.Discard)
|
else if (_preAction == Models.DealWithLocalChanges.Discard)
|
||||||
{
|
{
|
||||||
SetProgressDescription("Discard local changes...");
|
SetProgressDescription("Discard local changes...");
|
||||||
Commands.Discard.All(_repo.FullPath);
|
Commands.Discard.All(_repo.FullPath);
|
||||||
|
@ -133,6 +126,7 @@ namespace SourceGit.ViewModels
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
SetProgressDescription($"Create new branch '{_name}'");
|
||||||
Commands.Branch.Create(_repo.FullPath, _name, _baseOnRevision);
|
Commands.Branch.Create(_repo.FullPath, _name, _baseOnRevision);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,6 +138,6 @@ namespace SourceGit.ViewModels
|
||||||
private readonly Repository _repo = null;
|
private readonly Repository _repo = null;
|
||||||
private string _name = null;
|
private string _name = null;
|
||||||
private readonly string _baseOnRevision = null;
|
private readonly string _baseOnRevision = null;
|
||||||
private BeforeCreateBranchAction _preAction = BeforeCreateBranchAction.StashAndReaply;
|
private Models.DealWithLocalChanges _preAction = Models.DealWithLocalChanges.StashAndReaply;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,6 +85,11 @@ namespace SourceGit.ViewModels
|
||||||
_content = previous._content;
|
_content = previous._content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(_option.OrgPath) || _option.OrgPath == "/dev/null")
|
||||||
|
_title = _option.Path;
|
||||||
|
else
|
||||||
|
_title = $"{_option.OrgPath} → {_option.Path}";
|
||||||
|
|
||||||
LoadDiffContent();
|
LoadDiffContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,10 +127,33 @@ namespace SourceGit.ViewModels
|
||||||
var rs = null as object;
|
var rs = null as object;
|
||||||
|
|
||||||
if (latest.TextDiff != null)
|
if (latest.TextDiff != null)
|
||||||
|
{
|
||||||
|
var repo = Preference.FindRepository(_repo);
|
||||||
|
if (repo != null && repo.Submodules.Contains(_option.Path))
|
||||||
|
{
|
||||||
|
var submoduleDiff = new Models.SubmoduleDiff();
|
||||||
|
var submoduleRoot = $"{_repo}/{_option.Path}".Replace("\\", "/");
|
||||||
|
foreach (var line in latest.TextDiff.Lines)
|
||||||
|
{
|
||||||
|
if (line.Type == Models.TextDiffLineType.Added)
|
||||||
|
{
|
||||||
|
var sha = line.Content.Substring("Subproject commit ".Length);
|
||||||
|
submoduleDiff.New = new Commands.QuerySingleCommit(submoduleRoot, sha).Result();
|
||||||
|
}
|
||||||
|
else if (line.Type == Models.TextDiffLineType.Deleted)
|
||||||
|
{
|
||||||
|
var sha = line.Content.Substring("Subproject commit ".Length);
|
||||||
|
submoduleDiff.Old = new Commands.QuerySingleCommit(submoduleRoot, sha).Result();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rs = submoduleDiff;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
latest.TextDiff.File = _option.Path;
|
latest.TextDiff.File = _option.Path;
|
||||||
rs = latest.TextDiff;
|
rs = latest.TextDiff;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (latest.IsBinary)
|
else if (latest.IsBinary)
|
||||||
{
|
{
|
||||||
var oldPath = string.IsNullOrEmpty(_option.OrgPath) ? _option.Path : _option.OrgPath;
|
var oldPath = string.IsNullOrEmpty(_option.OrgPath) ? _option.Path : _option.OrgPath;
|
||||||
|
@ -175,14 +203,9 @@ namespace SourceGit.ViewModels
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(() =>
|
Dispatcher.UIThread.Post(() =>
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(_option.OrgPath) || _option.OrgPath == "/dev/null")
|
|
||||||
Title = _option.Path;
|
|
||||||
else
|
|
||||||
Title = $"{_option.OrgPath} → {_option.Path}";
|
|
||||||
|
|
||||||
FileModeChange = latest.FileModeChange;
|
FileModeChange = latest.FileModeChange;
|
||||||
Content = rs;
|
Content = rs;
|
||||||
IsTextDiff = latest.TextDiff != null;
|
IsTextDiff = rs is Models.TextDiff;
|
||||||
IsLoading = false;
|
IsLoading = false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -42,6 +42,7 @@ namespace SourceGit.ViewModels
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SetProgressDescription($"Git Flow - finishing {_branch.Name} ...");
|
||||||
var succ = new Commands.GitFlow(_repo.FullPath).Finish(_type, branch, KeepBranch);
|
var succ = new Commands.GitFlow(_repo.FullPath).Finish(_type, branch, KeepBranch);
|
||||||
CallUIThread(() => _repo.SetWatcherEnabled(true));
|
CallUIThread(() => _repo.SetWatcherEnabled(true));
|
||||||
return succ;
|
return succ;
|
||||||
|
|
|
@ -65,6 +65,7 @@ namespace SourceGit.ViewModels
|
||||||
_repo.SetWatcherEnabled(false);
|
_repo.SetWatcherEnabled(false);
|
||||||
return Task.Run(() =>
|
return Task.Run(() =>
|
||||||
{
|
{
|
||||||
|
SetProgressDescription($"Git Flow - starting {_prefix}{_name} ...");
|
||||||
var succ = new Commands.GitFlow(_repo.FullPath).Start(_type, _name);
|
var succ = new Commands.GitFlow(_repo.FullPath).Start(_type, _name);
|
||||||
CallUIThread(() => _repo.SetWatcherEnabled(true));
|
CallUIThread(() => _repo.SetWatcherEnabled(true));
|
||||||
return succ;
|
return succ;
|
||||||
|
|
|
@ -69,7 +69,7 @@ namespace SourceGit.ViewModels
|
||||||
public Models.Commit AutoSelectedCommit
|
public Models.Commit AutoSelectedCommit
|
||||||
{
|
{
|
||||||
get => _autoSelectedCommit;
|
get => _autoSelectedCommit;
|
||||||
private set => SetProperty(ref _autoSelectedCommit, value);
|
set => SetProperty(ref _autoSelectedCommit, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long NavigationId
|
public long NavigationId
|
||||||
|
@ -81,7 +81,7 @@ namespace SourceGit.ViewModels
|
||||||
public object DetailContext
|
public object DetailContext
|
||||||
{
|
{
|
||||||
get => _detailContext;
|
get => _detailContext;
|
||||||
private set => SetProperty(ref _detailContext, value);
|
set => SetProperty(ref _detailContext, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Histories(Repository repo)
|
public Histories(Repository repo)
|
||||||
|
@ -171,17 +171,16 @@ namespace SourceGit.ViewModels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ContextMenu MakeContextMenu()
|
public ContextMenu MakeContextMenu(DataGrid datagrid)
|
||||||
{
|
{
|
||||||
var detail = _detailContext as CommitDetail;
|
if (datagrid.SelectedItems.Count != 1)
|
||||||
if (detail == null)
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var current = _repo.Branches.Find(x => x.IsCurrent);
|
var current = _repo.Branches.Find(x => x.IsCurrent);
|
||||||
if (current == null)
|
if (current == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var commit = detail.Commit;
|
var commit = datagrid.SelectedItem as Models.Commit;
|
||||||
var menu = new ContextMenu();
|
var menu = new ContextMenu();
|
||||||
var tags = new List<Models.Tag>();
|
var tags = new List<Models.Tag>();
|
||||||
|
|
||||||
|
@ -317,6 +316,46 @@ namespace SourceGit.ViewModels
|
||||||
|
|
||||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||||
|
|
||||||
|
if (current.Head != commit.SHA)
|
||||||
|
{
|
||||||
|
var compareWithHead = new MenuItem();
|
||||||
|
compareWithHead.Header = App.Text("CommitCM.CompareWithHead");
|
||||||
|
compareWithHead.Icon = App.CreateMenuIcon("Icons.Compare");
|
||||||
|
compareWithHead.Click += (o, e) =>
|
||||||
|
{
|
||||||
|
var head = _commits.Find(x => x.SHA == current.Head);
|
||||||
|
if (head == null)
|
||||||
|
{
|
||||||
|
_repo.SearchResultSelectedCommit = null;
|
||||||
|
head = new Commands.QuerySingleCommit(_repo.FullPath, current.Head).Result();
|
||||||
|
if (head != null)
|
||||||
|
DetailContext = new RevisionCompare(_repo.FullPath, commit, head);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
datagrid.SelectedItems.Add(head);
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
};
|
||||||
|
menu.Items.Add(compareWithHead);
|
||||||
|
|
||||||
|
if (_repo.WorkingCopyChangesCount > 0)
|
||||||
|
{
|
||||||
|
var compareWithWorktree = new MenuItem();
|
||||||
|
compareWithWorktree.Header = App.Text("CommitCM.CompareWithWorktree");
|
||||||
|
compareWithWorktree.Icon = App.CreateMenuIcon("Icons.Compare");
|
||||||
|
compareWithWorktree.Click += (o, e) =>
|
||||||
|
{
|
||||||
|
DetailContext = new RevisionCompare(_repo.FullPath, commit, null);
|
||||||
|
e.Handled = true;
|
||||||
|
};
|
||||||
|
menu.Items.Add(compareWithWorktree);
|
||||||
|
}
|
||||||
|
|
||||||
|
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||||
|
}
|
||||||
|
|
||||||
var createBranch = new MenuItem();
|
var createBranch = new MenuItem();
|
||||||
createBranch.Icon = App.CreateMenuIcon("Icons.Branch.Add");
|
createBranch.Icon = App.CreateMenuIcon("Icons.Branch.Add");
|
||||||
createBranch.Header = App.Text("CreateBranch");
|
createBranch.Header = App.Text("CreateBranch");
|
||||||
|
@ -474,7 +513,7 @@ namespace SourceGit.ViewModels
|
||||||
checkout.Icon = App.CreateMenuIcon("Icons.Check");
|
checkout.Icon = App.CreateMenuIcon("Icons.Check");
|
||||||
checkout.Click += (o, e) =>
|
checkout.Click += (o, e) =>
|
||||||
{
|
{
|
||||||
_repo.CheckoutLocalBranch(branch.Name);
|
_repo.CheckoutBranch(branch);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
submenu.Items.Add(checkout);
|
submenu.Items.Add(checkout);
|
||||||
|
@ -546,20 +585,7 @@ namespace SourceGit.ViewModels
|
||||||
checkout.Icon = App.CreateMenuIcon("Icons.Check");
|
checkout.Icon = App.CreateMenuIcon("Icons.Check");
|
||||||
checkout.Click += (o, e) =>
|
checkout.Click += (o, e) =>
|
||||||
{
|
{
|
||||||
foreach (var b in _repo.Branches)
|
_repo.CheckoutBranch(branch);
|
||||||
{
|
|
||||||
if (b.IsLocal && b.Upstream == branch.FullName)
|
|
||||||
{
|
|
||||||
if (!b.IsCurrent)
|
|
||||||
_repo.CheckoutLocalBranch(b.Name);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PopupHost.CanCreatePopup())
|
|
||||||
PopupHost.ShowPopup(new CreateBranch(_repo, branch));
|
|
||||||
|
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
submenu.Items.Add(checkout);
|
submenu.Items.Add(checkout);
|
||||||
|
|
|
@ -52,17 +52,13 @@ namespace SourceGit.ViewModels
|
||||||
if (task != null)
|
if (task != null)
|
||||||
{
|
{
|
||||||
var finished = await task;
|
var finished = await task;
|
||||||
|
_popup.InProgress = false;
|
||||||
if (finished)
|
if (finished)
|
||||||
{
|
|
||||||
Popup = null;
|
Popup = null;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_popup.InProgress = false;
|
_popup.InProgress = false;
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Popup = null;
|
Popup = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -459,10 +459,6 @@ namespace SourceGit.ViewModels
|
||||||
|
|
||||||
public static void Save()
|
public static void Save()
|
||||||
{
|
{
|
||||||
var dir = Path.GetDirectoryName(_savePath);
|
|
||||||
if (!Directory.Exists(dir))
|
|
||||||
Directory.CreateDirectory(dir);
|
|
||||||
|
|
||||||
var data = JsonSerializer.Serialize(_instance, JsonCodeGen.Default.Preference);
|
var data = JsonSerializer.Serialize(_instance, JsonCodeGen.Default.Preference);
|
||||||
File.WriteAllText(_savePath, data);
|
File.WriteAllText(_savePath, data);
|
||||||
}
|
}
|
||||||
|
@ -515,10 +511,7 @@ namespace SourceGit.ViewModels
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Preference _instance = null;
|
private static Preference _instance = null;
|
||||||
private static readonly string _savePath = Path.Combine(
|
private static readonly string _savePath = Path.Combine(Native.OS.DataDir, "preference.json");
|
||||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
|
||||||
"SourceGit",
|
|
||||||
"preference.json");
|
|
||||||
|
|
||||||
private string _locale = "en_US";
|
private string _locale = "en_US";
|
||||||
private string _theme = "Default";
|
private string _theme = "Default";
|
||||||
|
|
|
@ -47,13 +47,13 @@ namespace SourceGit.ViewModels
|
||||||
set => SetProperty(ref _selectedBranch, value);
|
set => SetProperty(ref _selectedBranch, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool UseRebase
|
public Models.DealWithLocalChanges PreAction
|
||||||
{
|
{
|
||||||
get;
|
get => _preAction;
|
||||||
set;
|
set => SetProperty(ref _preAction, value);
|
||||||
} = true;
|
}
|
||||||
|
|
||||||
public bool AutoStash
|
public bool UseRebase
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
set;
|
set;
|
||||||
|
@ -117,7 +117,9 @@ namespace SourceGit.ViewModels
|
||||||
return Task.Run(() =>
|
return Task.Run(() =>
|
||||||
{
|
{
|
||||||
var needPopStash = false;
|
var needPopStash = false;
|
||||||
if (AutoStash && _repo.WorkingCopyChangesCount > 0)
|
if (_repo.WorkingCopyChangesCount > 0)
|
||||||
|
{
|
||||||
|
if (_preAction == Models.DealWithLocalChanges.StashAndReaply)
|
||||||
{
|
{
|
||||||
SetProgressDescription("Adding untracked changes...");
|
SetProgressDescription("Adding untracked changes...");
|
||||||
var succ = new Commands.Add(_repo.FullPath).Exec();
|
var succ = new Commands.Add(_repo.FullPath).Exec();
|
||||||
|
@ -135,6 +137,12 @@ namespace SourceGit.ViewModels
|
||||||
|
|
||||||
needPopStash = true;
|
needPopStash = true;
|
||||||
}
|
}
|
||||||
|
else if (_preAction == Models.DealWithLocalChanges.Discard)
|
||||||
|
{
|
||||||
|
SetProgressDescription("Discard local changes ...");
|
||||||
|
Commands.Discard.All(_repo.FullPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SetProgressDescription($"Pull {_selectedRemote.Name}/{_selectedBranch.Name}...");
|
SetProgressDescription($"Pull {_selectedRemote.Name}/{_selectedBranch.Name}...");
|
||||||
var rs = new Commands.Pull(_repo.FullPath, _selectedRemote.Name, _selectedBranch.Name, UseRebase, SetProgressDescription).Exec();
|
var rs = new Commands.Pull(_repo.FullPath, _selectedRemote.Name, _selectedBranch.Name, UseRebase, SetProgressDescription).Exec();
|
||||||
|
@ -154,5 +162,6 @@ namespace SourceGit.ViewModels
|
||||||
private Models.Remote _selectedRemote = null;
|
private Models.Remote _selectedRemote = null;
|
||||||
private List<Models.Branch> _remoteBranches = null;
|
private List<Models.Branch> _remoteBranches = null;
|
||||||
private Models.Branch _selectedBranch = null;
|
private Models.Branch _selectedBranch = null;
|
||||||
|
private Models.DealWithLocalChanges _preAction = Models.DealWithLocalChanges.StashAndReaply;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,13 +158,6 @@ namespace SourceGit.ViewModels
|
||||||
get => _stashesPage == null ? 0 : _stashesPage.Count;
|
get => _stashesPage == null ? 0 : _stashesPage.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public bool CanCommitWithPush
|
|
||||||
{
|
|
||||||
get => _canCommitWithPush;
|
|
||||||
private set => SetProperty(ref _canCommitWithPush, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public bool IncludeUntracked
|
public bool IncludeUntracked
|
||||||
{
|
{
|
||||||
|
@ -172,11 +165,9 @@ namespace SourceGit.ViewModels
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (SetProperty(ref _includeUntracked, value))
|
if (SetProperty(ref _includeUntracked, value))
|
||||||
{
|
|
||||||
Task.Run(RefreshWorkingCopyChanges);
|
Task.Run(RefreshWorkingCopyChanges);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public bool IsSearching
|
public bool IsSearching
|
||||||
|
@ -385,7 +376,11 @@ namespace SourceGit.ViewModels
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Branches.Find(x => x.IsCurrent) == null)
|
if (Branches.Find(x => x.IsCurrent) == null)
|
||||||
|
{
|
||||||
App.RaiseException(_fullpath, "Can NOT found current branch!!!");
|
App.RaiseException(_fullpath, "Can NOT found current branch!!!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
PopupHost.ShowPopup(new Push(this, null));
|
PopupHost.ShowPopup(new Push(this, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -563,8 +558,11 @@ namespace SourceGit.ViewModels
|
||||||
LocalBranchTrees = builder.Locals;
|
LocalBranchTrees = builder.Locals;
|
||||||
RemoteBranchTrees = builder.Remotes;
|
RemoteBranchTrees = builder.Remotes;
|
||||||
|
|
||||||
|
if (_workingCopy != null)
|
||||||
|
{
|
||||||
var cur = Branches.Find(x => x.IsCurrent);
|
var cur = Branches.Find(x => x.IsCurrent);
|
||||||
CanCommitWithPush = cur != null && !string.IsNullOrEmpty(cur.Upstream);
|
_workingCopy.CanCommitWithPush = cur != null && !string.IsNullOrEmpty(cur.Upstream);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -711,15 +709,33 @@ namespace SourceGit.ViewModels
|
||||||
PopupHost.ShowPopup(new CreateBranch(this, current));
|
PopupHost.ShowPopup(new CreateBranch(this, current));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CheckoutLocalBranch(string branch)
|
public void CheckoutBranch(Models.Branch branch)
|
||||||
{
|
{
|
||||||
if (!PopupHost.CanCreatePopup())
|
if (!PopupHost.CanCreatePopup())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (branch.IsLocal)
|
||||||
|
{
|
||||||
if (WorkingCopyChangesCount > 0)
|
if (WorkingCopyChangesCount > 0)
|
||||||
PopupHost.ShowPopup(new Checkout(this, branch));
|
PopupHost.ShowPopup(new Checkout(this, branch.Name));
|
||||||
else
|
else
|
||||||
PopupHost.ShowAndStartPopup(new Checkout(this, branch));
|
PopupHost.ShowAndStartPopup(new Checkout(this, branch.Name));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var b in Branches)
|
||||||
|
{
|
||||||
|
if (b.IsLocal && b.Upstream == branch.FullName)
|
||||||
|
{
|
||||||
|
if (!b.IsCurrent)
|
||||||
|
CheckoutBranch(b);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PopupHost.ShowPopup(new CreateBranch(this, branch));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DeleteMultipleBranches(List<Models.Branch> branches, bool isLocal)
|
public void DeleteMultipleBranches(List<Models.Branch> branches, bool isLocal)
|
||||||
|
@ -880,7 +896,7 @@ namespace SourceGit.ViewModels
|
||||||
checkout.Icon = App.CreateMenuIcon("Icons.Check");
|
checkout.Icon = App.CreateMenuIcon("Icons.Check");
|
||||||
checkout.Click += (o, e) =>
|
checkout.Click += (o, e) =>
|
||||||
{
|
{
|
||||||
CheckoutLocalBranch(branch.Name);
|
CheckoutBranch(branch);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
menu.Items.Add(checkout);
|
menu.Items.Add(checkout);
|
||||||
|
@ -928,6 +944,46 @@ namespace SourceGit.ViewModels
|
||||||
|
|
||||||
menu.Items.Add(merge);
|
menu.Items.Add(merge);
|
||||||
menu.Items.Add(rebase);
|
menu.Items.Add(rebase);
|
||||||
|
|
||||||
|
var compare = new MenuItem();
|
||||||
|
compare.Header = App.Text("BranchCM.CompareWithHead");
|
||||||
|
compare.Icon = App.CreateMenuIcon("Icons.Compare");
|
||||||
|
compare.Click += (o, e) =>
|
||||||
|
{
|
||||||
|
SearchResultSelectedCommit = null;
|
||||||
|
|
||||||
|
if (_histories != null)
|
||||||
|
{
|
||||||
|
var target = new Commands.QuerySingleCommit(FullPath, branch.Head).Result();
|
||||||
|
var head = new Commands.QuerySingleCommit(FullPath, current.Head).Result();
|
||||||
|
_histories.AutoSelectedCommit = null;
|
||||||
|
_histories.DetailContext = new RevisionCompare(FullPath, target, head);
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (WorkingCopyChangesCount > 0)
|
||||||
|
{
|
||||||
|
var compareWithWorktree = new MenuItem();
|
||||||
|
compareWithWorktree.Header = App.Text("BranchCM.CompareWithWorktree");
|
||||||
|
compareWithWorktree.Icon = App.CreateMenuIcon("Icons.Compare");
|
||||||
|
compareWithWorktree.Click += (o, e) =>
|
||||||
|
{
|
||||||
|
SearchResultSelectedCommit = null;
|
||||||
|
|
||||||
|
if (_histories != null)
|
||||||
|
{
|
||||||
|
var target = new Commands.QuerySingleCommit(FullPath, branch.Head).Result();
|
||||||
|
_histories.AutoSelectedCommit = null;
|
||||||
|
_histories.DetailContext = new RevisionCompare(FullPath, target, null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
menu.Items.Add(compareWithWorktree);
|
||||||
|
}
|
||||||
|
|
||||||
|
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||||
|
menu.Items.Add(compare);
|
||||||
}
|
}
|
||||||
|
|
||||||
var type = GitFlow.GetBranchType(branch.Name);
|
var type = GitFlow.GetBranchType(branch.Name);
|
||||||
|
@ -1142,20 +1198,7 @@ namespace SourceGit.ViewModels
|
||||||
checkout.Icon = App.CreateMenuIcon("Icons.Check");
|
checkout.Icon = App.CreateMenuIcon("Icons.Check");
|
||||||
checkout.Click += (o, e) =>
|
checkout.Click += (o, e) =>
|
||||||
{
|
{
|
||||||
foreach (var b in Branches)
|
CheckoutBranch(branch);
|
||||||
{
|
|
||||||
if (b.IsLocal && b.Upstream == branch.FullName)
|
|
||||||
{
|
|
||||||
if (b.IsCurrent)
|
|
||||||
return;
|
|
||||||
if (PopupHost.CanCreatePopup())
|
|
||||||
PopupHost.ShowAndStartPopup(new Checkout(this, b.Name));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PopupHost.CanCreatePopup())
|
|
||||||
PopupHost.ShowPopup(new CreateBranch(this, branch));
|
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
menu.Items.Add(checkout);
|
menu.Items.Add(checkout);
|
||||||
|
@ -1197,6 +1240,49 @@ namespace SourceGit.ViewModels
|
||||||
menu.Items.Add(merge);
|
menu.Items.Add(merge);
|
||||||
menu.Items.Add(rebase);
|
menu.Items.Add(rebase);
|
||||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||||
|
|
||||||
|
if (current.Head != branch.Head)
|
||||||
|
{
|
||||||
|
var compare = new MenuItem();
|
||||||
|
compare.Header = App.Text("BranchCM.CompareWithHead");
|
||||||
|
compare.Icon = App.CreateMenuIcon("Icons.Compare");
|
||||||
|
compare.Click += (o, e) =>
|
||||||
|
{
|
||||||
|
SearchResultSelectedCommit = null;
|
||||||
|
|
||||||
|
if (_histories != null)
|
||||||
|
{
|
||||||
|
var target = new Commands.QuerySingleCommit(FullPath, branch.Head).Result();
|
||||||
|
var head = new Commands.QuerySingleCommit(FullPath, current.Head).Result();
|
||||||
|
_histories.AutoSelectedCommit = null;
|
||||||
|
_histories.DetailContext = new RevisionCompare(FullPath, target, head);
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
};
|
||||||
|
menu.Items.Add(compare);
|
||||||
|
|
||||||
|
if (WorkingCopyChangesCount > 0)
|
||||||
|
{
|
||||||
|
var compareWithWorktree = new MenuItem();
|
||||||
|
compareWithWorktree.Header = App.Text("BranchCM.CompareWithWorktree");
|
||||||
|
compareWithWorktree.Icon = App.CreateMenuIcon("Icons.Compare");
|
||||||
|
compareWithWorktree.Click += (o, e) =>
|
||||||
|
{
|
||||||
|
SearchResultSelectedCommit = null;
|
||||||
|
|
||||||
|
if (_histories != null)
|
||||||
|
{
|
||||||
|
var target = new Commands.QuerySingleCommit(FullPath, branch.Head).Result();
|
||||||
|
_histories.AutoSelectedCommit = null;
|
||||||
|
_histories.DetailContext = new RevisionCompare(FullPath, target, null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
menu.Items.Add(compareWithWorktree);
|
||||||
|
}
|
||||||
|
|
||||||
|
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var delete = new MenuItem();
|
var delete = new MenuItem();
|
||||||
|
@ -1428,7 +1514,6 @@ namespace SourceGit.ViewModels
|
||||||
private List<BranchTreeNode> _remoteBranchTrees = new List<BranchTreeNode>();
|
private List<BranchTreeNode> _remoteBranchTrees = new List<BranchTreeNode>();
|
||||||
private List<Models.Tag> _tags = new List<Models.Tag>();
|
private List<Models.Tag> _tags = new List<Models.Tag>();
|
||||||
private List<string> _submodules = new List<string>();
|
private List<string> _submodules = new List<string>();
|
||||||
private bool _canCommitWithPush = false;
|
|
||||||
private bool _includeUntracked = true;
|
private bool _includeUntracked = true;
|
||||||
|
|
||||||
private InProgressContext _inProgressContext = null;
|
private InProgressContext _inProgressContext = null;
|
||||||
|
|
|
@ -17,7 +17,19 @@ namespace SourceGit.ViewModels
|
||||||
set;
|
set;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool GPGSigningEnabled
|
public Models.GPGFormat GPGFormat
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GPGCommitSigningEnabled
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GPGTagSigningEnabled
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
set;
|
set;
|
||||||
|
@ -44,8 +56,14 @@ namespace SourceGit.ViewModels
|
||||||
UserName = name;
|
UserName = name;
|
||||||
if (_cached.TryGetValue("user.email", out var email))
|
if (_cached.TryGetValue("user.email", out var email))
|
||||||
UserEmail = email;
|
UserEmail = email;
|
||||||
if (_cached.TryGetValue("commit.gpgsign", out var gpgsign))
|
if (_cached.TryGetValue("commit.gpgsign", out var gpgCommitSign))
|
||||||
GPGSigningEnabled = gpgsign == "true";
|
GPGCommitSigningEnabled = gpgCommitSign == "true";
|
||||||
|
if (_cached.TryGetValue("tag.gpgSign", out var gpgTagSign))
|
||||||
|
GPGTagSigningEnabled = gpgTagSign == "true";
|
||||||
|
if (_cached.TryGetValue("gpg.format", out var gpgFormat))
|
||||||
|
GPGFormat = Models.GPGFormat.Supported.Find(x => x.Value == gpgFormat);
|
||||||
|
else
|
||||||
|
GPGFormat = Models.GPGFormat.OPENPGP;
|
||||||
if (_cached.TryGetValue("user.signingkey", out var signingKey))
|
if (_cached.TryGetValue("user.signingkey", out var signingKey))
|
||||||
GPGUserSigningKey = signingKey;
|
GPGUserSigningKey = signingKey;
|
||||||
if (_cached.TryGetValue("http.proxy", out var proxy))
|
if (_cached.TryGetValue("http.proxy", out var proxy))
|
||||||
|
@ -58,20 +76,22 @@ namespace SourceGit.ViewModels
|
||||||
{
|
{
|
||||||
SetIfChanged("user.name", UserName);
|
SetIfChanged("user.name", UserName);
|
||||||
SetIfChanged("user.email", UserEmail);
|
SetIfChanged("user.email", UserEmail);
|
||||||
SetIfChanged("commit.gpgsign", GPGSigningEnabled ? "true" : "false");
|
SetIfChanged("commit.gpgsign", GPGCommitSigningEnabled ? "true" : "false");
|
||||||
|
SetIfChanged("tag.gpgSign", GPGTagSigningEnabled ? "true" : "false");
|
||||||
|
SetIfChanged("gpg.format", GPGFormat?.Value, Models.GPGFormat.OPENPGP.Value);
|
||||||
SetIfChanged("user.signingkey", GPGUserSigningKey);
|
SetIfChanged("user.signingkey", GPGUserSigningKey);
|
||||||
SetIfChanged("http.proxy", HttpProxy);
|
SetIfChanged("http.proxy", HttpProxy);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetIfChanged(string key, string value)
|
private void SetIfChanged(string key, string value, string defaultValue = null)
|
||||||
{
|
{
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
if (_cached.TryGetValue(key, out var old))
|
if (_cached.TryGetValue(key, out var old))
|
||||||
{
|
{
|
||||||
changed = old != value;
|
changed = old != value;
|
||||||
}
|
}
|
||||||
else if (!string.IsNullOrEmpty(value))
|
else if (!string.IsNullOrEmpty(value) && value != defaultValue)
|
||||||
{
|
{
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,11 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
|
||||||
namespace SourceGit.ViewModels
|
namespace SourceGit.ViewModels
|
||||||
{
|
{
|
||||||
|
public class CompareTargetWorktree
|
||||||
|
{
|
||||||
|
public string SHA => string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
public class RevisionCompare : ObservableObject
|
public class RevisionCompare : ObservableObject
|
||||||
{
|
{
|
||||||
public Models.Commit StartPoint
|
public Models.Commit StartPoint
|
||||||
|
@ -18,7 +23,7 @@ namespace SourceGit.ViewModels
|
||||||
private set;
|
private set;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Models.Commit EndPoint
|
public object EndPoint
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
private set;
|
private set;
|
||||||
|
@ -30,49 +35,18 @@ namespace SourceGit.ViewModels
|
||||||
private set => SetProperty(ref _visibleChanges, value);
|
private set => SetProperty(ref _visibleChanges, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<FileTreeNode> ChangeTree
|
public List<Models.Change> SelectedChanges
|
||||||
{
|
{
|
||||||
get => _changeTree;
|
get => _selectedChanges;
|
||||||
private set => SetProperty(ref _changeTree, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Models.Change SelectedChange
|
|
||||||
{
|
|
||||||
get => _selectedChange;
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (SetProperty(ref _selectedChange, value))
|
if (SetProperty(ref _selectedChanges, value))
|
||||||
{
|
{
|
||||||
if (value == null)
|
if (value != null && value.Count == 1)
|
||||||
{
|
DiffContext = new DiffContext(_repo, new Models.DiffOption(StartPoint.SHA, _endPoint, value[0]), _diffContext);
|
||||||
SelectedNode = null;
|
else
|
||||||
DiffContext = null;
|
DiffContext = null;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
SelectedNode = FileTreeNode.SelectByPath(_changeTree, value.Path);
|
|
||||||
DiffContext = new DiffContext(_repo, new Models.DiffOption(StartPoint.SHA, EndPoint.SHA, value), _diffContext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FileTreeNode SelectedNode
|
|
||||||
{
|
|
||||||
get => _selectedNode;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (SetProperty(ref _selectedNode, value))
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
SelectedChange = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SelectedChange = value.Backend as Models.Change;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,11 +72,21 @@ namespace SourceGit.ViewModels
|
||||||
{
|
{
|
||||||
_repo = repo;
|
_repo = repo;
|
||||||
StartPoint = startPoint;
|
StartPoint = startPoint;
|
||||||
|
|
||||||
|
if (endPoint == null)
|
||||||
|
{
|
||||||
|
EndPoint = new CompareTargetWorktree();
|
||||||
|
_endPoint = string.Empty;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
EndPoint = endPoint;
|
EndPoint = endPoint;
|
||||||
|
_endPoint = endPoint.SHA;
|
||||||
|
}
|
||||||
|
|
||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
{
|
{
|
||||||
_changes = new Commands.CompareRevisions(_repo, startPoint.SHA, endPoint.SHA).Result();
|
_changes = new Commands.CompareRevisions(_repo, startPoint.SHA, _endPoint).Result();
|
||||||
|
|
||||||
var visible = _changes;
|
var visible = _changes;
|
||||||
if (!string.IsNullOrWhiteSpace(_searchFilter))
|
if (!string.IsNullOrWhiteSpace(_searchFilter))
|
||||||
|
@ -111,18 +95,11 @@ namespace SourceGit.ViewModels
|
||||||
foreach (var c in _changes)
|
foreach (var c in _changes)
|
||||||
{
|
{
|
||||||
if (c.Path.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase))
|
if (c.Path.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
|
||||||
visible.Add(c);
|
visible.Add(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var tree = FileTreeNode.Build(visible);
|
Dispatcher.UIThread.Invoke(() => VisibleChanges = visible);
|
||||||
Dispatcher.UIThread.Invoke(() =>
|
|
||||||
{
|
|
||||||
VisibleChanges = visible;
|
|
||||||
ChangeTree = tree;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,10 +110,8 @@ namespace SourceGit.ViewModels
|
||||||
_changes.Clear();
|
_changes.Clear();
|
||||||
if (_visibleChanges != null)
|
if (_visibleChanges != null)
|
||||||
_visibleChanges.Clear();
|
_visibleChanges.Clear();
|
||||||
if (_changeTree != null)
|
if (_selectedChanges != null)
|
||||||
_changeTree.Clear();
|
_selectedChanges.Clear();
|
||||||
_selectedChange = null;
|
|
||||||
_selectedNode = null;
|
|
||||||
_searchFilter = null;
|
_searchFilter = null;
|
||||||
_diffContext = null;
|
_diffContext = null;
|
||||||
}
|
}
|
||||||
|
@ -153,8 +128,12 @@ namespace SourceGit.ViewModels
|
||||||
SearchFilter = string.Empty;
|
SearchFilter = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ContextMenu CreateChangeContextMenu(Models.Change change)
|
public ContextMenu CreateChangeContextMenu()
|
||||||
{
|
{
|
||||||
|
if (_selectedChanges == null || _selectedChanges.Count != 1)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var change = _selectedChanges[0];
|
||||||
var menu = new ContextMenu();
|
var menu = new ContextMenu();
|
||||||
|
|
||||||
var diffWithMerger = new MenuItem();
|
var diffWithMerger = new MenuItem();
|
||||||
|
@ -162,7 +141,7 @@ namespace SourceGit.ViewModels
|
||||||
diffWithMerger.Icon = App.CreateMenuIcon("Icons.Diff");
|
diffWithMerger.Icon = App.CreateMenuIcon("Icons.Diff");
|
||||||
diffWithMerger.Click += (_, ev) =>
|
diffWithMerger.Click += (_, ev) =>
|
||||||
{
|
{
|
||||||
var opt = new Models.DiffOption(StartPoint.SHA, EndPoint.SHA, change);
|
var opt = new Models.DiffOption(StartPoint.SHA, _endPoint, change);
|
||||||
var type = Preference.Instance.ExternalMergeToolType;
|
var type = Preference.Instance.ExternalMergeToolType;
|
||||||
var exec = Preference.Instance.ExternalMergeToolPath;
|
var exec = Preference.Instance.ExternalMergeToolPath;
|
||||||
|
|
||||||
|
@ -202,8 +181,18 @@ namespace SourceGit.ViewModels
|
||||||
App.CopyText(change.Path);
|
App.CopyText(change.Path);
|
||||||
ev.Handled = true;
|
ev.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
menu.Items.Add(copyPath);
|
menu.Items.Add(copyPath);
|
||||||
|
|
||||||
|
var copyFileName = new MenuItem();
|
||||||
|
copyFileName.Header = App.Text("CopyFileName");
|
||||||
|
copyFileName.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||||
|
copyFileName.Click += (_, e) =>
|
||||||
|
{
|
||||||
|
App.CopyText(Path.GetFileName(change.Path));
|
||||||
|
e.Handled = true;
|
||||||
|
};
|
||||||
|
menu.Items.Add(copyFileName);
|
||||||
|
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,23 +211,18 @@ namespace SourceGit.ViewModels
|
||||||
foreach (var c in _changes)
|
foreach (var c in _changes)
|
||||||
{
|
{
|
||||||
if (c.Path.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase))
|
if (c.Path.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
|
||||||
visible.Add(c);
|
visible.Add(c);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
VisibleChanges = visible;
|
VisibleChanges = visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
ChangeTree = FileTreeNode.Build(_visibleChanges);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private string _repo = string.Empty;
|
private string _repo = string.Empty;
|
||||||
|
private string _endPoint = string.Empty;
|
||||||
private List<Models.Change> _changes = null;
|
private List<Models.Change> _changes = null;
|
||||||
private List<Models.Change> _visibleChanges = null;
|
private List<Models.Change> _visibleChanges = null;
|
||||||
private List<FileTreeNode> _changeTree = null;
|
private List<Models.Change> _selectedChanges = null;
|
||||||
private Models.Change _selectedChange = null;
|
|
||||||
private FileTreeNode _selectedNode = null;
|
|
||||||
private string _searchFilter = string.Empty;
|
private string _searchFilter = string.Empty;
|
||||||
private DiffContext _diffContext = null;
|
private DiffContext _diffContext = null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Avalonia.Controls;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
|
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
@ -106,34 +106,42 @@ namespace SourceGit.ViewModels
|
||||||
_diffContext = null;
|
_diffContext = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Apply(object param)
|
public ContextMenu MakeContextMenu(Models.Stash stash)
|
||||||
{
|
{
|
||||||
if (param is Models.Stash stash)
|
if (stash == null)
|
||||||
{
|
return null;
|
||||||
Task.Run(() =>
|
|
||||||
{
|
|
||||||
new Commands.Stash(_repo.FullPath).Apply(stash.Name);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Pop(object param)
|
var apply = new MenuItem();
|
||||||
|
apply.Header = App.Text("StashCM.Apply");
|
||||||
|
apply.Click += (o, ev) =>
|
||||||
{
|
{
|
||||||
if (param is Models.Stash stash)
|
Task.Run(() => new Commands.Stash(_repo.FullPath).Apply(stash.Name));
|
||||||
{
|
ev.Handled = true;
|
||||||
Task.Run(() =>
|
};
|
||||||
{
|
|
||||||
new Commands.Stash(_repo.FullPath).Pop(stash.Name);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Drop(object param)
|
var pop = new MenuItem();
|
||||||
|
pop.Header = App.Text("StashCM.Pop");
|
||||||
|
pop.Click += (o, ev) =>
|
||||||
{
|
{
|
||||||
if (param is Models.Stash stash && PopupHost.CanCreatePopup())
|
Task.Run(() => new Commands.Stash(_repo.FullPath).Pop(stash.Name));
|
||||||
|
ev.Handled = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
var drop = new MenuItem();
|
||||||
|
drop.Header = App.Text("StashCM.Drop");
|
||||||
|
drop.Click += (o, ev) =>
|
||||||
{
|
{
|
||||||
|
if (PopupHost.CanCreatePopup())
|
||||||
PopupHost.ShowPopup(new DropStash(_repo.FullPath, stash));
|
PopupHost.ShowPopup(new DropStash(_repo.FullPath, stash));
|
||||||
}
|
|
||||||
|
ev.Handled = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
var menu = new ContextMenu();
|
||||||
|
menu.Items.Add(apply);
|
||||||
|
menu.Items.Add(pop);
|
||||||
|
menu.Items.Add(drop);
|
||||||
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
using Avalonia.Collections;
|
using Avalonia.Collections;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
|
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
|
||||||
|
@ -40,20 +41,21 @@ namespace SourceGit.ViewModels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clone(object param)
|
public void Clone()
|
||||||
{
|
{
|
||||||
var launcher = param as Launcher;
|
|
||||||
var page = launcher.ActivePage;
|
|
||||||
|
|
||||||
if (!Preference.Instance.IsGitConfigured)
|
if (!Preference.Instance.IsGitConfigured)
|
||||||
{
|
{
|
||||||
App.RaiseException(page.GetId(), App.Text("NotConfigured"));
|
App.RaiseException(string.Empty, App.Text("NotConfigured"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PopupHost.CanCreatePopup())
|
if (PopupHost.CanCreatePopup())
|
||||||
{
|
{
|
||||||
PopupHost.ShowPopup(new Clone(launcher, page));
|
if (App.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
|
{
|
||||||
|
var launcher = desktop.MainWindow.DataContext as Launcher;
|
||||||
|
PopupHost.ShowPopup(new Clone(launcher));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,29 @@ namespace SourceGit.ViewModels
|
||||||
|
|
||||||
public class WorkingCopy : ObservableObject
|
public class WorkingCopy : ObservableObject
|
||||||
{
|
{
|
||||||
|
public bool IncludeUntracked
|
||||||
|
{
|
||||||
|
get => _repo.IncludeUntracked;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_repo.IncludeUntracked != value)
|
||||||
|
{
|
||||||
|
_repo.IncludeUntracked = value;
|
||||||
|
OnPropertyChanged(nameof(IncludeUntracked));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanCommitWithPush
|
||||||
|
{
|
||||||
|
get => _canCommitWithPush;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _canCommitWithPush, value))
|
||||||
|
OnPropertyChanged(nameof(IsCommitWithPushVisible));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsStaging
|
public bool IsStaging
|
||||||
{
|
{
|
||||||
get => _isStaging;
|
get => _isStaging;
|
||||||
|
@ -58,7 +81,30 @@ namespace SourceGit.ViewModels
|
||||||
public bool UseAmend
|
public bool UseAmend
|
||||||
{
|
{
|
||||||
get => _useAmend;
|
get => _useAmend;
|
||||||
set => SetProperty(ref _useAmend, value);
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _useAmend, value) && value)
|
||||||
|
{
|
||||||
|
var commits = new Commands.QueryCommits(_repo.FullPath, "-n 1", false).Result();
|
||||||
|
if (commits.Count == 0)
|
||||||
|
{
|
||||||
|
App.RaiseException(_repo.FullPath, "No commits to amend!!!");
|
||||||
|
_useAmend = false;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CommitMessage = commits[0].FullMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OnPropertyChanged(nameof(IsCommitWithPushVisible));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsCommitWithPushVisible
|
||||||
|
{
|
||||||
|
get => !UseAmend && CanCommitWithPush;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Models.Change> Unstaged
|
public List<Models.Change> Unstaged
|
||||||
|
@ -73,103 +119,58 @@ namespace SourceGit.ViewModels
|
||||||
private set => SetProperty(ref _staged, value);
|
private set => SetProperty(ref _staged, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Count
|
public List<Models.Change> SelectedUnstaged
|
||||||
{
|
{
|
||||||
get => _count;
|
get => _selectedUnstaged;
|
||||||
}
|
|
||||||
|
|
||||||
public Models.Change SelectedUnstagedChange
|
|
||||||
{
|
|
||||||
get => _selectedUnstagedChange;
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (SetProperty(ref _selectedUnstagedChange, value) && value != null)
|
if (SetProperty(ref _selectedUnstaged, value))
|
||||||
{
|
{
|
||||||
SelectedStagedChange = null;
|
if (value == null || value.Count == 0)
|
||||||
SelectedStagedTreeNode = null;
|
|
||||||
SetDetail(value, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Models.Change SelectedStagedChange
|
|
||||||
{
|
{
|
||||||
get => _selectedStagedChange;
|
if (_selectedStaged == null || _selectedStaged.Count == 0)
|
||||||
set
|
SetDetail(null);
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<FileTreeNode> StagedTree
|
|
||||||
{
|
|
||||||
get => _stagedTree;
|
|
||||||
private set => SetProperty(ref _stagedTree, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public FileTreeNode SelectedUnstagedTreeNode
|
|
||||||
{
|
|
||||||
get => _selectedUnstagedTreeNode;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (SetProperty(ref _selectedUnstagedTreeNode, value))
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
SelectedUnstagedChange = null;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SelectedUnstagedChange = value.Backend as Models.Change;
|
SelectedStaged = null;
|
||||||
SelectedStagedTreeNode = null;
|
|
||||||
SelectedStagedChange = null;
|
|
||||||
|
|
||||||
if (value.IsFolder)
|
if (value.Count == 1)
|
||||||
{
|
SetDetail(value[0]);
|
||||||
SetDetail(null, true);
|
else
|
||||||
}
|
SetDetail(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FileTreeNode SelectedStagedTreeNode
|
public List<Models.Change> SelectedStaged
|
||||||
{
|
{
|
||||||
get => _selectedStagedTreeNode;
|
get => _selectedStaged;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (SetProperty(ref _selectedStagedTreeNode, value))
|
if (SetProperty(ref _selectedStaged, value))
|
||||||
{
|
{
|
||||||
if (value == null)
|
if (value == null || value.Count == 0)
|
||||||
{
|
{
|
||||||
SelectedStagedChange = null;
|
if (_selectedUnstaged == null || _selectedUnstaged.Count == 0)
|
||||||
|
SetDetail(null);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SelectedStagedChange = value.Backend as Models.Change;
|
SelectedUnstaged = null;
|
||||||
SelectedUnstagedTreeNode = null;
|
|
||||||
SelectedUnstagedChange = null;
|
|
||||||
|
|
||||||
if (value.IsFolder)
|
if (value.Count == 1)
|
||||||
{
|
SetDetail(value[0]);
|
||||||
SetDetail(null, false);
|
else
|
||||||
}
|
SetDetail(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int Count => _count;
|
||||||
|
|
||||||
public object DetailContext
|
public object DetailContext
|
||||||
{
|
{
|
||||||
get => _detailContext;
|
get => _detailContext;
|
||||||
|
@ -190,18 +191,31 @@ namespace SourceGit.ViewModels
|
||||||
public void Cleanup()
|
public void Cleanup()
|
||||||
{
|
{
|
||||||
_repo = null;
|
_repo = null;
|
||||||
|
|
||||||
|
if (_selectedUnstaged != null)
|
||||||
|
{
|
||||||
|
_selectedUnstaged.Clear();
|
||||||
|
OnPropertyChanged(nameof(SelectedUnstaged));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_selectedStaged != null)
|
||||||
|
{
|
||||||
|
_selectedStaged.Clear();
|
||||||
|
OnPropertyChanged(nameof(SelectedStaged));
|
||||||
|
}
|
||||||
|
|
||||||
if (_unstaged != null)
|
if (_unstaged != null)
|
||||||
|
{
|
||||||
_unstaged.Clear();
|
_unstaged.Clear();
|
||||||
|
OnPropertyChanged(nameof(Unstaged));
|
||||||
|
}
|
||||||
|
|
||||||
if (_staged != null)
|
if (_staged != null)
|
||||||
|
{
|
||||||
_staged.Clear();
|
_staged.Clear();
|
||||||
if (_unstagedTree != null)
|
OnPropertyChanged(nameof(Staged));
|
||||||
_unstagedTree.Clear();
|
}
|
||||||
if (_stagedTree != null)
|
|
||||||
_stagedTree.Clear();
|
|
||||||
_selectedUnstagedChange = null;
|
|
||||||
_selectedStagedChange = null;
|
|
||||||
_selectedUnstagedTreeNode = null;
|
|
||||||
_selectedStagedTreeNode = null;
|
|
||||||
_detailContext = null;
|
_detailContext = null;
|
||||||
_commitMessage = string.Empty;
|
_commitMessage = string.Empty;
|
||||||
}
|
}
|
||||||
|
@ -210,20 +224,22 @@ namespace SourceGit.ViewModels
|
||||||
{
|
{
|
||||||
var unstaged = new List<Models.Change>();
|
var unstaged = new List<Models.Change>();
|
||||||
var staged = new List<Models.Change>();
|
var staged = new List<Models.Change>();
|
||||||
|
var selectedUnstaged = new List<Models.Change>();
|
||||||
|
var selectedStaged = new List<Models.Change>();
|
||||||
|
|
||||||
var viewFile = string.Empty;
|
var lastSelectedUnstaged = new HashSet<string>();
|
||||||
var lastSelectedIsUnstaged = false;
|
var lastSelectedStaged = new HashSet<string>();
|
||||||
if (_selectedUnstagedChange != null)
|
if (_selectedUnstaged != null)
|
||||||
{
|
{
|
||||||
viewFile = _selectedUnstagedChange.Path;
|
foreach (var c in _selectedUnstaged)
|
||||||
lastSelectedIsUnstaged = true;
|
lastSelectedUnstaged.Add(c.Path);
|
||||||
}
|
}
|
||||||
else if (_selectedStagedChange != null)
|
else if (_selectedStaged != null)
|
||||||
{
|
{
|
||||||
viewFile = _selectedStagedChange.Path;
|
foreach (var c in _selectedStaged)
|
||||||
|
lastSelectedStaged.Add(c.Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
var viewChange = null as Models.Change;
|
|
||||||
var hasConflict = false;
|
var hasConflict = false;
|
||||||
foreach (var c in changes)
|
foreach (var c in changes)
|
||||||
{
|
{
|
||||||
|
@ -233,65 +249,43 @@ namespace SourceGit.ViewModels
|
||||||
|| c.Index == Models.ChangeState.Renamed)
|
|| c.Index == Models.ChangeState.Renamed)
|
||||||
{
|
{
|
||||||
staged.Add(c);
|
staged.Add(c);
|
||||||
if (!lastSelectedIsUnstaged && c.Path == viewFile)
|
|
||||||
{
|
if (lastSelectedStaged.Contains(c.Path))
|
||||||
viewChange = c;
|
selectedStaged.Add(c);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c.WorkTree != Models.ChangeState.None)
|
if (c.WorkTree != Models.ChangeState.None)
|
||||||
{
|
{
|
||||||
unstaged.Add(c);
|
unstaged.Add(c);
|
||||||
hasConflict |= c.IsConflit;
|
hasConflict |= c.IsConflit;
|
||||||
if (lastSelectedIsUnstaged && c.Path == viewFile)
|
|
||||||
{
|
if (lastSelectedUnstaged.Contains(c.Path))
|
||||||
viewChange = c;
|
selectedUnstaged.Add(c);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_count = changes.Count;
|
_count = changes.Count;
|
||||||
|
|
||||||
var unstagedTree = FileTreeNode.Build(unstaged);
|
|
||||||
var stagedTree = FileTreeNode.Build(staged);
|
|
||||||
Dispatcher.UIThread.Invoke(() =>
|
Dispatcher.UIThread.Invoke(() =>
|
||||||
{
|
{
|
||||||
_isLoadingData = true;
|
_isLoadingData = true;
|
||||||
Unstaged = unstaged;
|
Unstaged = unstaged;
|
||||||
Staged = staged;
|
Staged = staged;
|
||||||
UnstagedTree = unstagedTree;
|
|
||||||
StagedTree = stagedTree;
|
|
||||||
_isLoadingData = false;
|
_isLoadingData = false;
|
||||||
|
|
||||||
// Restore last selection states.
|
|
||||||
if (viewChange != null)
|
|
||||||
{
|
|
||||||
var scrollOffset = Vector.Zero;
|
var scrollOffset = Vector.Zero;
|
||||||
if (_detailContext is DiffContext old)
|
if (_detailContext is DiffContext old)
|
||||||
scrollOffset = old.SyncScrollOffset;
|
scrollOffset = old.SyncScrollOffset;
|
||||||
|
|
||||||
if (lastSelectedIsUnstaged)
|
if (selectedUnstaged.Count > 0)
|
||||||
{
|
SelectedUnstaged = selectedUnstaged;
|
||||||
SelectedUnstagedChange = viewChange;
|
else if (selectedStaged.Count > 0)
|
||||||
SelectedUnstagedTreeNode = FileTreeNode.SelectByPath(_unstagedTree, viewFile);
|
SelectedStaged = selectedStaged;
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
SetDetail(null);
|
||||||
SelectedStagedChange = viewChange;
|
|
||||||
SelectedStagedTreeNode = FileTreeNode.SelectByPath(_stagedTree, viewFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_detailContext is DiffContext cur)
|
if (_detailContext is DiffContext cur)
|
||||||
cur.SyncScrollOffset = scrollOffset;
|
cur.SyncScrollOffset = scrollOffset;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SelectedUnstagedChange = null;
|
|
||||||
SelectedUnstagedTreeNode = null;
|
|
||||||
SelectedStagedChange = null;
|
|
||||||
SelectedStagedTreeNode = null;
|
|
||||||
SetDetail(null, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to load merge message from MERGE_MSG
|
// Try to load merge message from MERGE_MSG
|
||||||
if (string.IsNullOrEmpty(_commitMessage))
|
if (string.IsNullOrEmpty(_commitMessage))
|
||||||
|
@ -305,30 +299,24 @@ namespace SourceGit.ViewModels
|
||||||
return hasConflict;
|
return hasConflict;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetDetail(Models.Change change, bool isUnstaged)
|
public void OpenAssumeUnchanged()
|
||||||
{
|
{
|
||||||
if (_isLoadingData)
|
var dialog = new Views.AssumeUnchangedManager()
|
||||||
return;
|
{
|
||||||
|
DataContext = new AssumeUnchangedManager(_repo.FullPath)
|
||||||
|
};
|
||||||
|
|
||||||
if (change == null)
|
dialog.ShowDialog(App.GetTopLevel() as Window);
|
||||||
{
|
|
||||||
DetailContext = null;
|
|
||||||
}
|
}
|
||||||
else if (change.IsConflit && isUnstaged)
|
|
||||||
|
public void StageSelected()
|
||||||
{
|
{
|
||||||
DetailContext = new ConflictContext(_repo.FullPath, change);
|
StageChanges(_selectedUnstaged);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
public void StageAll()
|
||||||
{
|
{
|
||||||
if (_detailContext is DiffContext previous)
|
StageChanges(_unstaged);
|
||||||
{
|
|
||||||
DetailContext = new DiffContext(_repo.FullPath, new Models.DiffOption(change, isUnstaged), previous);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DetailContext = new DiffContext(_repo.FullPath, new Models.DiffOption(change, isUnstaged));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void StageChanges(List<Models.Change> changes)
|
public async void StageChanges(List<Models.Change> changes)
|
||||||
|
@ -336,7 +324,7 @@ namespace SourceGit.ViewModels
|
||||||
if (_unstaged.Count == 0 || changes.Count == 0)
|
if (_unstaged.Count == 0 || changes.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SetDetail(null, true);
|
SetDetail(null);
|
||||||
IsStaging = true;
|
IsStaging = true;
|
||||||
_repo.SetWatcherEnabled(false);
|
_repo.SetWatcherEnabled(false);
|
||||||
if (changes.Count == _unstaged.Count)
|
if (changes.Count == _unstaged.Count)
|
||||||
|
@ -357,12 +345,22 @@ namespace SourceGit.ViewModels
|
||||||
IsStaging = false;
|
IsStaging = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UnstageSelected()
|
||||||
|
{
|
||||||
|
UnstageChanges(_selectedStaged);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnstageAll()
|
||||||
|
{
|
||||||
|
UnstageChanges(_staged);
|
||||||
|
}
|
||||||
|
|
||||||
public async void UnstageChanges(List<Models.Change> changes)
|
public async void UnstageChanges(List<Models.Change> changes)
|
||||||
{
|
{
|
||||||
if (_staged.Count == 0 || changes.Count == 0)
|
if (_staged.Count == 0 || changes.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SetDetail(null, false);
|
SetDetail(null);
|
||||||
IsUnstaging = true;
|
IsUnstaging = true;
|
||||||
_repo.SetWatcherEnabled(false);
|
_repo.SetWatcherEnabled(false);
|
||||||
if (changes.Count == _staged.Count)
|
if (changes.Count == _staged.Count)
|
||||||
|
@ -412,113 +410,25 @@ namespace SourceGit.ViewModels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void UseTheirs(List<Models.Change> changes)
|
public void Commit()
|
||||||
{
|
{
|
||||||
var files = new List<string>();
|
DoCommit(false);
|
||||||
foreach (var change in changes)
|
|
||||||
{
|
|
||||||
if (change.IsConflit)
|
|
||||||
files.Add(change.Path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_repo.SetWatcherEnabled(false);
|
public void CommitWithPush()
|
||||||
var succ = await Task.Run(() => new Commands.Checkout(_repo.FullPath).UseTheirs(files));
|
|
||||||
if (succ)
|
|
||||||
{
|
{
|
||||||
await Task.Run(() => new Commands.Add(_repo.FullPath, changes).Exec());
|
DoCommit(true);
|
||||||
}
|
|
||||||
_repo.MarkWorkingCopyDirtyManually();
|
|
||||||
_repo.SetWatcherEnabled(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void UseMine(List<Models.Change> changes)
|
public ContextMenu CreateContextMenuForUnstagedChanges()
|
||||||
{
|
{
|
||||||
var files = new List<string>();
|
if (_selectedUnstaged.Count == 0)
|
||||||
foreach (var change in changes)
|
|
||||||
{
|
|
||||||
if (change.IsConflit)
|
|
||||||
files.Add(change.Path);
|
|
||||||
}
|
|
||||||
|
|
||||||
_repo.SetWatcherEnabled(false);
|
|
||||||
var succ = await Task.Run(() => new Commands.Checkout(_repo.FullPath).UseMine(files));
|
|
||||||
if (succ)
|
|
||||||
{
|
|
||||||
await Task.Run(() => new Commands.Add(_repo.FullPath, changes).Exec());
|
|
||||||
}
|
|
||||||
_repo.MarkWorkingCopyDirtyManually();
|
|
||||||
_repo.SetWatcherEnabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async void UseExternalMergeTool(Models.Change change)
|
|
||||||
{
|
|
||||||
var type = Preference.Instance.ExternalMergeToolType;
|
|
||||||
var exec = Preference.Instance.ExternalMergeToolPath;
|
|
||||||
|
|
||||||
var tool = Models.ExternalMerger.Supported.Find(x => x.Type == type);
|
|
||||||
if (tool == null)
|
|
||||||
{
|
|
||||||
App.RaiseException(_repo.FullPath, "Invalid merge tool in preference setting!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var args = tool.Type != 0 ? tool.Cmd : Preference.Instance.ExternalMergeToolCmd;
|
|
||||||
|
|
||||||
_repo.SetWatcherEnabled(false);
|
|
||||||
await Task.Run(() => Commands.MergeTool.OpenForMerge(_repo.FullPath, exec, args, change.Path));
|
|
||||||
_repo.SetWatcherEnabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async void DoCommit(bool autoPush)
|
|
||||||
{
|
|
||||||
if (!PopupHost.CanCreatePopup())
|
|
||||||
{
|
|
||||||
App.RaiseException(_repo.FullPath, "Repository has unfinished job! Please wait!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_staged.Count == 0)
|
|
||||||
{
|
|
||||||
App.RaiseException(_repo.FullPath, "No files added to commit!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(_commitMessage))
|
|
||||||
{
|
|
||||||
App.RaiseException(_repo.FullPath, "Commit without message is NOT allowed!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PushCommitMessage();
|
|
||||||
|
|
||||||
SetDetail(null, false);
|
|
||||||
IsCommitting = true;
|
|
||||||
_repo.SetWatcherEnabled(false);
|
|
||||||
var succ = await Task.Run(() => new Commands.Commit(_repo.FullPath, _commitMessage, _useAmend).Exec());
|
|
||||||
if (succ)
|
|
||||||
{
|
|
||||||
CommitMessage = string.Empty;
|
|
||||||
UseAmend = false;
|
|
||||||
|
|
||||||
if (autoPush)
|
|
||||||
{
|
|
||||||
PopupHost.ShowAndStartPopup(new Push(_repo, null));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_repo.MarkWorkingCopyDirtyManually();
|
|
||||||
_repo.SetWatcherEnabled(true);
|
|
||||||
IsCommitting = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ContextMenu CreateContextMenuForUnstagedChanges(List<Models.Change> changes)
|
|
||||||
{
|
|
||||||
if (changes.Count == 0)
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var menu = new ContextMenu();
|
var menu = new ContextMenu();
|
||||||
if (changes.Count == 1)
|
if (_selectedUnstaged.Count == 1)
|
||||||
{
|
{
|
||||||
var change = changes[0];
|
var change = _selectedUnstaged[0];
|
||||||
var path = Path.GetFullPath(Path.Combine(_repo.FullPath, change.Path));
|
var path = Path.GetFullPath(Path.Combine(_repo.FullPath, change.Path));
|
||||||
|
|
||||||
var explore = new MenuItem();
|
var explore = new MenuItem();
|
||||||
|
@ -551,7 +461,7 @@ namespace SourceGit.ViewModels
|
||||||
useTheirs.Header = App.Text("FileCM.UseTheirs");
|
useTheirs.Header = App.Text("FileCM.UseTheirs");
|
||||||
useTheirs.Click += (_, e) =>
|
useTheirs.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
UseTheirs(changes);
|
UseTheirs(_selectedUnstaged);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -560,7 +470,7 @@ namespace SourceGit.ViewModels
|
||||||
useMine.Header = App.Text("FileCM.UseMine");
|
useMine.Header = App.Text("FileCM.UseMine");
|
||||||
useMine.Click += (_, e) =>
|
useMine.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
UseMine(changes);
|
UseMine(_selectedUnstaged);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -585,7 +495,7 @@ namespace SourceGit.ViewModels
|
||||||
stage.Icon = App.CreateMenuIcon("Icons.File.Add");
|
stage.Icon = App.CreateMenuIcon("Icons.File.Add");
|
||||||
stage.Click += (_, e) =>
|
stage.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
StageChanges(changes);
|
StageChanges(_selectedUnstaged);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -594,7 +504,7 @@ namespace SourceGit.ViewModels
|
||||||
discard.Icon = App.CreateMenuIcon("Icons.Undo");
|
discard.Icon = App.CreateMenuIcon("Icons.Undo");
|
||||||
discard.Click += (_, e) =>
|
discard.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
Discard(changes, true);
|
Discard(_selectedUnstaged, true);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -605,7 +515,7 @@ namespace SourceGit.ViewModels
|
||||||
{
|
{
|
||||||
if (PopupHost.CanCreatePopup())
|
if (PopupHost.CanCreatePopup())
|
||||||
{
|
{
|
||||||
PopupHost.ShowPopup(new StashChanges(_repo, changes, false));
|
PopupHost.ShowPopup(new StashChanges(_repo, _selectedUnstaged, false));
|
||||||
}
|
}
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
@ -627,7 +537,7 @@ namespace SourceGit.ViewModels
|
||||||
var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options);
|
var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options);
|
||||||
if (storageFile != null)
|
if (storageFile != null)
|
||||||
{
|
{
|
||||||
var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, changes, true, storageFile.Path.LocalPath));
|
var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, _selectedUnstaged, true, storageFile.Path.LocalPath));
|
||||||
if (succ)
|
if (succ)
|
||||||
App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
||||||
}
|
}
|
||||||
|
@ -674,12 +584,22 @@ namespace SourceGit.ViewModels
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
menu.Items.Add(copy);
|
menu.Items.Add(copy);
|
||||||
|
|
||||||
|
var copyFileName = new MenuItem();
|
||||||
|
copyFileName.Header = App.Text("CopyFileName");
|
||||||
|
copyFileName.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||||
|
copyFileName.Click += (_, e) =>
|
||||||
|
{
|
||||||
|
App.CopyText(Path.GetFileName(change.Path));
|
||||||
|
e.Handled = true;
|
||||||
|
};
|
||||||
|
menu.Items.Add(copyFileName);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var hasConflicts = false;
|
var hasConflicts = false;
|
||||||
var hasNoneConflicts = false;
|
var hasNoneConflicts = false;
|
||||||
foreach (var change in changes)
|
foreach (var change in _selectedUnstaged)
|
||||||
{
|
{
|
||||||
if (change.IsConflit)
|
if (change.IsConflit)
|
||||||
{
|
{
|
||||||
|
@ -704,7 +624,7 @@ namespace SourceGit.ViewModels
|
||||||
useTheirs.Header = App.Text("FileCM.UseTheirs");
|
useTheirs.Header = App.Text("FileCM.UseTheirs");
|
||||||
useTheirs.Click += (_, e) =>
|
useTheirs.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
UseTheirs(changes);
|
UseTheirs(_selectedUnstaged);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -713,7 +633,7 @@ namespace SourceGit.ViewModels
|
||||||
useMine.Header = App.Text("FileCM.UseMine");
|
useMine.Header = App.Text("FileCM.UseMine");
|
||||||
useMine.Click += (_, e) =>
|
useMine.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
UseMine(changes);
|
UseMine(_selectedUnstaged);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -723,31 +643,31 @@ namespace SourceGit.ViewModels
|
||||||
}
|
}
|
||||||
|
|
||||||
var stage = new MenuItem();
|
var stage = new MenuItem();
|
||||||
stage.Header = App.Text("FileCM.StageMulti", changes.Count);
|
stage.Header = App.Text("FileCM.StageMulti", _selectedUnstaged.Count);
|
||||||
stage.Icon = App.CreateMenuIcon("Icons.File.Add");
|
stage.Icon = App.CreateMenuIcon("Icons.File.Add");
|
||||||
stage.Click += (_, e) =>
|
stage.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
StageChanges(changes);
|
StageChanges(_selectedUnstaged);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
var discard = new MenuItem();
|
var discard = new MenuItem();
|
||||||
discard.Header = App.Text("FileCM.DiscardMulti", changes.Count);
|
discard.Header = App.Text("FileCM.DiscardMulti", _selectedUnstaged.Count);
|
||||||
discard.Icon = App.CreateMenuIcon("Icons.Undo");
|
discard.Icon = App.CreateMenuIcon("Icons.Undo");
|
||||||
discard.Click += (_, e) =>
|
discard.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
Discard(changes, true);
|
Discard(_selectedUnstaged, true);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
var stash = new MenuItem();
|
var stash = new MenuItem();
|
||||||
stash.Header = App.Text("FileCM.StashMulti", changes.Count);
|
stash.Header = App.Text("FileCM.StashMulti", _selectedUnstaged.Count);
|
||||||
stash.Icon = App.CreateMenuIcon("Icons.Stashes");
|
stash.Icon = App.CreateMenuIcon("Icons.Stashes");
|
||||||
stash.Click += (_, e) =>
|
stash.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
if (PopupHost.CanCreatePopup())
|
if (PopupHost.CanCreatePopup())
|
||||||
{
|
{
|
||||||
PopupHost.ShowPopup(new StashChanges(_repo, changes, false));
|
PopupHost.ShowPopup(new StashChanges(_repo, _selectedUnstaged, false));
|
||||||
}
|
}
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
@ -769,7 +689,7 @@ namespace SourceGit.ViewModels
|
||||||
var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options);
|
var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options);
|
||||||
if (storageFile != null)
|
if (storageFile != null)
|
||||||
{
|
{
|
||||||
var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, changes, true, storageFile.Path.LocalPath));
|
var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, _selectedUnstaged, true, storageFile.Path.LocalPath));
|
||||||
if (succ)
|
if (succ)
|
||||||
App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
||||||
}
|
}
|
||||||
|
@ -786,15 +706,15 @@ namespace SourceGit.ViewModels
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ContextMenu CreateContextMenuForStagedChanges(List<Models.Change> changes)
|
public ContextMenu CreateContextMenuForStagedChanges()
|
||||||
{
|
{
|
||||||
if (changes.Count == 0)
|
if (_selectedStaged.Count == 0)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var menu = new ContextMenu();
|
var menu = new ContextMenu();
|
||||||
if (changes.Count == 1)
|
if (_selectedStaged.Count == 1)
|
||||||
{
|
{
|
||||||
var change = changes[0];
|
var change = _selectedStaged[0];
|
||||||
var path = Path.GetFullPath(Path.Combine(_repo.FullPath, change.Path));
|
var path = Path.GetFullPath(Path.Combine(_repo.FullPath, change.Path));
|
||||||
|
|
||||||
var explore = new MenuItem();
|
var explore = new MenuItem();
|
||||||
|
@ -822,7 +742,7 @@ namespace SourceGit.ViewModels
|
||||||
unstage.Icon = App.CreateMenuIcon("Icons.File.Remove");
|
unstage.Icon = App.CreateMenuIcon("Icons.File.Remove");
|
||||||
unstage.Click += (o, e) =>
|
unstage.Click += (o, e) =>
|
||||||
{
|
{
|
||||||
UnstageChanges(changes);
|
UnstageChanges(_selectedStaged);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -831,7 +751,7 @@ namespace SourceGit.ViewModels
|
||||||
discard.Icon = App.CreateMenuIcon("Icons.Undo");
|
discard.Icon = App.CreateMenuIcon("Icons.Undo");
|
||||||
discard.Click += (_, e) =>
|
discard.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
Discard(changes, false);
|
Discard(_selectedStaged, false);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -842,7 +762,7 @@ namespace SourceGit.ViewModels
|
||||||
{
|
{
|
||||||
if (PopupHost.CanCreatePopup())
|
if (PopupHost.CanCreatePopup())
|
||||||
{
|
{
|
||||||
PopupHost.ShowPopup(new StashChanges(_repo, changes, false));
|
PopupHost.ShowPopup(new StashChanges(_repo, _selectedStaged, false));
|
||||||
}
|
}
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
@ -864,7 +784,7 @@ namespace SourceGit.ViewModels
|
||||||
var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options);
|
var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options);
|
||||||
if (storageFile != null)
|
if (storageFile != null)
|
||||||
{
|
{
|
||||||
var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, changes, false, storageFile.Path.LocalPath));
|
var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, _selectedStaged, false, storageFile.Path.LocalPath));
|
||||||
if (succ)
|
if (succ)
|
||||||
App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
||||||
}
|
}
|
||||||
|
@ -881,6 +801,15 @@ namespace SourceGit.ViewModels
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var copyFileName = new MenuItem();
|
||||||
|
copyFileName.Header = App.Text("CopyFileName");
|
||||||
|
copyFileName.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||||
|
copyFileName.Click += (_, e) =>
|
||||||
|
{
|
||||||
|
App.CopyText(Path.GetFileName(change.Path));
|
||||||
|
e.Handled = true;
|
||||||
|
};
|
||||||
|
|
||||||
menu.Items.Add(explore);
|
menu.Items.Add(explore);
|
||||||
menu.Items.Add(openWith);
|
menu.Items.Add(openWith);
|
||||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||||
|
@ -890,35 +819,36 @@ namespace SourceGit.ViewModels
|
||||||
menu.Items.Add(patch);
|
menu.Items.Add(patch);
|
||||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||||
menu.Items.Add(copyPath);
|
menu.Items.Add(copyPath);
|
||||||
|
menu.Items.Add(copyFileName);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var unstage = new MenuItem();
|
var unstage = new MenuItem();
|
||||||
unstage.Header = App.Text("FileCM.UnstageMulti", changes.Count);
|
unstage.Header = App.Text("FileCM.UnstageMulti", _selectedStaged.Count);
|
||||||
unstage.Icon = App.CreateMenuIcon("Icons.File.Remove");
|
unstage.Icon = App.CreateMenuIcon("Icons.File.Remove");
|
||||||
unstage.Click += (o, e) =>
|
unstage.Click += (o, e) =>
|
||||||
{
|
{
|
||||||
UnstageChanges(changes);
|
UnstageChanges(_selectedStaged);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
var discard = new MenuItem();
|
var discard = new MenuItem();
|
||||||
discard.Header = App.Text("FileCM.DiscardMulti", changes.Count);
|
discard.Header = App.Text("FileCM.DiscardMulti", _selectedStaged.Count);
|
||||||
discard.Icon = App.CreateMenuIcon("Icons.Undo");
|
discard.Icon = App.CreateMenuIcon("Icons.Undo");
|
||||||
discard.Click += (_, e) =>
|
discard.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
Discard(changes, false);
|
Discard(_selectedStaged, false);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
var stash = new MenuItem();
|
var stash = new MenuItem();
|
||||||
stash.Header = App.Text("FileCM.StashMulti", changes.Count);
|
stash.Header = App.Text("FileCM.StashMulti", _selectedStaged.Count);
|
||||||
stash.Icon = App.CreateMenuIcon("Icons.Stashes");
|
stash.Icon = App.CreateMenuIcon("Icons.Stashes");
|
||||||
stash.Click += (_, e) =>
|
stash.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
if (PopupHost.CanCreatePopup())
|
if (PopupHost.CanCreatePopup())
|
||||||
{
|
{
|
||||||
PopupHost.ShowPopup(new StashChanges(_repo, changes, false));
|
PopupHost.ShowPopup(new StashChanges(_repo, _selectedStaged, false));
|
||||||
}
|
}
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
@ -940,7 +870,7 @@ namespace SourceGit.ViewModels
|
||||||
var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options);
|
var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options);
|
||||||
if (storageFile != null)
|
if (storageFile != null)
|
||||||
{
|
{
|
||||||
var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, changes, false, storageFile.Path.LocalPath));
|
var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, _selectedStaged, false, storageFile.Path.LocalPath));
|
||||||
if (succ)
|
if (succ)
|
||||||
App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
||||||
}
|
}
|
||||||
|
@ -993,6 +923,139 @@ namespace SourceGit.ViewModels
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SetDetail(Models.Change change)
|
||||||
|
{
|
||||||
|
if (_isLoadingData)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var isUnstaged = _selectedUnstaged != null && _selectedUnstaged.Count > 0;
|
||||||
|
if (change == null)
|
||||||
|
{
|
||||||
|
DetailContext = null;
|
||||||
|
}
|
||||||
|
else if (change.IsConflit && isUnstaged)
|
||||||
|
{
|
||||||
|
DetailContext = new ConflictContext(_repo.FullPath, change);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_detailContext is DiffContext previous)
|
||||||
|
{
|
||||||
|
DetailContext = new DiffContext(_repo.FullPath, new Models.DiffOption(change, isUnstaged), previous);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DetailContext = new DiffContext(_repo.FullPath, new Models.DiffOption(change, isUnstaged));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void UseTheirs(List<Models.Change> changes)
|
||||||
|
{
|
||||||
|
var files = new List<string>();
|
||||||
|
foreach (var change in changes)
|
||||||
|
{
|
||||||
|
if (change.IsConflit)
|
||||||
|
files.Add(change.Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
_repo.SetWatcherEnabled(false);
|
||||||
|
var succ = await Task.Run(() => new Commands.Checkout(_repo.FullPath).UseTheirs(files));
|
||||||
|
if (succ)
|
||||||
|
{
|
||||||
|
await Task.Run(() => new Commands.Add(_repo.FullPath, changes).Exec());
|
||||||
|
}
|
||||||
|
_repo.MarkWorkingCopyDirtyManually();
|
||||||
|
_repo.SetWatcherEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void UseMine(List<Models.Change> changes)
|
||||||
|
{
|
||||||
|
var files = new List<string>();
|
||||||
|
foreach (var change in changes)
|
||||||
|
{
|
||||||
|
if (change.IsConflit)
|
||||||
|
files.Add(change.Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
_repo.SetWatcherEnabled(false);
|
||||||
|
var succ = await Task.Run(() => new Commands.Checkout(_repo.FullPath).UseMine(files));
|
||||||
|
if (succ)
|
||||||
|
{
|
||||||
|
await Task.Run(() => new Commands.Add(_repo.FullPath, changes).Exec());
|
||||||
|
}
|
||||||
|
_repo.MarkWorkingCopyDirtyManually();
|
||||||
|
_repo.SetWatcherEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void UseExternalMergeTool(Models.Change change)
|
||||||
|
{
|
||||||
|
var type = Preference.Instance.ExternalMergeToolType;
|
||||||
|
var exec = Preference.Instance.ExternalMergeToolPath;
|
||||||
|
|
||||||
|
var tool = Models.ExternalMerger.Supported.Find(x => x.Type == type);
|
||||||
|
if (tool == null)
|
||||||
|
{
|
||||||
|
App.RaiseException(_repo.FullPath, "Invalid merge tool in preference setting!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var args = tool.Type != 0 ? tool.Cmd : Preference.Instance.ExternalMergeToolCmd;
|
||||||
|
|
||||||
|
_repo.SetWatcherEnabled(false);
|
||||||
|
await Task.Run(() => Commands.MergeTool.OpenForMerge(_repo.FullPath, exec, args, change.Path));
|
||||||
|
_repo.SetWatcherEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DoCommit(bool autoPush)
|
||||||
|
{
|
||||||
|
if (!PopupHost.CanCreatePopup())
|
||||||
|
{
|
||||||
|
App.RaiseException(_repo.FullPath, "Repository has unfinished job! Please wait!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_staged.Count == 0)
|
||||||
|
{
|
||||||
|
App.RaiseException(_repo.FullPath, "No files added to commit!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(_commitMessage))
|
||||||
|
{
|
||||||
|
App.RaiseException(_repo.FullPath, "Commit without message is NOT allowed!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PushCommitMessage();
|
||||||
|
|
||||||
|
SetDetail(null);
|
||||||
|
IsCommitting = true;
|
||||||
|
_repo.SetWatcherEnabled(false);
|
||||||
|
|
||||||
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
var succ = new Commands.Commit(_repo.FullPath, _commitMessage, _useAmend).Exec();
|
||||||
|
Dispatcher.UIThread.Post(() =>
|
||||||
|
{
|
||||||
|
if (succ)
|
||||||
|
{
|
||||||
|
CommitMessage = string.Empty;
|
||||||
|
UseAmend = false;
|
||||||
|
|
||||||
|
if (autoPush)
|
||||||
|
{
|
||||||
|
PopupHost.ShowAndStartPopup(new Push(_repo, null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_repo.MarkWorkingCopyDirtyManually();
|
||||||
|
_repo.SetWatcherEnabled(true);
|
||||||
|
|
||||||
|
IsCommitting = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void PushCommitMessage()
|
private void PushCommitMessage()
|
||||||
{
|
{
|
||||||
var existIdx = _repo.CommitMessages.IndexOf(CommitMessage);
|
var existIdx = _repo.CommitMessages.IndexOf(CommitMessage);
|
||||||
|
@ -1020,15 +1083,12 @@ namespace SourceGit.ViewModels
|
||||||
private bool _isUnstaging = false;
|
private bool _isUnstaging = false;
|
||||||
private bool _isCommitting = false;
|
private bool _isCommitting = false;
|
||||||
private bool _useAmend = false;
|
private bool _useAmend = false;
|
||||||
|
private bool _canCommitWithPush = false;
|
||||||
private List<Models.Change> _unstaged = null;
|
private List<Models.Change> _unstaged = null;
|
||||||
private List<Models.Change> _staged = null;
|
private List<Models.Change> _staged = null;
|
||||||
private Models.Change _selectedUnstagedChange = null;
|
private List<Models.Change> _selectedUnstaged = null;
|
||||||
private Models.Change _selectedStagedChange = null;
|
private List<Models.Change> _selectedStaged = null;
|
||||||
private int _count = 0;
|
private int _count = 0;
|
||||||
private List<FileTreeNode> _unstagedTree = null;
|
|
||||||
private List<FileTreeNode> _stagedTree = null;
|
|
||||||
private FileTreeNode _selectedUnstagedTreeNode = null;
|
|
||||||
private FileTreeNode _selectedStagedTreeNode = null;
|
|
||||||
private object _detailContext = null;
|
private object _detailContext = null;
|
||||||
private string _commitMessage = string.Empty;
|
private string _commitMessage = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,9 +92,7 @@
|
||||||
<DataGridTemplateColumn>
|
<DataGridTemplateColumn>
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<Button Classes="icon_button"
|
<Button Classes="icon_button" Click="OnRemoveButtonClicked">
|
||||||
Command="{Binding $parent[v:AssumeUnchangedManager].DataContext.(vm:AssumeUnchangedManager).Remove}"
|
|
||||||
CommandParameter="{Binding}">
|
|
||||||
<Path Width="14" Height="14" Data="{StaticResource Icons.Clear}"/>
|
<Path Width="14" Height="14" Data="{StaticResource Icons.Clear}"/>
|
||||||
</Button>
|
</Button>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
|
@ -20,5 +20,12 @@ namespace SourceGit.Views
|
||||||
{
|
{
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnRemoveButtonClicked(object sender, RoutedEventArgs e) {
|
||||||
|
if (DataContext is ViewModels.AssumeUnchangedManager vm && sender is Button button)
|
||||||
|
vm.Remove(button.DataContext as string);
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,10 +84,7 @@
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<!-- Loading -->
|
<!-- Loading -->
|
||||||
<Path Width="48" Height="48"
|
<v:LoadingIcon Width="48" Height="48" IsVisible="{Binding Data, Converter={x:Static ObjectConverters.IsNull}}"/>
|
||||||
Classes="rotating"
|
|
||||||
Data="{StaticResource Icons.Loading}"
|
|
||||||
IsVisible="{Binding Data, Converter={x:Static ObjectConverters.IsNull}}"/>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<!-- Custom window sizer for Linux -->
|
<!-- Custom window sizer for Linux -->
|
||||||
|
|
|
@ -11,8 +11,8 @@
|
||||||
<Button Classes="caption_button" Click="MinimizeWindow">
|
<Button Classes="caption_button" Click="MinimizeWindow">
|
||||||
<Path Data="{StaticResource Icons.Window.Minimize}"/>
|
<Path Data="{StaticResource Icons.Window.Minimize}"/>
|
||||||
</Button>
|
</Button>
|
||||||
<Button Classes="caption_button" Click="MaximizeOrRestoreWindow">
|
<Button Classes="caption_button max_or_restore_btn" Click="MaximizeOrRestoreWindow">
|
||||||
<Path Data="{Binding $parent[Window].WindowState, Converter={x:Static c:WindowStateConverters.ToMaxOrRestoreIcon}}"/>
|
<Path/>
|
||||||
</Button>
|
</Button>
|
||||||
<Button Classes="caption_button" Click="CloseWindow">
|
<Button Classes="caption_button" Click="CloseWindow">
|
||||||
<Path Data="{StaticResource Icons.Window.Close}" Width="9" Height="9"/>
|
<Path Data="{StaticResource Icons.Window.Close}" Width="9" Height="9"/>
|
||||||
|
|
35
src/Views/ChangeCollectionView.axaml
Normal file
35
src/Views/ChangeCollectionView.axaml
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<UserControl 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:v="using:SourceGit.Views"
|
||||||
|
xmlns:c="using:SourceGit.Converters"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="SourceGit.Views.ChangeCollectionView"
|
||||||
|
x:Name="ThisControl">
|
||||||
|
<UserControl.Resources>
|
||||||
|
<DataTemplate x:Key="TreeModeTemplate" DataType="m:FileTreeNode">
|
||||||
|
<Grid HorizontalAlignment="Stretch" Height="24" ColumnDefinitions="Auto,*">
|
||||||
|
<Path Grid.Column="0" Classes="folder_icon" Width="14" Height="14" Margin="0,2,0,0" IsVisible="{Binding IsFolder}" Fill="Goldenrod" VerticalAlignment="Center"/>
|
||||||
|
<v:ChangeStatusIcon Grid.Column="0" Width="14" Height="14" IsWorkingCopyChange="{Binding #ThisControl.IsWorkingCopyChange}" Change="{Binding Backend}" IsVisible="{Binding !IsFolder}"/>
|
||||||
|
<TextBlock Grid.Column="1" Classes="monospace" Text="{Binding FullPath, Converter={x:Static c:PathConverters.PureFileName}}" Margin="6,0,0,0"/>
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
|
<DataTemplate x:Key="ListModeTemplate" DataType="m:Change">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<v:ChangeStatusIcon Width="14" Height="14" IsWorkingCopyChange="{Binding #ThisControl.IsWorkingCopyChange}" Change="{Binding}" Margin="4,0,0,0"/>
|
||||||
|
<TextBlock Classes="monospace" Text="{Binding Path}" Margin="4,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
|
<DataTemplate x:Key="GridModeTemplate" DataType="m:Change">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<v:ChangeStatusIcon Width="14" Height="14" IsWorkingCopyChange="{Binding #ThisControl.IsWorkingCopyChange}" Change="{Binding}" Margin="4,0,0,0"/>
|
||||||
|
<TextBlock Classes="monospace" Text="{Binding Path, Converter={x:Static c:PathConverters.PureFileName}}" Margin="4,0"/>
|
||||||
|
<TextBlock Classes="monospace" Text="{Binding Path, Converter={x:Static c:PathConverters.PureDirectoryName}}" Foreground="{DynamicResource Brush.FG2}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
</UserControl.Resources>
|
||||||
|
</UserControl>
|
274
src/Views/ChangeCollectionView.axaml.cs
Normal file
274
src/Views/ChangeCollectionView.axaml.cs
Normal file
|
@ -0,0 +1,274 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Models.TreeDataGrid;
|
||||||
|
using Avalonia.Controls.Templates;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
|
|
||||||
|
namespace SourceGit.Views
|
||||||
|
{
|
||||||
|
public partial class ChangeCollectionView : UserControl
|
||||||
|
{
|
||||||
|
public static readonly StyledProperty<bool> IsWorkingCopyChangeProperty =
|
||||||
|
AvaloniaProperty.Register<ChangeCollectionView, bool>(nameof(IsWorkingCopyChange), false);
|
||||||
|
|
||||||
|
public bool IsWorkingCopyChange
|
||||||
|
{
|
||||||
|
get => GetValue(IsWorkingCopyChangeProperty);
|
||||||
|
set => SetValue(IsWorkingCopyChangeProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<bool> SingleSelectProperty =
|
||||||
|
AvaloniaProperty.Register<ChangeCollectionView, bool>(nameof(SingleSelect), true);
|
||||||
|
|
||||||
|
public bool SingleSelect
|
||||||
|
{
|
||||||
|
get => GetValue(SingleSelectProperty);
|
||||||
|
set => SetValue(SingleSelectProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<Models.ChangeViewMode> ViewModeProperty =
|
||||||
|
AvaloniaProperty.Register<ChangeCollectionView, Models.ChangeViewMode>(nameof(ViewMode), Models.ChangeViewMode.Tree);
|
||||||
|
|
||||||
|
public Models.ChangeViewMode ViewMode
|
||||||
|
{
|
||||||
|
get => GetValue(ViewModeProperty);
|
||||||
|
set => SetValue(ViewModeProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<List<Models.Change>> ChangesProperty =
|
||||||
|
AvaloniaProperty.Register<ChangeCollectionView, List<Models.Change>>(nameof(Changes), null);
|
||||||
|
|
||||||
|
public List<Models.Change> Changes
|
||||||
|
{
|
||||||
|
get => GetValue(ChangesProperty);
|
||||||
|
set => SetValue(ChangesProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<List<Models.Change>> SelectedChangesProperty =
|
||||||
|
AvaloniaProperty.Register<ChangeCollectionView, List<Models.Change>>(nameof(SelectedChanges), null);
|
||||||
|
|
||||||
|
public List<Models.Change> SelectedChanges
|
||||||
|
{
|
||||||
|
get => GetValue(SelectedChangesProperty);
|
||||||
|
set => SetValue(SelectedChangesProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly RoutedEvent<RoutedEventArgs> ChangeDoubleTappedEvent =
|
||||||
|
RoutedEvent.Register<ChangeCollectionView, RoutedEventArgs>(nameof(ChangeDoubleTapped), RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
|
||||||
|
|
||||||
|
public event EventHandler<RoutedEventArgs> ChangeDoubleTapped
|
||||||
|
{
|
||||||
|
add { AddHandler(ChangeDoubleTappedEvent, value); }
|
||||||
|
remove { RemoveHandler(ChangeDoubleTappedEvent, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
static ChangeCollectionView()
|
||||||
|
{
|
||||||
|
ViewModeProperty.Changed.AddClassHandler<ChangeCollectionView>((c, e) => c.UpdateSource());
|
||||||
|
ChangesProperty.Changed.AddClassHandler<ChangeCollectionView>((c, e) => c.UpdateSource());
|
||||||
|
SelectedChangesProperty.Changed.AddClassHandler<ChangeCollectionView>((c, e) => c.UpdateSelected());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChangeCollectionView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateSource()
|
||||||
|
{
|
||||||
|
if (Content is TreeDataGrid tree && tree.Source is IDisposable disposable)
|
||||||
|
disposable.Dispose();
|
||||||
|
|
||||||
|
Content = null;
|
||||||
|
|
||||||
|
var changes = Changes;
|
||||||
|
if (changes == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var viewMode = ViewMode;
|
||||||
|
if (viewMode == Models.ChangeViewMode.Tree)
|
||||||
|
{
|
||||||
|
var filetree = Models.FileTreeNode.Build(changes, true);
|
||||||
|
var template = this.FindResource("TreeModeTemplate") as IDataTemplate;
|
||||||
|
var source = new HierarchicalTreeDataGridSource<Models.FileTreeNode>(filetree)
|
||||||
|
{
|
||||||
|
Columns =
|
||||||
|
{
|
||||||
|
new HierarchicalExpanderColumn<Models.FileTreeNode>(
|
||||||
|
new TemplateColumn<Models.FileTreeNode>(null, template, null, GridLength.Auto),
|
||||||
|
x => x.Children,
|
||||||
|
x => x.Children.Count > 0,
|
||||||
|
x => x.IsExpanded)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var selection = new Models.TreeDataGridSelectionModel<Models.FileTreeNode>(source, x => x.Children);
|
||||||
|
selection.SingleSelect = SingleSelect;
|
||||||
|
selection.RowDoubleTapped += (_, e) => RaiseEvent(new RoutedEventArgs(ChangeDoubleTappedEvent));
|
||||||
|
selection.SelectionChanged += (s, _) =>
|
||||||
|
{
|
||||||
|
if (!_isSelecting && s is Models.TreeDataGridSelectionModel<Models.FileTreeNode> model)
|
||||||
|
{
|
||||||
|
var selected = new List<Models.Change>();
|
||||||
|
foreach (var c in model.SelectedItems)
|
||||||
|
CollectChangesInNode(selected, c);
|
||||||
|
|
||||||
|
_isSelecting = true;
|
||||||
|
SetCurrentValue(SelectedChangesProperty, selected);
|
||||||
|
_isSelecting = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
source.Selection = selection;
|
||||||
|
CreateTreeDataGrid(source);
|
||||||
|
}
|
||||||
|
else if (viewMode == Models.ChangeViewMode.List)
|
||||||
|
{
|
||||||
|
var template = this.FindResource("ListModeTemplate") as IDataTemplate;
|
||||||
|
var source = new FlatTreeDataGridSource<Models.Change>(changes)
|
||||||
|
{
|
||||||
|
Columns = { new TemplateColumn<Models.Change>(null, template, null, GridLength.Auto) }
|
||||||
|
};
|
||||||
|
|
||||||
|
var selection = new Models.TreeDataGridSelectionModel<Models.Change>(source, null);
|
||||||
|
selection.SingleSelect = SingleSelect;
|
||||||
|
selection.RowDoubleTapped += (_, e) => RaiseEvent(new RoutedEventArgs(ChangeDoubleTappedEvent));
|
||||||
|
selection.SelectionChanged += (s, _) =>
|
||||||
|
{
|
||||||
|
if (!_isSelecting && s is Models.TreeDataGridSelectionModel<Models.Change> model)
|
||||||
|
{
|
||||||
|
var selected = new List<Models.Change>();
|
||||||
|
foreach (var c in model.SelectedItems)
|
||||||
|
selected.Add(c);
|
||||||
|
|
||||||
|
_isSelecting = true;
|
||||||
|
SetCurrentValue(SelectedChangesProperty, selected);
|
||||||
|
_isSelecting = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
source.Selection = selection;
|
||||||
|
CreateTreeDataGrid(source);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var template = this.FindResource("GridModeTemplate") as IDataTemplate;
|
||||||
|
var source = new FlatTreeDataGridSource<Models.Change>(changes)
|
||||||
|
{
|
||||||
|
Columns = { new TemplateColumn<Models.Change>(null, template, null, GridLength.Auto) },
|
||||||
|
};
|
||||||
|
|
||||||
|
var selection = new Models.TreeDataGridSelectionModel<Models.Change>(source, null);
|
||||||
|
selection.SingleSelect = SingleSelect;
|
||||||
|
selection.RowDoubleTapped += (_, e) => RaiseEvent(new RoutedEventArgs(ChangeDoubleTappedEvent));
|
||||||
|
selection.SelectionChanged += (s, _) =>
|
||||||
|
{
|
||||||
|
if (!_isSelecting && s is Models.TreeDataGridSelectionModel<Models.Change> model)
|
||||||
|
{
|
||||||
|
var selected = new List<Models.Change>();
|
||||||
|
foreach (var c in model.SelectedItems)
|
||||||
|
selected.Add(c);
|
||||||
|
|
||||||
|
_isSelecting = true;
|
||||||
|
SetCurrentValue(SelectedChangesProperty, selected);
|
||||||
|
_isSelecting = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
source.Selection = selection;
|
||||||
|
CreateTreeDataGrid(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateSelected()
|
||||||
|
{
|
||||||
|
if (_isSelecting || Content == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var tree = Content as TreeDataGrid;
|
||||||
|
if (tree == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_isSelecting = true;
|
||||||
|
var selected = SelectedChanges;
|
||||||
|
if (tree.Source.Selection is Models.TreeDataGridSelectionModel<Models.Change> changeSelection)
|
||||||
|
{
|
||||||
|
if (selected == null || selected.Count == 0)
|
||||||
|
changeSelection.Clear();
|
||||||
|
else
|
||||||
|
changeSelection.Select(selected);
|
||||||
|
}
|
||||||
|
else if (tree.Source.Selection is Models.TreeDataGridSelectionModel<Models.FileTreeNode> treeSelection)
|
||||||
|
{
|
||||||
|
if (selected == null || selected.Count == 0)
|
||||||
|
{
|
||||||
|
treeSelection.Clear();
|
||||||
|
_isSelecting = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var set = new HashSet<object>();
|
||||||
|
foreach (var c in selected)
|
||||||
|
set.Add(c);
|
||||||
|
|
||||||
|
var nodes = new List<Models.FileTreeNode>();
|
||||||
|
foreach (var node in tree.Source.Items)
|
||||||
|
CollectSelectedNodeByChange(nodes, node as Models.FileTreeNode, set);
|
||||||
|
|
||||||
|
if (nodes.Count == 0)
|
||||||
|
treeSelection.Clear();
|
||||||
|
else
|
||||||
|
treeSelection.Select(nodes);
|
||||||
|
}
|
||||||
|
_isSelecting = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateTreeDataGrid(ITreeDataGridSource source)
|
||||||
|
{
|
||||||
|
Content = new TreeDataGrid()
|
||||||
|
{
|
||||||
|
AutoDragDropRows = false,
|
||||||
|
ShowColumnHeaders = false,
|
||||||
|
CanUserResizeColumns = false,
|
||||||
|
CanUserSortColumns = false,
|
||||||
|
Source = source,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CollectChangesInNode(List<Models.Change> outs, Models.FileTreeNode node)
|
||||||
|
{
|
||||||
|
if (node.IsFolder)
|
||||||
|
{
|
||||||
|
foreach (var child in node.Children)
|
||||||
|
CollectChangesInNode(outs, child);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var change = node.Backend as Models.Change;
|
||||||
|
if (change != null && !outs.Contains(change))
|
||||||
|
outs.Add(change);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CollectSelectedNodeByChange(List<Models.FileTreeNode> outs, Models.FileTreeNode node, HashSet<object> selected)
|
||||||
|
{
|
||||||
|
if (node == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (node.IsFolder)
|
||||||
|
{
|
||||||
|
foreach (var child in node.Children)
|
||||||
|
CollectSelectedNodeByChange(outs, child, selected);
|
||||||
|
}
|
||||||
|
else if (node.Backend != null && selected.Contains(node.Backend))
|
||||||
|
{
|
||||||
|
outs.Add(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _isSelecting = false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,22 +14,22 @@
|
||||||
<MenuFlyout Placement="BottomEdgeAlignedLeft">
|
<MenuFlyout Placement="BottomEdgeAlignedLeft">
|
||||||
<MenuItem Header="{DynamicResource Text.ChangeDisplayMode.List}" Command="{Binding SwitchMode}" CommandParameter="{x:Static m:ChangeViewMode.List}">
|
<MenuItem Header="{DynamicResource Text.ChangeDisplayMode.List}" Command="{Binding SwitchMode}" CommandParameter="{x:Static m:ChangeViewMode.List}">
|
||||||
<MenuItem.Icon>
|
<MenuItem.Icon>
|
||||||
<Path Width="12" Height="12" Fill="{DynamicResource Brush.FG2}" Data="{StaticResource Icons.List}"/>
|
<Path Width="12" Height="12" Data="{StaticResource Icons.List}"/>
|
||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem Header="{DynamicResource Text.ChangeDisplayMode.Grid}" Command="{Binding SwitchMode}" CommandParameter="{x:Static m:ChangeViewMode.Grid}">
|
<MenuItem Header="{DynamicResource Text.ChangeDisplayMode.Grid}" Command="{Binding SwitchMode}" CommandParameter="{x:Static m:ChangeViewMode.Grid}">
|
||||||
<MenuItem.Icon>
|
<MenuItem.Icon>
|
||||||
<Path Width="12" Height="12" Fill="{DynamicResource Brush.FG2}" Data="{StaticResource Icons.Grid}"/>
|
<Path Width="12" Height="12" Data="{StaticResource Icons.Grid}"/>
|
||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem Header="{DynamicResource Text.ChangeDisplayMode.Tree}" Command="{Binding SwitchMode}" CommandParameter="{x:Static m:ChangeViewMode.Tree}">
|
<MenuItem Header="{DynamicResource Text.ChangeDisplayMode.Tree}" Command="{Binding SwitchMode}" CommandParameter="{x:Static m:ChangeViewMode.Tree}">
|
||||||
<MenuItem.Icon>
|
<MenuItem.Icon>
|
||||||
<Path Width="12" Height="12" Fill="{DynamicResource Brush.FG2}" Data="{StaticResource Icons.Tree}"/>
|
<Path Width="12" Height="12" Data="{StaticResource Icons.Tree}"/>
|
||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</MenuFlyout>
|
</MenuFlyout>
|
||||||
</Button.Flyout>
|
</Button.Flyout>
|
||||||
|
|
||||||
<Path Stretch="Uniform" Fill="{DynamicResource Brush.FG2}" Data="{Binding ViewMode, Converter={x:Static c:ChangeViewModeConverters.ToIcon}}"/>
|
<Path Classes="change_mode_switcher_icon" Stretch="Uniform" Data="{Binding ViewMode, Converter={x:Static c:ChangeViewModeConverters.ToIcon}}"/>
|
||||||
</Button>
|
</Button>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:m="using:SourceGit.Models"
|
||||||
xmlns:vm="using:SourceGit.ViewModels"
|
xmlns:vm="using:SourceGit.ViewModels"
|
||||||
|
xmlns:ac="using:Avalonia.Controls.Converters"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="SourceGit.Views.Checkout"
|
x:Class="SourceGit.Views.Checkout"
|
||||||
x:DataType="vm:Checkout">
|
x:DataType="vm:Checkout">
|
||||||
|
@ -11,7 +13,7 @@
|
||||||
Classes="bold"
|
Classes="bold"
|
||||||
Text="{DynamicResource Text.Checkout}"/>
|
Text="{DynamicResource Text.Checkout}"/>
|
||||||
|
|
||||||
<Grid Margin="0,16,0,0" RowDefinitions="32,32" ColumnDefinitions="150,*">
|
<Grid Margin="0,16,0,0" RowDefinitions="32,32" ColumnDefinitions="140,*">
|
||||||
<TextBlock Grid.Row="0" Grid.Column="0"
|
<TextBlock Grid.Row="0" Grid.Column="0"
|
||||||
HorizontalAlignment="Right" VerticalAlignment="Center"
|
HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||||
Margin="0,0,8,0"
|
Margin="0,0,8,0"
|
||||||
|
@ -26,12 +28,21 @@
|
||||||
Margin="0,0,8,0"
|
Margin="0,0,8,0"
|
||||||
Text="{DynamicResource Text.Checkout.LocalChanges}"/>
|
Text="{DynamicResource Text.Checkout.LocalChanges}"/>
|
||||||
<StackPanel Grid.Row="1" Grid.Column="1" Orientation="Horizontal">
|
<StackPanel Grid.Row="1" Grid.Column="1" Orientation="Horizontal">
|
||||||
<RadioButton Content="{DynamicResource Text.Checkout.LocalChanges.StashAndReply}"
|
<StackPanel.Resources>
|
||||||
|
<ac:EnumToBoolConverter x:Key="EnumToBoolConverter"/>
|
||||||
|
</StackPanel.Resources>
|
||||||
|
|
||||||
|
<RadioButton Content="{DynamicResource Text.CreateBranch.LocalChanges.StashAndReply}"
|
||||||
GroupName="LocalChanges"
|
GroupName="LocalChanges"
|
||||||
IsChecked="{Binding AutoStash, Mode=TwoWay}"/>
|
IsChecked="{Binding PreAction, Mode=TwoWay, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static m:DealWithLocalChanges.StashAndReaply}}"/>
|
||||||
<RadioButton Content="{DynamicResource Text.Checkout.LocalChanges.Discard}"
|
<RadioButton Content="{DynamicResource Text.CreateBranch.LocalChanges.Discard}"
|
||||||
GroupName="LocalChanges"
|
GroupName="LocalChanges"
|
||||||
Margin="8,0,0,0"/>
|
Margin="8,0,0,0"
|
||||||
|
IsChecked="{Binding PreAction, Mode=TwoWay, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static m:DealWithLocalChanges.Discard}}"/>
|
||||||
|
<RadioButton Content="{DynamicResource Text.CreateBranch.LocalChanges.DoNothing}"
|
||||||
|
GroupName="LocalChanges"
|
||||||
|
Margin="8,0,0,0"
|
||||||
|
IsChecked="{Binding PreAction, Mode=TwoWay, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static m:DealWithLocalChanges.DoNothing}}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
Foreground="{DynamicResource Brush.FG2}"
|
Foreground="{DynamicResource Brush.FG2}"
|
||||||
FontStyle="Italic"/>
|
FontStyle="Italic"/>
|
||||||
|
|
||||||
<Grid RowDefinitions="32,Auto" ColumnDefinitions="120,*">
|
<Grid RowDefinitions="32,Auto" ColumnDefinitions="Auto,*" ClipToBounds="True">
|
||||||
<TextBlock Grid.Row="0" Grid.Column="0"
|
<TextBlock Grid.Row="0" Grid.Column="0"
|
||||||
HorizontalAlignment="Right" VerticalAlignment="Center"
|
HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||||
Margin="0,0,8,0"
|
Margin="0,0,8,0"
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:m="using:SourceGit.Models"
|
|
||||||
xmlns:vm="using:SourceGit.ViewModels"
|
xmlns:vm="using:SourceGit.ViewModels"
|
||||||
xmlns:v="using:SourceGit.Views"
|
xmlns:v="using:SourceGit.Views"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
@ -11,14 +10,14 @@
|
||||||
<StackPanel Orientation="Vertical" Margin="8,0,0,0">
|
<StackPanel Orientation="Vertical" Margin="8,0,0,0">
|
||||||
<TextBlock Classes="bold" FontSize="18" Text="{DynamicResource Text.Clone}"/>
|
<TextBlock Classes="bold" FontSize="18" Text="{DynamicResource Text.Clone}"/>
|
||||||
|
|
||||||
<Grid Margin="8,16,0,0" Height="28" ColumnDefinitions="120,*">
|
<Grid Margin="8,16,0,0" Height="28" ColumnDefinitions="140,*">
|
||||||
<TextBlock Grid.Column="0" HorizontalAlignment="Right" Margin="0,0,8,0" Text="{DynamicResource Text.Clone.RemoteURL}"/>
|
<TextBlock Grid.Column="0" HorizontalAlignment="Right" Margin="0,0,8,0" Text="{DynamicResource Text.Clone.RemoteURL}"/>
|
||||||
<TextBox Grid.Column="1" CornerRadius="3" Text="{Binding Remote, Mode=TwoWay}" v:AutoFocusBehaviour.IsEnabled="True"/>
|
<TextBox Grid.Column="1" CornerRadius="3" Text="{Binding Remote, Mode=TwoWay}" v:AutoFocusBehaviour.IsEnabled="True"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid Margin="8,4,0,0" Height="28" ColumnDefinitions="120,*" IsVisible="{Binding UseSSH}">
|
<Grid Margin="8,4,0,0" Height="28" ColumnDefinitions="140,*" IsVisible="{Binding UseSSH}">
|
||||||
<TextBlock Grid.Column="0" HorizontalAlignment="Right" Margin="0,0,8,0" Text="{DynamicResource Text.SSHKey}"/>
|
<TextBlock Grid.Column="0" HorizontalAlignment="Right" Margin="0,0,8,0" Text="{DynamicResource Text.SSHKey}"/>
|
||||||
<TextBox Grid.Column="1"
|
<TextBox Grid.Column="1"
|
||||||
x:Name="txtSSHKey"
|
x:Name="TxtSshKey"
|
||||||
Height="28"
|
Height="28"
|
||||||
CornerRadius="3"
|
CornerRadius="3"
|
||||||
Watermark="{DynamicResource Text.SSHKey.Placeholder}"
|
Watermark="{DynamicResource Text.SSHKey.Placeholder}"
|
||||||
|
@ -30,10 +29,10 @@
|
||||||
</TextBox.InnerRightContent>
|
</TextBox.InnerRightContent>
|
||||||
</TextBox>
|
</TextBox>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid Margin="8,4,0,0" Height="28" ColumnDefinitions="120,*">
|
<Grid Margin="8,4,0,0" Height="28" ColumnDefinitions="140,*">
|
||||||
<TextBlock Grid.Column="0" HorizontalAlignment="Right" Margin="0,0,8,0" Text="{DynamicResource Text.Clone.ParentFolder}"/>
|
<TextBlock Grid.Column="0" HorizontalAlignment="Right" Margin="0,0,8,0" Text="{DynamicResource Text.Clone.ParentFolder}"/>
|
||||||
<TextBox Grid.Column="1"
|
<TextBox Grid.Column="1"
|
||||||
x:Name="txtParentFolder"
|
x:Name="TxtParentFolder"
|
||||||
Height="28"
|
Height="28"
|
||||||
CornerRadius="3"
|
CornerRadius="3"
|
||||||
Text="{Binding ParentFolder, Mode=TwoWay}">
|
Text="{Binding ParentFolder, Mode=TwoWay}">
|
||||||
|
@ -44,11 +43,11 @@
|
||||||
</TextBox.InnerRightContent>
|
</TextBox.InnerRightContent>
|
||||||
</TextBox>
|
</TextBox>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid Margin="8,4,0,0" Height="28" ColumnDefinitions="120,*">
|
<Grid Margin="8,4,0,0" Height="28" ColumnDefinitions="140,*">
|
||||||
<TextBlock Grid.Column="0" HorizontalAlignment="Right" Margin="0,0,8,0" Text="{DynamicResource Text.Clone.LocalName}"/>
|
<TextBlock Grid.Column="0" HorizontalAlignment="Right" Margin="0,0,8,0" Text="{DynamicResource Text.Clone.LocalName}"/>
|
||||||
<TextBox Grid.Column="1" CornerRadius="3" Watermark="{DynamicResource Text.Clone.LocalName.Placeholder}" Text="{Binding Local, Mode=TwoWay}"/>
|
<TextBox Grid.Column="1" CornerRadius="3" Watermark="{DynamicResource Text.Clone.LocalName.Placeholder}" Text="{Binding Local, Mode=TwoWay}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid Margin="8,4,0,0" Height="28" ColumnDefinitions="120,*">
|
<Grid Margin="8,4,0,0" Height="28" ColumnDefinitions="140,*">
|
||||||
<TextBlock Grid.Column="0" HorizontalAlignment="Right" Margin="0,0,8,0" Text="{DynamicResource Text.Clone.AdditionalParam}"/>
|
<TextBlock Grid.Column="0" HorizontalAlignment="Right" Margin="0,0,8,0" Text="{DynamicResource Text.Clone.AdditionalParam}"/>
|
||||||
<TextBox Grid.Column="1" CornerRadius="3" Watermark="{DynamicResource Text.Clone.AdditionalParam.Placeholder}" Text="{Binding ExtraArgs, Mode=TwoWay}"/>
|
<TextBox Grid.Column="1" CornerRadius="3" Watermark="{DynamicResource Text.Clone.AdditionalParam.Placeholder}" Text="{Binding ExtraArgs, Mode=TwoWay}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
|
@ -15,11 +15,12 @@ namespace SourceGit.Views
|
||||||
{
|
{
|
||||||
var options = new FolderPickerOpenOptions() { AllowMultiple = false };
|
var options = new FolderPickerOpenOptions() { AllowMultiple = false };
|
||||||
var toplevel = TopLevel.GetTopLevel(this);
|
var toplevel = TopLevel.GetTopLevel(this);
|
||||||
|
if (toplevel == null)
|
||||||
|
return;
|
||||||
|
|
||||||
var selected = await toplevel.StorageProvider.OpenFolderPickerAsync(options);
|
var selected = await toplevel.StorageProvider.OpenFolderPickerAsync(options);
|
||||||
if (selected.Count == 1)
|
if (selected.Count == 1)
|
||||||
{
|
TxtParentFolder.Text = selected[0].Path.LocalPath;
|
||||||
txtParentFolder.Text = selected[0].Path.LocalPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
|
@ -28,11 +29,12 @@ namespace SourceGit.Views
|
||||||
{
|
{
|
||||||
var options = new FilePickerOpenOptions() { AllowMultiple = false, FileTypeFilter = [new FilePickerFileType("SSHKey") { Patterns = ["*.*"] }] };
|
var options = new FilePickerOpenOptions() { AllowMultiple = false, FileTypeFilter = [new FilePickerFileType("SSHKey") { Patterns = ["*.*"] }] };
|
||||||
var toplevel = TopLevel.GetTopLevel(this);
|
var toplevel = TopLevel.GetTopLevel(this);
|
||||||
|
if (toplevel == null)
|
||||||
|
return;
|
||||||
|
|
||||||
var selected = await toplevel.StorageProvider.OpenFilePickerAsync(options);
|
var selected = await toplevel.StorageProvider.OpenFilePickerAsync(options);
|
||||||
if (selected.Count == 1)
|
if (selected.Count == 1)
|
||||||
{
|
TxtSshKey.Text = selected[0].Path.LocalPath;
|
||||||
txtSSHKey.Text = selected[0].Path.LocalPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,11 +12,7 @@
|
||||||
<DataTemplate DataType="m:Commit">
|
<DataTemplate DataType="m:Commit">
|
||||||
<StackPanel Orientation="Vertical">
|
<StackPanel Orientation="Vertical">
|
||||||
<!-- Author & Committer -->
|
<!-- Author & Committer -->
|
||||||
<Grid Margin="0,8">
|
<UniformGrid Rows="1" Margin="0,8">
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="{Binding IsCommitterVisible, Converter={x:Static c:BoolConverters.ToStarOrAutoGridLength}}"/>
|
|
||||||
<ColumnDefinition Width="*"/>
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<!-- Author -->
|
<!-- Author -->
|
||||||
<Grid Grid.Column="0" ColumnDefinitions="96,*">
|
<Grid Grid.Column="0" ColumnDefinitions="96,*">
|
||||||
<v:Avatar Grid.Column="0" Width="64" Height="64" HorizontalAlignment="Right" User="{Binding Author}"/>
|
<v:Avatar Grid.Column="0" Width="64" Height="64" HorizontalAlignment="Right" User="{Binding Author}"/>
|
||||||
|
@ -48,7 +44,7 @@
|
||||||
Foreground="{DynamicResource Brush.FG2}"/>
|
Foreground="{DynamicResource Brush.FG2}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</UniformGrid>
|
||||||
|
|
||||||
<!-- Line -->
|
<!-- Line -->
|
||||||
<Rectangle Height=".65" Margin="8" Fill="{DynamicResource Brush.Border2}" VerticalAlignment="Center"/>
|
<Rectangle Height=".65" Margin="8" Fill="{DynamicResource Brush.Border2}" VerticalAlignment="Center"/>
|
||||||
|
@ -111,9 +107,6 @@
|
||||||
<SelectableTextBlock Text="{Binding FullMessage}" FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}" TextWrapping="Wrap"/>
|
<SelectableTextBlock Text="{Binding FullMessage}" FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}" TextWrapping="Wrap"/>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<!-- Line -->
|
|
||||||
<Rectangle Height=".65" Margin="8" Fill="{DynamicResource Brush.Border2}" VerticalAlignment="Center"/>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</UserControl.DataTemplates>
|
</UserControl.DataTemplates>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
|
|
||||||
|
@ -5,6 +6,15 @@ namespace SourceGit.Views
|
||||||
{
|
{
|
||||||
public partial class CommitBaseInfo : UserControl
|
public partial class CommitBaseInfo : UserControl
|
||||||
{
|
{
|
||||||
|
public static readonly StyledProperty<bool> CanNavigateProperty =
|
||||||
|
AvaloniaProperty.Register<CommitBaseInfo, bool>(nameof(CanNavigate), true);
|
||||||
|
|
||||||
|
public bool CanNavigate
|
||||||
|
{
|
||||||
|
get => GetValue(CanNavigateProperty);
|
||||||
|
set => SetValue(CanNavigateProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
public CommitBaseInfo()
|
public CommitBaseInfo()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
@ -12,10 +22,9 @@ namespace SourceGit.Views
|
||||||
|
|
||||||
private void OnParentSHAPressed(object sender, PointerPressedEventArgs e)
|
private void OnParentSHAPressed(object sender, PointerPressedEventArgs e)
|
||||||
{
|
{
|
||||||
if (DataContext is ViewModels.CommitDetail detail)
|
if (DataContext is ViewModels.CommitDetail detail && CanNavigate)
|
||||||
{
|
|
||||||
detail.NavigateTo((sender as Control).DataContext as string);
|
detail.NavigateTo((sender as Control).DataContext as string);
|
||||||
}
|
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
<Grid Grid.Column="0" RowDefinitions="26,*">
|
<Grid Grid.Column="0" RowDefinitions="26,*">
|
||||||
<!-- Search & Display Mode -->
|
<!-- Search & Display Mode -->
|
||||||
<Grid Grid.Row="0" ColumnDefinitions="*,24">
|
<Grid Grid.Row="0" ColumnDefinitions="*,18">
|
||||||
<TextBox Grid.Column="0"
|
<TextBox Grid.Column="0"
|
||||||
Height="26"
|
Height="26"
|
||||||
BorderThickness="1" BorderBrush="{DynamicResource Brush.Border2}"
|
BorderThickness="1" BorderBrush="{DynamicResource Brush.Border2}"
|
||||||
|
@ -39,116 +39,18 @@
|
||||||
</TextBox>
|
</TextBox>
|
||||||
|
|
||||||
<v:ChangeViewModeSwitcher Grid.Column="1"
|
<v:ChangeViewModeSwitcher Grid.Column="1"
|
||||||
Width="18" Height="18"
|
Width="14" Height="14"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=CommitChangeViewMode, Mode=TwoWay}"/>
|
ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=CommitChangeViewMode, Mode=TwoWay}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<!-- Changes -->
|
<!-- Changes -->
|
||||||
<Border Grid.Row="1" Margin="0,4,0,0" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}">
|
<Border Grid.Row="1" Margin="0,4,0,0" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}">
|
||||||
<Grid>
|
<v:ChangeCollectionView IsWorkingCopyChange="False"
|
||||||
<DataGrid Background="Transparent"
|
ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=CommitChangeViewMode}"
|
||||||
ItemsSource="{Binding VisibleChanges}"
|
Changes="{Binding VisibleChanges}"
|
||||||
SelectedItem="{Binding SelectedChange, Mode=TwoWay}"
|
SelectedChanges="{Binding SelectedChanges, Mode=TwoWay}"
|
||||||
SelectionMode="Single"
|
ContextRequested="OnChangeContextRequested"/>
|
||||||
CanUserReorderColumns="False"
|
|
||||||
CanUserResizeColumns="False"
|
|
||||||
CanUserSortColumns="False"
|
|
||||||
IsReadOnly="True"
|
|
||||||
HeadersVisibility="None"
|
|
||||||
Focusable="False"
|
|
||||||
RowHeight="26"
|
|
||||||
HorizontalScrollBarVisibility="Auto"
|
|
||||||
VerticalScrollBarVisibility="Auto"
|
|
||||||
SelectionChanged="OnDataGridSelectionChanged"
|
|
||||||
ContextRequested="OnDataGridContextRequested"
|
|
||||||
IsVisible="{Binding Source={x:Static vm:Preference.Instance}, Path=CommitChangeViewMode, Converter={x:Static c:ChangeViewModeConverters.IsList}}">
|
|
||||||
<DataGrid.Columns>
|
|
||||||
<DataGridTemplateColumn Header="ICON">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<v:ChangeStatusIcon Width="14" Height="14" IsWorkingCopyChange="False" Change="{Binding}"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
|
|
||||||
<DataGridTemplateColumn Width="*" Header="PATH">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock Classes="monospace" Text="{Binding Path}" Margin="4,0,0,0"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
</DataGrid.Columns>
|
|
||||||
</DataGrid>
|
|
||||||
|
|
||||||
<DataGrid Background="Transparent"
|
|
||||||
ItemsSource="{Binding VisibleChanges}"
|
|
||||||
SelectedItem="{Binding SelectedChange, Mode=TwoWay}"
|
|
||||||
SelectionMode="Single"
|
|
||||||
CanUserReorderColumns="False"
|
|
||||||
CanUserResizeColumns="False"
|
|
||||||
CanUserSortColumns="False"
|
|
||||||
IsReadOnly="True"
|
|
||||||
HeadersVisibility="None"
|
|
||||||
Focusable="False"
|
|
||||||
RowHeight="26"
|
|
||||||
HorizontalScrollBarVisibility="Auto"
|
|
||||||
VerticalScrollBarVisibility="Auto"
|
|
||||||
SelectionChanged="OnDataGridSelectionChanged"
|
|
||||||
ContextRequested="OnDataGridContextRequested"
|
|
||||||
IsVisible="{Binding Source={x:Static vm:Preference.Instance}, Path=CommitChangeViewMode, Converter={x:Static c:ChangeViewModeConverters.IsGrid}}">
|
|
||||||
<DataGrid.Columns>
|
|
||||||
<DataGridTemplateColumn Header="ICON">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<v:ChangeStatusIcon Width="14" Height="14" IsWorkingCopyChange="False" Change="{Binding}"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
|
|
||||||
<DataGridTemplateColumn Header="FILE_NAME">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock Classes="monospace" Text="{Binding Path, Converter={x:Static c:PathConverters.PureFileName}}" Margin="4,0,0,0"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
|
|
||||||
<DataGridTemplateColumn Header="FOLDER_PATH">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock Classes="monospace" Text="{Binding Path, Converter={x:Static c:PathConverters.PureDirectoryName}}" Margin="4,0,0,0" Foreground="{DynamicResource Brush.FG2}"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
</DataGrid.Columns>
|
|
||||||
</DataGrid>
|
|
||||||
|
|
||||||
<TreeView ItemsSource="{Binding ChangeTree}"
|
|
||||||
SelectedItem="{Binding SelectedChangeNode, Mode=TwoWay}"
|
|
||||||
AutoScrollToSelectedItem="True"
|
|
||||||
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
|
||||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
|
||||||
ContextRequested="OnTreeViewContextRequested"
|
|
||||||
IsVisible="{Binding Source={x:Static vm:Preference.Instance}, Path=CommitChangeViewMode, Converter={x:Static c:ChangeViewModeConverters.IsTree}}">
|
|
||||||
<TreeView.Styles>
|
|
||||||
<Style Selector="TreeViewItem" x:DataType="vm:FileTreeNode">
|
|
||||||
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
|
|
||||||
</Style>
|
|
||||||
</TreeView.Styles>
|
|
||||||
|
|
||||||
<TreeView.ItemTemplate>
|
|
||||||
<TreeDataTemplate ItemsSource="{Binding Children}" x:DataType="{x:Type vm:FileTreeNode}">
|
|
||||||
<Grid Height="24" ColumnDefinitions="Auto,*">
|
|
||||||
<Path Grid.Column="0" Classes="folder_icon" Width="14" Height="14" Margin="0,2,0,0" IsVisible="{Binding IsFolder}" Fill="Goldenrod" VerticalAlignment="Center"/>
|
|
||||||
<v:ChangeStatusIcon Grid.Column="0" Width="14" Height="14" IsWorkingCopyChange="False" Change="{Binding Backend}" IsVisible="{Binding !IsFolder}"/>
|
|
||||||
<TextBlock Grid.Column="1" Classes="monospace" Text="{Binding FullPath, Converter={x:Static c:PathConverters.PureFileName}}" Margin="6,0,0,0"/>
|
|
||||||
</Grid>
|
|
||||||
</TreeDataTemplate>
|
|
||||||
</TreeView.ItemTemplate>
|
|
||||||
</TreeView>
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|
|
@ -9,37 +9,15 @@ namespace SourceGit.Views
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDataGridSelectionChanged(object sender, SelectionChangedEventArgs e)
|
private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e)
|
||||||
{
|
{
|
||||||
if (sender is DataGrid datagrid && datagrid.IsVisible && datagrid.SelectedItem != null)
|
if (DataContext is ViewModels.CommitDetail vm)
|
||||||
{
|
{
|
||||||
datagrid.ScrollIntoView(datagrid.SelectedItem, null);
|
var selected = (sender as ChangeCollectionView)?.SelectedChanges;
|
||||||
}
|
if (selected != null && selected.Count == 1)
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDataGridContextRequested(object sender, ContextRequestedEventArgs e)
|
|
||||||
{
|
{
|
||||||
if (sender is DataGrid datagrid && datagrid.SelectedItem != null)
|
var menu = vm.CreateChangeContextMenu(selected[0]);
|
||||||
{
|
(sender as Control)?.OpenContextMenu(menu);
|
||||||
var detail = DataContext as ViewModels.CommitDetail;
|
|
||||||
var menu = detail.CreateChangeContextMenu(datagrid.SelectedItem as Models.Change);
|
|
||||||
datagrid.OpenContextMenu(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnTreeViewContextRequested(object sender, ContextRequestedEventArgs e)
|
|
||||||
{
|
|
||||||
if (sender is TreeView view && view.SelectedItem != null)
|
|
||||||
{
|
|
||||||
var detail = DataContext as ViewModels.CommitDetail;
|
|
||||||
var node = view.SelectedItem as ViewModels.FileTreeNode;
|
|
||||||
if (node != null && !node.IsFolder)
|
|
||||||
{
|
|
||||||
var menu = detail.CreateChangeContextMenu(node.Backend as Models.Change);
|
|
||||||
view.OpenContextMenu(menu);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,12 +17,15 @@
|
||||||
<TextBlock Classes="tab_header" Text="{DynamicResource Text.CommitDetail.Info}"/>
|
<TextBlock Classes="tab_header" Text="{DynamicResource Text.CommitDetail.Info}"/>
|
||||||
</TabItem.Header>
|
</TabItem.Header>
|
||||||
|
|
||||||
<Grid RowDefinitions="Auto,*">
|
<Grid RowDefinitions="Auto,1,*">
|
||||||
<!-- Base Information -->
|
<!-- Base Information -->
|
||||||
<v:CommitBaseInfo Grid.Row="0" Content="{Binding Commit}"/>
|
<v:CommitBaseInfo Grid.Row="0" Content="{Binding Commit}"/>
|
||||||
|
|
||||||
|
<!-- Line -->
|
||||||
|
<Rectangle Grid.Row="1" Height=".65" Margin="8" Fill="{DynamicResource Brush.Border2}" VerticalAlignment="Center"/>
|
||||||
|
|
||||||
<!-- Change List -->
|
<!-- Change List -->
|
||||||
<DataGrid Grid.Row="1"
|
<DataGrid Grid.Row="2"
|
||||||
Background="Transparent"
|
Background="Transparent"
|
||||||
ItemsSource="{Binding Changes}"
|
ItemsSource="{Binding Changes}"
|
||||||
SelectionMode="Single"
|
SelectionMode="Single"
|
||||||
|
@ -33,7 +36,7 @@
|
||||||
HeadersVisibility="None"
|
HeadersVisibility="None"
|
||||||
Focusable="False"
|
Focusable="False"
|
||||||
RowHeight="26"
|
RowHeight="26"
|
||||||
Margin="80,0,8,0"
|
Margin="80,0,8,16"
|
||||||
HorizontalScrollBarVisibility="Disabled"
|
HorizontalScrollBarVisibility="Disabled"
|
||||||
VerticalScrollBarVisibility="Auto"
|
VerticalScrollBarVisibility="Auto"
|
||||||
ContextRequested="OnChangeListContextRequested"
|
ContextRequested="OnChangeListContextRequested"
|
||||||
|
|
|
@ -16,8 +16,9 @@ namespace SourceGit.Views
|
||||||
{
|
{
|
||||||
var datagrid = sender as DataGrid;
|
var datagrid = sender as DataGrid;
|
||||||
detail.ActivePageIndex = 1;
|
detail.ActivePageIndex = 1;
|
||||||
detail.SelectedChange = datagrid.SelectedItem as Models.Change;
|
detail.SelectedChanges = new () { datagrid.SelectedItem as Models.Change };
|
||||||
}
|
}
|
||||||
|
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +36,7 @@ namespace SourceGit.Views
|
||||||
var menu = detail.CreateChangeContextMenu(datagrid.SelectedItem as Models.Change);
|
var menu = detail.CreateChangeContextMenu(datagrid.SelectedItem as Models.Change);
|
||||||
datagrid.OpenContextMenu(menu);
|
datagrid.OpenContextMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<TextBlock FontSize="18"
|
<TextBlock FontSize="18"
|
||||||
Classes="bold"
|
Classes="bold"
|
||||||
Text="{DynamicResource Text.CreateBranch}"/>
|
Text="{DynamicResource Text.CreateBranch}"/>
|
||||||
<Grid Margin="0,16,0,0" RowDefinitions="32,32,32,32" ColumnDefinitions="150,*">
|
<Grid Margin="0,16,0,0" RowDefinitions="32,32,32,32" ColumnDefinitions="140,*">
|
||||||
<TextBlock Grid.Column="0"
|
<TextBlock Grid.Column="0"
|
||||||
HorizontalAlignment="Right" VerticalAlignment="Center"
|
HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||||
Margin="0,0,8,0"
|
Margin="0,0,8,0"
|
||||||
|
@ -67,15 +67,15 @@
|
||||||
|
|
||||||
<RadioButton Content="{DynamicResource Text.CreateBranch.LocalChanges.StashAndReply}"
|
<RadioButton Content="{DynamicResource Text.CreateBranch.LocalChanges.StashAndReply}"
|
||||||
GroupName="LocalChanges"
|
GroupName="LocalChanges"
|
||||||
IsChecked="{Binding PreAction, Mode=TwoWay, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static vm:BeforeCreateBranchAction.StashAndReaply}}"/>
|
IsChecked="{Binding PreAction, Mode=TwoWay, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static m:DealWithLocalChanges.StashAndReaply}}"/>
|
||||||
<RadioButton Content="{DynamicResource Text.CreateBranch.LocalChanges.Discard}"
|
<RadioButton Content="{DynamicResource Text.CreateBranch.LocalChanges.Discard}"
|
||||||
GroupName="LocalChanges"
|
GroupName="LocalChanges"
|
||||||
Margin="8,0,0,0"
|
Margin="8,0,0,0"
|
||||||
IsChecked="{Binding PreAction, Mode=TwoWay, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static vm:BeforeCreateBranchAction.Discard}}"/>
|
IsChecked="{Binding PreAction, Mode=TwoWay, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static m:DealWithLocalChanges.Discard}}"/>
|
||||||
<RadioButton Content="{DynamicResource Text.CreateBranch.LocalChanges.DoNothing}"
|
<RadioButton Content="{DynamicResource Text.CreateBranch.LocalChanges.DoNothing}"
|
||||||
GroupName="LocalChanges"
|
GroupName="LocalChanges"
|
||||||
Margin="8,0,0,0"
|
Margin="8,0,0,0"
|
||||||
IsChecked="{Binding PreAction, Mode=TwoWay, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static vm:BeforeCreateBranchAction.DoNothing}}"/>
|
IsChecked="{Binding PreAction, Mode=TwoWay, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static m:DealWithLocalChanges.DoNothing}}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<CheckBox Grid.Row="3" Grid.Column="1"
|
<CheckBox Grid.Row="3" Grid.Column="1"
|
||||||
|
|
|
@ -126,6 +126,44 @@
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
|
<!-- Submodule Diff -->
|
||||||
|
<DataTemplate DataType="m:SubmoduleDiff">
|
||||||
|
<Grid Background="{DynamicResource Brush.Contents}" RowDefinitions="Auto,*">
|
||||||
|
<TextBlock Grid.Row="0"
|
||||||
|
Text="{DynamicResource Text.Diff.Submodule}"
|
||||||
|
Margin="8,8,8,8"
|
||||||
|
FontSize="18" FontWeight="Bold"
|
||||||
|
Foreground="{DynamicResource Brush.FG2}"
|
||||||
|
HorizontalAlignment="Center"/>
|
||||||
|
|
||||||
|
<ScrollViewer Grid.Row="1" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
|
||||||
|
<StackPanel Orientation="Vertical" Margin="8,0">
|
||||||
|
<Border IsVisible="{Binding Old, Converter={x:Static ObjectConverters.IsNotNull}}">
|
||||||
|
<ContentControl Content="{Binding Old}">
|
||||||
|
<ContentControl.DataTemplates>
|
||||||
|
<DataTemplate DataType="m:Commit">
|
||||||
|
<Border Margin="0,0,0,8" BorderThickness="1" BorderBrush="{DynamicResource Brush.Border1}" Background="{DynamicResource Brush.Window}">
|
||||||
|
<v:CommitBaseInfo MaxHeight="256" Margin="0,0,0,4" CanNavigate="False" Content="{Binding}"/>
|
||||||
|
</Border>
|
||||||
|
</DataTemplate>
|
||||||
|
</ContentControl.DataTemplates>
|
||||||
|
</ContentControl>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Border Height="16" HorizontalAlignment="Center" Background="Green" CornerRadius="8" IsVisible="{Binding Old, Converter={x:Static ObjectConverters.IsNull}}">
|
||||||
|
<TextBlock Classes="monospace" Text="{DynamicResource Text.Diff.Submodule.New}" Margin="8,0" FontSize="10"/>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Path Width="16" Height="16" Data="{StaticResource Icons.DoubleDown}" HorizontalAlignment="Center" IsVisible="{Binding Old, Converter={x:Static ObjectConverters.IsNotNull}}"/>
|
||||||
|
|
||||||
|
<Border Margin="0,8,0,0" BorderThickness="1" BorderBrush="Green" Background="{DynamicResource Brush.Window}">
|
||||||
|
<v:CommitBaseInfo MaxHeight="256" Margin="0,0,0,4" CanNavigate="False" Content="{Binding New}"/>
|
||||||
|
</Border>
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
<!-- Image Diff -->
|
<!-- Image Diff -->
|
||||||
<DataTemplate DataType="m:ImageDiff">
|
<DataTemplate DataType="m:ImageDiff">
|
||||||
<Grid RowDefinitions="Auto,*,Auto" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="8,8,8,0">
|
<Grid RowDefinitions="Auto,*,Auto" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="8,8,8,0">
|
||||||
|
@ -175,7 +213,8 @@
|
||||||
|
|
||||||
<!-- Text Diff -->
|
<!-- Text Diff -->
|
||||||
<DataTemplate DataType="m:TextDiff">
|
<DataTemplate DataType="m:TextDiff">
|
||||||
<v:TextDiffView TextDiff="{Binding}" UseSideBySideDiff="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSideBySideDiff, Mode=OneWay}"/>
|
<v:TextDiffView TextDiff="{Binding}"
|
||||||
|
UseSideBySideDiff="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSideBySideDiff, Mode=OneWay}"/>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
<!-- No or only EOL changes -->
|
<!-- No or only EOL changes -->
|
||||||
|
|
|
@ -86,7 +86,7 @@
|
||||||
<Grid Grid.Row="0" ColumnDefinitions="Auto,*,Auto,Auto">
|
<Grid Grid.Row="0" ColumnDefinitions="Auto,*,Auto,Auto">
|
||||||
<v:Avatar Grid.Column="0" Width="16" Height="16" VerticalAlignment="Center" IsHitTestVisible="False" User="{Binding Author}"/>
|
<v:Avatar Grid.Column="0" Width="16" Height="16" VerticalAlignment="Center" IsHitTestVisible="False" User="{Binding Author}"/>
|
||||||
<TextBlock Grid.Column="1" Classes="monospace" Text="{Binding Author.Name}" Margin="8,0,0,0"/>
|
<TextBlock Grid.Column="1" Classes="monospace" Text="{Binding Author.Name}" Margin="8,0,0,0"/>
|
||||||
<TextBlock Grid.Column="2" Classes="monospace" Text="{Binding SHA, Converter={x:Static c:StringConverters.ToShortSHA}}" Foreground="DarkOrange" Margin="8,0,0,0" TextDecorations="Underline" Cursor="Hand"/>
|
<TextBlock Grid.Column="2" Classes="monospace" Text="{Binding SHA, Converter={x:Static c:StringConverters.ToShortSHA}}" Foreground="DarkOrange" Margin="8,0,0,0"/>
|
||||||
<TextBlock Grid.Column="3" Classes="monospace" Text="{Binding AuthorTimeShortStr}" Foreground="{DynamicResource Brush.FG2}" Margin="8,0,0,0"/>
|
<TextBlock Grid.Column="3" Classes="monospace" Text="{Binding AuthorTimeShortStr}" Foreground="{DynamicResource Brush.FG2}" Margin="8,0,0,0"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
@ -106,10 +106,8 @@
|
||||||
BorderThickness="1,0,0,0"
|
BorderThickness="1,0,0,0"
|
||||||
BorderBrush="{DynamicResource Brush.Border2}"/>
|
BorderBrush="{DynamicResource Brush.Border2}"/>
|
||||||
|
|
||||||
<Path Grid.Column="2"
|
<v:LoadingIcon Grid.Column="2"
|
||||||
Classes="rotating"
|
|
||||||
Width="48" Height="48"
|
Width="48" Height="48"
|
||||||
Data="{DynamicResource Icons.Loading}"
|
|
||||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||||
IsVisible="{Binding IsLoading}"/>
|
IsVisible="{Binding IsLoading}"/>
|
||||||
|
|
||||||
|
|
|
@ -129,18 +129,14 @@
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
|
|
||||||
<v:CommitGraph x:Name="commitGraph"
|
<v:CommitGraph x:Name="commitGraph"
|
||||||
BindingDataGrid="{Binding #commitDataGrid}"
|
|
||||||
Graph="{Binding Graph}"
|
Graph="{Binding Graph}"
|
||||||
DotBrush="{DynamicResource Brush.Contents}"
|
DotBrush="{DynamicResource Brush.Contents}"
|
||||||
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
|
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
|
||||||
IsHitTestVisible="False"
|
IsHitTestVisible="False"
|
||||||
ClipToBounds="True"/>
|
ClipToBounds="True"/>
|
||||||
|
|
||||||
<Path Classes="rotating"
|
<!-- Fix memory leak -->
|
||||||
Width="48" Height="48"
|
<v:LoadingIcon Width="48" Height="48" HorizontalAlignment="Center" VerticalAlignment="Center" IsVisible="{Binding IsLoading}"/>
|
||||||
Data="{DynamicResource Icons.Loading}"
|
|
||||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
|
||||||
IsVisible="{Binding IsLoading}"/>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<GridSplitter Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3"
|
<GridSplitter Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3"
|
||||||
|
|
|
@ -3,7 +3,6 @@ using System;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.Primitives;
|
using Avalonia.Controls.Primitives;
|
||||||
using Avalonia.Data;
|
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using Avalonia.VisualTree;
|
using Avalonia.VisualTree;
|
||||||
|
|
||||||
|
@ -90,15 +89,6 @@ namespace SourceGit.Views
|
||||||
set => SetValue(GraphProperty, value);
|
set => SetValue(GraphProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly StyledProperty<DataGrid> BindingDataGridProperty =
|
|
||||||
AvaloniaProperty.Register<CommitGraph, DataGrid>(nameof(BindingDataGrid));
|
|
||||||
|
|
||||||
public DataGrid BindingDataGrid
|
|
||||||
{
|
|
||||||
get => GetValue(BindingDataGridProperty);
|
|
||||||
set => SetValue(BindingDataGridProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static readonly StyledProperty<IBrush> DotBrushProperty =
|
public static readonly StyledProperty<IBrush> DotBrushProperty =
|
||||||
AvaloniaProperty.Register<CommitGraph, IBrush>(nameof(DotBrush), Brushes.Transparent);
|
AvaloniaProperty.Register<CommitGraph, IBrush>(nameof(DotBrush), Brushes.Transparent);
|
||||||
|
|
||||||
|
@ -110,15 +100,16 @@ namespace SourceGit.Views
|
||||||
|
|
||||||
static CommitGraph()
|
static CommitGraph()
|
||||||
{
|
{
|
||||||
AffectsRender<CommitGraph>(BindingDataGridProperty, GraphProperty, DotBrushProperty);
|
AffectsRender<CommitGraph>(GraphProperty, DotBrushProperty);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Render(DrawingContext context)
|
public override void Render(DrawingContext context)
|
||||||
{
|
{
|
||||||
base.Render(context);
|
base.Render(context);
|
||||||
|
|
||||||
|
var parent = this.FindAncestorOfType<Histories>();
|
||||||
var graph = Graph;
|
var graph = Graph;
|
||||||
var grid = BindingDataGrid;
|
var grid = parent.commitDataGrid;
|
||||||
if (graph == null || grid == null)
|
if (graph == null || grid == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -277,8 +268,6 @@ namespace SourceGit.Views
|
||||||
public Histories()
|
public Histories()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
this.Bind(NavigationIdProperty, new Binding("NavigationId", BindingMode.OneWay));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCommitDataGridLayoutUpdated(object sender, EventArgs e)
|
private void OnCommitDataGridLayoutUpdated(object sender, EventArgs e)
|
||||||
|
@ -297,10 +286,10 @@ namespace SourceGit.Views
|
||||||
|
|
||||||
private void OnCommitDataGridContextRequested(object sender, ContextRequestedEventArgs e)
|
private void OnCommitDataGridContextRequested(object sender, ContextRequestedEventArgs e)
|
||||||
{
|
{
|
||||||
if (DataContext is ViewModels.Histories histories)
|
if (DataContext is ViewModels.Histories histories && sender is DataGrid datagrid)
|
||||||
{
|
{
|
||||||
var menu = histories.MakeContextMenu();
|
var menu = histories.MakeContextMenu(datagrid);
|
||||||
(sender as Control)?.OpenContextMenu(menu);
|
datagrid.OpenContextMenu(menu);
|
||||||
}
|
}
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,14 +121,14 @@
|
||||||
<Border.ContextMenu>
|
<Border.ContextMenu>
|
||||||
<ContextMenu>
|
<ContextMenu>
|
||||||
<MenuItem Header="{DynamicResource Text.PageTabBar.Tab.Close}"
|
<MenuItem Header="{DynamicResource Text.PageTabBar.Tab.Close}"
|
||||||
Command="{Binding #me.DataContext.(vm:Launcher).CloseTab}"
|
Command="{Binding #me.((vm:Launcher)DataContext).CloseTab}"
|
||||||
CommandParameter="{Binding}"
|
CommandParameter="{Binding}"
|
||||||
InputGesture="{OnPlatform Ctrl+W, macOS=⌘+W}"/>
|
InputGesture="{OnPlatform Ctrl+W, macOS=⌘+W}"/>
|
||||||
<MenuItem Header="{DynamicResource Text.PageTabBar.Tab.CloseOther}"
|
<MenuItem Header="{DynamicResource Text.PageTabBar.Tab.CloseOther}"
|
||||||
Command="{Binding #me.DataContext.(vm:Launcher).CloseOtherTabs}"
|
Command="{Binding #me.((vm:Launcher)DataContext).CloseOtherTabs}"
|
||||||
CommandParameter="{Binding}"/>
|
CommandParameter="{Binding}"/>
|
||||||
<MenuItem Header="{DynamicResource Text.PageTabBar.Tab.CloseRight}"
|
<MenuItem Header="{DynamicResource Text.PageTabBar.Tab.CloseRight}"
|
||||||
Command="{Binding #me.DataContext.(vm:Launcher).CloseRightTabs}"
|
Command="{Binding #me.((vm:Launcher)DataContext).CloseRightTabs}"
|
||||||
CommandParameter="{Binding}"/>
|
CommandParameter="{Binding}"/>
|
||||||
<MenuItem Header="-" IsVisible="{Binding Node.IsRepository}"/>
|
<MenuItem Header="-" IsVisible="{Binding Node.IsRepository}"/>
|
||||||
<MenuItem IsVisible="{Binding Node.IsRepository}">
|
<MenuItem IsVisible="{Binding Node.IsRepository}">
|
||||||
|
@ -166,7 +166,7 @@
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
</Border.ContextMenu>
|
</Border.ContextMenu>
|
||||||
|
|
||||||
<v:LauncherTab UseFixedTabWidth="{Binding Source={x:Static vm:Preference.Instance}, Path=UseFixedTabWidth}" Height="30" ColumnDefinitions="Auto,*,Auto" VerticalAlignment="Center">
|
<Grid Width="{Binding Source={x:Static vm:Preference.Instance}, Path=UseFixedTabWidth, Converter={x:Static c:BoolConverters.ToPageTabWidth}}" Height="30" ColumnDefinitions="Auto,*,Auto" VerticalAlignment="Center">
|
||||||
<Path Grid.Column="0"
|
<Path Grid.Column="0"
|
||||||
Width="12" Height="12" Margin="12,0"
|
Width="12" Height="12" Margin="12,0"
|
||||||
Fill="{Binding Node.Bookmark, Converter={x:Static c:BookmarkConverters.ToBrush}}"
|
Fill="{Binding Node.Bookmark, Converter={x:Static c:BookmarkConverters.ToBrush}}"
|
||||||
|
@ -200,7 +200,7 @@
|
||||||
<Button Grid.Column="2"
|
<Button Grid.Column="2"
|
||||||
Classes="icon_button"
|
Classes="icon_button"
|
||||||
Width="16" Height="16" Margin="12,0"
|
Width="16" Height="16" Margin="12,0"
|
||||||
Command="{Binding #me.DataContext.(vm:Launcher).CloseTab}"
|
Command="{Binding #me.((vm:Launcher)DataContext).CloseTab}"
|
||||||
CommandParameter="{Binding}">
|
CommandParameter="{Binding}">
|
||||||
<ToolTip.Tip>
|
<ToolTip.Tip>
|
||||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||||
|
@ -215,11 +215,11 @@
|
||||||
<MultiBinding Converter="{x:Static c:LauncherPageConverters.ToTabSeperatorVisible}">
|
<MultiBinding Converter="{x:Static c:LauncherPageConverters.ToTabSeperatorVisible}">
|
||||||
<Binding/>
|
<Binding/>
|
||||||
<Binding Path="$parent[ListBox].SelectedItem"/>
|
<Binding Path="$parent[ListBox].SelectedItem"/>
|
||||||
<Binding Path="#me.DataContext.(vm:Launcher).Pages"/>
|
<Binding Path="#me.((vm:Launcher)DataContext).Pages"/>
|
||||||
</MultiBinding>
|
</MultiBinding>
|
||||||
</Rectangle.IsVisible>
|
</Rectangle.IsVisible>
|
||||||
</Rectangle>
|
</Rectangle>
|
||||||
</v:LauncherTab>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListBox.ItemTemplate>
|
</ListBox.ItemTemplate>
|
||||||
|
@ -253,7 +253,17 @@
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<!-- Page body -->
|
<!-- Page body -->
|
||||||
<v:LauncherBody Grid.Row="1" Background="{DynamicResource Brush.ToolBar}" Data="{Binding ActivePage.Data}"/>
|
<ContentControl Grid.Row="1" Background="{DynamicResource Brush.ToolBar}" Content="{Binding ActivePage.Data}">
|
||||||
|
<ContentControl.DataTemplates>
|
||||||
|
<DataTemplate DataType="vm:Welcome">
|
||||||
|
<v:Welcome/>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
|
<DataTemplate DataType="vm:Repository">
|
||||||
|
<v:Repository/>
|
||||||
|
</DataTemplate>
|
||||||
|
</ContentControl.DataTemplates>
|
||||||
|
</ContentControl>
|
||||||
|
|
||||||
<!-- Popup container -->
|
<!-- Popup container -->
|
||||||
<Grid Grid.Row="1" Margin="0,36,0,0" IsVisible="{Binding ActivePage.Popup, Converter={x:Static ObjectConverters.IsNotNull}}">
|
<Grid Grid.Row="1" Margin="0,36,0,0" IsVisible="{Binding ActivePage.Popup, Converter={x:Static ObjectConverters.IsNotNull}}">
|
||||||
|
@ -275,11 +285,7 @@
|
||||||
</Border.BorderThickness>
|
</Border.BorderThickness>
|
||||||
|
|
||||||
<Border.Effect>
|
<Border.Effect>
|
||||||
<OnPlatform>
|
<OnPlatform Default="drop-shadow(0 0 8 #8F000000)" Linux="{x:Null}"/>
|
||||||
<On Options="Windows, macOS">
|
|
||||||
<DropShadowEffect BlurRadius="8" OffsetX="0" OffsetY="0" Color="Black" Opacity=".5"/>
|
|
||||||
</On>
|
|
||||||
</OnPlatform>
|
|
||||||
</Border.Effect>
|
</Border.Effect>
|
||||||
|
|
||||||
<StackPanel Margin="8" Orientation="Vertical">
|
<StackPanel Margin="8" Orientation="Vertical">
|
||||||
|
@ -307,23 +313,9 @@
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<!-- Running -->
|
<!-- Running -->
|
||||||
<StackPanel Orientation="Vertical" Margin="8" IsVisible="{Binding InProgress}">
|
<v:PopupRunningStatus Margin="8"
|
||||||
<Rectangle Height="1" Margin="-8,0" HorizontalAlignment="Stretch" Fill="{DynamicResource Brush.Border1}" />
|
Description="{Binding ProgressDescription}"
|
||||||
<StackPanel Orientation="Horizontal" Margin="0,8">
|
IsVisible="{Binding InProgress}"/>
|
||||||
<Path Width="12" Height="12" Classes="waiting" Data="{StaticResource Icons.Waiting}" IsVisible="{Binding InProgress}"/>
|
|
||||||
<TextBlock Margin="6,0,0,0" FontSize="14" FontWeight="Bold" Text="{DynamicResource Text.Running}"/>
|
|
||||||
</StackPanel>
|
|
||||||
<TextBlock HorizontalAlignment="Stretch"
|
|
||||||
TextWrapping="Wrap"
|
|
||||||
FontSize="{Binding Source={x:Static vm:Preference.Instance}, Path=DefaultFontSize, Converter={x:Static c:FontSizeModifyConverters.Decrease}}"
|
|
||||||
FontStyle="Italic"
|
|
||||||
Text="{Binding ProgressDescription}"/>
|
|
||||||
<ProgressBar Margin="0,8,0,0"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
IsIndeterminate="True"
|
|
||||||
Background="{DynamicResource Brush.FG2}" Foreground="{DynamicResource Brush.Accent}"
|
|
||||||
Minimum="0" Maximum="100"/>
|
|
||||||
</StackPanel>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
@ -360,11 +352,11 @@
|
||||||
Margin="8" Padding="0"
|
Margin="8" Padding="0"
|
||||||
VerticalContentAlignment="Top"
|
VerticalContentAlignment="Top"
|
||||||
Text="{Binding Message}"/>
|
Text="{Binding Message}"/>
|
||||||
<Button Grid.Row="3"
|
<Button Grid.Row="2"
|
||||||
Classes="flat primary"
|
Classes="flat primary"
|
||||||
Height="30"
|
Height="30"
|
||||||
Margin="4,0"
|
Margin="4,0"
|
||||||
Command="{Binding #me.DataContext.(vm:Launcher).ActivePage.DismissNotification}"
|
Command="{Binding #me.((vm:Launcher)DataContext).ActivePage.DismissNotification}"
|
||||||
CommandParameter="{Binding}"
|
CommandParameter="{Binding}"
|
||||||
Content="{DynamicResource Text.Close}"
|
Content="{DynamicResource Text.Close}"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
|
|
|
@ -7,67 +7,6 @@ using Avalonia.Interactivity;
|
||||||
|
|
||||||
namespace SourceGit.Views
|
namespace SourceGit.Views
|
||||||
{
|
{
|
||||||
public class LauncherTab : Grid
|
|
||||||
{
|
|
||||||
public static readonly StyledProperty<bool> UseFixedTabWidthProperty =
|
|
||||||
AvaloniaProperty.Register<LauncherTab, bool>(nameof(UseFixedTabWidth), false);
|
|
||||||
|
|
||||||
public bool UseFixedTabWidth
|
|
||||||
{
|
|
||||||
get => GetValue(UseFixedTabWidthProperty);
|
|
||||||
set => SetValue(UseFixedTabWidthProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Type StyleKeyOverride => typeof(Grid);
|
|
||||||
|
|
||||||
static LauncherTab()
|
|
||||||
{
|
|
||||||
UseFixedTabWidthProperty.Changed.AddClassHandler<LauncherTab>((tab, ev) =>
|
|
||||||
{
|
|
||||||
tab.Width = tab.UseFixedTabWidth ? 200.0 : double.NaN;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class LauncherBody : Border
|
|
||||||
{
|
|
||||||
public static readonly StyledProperty<object> DataProperty =
|
|
||||||
AvaloniaProperty.Register<LauncherBody, object>(nameof(Data), false);
|
|
||||||
|
|
||||||
public object Data
|
|
||||||
{
|
|
||||||
get => GetValue(DataProperty);
|
|
||||||
set => SetValue(DataProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Type StyleKeyOverride => typeof(Border);
|
|
||||||
|
|
||||||
static LauncherBody()
|
|
||||||
{
|
|
||||||
DataProperty.Changed.AddClassHandler<LauncherBody>((body, ev) =>
|
|
||||||
{
|
|
||||||
var data = body.Data;
|
|
||||||
|
|
||||||
if (data == null)
|
|
||||||
{
|
|
||||||
body.Child = null;
|
|
||||||
}
|
|
||||||
else if (data is ViewModels.Welcome)
|
|
||||||
{
|
|
||||||
body.Child = new Welcome { DataContext = data };
|
|
||||||
}
|
|
||||||
else if (data is ViewModels.Repository)
|
|
||||||
{
|
|
||||||
body.Child = new Repository { DataContext = data };
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
body.Child = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial class Launcher : Window, Models.INotificationReceiver
|
public partial class Launcher : Window, Models.INotificationReceiver
|
||||||
{
|
{
|
||||||
public Launcher()
|
public Launcher()
|
||||||
|
|
8
src/Views/LoadingIcon.axaml
Normal file
8
src/Views/LoadingIcon.axaml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<UserControl 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"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
xmlns:m="using:SourceGit.Models"
|
||||||
|
x:Class="SourceGit.Views.LoadingIcon">
|
||||||
|
</UserControl>
|
61
src/Views/LoadingIcon.axaml.cs
Normal file
61
src/Views/LoadingIcon.axaml.cs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Shapes;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
|
using Avalonia.Media;
|
||||||
|
|
||||||
|
namespace SourceGit.Views
|
||||||
|
{
|
||||||
|
public partial class LoadingIcon : UserControl
|
||||||
|
{
|
||||||
|
public LoadingIcon()
|
||||||
|
{
|
||||||
|
IsHitTestVisible = false;
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnLoaded(RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnLoaded(e);
|
||||||
|
|
||||||
|
if (IsVisible)
|
||||||
|
StartAnim();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnUnloaded(RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
StopAnim();
|
||||||
|
base.OnUnloaded(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||||
|
{
|
||||||
|
base.OnPropertyChanged(change);
|
||||||
|
|
||||||
|
if (change.Property == IsVisibleProperty)
|
||||||
|
{
|
||||||
|
if (IsVisible)
|
||||||
|
StartAnim();
|
||||||
|
else
|
||||||
|
StopAnim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StartAnim()
|
||||||
|
{
|
||||||
|
Content = new Path()
|
||||||
|
{
|
||||||
|
Data = this.FindResource("Icons.Loading") as StreamGeometry,
|
||||||
|
Classes = { "rotating" },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StopAnim()
|
||||||
|
{
|
||||||
|
if (Content is Path path)
|
||||||
|
path.Classes.Clear();
|
||||||
|
|
||||||
|
Content = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
src/Views/PopupRunningStatus.axaml
Normal file
31
src/Views/PopupRunningStatus.axaml
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
<UserControl 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:vm="using:SourceGit.ViewModels"
|
||||||
|
xmlns:c="using:SourceGit.Converters"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="SourceGit.Views.PopupRunningStatus"
|
||||||
|
x:Name="me">
|
||||||
|
<StackPanel Orientation="Vertical">
|
||||||
|
<Rectangle Height="1" Margin="-8,0" HorizontalAlignment="Stretch" Fill="{DynamicResource Brush.Border1}" />
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="0,8">
|
||||||
|
<ContentPresenter x:Name="icon" Width="12" Height="12"/>
|
||||||
|
<TextBlock Margin="6,0,0,0" FontSize="14" FontWeight="Bold" Text="{DynamicResource Text.Running}"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<TextBlock x:Name="txtDesc"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
FontSize="{Binding Source={x:Static vm:Preference.Instance}, Path=DefaultFontSize, Converter={x:Static c:FontSizeModifyConverters.Decrease}}"
|
||||||
|
FontStyle="Italic"
|
||||||
|
Text="{Binding #me.Description}"/>
|
||||||
|
|
||||||
|
<ProgressBar x:Name="progressBar"
|
||||||
|
Margin="0,8,0,0"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Background="{DynamicResource Brush.FG2}" Foreground="{DynamicResource Brush.Accent}"
|
||||||
|
Minimum="0" Maximum="100"/>
|
||||||
|
</StackPanel>
|
||||||
|
</UserControl>
|
70
src/Views/PopupRunningStatus.axaml.cs
Normal file
70
src/Views/PopupRunningStatus.axaml.cs
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Shapes;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
|
using Avalonia.Media;
|
||||||
|
|
||||||
|
namespace SourceGit.Views
|
||||||
|
{
|
||||||
|
public partial class PopupRunningStatus : UserControl
|
||||||
|
{
|
||||||
|
public static readonly StyledProperty<string> DescriptionProperty =
|
||||||
|
AvaloniaProperty.Register<PopupRunningStatus, string>(nameof(Description));
|
||||||
|
|
||||||
|
public string Description
|
||||||
|
{
|
||||||
|
get => GetValue(DescriptionProperty);
|
||||||
|
set => SetValue(DescriptionProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PopupRunningStatus()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnLoaded(RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnLoaded(e);
|
||||||
|
|
||||||
|
if (IsVisible)
|
||||||
|
StartAnim();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnUnloaded(RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
StopAnim();
|
||||||
|
base.OnUnloaded(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||||
|
{
|
||||||
|
base.OnPropertyChanged(change);
|
||||||
|
|
||||||
|
if (change.Property == IsVisibleProperty)
|
||||||
|
{
|
||||||
|
if (IsVisible)
|
||||||
|
StartAnim();
|
||||||
|
else
|
||||||
|
StopAnim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StartAnim()
|
||||||
|
{
|
||||||
|
icon.Content = new Path()
|
||||||
|
{
|
||||||
|
Data = this.FindResource("Icons.Waiting") as StreamGeometry,
|
||||||
|
Classes = { "waiting" },
|
||||||
|
};
|
||||||
|
progressBar.IsIndeterminate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StopAnim()
|
||||||
|
{
|
||||||
|
if (icon.Content is Path path)
|
||||||
|
path.Classes.Clear();
|
||||||
|
icon.Content = null;
|
||||||
|
progressBar.IsIndeterminate = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@
|
||||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||||
xmlns:m="using:SourceGit.Models"
|
xmlns:m="using:SourceGit.Models"
|
||||||
xmlns:c="using:SourceGit.Converters"
|
xmlns:c="using:SourceGit.Converters"
|
||||||
|
xmlns:ac="using:Avalonia.Controls.Converters"
|
||||||
xmlns:vm="using:SourceGit.ViewModels"
|
xmlns:vm="using:SourceGit.ViewModels"
|
||||||
xmlns:v="using:SourceGit.Views"
|
xmlns:v="using:SourceGit.Views"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
@ -251,8 +252,18 @@
|
||||||
Text="{DynamicResource Text.Preference.Git.Version}"
|
Text="{DynamicResource Text.Preference.Git.Version}"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
Margin="0,0,16,0"/>
|
Margin="0,0,16,0"/>
|
||||||
<TextBlock Grid.Row="1" Grid.Column="1"
|
<StackPanel Grid.Row="1" Grid.Column="1" Orientation="Horizontal">
|
||||||
x:Name="txtVersion"/>
|
<TextBlock Classes="monospace"
|
||||||
|
Margin="0,0,8,0"
|
||||||
|
Text="{Binding #me.GitVersion}"
|
||||||
|
IsVisible="{Binding #me.GitVersion, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
|
||||||
|
|
||||||
|
<Border Background="Transparent"
|
||||||
|
ToolTip.Tip="{DynamicResource Text.Preference.Git.Invalid}"
|
||||||
|
IsVisible="{Binding #me.GitVersion, Converter={x:Static c:StringConverters.UnderRecommendGitVersion}}">
|
||||||
|
<Path Width="14" Height="14" Data="{StaticResource Icons.Error}" Fill="Red"/>
|
||||||
|
</Border>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
<Border Grid.Row="2" Grid.Column="0"
|
<Border Grid.Row="2" Grid.Column="0"
|
||||||
Height="32"
|
Height="32"
|
||||||
|
@ -394,15 +405,40 @@
|
||||||
<TextBlock Classes="tab_header" Text="{DynamicResource Text.Preference.GPG}"/>
|
<TextBlock Classes="tab_header" Text="{DynamicResource Text.Preference.GPG}"/>
|
||||||
</TabItem.Header>
|
</TabItem.Header>
|
||||||
|
|
||||||
<Grid Margin="8" RowDefinitions="32,32,32" ColumnDefinitions="Auto,*">
|
<TabItem.Resources>
|
||||||
|
<ac:EnumToBoolConverter x:Key="EnumToBoolConverter"/>
|
||||||
|
</TabItem.Resources>
|
||||||
|
|
||||||
|
<Grid Margin="8" RowDefinitions="32,32,32,32,32" ColumnDefinitions="Auto,*">
|
||||||
<TextBlock Grid.Row="0" Grid.Column="0"
|
<TextBlock Grid.Row="0" Grid.Column="0"
|
||||||
|
Text="{DynamicResource Text.Preference.GPG.Format}"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Margin="0,0,16,0"/>
|
||||||
|
<ComboBox Grid.Row="0" Grid.Column="1"
|
||||||
|
MinHeight="28"
|
||||||
|
Padding="8,0"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
ItemsSource="{Binding Source={x:Static m:GPGFormat.Supported}}"
|
||||||
|
SelectedItem="{Binding #me.GPGFormat, Mode=TwoWay, FallbackValue={x:Static m:GPGFormat.OPENPGP}}">
|
||||||
|
<ComboBox.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="{x:Type m:GPGFormat}">
|
||||||
|
<Grid ColumnDefinitions="Auto,*">
|
||||||
|
<TextBlock Grid.Column="0" Text="{Binding Name}"/>
|
||||||
|
<TextBlock Grid.Column="1" Text="{Binding Desc}" Foreground="{DynamicResource Brush.FG2}" HorizontalAlignment="Right"/>
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</ComboBox.ItemTemplate>
|
||||||
|
</ComboBox>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1" Grid.Column="0"
|
||||||
Text="{DynamicResource Text.Preference.GPG.Path}"
|
Text="{DynamicResource Text.Preference.GPG.Path}"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
Margin="0,0,16,0"/>
|
Margin="0,0,16,0"/>
|
||||||
<TextBox Grid.Row="0" Grid.Column="1"
|
<TextBox Grid.Row="1" Grid.Column="1"
|
||||||
Height="28"
|
Height="28"
|
||||||
CornerRadius="3"
|
CornerRadius="3"
|
||||||
Text="{Binding #me.GPGExecutableFile, Mode=TwoWay}">
|
Text="{Binding #me.GPGExecutableFile, Mode=TwoWay}"
|
||||||
|
IsEnabled="{Binding #me.GPGFormat, Mode=TwoWay, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static m:GPGFormat.OPENPGP}}">
|
||||||
<TextBox.InnerRightContent>
|
<TextBox.InnerRightContent>
|
||||||
<Button Classes="icon_button" Width="30" Height="30" Click="SelectGPGExecutable">
|
<Button Classes="icon_button" Width="30" Height="30" Click="SelectGPGExecutable">
|
||||||
<Path Data="{StaticResource Icons.Folder.Open}" Fill="{DynamicResource Brush.FG1}"/>
|
<Path Data="{StaticResource Icons.Folder.Open}" Fill="{DynamicResource Brush.FG1}"/>
|
||||||
|
@ -410,19 +446,23 @@
|
||||||
</TextBox.InnerRightContent>
|
</TextBox.InnerRightContent>
|
||||||
</TextBox>
|
</TextBox>
|
||||||
|
|
||||||
<TextBlock Grid.Row="1" Grid.Column="0"
|
<TextBlock Grid.Row="2" Grid.Column="0"
|
||||||
Text="{DynamicResource Text.Preference.GPG.UserKey}"
|
Text="{DynamicResource Text.Preference.GPG.UserKey}"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
Margin="0,0,16,0"/>
|
Margin="0,0,16,0"/>
|
||||||
<TextBox Grid.Row="1" Grid.Column="1"
|
<TextBox Grid.Row="2" Grid.Column="1"
|
||||||
Height="28"
|
Height="28"
|
||||||
CornerRadius="3"
|
CornerRadius="3"
|
||||||
Text="{Binding #me.GPGUserKey, Mode=TwoWay}"
|
Text="{Binding #me.GPGUserKey, Mode=TwoWay}"
|
||||||
Watermark="{DynamicResource Text.Preference.GPG.UserKey.Placeholder}"/>
|
Watermark="{DynamicResource Text.Preference.GPG.UserKey.Placeholder}"/>
|
||||||
|
|
||||||
<CheckBox Grid.Row="2" Grid.Column="1"
|
<CheckBox Grid.Row="3" Grid.Column="1"
|
||||||
Content="{DynamicResource Text.Preference.GPG.Enabled}"
|
Content="{DynamicResource Text.Preference.GPG.CommitEnabled}"
|
||||||
IsChecked="{Binding #me.EnableGPGSigning, Mode=TwoWay}"/>
|
IsChecked="{Binding #me.EnableGPGCommitSigning, Mode=TwoWay}"/>
|
||||||
|
|
||||||
|
<CheckBox Grid.Row="4" Grid.Column="1"
|
||||||
|
Content="{DynamicResource Text.Preference.GPG.TagEnabled}"
|
||||||
|
IsChecked="{Binding #me.EnableGPGTagSigning, Mode=TwoWay}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|
||||||
|
|
|
@ -45,12 +45,36 @@ namespace SourceGit.Views
|
||||||
set;
|
set;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool EnableGPGSigning
|
public static readonly StyledProperty<string> GitVersionProperty =
|
||||||
|
AvaloniaProperty.Register<Preference, string>(nameof(GitVersion));
|
||||||
|
|
||||||
|
public string GitVersion
|
||||||
|
{
|
||||||
|
get => GetValue(GitVersionProperty);
|
||||||
|
set => SetValue(GitVersionProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool EnableGPGCommitSigning
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
set;
|
set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool EnableGPGTagSigning
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<Models.GPGFormat> GPGFormatProperty =
|
||||||
|
AvaloniaProperty.Register<Preference, Models.GPGFormat>(nameof(GPGFormat));
|
||||||
|
|
||||||
|
public Models.GPGFormat GPGFormat
|
||||||
|
{
|
||||||
|
get => GetValue(GPGFormatProperty);
|
||||||
|
set => SetValue(GPGFormatProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
public static readonly StyledProperty<string> GPGExecutableFileProperty =
|
public static readonly StyledProperty<string> GPGExecutableFileProperty =
|
||||||
AvaloniaProperty.Register<Preference, string>(nameof(GPGExecutableFile));
|
AvaloniaProperty.Register<Preference, string>(nameof(GPGExecutableFile));
|
||||||
|
|
||||||
|
@ -131,8 +155,14 @@ namespace SourceGit.Views
|
||||||
GPGUserKey = signingKey;
|
GPGUserKey = signingKey;
|
||||||
if (config.TryGetValue("core.autocrlf", out var crlf))
|
if (config.TryGetValue("core.autocrlf", out var crlf))
|
||||||
CRLFMode = Models.CRLFMode.Supported.Find(x => x.Value == crlf);
|
CRLFMode = Models.CRLFMode.Supported.Find(x => x.Value == crlf);
|
||||||
if (config.TryGetValue("commit.gpgsign", out var gpgsign))
|
if (config.TryGetValue("commit.gpgsign", out var gpgCommitSign))
|
||||||
EnableGPGSigning = (gpgsign == "true");
|
EnableGPGCommitSigning = (gpgCommitSign == "true");
|
||||||
|
if (config.TryGetValue("tag.gpgSign", out var gpgTagSign))
|
||||||
|
EnableGPGTagSigning = (gpgTagSign == "true");
|
||||||
|
if (config.TryGetValue("gpg.format", out var gpgFormat))
|
||||||
|
GPGFormat = Models.GPGFormat.Supported.Find(x => x.Value == gpgFormat);
|
||||||
|
else
|
||||||
|
GPGFormat = Models.GPGFormat.OPENPGP;
|
||||||
if (config.TryGetValue("gpg.program", out var gpgProgram))
|
if (config.TryGetValue("gpg.program", out var gpgProgram))
|
||||||
GPGExecutableFile = gpgProgram;
|
GPGExecutableFile = gpgProgram;
|
||||||
|
|
||||||
|
@ -140,7 +170,7 @@ namespace SourceGit.Views
|
||||||
}
|
}
|
||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
txtVersion.Text = ver;
|
GitVersion = ver;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BeginMoveWindow(object sender, PointerPressedEventArgs e)
|
private void BeginMoveWindow(object sender, PointerPressedEventArgs e)
|
||||||
|
@ -157,7 +187,9 @@ namespace SourceGit.Views
|
||||||
var oldEmail = config.TryGetValue("user.email", out var email) ? email : string.Empty;
|
var oldEmail = config.TryGetValue("user.email", out var email) ? email : string.Empty;
|
||||||
var oldGPGSignKey = config.TryGetValue("user.signingkey", out var signingKey) ? signingKey : string.Empty;
|
var oldGPGSignKey = config.TryGetValue("user.signingkey", out var signingKey) ? signingKey : string.Empty;
|
||||||
var oldCRLF = config.TryGetValue("core.autocrlf", out var crlf) ? crlf : string.Empty;
|
var oldCRLF = config.TryGetValue("core.autocrlf", out var crlf) ? crlf : string.Empty;
|
||||||
var oldGPGSignEnable = config.TryGetValue("commit.gpgsign", out var gpgsign) ? gpgsign : "false";
|
var oldGPGFormat = config.TryGetValue("gpg.format", out var gpgFormat) ? gpgFormat : Models.GPGFormat.OPENPGP.Value;
|
||||||
|
var oldGPGCommitSignEnable = config.TryGetValue("commit.gpgsign", out var gpgCommitSign) ? gpgCommitSign : "false";
|
||||||
|
var oldGPGTagSignEnable = config.TryGetValue("tag.gpgSign", out var gpgTagSign) ? gpgTagSign : "false";
|
||||||
var oldGPGExec = config.TryGetValue("gpg.program", out var program) ? program : string.Empty;
|
var oldGPGExec = config.TryGetValue("gpg.program", out var program) ? program : string.Empty;
|
||||||
|
|
||||||
if (DefaultUser != oldUser)
|
if (DefaultUser != oldUser)
|
||||||
|
@ -168,8 +200,12 @@ namespace SourceGit.Views
|
||||||
cmd.Set("user.signingkey", GPGUserKey);
|
cmd.Set("user.signingkey", GPGUserKey);
|
||||||
if (CRLFMode != null && CRLFMode.Value != oldCRLF)
|
if (CRLFMode != null && CRLFMode.Value != oldCRLF)
|
||||||
cmd.Set("core.autocrlf", CRLFMode.Value);
|
cmd.Set("core.autocrlf", CRLFMode.Value);
|
||||||
if (EnableGPGSigning != (oldGPGSignEnable == "true"))
|
if (EnableGPGCommitSigning != (oldGPGCommitSignEnable == "true"))
|
||||||
cmd.Set("commit.gpgsign", EnableGPGSigning ? "true" : "false");
|
cmd.Set("commit.gpgsign", EnableGPGCommitSigning ? "true" : "false");
|
||||||
|
if (EnableGPGTagSigning != (oldGPGTagSignEnable == "true"))
|
||||||
|
cmd.Set("tag.gpgSign", EnableGPGTagSigning ? "true" : "false");
|
||||||
|
if (GPGFormat != null && GPGFormat.Value != oldGPGFormat)
|
||||||
|
cmd.Set("gpg.format", GPGFormat.Value);
|
||||||
if (GPGExecutableFile != oldGPGExec)
|
if (GPGExecutableFile != oldGPGExec)
|
||||||
cmd.Set("gpg.program", GPGExecutableFile);
|
cmd.Set("gpg.program", GPGExecutableFile);
|
||||||
|
|
||||||
|
@ -189,7 +225,7 @@ namespace SourceGit.Views
|
||||||
if (selected.Count == 1)
|
if (selected.Count == 1)
|
||||||
{
|
{
|
||||||
ViewModels.Preference.Instance.GitInstallPath = selected[0].Path.LocalPath;
|
ViewModels.Preference.Instance.GitInstallPath = selected[0].Path.LocalPath;
|
||||||
txtVersion.Text = new Commands.Version().Query();
|
GitVersion = new Commands.Version().Query();
|
||||||
}
|
}
|
||||||
|
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
xmlns:m="using:SourceGit.Models"
|
xmlns:m="using:SourceGit.Models"
|
||||||
xmlns:vm="using:SourceGit.ViewModels"
|
xmlns:vm="using:SourceGit.ViewModels"
|
||||||
xmlns:c="using:SourceGit.Converters"
|
xmlns:c="using:SourceGit.Converters"
|
||||||
|
xmlns:ac="using:Avalonia.Controls.Converters"
|
||||||
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450"
|
||||||
x:Class="SourceGit.Views.Pull"
|
x:Class="SourceGit.Views.Pull"
|
||||||
x:DataType="vm:Pull">
|
x:DataType="vm:Pull">
|
||||||
|
@ -12,7 +13,7 @@
|
||||||
<TextBlock FontSize="18"
|
<TextBlock FontSize="18"
|
||||||
Classes="bold"
|
Classes="bold"
|
||||||
Text="{DynamicResource Text.Pull.Title}"/>
|
Text="{DynamicResource Text.Pull.Title}"/>
|
||||||
<Grid Margin="0,16,0,0" RowDefinitions="32,32,32,32,32" ColumnDefinitions="150,*">
|
<Grid Margin="0,16,0,0" RowDefinitions="32,32,32,32,32" ColumnDefinitions="140,*">
|
||||||
<TextBlock Grid.Column="0"
|
<TextBlock Grid.Column="0"
|
||||||
HorizontalAlignment="Right" VerticalAlignment="Center"
|
HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||||
Margin="0,0,8,0"
|
Margin="0,0,8,0"
|
||||||
|
@ -58,17 +59,35 @@
|
||||||
Margin="0,0,8,0"
|
Margin="0,0,8,0"
|
||||||
Text="{DynamicResource Text.Pull.Into}"/>
|
Text="{DynamicResource Text.Pull.Into}"/>
|
||||||
<StackPanel Grid.Row="2" Grid.Column="1" Orientation="Horizontal" Height="20" VerticalAlignment="Center">
|
<StackPanel Grid.Row="2" Grid.Column="1" Orientation="Horizontal" Height="20" VerticalAlignment="Center">
|
||||||
<Path Margin="0,0,8,0" Width="14" Height="14" Fill="{DynamicResource Brush.FG1}" Data="{StaticResource Icons.Branch}"/>
|
<Path Margin="2,0,8,0" Width="14" Height="14" Fill="{DynamicResource Brush.FG1}" Data="{StaticResource Icons.Branch}"/>
|
||||||
<TextBlock Text="{Binding Current.Name}"/>
|
<TextBlock Text="{Binding Current.Name}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<CheckBox Grid.Row="3" Grid.Column="1"
|
<TextBlock Grid.Row="3" Grid.Column="0"
|
||||||
Content="{DynamicResource Text.Pull.UseRebase}"
|
HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||||
IsChecked="{Binding UseRebase, Mode=TwoWay}"/>
|
Margin="0,0,8,0"
|
||||||
|
Text="{DynamicResource Text.Pull.LocalChanges}"/>
|
||||||
|
<StackPanel Grid.Row="3" Grid.Column="1" Orientation="Horizontal">
|
||||||
|
<StackPanel.Resources>
|
||||||
|
<ac:EnumToBoolConverter x:Key="EnumToBoolConverter"/>
|
||||||
|
</StackPanel.Resources>
|
||||||
|
|
||||||
|
<RadioButton Content="{DynamicResource Text.Pull.LocalChanges.StashAndReply}"
|
||||||
|
GroupName="LocalChanges"
|
||||||
|
IsChecked="{Binding PreAction, Mode=TwoWay, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static m:DealWithLocalChanges.StashAndReaply}}"/>
|
||||||
|
<RadioButton Content="{DynamicResource Text.Pull.LocalChanges.Discard}"
|
||||||
|
GroupName="LocalChanges"
|
||||||
|
Margin="8,0,0,0"
|
||||||
|
IsChecked="{Binding PreAction, Mode=TwoWay, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static m:DealWithLocalChanges.Discard}}"/>
|
||||||
|
<RadioButton Content="{DynamicResource Text.Pull.LocalChanges.DoNothing}"
|
||||||
|
GroupName="LocalChanges"
|
||||||
|
Margin="8,0,0,0"
|
||||||
|
IsChecked="{Binding PreAction, Mode=TwoWay, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static m:DealWithLocalChanges.DoNothing}}"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
<CheckBox Grid.Row="4" Grid.Column="1"
|
<CheckBox Grid.Row="4" Grid.Column="1"
|
||||||
Content="{DynamicResource Text.Pull.AutoStash}"
|
Content="{DynamicResource Text.Pull.UseRebase}"
|
||||||
IsChecked="{Binding AutoStash, Mode=TwoWay}"/>
|
IsChecked="{Binding UseRebase, Mode=TwoWay}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
<Path Width="13" Height="13" Data="{StaticResource Icons.Terminal}"/>
|
<Path Width="13" Height="13" Data="{StaticResource Icons.Terminal}"/>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button Classes="icon_button" Width="32" Click="OnOpenWithExternalTools" ToolTip.Tip="{DynamicResource Text.Repository.OpenWithExternalTools}">
|
<Button Classes="icon_button" Width="32" Click="OpenWithExternalTools" ToolTip.Tip="{DynamicResource Text.Repository.OpenWithExternalTools}">
|
||||||
<Path Width="13" Height="13" Data="{StaticResource Icons.OpenWith}"/>
|
<Path Width="13" Height="13" Data="{StaticResource Icons.OpenWith}"/>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
@ -222,7 +222,7 @@
|
||||||
</TreeView.Styles>
|
</TreeView.Styles>
|
||||||
<TreeView.ItemTemplate>
|
<TreeView.ItemTemplate>
|
||||||
<TreeDataTemplate ItemsSource="{Binding Children}" x:DataType="{x:Type vm:BranchTreeNode}">
|
<TreeDataTemplate ItemsSource="{Binding Children}" x:DataType="{x:Type vm:BranchTreeNode}">
|
||||||
<Grid Height="24" ColumnDefinitions="20,*,Auto,Auto" Background="Transparent" DoubleTapped="OnDoubleTappedLocalBranchNode">
|
<Grid Height="24" ColumnDefinitions="20,*,Auto,Auto" Background="Transparent" DoubleTapped="OnDoubleTappedBranchNode">
|
||||||
<Path Grid.Column="0" Classes="folder_icon" Width="12" Height="12" HorizontalAlignment="Left" Margin="0,1,0,0" IsVisible="{Binding IsFolder}"/>
|
<Path Grid.Column="0" Classes="folder_icon" Width="12" Height="12" HorizontalAlignment="Left" Margin="0,1,0,0" IsVisible="{Binding IsFolder}"/>
|
||||||
<Path Grid.Column="0" Width="12" Height="12" HorizontalAlignment="Left" Margin="0,2,0,0" Data="{StaticResource Icons.Check}" IsVisible="{Binding IsCurrent}" VerticalAlignment="Center"/>
|
<Path Grid.Column="0" Width="12" Height="12" HorizontalAlignment="Left" Margin="0,2,0,0" Data="{StaticResource Icons.Check}" IsVisible="{Binding IsCurrent}" VerticalAlignment="Center"/>
|
||||||
<Path Grid.Column="0" Width="12" Height="12" HorizontalAlignment="Left" Margin="2,0,0,0" Data="{StaticResource Icons.Branch}" VerticalAlignment="Center">
|
<Path Grid.Column="0" Width="12" Height="12" HorizontalAlignment="Left" Margin="2,0,0,0" Data="{StaticResource Icons.Branch}" VerticalAlignment="Center">
|
||||||
|
@ -287,7 +287,7 @@
|
||||||
|
|
||||||
<TreeView.ItemTemplate>
|
<TreeView.ItemTemplate>
|
||||||
<TreeDataTemplate ItemsSource="{Binding Children}" x:DataType="{x:Type vm:BranchTreeNode}">
|
<TreeDataTemplate ItemsSource="{Binding Children}" x:DataType="{x:Type vm:BranchTreeNode}">
|
||||||
<Grid Height="24" ColumnDefinitions="20,*,Auto" Background="Transparent">
|
<Grid Height="24" ColumnDefinitions="20,*,Auto" Background="Transparent" DoubleTapped="OnDoubleTappedBranchNode">
|
||||||
<Path Grid.Column="0" Classes="folder_icon" Width="10" Height="10" HorizontalAlignment="Left" Margin="0,2,0,0" IsVisible="{Binding IsFolder}" VerticalAlignment="Center"/>
|
<Path Grid.Column="0" Classes="folder_icon" Width="10" Height="10" HorizontalAlignment="Left" Margin="0,2,0,0" IsVisible="{Binding IsFolder}" VerticalAlignment="Center"/>
|
||||||
<Path Grid.Column="0" Width="12" Height="12" HorizontalAlignment="Left" Margin="0,2,0,0" Data="{StaticResource Icons.Remote}" IsVisible="{Binding IsRemote}" VerticalAlignment="Center"/>
|
<Path Grid.Column="0" Width="12" Height="12" HorizontalAlignment="Left" Margin="0,2,0,0" Data="{StaticResource Icons.Remote}" IsVisible="{Binding IsRemote}" VerticalAlignment="Center"/>
|
||||||
<Path Grid.Column="0" Width="12" Height="12" HorizontalAlignment="Left" Margin="2,0,0,0" Data="{StaticResource Icons.Branch}" IsVisible="{Binding IsBranch}" VerticalAlignment="Center"/>
|
<Path Grid.Column="0" Width="12" Height="12" HorizontalAlignment="Left" Margin="2,0,0,0" Data="{StaticResource Icons.Branch}" IsVisible="{Binding IsBranch}" VerticalAlignment="Center"/>
|
||||||
|
@ -589,7 +589,21 @@
|
||||||
<Button Grid.Column="3" Classes="flat" FontWeight="Regular" Content="{DynamicResource Text.Repository.Abort}" Height="20" Padding="8,0" Margin="4,0" Command="{Binding AbortMerge}"/>
|
<Button Grid.Column="3" Classes="flat" FontWeight="Regular" Content="{DynamicResource Text.Repository.Abort}" Height="20" Padding="8,0" Margin="4,0" Command="{Binding AbortMerge}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<v:RepositorySubView Grid.Row="1" Data="{Binding SelectedView}"/>
|
<ContentControl Grid.Row="1" Content="{Binding SelectedView}">
|
||||||
|
<ContentControl.DataTemplates>
|
||||||
|
<DataTemplate DataType="vm:Histories">
|
||||||
|
<v:Histories NavigationId="{Binding NavigationId}"/>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
|
<DataTemplate DataType="vm:WorkingCopy">
|
||||||
|
<v:WorkingCopy/>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
|
<DataTemplate DataType="vm:StashesPage">
|
||||||
|
<v:StashesPage/>
|
||||||
|
</DataTemplate>
|
||||||
|
</ContentControl.DataTemplates>
|
||||||
|
</ContentControl>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
|
@ -11,51 +11,6 @@ using Avalonia.VisualTree;
|
||||||
|
|
||||||
namespace SourceGit.Views
|
namespace SourceGit.Views
|
||||||
{
|
{
|
||||||
public class RepositorySubView : Border
|
|
||||||
{
|
|
||||||
public static readonly StyledProperty<object> DataProperty =
|
|
||||||
AvaloniaProperty.Register<RepositorySubView, object>(nameof(Data), false);
|
|
||||||
|
|
||||||
public object Data
|
|
||||||
{
|
|
||||||
get => GetValue(DataProperty);
|
|
||||||
set => SetValue(DataProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Type StyleKeyOverride => typeof(Border);
|
|
||||||
|
|
||||||
static RepositorySubView()
|
|
||||||
{
|
|
||||||
DataProperty.Changed.AddClassHandler<RepositorySubView>((view, ev) =>
|
|
||||||
{
|
|
||||||
var data = view.Data;
|
|
||||||
|
|
||||||
if (data == null)
|
|
||||||
{
|
|
||||||
view.Child = null;
|
|
||||||
}
|
|
||||||
else if (data is ViewModels.Histories)
|
|
||||||
{
|
|
||||||
view.Child = new Histories { DataContext = data };
|
|
||||||
}
|
|
||||||
else if (data is ViewModels.WorkingCopy)
|
|
||||||
{
|
|
||||||
view.Child = new WorkingCopy { DataContext = data };
|
|
||||||
}
|
|
||||||
else if (data is ViewModels.StashesPage)
|
|
||||||
{
|
|
||||||
view.Child = new StashesPage { DataContext = data };
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
view.Child = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
GC.Collect();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial class Repository : UserControl
|
public partial class Repository : UserControl
|
||||||
{
|
{
|
||||||
public Repository()
|
public Repository()
|
||||||
|
@ -63,7 +18,7 @@ namespace SourceGit.Views
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnOpenWithExternalTools(object sender, RoutedEventArgs e)
|
private void OpenWithExternalTools(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (sender is Button button && DataContext is ViewModels.Repository repo)
|
if (sender is Button button && DataContext is ViewModels.Repository repo)
|
||||||
{
|
{
|
||||||
|
@ -73,6 +28,58 @@ namespace SourceGit.Views
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OpenGitFlowMenu(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (DataContext is ViewModels.Repository repo)
|
||||||
|
{
|
||||||
|
var menu = repo.CreateContextMenuForGitFlow();
|
||||||
|
(sender as Control)?.OpenContextMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OpenStatistics(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (DataContext is ViewModels.Repository repo)
|
||||||
|
{
|
||||||
|
var dialog = new Statistics() { DataContext = new ViewModels.Statistics(repo.FullPath) };
|
||||||
|
await dialog.ShowDialog(TopLevel.GetTopLevel(this) as Window);
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSearchCommitPanelPropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
var grid = sender as Grid;
|
||||||
|
if (e.Property == IsVisibleProperty && grid.IsVisible)
|
||||||
|
txtSearchCommitsBox.Focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSearchKeyDown(object sender, KeyEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Key == Key.Enter)
|
||||||
|
{
|
||||||
|
if (DataContext is ViewModels.Repository repo)
|
||||||
|
repo.StartSearchCommits();
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSearchResultDataGridSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is DataGrid datagrid && datagrid.SelectedItem != null)
|
||||||
|
{
|
||||||
|
if (DataContext is ViewModels.Repository repo)
|
||||||
|
{
|
||||||
|
var commit = datagrid.SelectedItem as Models.Commit;
|
||||||
|
repo.NavigateToCommit(commit.SHA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
private void OnLocalBranchTreeSelectionChanged(object sender, SelectionChangedEventArgs e)
|
private void OnLocalBranchTreeSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (sender is TreeView tree && tree.SelectedItem != null && DataContext is ViewModels.Repository repo)
|
if (sender is TreeView tree && tree.SelectedItem != null && DataContext is ViewModels.Repository repo)
|
||||||
|
@ -113,74 +120,6 @@ namespace SourceGit.Views
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTagDataGridSelectionChanged(object sender, SelectionChangedEventArgs e)
|
|
||||||
{
|
|
||||||
if (sender is DataGrid datagrid && datagrid.SelectedItem != null)
|
|
||||||
{
|
|
||||||
localBranchTree.UnselectAll();
|
|
||||||
remoteBranchTree.UnselectAll();
|
|
||||||
|
|
||||||
var tag = datagrid.SelectedItem as Models.Tag;
|
|
||||||
if (DataContext is ViewModels.Repository repo)
|
|
||||||
repo.NavigateToCommit(tag.SHA);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnSearchCommitPanelPropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e)
|
|
||||||
{
|
|
||||||
var grid = sender as Grid;
|
|
||||||
if (e.Property == IsVisibleProperty && grid.IsVisible)
|
|
||||||
txtSearchCommitsBox.Focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnSearchKeyDown(object sender, KeyEventArgs e)
|
|
||||||
{
|
|
||||||
if (e.Key == Key.Enter)
|
|
||||||
{
|
|
||||||
if (DataContext is ViewModels.Repository repo)
|
|
||||||
repo.StartSearchCommits();
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnSearchResultDataGridSelectionChanged(object sender, SelectionChangedEventArgs e)
|
|
||||||
{
|
|
||||||
if (sender is DataGrid datagrid && datagrid.SelectedItem != null)
|
|
||||||
{
|
|
||||||
if (DataContext is ViewModels.Repository repo)
|
|
||||||
{
|
|
||||||
var commit = datagrid.SelectedItem as Models.Commit;
|
|
||||||
repo.NavigateToCommit(commit.SHA);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnToggleFilter(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
if (sender is ToggleButton toggle)
|
|
||||||
{
|
|
||||||
var filter = string.Empty;
|
|
||||||
if (toggle.DataContext is ViewModels.BranchTreeNode node)
|
|
||||||
{
|
|
||||||
if (node.IsBranch)
|
|
||||||
filter = (node.Backend as Models.Branch).FullName;
|
|
||||||
}
|
|
||||||
else if (toggle.DataContext is Models.Tag tag)
|
|
||||||
{
|
|
||||||
filter = tag.Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(filter) && DataContext is ViewModels.Repository repo)
|
|
||||||
{
|
|
||||||
repo.UpdateFilter(filter, toggle.IsChecked == true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnLocalBranchContextMenuRequested(object sender, ContextRequestedEventArgs e)
|
private void OnLocalBranchContextMenuRequested(object sender, ContextRequestedEventArgs e)
|
||||||
{
|
{
|
||||||
remoteBranchTree.UnselectAll();
|
remoteBranchTree.UnselectAll();
|
||||||
|
@ -286,6 +225,39 @@ namespace SourceGit.Views
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnDoubleTappedBranchNode(object sender, TappedEventArgs e)
|
||||||
|
{
|
||||||
|
if (!ViewModels.PopupHost.CanCreatePopup())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (sender is Grid grid && DataContext is ViewModels.Repository repo)
|
||||||
|
{
|
||||||
|
var node = grid.DataContext as ViewModels.BranchTreeNode;
|
||||||
|
if (node != null && node.IsBranch)
|
||||||
|
{
|
||||||
|
var branch = node.Backend as Models.Branch;
|
||||||
|
if (branch.IsCurrent)
|
||||||
|
return;
|
||||||
|
|
||||||
|
repo.CheckoutBranch(branch);
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTagDataGridSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is DataGrid datagrid && datagrid.SelectedItem != null)
|
||||||
|
{
|
||||||
|
localBranchTree.UnselectAll();
|
||||||
|
remoteBranchTree.UnselectAll();
|
||||||
|
|
||||||
|
var tag = datagrid.SelectedItem as Models.Tag;
|
||||||
|
if (DataContext is ViewModels.Repository repo)
|
||||||
|
repo.NavigateToCommit(tag.SHA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void OnTagContextRequested(object sender, ContextRequestedEventArgs e)
|
private void OnTagContextRequested(object sender, ContextRequestedEventArgs e)
|
||||||
{
|
{
|
||||||
if (sender is DataGrid datagrid && datagrid.SelectedItem != null && DataContext is ViewModels.Repository repo)
|
if (sender is DataGrid datagrid && datagrid.SelectedItem != null && DataContext is ViewModels.Repository repo)
|
||||||
|
@ -298,6 +270,30 @@ namespace SourceGit.Views
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnToggleFilter(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is ToggleButton toggle)
|
||||||
|
{
|
||||||
|
var filter = string.Empty;
|
||||||
|
if (toggle.DataContext is ViewModels.BranchTreeNode node)
|
||||||
|
{
|
||||||
|
if (node.IsBranch)
|
||||||
|
filter = (node.Backend as Models.Branch).FullName;
|
||||||
|
}
|
||||||
|
else if (toggle.DataContext is Models.Tag tag)
|
||||||
|
{
|
||||||
|
filter = tag.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(filter) && DataContext is ViewModels.Repository repo)
|
||||||
|
{
|
||||||
|
repo.UpdateFilter(filter, toggle.IsChecked == true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
private void OnSubmoduleContextRequested(object sender, ContextRequestedEventArgs e)
|
private void OnSubmoduleContextRequested(object sender, ContextRequestedEventArgs e)
|
||||||
{
|
{
|
||||||
if (sender is DataGrid datagrid && datagrid.SelectedItem != null && DataContext is ViewModels.Repository repo)
|
if (sender is DataGrid datagrid && datagrid.SelectedItem != null && DataContext is ViewModels.Repository repo)
|
||||||
|
@ -310,17 +306,6 @@ namespace SourceGit.Views
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenGitFlowMenu(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
if (DataContext is ViewModels.Repository repo)
|
|
||||||
{
|
|
||||||
var menu = repo.CreateContextMenuForGitFlow();
|
|
||||||
(sender as Control)?.OpenContextMenu(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void UpdateSubmodules(object sender, RoutedEventArgs e)
|
private async void UpdateSubmodules(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (DataContext is ViewModels.Repository repo)
|
if (DataContext is ViewModels.Repository repo)
|
||||||
|
@ -335,36 +320,6 @@ namespace SourceGit.Views
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDoubleTappedLocalBranchNode(object sender, TappedEventArgs e)
|
|
||||||
{
|
|
||||||
if (!ViewModels.PopupHost.CanCreatePopup())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (sender is Grid grid && DataContext is ViewModels.Repository repo)
|
|
||||||
{
|
|
||||||
var node = grid.DataContext as ViewModels.BranchTreeNode;
|
|
||||||
if (node != null && node.IsBranch)
|
|
||||||
{
|
|
||||||
var branch = node.Backend as Models.Branch;
|
|
||||||
if (branch.IsCurrent)
|
|
||||||
return;
|
|
||||||
|
|
||||||
repo.CheckoutLocalBranch(branch.Name);
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void OpenStatistics(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
if (DataContext is ViewModels.Repository repo)
|
|
||||||
{
|
|
||||||
var dialog = new Statistics() { DataContext = new ViewModels.Statistics(repo.FullPath) };
|
|
||||||
await dialog.ShowDialog(TopLevel.GetTopLevel(this) as Window);
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CollectBranchesFromNode(List<Models.Branch> outs, ViewModels.BranchTreeNode node)
|
private void CollectBranchesFromNode(List<Models.Branch> outs, ViewModels.BranchTreeNode node)
|
||||||
{
|
{
|
||||||
if (node == null || node.IsRemote)
|
if (node == null || node.IsRemote)
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
Classes="bold"
|
Classes="bold"
|
||||||
Text="{DynamicResource Text.Configure}"/>
|
Text="{DynamicResource Text.Configure}"/>
|
||||||
|
|
||||||
<Grid Margin="0,16,0,0" RowDefinitions="32,32,32,32,32" ColumnDefinitions="150,*">
|
<Grid Margin="0,16,0,0" RowDefinitions="32,32,32,32,32,32,32" ColumnDefinitions="150,*">
|
||||||
<TextBlock Grid.Column="0"
|
<TextBlock Grid.Column="0"
|
||||||
HorizontalAlignment="Right" VerticalAlignment="Center"
|
HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||||
Margin="0,0,8,0"
|
Margin="0,0,8,0"
|
||||||
|
@ -47,18 +47,42 @@
|
||||||
Text="{Binding HttpProxy, Mode=TwoWay}"/>
|
Text="{Binding HttpProxy, Mode=TwoWay}"/>
|
||||||
|
|
||||||
<TextBlock Grid.Row="3" Grid.Column="0"
|
<TextBlock Grid.Row="3" Grid.Column="0"
|
||||||
|
Text="{DynamicResource Text.Preference.GPG.Format}"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Margin="0,0,16,0"/>
|
||||||
|
<ComboBox Grid.Row="3" Grid.Column="1"
|
||||||
|
MinHeight="28"
|
||||||
|
Padding="8,0"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
ItemsSource="{Binding Source={x:Static m:GPGFormat.Supported}}"
|
||||||
|
SelectedItem="{Binding GPGFormat, Mode=TwoWay, FallbackValue={x:Static m:GPGFormat.OPENPGP}}">
|
||||||
|
<ComboBox.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="{x:Type m:GPGFormat}">
|
||||||
|
<Grid ColumnDefinitions="Auto,*">
|
||||||
|
<TextBlock Grid.Column="0" Text="{Binding Name}"/>
|
||||||
|
<TextBlock Grid.Column="1" Text="{Binding Desc}" Foreground="{DynamicResource Brush.FG2}" HorizontalAlignment="Right"/>
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</ComboBox.ItemTemplate>
|
||||||
|
</ComboBox>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="4" Grid.Column="0"
|
||||||
HorizontalAlignment="Right" VerticalAlignment="Center"
|
HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||||
Margin="0,0,8,0"
|
Margin="0,0,8,0"
|
||||||
Text="{DynamicResource Text.Preference.GPG.UserKey}"/>
|
Text="{DynamicResource Text.Preference.GPG.UserKey}"/>
|
||||||
<TextBox Grid.Row="3" Grid.Column="1"
|
<TextBox Grid.Row="4" Grid.Column="1"
|
||||||
Height="28"
|
Height="28"
|
||||||
CornerRadius="3"
|
CornerRadius="3"
|
||||||
Watermark="{DynamicResource Text.Preference.GPG.UserKey.Placeholder}"
|
Watermark="{DynamicResource Text.Preference.GPG.UserKey.Placeholder}"
|
||||||
Text="{Binding GPGUserSigningKey, Mode=TwoWay}"/>
|
Text="{Binding GPGUserSigningKey, Mode=TwoWay}"/>
|
||||||
|
|
||||||
<CheckBox Grid.Row="4" Grid.Column="1"
|
<CheckBox Grid.Row="5" Grid.Column="1"
|
||||||
Content="{DynamicResource Text.Preference.GPG.Enabled}"
|
Content="{DynamicResource Text.Preference.GPG.CommitEnabled}"
|
||||||
IsChecked="{Binding GPGSigningEnabled, Mode=TwoWay}"/>
|
IsChecked="{Binding GPGCommitSigningEnabled, Mode=TwoWay}"/>
|
||||||
|
|
||||||
|
<CheckBox Grid.Row="6" Grid.Column="1"
|
||||||
|
Content="{DynamicResource Text.Preference.GPG.TagEnabled}"
|
||||||
|
IsChecked="{Binding GPGTagSigningEnabled, Mode=TwoWay}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|
|
@ -11,39 +11,57 @@
|
||||||
x:DataType="vm:RevisionCompare"
|
x:DataType="vm:RevisionCompare"
|
||||||
Background="{DynamicResource Brush.Window}">
|
Background="{DynamicResource Brush.Window}">
|
||||||
<Grid RowDefinitions="50,*" Margin="4">
|
<Grid RowDefinitions="50,*" Margin="4">
|
||||||
<Grid Grid.Row="0" ColumnDefinitions="48,*,48,*,48" Margin="0,0,0,4">
|
<Grid Grid.Row="0" Margin="48,0,48,4" ColumnDefinitions="*,48,*">
|
||||||
<Border Grid.Column="1" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}" CornerRadius="4" Padding="4">
|
<Border Grid.Column="0" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}" CornerRadius="4" Padding="4">
|
||||||
<Grid RowDefinitions="Auto,*">
|
<Grid RowDefinitions="Auto,*">
|
||||||
<Grid Grid.Row="0" ColumnDefinitions="Auto,*,Auto,Auto">
|
<Grid Grid.Row="0" ColumnDefinitions="Auto,*,Auto,Auto,Auto">
|
||||||
<v:Avatar Width="16" Height="16"
|
<v:Avatar Width="16" Height="16"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
IsHitTestVisible="False"
|
IsHitTestVisible="False"
|
||||||
User="{Binding StartPoint.Author}"/>
|
User="{Binding StartPoint.Author}"/>
|
||||||
<TextBlock Grid.Column="1" Classes="monospace" Text="{Binding StartPoint.Author.Name}" Margin="8,0,0,0"/>
|
<TextBlock Grid.Column="1" Classes="monospace" Text="{Binding StartPoint.Author.Name}" Margin="8,0,0,0"/>
|
||||||
<TextBlock Grid.Column="2" Classes="monospace" Text="{Binding StartPoint.SHA, Converter={x:Static c:StringConverters.ToShortSHA}}" Foreground="DarkOrange" Margin="8,0,0,0" TextDecorations="Underline" PointerPressed="OnPressedSHA"/>
|
<Border Grid.Column="2" Background="DarkGreen" CornerRadius="4" IsVisible="{Binding StartPoint.IsCurrentHead}">
|
||||||
<TextBlock Grid.Column="3" Classes="monospace" Text="{Binding StartPoint.CommitterTimeStr}" Foreground="{DynamicResource Brush.FG2}" Margin="8,0,0,0"/>
|
<TextBlock Text="HEAD" Classes="monospace" Margin="4,0"/>
|
||||||
|
</Border>
|
||||||
|
<TextBlock Grid.Column="3" Classes="monospace" Text="{Binding StartPoint.SHA, Converter={x:Static c:StringConverters.ToShortSHA}}" Foreground="DarkOrange" Margin="8,0,0,0" TextDecorations="Underline" PointerPressed="OnPressedSHA"/>
|
||||||
|
<TextBlock Grid.Column="4" Classes="monospace" Text="{Binding StartPoint.CommitterTimeStr}" Foreground="{DynamicResource Brush.FG2}" Margin="8,0,0,0"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<TextBlock Grid.Row="1" Classes="monospace" Text="{Binding StartPoint.Subject}" VerticalAlignment="Bottom"/>
|
<TextBlock Grid.Row="1" Classes="monospace" Text="{Binding StartPoint.Subject}" VerticalAlignment="Bottom"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<Path Grid.Column="2" Width="16" Height="16" Fill="{DynamicResource Brush.FG2}" Data="{DynamicResource Icons.Down}" RenderTransformOrigin="50%,50%" RenderTransform="rotate(270deg)"/>
|
<Path Grid.Column="1" Width="16" Height="16" Fill="{DynamicResource Brush.FG2}" Data="{DynamicResource Icons.Down}" RenderTransformOrigin="50%,50%" RenderTransform="rotate(270deg)"/>
|
||||||
|
|
||||||
<Border Grid.Column="3" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}" CornerRadius="4" Padding="4">
|
<Border Grid.Column="2" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}" CornerRadius="4" Padding="4">
|
||||||
|
<ContentControl Grid.Column="2" Content="{Binding EndPoint}">
|
||||||
|
<ContentControl.DataTemplates>
|
||||||
|
<DataTemplate DataType="m:Commit">
|
||||||
<Grid RowDefinitions="Auto,*">
|
<Grid RowDefinitions="Auto,*">
|
||||||
<Grid Grid.Row="0" ColumnDefinitions="Auto,*,Auto,Auto">
|
<Grid Grid.Row="0" ColumnDefinitions="Auto,*,Auto,Auto,Auto">
|
||||||
<v:Avatar Width="16" Height="16"
|
<v:Avatar Width="16" Height="16"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
IsHitTestVisible="False"
|
IsHitTestVisible="False"
|
||||||
User="{Binding EndPoint.Author}"/>
|
User="{Binding Author}"/>
|
||||||
<TextBlock Grid.Column="1" Classes="monospace" Text="{Binding EndPoint.Author.Name}" Margin="8,0,0,0"/>
|
<TextBlock Grid.Column="1" Classes="monospace" Text="{Binding Author.Name}" Margin="8,0,0,0"/>
|
||||||
<TextBlock Grid.Column="2" Classes="monospace" Text="{Binding EndPoint.SHA, Converter={x:Static c:StringConverters.ToShortSHA}}" Foreground="DarkOrange" Margin="8,0,0,0" TextDecorations="Underline" PointerPressed="OnPressedSHA" />
|
<Border Grid.Column="2" Background="DarkGreen" CornerRadius="4" IsVisible="{Binding IsCurrentHead}">
|
||||||
<TextBlock Grid.Column="3" Classes="monospace" Text="{Binding EndPoint.CommitterTimeStr}" Foreground="{DynamicResource Brush.FG2}" Margin="8,0,0,0"/>
|
<TextBlock Text="HEAD" Classes="monospace" Margin="4,0"/>
|
||||||
|
</Border>
|
||||||
|
<TextBlock Grid.Column="3" Classes="monospace" Text="{Binding SHA, Converter={x:Static c:StringConverters.ToShortSHA}}" Foreground="DarkOrange" Margin="8,0,0,0" TextDecorations="Underline" PointerPressed="OnPressedSHA" />
|
||||||
|
<TextBlock Grid.Column="4" Classes="monospace" Text="{Binding CommitterTimeStr}" Foreground="{DynamicResource Brush.FG2}" Margin="8,0,0,0"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<TextBlock Grid.Row="1" Classes="monospace" Text="{Binding EndPoint.Subject}" VerticalAlignment="Bottom"/>
|
<TextBlock Grid.Row="1" Classes="monospace" Text="{Binding Subject}" VerticalAlignment="Bottom"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
|
<DataTemplate DataType="vm:CompareTargetWorktree">
|
||||||
|
<Border HorizontalAlignment="Center" VerticalAlignment="Center" Background="DarkGreen" CornerRadius="4">
|
||||||
|
<TextBlock Text="{DynamicResource Text.Worktree}" Classes="monospace" Margin="4,2"/>
|
||||||
|
</Border>
|
||||||
|
</DataTemplate>
|
||||||
|
</ContentControl.DataTemplates>
|
||||||
|
</ContentControl>
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
@ -56,7 +74,7 @@
|
||||||
|
|
||||||
<Grid Grid.Column="0" RowDefinitions="26,*">
|
<Grid Grid.Column="0" RowDefinitions="26,*">
|
||||||
<!-- Search & Display Mode -->
|
<!-- Search & Display Mode -->
|
||||||
<Grid Grid.Row="0" ColumnDefinitions="*,24">
|
<Grid Grid.Row="0" ColumnDefinitions="*,18">
|
||||||
<TextBox Grid.Column="0"
|
<TextBox Grid.Column="0"
|
||||||
Height="26"
|
Height="26"
|
||||||
BorderThickness="1" BorderBrush="{DynamicResource Brush.Border2}"
|
BorderThickness="1" BorderBrush="{DynamicResource Brush.Border2}"
|
||||||
|
@ -78,116 +96,18 @@
|
||||||
</TextBox>
|
</TextBox>
|
||||||
|
|
||||||
<v:ChangeViewModeSwitcher Grid.Column="1"
|
<v:ChangeViewModeSwitcher Grid.Column="1"
|
||||||
Width="18" Height="18"
|
Width="14" Height="14"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=CommitChangeViewMode, Mode=TwoWay}"/>
|
ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=CommitChangeViewMode, Mode=TwoWay}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<!-- Changes -->
|
<!-- Changes -->
|
||||||
<Border Grid.Row="1" Margin="0,4,0,0" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}">
|
<Border Grid.Row="1" Margin="0,4,0,0" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}">
|
||||||
<Grid>
|
<v:ChangeCollectionView IsWorkingCopyChange="False"
|
||||||
<DataGrid Background="Transparent"
|
ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=CommitChangeViewMode}"
|
||||||
ItemsSource="{Binding VisibleChanges}"
|
Changes="{Binding VisibleChanges}"
|
||||||
SelectedItem="{Binding SelectedChange, Mode=TwoWay}"
|
SelectedChanges="{Binding SelectedChanges, Mode=TwoWay}"
|
||||||
SelectionMode="Single"
|
ContextRequested="OnChangeContextRequested"/>
|
||||||
CanUserReorderColumns="False"
|
|
||||||
CanUserResizeColumns="False"
|
|
||||||
CanUserSortColumns="False"
|
|
||||||
IsReadOnly="True"
|
|
||||||
HeadersVisibility="None"
|
|
||||||
Focusable="False"
|
|
||||||
RowHeight="26"
|
|
||||||
HorizontalScrollBarVisibility="Auto"
|
|
||||||
VerticalScrollBarVisibility="Auto"
|
|
||||||
SelectionChanged="OnDataGridSelectionChanged"
|
|
||||||
ContextRequested="OnDataGridContextRequested"
|
|
||||||
IsVisible="{Binding Source={x:Static vm:Preference.Instance}, Path=CommitChangeViewMode, Converter={x:Static c:ChangeViewModeConverters.IsList}}">
|
|
||||||
<DataGrid.Columns>
|
|
||||||
<DataGridTemplateColumn Header="ICON">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<v:ChangeStatusIcon Width="14" Height="14" IsWorkingCopyChange="False" Change="{Binding}"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
|
|
||||||
<DataGridTemplateColumn Width="*" Header="PATH">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock Classes="monospace" Text="{Binding Path}" Margin="4,0,0,0"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
</DataGrid.Columns>
|
|
||||||
</DataGrid>
|
|
||||||
|
|
||||||
<DataGrid Background="Transparent"
|
|
||||||
ItemsSource="{Binding VisibleChanges}"
|
|
||||||
SelectedItem="{Binding SelectedChange, Mode=TwoWay}"
|
|
||||||
SelectionMode="Single"
|
|
||||||
CanUserReorderColumns="False"
|
|
||||||
CanUserResizeColumns="False"
|
|
||||||
CanUserSortColumns="False"
|
|
||||||
IsReadOnly="True"
|
|
||||||
HeadersVisibility="None"
|
|
||||||
Focusable="False"
|
|
||||||
RowHeight="26"
|
|
||||||
HorizontalScrollBarVisibility="Auto"
|
|
||||||
VerticalScrollBarVisibility="Auto"
|
|
||||||
SelectionChanged="OnDataGridSelectionChanged"
|
|
||||||
ContextRequested="OnDataGridContextRequested"
|
|
||||||
IsVisible="{Binding Source={x:Static vm:Preference.Instance}, Path=CommitChangeViewMode, Converter={x:Static c:ChangeViewModeConverters.IsGrid}}">
|
|
||||||
<DataGrid.Columns>
|
|
||||||
<DataGridTemplateColumn Header="ICON">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<v:ChangeStatusIcon Width="14" Height="14" IsWorkingCopyChange="False" Change="{Binding}"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
|
|
||||||
<DataGridTemplateColumn Header="FILE_NAME">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock Classes="monospace" Text="{Binding Path, Converter={x:Static c:PathConverters.PureFileName}}" Margin="4,0,0,0"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
|
|
||||||
<DataGridTemplateColumn Header="FOLDER_PATH">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock Classes="monospace" Text="{Binding Path, Converter={x:Static c:PathConverters.PureDirectoryName}}" Margin="4,0,0,0" Foreground="{DynamicResource Brush.FG2}"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
</DataGrid.Columns>
|
|
||||||
</DataGrid>
|
|
||||||
|
|
||||||
<TreeView ItemsSource="{Binding ChangeTree}"
|
|
||||||
SelectedItem="{Binding SelectedNode, Mode=TwoWay}"
|
|
||||||
AutoScrollToSelectedItem="True"
|
|
||||||
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
|
||||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
|
||||||
ContextRequested="OnTreeViewContextRequested"
|
|
||||||
IsVisible="{Binding Source={x:Static vm:Preference.Instance}, Path=CommitChangeViewMode, Converter={x:Static c:ChangeViewModeConverters.IsTree}}">
|
|
||||||
<TreeView.Styles>
|
|
||||||
<Style Selector="TreeViewItem" x:DataType="vm:FileTreeNode">
|
|
||||||
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
|
|
||||||
</Style>
|
|
||||||
</TreeView.Styles>
|
|
||||||
|
|
||||||
<TreeView.ItemTemplate>
|
|
||||||
<TreeDataTemplate ItemsSource="{Binding Children}" x:DataType="{x:Type vm:FileTreeNode}">
|
|
||||||
<Grid Height="24" ColumnDefinitions="Auto,*">
|
|
||||||
<Path Grid.Column="0" Classes="folder_icon" Width="14" Height="14" Margin="0,2,0,0" IsVisible="{Binding IsFolder}" Fill="Goldenrod" VerticalAlignment="Center"/>
|
|
||||||
<v:ChangeStatusIcon Grid.Column="0" Width="14" Height="14" IsWorkingCopyChange="False" Change="{Binding Backend}" IsVisible="{Binding !IsFolder}"/>
|
|
||||||
<TextBlock Grid.Column="1" Classes="monospace" Text="{Binding FullPath, Converter={x:Static c:PathConverters.PureFileName}}" Margin="6,0,0,0"/>
|
|
||||||
</Grid>
|
|
||||||
</TreeDataTemplate>
|
|
||||||
</TreeView.ItemTemplate>
|
|
||||||
</TreeView>
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|
|
@ -10,50 +10,21 @@ namespace SourceGit.Views
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDataGridSelectionChanged(object sender, SelectionChangedEventArgs e)
|
private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e)
|
||||||
{
|
{
|
||||||
if (sender is DataGrid datagrid && datagrid.IsVisible)
|
if (DataContext is ViewModels.RevisionCompare vm && sender is ChangeCollectionView view)
|
||||||
{
|
{
|
||||||
datagrid.ScrollIntoView(datagrid.SelectedItem, null);
|
var menu = vm.CreateChangeContextMenu();
|
||||||
}
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDataGridContextRequested(object sender, ContextRequestedEventArgs e)
|
|
||||||
{
|
|
||||||
if (sender is DataGrid datagrid && datagrid.SelectedItem != null)
|
|
||||||
{
|
|
||||||
var compare = DataContext as ViewModels.RevisionCompare;
|
|
||||||
var menu = compare.CreateChangeContextMenu(datagrid.SelectedItem as Models.Change);
|
|
||||||
datagrid.OpenContextMenu(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnTreeViewContextRequested(object sender, ContextRequestedEventArgs e)
|
|
||||||
{
|
|
||||||
if (sender is TreeView view && view.SelectedItem != null)
|
|
||||||
{
|
|
||||||
var compare = DataContext as ViewModels.RevisionCompare;
|
|
||||||
var node = view.SelectedItem as ViewModels.FileTreeNode;
|
|
||||||
if (node != null && !node.IsFolder)
|
|
||||||
{
|
|
||||||
var menu = compare.CreateChangeContextMenu(node.Backend as Models.Change);
|
|
||||||
view.OpenContextMenu(menu);
|
view.OpenContextMenu(menu);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPressedSHA(object sender, PointerPressedEventArgs e)
|
private void OnPressedSHA(object sender, PointerPressedEventArgs e)
|
||||||
{
|
{
|
||||||
if (sender is TextBlock block)
|
if (DataContext is ViewModels.RevisionCompare vm && sender is TextBlock block)
|
||||||
{
|
vm.NavigateTo(block.Text);
|
||||||
var compare = DataContext as ViewModels.RevisionCompare;
|
|
||||||
compare.NavigateTo(block.Text);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,29 +41,22 @@
|
||||||
|
|
||||||
<!-- File Tree -->
|
<!-- File Tree -->
|
||||||
<Border Grid.Row="1" Margin="0,4,0,0" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}">
|
<Border Grid.Row="1" Margin="0,4,0,0" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}">
|
||||||
<TreeView Grid.Row="5"
|
<TreeDataGrid AutoDragDropRows="False"
|
||||||
ItemsSource="{Binding RevisionFilesTree}"
|
ShowColumnHeaders="False"
|
||||||
SelectedItem="{Binding SelectedRevisionFileNode, Mode=TwoWay}"
|
CanUserResizeColumns="False"
|
||||||
AutoScrollToSelectedItem="True"
|
CanUserSortColumns="False"
|
||||||
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
Source="{Binding RevisionFiles}"
|
||||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
ContextRequested="OnFileContextRequested">
|
||||||
ContextRequested="OnTreeViewContextRequested">
|
<TreeDataGrid.Resources>
|
||||||
<TreeView.Styles>
|
<DataTemplate x:Key="FileTreeNodeExpanderTemplate" DataType="m:FileTreeNode">
|
||||||
<Style Selector="TreeViewItem" x:DataType="vm:FileTreeNode">
|
<Grid HorizontalAlignment="Stretch" Height="24" ColumnDefinitions="Auto,*">
|
||||||
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
|
|
||||||
</Style>
|
|
||||||
</TreeView.Styles>
|
|
||||||
|
|
||||||
<TreeView.ItemTemplate>
|
|
||||||
<TreeDataTemplate ItemsSource="{Binding Children}" x:DataType="{x:Type vm:FileTreeNode}">
|
|
||||||
<Grid Height="24" ColumnDefinitions="Auto,*">
|
|
||||||
<Path Grid.Column="0" Classes="folder_icon" Width="14" Height="14" Margin="0,2,0,0" IsVisible="{Binding IsFolder}" Fill="Goldenrod" VerticalAlignment="Center"/>
|
<Path Grid.Column="0" Classes="folder_icon" Width="14" Height="14" Margin="0,2,0,0" IsVisible="{Binding IsFolder}" Fill="Goldenrod" VerticalAlignment="Center"/>
|
||||||
<Path Grid.Column="0" Width="14" Height="14" IsVisible="{Binding !IsFolder}" Data="{StaticResource Icons.File}" VerticalAlignment="Center"/>
|
<Path Grid.Column="0" Width="14" Height="14" IsVisible="{Binding !IsFolder}" Data="{StaticResource Icons.File}" VerticalAlignment="Center"/>
|
||||||
<TextBlock Grid.Column="1" Classes="monospace" Text="{Binding FullPath, Converter={x:Static c:PathConverters.PureFileName}}" Margin="6,0,0,0"/>
|
<TextBlock Grid.Column="1" Classes="monospace" Text="{Binding FullPath, Converter={x:Static c:PathConverters.PureFileName}}" Margin="6,0,0,0"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</TreeDataTemplate>
|
</DataTemplate>
|
||||||
</TreeView.ItemTemplate>
|
</TreeDataGrid.Resources>
|
||||||
</TreeView>
|
</TreeDataGrid>
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|
|
@ -213,14 +213,16 @@ namespace SourceGit.Views
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTreeViewContextRequested(object sender, ContextRequestedEventArgs e)
|
private void OnFileContextRequested(object sender, ContextRequestedEventArgs e)
|
||||||
{
|
{
|
||||||
var detail = DataContext as ViewModels.CommitDetail;
|
if (DataContext is ViewModels.CommitDetail vm && sender is TreeDataGrid tree)
|
||||||
var node = detail.SelectedRevisionFileNode;
|
|
||||||
if (!node.IsFolder)
|
|
||||||
{
|
{
|
||||||
var menu = detail.CreateRevisionFileContextMenu(node.Backend as Models.Object);
|
var selected = tree.RowSelection.SelectedItem as Models.FileTreeNode;
|
||||||
(sender as Control)?.OpenContextMenu(menu);
|
if (selected != null && !selected.IsFolder && selected.Backend is Models.Object obj)
|
||||||
|
{
|
||||||
|
var menu = vm.CreateRevisionFileContextMenu(obj);
|
||||||
|
tree.OpenContextMenu(menu);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
|
|
|
@ -54,15 +54,7 @@
|
||||||
<DataGridTemplateColumn Width="*">
|
<DataGridTemplateColumn Width="*">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate DataType="m:Stash">
|
<DataTemplate DataType="m:Stash">
|
||||||
<Border BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="0,0,0,1" Padding="4" Background="Transparent">
|
<Border BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="0,0,0,1" Padding="4" Background="Transparent" ContextRequested="OnStashContextRequested">
|
||||||
<Border.ContextMenu>
|
|
||||||
<ContextMenu>
|
|
||||||
<MenuItem Header="{DynamicResource Text.StashCM.Apply}" Command="{Binding $parent[v:StashesPage].DataContext.(vm:StashesPage).Apply}" CommandParameter="{Binding}"/>
|
|
||||||
<MenuItem Header="{DynamicResource Text.StashCM.Pop}" Command="{Binding $parent[v:StashesPage].DataContext.(vm:StashesPage).Pop}" CommandParameter="{Binding}"/>
|
|
||||||
<MenuItem Header="{DynamicResource Text.StashCM.Drop}" Command="{Binding $parent[v:StashesPage].DataContext.(vm:StashesPage).Drop}" CommandParameter="{Binding}"/>
|
|
||||||
</ContextMenu>
|
|
||||||
</Border.ContextMenu>
|
|
||||||
|
|
||||||
<Grid RowDefinitions="Auto,*" >
|
<Grid RowDefinitions="Auto,*" >
|
||||||
<Grid Grid.Row="0" ColumnDefinitions="*,Auto">
|
<Grid Grid.Row="0" ColumnDefinitions="*,Auto">
|
||||||
<TextBlock Grid.Column="0" Classes="monospace" Text="{Binding SHA, Converter={x:Static c:StringConverters.ToShortSHA}}" Foreground="DarkOrange" TextDecorations="Underline" Cursor="Hand"/>
|
<TextBlock Grid.Column="0" Classes="monospace" Text="{Binding SHA, Converter={x:Static c:StringConverters.ToShortSHA}}" Foreground="DarkOrange" TextDecorations="Underline" Cursor="Hand"/>
|
||||||
|
|
|
@ -8,5 +8,15 @@ namespace SourceGit.Views
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnStashContextRequested(object sender, ContextRequestedEventArgs e)
|
||||||
|
{
|
||||||
|
if (DataContext is ViewModels.StashesPage vm && sender is Border border)
|
||||||
|
{
|
||||||
|
var menu = vm.MakeContextMenu(border.DataContext as Models.Stash);
|
||||||
|
border.OpenContextMenu(menu);
|
||||||
|
}
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -180,10 +180,8 @@
|
||||||
</ContentControl>
|
</ContentControl>
|
||||||
|
|
||||||
<!-- Loading Mask -->
|
<!-- Loading Mask -->
|
||||||
<Path Grid.Row="2"
|
<v:LoadingIcon Grid.Row="2"
|
||||||
Classes="rotating"
|
|
||||||
Width="48" Height="48"
|
Width="48" Height="48"
|
||||||
Data="{DynamicResource Icons.Loading}"
|
|
||||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||||
IsVisible="{Binding IsLoading}"/>
|
IsVisible="{Binding IsLoading}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
xmlns:m="using:SourceGit.Models"
|
xmlns:m="using:SourceGit.Models"
|
||||||
xmlns:vm="using:SourceGit.ViewModels"
|
xmlns:vm="using:SourceGit.ViewModels"
|
||||||
xmlns:v="using:SourceGit.Views"
|
xmlns:v="using:SourceGit.Views"
|
||||||
xmlns:c="using:SourceGit.Converters"
|
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="SourceGit.Views.TextDiffView"
|
x:Class="SourceGit.Views.TextDiffView"
|
||||||
Background="{DynamicResource Brush.Contents}">
|
Background="{DynamicResource Brush.Contents}">
|
||||||
|
@ -19,14 +18,14 @@
|
||||||
SecondaryFG="{DynamicResource Brush.FG2}"
|
SecondaryFG="{DynamicResource Brush.FG2}"
|
||||||
FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}"
|
FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}"
|
||||||
DiffData="{Binding}"
|
DiffData="{Binding}"
|
||||||
SyncScrollOffset="{Binding $parent[v:DiffView].DataContext.(vm:DiffContext).SyncScrollOffset, Mode=TwoWay}"
|
SyncScrollOffset="{Binding $parent[v:DiffView].((vm:DiffContext)DataContext).SyncScrollOffset, Mode=TwoWay}"
|
||||||
UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting, Mode=TwoWay}"/>
|
UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting, Mode=TwoWay}"/>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
<DataTemplate DataType="vm:TwoSideTextDiff">
|
<DataTemplate DataType="vm:TwoSideTextDiff">
|
||||||
<Grid ColumnDefinitions="*,1,*">
|
<Grid ColumnDefinitions="*,1,*">
|
||||||
<v:SingleSideTextDiffPresenter Grid.Column="0"
|
<v:SingleSideTextDiffPresenter Grid.Column="0"
|
||||||
SyncScrollOffset="{Binding $parent[v:DiffView].DataContext.(vm:DiffContext).SyncScrollOffset, Mode=TwoWay}"
|
SyncScrollOffset="{Binding $parent[v:DiffView].((vm:DiffContext)DataContext).SyncScrollOffset, Mode=TwoWay}"
|
||||||
UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting, Mode=TwoWay}"
|
UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting, Mode=TwoWay}"
|
||||||
IsOld="True"
|
IsOld="True"
|
||||||
HorizontalScrollBarVisibility="Auto"
|
HorizontalScrollBarVisibility="Auto"
|
||||||
|
@ -41,7 +40,7 @@
|
||||||
<Rectangle Grid.Column="1" Fill="{DynamicResource Brush.Border2}" Width="1" HorizontalAlignment="Center" VerticalAlignment="Stretch"/>
|
<Rectangle Grid.Column="1" Fill="{DynamicResource Brush.Border2}" Width="1" HorizontalAlignment="Center" VerticalAlignment="Stretch"/>
|
||||||
|
|
||||||
<v:SingleSideTextDiffPresenter Grid.Column="2"
|
<v:SingleSideTextDiffPresenter Grid.Column="2"
|
||||||
SyncScrollOffset="{Binding $parent[v:DiffView].DataContext.(vm:DiffContext).SyncScrollOffset, Mode=TwoWay}"
|
SyncScrollOffset="{Binding $parent[v:DiffView].((vm:DiffContext)DataContext).SyncScrollOffset, Mode=TwoWay}"
|
||||||
UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting, Mode=TwoWay}"
|
UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting, Mode=TwoWay}"
|
||||||
IsOld="False"
|
IsOld="False"
|
||||||
HorizontalScrollBarVisibility="Auto"
|
HorizontalScrollBarVisibility="Auto"
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
<Grid RowDefinitions="36,*,36" Background="{DynamicResource Brush.Window}">
|
<Grid RowDefinitions="36,*,36" Background="{DynamicResource Brush.Window}">
|
||||||
<Border Grid.Row="0" BorderBrush="{DynamicResource Brush.Border0}" BorderThickness="0,0,0,1" Background="{DynamicResource Brush.ToolBar}">
|
<Border Grid.Row="0" BorderBrush="{DynamicResource Brush.Border0}" BorderThickness="0,0,0,1" Background="{DynamicResource Brush.ToolBar}">
|
||||||
<StackPanel Orientation="Horizontal" Margin="4,0,0,0">
|
<StackPanel Orientation="Horizontal" Margin="4,0,0,0">
|
||||||
<Button Classes="icon_button" Width="32" Command="{Binding Clone}" CommandParameter="{Binding $parent[v:Launcher].DataContext}" ToolTip.Tip="{DynamicResource Text.Welcome.Clone}">
|
<Button Classes="icon_button" Width="32" Command="{Binding Clone}" ToolTip.Tip="{DynamicResource Text.Welcome.Clone}">
|
||||||
<Path Width="13" Height="13" Data="{StaticResource Icons.Pull}"/>
|
<Path Width="13" Height="13" Data="{StaticResource Icons.Pull}"/>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:m="using:SourceGit.Models"
|
|
||||||
xmlns:vm="using:SourceGit.ViewModels"
|
xmlns:vm="using:SourceGit.ViewModels"
|
||||||
xmlns:v="using:SourceGit.Views"
|
xmlns:v="using:SourceGit.Views"
|
||||||
xmlns:c="using:SourceGit.Converters"
|
xmlns:c="using:SourceGit.Converters"
|
||||||
|
@ -21,29 +20,29 @@
|
||||||
<!-- Unstaged Toolbar -->
|
<!-- Unstaged Toolbar -->
|
||||||
<Border Grid.Row="0" BorderThickness="0,0,0,1" BorderBrush="{DynamicResource Brush.Border0}">
|
<Border Grid.Row="0" BorderThickness="0,0,0,1" BorderBrush="{DynamicResource Brush.Border0}">
|
||||||
<Grid ColumnDefinitions="Auto,Auto,Auto,Auto,*,Auto,Auto,Auto,Auto">
|
<Grid ColumnDefinitions="Auto,Auto,Auto,Auto,*,Auto,Auto,Auto,Auto">
|
||||||
<v:ChangeViewModeSwitcher Grid.Column="0" Width="14" Height="14" Margin="8,0,0,0" ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=UnstagedChangeViewMode, Mode=TwoWay}"/>
|
<v:ChangeViewModeSwitcher Grid.Column="0" Width="12" Height="12" Margin="8,0,0,0" ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=UnstagedChangeViewMode, Mode=TwoWay}"/>
|
||||||
<TextBlock Grid.Column="1" Text="{DynamicResource Text.WorkingCopy.Unstaged}" Foreground="{DynamicResource Brush.FG2}" FontWeight="Bold" Margin="8,0,0,0"/>
|
<TextBlock Grid.Column="1" Text="{DynamicResource Text.WorkingCopy.Unstaged}" Foreground="{DynamicResource Brush.FG2}" FontWeight="Bold" Margin="8,0,0,0"/>
|
||||||
<TextBlock Grid.Column="2" FontWeight="Bold" Foreground="{DynamicResource Brush.FG2}" Text="{Binding Unstaged, Converter={x:Static c:ListConverters.ToCount}}"/>
|
<TextBlock Grid.Column="2" FontWeight="Bold" Foreground="{DynamicResource Brush.FG2}" Text="{Binding Unstaged, Converter={x:Static c:ListConverters.ToCount}}"/>
|
||||||
<Path Grid.Column="3" Classes="rotating" Width="14" Height="14" Data="{StaticResource Icons.Loading}" Margin="8,0,0,0" IsVisible="{Binding IsStaging}"/>
|
<v:LoadingIcon Grid.Column="3" Width="14" Height="14" Margin="8,0,0,0" IsVisible="{Binding IsStaging}"/>
|
||||||
|
|
||||||
<Button Grid.Column="5"
|
<Button Grid.Column="5"
|
||||||
Classes="icon_button"
|
Classes="icon_button"
|
||||||
Width="26" Height="14"
|
Width="26" Height="14"
|
||||||
Padding="0"
|
Padding="0"
|
||||||
ToolTip.Tip="{DynamicResource Text.WorkingCopy.Unstaged.ViewAssumeUnchaged}"
|
ToolTip.Tip="{DynamicResource Text.WorkingCopy.Unstaged.ViewAssumeUnchaged}"
|
||||||
Click="ViewAssumeUnchanged">
|
Command="{Binding OpenAssumeUnchanged}">
|
||||||
<Path Width="14" Height="14" Data="{StaticResource Icons.File.Ignore}"/>
|
<Path Width="14" Height="14" Data="{StaticResource Icons.File.Ignore}"/>
|
||||||
</Button>
|
</Button>
|
||||||
<ToggleButton Grid.Column="6"
|
<ToggleButton Grid.Column="6"
|
||||||
Classes="toggle_untracked"
|
Classes="toggle_untracked"
|
||||||
Width="26" Height="14"
|
Width="26" Height="14"
|
||||||
ToolTip.Tip="{DynamicResource Text.WorkingCopy.IncludeUntracked}"
|
ToolTip.Tip="{DynamicResource Text.WorkingCopy.IncludeUntracked}"
|
||||||
IsChecked="{Binding $parent[v:Repository].DataContext.(vm:Repository).IncludeUntracked, Mode=TwoWay}"/>
|
IsChecked="{Binding IncludeUntracked, Mode=TwoWay}"/>
|
||||||
<Button Grid.Column="7"
|
<Button Grid.Column="7"
|
||||||
Classes="icon_button"
|
Classes="icon_button"
|
||||||
Width="26" Height="14"
|
Width="26" Height="14"
|
||||||
Padding="0"
|
Padding="0"
|
||||||
Click="StageSelected">
|
Command="{Binding StageSelected}">
|
||||||
<ToolTip.Tip>
|
<ToolTip.Tip>
|
||||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||||
<TextBlock Text="{DynamicResource Text.WorkingCopy.Unstaged.Stage}" VerticalAlignment="Center"/>
|
<TextBlock Text="{DynamicResource Text.WorkingCopy.Unstaged.Stage}" VerticalAlignment="Center"/>
|
||||||
|
@ -56,130 +55,33 @@
|
||||||
Classes="icon_button"
|
Classes="icon_button"
|
||||||
Width="26" Height="14"
|
Width="26" Height="14"
|
||||||
Padding="0"
|
Padding="0"
|
||||||
ToolTip.Tip="{DynamicResource Text.WorkingCopy.Unstaged.StageAll}" Click="StageAll">
|
ToolTip.Tip="{DynamicResource Text.WorkingCopy.Unstaged.StageAll}"
|
||||||
|
Command="{Binding StageAll}">
|
||||||
<Path Width="14" Height="14" Data="{StaticResource Icons.DoubleDown}"/>
|
<Path Width="14" Height="14" Data="{StaticResource Icons.DoubleDown}"/>
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<!-- Unstaged Changes -->
|
<!-- Unstaged Changes -->
|
||||||
<Grid Grid.Row="1" Background="{DynamicResource Brush.Contents}">
|
<v:ChangeCollectionView Grid.Row="1"
|
||||||
<DataGrid x:Name="unstagedList"
|
IsWorkingCopyChange="True"
|
||||||
Background="Transparent"
|
SingleSelect="False"
|
||||||
ItemsSource="{Binding Unstaged}"
|
Background="{DynamicResource Brush.Contents}"
|
||||||
SelectedItem="{Binding SelectedUnstagedChange, Mode=TwoWay}"
|
ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=UnstagedChangeViewMode}"
|
||||||
SelectionMode="Extended"
|
Changes="{Binding Unstaged}"
|
||||||
CanUserReorderColumns="False"
|
SelectedChanges="{Binding SelectedUnstaged, Mode=TwoWay}"
|
||||||
CanUserResizeColumns="False"
|
ContextRequested="OnUnstagedContextRequested"
|
||||||
CanUserSortColumns="False"
|
ChangeDoubleTapped="OnUnstagedChangeDoubleTapped"
|
||||||
IsReadOnly="True"
|
KeyDown="OnUnstagedKeyDown"/>
|
||||||
HeadersVisibility="None"
|
|
||||||
Focusable="False"
|
|
||||||
RowHeight="26"
|
|
||||||
HorizontalScrollBarVisibility="Auto"
|
|
||||||
VerticalScrollBarVisibility="Auto"
|
|
||||||
KeyDown="OnUnstagedListKeyDown"
|
|
||||||
ContextRequested="OnUnstagedListContextRequested"
|
|
||||||
IsVisible="{Binding Source={x:Static vm:Preference.Instance}, Path=UnstagedChangeViewMode, Converter={x:Static c:ChangeViewModeConverters.IsList}}">
|
|
||||||
<DataGrid.Columns>
|
|
||||||
<DataGridTemplateColumn Header="ICON">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<v:ChangeStatusIcon Width="14" Height="14" IsWorkingCopyChange="True" Change="{Binding}"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
|
|
||||||
<DataGridTemplateColumn Width="*" Header="PATH">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock Classes="monospace" Text="{Binding Path}" Margin="4,0,0,0"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
</DataGrid.Columns>
|
|
||||||
</DataGrid>
|
|
||||||
|
|
||||||
<DataGrid x:Name="unstagedGrid"
|
|
||||||
Background="Transparent"
|
|
||||||
ItemsSource="{Binding Unstaged}"
|
|
||||||
SelectedItem="{Binding SelectedUnstagedChange, Mode=TwoWay}"
|
|
||||||
SelectionMode="Extended"
|
|
||||||
CanUserReorderColumns="False"
|
|
||||||
CanUserResizeColumns="False"
|
|
||||||
CanUserSortColumns="False"
|
|
||||||
IsReadOnly="True"
|
|
||||||
HeadersVisibility="None"
|
|
||||||
Focusable="False"
|
|
||||||
RowHeight="26"
|
|
||||||
HorizontalScrollBarVisibility="Auto"
|
|
||||||
VerticalScrollBarVisibility="Auto"
|
|
||||||
KeyDown="OnUnstagedListKeyDown"
|
|
||||||
ContextRequested="OnUnstagedListContextRequested"
|
|
||||||
IsVisible="{Binding Source={x:Static vm:Preference.Instance}, Path=UnstagedChangeViewMode, Converter={x:Static c:ChangeViewModeConverters.IsGrid}}">
|
|
||||||
<DataGrid.Columns>
|
|
||||||
<DataGridTemplateColumn Header="ICON">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<v:ChangeStatusIcon Width="14" Height="14" IsWorkingCopyChange="True" Change="{Binding}"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
|
|
||||||
<DataGridTemplateColumn Header="FILE_NAME">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock Classes="monospace" Text="{Binding Path, Converter={x:Static c:PathConverters.PureFileName}}" Margin="4,0,0,0"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
|
|
||||||
<DataGridTemplateColumn Header="FOLDER_PATH">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock Classes="monospace" Text="{Binding Path, Converter={x:Static c:PathConverters.PureDirectoryName}}" Margin="4,0,0,0" Foreground="{DynamicResource Brush.FG2}"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
</DataGrid.Columns>
|
|
||||||
</DataGrid>
|
|
||||||
|
|
||||||
<TreeView x:Name="unstagedTree"
|
|
||||||
ItemsSource="{Binding UnstagedTree}"
|
|
||||||
SelectedItem="{Binding SelectedUnstagedTreeNode, Mode=TwoWay}"
|
|
||||||
SelectionMode="Multiple"
|
|
||||||
AutoScrollToSelectedItem="True"
|
|
||||||
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
|
||||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
|
||||||
KeyDown="OnUnstagedTreeViewKeyDown"
|
|
||||||
ContextRequested="OnUnstagedTreeViewContextRequested"
|
|
||||||
IsVisible="{Binding Source={x:Static vm:Preference.Instance}, Path=UnstagedChangeViewMode, Converter={x:Static c:ChangeViewModeConverters.IsTree}}">
|
|
||||||
<TreeView.Styles>
|
|
||||||
<Style Selector="TreeViewItem" x:DataType="vm:FileTreeNode">
|
|
||||||
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
|
|
||||||
</Style>
|
|
||||||
</TreeView.Styles>
|
|
||||||
|
|
||||||
<TreeView.ItemTemplate>
|
|
||||||
<TreeDataTemplate ItemsSource="{Binding Children}" x:DataType="{x:Type vm:FileTreeNode}">
|
|
||||||
<Grid Height="24" ColumnDefinitions="Auto,*">
|
|
||||||
<Path Grid.Column="0" Classes="folder_icon" Width="14" Height="14" Margin="0,2,0,0" IsVisible="{Binding IsFolder}" Fill="Goldenrod" VerticalAlignment="Center"/>
|
|
||||||
<v:ChangeStatusIcon Grid.Column="0" Width="14" Height="14" IsWorkingCopyChange="True" Change="{Binding Backend}" IsVisible="{Binding !IsFolder}"/>
|
|
||||||
<TextBlock Grid.Column="1" Classes="monospace" Text="{Binding FullPath, Converter={x:Static c:PathConverters.PureFileName}}" Margin="6,0,0,0"/>
|
|
||||||
</Grid>
|
|
||||||
</TreeDataTemplate>
|
|
||||||
</TreeView.ItemTemplate>
|
|
||||||
</TreeView>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<!-- Staged Toolbar -->
|
<!-- Staged Toolbar -->
|
||||||
<Border Grid.Row="2" BorderThickness="0,1" BorderBrush="{DynamicResource Brush.Border0}">
|
<Border Grid.Row="2" BorderThickness="0,1" BorderBrush="{DynamicResource Brush.Border0}">
|
||||||
<Grid ColumnDefinitions="Auto,Auto,Auto,Auto,*,Auto,Auto">
|
<Grid ColumnDefinitions="Auto,Auto,Auto,Auto,*,Auto,Auto">
|
||||||
<v:ChangeViewModeSwitcher Grid.Column="0" Width="14" Height="14" Margin="8,0,0,0" ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=StagedChangeViewMode, Mode=TwoWay}"/>
|
<v:ChangeViewModeSwitcher Grid.Column="0" Width="12" Height="12" Margin="8,0,0,0" ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=StagedChangeViewMode, Mode=TwoWay}"/>
|
||||||
<TextBlock Grid.Column="1" Text="{DynamicResource Text.WorkingCopy.Staged}" Foreground="{DynamicResource Brush.FG2}" FontWeight="Bold" Margin="8,0,0,0"/>
|
<TextBlock Grid.Column="1" Text="{DynamicResource Text.WorkingCopy.Staged}" Foreground="{DynamicResource Brush.FG2}" FontWeight="Bold" Margin="8,0,0,0"/>
|
||||||
<TextBlock Grid.Column="2" FontWeight="Bold" Foreground="{DynamicResource Brush.FG2}" Text="{Binding Staged, Converter={x:Static c:ListConverters.ToCount}}"/>
|
<TextBlock Grid.Column="2" FontWeight="Bold" Foreground="{DynamicResource Brush.FG2}" Text="{Binding Staged, Converter={x:Static c:ListConverters.ToCount}}"/>
|
||||||
<Path Grid.Column="3" Classes="rotating" Width="14" Height="14" Data="{StaticResource Icons.Loading}" Margin="8,0,0,0" IsVisible="{Binding IsUnstaging}"/>
|
<v:LoadingIcon Grid.Column="3" Width="14" Height="14" Margin="8,0,0,0" IsVisible="{Binding IsUnstaging}"/>
|
||||||
<Button Grid.Column="5" Classes="icon_button" Width="26" Height="14" Padding="0" Click="UnstageSelected">
|
<Button Grid.Column="5" Classes="icon_button" Width="26" Height="14" Padding="0" Command="{Binding UnstageSelected}">
|
||||||
<ToolTip.Tip>
|
<ToolTip.Tip>
|
||||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||||
<TextBlock Text="{DynamicResource Text.WorkingCopy.Staged.Unstage}" VerticalAlignment="Center"/>
|
<TextBlock Text="{DynamicResource Text.WorkingCopy.Staged.Unstage}" VerticalAlignment="Center"/>
|
||||||
|
@ -188,121 +90,23 @@
|
||||||
</ToolTip.Tip>
|
</ToolTip.Tip>
|
||||||
<Path Width="14" Height="14" Margin="0,6,0,0" Data="{StaticResource Icons.Up}"/>
|
<Path Width="14" Height="14" Margin="0,6,0,0" Data="{StaticResource Icons.Up}"/>
|
||||||
</Button>
|
</Button>
|
||||||
<Button Grid.Column="6" Classes="icon_button" Width="26" Height="14" Padding="0" ToolTip.Tip="{DynamicResource Text.WorkingCopy.Staged.UnstageAll}" Click="UnstageAll">
|
<Button Grid.Column="6" Classes="icon_button" Width="26" Height="14" Padding="0" ToolTip.Tip="{DynamicResource Text.WorkingCopy.Staged.UnstageAll}" Command="{Binding UnstageAll}">
|
||||||
<Path Width="14" Height="14" Data="{StaticResource Icons.DoubleUp}"/>
|
<Path Width="14" Height="14" Data="{StaticResource Icons.DoubleUp}"/>
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<!-- Staged Changes -->
|
<!-- Staged Changes -->
|
||||||
<Grid Grid.Row="3" Background="{DynamicResource Brush.Contents}">
|
<v:ChangeCollectionView Grid.Row="3"
|
||||||
<DataGrid x:Name="stagedList"
|
IsWorkingCopyChange="False"
|
||||||
Background="Transparent"
|
SingleSelect="False"
|
||||||
ItemsSource="{Binding Staged}"
|
Background="{DynamicResource Brush.Contents}"
|
||||||
SelectedItem="{Binding SelectedStagedChange, Mode=TwoWay}"
|
ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=StagedChangeViewMode}"
|
||||||
SelectionMode="Extended"
|
Changes="{Binding Staged}"
|
||||||
CanUserReorderColumns="False"
|
SelectedChanges="{Binding SelectedStaged, Mode=TwoWay}"
|
||||||
CanUserResizeColumns="False"
|
ContextRequested="OnStagedContextRequested"
|
||||||
CanUserSortColumns="False"
|
ChangeDoubleTapped="OnStagedChangeDoubleTapped"
|
||||||
IsReadOnly="True"
|
KeyDown="OnStagedKeyDown"/>
|
||||||
HeadersVisibility="None"
|
|
||||||
Focusable="False"
|
|
||||||
RowHeight="26"
|
|
||||||
HorizontalScrollBarVisibility="Auto"
|
|
||||||
VerticalScrollBarVisibility="Auto"
|
|
||||||
KeyDown="OnStagedListKeyDown"
|
|
||||||
ContextRequested="OnStagedListContextRequested"
|
|
||||||
IsVisible="{Binding Source={x:Static vm:Preference.Instance}, Path=StagedChangeViewMode, Converter={x:Static c:ChangeViewModeConverters.IsList}}">
|
|
||||||
<DataGrid.Columns>
|
|
||||||
<DataGridTemplateColumn Header="ICON">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<v:ChangeStatusIcon Width="14" Height="14" IsWorkingCopyChange="False" Change="{Binding}"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
|
|
||||||
<DataGridTemplateColumn Width="*" Header="PATH">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock Classes="monospace" Text="{Binding Path}" Margin="4,0,0,0"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
</DataGrid.Columns>
|
|
||||||
</DataGrid>
|
|
||||||
|
|
||||||
<DataGrid x:Name="stagedGrid"
|
|
||||||
Background="Transparent"
|
|
||||||
ItemsSource="{Binding Staged}"
|
|
||||||
SelectedItem="{Binding SelectedStagedChange, Mode=TwoWay}"
|
|
||||||
SelectionMode="Extended"
|
|
||||||
CanUserReorderColumns="False"
|
|
||||||
CanUserResizeColumns="False"
|
|
||||||
CanUserSortColumns="False"
|
|
||||||
IsReadOnly="True"
|
|
||||||
HeadersVisibility="None"
|
|
||||||
Focusable="False"
|
|
||||||
RowHeight="26"
|
|
||||||
HorizontalScrollBarVisibility="Auto"
|
|
||||||
VerticalScrollBarVisibility="Auto"
|
|
||||||
KeyDown="OnStagedListKeyDown"
|
|
||||||
ContextRequested="OnStagedListContextRequested"
|
|
||||||
IsVisible="{Binding Source={x:Static vm:Preference.Instance}, Path=StagedChangeViewMode, Converter={x:Static c:ChangeViewModeConverters.IsGrid}}">
|
|
||||||
<DataGrid.Columns>
|
|
||||||
<DataGridTemplateColumn Header="ICON">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<v:ChangeStatusIcon Width="14" Height="14" IsWorkingCopyChange="False" Change="{Binding}"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
|
|
||||||
<DataGridTemplateColumn Header="FILE_NAME">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock Classes="monospace" Text="{Binding Path, Converter={x:Static c:PathConverters.PureFileName}}" Margin="4,0,0,0"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
|
|
||||||
<DataGridTemplateColumn Header="FOLDER_PATH">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock Classes="monospace" Text="{Binding Path, Converter={x:Static c:PathConverters.PureDirectoryName}}" Margin="4,0,0,0" Foreground="{DynamicResource Brush.FG2}"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
</DataGrid.Columns>
|
|
||||||
</DataGrid>
|
|
||||||
|
|
||||||
<TreeView x:Name="stagedTree"
|
|
||||||
ItemsSource="{Binding StagedTree}"
|
|
||||||
SelectedItem="{Binding SelectedStagedTreeNode, Mode=TwoWay}"
|
|
||||||
SelectionMode="Multiple"
|
|
||||||
AutoScrollToSelectedItem="True"
|
|
||||||
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
|
||||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
|
||||||
KeyDown="OnStagedTreeViewKeyDown"
|
|
||||||
ContextRequested="OnStagedTreeViewContextRequested"
|
|
||||||
IsVisible="{Binding Source={x:Static vm:Preference.Instance}, Path=StagedChangeViewMode, Converter={x:Static c:ChangeViewModeConverters.IsTree}}">
|
|
||||||
<TreeView.Styles>
|
|
||||||
<Style Selector="TreeViewItem" x:DataType="vm:FileTreeNode">
|
|
||||||
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
|
|
||||||
</Style>
|
|
||||||
</TreeView.Styles>
|
|
||||||
|
|
||||||
<TreeView.ItemTemplate>
|
|
||||||
<TreeDataTemplate ItemsSource="{Binding Children}" x:DataType="{x:Type vm:FileTreeNode}">
|
|
||||||
<Grid Height="24" ColumnDefinitions="Auto,*">
|
|
||||||
<Path Grid.Column="0" Classes="folder_icon" Width="14" Height="14" Margin="0,2,0,0" IsVisible="{Binding IsFolder}" Fill="Goldenrod" VerticalAlignment="Center"/>
|
|
||||||
<v:ChangeStatusIcon Grid.Column="0" Width="14" Height="14" IsWorkingCopyChange="False" Change="{Binding Backend}" IsVisible="{Binding !IsFolder}"/>
|
|
||||||
<TextBlock Grid.Column="1" Classes="monospace" Text="{Binding FullPath, Converter={x:Static c:PathConverters.PureFileName}}" Margin="6,0,0,0"/>
|
|
||||||
</Grid>
|
|
||||||
</TreeDataTemplate>
|
|
||||||
</TreeView.ItemTemplate>
|
|
||||||
</TreeView>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<GridSplitter Grid.Column="1"
|
<GridSplitter Grid.Column="1"
|
||||||
|
@ -384,14 +188,9 @@
|
||||||
Margin="12,0,0,0"
|
Margin="12,0,0,0"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
IsChecked="{Binding UseAmend, Mode=TwoWay}"
|
IsChecked="{Binding UseAmend, Mode=TwoWay}"
|
||||||
Content="{DynamicResource Text.WorkingCopy.Amend}"
|
Content="{DynamicResource Text.WorkingCopy.Amend}"/>
|
||||||
Checked="StartAmend"/>
|
|
||||||
|
|
||||||
<Path Grid.Column="3"
|
<v:LoadingIcon Grid.Column="3" Width="18" Height="18" IsVisible="{Binding IsCommitting}"/>
|
||||||
Classes="rotating"
|
|
||||||
Width="18" Height="18"
|
|
||||||
Data="{StaticResource Icons.Loading}"
|
|
||||||
IsVisible="{Binding IsCommitting}"/>
|
|
||||||
|
|
||||||
<Button Grid.Column="4"
|
<Button Grid.Column="4"
|
||||||
Classes="flat primary"
|
Classes="flat primary"
|
||||||
|
@ -399,7 +198,7 @@
|
||||||
Height="28"
|
Height="28"
|
||||||
Margin="8,0,0,0"
|
Margin="8,0,0,0"
|
||||||
Padding="8,0"
|
Padding="8,0"
|
||||||
Click="Commit"/>
|
Command="{Binding Commit}"/>
|
||||||
|
|
||||||
<Button Grid.Column="5"
|
<Button Grid.Column="5"
|
||||||
Classes="flat"
|
Classes="flat"
|
||||||
|
@ -407,14 +206,8 @@
|
||||||
Height="28"
|
Height="28"
|
||||||
Margin="8,0,0,0"
|
Margin="8,0,0,0"
|
||||||
Padding="8,0"
|
Padding="8,0"
|
||||||
Click="CommitWithPush">
|
Command="{Binding CommitWithPush}"
|
||||||
<Button.IsVisible>
|
IsVisible="{Binding IsCommitWithPushVisible}"/>
|
||||||
<MultiBinding Converter="{x:Static BoolConverters.And}">
|
|
||||||
<Binding Path="$parent[v:Repository].DataContext.(vm:Repository).CanCommitWithPush"/>
|
|
||||||
<Binding Path="!UseAmend"/>
|
|
||||||
</MultiBinding>
|
|
||||||
</Button.IsVisible>
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.VisualTree;
|
|
||||||
|
|
||||||
namespace SourceGit.Views
|
namespace SourceGit.Views
|
||||||
{
|
{
|
||||||
|
@ -14,313 +11,6 @@ namespace SourceGit.Views
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ViewAssumeUnchanged(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
var repoPage = this.FindAncestorOfType<Repository>();
|
|
||||||
if (repoPage != null)
|
|
||||||
{
|
|
||||||
var repo = (repoPage.DataContext as ViewModels.Repository).FullPath;
|
|
||||||
var window = new AssumeUnchangedManager();
|
|
||||||
window.DataContext = new ViewModels.AssumeUnchangedManager(repo);
|
|
||||||
window.ShowDialog((Window)TopLevel.GetTopLevel(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StageSelected(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
var vm = DataContext as ViewModels.WorkingCopy;
|
|
||||||
if (vm == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
List<Models.Change> selected = new List<Models.Change>();
|
|
||||||
switch (ViewModels.Preference.Instance.UnstagedChangeViewMode)
|
|
||||||
{
|
|
||||||
case Models.ChangeViewMode.List:
|
|
||||||
foreach (var item in unstagedList.SelectedItems)
|
|
||||||
{
|
|
||||||
if (item is Models.Change change)
|
|
||||||
selected.Add(change);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Models.ChangeViewMode.Grid:
|
|
||||||
foreach (var item in unstagedGrid.SelectedItems)
|
|
||||||
{
|
|
||||||
if (item is Models.Change change)
|
|
||||||
selected.Add(change);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
foreach (var item in unstagedTree.SelectedItems)
|
|
||||||
{
|
|
||||||
if (item is ViewModels.FileTreeNode node)
|
|
||||||
CollectChangesFromNode(selected, node);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
vm.StageChanges(selected);
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StageAll(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
var vm = DataContext as ViewModels.WorkingCopy;
|
|
||||||
if (vm == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
vm.StageChanges(vm.Unstaged);
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UnstageSelected(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
var vm = DataContext as ViewModels.WorkingCopy;
|
|
||||||
if (vm == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
List<Models.Change> selected = new List<Models.Change>();
|
|
||||||
switch (ViewModels.Preference.Instance.StagedChangeViewMode)
|
|
||||||
{
|
|
||||||
case Models.ChangeViewMode.List:
|
|
||||||
foreach (var item in stagedList.SelectedItems)
|
|
||||||
{
|
|
||||||
if (item is Models.Change change)
|
|
||||||
selected.Add(change);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Models.ChangeViewMode.Grid:
|
|
||||||
foreach (var item in stagedGrid.SelectedItems)
|
|
||||||
{
|
|
||||||
if (item is Models.Change change)
|
|
||||||
selected.Add(change);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
foreach (var item in stagedTree.SelectedItems)
|
|
||||||
{
|
|
||||||
if (item is ViewModels.FileTreeNode node)
|
|
||||||
CollectChangesFromNode(selected, node);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
vm.UnstageChanges(selected);
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UnstageAll(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
var vm = DataContext as ViewModels.WorkingCopy;
|
|
||||||
if (vm == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
vm.UnstageChanges(vm.Staged);
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnUnstagedListKeyDown(object sender, KeyEventArgs e)
|
|
||||||
{
|
|
||||||
var datagrid = sender as DataGrid;
|
|
||||||
if (datagrid.SelectedItems.Count > 0 && e.Key == Key.Space && DataContext is ViewModels.WorkingCopy vm)
|
|
||||||
{
|
|
||||||
List<Models.Change> selected = new List<Models.Change>();
|
|
||||||
foreach (var item in datagrid.SelectedItems)
|
|
||||||
{
|
|
||||||
if (item is Models.Change change)
|
|
||||||
selected.Add(change);
|
|
||||||
}
|
|
||||||
|
|
||||||
vm.StageChanges(selected);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnUnstagedTreeViewKeyDown(object sender, KeyEventArgs e)
|
|
||||||
{
|
|
||||||
var tree = sender as TreeView;
|
|
||||||
if (tree.SelectedItems.Count > 0 && e.Key == Key.Space && DataContext is ViewModels.WorkingCopy vm)
|
|
||||||
{
|
|
||||||
List<Models.Change> selected = new List<Models.Change>();
|
|
||||||
foreach (var item in tree.SelectedItems)
|
|
||||||
{
|
|
||||||
if (item is ViewModels.FileTreeNode node)
|
|
||||||
CollectChangesFromNode(selected, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
vm.StageChanges(selected);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnStagedListKeyDown(object sender, KeyEventArgs e)
|
|
||||||
{
|
|
||||||
var datagrid = sender as DataGrid;
|
|
||||||
if (datagrid.SelectedItems.Count > 0 && e.Key == Key.Space && DataContext is ViewModels.WorkingCopy vm)
|
|
||||||
{
|
|
||||||
List<Models.Change> selected = new List<Models.Change>();
|
|
||||||
foreach (var item in datagrid.SelectedItems)
|
|
||||||
{
|
|
||||||
if (item is Models.Change change)
|
|
||||||
selected.Add(change);
|
|
||||||
}
|
|
||||||
|
|
||||||
vm.UnstageChanges(selected);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnStagedTreeViewKeyDown(object sender, KeyEventArgs e)
|
|
||||||
{
|
|
||||||
var tree = sender as TreeView;
|
|
||||||
if (tree.SelectedItems.Count > 0 && e.Key == Key.Space && DataContext is ViewModels.WorkingCopy vm)
|
|
||||||
{
|
|
||||||
List<Models.Change> selected = new List<Models.Change>();
|
|
||||||
foreach (var item in tree.SelectedItems)
|
|
||||||
{
|
|
||||||
if (item is ViewModels.FileTreeNode node)
|
|
||||||
CollectChangesFromNode(selected, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
vm.UnstageChanges(selected);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnUnstagedListContextRequested(object sender, ContextRequestedEventArgs e)
|
|
||||||
{
|
|
||||||
var datagrid = sender as DataGrid;
|
|
||||||
if (datagrid.SelectedItems.Count > 0 && DataContext is ViewModels.WorkingCopy vm)
|
|
||||||
{
|
|
||||||
List<Models.Change> selected = new List<Models.Change>();
|
|
||||||
foreach (var item in datagrid.SelectedItems)
|
|
||||||
{
|
|
||||||
if (item is Models.Change change)
|
|
||||||
selected.Add(change);
|
|
||||||
}
|
|
||||||
|
|
||||||
var menu = vm.CreateContextMenuForUnstagedChanges(selected);
|
|
||||||
datagrid.OpenContextMenu(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnUnstagedTreeViewContextRequested(object sender, ContextRequestedEventArgs e)
|
|
||||||
{
|
|
||||||
var tree = sender as TreeView;
|
|
||||||
if (tree.SelectedItems.Count > 0 && DataContext is ViewModels.WorkingCopy vm)
|
|
||||||
{
|
|
||||||
List<Models.Change> selected = new List<Models.Change>();
|
|
||||||
foreach (var item in tree.SelectedItems)
|
|
||||||
{
|
|
||||||
if (item is ViewModels.FileTreeNode node)
|
|
||||||
CollectChangesFromNode(selected, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
var menu = vm.CreateContextMenuForUnstagedChanges(selected);
|
|
||||||
tree.OpenContextMenu(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnStagedListContextRequested(object sender, ContextRequestedEventArgs e)
|
|
||||||
{
|
|
||||||
var datagrid = sender as DataGrid;
|
|
||||||
if (datagrid.SelectedItems.Count > 0 && DataContext is ViewModels.WorkingCopy vm)
|
|
||||||
{
|
|
||||||
List<Models.Change> selected = new List<Models.Change>();
|
|
||||||
foreach (var item in datagrid.SelectedItems)
|
|
||||||
{
|
|
||||||
if (item is Models.Change change)
|
|
||||||
selected.Add(change);
|
|
||||||
}
|
|
||||||
|
|
||||||
var menu = vm.CreateContextMenuForStagedChanges(selected);
|
|
||||||
datagrid.OpenContextMenu(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnStagedTreeViewContextRequested(object sender, ContextRequestedEventArgs e)
|
|
||||||
{
|
|
||||||
var tree = sender as TreeView;
|
|
||||||
if (tree.SelectedItems.Count > 0 && DataContext is ViewModels.WorkingCopy vm)
|
|
||||||
{
|
|
||||||
List<Models.Change> selected = new List<Models.Change>();
|
|
||||||
foreach (var item in tree.SelectedItems)
|
|
||||||
{
|
|
||||||
if (item is ViewModels.FileTreeNode node)
|
|
||||||
CollectChangesFromNode(selected, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
var menu = vm.CreateContextMenuForStagedChanges(selected);
|
|
||||||
tree.OpenContextMenu(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StartAmend(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
var repoPage = this.FindAncestorOfType<Repository>();
|
|
||||||
if (repoPage != null)
|
|
||||||
{
|
|
||||||
var repo = (repoPage.DataContext as ViewModels.Repository).FullPath;
|
|
||||||
var commits = new Commands.QueryCommits(repo, "-n 1", false).Result();
|
|
||||||
if (commits.Count == 0)
|
|
||||||
{
|
|
||||||
App.RaiseException(repo, "No commits to amend!!!");
|
|
||||||
|
|
||||||
var chkBox = sender as CheckBox;
|
|
||||||
chkBox.IsChecked = false;
|
|
||||||
e.Handled = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var vm = DataContext as ViewModels.WorkingCopy;
|
|
||||||
vm.CommitMessage = commits[0].FullMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Commit(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
var vm = DataContext as ViewModels.WorkingCopy;
|
|
||||||
vm.DoCommit(false);
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CommitWithPush(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
var vm = DataContext as ViewModels.WorkingCopy;
|
|
||||||
vm.DoCommit(true);
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CollectChangesFromNode(List<Models.Change> outs, ViewModels.FileTreeNode node)
|
|
||||||
{
|
|
||||||
if (node.IsFolder)
|
|
||||||
{
|
|
||||||
foreach (var child in node.Children)
|
|
||||||
CollectChangesFromNode(outs, child);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var change = node.Backend as Models.Change;
|
|
||||||
if (change != null && !outs.Contains(change))
|
|
||||||
outs.Add(change);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnOpenCommitMessagePicker(object sender, RoutedEventArgs e)
|
private void OnOpenCommitMessagePicker(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (sender is Button button && DataContext is ViewModels.WorkingCopy vm)
|
if (sender is Button button && DataContext is ViewModels.WorkingCopy vm)
|
||||||
|
@ -331,5 +21,61 @@ namespace SourceGit.Views
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnUnstagedContextRequested(object sender, ContextRequestedEventArgs e)
|
||||||
|
{
|
||||||
|
if (DataContext is ViewModels.WorkingCopy vm)
|
||||||
|
{
|
||||||
|
var menu = vm.CreateContextMenuForUnstagedChanges();
|
||||||
|
(sender as Control)?.OpenContextMenu(menu);
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStagedContextRequested(object sender, ContextRequestedEventArgs e)
|
||||||
|
{
|
||||||
|
if (DataContext is ViewModels.WorkingCopy vm)
|
||||||
|
{
|
||||||
|
var menu = vm.CreateContextMenuForStagedChanges();
|
||||||
|
(sender as Control)?.OpenContextMenu(menu);
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUnstagedChangeDoubleTapped(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (DataContext is ViewModels.WorkingCopy vm)
|
||||||
|
{
|
||||||
|
vm.StageSelected();
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStagedChangeDoubleTapped(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (DataContext is ViewModels.WorkingCopy vm)
|
||||||
|
{
|
||||||
|
vm.UnstageSelected();
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUnstagedKeyDown(object sender, KeyEventArgs e)
|
||||||
|
{
|
||||||
|
if (DataContext is ViewModels.WorkingCopy vm && e.Key == Key.Space)
|
||||||
|
{
|
||||||
|
vm.StageSelected();
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStagedKeyDown(object sender, KeyEventArgs e)
|
||||||
|
{
|
||||||
|
if (DataContext is ViewModels.WorkingCopy vm && e.Key == Key.Space)
|
||||||
|
{
|
||||||
|
vm.UnstageSelected();
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue