Merge branch 'release/v8.22'
6
.github/workflows/ci.yml
vendored
|
@ -9,7 +9,7 @@ on:
|
|||
jobs:
|
||||
build-windows:
|
||||
name: Build Windows x64
|
||||
runs-on: windows-latest
|
||||
runs-on: windows-2019
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v4
|
||||
|
@ -30,7 +30,7 @@ jobs:
|
|||
path: publish
|
||||
build-macos-intel:
|
||||
name: Build macOS (Intel)
|
||||
runs-on: macos-latest
|
||||
runs-on: macos-13
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v4
|
||||
|
@ -76,7 +76,7 @@ jobs:
|
|||
path: sourcegit.osx-arm64.tar
|
||||
build-linux:
|
||||
name: Build Linux
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v4
|
||||
|
|
26
README.md
|
@ -41,7 +41,7 @@ Opensource Git GUI client.
|
|||
|
||||
You can download the latest stable from [Releases](https://github.com/sourcegit-scm/sourcegit/releases/latest) or download workflow artifacts from [Github Actions](https://github.com/sourcegit-scm/sourcegit/actions) to try this app based on latest commits.
|
||||
|
||||
This software creates a folder `$"{System.Environment.SpecialFolder.ApplicationData}/SourceGit"`, which is platform-dependent, to store user settings, downloaded avatars and crash logs.
|
||||
This software creates a folder `$"{System.Environment.SpecialFolder.ApplicationData}/SourceGit"`, which is platform-dependent, to store user settings, downloaded avatars and crash logs.
|
||||
|
||||
| OS | PATH |
|
||||
|---------|-------------------------------------------------|
|
||||
|
@ -52,8 +52,16 @@ This software creates a folder `$"{System.Environment.SpecialFolder.ApplicationD
|
|||
For **Windows** users:
|
||||
|
||||
* **MSYS Git is NOT supported**. Please use official [Git for Windows](https://git-scm.com/download/win) instead.
|
||||
* You can install the latest stable by `winget install SourceGit`.
|
||||
- Note: `winget` will install this software as a commandline tool. You need run `SourceGit` from console or `Win+R` at the first time. Then you can add it to the taskbar.
|
||||
* You can install the latest stable from `winget` with follow commands:
|
||||
```shell
|
||||
winget install SourceGit
|
||||
```
|
||||
> `winget` will install this software as a commandline tool. You need run `SourceGit` from console or `Win+R` at the first time. Then you can add it to the taskbar.
|
||||
* You can install the latest stable by `scoope` with follow commands:
|
||||
```shell
|
||||
scoop bucket add extras
|
||||
scoop install sourcegit
|
||||
```
|
||||
* Portable versions can be found in [Releases](https://github.com/sourcegit-scm/sourcegit/releases/latest)
|
||||
|
||||
For **macOS** users:
|
||||
|
@ -68,7 +76,7 @@ For **Linux** users:
|
|||
|
||||
* `xdg-open` must be installed to support open native file manager.
|
||||
* Make sure [git-credential-manager](https://github.com/git-ecosystem/git-credential-manager/releases) is installed on your linux.
|
||||
* Maybe you need to set environment variable `AVALONIA_SCREEN_SCALE_FACTORS`. See https://github.com/AvaloniaUI/Avalonia/wiki/Configuring-X11-per-monitor-DPI.
|
||||
* Maybe you need to set environment variable `AVALONIA_SCREEN_SCALE_FACTORS`. See https://github.com/AvaloniaUI/Avalonia/wiki/Configuring-X11-per-monitor-DPI.
|
||||
|
||||
## External Tools
|
||||
|
||||
|
@ -82,7 +90,7 @@ This app supports open repository in external tools listed in the table below.
|
|||
| JetBrains Fleet | YES | YES | YES | FLEET_PATH |
|
||||
| Sublime Text | YES | YES | YES | SUBLIME_TEXT_PATH |
|
||||
|
||||
* You can set the given environment variable for special tool if it can NOT be found by this app automatically.
|
||||
* You can set the given environment variable for special tool if it can NOT be found by this app automatically.
|
||||
* Installing `JetBrains Toolbox` will help this app to find other JetBrains tools installed on your device.
|
||||
* On macOS, you may need to use `launchctl setenv` to make sure the app can read these environment variables.
|
||||
|
||||
|
@ -90,15 +98,15 @@ This app supports open repository in external tools listed in the table below.
|
|||
|
||||
* Dark Theme
|
||||
|
||||
![Theme Dark](./screenshots/theme_dark.png)
|
||||
![Theme Dark](./screenshots/theme_dark.png)
|
||||
|
||||
* Light Theme
|
||||
|
||||
![Theme Light](./screenshots/theme_light.png)
|
||||
![Theme Light](./screenshots/theme_light.png)
|
||||
|
||||
* Custom Themes
|
||||
* Custom
|
||||
|
||||
You can find custom themes from [sourcegit-theme](https://github.com/sourcegit-scm/sourcegit-theme.git)
|
||||
You can find custom themes from [sourcegit-theme](https://github.com/sourcegit-scm/sourcegit-theme.git)
|
||||
|
||||
## Contributing
|
||||
|
||||
|
|
2
VERSION
|
@ -1 +1 @@
|
|||
8.21
|
||||
8.22
|
|
@ -188,6 +188,8 @@ namespace SourceGit
|
|||
else
|
||||
Models.CommitGraph.SetDefaultPens(overrides.GraphPenThickness);
|
||||
|
||||
Models.Commit.OpacityForNotMerged = overrides.OpacityForNotMergedCommits;
|
||||
|
||||
app.Resources.MergedDictionaries.Add(resDic);
|
||||
app._themeOverrides = resDic;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,11 @@ namespace SourceGit.Commands
|
|||
|
||||
public Diff(string repo, Models.DiffOption opt, int unified)
|
||||
{
|
||||
_result.TextDiff = new Models.TextDiff() {
|
||||
Repo = repo,
|
||||
Option = opt,
|
||||
};
|
||||
|
||||
WorkingDirectory = repo;
|
||||
Context = repo;
|
||||
Args = $"diff --ignore-cr-at-eol --unified={unified} {opt}";
|
||||
|
@ -214,7 +219,7 @@ namespace SourceGit.Commands
|
|||
}
|
||||
}
|
||||
|
||||
private readonly Models.DiffResult _result = new Models.DiffResult() { TextDiff = new Models.TextDiff() };
|
||||
private readonly Models.DiffResult _result = new Models.DiffResult();
|
||||
private readonly List<Models.TextDiffLine> _deleted = new List<Models.TextDiffLine>();
|
||||
private readonly List<Models.TextDiffLine> _added = new List<Models.TextDiffLine>();
|
||||
private int _oldLine = 0;
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace SourceGit.Commands
|
|||
WorkingDirectory = repo;
|
||||
Context = repo;
|
||||
TraitErrorAsOutput = true;
|
||||
Args = "gc";
|
||||
Args = "gc --prune";
|
||||
}
|
||||
|
||||
protected override void OnReadline(string line)
|
||||
|
|
|
@ -24,20 +24,8 @@ namespace SourceGit.Commands
|
|||
{
|
||||
Exec();
|
||||
|
||||
foreach (var b in _branches)
|
||||
{
|
||||
if (b.IsLocal && !string.IsNullOrEmpty(b.UpstreamTrackStatus))
|
||||
{
|
||||
if (b.UpstreamTrackStatus == "=")
|
||||
{
|
||||
b.UpstreamTrackStatus = string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
b.UpstreamTrackStatus = ParseTrackStatus(b.Name, b.Upstream);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (var b in _needQueryTrackStatus)
|
||||
b.TrackStatus = new QueryTrackStatus(WorkingDirectory, b.Name, b.Upstream).Result();
|
||||
|
||||
return _branches;
|
||||
}
|
||||
|
@ -84,35 +72,16 @@ namespace SourceGit.Commands
|
|||
branch.Head = parts[1];
|
||||
branch.IsCurrent = parts[2] == "*";
|
||||
branch.Upstream = parts[3];
|
||||
branch.UpstreamTrackStatus = parts[4];
|
||||
|
||||
if (branch.IsLocal && !parts[4].Equals("=", StringComparison.Ordinal))
|
||||
_needQueryTrackStatus.Add(branch);
|
||||
else
|
||||
branch.TrackStatus = new Models.BranchTrackStatus();
|
||||
|
||||
_branches.Add(branch);
|
||||
}
|
||||
|
||||
private string ParseTrackStatus(string local, string upstream)
|
||||
{
|
||||
var cmd = new Command();
|
||||
cmd.WorkingDirectory = WorkingDirectory;
|
||||
cmd.Context = Context;
|
||||
cmd.Args = $"rev-list --left-right --count {local}...{upstream}";
|
||||
|
||||
var rs = cmd.ReadToEnd();
|
||||
if (!rs.IsSuccess)
|
||||
return string.Empty;
|
||||
|
||||
var match = REG_AHEAD_BEHIND().Match(rs.StdOut);
|
||||
if (!match.Success)
|
||||
return string.Empty;
|
||||
|
||||
var ahead = int.Parse(match.Groups[1].Value);
|
||||
var behind = int.Parse(match.Groups[2].Value);
|
||||
var track = "";
|
||||
if (ahead > 0)
|
||||
track += $"{ahead}↑";
|
||||
if (behind > 0)
|
||||
track += $" {behind}↓";
|
||||
return track.Trim();
|
||||
}
|
||||
|
||||
private readonly List<Models.Branch> _branches = new List<Models.Branch>();
|
||||
private List<Models.Branch> _needQueryTrackStatus = new List<Models.Branch>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,20 +14,29 @@ namespace SourceGit.Commands
|
|||
_findFirstMerged = needFindHead;
|
||||
}
|
||||
|
||||
public QueryCommits(string repo, int maxCount, string messageFilter)
|
||||
public QueryCommits(string repo, int maxCount, string messageFilter, bool isFile)
|
||||
{
|
||||
var argsBuilder = new StringBuilder();
|
||||
var words = messageFilter.Split(new[] { ' ', '\t', '\r' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var word in words)
|
||||
string search;
|
||||
if (isFile)
|
||||
{
|
||||
var escaped = word.Trim().Replace("\"", "\\\"", StringComparison.Ordinal);
|
||||
argsBuilder.Append($"--grep=\"{escaped}\" ");
|
||||
search = $"-- \"{messageFilter}\"";
|
||||
}
|
||||
else
|
||||
{
|
||||
var argsBuilder = new StringBuilder();
|
||||
var words = messageFilter.Split(new[] { ' ', '\t', '\r' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var word in words)
|
||||
{
|
||||
var escaped = word.Trim().Replace("\"", "\\\"", StringComparison.Ordinal);
|
||||
argsBuilder.Append($"--grep=\"{escaped}\" ");
|
||||
}
|
||||
argsBuilder.Append("--all-match -i");
|
||||
search = argsBuilder.ToString();
|
||||
}
|
||||
argsBuilder.Append("--all-match");
|
||||
|
||||
WorkingDirectory = repo;
|
||||
Context = repo;
|
||||
Args = $"log -{maxCount} --date-order --no-show-signature --decorate=full --pretty=format:%H%n%P%n%D%n%aN±%aE%n%at%n%cN±%cE%n%ct%n%s " + argsBuilder.ToString();
|
||||
Args = $"log -{maxCount} --date-order --no-show-signature --decorate=full --pretty=format:%H%n%P%n%D%n%aN±%aE%n%at%n%cN±%cE%n%ct%n%s --branches --remotes " + search;
|
||||
_findFirstMerged = false;
|
||||
}
|
||||
|
||||
|
@ -53,7 +62,9 @@ namespace SourceGit.Commands
|
|||
ParseParent(line);
|
||||
break;
|
||||
case 2:
|
||||
ParseDecorators(line);
|
||||
_current.ParseDecorators(line);
|
||||
if (_current.IsMerged && !_isHeadFounded)
|
||||
_isHeadFounded = true;
|
||||
break;
|
||||
case 3:
|
||||
_current.Author = Models.User.FindOrAdd(line);
|
||||
|
@ -104,74 +115,6 @@ namespace SourceGit.Commands
|
|||
_current.Parents.Add(data.Substring(idx + 1));
|
||||
}
|
||||
|
||||
private void ParseDecorators(string data)
|
||||
{
|
||||
if (data.Length < 3)
|
||||
return;
|
||||
|
||||
var subs = data.Split(',', StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var sub in subs)
|
||||
{
|
||||
var d = sub.Trim();
|
||||
if (d.EndsWith("/HEAD", StringComparison.Ordinal))
|
||||
continue;
|
||||
|
||||
if (d.StartsWith("tag: refs/tags/", StringComparison.Ordinal))
|
||||
{
|
||||
_current.Decorators.Add(new Models.Decorator()
|
||||
{
|
||||
Type = Models.DecoratorType.Tag,
|
||||
Name = d.Substring(15),
|
||||
});
|
||||
}
|
||||
else if (d.StartsWith("HEAD -> refs/heads/", StringComparison.Ordinal))
|
||||
{
|
||||
_current.IsMerged = true;
|
||||
_current.Decorators.Add(new Models.Decorator()
|
||||
{
|
||||
Type = Models.DecoratorType.CurrentBranchHead,
|
||||
Name = d.Substring(19),
|
||||
});
|
||||
}
|
||||
else if (d.Equals("HEAD"))
|
||||
{
|
||||
_current.IsMerged = true;
|
||||
_current.Decorators.Add(new Models.Decorator()
|
||||
{
|
||||
Type = Models.DecoratorType.CurrentCommitHead,
|
||||
Name = d,
|
||||
});
|
||||
}
|
||||
else if (d.StartsWith("refs/heads/", StringComparison.Ordinal))
|
||||
{
|
||||
_current.Decorators.Add(new Models.Decorator()
|
||||
{
|
||||
Type = Models.DecoratorType.LocalBranchHead,
|
||||
Name = d.Substring(11),
|
||||
});
|
||||
}
|
||||
else if (d.StartsWith("refs/remotes/", StringComparison.Ordinal))
|
||||
{
|
||||
_current.Decorators.Add(new Models.Decorator()
|
||||
{
|
||||
Type = Models.DecoratorType.RemoteBranchHead,
|
||||
Name = d.Substring(13),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_current.Decorators.Sort((l, r) =>
|
||||
{
|
||||
if (l.Type != r.Type)
|
||||
return (int)l.Type - (int)r.Type;
|
||||
else
|
||||
return string.Compare(l.Name, r.Name, StringComparison.Ordinal);
|
||||
});
|
||||
|
||||
if (_current.IsMerged && !_isHeadFounded)
|
||||
_isHeadFounded = true;
|
||||
}
|
||||
|
||||
private void MarkFirstMerged()
|
||||
{
|
||||
Args = $"log --since=\"{_commits[^1].CommitterTimeStr}\" --format=\"%H\"";
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace SourceGit.Commands
|
|||
if (!string.IsNullOrEmpty(lines[1]))
|
||||
commit.Parents.AddRange(lines[1].Split(' ', StringSplitOptions.RemoveEmptyEntries));
|
||||
if (!string.IsNullOrEmpty(lines[2]))
|
||||
commit.IsMerged = ParseDecorators(commit.Decorators, lines[2]);
|
||||
commit.ParseDecorators(lines[2]);
|
||||
commit.Author = Models.User.FindOrAdd(lines[3]);
|
||||
commit.AuthorTime = ulong.Parse(lines[4]);
|
||||
commit.Committer = Models.User.FindOrAdd(lines[5]);
|
||||
|
@ -39,70 +39,6 @@ namespace SourceGit.Commands
|
|||
return null;
|
||||
}
|
||||
|
||||
private bool ParseDecorators(List<Models.Decorator> decorators, string data)
|
||||
{
|
||||
bool isHeadOfCurrent = false;
|
||||
|
||||
var subs = data.Split(',', StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var sub in subs)
|
||||
{
|
||||
var d = sub.Trim();
|
||||
if (d.EndsWith("/HEAD", StringComparison.Ordinal))
|
||||
continue;
|
||||
|
||||
if (d.StartsWith("tag: refs/tags/", StringComparison.Ordinal))
|
||||
{
|
||||
decorators.Add(new Models.Decorator()
|
||||
{
|
||||
Type = Models.DecoratorType.Tag,
|
||||
Name = d.Substring(15),
|
||||
});
|
||||
}
|
||||
else if (d.StartsWith("HEAD -> refs/heads/", StringComparison.Ordinal))
|
||||
{
|
||||
isHeadOfCurrent = true;
|
||||
decorators.Add(new Models.Decorator()
|
||||
{
|
||||
Type = Models.DecoratorType.CurrentBranchHead,
|
||||
Name = d.Substring(19),
|
||||
});
|
||||
}
|
||||
else if (d.Equals("HEAD"))
|
||||
{
|
||||
isHeadOfCurrent = true;
|
||||
decorators.Add(new Models.Decorator()
|
||||
{
|
||||
Type = Models.DecoratorType.CurrentCommitHead,
|
||||
Name = d,
|
||||
});
|
||||
}
|
||||
else if (d.StartsWith("refs/heads/", StringComparison.Ordinal))
|
||||
{
|
||||
decorators.Add(new Models.Decorator()
|
||||
{
|
||||
Type = Models.DecoratorType.LocalBranchHead,
|
||||
Name = d.Substring(11),
|
||||
});
|
||||
}
|
||||
else if (d.StartsWith("refs/remotes/", StringComparison.Ordinal))
|
||||
{
|
||||
decorators.Add(new Models.Decorator()
|
||||
{
|
||||
Type = Models.DecoratorType.RemoteBranchHead,
|
||||
Name = d.Substring(13),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
decorators.Sort((l, r) =>
|
||||
{
|
||||
if (l.Type != r.Type)
|
||||
return (int)l.Type - (int)r.Type;
|
||||
else
|
||||
return string.Compare(l.Name, r.Name, StringComparison.Ordinal);
|
||||
});
|
||||
|
||||
return isHeadOfCurrent;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
34
src/Commands/QueryTrackStatus.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
|
||||
namespace SourceGit.Commands
|
||||
{
|
||||
public class QueryTrackStatus : Command
|
||||
{
|
||||
public QueryTrackStatus(string repo, string local, string upstream)
|
||||
{
|
||||
WorkingDirectory = repo;
|
||||
Context = repo;
|
||||
Args = $"rev-list --left-right {local}...{upstream}";
|
||||
}
|
||||
|
||||
public Models.BranchTrackStatus Result()
|
||||
{
|
||||
var status = new Models.BranchTrackStatus();
|
||||
|
||||
var rs = ReadToEnd();
|
||||
if (!rs.IsSuccess)
|
||||
return status;
|
||||
|
||||
var lines = rs.StdOut.Split(['\n', '\r'], StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (line[0] == '>')
|
||||
status.Behind.Add(line.Substring(1));
|
||||
else
|
||||
status.Ahead.Add(line.Substring(1));
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
using Avalonia.Data.Converters;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Media;
|
||||
|
||||
namespace SourceGit.Converters
|
||||
|
@ -6,9 +8,12 @@ namespace SourceGit.Converters
|
|||
public static class BookmarkConverters
|
||||
{
|
||||
public static readonly FuncValueConverter<int, IBrush> ToBrush =
|
||||
new FuncValueConverter<int, IBrush>(bookmark => Models.Bookmarks.Brushes[bookmark]);
|
||||
|
||||
public static readonly FuncValueConverter<int, double> ToStrokeThickness =
|
||||
new FuncValueConverter<int, double>(bookmark => bookmark == 0 ? 1.0 : 0);
|
||||
new FuncValueConverter<int, IBrush>(bookmark =>
|
||||
{
|
||||
if (bookmark == 0)
|
||||
return Application.Current?.FindResource("Brush.FG1") as IBrush;
|
||||
else
|
||||
return Models.Bookmarks.Brushes[bookmark];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Media;
|
||||
|
||||
namespace SourceGit.Converters
|
||||
{
|
||||
|
@ -7,11 +6,5 @@ namespace SourceGit.Converters
|
|||
{
|
||||
public static readonly FuncValueConverter<bool, double> ToPageTabWidth =
|
||||
new FuncValueConverter<bool, double>(x => x ? 200 : double.NaN);
|
||||
|
||||
public static readonly FuncValueConverter<bool, double> HalfIfFalse =
|
||||
new FuncValueConverter<bool, double>(x => x ? 1 : 0.5);
|
||||
|
||||
public static readonly FuncValueConverter<bool, FontWeight> BoldIfTrue =
|
||||
new FuncValueConverter<bool, FontWeight>(x => x ? FontWeight.Bold : FontWeight.Regular);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,26 @@
|
|||
namespace SourceGit.Models
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SourceGit.Models
|
||||
{
|
||||
public class BranchTrackStatus
|
||||
{
|
||||
public List<string> Ahead { get; set; } = new List<string>();
|
||||
public List<string> Behind { get; set; } = new List<string>();
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (Ahead.Count == 0 && Behind.Count == 0)
|
||||
return string.Empty;
|
||||
|
||||
var track = "";
|
||||
if (Ahead.Count > 0)
|
||||
track += $"{Ahead.Count}↑";
|
||||
if (Behind.Count > 0)
|
||||
track += $" {Behind.Count}↓";
|
||||
return track.Trim();
|
||||
}
|
||||
}
|
||||
|
||||
public class Branch
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
@ -8,7 +29,7 @@
|
|||
public bool IsLocal { get; set; }
|
||||
public bool IsCurrent { get; set; }
|
||||
public string Upstream { get; set; }
|
||||
public string UpstreamTrackStatus { get; set; }
|
||||
public BranchTrackStatus TrackStatus { get; set; }
|
||||
public string Remote { get; set; }
|
||||
public bool IsHead { get; set; }
|
||||
|
||||
|
|
|
@ -2,11 +2,18 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
using Avalonia;
|
||||
using Avalonia.Media;
|
||||
|
||||
namespace SourceGit.Models
|
||||
{
|
||||
public class Commit
|
||||
{
|
||||
public static double OpacityForNotMerged
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} = 0.65;
|
||||
|
||||
public string SHA { get; set; } = string.Empty;
|
||||
public User Author { get; set; } = User.Invalid;
|
||||
public ulong AuthorTime { get; set; } = 0;
|
||||
|
@ -16,7 +23,10 @@ namespace SourceGit.Models
|
|||
public List<string> Parents { get; set; } = new List<string>();
|
||||
public List<Decorator> Decorators { get; set; } = new List<Decorator>();
|
||||
public bool HasDecorators => Decorators.Count > 0;
|
||||
|
||||
public bool IsMerged { get; set; } = false;
|
||||
public bool CanPushToUpstream { get; set; } = false;
|
||||
public bool CanPullFromUpstream { get; set; } = false;
|
||||
public Thickness Margin { get; set; } = new Thickness(0);
|
||||
|
||||
public string AuthorTimeStr => DateTime.UnixEpoch.AddSeconds(AuthorTime).ToLocalTime().ToString("yyyy/MM/dd HH:mm:ss");
|
||||
|
@ -25,5 +35,73 @@ namespace SourceGit.Models
|
|||
|
||||
public bool IsCommitterVisible => !Author.Equals(Committer) || AuthorTime != CommitterTime;
|
||||
public bool IsCurrentHead => Decorators.Find(x => x.Type is DecoratorType.CurrentBranchHead or DecoratorType.CurrentCommitHead) != null;
|
||||
|
||||
public double Opacity => IsMerged ? 1 : OpacityForNotMerged;
|
||||
public FontWeight FontWeight => IsCurrentHead ? FontWeight.Bold : FontWeight.Regular;
|
||||
|
||||
public void ParseDecorators(string data)
|
||||
{
|
||||
if (data.Length < 3)
|
||||
return;
|
||||
|
||||
var subs = data.Split(',', StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var sub in subs)
|
||||
{
|
||||
var d = sub.Trim();
|
||||
if (d.EndsWith("/HEAD", StringComparison.Ordinal))
|
||||
continue;
|
||||
|
||||
if (d.StartsWith("tag: refs/tags/", StringComparison.Ordinal))
|
||||
{
|
||||
Decorators.Add(new Decorator()
|
||||
{
|
||||
Type = DecoratorType.Tag,
|
||||
Name = d.Substring(15),
|
||||
});
|
||||
}
|
||||
else if (d.StartsWith("HEAD -> refs/heads/", StringComparison.Ordinal))
|
||||
{
|
||||
IsMerged = true;
|
||||
Decorators.Add(new Decorator()
|
||||
{
|
||||
Type = DecoratorType.CurrentBranchHead,
|
||||
Name = d.Substring(19),
|
||||
});
|
||||
}
|
||||
else if (d.Equals("HEAD"))
|
||||
{
|
||||
IsMerged = true;
|
||||
Decorators.Add(new Decorator()
|
||||
{
|
||||
Type = DecoratorType.CurrentCommitHead,
|
||||
Name = d,
|
||||
});
|
||||
}
|
||||
else if (d.StartsWith("refs/heads/", StringComparison.Ordinal))
|
||||
{
|
||||
Decorators.Add(new Decorator()
|
||||
{
|
||||
Type = DecoratorType.LocalBranchHead,
|
||||
Name = d.Substring(11),
|
||||
});
|
||||
}
|
||||
else if (d.StartsWith("refs/remotes/", StringComparison.Ordinal))
|
||||
{
|
||||
Decorators.Add(new Decorator()
|
||||
{
|
||||
Type = DecoratorType.RemoteBranchHead,
|
||||
Name = d.Substring(13),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Decorators.Sort((l, r) =>
|
||||
{
|
||||
if (l.Type != r.Type)
|
||||
return (int)l.Type - (int)r.Type;
|
||||
else
|
||||
return string.Compare(l.Name, r.Name, StringComparison.Ordinal);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -128,7 +128,7 @@ namespace SourceGit.Models
|
|||
_penCount = colors.Count;
|
||||
}
|
||||
|
||||
public static CommitGraph Parse(List<Commit> commits)
|
||||
public static CommitGraph Parse(List<Commit> commits, HashSet<string> canPushCommits, HashSet<string> canPullCommits)
|
||||
{
|
||||
double UNIT_WIDTH = 12;
|
||||
double HALF_WIDTH = 6;
|
||||
|
@ -148,6 +148,9 @@ namespace SourceGit.Models
|
|||
var isMerged = commit.IsMerged;
|
||||
var oldCount = unsolved.Count;
|
||||
|
||||
commit.CanPushToUpstream = canPushCommits.Remove(commit.SHA);
|
||||
commit.CanPullFromUpstream = canPullCommits.Remove(commit.SHA);
|
||||
|
||||
// Update current y offset
|
||||
offsetY += UNIT_HEIGHT;
|
||||
|
||||
|
|
|
@ -66,6 +66,83 @@ namespace SourceGit.Models
|
|||
public Vector SyncScrollOffset { get; set; } = Vector.Zero;
|
||||
public int MaxLineNumber = 0;
|
||||
|
||||
public string Repo { get; set; } = null;
|
||||
public DiffOption Option { get; set; } = null;
|
||||
|
||||
public TextDiffSelection MakeSelection(int startLine, int endLine, bool isCombined, bool isOldSide)
|
||||
{
|
||||
var rs = new TextDiffSelection();
|
||||
rs.StartLine = startLine;
|
||||
rs.EndLine = endLine;
|
||||
|
||||
for (int i = 0; i < startLine - 1; i++)
|
||||
{
|
||||
var line = Lines[i];
|
||||
if (line.Type == TextDiffLineType.Added)
|
||||
{
|
||||
rs.HasLeftChanges = true;
|
||||
rs.IgnoredAdds++;
|
||||
}
|
||||
else if (line.Type == TextDiffLineType.Deleted)
|
||||
{
|
||||
rs.HasLeftChanges = true;
|
||||
rs.IgnoredDeletes++;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = startLine - 1; i < endLine; i++)
|
||||
{
|
||||
var line = Lines[i];
|
||||
if (line.Type == TextDiffLineType.Added)
|
||||
{
|
||||
if (isCombined)
|
||||
{
|
||||
rs.HasChanges = true;
|
||||
break;
|
||||
}
|
||||
else if (isOldSide)
|
||||
{
|
||||
rs.HasLeftChanges = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
rs.HasChanges = true;
|
||||
}
|
||||
}
|
||||
else if (line.Type == TextDiffLineType.Deleted)
|
||||
{
|
||||
if (isCombined)
|
||||
{
|
||||
rs.HasChanges = true;
|
||||
break;
|
||||
}
|
||||
else if (isOldSide)
|
||||
{
|
||||
rs.HasChanges = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
rs.HasLeftChanges = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!rs.HasLeftChanges)
|
||||
{
|
||||
for (int i = endLine; i < Lines.Count; i++)
|
||||
{
|
||||
var line = Lines[i];
|
||||
if (line.Type == TextDiffLineType.Added || line.Type == TextDiffLineType.Deleted)
|
||||
{
|
||||
rs.HasLeftChanges = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
public void GenerateNewPatchFromSelection(Change change, string fileBlobGuid, TextDiffSelection selection, bool revert, string output)
|
||||
{
|
||||
var isTracked = !string.IsNullOrEmpty(fileBlobGuid);
|
||||
|
@ -389,9 +466,6 @@ namespace SourceGit.Models
|
|||
System.IO.File.WriteAllText(output, builder.ToString());
|
||||
}
|
||||
|
||||
[GeneratedRegex(@"^@@ \-(\d+),?\d* \+(\d+),?\d* @@")]
|
||||
private static partial Regex REG_INDICATOR();
|
||||
|
||||
private bool ProcessIndicatorForPatch(StringBuilder builder, TextDiffLine indicator, int idx, int start, int end, int ignoreRemoves, int ignoreAdds, bool revert, bool tailed)
|
||||
{
|
||||
var match = REG_INDICATOR().Match(indicator.Content);
|
||||
|
@ -551,6 +625,9 @@ namespace SourceGit.Models
|
|||
builder.Append($"\n@@ -{oldStart},{oldCount} +{newStart},{newCount} @@");
|
||||
return true;
|
||||
}
|
||||
|
||||
[GeneratedRegex(@"^@@ \-(\d+),?\d* \+(\d+),?\d* @@")]
|
||||
private static partial Regex REG_INDICATOR();
|
||||
}
|
||||
|
||||
public class LFSDiff
|
||||
|
|
|
@ -8,6 +8,7 @@ namespace SourceGit.Models
|
|||
{
|
||||
public Dictionary<string, Color> BasicColors { get; set; } = new Dictionary<string, Color>();
|
||||
public double GraphPenThickness { get; set; } = 2;
|
||||
public double OpacityForNotMergedCommits { get; set; } = 0.5;
|
||||
public List<Color> GraphColors { get; set; } = new List<Color>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -164,7 +164,7 @@ namespace SourceGit.Models
|
|||
|
||||
private void OnRepositoryChanged(object o, FileSystemEventArgs e)
|
||||
{
|
||||
if (string.IsNullOrEmpty(e.Name))
|
||||
if (string.IsNullOrEmpty(e.Name) || e.Name.EndsWith(".lock", StringComparison.Ordinal))
|
||||
return;
|
||||
|
||||
var name = e.Name.Replace("\\", "/");
|
||||
|
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 3 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 3 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 3 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 2.5 KiB |
|
@ -1,10 +1,11 @@
|
|||
<ResourceDictionary xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<StreamGeometry x:Key="Icons.Archive">M715 254h-405l-58 57h520zm-492 86v201h578V340zm405 143h-29v-29H425v29h-29v-57h231v57zm-405 295h578V559H223zm174-133h231v57h-29v-29H425v29h-29v-57z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Archive">M296 392h64v64h-64zM296 582v160h128V582h-64v-62h-64v62zm80 48v64h-32v-64h32zM360 328h64v64h-64zM296 264h64v64h-64zM360 456h64v64h-64zM360 200h64v64h-64zM855 289 639 73c-6-6-14-9-23-9H192c-18 0-32 14-32 32v832c0 18 14 32 32 32h640c18 0 32-14 32-32V311c0-9-3-17-9-23zM790 326H602V138L790 326zm2 562H232V136h64v64h64v-64h174v216c0 23 19 42 42 42h216v494z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Binary">M71 1024V0h661L953 219V1024H71zm808-731-220-219H145V951h735V293zM439 512h-220V219h220V512zm-74-219H292v146h74v-146zm0 512h74v73h-220v-73H292v-146H218V585h147v219zm294-366h74V512H512v-73h74v-146H512V219h147v219zm74 439H512V585h220v293zm-74-219h-74v146h74v-146z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Blame">M128 256h192a64 64 0 110 128H128a64 64 0 110-128zm576 192h192a64 64 0 010 128h-192a64 64 0 010-128zm-576 192h192a64 64 0 010 128H128a64 64 0 010-128zm576 0h192a64 64 0 010 128h-192a64 64 0 010-128zm0-384h192a64 64 0 010 128h-192a64 64 0 010-128zM128 448h192a64 64 0 110 128H128a64 64 0 110-128zm384-320a64 64 0 0164 64v640a64 64 0 01-128 0V192a64 64 0 0164-64z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Bookmark">M800 928l-512 0 0-704 224 0 0 292 113-86 111 86 0-292 128 0 0 640c0 35-29 64-64 64zM625 388l-81 64 0-260 160 0 0 260-79-64zM192 160l0 32c0 18 14 32 32 32l32 0 0 704-32 0c-35 0-64-29-64-64l0-704c0-35 29-64 64-64l576 0c24 0 44 13 55 32l-631 0c-18 0-32 14-32 32z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Bookmark">M832 64H192c-18 0-32 14-32 32v832c0 18 14 32 32 32h640c18 0 32-14 32-32V96c0-18-14-32-32-32zM736 596 624 502 506 596V131h230v318z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Branch">M757 226a143 143 0 00-55 276 96 96 0 01-88 59h-191a187 187 0 00-96 27V312a143 143 0 10-96 0v399a143 143 0 10103 2 96 96 0 0188-59h191a191 191 0 00187-151 143 143 0 00-43-279zM280 130a48 48 0 110 96 48 48 0 010-96zm0 764a48 48 0 110-96 48 48 0 010 96zM757 417a48 48 0 110-96 48 48 0 010 96z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Branch.Add">M896 128h-64V64c0-35-29-64-64-64s-64 29-64 64v64h-64c-35 0-64 29-64 64s29 64 64 64h64v64c0 35 29 64 64 64s64-29 64-64V256h64c35 0 64-29 64-64s-29-64-64-64zm-204 307C673 481 628 512 576 512H448c-47 0-90 13-128 35V372C394 346 448 275 448 192c0-106-86-192-192-192S64 86 64 192c0 83 54 154 128 180v280c-74 26-128 97-128 180c0 106 86 192 192 192s192-86 192-192c0-67-34-125-84-159c22-20 52-33 84-33h128c122 0 223-85 249-199c-19 4-37 7-57 7c-26 0-51-5-76-13zM256 128c35 0 64 29 64 64s-29 64-64 64s-64-29-64-64s29-64 64-64zm0 768c-35 0-64-29-64-64s29-64 64-64s64 29 64 64s-29 64-64 64z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Calender">M378 116l265 0 0 47-265 0 0-47ZM888 116 748 116l0 47 124 0c18 0 33 15 33 33l0 93L115 290l0-93c0-18 15-33 33-33l124 0 0-47L132 116c-35 0-64 29-64 64l0 714c0 35 29 64 64 64l757 0c35 0 64-29 64-64l-0-714C952 145 924 116 888 116zM905 337l0 540c0 18-15 33-33 33L148 910c-18 0-33-15-33-33L115 337 905 337zM301 65l47 0 0 170-47 0 0-170ZM673 65l47 0 0 170-47 0 0-170ZM358 548l0 231 53 0L411 459l-35 0-3 4c-18 26-41 49-70 68l-4 3 0 54 13-8C331 569 346 559 358 548zM618 727c-10 6-24 8-42 5-16-3-28-18-35-46l-2-9-48 13 2 8c6 30 18 52 36 65 17 13 36 20 55 21 3 0 7 0 10 0 15 0 28-2 40-7 14-6 27-13 37-23 10-10 18-22 23-37 5-14 8-28 8-42 1-14-1-27-4-39l-0-0c-3-12-8-24-15-36-7-13-19-23-35-30-15-7-31-11-47-11-11-0-23 1-36 5 4-15 8-32 11-52l114 0 0-49L536 464l-1 7c-25 116-32 145-33 150l-3 10 46 5 3-4c8-11 18-18 31-21 13-3 25-3 35-0 10 3 18 9 24 18 7 9 10 20 11 34 1 14-2 26-6 37C636 711 629 720 618 727z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Check">M512 597m-1 0a1 1 0 103 0a1 1 0 10-3 0ZM810 393 732 315 448 600 293 444 214 522l156 156 78 78 362-362z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.CherryPick">M529 511c115 0 212 79 239 185h224a62 62 0 017 123l-7 0-224 0a247 247 0 01-479 0H65a62 62 0 01-7-123l7-0h224a247 247 0 01239-185zm0 124a124 124 0 100 247 124 124 0 000-247zm0-618c32 0 58 24 61 55l0 7V206c89 11 165 45 225 103a74 74 0 0122 45l0 9v87a62 62 0 01-123 7l-0-7v-65l-6-4c-43-33-97-51-163-53l-17-0c-74 0-133 18-180 54l-6 4v65a62 62 0 01-55 61l-7 0a62 62 0 01-61-55l-0-7V362c0-20 8-39 23-53 60-58 135-92 224-103V79c0-34 28-62 62-62z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Clear">M512 57c251 0 455 204 455 455S763 967 512 967 57 763 57 512 261 57 512 57zm181 274c-11-11-29-11-40 0L512 472 371 331c-11-11-29-11-40 0-11 11-11 29 0 40L471 512 331 653c-11 11-11 29 0 40 11 11 29 11 40 0l141-141 141 141c11 11 29 11 40 0 11-11 11-29 0-40L552 512l141-141c11-11 11-29 0-40z</StreamGeometry>
|
||||
|
@ -23,6 +24,7 @@
|
|||
<StreamGeometry x:Key="Icons.Edit">M652 157a113 113 0 11156 161L731 395 572 236l80-80 1 1zM334 792v0H175v-159l358-358 159 159-358 358zM62 850h900v113H62v-113z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Empty">M469 235V107h85v128h-85zm-162-94 85 85-60 60-85-85 60-60zm469 60-85 85-60-60 85-85 60 60zm-549 183A85 85 0 01302 341H722a85 85 0 0174 42l131 225A85 85 0 01939 652V832a85 85 0 01-85 85H171a85 85 0 01-85-85v-180a85 85 0 0112-43l131-225zM722 427H302l-100 171h255l10 29a59 59 0 002 5c2 4 5 9 9 14 8 9 18 17 34 17 16 0 26-7 34-17a72 72 0 0011-18l0-0 10-29h255l-100-171zM853 683H624a155 155 0 01-12 17C593 722 560 747 512 747c-48 0-81-25-99-47a155 155 0 01-12-17H171v149h683v-149z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Error">M576 832C576 867 547 896 512 896 477 896 448 867 448 832 448 797 477 768 512 768 547 768 576 797 576 832ZM512 256C477 256 448 285 448 320L448 640C448 675 477 704 512 704 547 704 576 675 576 640L576 320C576 285 547 256 512 256ZM1024 896C1024 967 967 1024 896 1024L128 1024C57 1024 0 967 0 896 0 875 5 855 14 837L14 837 398 69 398 69C420 28 462 0 512 0 562 0 604 28 626 69L1008 835C1018 853 1024 874 1024 896ZM960 896C960 885 957 875 952 865L952 864 951 863 569 98C557 77 536 64 512 64 488 64 466 77 455 99L452 105 92 825 93 825 71 867C66 876 64 886 64 896 64 931 93 960 128 960L896 960C931 960 960 931 960 896Z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Explore">M928 128l-416 0-32-64-352 0-64 128 896 0zM904 704l75 0 45-448-1024 0 64 640 484 0c-105-38-180-138-180-256 0-150 122-272 272-272s272 122 272 272c0 22-3 43-8 64zM1003 914l-198-175c17-29 27-63 27-99 0-106-86-192-192-192s-192 86-192 192 86 192 192 192c36 0 70-10 99-27l175 198c23 27 62 28 87 3l6-6c25-25 23-64-3-87zM640 764c-68 0-124-56-124-124s56-124 124-124 124 56 124 124-56 124-124 124z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Eye">M520 168C291 168 95 311 16 512c79 201 275 344 504 344 229 0 425-143 504-344-79-201-275-344-504-344zm0 573c-126 0-229-103-229-229s103-229 229-229c126 0 229 103 229 229s-103 229-229 229zm0-367c-76 0-137 62-137 137s62 137 137 137S657 588 657 512s-62-137-137-137z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.EyeClose">M734 128c-33-19-74-8-93 25l-41 70c-28-6-58-9-90-9-294 0-445 298-445 298s82 149 231 236l-31 54c-19 33-8 74 25 93 33 19 74 8 93-25L759 222C778 189 767 147 734 128zM305 512c0-115 93-208 207-208 14 0 27 1 40 4l-37 64c-1 0-2 0-2 0-77 0-140 63-140 140 0 26 7 51 20 71l-37 64C324 611 305 564 305 512zM771 301 700 423c13 27 20 57 20 89 0 110-84 200-192 208l-51 89c12 1 24 2 36 2 292 0 446-298 446-298S895 388 771 301z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.FastForward">M826 498 538 250c-11-9-26-1-26 14v496c0 15 16 23 26 14L826 526c8-7 8-21 0-28zm-320 0L218 250c-11-9-26-1-26 14v496c0 15 16 23 26 14L506 526c4-4 6-9 6-14 0-5-2-10-6-14z</StreamGeometry>
|
||||
|
@ -32,11 +34,9 @@
|
|||
<StreamGeometry x:Key="Icons.File.Ignore">M416 832H128V128h384v192C512 355 541 384 576 384L768 384v32c0 19 13 32 32 32S832 435 832 416v-64c0-6 0-19-6-25l-256-256c-6-6-19-6-25-6H128A64 64 0 0064 128v704C64 867 93 896 129 896h288c19 0 32-13 32-32S435 832 416 832zM576 172 722 320H576V172zM736 512C614 512 512 614 512 736S614 960 736 960s224-102 224-224S858 512 736 512zM576 736C576 646 646 576 736 576c32 0 58 6 83 26l-218 218c-19-26-26-51-26-83zm160 160c-32 0-64-13-96-32l224-224c19 26 32 58 32 96 0 90-70 160-160 160z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.File.Remove">M896 320c0-19-6-32-19-45l-192-192c-13-13-26-19-45-19H192c-38 0-64 26-64 64v768c0 38 26 64 64 64h640c38 0 64-26 64-64V320zm-256 384H384c-19 0-32-13-32-32s13-32 32-32h256c19 0 32 13 32 32s-13 32-32 32zm166-384H640V128l192 192h-26z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Filter">M599 425 599 657 425 832 425 425 192 192 832 192Z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Folder">M64 864h896V288h-396a64 64 0 01-57-35L460 160H64v704zm-64 32V128a32 32 0 0132-32h448a32 32 0 0129 18L564 224H992a32 32 0 0132 32v640a32 32 0 01-32 32H32a32 32 0 01-32-32z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Folder">M853 267H514c-4 0-6-2-9-4l-38-66c-13-21-38-36-64-36H171c-41 0-75 34-75 75v555c0 41 34 75 75 75h683c41 0 75-34 75-75V341c0-41-34-75-75-75zm-683-43h233c4 0 6 2 9 4l38 66c13 21 38 36 64 36H853c6 0 11 4 11 11v75h-704V235c0-6 4-11 11-11zm683 576H171c-6 0-11-4-11-11V480h704V789c0 6-4 11-11 11z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Folder.Add">M1088 227H609L453 78a11 11 0 00-7-3H107a43 43 0 00-43 43v789a43 43 0 0043 43h981a43 43 0 0043-43V270a43 43 0 00-43-43zM757 599c0 5-5 9-10 9h-113v113c0 5-4 9-9 9h-56c-5 0-9-4-9-9V608h-113c-5 0-10-4-10-9V543c0-5 5-9 10-9h113V420c0-5 4-9 9-9h56c5 0 9 4 9 9V533h113c5 0 10 4 10 9v56z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Folder.Add2">M960 784h-48v-48a48 48 0 10-96 0v48h-48a48 48 0 100 96h48v48a48 48 0 1096 0v-48h48a48 48 0 100-96zM0 816a48 48 0 0048 48h630A96 96 0 01672 832a96 96 0 0196-96 96 96 0 11192 0c25 0 47 10 64 25V304H0v512zm976-656H384L288 64H48a48 48 0 00-48 48v144h1024v-48a48 48 0 00-48-48z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Folder.Fill">M448 64l128 128h448v768H0V64z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Folder.Open">M832 960l192-512H192L0 960zM128 384L0 960V128h288l128 128h416v128z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Folder.Open">M922 450c-6-9-15-13-26-13h-11V341c0-41-34-75-75-75H514c-4 0-6-2-9-4l-38-66c-13-21-38-36-64-36H171c-41 0-75 34-75 75v597c0 6 2 13 6 19 6 9 15 13 26 13h640c13 0 26-9 30-21l128-363c4-11 2-21-4-30zM171 224h233c4 0 6 2 9 4l38 66c13 21 38 36 64 36H811c6 0 11 4 11 11v96H256c-13 0-26 9-30 21l-66 186V235c0-6 4-11 11-11zm574 576H173l105-299h572l-105 299z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.GitFlow">M509 556l93 149h124l-80-79 49-50 165 164-165 163-49-50 79-79h-163l-96-153 41-65zm187-395 165 164-165 163-49-50L726 360H530l-136 224H140v-70h215l136-224h236l-80-79 49-50z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.GitFlow.Feature">M939 94v710L512 998 85 805V94h-64A21 21 0 010 73v-0C0 61 10 51 21 51h981c12 0 21 10 21 21v0c0 12-10 21-21 21h-64zm-536 588L512 624l109 58c6 3 13 4 20 3a32 32 0 0026-37l-21-122 88-87c5-5 8-11 9-18a32 32 0 00-27-37l-122-18-54-111a32 32 0 00-57 0l-54 111-122 18c-7 1-13 4-18 9a33 33 0 001 46l88 87-21 122c-1 7-0 14 3 20a32 32 0 0043 14z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.GitFlow.Hotfix">M236 542a32 32 0 109 63l86-12a180 180 0 0022 78l-71 47a32 32 0 1035 53l75-50a176 176 0 00166 40L326 529zM512 16C238 16 16 238 16 512s222 496 496 496 496-222 496-496S786 16 512 16zm0 896c-221 0-400-179-400-400a398 398 0 0186-247l561 561A398 398 0 01512 912zm314-154L690 622a179 179 0 004-29l85 12a32 32 0 109-63l-94-13v-49l94-13a32 32 0 10-9-63l-87 12a180 180 0 00-20-62l71-47A32 32 0 10708 252l-75 50a181 181 0 00-252 10l-115-115A398 398 0 01512 112c221 0 400 179 400 400a398 398 0 01-86 247z</StreamGeometry>
|
||||
|
@ -84,6 +84,7 @@
|
|||
<StreamGeometry x:Key="Icons.SquashIntoParent">M512 939C465 939 427 900 427 853 427 806 465 768 512 768 559 768 597 806 597 853 597 900 559 939 512 939M555 85 555 555 747 363 807 423 512 719 217 423 277 363 469 555 469 85 555 85Z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Stashes">M961 320 512 577 63 320 512 62l449 258zM512 628 185 442 63 512 512 770 961 512l-123-70L512 628zM512 821 185 634 63 704 512 962l449-258L839 634 512 821z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Statistics">M447 561a26 26 0 0126 26v171H421v-171a26 26 0 0126-26zm-98 65a26 26 0 0126 26v104H323v-104a26 26 0 0126-26zm0 0M561 268a32 32 0 0132 30v457h-65V299a32 32 0 0132-32zm0 0M675 384a26 26 0 0126 26v348H649v-350a26 26 0 0126-24zm0 0M801 223v579H223V223h579M805 171H219A49 49 0 00171 219v585A49 49 0 00219 853h585A49 49 0 00853 805V219A49 49 0 00805 171z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Stopwatch">M576 160H448c-18 0-32-14-32-32s14-32 32-32h128c18 0 32 14 32 32s-14 32-32 32zm243 186 36-36c13-13 13-33 0-45s-33-13-45 0l-33 33C708 233 614 192 512 192c-212 0-384 172-384 384s172 384 384 384 384-172 384-384c0-86-29-166-77-230zM544 894V864c0-18-14-32-32-32s-32 14-32 32v30C329 879 209 759 194 608H224c18 0 32-14 32-32s-14-32-32-32h-30C209 393 329 273 480 258V288c0 18 14 32 32 32s32-14 32-32v-30C695 273 815 393 830 544H800c-18 0-32 14-32 32s14 32 32 32h30C815 759 695 879 544 894zm108-471-160 128c-14 11-16 31-5 45 6 8 16 12 25 12 7 0 14-2 20-7l160-128c14-11 16-31 5-45-11-14-31-16-45-5z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Submodule">M557.7 545.3 789.9 402.7c24-15 31.3-46.5 16.4-70.5c-14.8-23.8-46-31.2-70-16.7L506.5 456.6 277.1 315.4c-24.1-14.8-55.6-7.3-70.5 16.8c-14.8 24.1-7.3 55.6 16.8 70.5l231.8 142.6V819.1c0 28.3 22.9 51.2 51.2 51.2c28.3 0 51.2-22.9 51.2-51.2V545.3h.1zM506.5 0l443.4 256v511.9L506.5 1023.9 63.1 767.9v-511.9L506.5 0z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Submodule.Add">M770 320a41 41 0 00-56-14l-252 153L207 306a41 41 0 10-43 70l255 153 2 296a41 41 0 0082 0l-2-295 255-155a41 41 0 0014-56zM481 935a42 42 0 01-42 0L105 741a42 42 0 01-21-36v-386a42 42 0 0121-36L439 89a42 42 0 0142 0l335 193a42 42 0 0121 36v87h84v-87a126 126 0 00-63-109L523 17a126 126 0 00-126 0L63 210a126 126 0 00-63 109v386a126 126 0 0063 109l335 193a126 126 0 00126 0l94-54-42-72zM1029 700h-126v-125a42 42 0 00-84 0v126h-126a42 42 0 000 84h126v126a42 42 0 1084 0v-126h126a42 42 0 000-84z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.SyntaxHighlight">M875 128h-725A107 107 0 0043 235v555A107 107 0 00149 896h725a107 107 0 00107-107v-555A107 107 0 00875 128zm-115 640h-183v-58l25-3c15 0 19-8 14-24l-22-61H419l-28 82 39 2V768h-166v-58l18-3c18-2 22-11 26-24l125-363-40-4V256h168l160 448 39 3zM506 340l-72 218h145l-71-218h-2z</StreamGeometry>
|
||||
|
|
|
@ -307,6 +307,9 @@
|
|||
<x:String x:Key="Text.Hotkeys.TextEditor.GotoNextMatch" xml:space="preserve">Find next match</x:String>
|
||||
<x:String x:Key="Text.Hotkeys.TextEditor.GotoPrevMatch" xml:space="preserve">Find previous match</x:String>
|
||||
<x:String x:Key="Text.Hotkeys.TextEditor.Search" xml:space="preserve">Open search panel</x:String>
|
||||
<x:String x:Key="Text.Hunk.Stage" xml:space="preserve">Stage</x:String>
|
||||
<x:String x:Key="Text.Hunk.Unstage" xml:space="preserve">Unstage</x:String>
|
||||
<x:String x:Key="Text.Hunk.Discard" xml:space="preserve">Discard</x:String>
|
||||
<x:String x:Key="Text.Init" xml:space="preserve">Initialize Repository</x:String>
|
||||
<x:String x:Key="Text.Init.Path" xml:space="preserve">Path:</x:String>
|
||||
<x:String x:Key="Text.Init.Tip" xml:space="preserve">Invalid repository detected. Run `git init` under this path?</x:String>
|
||||
|
@ -341,6 +344,15 @@
|
|||
<x:String x:Key="Text.PageTabBar.Tab.CopyPath" xml:space="preserve">Copy Repository Path</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Welcome.Title" xml:space="preserve">Repositories</x:String>
|
||||
<x:String x:Key="Text.Paste" xml:space="preserve">Paste</x:String>
|
||||
<x:String x:Key="Text.Period.JustNow" xml:space="preserve">Just now</x:String>
|
||||
<x:String x:Key="Text.Period.MinutesAgo" xml:space="preserve">{0} minutes ago</x:String>
|
||||
<x:String x:Key="Text.Period.HoursAgo" xml:space="preserve">{0} hours ago</x:String>
|
||||
<x:String x:Key="Text.Period.Yesterday" xml:space="preserve">Yesterday</x:String>
|
||||
<x:String x:Key="Text.Period.DaysAgo" xml:space="preserve">{0} days ago</x:String>
|
||||
<x:String x:Key="Text.Period.LastMonth" xml:space="preserve">Last month</x:String>
|
||||
<x:String x:Key="Text.Period.MonthsAgo" xml:space="preserve">{0} months ago</x:String>
|
||||
<x:String x:Key="Text.Period.LastYear" xml:space="preserve">Last year</x:String>
|
||||
<x:String x:Key="Text.Period.YearsAgo" xml:space="preserve">{0} years ago</x:String>
|
||||
<x:String x:Key="Text.Preference" xml:space="preserve">Preference</x:String>
|
||||
<x:String x:Key="Text.Preference.Appearance" xml:space="preserve">APPEARANCE</x:String>
|
||||
<x:String x:Key="Text.Preference.Appearance.DefaultFont" xml:space="preserve">Default Font</x:String>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<ResourceDictionary xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceInclude Source="en_US.axaml"/>
|
||||
<ResourceInclude Source="avares://SourceGit/Resources/Locales/en_US.axaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<x:String x:Key="Text.About" xml:space="preserve">关于软件</x:String>
|
||||
<x:String x:Key="Text.About.Menu" xml:space="preserve">关于本软件</x:String>
|
||||
|
@ -310,6 +310,9 @@
|
|||
<x:String x:Key="Text.Hotkeys.TextEditor.GotoNextMatch" xml:space="preserve">定位到下一个匹配搜索的位置</x:String>
|
||||
<x:String x:Key="Text.Hotkeys.TextEditor.GotoPrevMatch" xml:space="preserve">定位到上一个匹配搜索的位置</x:String>
|
||||
<x:String x:Key="Text.Hotkeys.TextEditor.Search" xml:space="preserve">打开搜索</x:String>
|
||||
<x:String x:Key="Text.Hunk.Stage" xml:space="preserve">暂存</x:String>
|
||||
<x:String x:Key="Text.Hunk.Unstage" xml:space="preserve">移出暂存区</x:String>
|
||||
<x:String x:Key="Text.Hunk.Discard" xml:space="preserve">丢弃</x:String>
|
||||
<x:String x:Key="Text.Init" xml:space="preserve">初始化新仓库</x:String>
|
||||
<x:String x:Key="Text.Init.Path" xml:space="preserve">路径 :</x:String>
|
||||
<x:String x:Key="Text.Init.Tip" xml:space="preserve">选择目录不是有效的Git仓库。是否需要在此目录执行`git init`操作?</x:String>
|
||||
|
@ -344,6 +347,15 @@
|
|||
<x:String x:Key="Text.PageTabBar.Tab.CopyPath" xml:space="preserve">复制仓库路径</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Welcome.Title" xml:space="preserve">新标签页</x:String>
|
||||
<x:String x:Key="Text.Paste" xml:space="preserve">粘贴</x:String>
|
||||
<x:String x:Key="Text.Period.JustNow" xml:space="preserve">刚刚</x:String>
|
||||
<x:String x:Key="Text.Period.MinutesAgo" xml:space="preserve">{0}分钟前</x:String>
|
||||
<x:String x:Key="Text.Period.HoursAgo" xml:space="preserve">{0}小时前</x:String>
|
||||
<x:String x:Key="Text.Period.Yesterday" xml:space="preserve">昨天</x:String>
|
||||
<x:String x:Key="Text.Period.DaysAgo" xml:space="preserve">{0}天前</x:String>
|
||||
<x:String x:Key="Text.Period.LastMonth" xml:space="preserve">上个月</x:String>
|
||||
<x:String x:Key="Text.Period.MonthsAgo" xml:space="preserve">{0}个月前</x:String>
|
||||
<x:String x:Key="Text.Period.LastYear" xml:space="preserve">一年前</x:String>
|
||||
<x:String x:Key="Text.Period.YearsAgo" xml:space="preserve">{0}年前</x:String>
|
||||
<x:String x:Key="Text.Preference" xml:space="preserve">偏好设置</x:String>
|
||||
<x:String x:Key="Text.Preference.Appearance" xml:space="preserve">外观配置</x:String>
|
||||
<x:String x:Key="Text.Preference.Appearance.DefaultFont" xml:space="preserve">缺省字体</x:String>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<ResourceDictionary xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceInclude Source="en_US.axaml"/>
|
||||
<ResourceInclude Source="avares://SourceGit/Resources/Locales/en_US.axaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<x:String x:Key="Text.About" xml:space="preserve">關於軟體</x:String>
|
||||
<x:String x:Key="Text.About.Menu" xml:space="preserve">關於本軟體</x:String>
|
||||
|
@ -310,6 +310,9 @@
|
|||
<x:String x:Key="Text.Hotkeys.TextEditor.GotoNextMatch" xml:space="preserve">定位到下一個匹配搜尋的位置</x:String>
|
||||
<x:String x:Key="Text.Hotkeys.TextEditor.GotoPrevMatch" xml:space="preserve">定位到上一個匹配搜尋的位置</x:String>
|
||||
<x:String x:Key="Text.Hotkeys.TextEditor.Search" xml:space="preserve">開啟搜尋</x:String>
|
||||
<x:String x:Key="Text.Hunk.Stage" xml:space="preserve">暫存</x:String>
|
||||
<x:String x:Key="Text.Hunk.Unstage" xml:space="preserve">移出暫存區</x:String>
|
||||
<x:String x:Key="Text.Hunk.Discard" xml:space="preserve">丟棄</x:String>
|
||||
<x:String x:Key="Text.Init" xml:space="preserve">初始化新倉庫</x:String>
|
||||
<x:String x:Key="Text.Init.Path" xml:space="preserve">路徑 :</x:String>
|
||||
<x:String x:Key="Text.Init.Tip" xml:space="preserve">選擇目錄不是有效的Git倉庫。是否需要在此目錄執行`git init`操作?</x:String>
|
||||
|
@ -344,6 +347,15 @@
|
|||
<x:String x:Key="Text.PageTabBar.Tab.CopyPath" xml:space="preserve">複製倉庫路徑</x:String>
|
||||
<x:String x:Key="Text.PageTabBar.Welcome.Title" xml:space="preserve">新標籤頁</x:String>
|
||||
<x:String x:Key="Text.Paste" xml:space="preserve">貼上</x:String>
|
||||
<x:String x:Key="Text.Period.JustNow" xml:space="preserve">剛剛</x:String>
|
||||
<x:String x:Key="Text.Period.MinutesAgo" xml:space="preserve">{0}分鐘前</x:String>
|
||||
<x:String x:Key="Text.Period.HoursAgo" xml:space="preserve">{0}小時前</x:String>
|
||||
<x:String x:Key="Text.Period.Yesterday" xml:space="preserve">昨天</x:String>
|
||||
<x:String x:Key="Text.Period.DaysAgo" xml:space="preserve">{0}天前</x:String>
|
||||
<x:String x:Key="Text.Period.LastMonth" xml:space="preserve">上個月</x:String>
|
||||
<x:String x:Key="Text.Period.MonthsAgo" xml:space="preserve">{0}個月前</x:String>
|
||||
<x:String x:Key="Text.Period.LastYear" xml:space="preserve">一年前</x:String>
|
||||
<x:String x:Key="Text.Period.YearsAgo" xml:space="preserve">{0}年前</x:String>
|
||||
<x:String x:Key="Text.Preference" xml:space="preserve">偏好設定</x:String>
|
||||
<x:String x:Key="Text.Preference.Appearance" xml:space="preserve">外觀配置</x:String>
|
||||
<x:String x:Key="Text.Preference.Appearance.DefaultFont" xml:space="preserve">預設字型</x:String>
|
||||
|
|
|
@ -1075,8 +1075,8 @@
|
|||
</Style>
|
||||
<Style Selector="ToggleButton.tree_expander">
|
||||
<Setter Property="Margin" Value="0" />
|
||||
<Setter Property="Width" Value="10" />
|
||||
<Setter Property="Height" Value="10" />
|
||||
<Setter Property="Width" Value="9" />
|
||||
<Setter Property="Height" Value="9" />
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
|
@ -1087,7 +1087,7 @@
|
|||
VerticalAlignment="Center">
|
||||
<Path x:Name="ChevronPath"
|
||||
Data="M 4 0 L 8 4 L 4 8 Z"
|
||||
Fill="{DynamicResource TreeViewItemForeground}"
|
||||
Fill="{DynamicResource Brush.FG1}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
|
@ -1098,6 +1098,36 @@
|
|||
<Setter Property="Data" Value="M 0 4 L 8 4 L 4 8 Z" />
|
||||
</Style>
|
||||
</Style>
|
||||
|
||||
<Style Selector="ToggleButton.time_display_mode">
|
||||
<Setter Property="Margin" Value="0" />
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<Border Background="Transparent"
|
||||
Width="{TemplateBinding Width}"
|
||||
Height="{TemplateBinding Height}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center">
|
||||
<Path x:Name="ChevronPath"
|
||||
Data="{StaticResource Icons.Calender}"
|
||||
Fill="{DynamicResource Brush.FG1}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Opacity="0.65"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
|
||||
<Style Selector="^:checked /template/ Path#ChevronPath">
|
||||
<Setter Property="Data" Value="{StaticResource Icons.Stopwatch}" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="^:pointerover /template/ Path#ChevronPath">
|
||||
<Setter Property="Fill" Value="{DynamicResource Brush.Accent}" />
|
||||
<Setter Property="Opacity" Value="1"/>
|
||||
</Style>
|
||||
</Style>
|
||||
|
||||
<Style Selector="ToggleButton.folder">
|
||||
<Setter Property="Margin" Value="0" />
|
||||
|
@ -1111,8 +1141,9 @@
|
|||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center">
|
||||
<Path x:Name="ChevronPath"
|
||||
Data="{StaticResource Icons.Folder.Fill}"
|
||||
Data="{StaticResource Icons.Folder}"
|
||||
Fill="{TemplateBinding Foreground}"
|
||||
Stretch="Uniform"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
|
|
|
@ -129,7 +129,7 @@ namespace SourceGit.ViewModels
|
|||
|
||||
var diffWithMerger = new MenuItem();
|
||||
diffWithMerger.Header = App.Text("DiffWithMerger");
|
||||
diffWithMerger.Icon = App.CreateMenuIcon("Icons.Diff");
|
||||
diffWithMerger.Icon = App.CreateMenuIcon("Icons.OpenWith");
|
||||
diffWithMerger.Click += (_, ev) =>
|
||||
{
|
||||
var toolType = Preference.Instance.ExternalMergeToolType;
|
||||
|
@ -146,7 +146,7 @@ namespace SourceGit.ViewModels
|
|||
var full = Path.GetFullPath(Path.Combine(_repo, change.Path));
|
||||
var explore = new MenuItem();
|
||||
explore.Header = App.Text("RevealFile");
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Folder.Open");
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Explore");
|
||||
explore.IsEnabled = File.Exists(full);
|
||||
explore.Click += (_, ev) =>
|
||||
{
|
||||
|
|
|
@ -36,14 +36,9 @@ namespace SourceGit.ViewModels
|
|||
get => Backend is Models.Branch;
|
||||
}
|
||||
|
||||
public bool IsUpstreamTrackStatusVisible
|
||||
public string TrackStatus
|
||||
{
|
||||
get => Backend is Models.Branch { IsLocal: true } branch && !string.IsNullOrEmpty(branch.UpstreamTrackStatus);
|
||||
}
|
||||
|
||||
public string UpstreamTrackStatus
|
||||
{
|
||||
get => Backend is Models.Branch branch ? branch.UpstreamTrackStatus : "";
|
||||
get => Backend is Models.Branch { IsLocal: true } branch ? branch.TrackStatus.ToString() : string.Empty;
|
||||
}
|
||||
|
||||
public FontWeight NameFontWeight
|
||||
|
@ -152,6 +147,8 @@ namespace SourceGit.ViewModels
|
|||
if (folders.TryGetValue(folder, out var val))
|
||||
{
|
||||
lastFolder = val;
|
||||
if (!lastFolder.IsExpanded)
|
||||
lastFolder.IsExpanded |= (branch.IsCurrent || _expanded.Contains(folder));
|
||||
}
|
||||
else if (lastFolder == null)
|
||||
{
|
||||
|
|
|
@ -8,15 +8,18 @@ namespace SourceGit.ViewModels
|
|||
{
|
||||
public List<ChangeTreeNode> Tree { get; set; } = new List<ChangeTreeNode>();
|
||||
public AvaloniaList<ChangeTreeNode> Rows { get; set; } = new AvaloniaList<ChangeTreeNode>();
|
||||
public AvaloniaList<ChangeTreeNode> SelectedRows { get; set; } = new AvaloniaList<ChangeTreeNode>();
|
||||
}
|
||||
|
||||
public class ChangeCollectionAsGrid
|
||||
{
|
||||
public AvaloniaList<Models.Change> Changes { get; set; } = new AvaloniaList<Models.Change>();
|
||||
public AvaloniaList<Models.Change> SelectedChanges { get; set; } = new AvaloniaList<Models.Change>();
|
||||
}
|
||||
|
||||
public class ChangeCollectionAsList
|
||||
{
|
||||
public AvaloniaList<Models.Change> Changes { get; set; } = new AvaloniaList<Models.Change>();
|
||||
public AvaloniaList<Models.Change> SelectedChanges { get; set; } = new AvaloniaList<Models.Change>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -223,7 +223,7 @@ namespace SourceGit.ViewModels
|
|||
|
||||
var diffWithMerger = new MenuItem();
|
||||
diffWithMerger.Header = App.Text("DiffWithMerger");
|
||||
diffWithMerger.Icon = App.CreateMenuIcon("Icons.Diff");
|
||||
diffWithMerger.Icon = App.CreateMenuIcon("Icons.OpenWith");
|
||||
diffWithMerger.Click += (_, ev) =>
|
||||
{
|
||||
var toolType = Preference.Instance.ExternalMergeToolType;
|
||||
|
@ -260,7 +260,7 @@ namespace SourceGit.ViewModels
|
|||
var full = Path.GetFullPath(Path.Combine(_repo, change.Path));
|
||||
var explore = new MenuItem();
|
||||
explore.Header = App.Text("RevealFile");
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Folder.Open");
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Explore");
|
||||
explore.IsEnabled = File.Exists(full);
|
||||
explore.Click += (_, ev) =>
|
||||
{
|
||||
|
@ -324,7 +324,7 @@ namespace SourceGit.ViewModels
|
|||
var full = Path.GetFullPath(Path.Combine(_repo, file.Path));
|
||||
var explore = new MenuItem();
|
||||
explore.Header = App.Text("RevealFile");
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Folder.Open");
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Explore");
|
||||
explore.Click += (_, ev) =>
|
||||
{
|
||||
Native.OS.OpenInFileManager(full, file.Type == Models.ObjectType.Blob);
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace SourceGit.ViewModels
|
|||
public class CreateBranch : Popup
|
||||
{
|
||||
[Required(ErrorMessage = "Branch name is required!")]
|
||||
[RegularExpression(@"^[\w\-/\.]+$", ErrorMessage = "Bad branch name format!")]
|
||||
[RegularExpression(@"^[\w\-/\.#]+$", ErrorMessage = "Bad branch name format!")]
|
||||
[CustomValidation(typeof(CreateBranch), nameof(ValidateBranchName))]
|
||||
public string Name
|
||||
{
|
||||
|
|
|
@ -12,21 +12,6 @@ namespace SourceGit.ViewModels
|
|||
{
|
||||
public class DiffContext : ObservableObject
|
||||
{
|
||||
public string RepositoryPath
|
||||
{
|
||||
get => _repo;
|
||||
}
|
||||
|
||||
public Models.Change WorkingCopyChange
|
||||
{
|
||||
get => _option.WorkingCopyChange;
|
||||
}
|
||||
|
||||
public bool IsUnstaged
|
||||
{
|
||||
get => _option.IsUnstaged;
|
||||
}
|
||||
|
||||
public string Title
|
||||
{
|
||||
get => _title;
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace SourceGit.ViewModels
|
|||
public class GitFlowStart : Popup
|
||||
{
|
||||
[Required(ErrorMessage = "Name is required!!!")]
|
||||
[RegularExpression(@"^[\w\-/\.]+$", ErrorMessage = "Bad branch name format!")]
|
||||
[RegularExpression(@"^[\w\-/\.#]+$", ErrorMessage = "Bad branch name format!")]
|
||||
[CustomValidation(typeof(GitFlowStart), nameof(ValidateBranchName))]
|
||||
public string Name
|
||||
{
|
||||
|
|
|
@ -451,7 +451,7 @@ namespace SourceGit.ViewModels
|
|||
var fastForward = new MenuItem();
|
||||
fastForward.Header = new Views.NameHighlightedTextBlock("BranchCM.FastForward", upstream);
|
||||
fastForward.Icon = App.CreateMenuIcon("Icons.FastForward");
|
||||
fastForward.IsEnabled = !string.IsNullOrEmpty(current.UpstreamTrackStatus) && current.UpstreamTrackStatus.IndexOf('↑') < 0;
|
||||
fastForward.IsEnabled = current.TrackStatus.Ahead.Count == 0;
|
||||
fastForward.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
|
|
|
@ -336,9 +336,9 @@ namespace SourceGit.ViewModels
|
|||
for (int i = 0; i < Models.Bookmarks.Supported.Count; i++)
|
||||
{
|
||||
var icon = App.CreateMenuIcon("Icons.Bookmark");
|
||||
icon.Fill = Models.Bookmarks.Brushes[i];
|
||||
icon.Stroke = Application.Current?.FindResource("Brush.FG1") as Brush;
|
||||
icon.StrokeThickness = i == 0 ? 1.0 : 0;
|
||||
|
||||
if (i != 0)
|
||||
icon.Fill = Models.Bookmarks.Brushes[i];
|
||||
|
||||
var dupIdx = i;
|
||||
var setter = new MenuItem();
|
||||
|
|
|
@ -159,6 +159,12 @@ namespace SourceGit.ViewModels
|
|||
set => SetProperty(ref _useTwoColumnsLayoutInHistories, value);
|
||||
}
|
||||
|
||||
public bool DisplayTimeAsPeriodInHistories
|
||||
{
|
||||
get => _displayTimeAsPeriodInHistories;
|
||||
set => SetProperty(ref _displayTimeAsPeriodInHistories, value);
|
||||
}
|
||||
|
||||
public bool UseSideBySideDiff
|
||||
{
|
||||
get => _useSideBySideDiff;
|
||||
|
@ -494,6 +500,7 @@ namespace SourceGit.ViewModels
|
|||
private bool _check4UpdatesOnStartup = true;
|
||||
|
||||
private bool _useTwoColumnsLayoutInHistories = false;
|
||||
private bool _displayTimeAsPeriodInHistories = false;
|
||||
private bool _useSideBySideDiff = false;
|
||||
private bool _useSyntaxHighlighting = false;
|
||||
private bool _enableDiffViewWordWrap = false;
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace SourceGit.ViewModels
|
|||
}
|
||||
|
||||
[Required(ErrorMessage = "Branch name is required!!!")]
|
||||
[RegularExpression(@"^[\w\-/\.]+$", ErrorMessage = "Bad branch name format!")]
|
||||
[RegularExpression(@"^[\w\-/\.#]+$", ErrorMessage = "Bad branch name format!")]
|
||||
[CustomValidation(typeof(RenameBranch), nameof(ValidateBranchName))]
|
||||
public string Name
|
||||
{
|
||||
|
|
|
@ -566,10 +566,10 @@ namespace SourceGit.ViewModels
|
|||
|
||||
break;
|
||||
case 2:
|
||||
visible = new Commands.QueryCommits(FullPath, 1000, _searchCommitFilter).Result();
|
||||
visible = new Commands.QueryCommits(FullPath, 1000, _searchCommitFilter, false).Result();
|
||||
break;
|
||||
case 3:
|
||||
visible = new Commands.QueryCommits(FullPath, $"-1000 -- \"{_searchCommitFilter}\"", false).Result();
|
||||
visible = new Commands.QueryCommits(FullPath, 1000, _searchCommitFilter, true).Result();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -784,8 +784,20 @@ namespace SourceGit.ViewModels
|
|||
limits += "--branches --remotes --tags";
|
||||
}
|
||||
|
||||
var commits = new Commands.QueryCommits(FullPath, limits).Result();
|
||||
var graph = Models.CommitGraph.Parse(commits);
|
||||
var canPushCommits = new HashSet<string>();
|
||||
var canPullCommits = new HashSet<string>();
|
||||
var currentBranch = Branches.Find(x => x.IsCurrent);
|
||||
if (currentBranch != null)
|
||||
{
|
||||
foreach (var sha in currentBranch.TrackStatus.Ahead)
|
||||
canPushCommits.Add(sha);
|
||||
|
||||
foreach (var sha in currentBranch.TrackStatus.Behind)
|
||||
canPullCommits.Add(sha);
|
||||
}
|
||||
|
||||
var commits = new Commands.QueryCommits(_fullpath, limits).Result();
|
||||
var graph = Models.CommitGraph.Parse(commits, canPushCommits, canPullCommits);
|
||||
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
|
@ -1244,7 +1256,7 @@ namespace SourceGit.ViewModels
|
|||
var fastForward = new MenuItem();
|
||||
fastForward.Header = new Views.NameHighlightedTextBlock("BranchCM.FastForward", upstream);
|
||||
fastForward.Icon = App.CreateMenuIcon("Icons.FastForward");
|
||||
fastForward.IsEnabled = !string.IsNullOrEmpty(branch.UpstreamTrackStatus) && branch.UpstreamTrackStatus.IndexOf('↑') < 0;
|
||||
fastForward.IsEnabled = branch.TrackStatus.Ahead.Count == 0;
|
||||
fastForward.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
|
@ -1295,7 +1307,7 @@ namespace SourceGit.ViewModels
|
|||
var fastForward = new MenuItem();
|
||||
fastForward.Header = new Views.NameHighlightedTextBlock("BranchCM.FastForward", upstream.FriendlyName);
|
||||
fastForward.Icon = App.CreateMenuIcon("Icons.FastForward");
|
||||
fastForward.IsEnabled = !string.IsNullOrEmpty(branch.UpstreamTrackStatus) && branch.UpstreamTrackStatus.IndexOf('↑') < 0;
|
||||
fastForward.IsEnabled = branch.TrackStatus.Ahead.Count == 0;
|
||||
fastForward.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
|
|
|
@ -137,7 +137,7 @@ namespace SourceGit.ViewModels
|
|||
|
||||
var diffWithMerger = new MenuItem();
|
||||
diffWithMerger.Header = App.Text("DiffWithMerger");
|
||||
diffWithMerger.Icon = App.CreateMenuIcon("Icons.Diff");
|
||||
diffWithMerger.Icon = App.CreateMenuIcon("Icons.OpenWith");
|
||||
diffWithMerger.Click += (_, ev) =>
|
||||
{
|
||||
var opt = new Models.DiffOption(StartPoint.SHA, _endPoint, change);
|
||||
|
@ -154,7 +154,7 @@ namespace SourceGit.ViewModels
|
|||
var full = Path.GetFullPath(Path.Combine(_repo, change.Path));
|
||||
var explore = new MenuItem();
|
||||
explore.Header = App.Text("RevealFile");
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Folder.Open");
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Explore");
|
||||
explore.IsEnabled = File.Exists(full);
|
||||
explore.Click += (_, ev) =>
|
||||
{
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Avalonia;
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
|
@ -46,6 +49,45 @@ namespace SourceGit.ViewModels
|
|||
_syncScrollOffset = previous._syncScrollOffset;
|
||||
}
|
||||
|
||||
public void ConvertsToCombinedRange(Models.TextDiff combined, ref int startLine, ref int endLine, bool isOldSide)
|
||||
{
|
||||
endLine = Math.Min(endLine, combined.Lines.Count - 1);
|
||||
|
||||
var oneSide = isOldSide ? Old : New;
|
||||
var firstContentLine = -1;
|
||||
for (int i = startLine; i <= endLine; i++)
|
||||
{
|
||||
var line = oneSide[i];
|
||||
if (line.Type != Models.TextDiffLineType.None)
|
||||
{
|
||||
firstContentLine = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (firstContentLine < 0)
|
||||
return;
|
||||
|
||||
var endContentLine = -1;
|
||||
for (int i = Math.Min(endLine, oneSide.Count - 1); i >= startLine; i--)
|
||||
{
|
||||
var line = oneSide[i];
|
||||
if (line.Type != Models.TextDiffLineType.None)
|
||||
{
|
||||
endContentLine = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (endContentLine < 0)
|
||||
return;
|
||||
|
||||
var firstContent = oneSide[firstContentLine];
|
||||
var endContent = oneSide[endContentLine];
|
||||
startLine = combined.Lines.IndexOf(firstContent);
|
||||
endLine = combined.Lines.IndexOf(endContent);
|
||||
}
|
||||
|
||||
private void FillEmptyLines()
|
||||
{
|
||||
if (Old.Count < New.Count)
|
||||
|
|
|
@ -122,7 +122,7 @@ namespace SourceGit.ViewModels
|
|||
{
|
||||
var explore = new MenuItem();
|
||||
explore.Header = App.Text("Repository.Explore");
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Folder.Open");
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Explore");
|
||||
explore.Click += (_, e) =>
|
||||
{
|
||||
node.OpenInFileManager();
|
||||
|
|
|
@ -301,11 +301,13 @@ namespace SourceGit.ViewModels
|
|||
public void StageSelected()
|
||||
{
|
||||
StageChanges(_selectedUnstaged);
|
||||
SelectedUnstaged = [];
|
||||
}
|
||||
|
||||
public void StageAll()
|
||||
{
|
||||
StageChanges(_unstaged);
|
||||
SelectedUnstaged = [];
|
||||
}
|
||||
|
||||
public async void StageChanges(List<Models.Change> changes)
|
||||
|
@ -337,11 +339,13 @@ namespace SourceGit.ViewModels
|
|||
public void UnstageSelected()
|
||||
{
|
||||
UnstageChanges(_selectedStaged);
|
||||
SelectedStaged = [];
|
||||
}
|
||||
|
||||
public void UnstageAll()
|
||||
{
|
||||
UnstageChanges(_staged);
|
||||
SelectedStaged = [];
|
||||
}
|
||||
|
||||
public async void UnstageChanges(List<Models.Change> changes)
|
||||
|
@ -414,7 +418,7 @@ namespace SourceGit.ViewModels
|
|||
|
||||
var explore = new MenuItem();
|
||||
explore.Header = App.Text("RevealFile");
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Folder.Open");
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Explore");
|
||||
explore.IsEnabled = File.Exists(path) || Directory.Exists(path);
|
||||
explore.Click += (_, e) =>
|
||||
{
|
||||
|
@ -872,7 +876,7 @@ namespace SourceGit.ViewModels
|
|||
var explore = new MenuItem();
|
||||
explore.IsEnabled = File.Exists(path) || Directory.Exists(path);
|
||||
explore.Header = App.Text("RevealFile");
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Folder.Open");
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Explore");
|
||||
explore.Click += (_, e) =>
|
||||
{
|
||||
Native.OS.OpenInFileManager(path, true);
|
||||
|
|
|
@ -132,8 +132,7 @@
|
|||
|
||||
<!-- Changes -->
|
||||
<Border Grid.Row="1" Margin="0,4,0,0" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}">
|
||||
<v:ChangeCollectionView IsWorkingCopyChange="False"
|
||||
ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=CommitChangeViewMode}"
|
||||
<v:ChangeCollectionView ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=CommitChangeViewMode}"
|
||||
Changes="{Binding VisibleChanges}"
|
||||
SelectedChanges="{Binding SelectedChanges, Mode=TwoWay}"
|
||||
ContextRequested="OnChangeContextRequested"/>
|
||||
|
|
|
@ -88,8 +88,13 @@
|
|||
CornerRadius="9"
|
||||
VerticalAlignment="Center"
|
||||
Background="{DynamicResource Brush.Badge}"
|
||||
IsVisible="{Binding IsUpstreamTrackStatusVisible}">
|
||||
<TextBlock Classes="monospace" FontSize="10" HorizontalAlignment="Center" Margin="9,0" Text="{Binding UpstreamTrackStatus}" Foreground="{DynamicResource Brush.BadgeFG}"/>
|
||||
IsVisible="{Binding TrackStatus, Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
|
||||
<TextBlock Classes="monospace"
|
||||
FontSize="10"
|
||||
HorizontalAlignment="Center"
|
||||
Margin="9,0"
|
||||
Text="{Binding TrackStatus}"
|
||||
Foreground="{DynamicResource Brush.BadgeFG}"/>
|
||||
</Border>
|
||||
|
||||
<!-- Filter Toggle Button -->
|
||||
|
|
|
@ -51,25 +51,25 @@ namespace SourceGit.Views
|
|||
|
||||
if (node.Backend is Models.Remote)
|
||||
{
|
||||
CreateContent(12, new Thickness(0, 2, 0, 0), "Icons.Remote");
|
||||
CreateContent(new Thickness(0, 2, 0, 0), "Icons.Remote");
|
||||
}
|
||||
else if (node.Backend is Models.Branch branch)
|
||||
{
|
||||
if (branch.IsCurrent)
|
||||
CreateContent(12, new Thickness(0, 2, 0, 0), "Icons.Check");
|
||||
CreateContent(new Thickness(0, 2, 0, 0), "Icons.Check");
|
||||
else
|
||||
CreateContent(12, new Thickness(2, 0, 0, 0), "Icons.Branch");
|
||||
CreateContent(new Thickness(2, 0, 0, 0), "Icons.Branch");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (node.IsExpanded)
|
||||
CreateContent(10, new Thickness(0, 2, 0, 0), "Icons.Folder.Open");
|
||||
CreateContent(new Thickness(0, 2, 0, 0), "Icons.Folder.Open");
|
||||
else
|
||||
CreateContent(10, new Thickness(0, 2, 0, 0), "Icons.Folder.Fill");
|
||||
CreateContent(new Thickness(0, 2, 0, 0), "Icons.Folder");
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateContent(double size, Thickness margin, string iconKey)
|
||||
private void CreateContent(Thickness margin, string iconKey)
|
||||
{
|
||||
var geo = this.FindResource(iconKey) as StreamGeometry;
|
||||
if (geo == null)
|
||||
|
@ -77,8 +77,8 @@ namespace SourceGit.Views
|
|||
|
||||
Content = new Path()
|
||||
{
|
||||
Width = size,
|
||||
Height = size,
|
||||
Width = 12,
|
||||
Height = 12,
|
||||
HorizontalAlignment = HorizontalAlignment.Left,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Margin = margin,
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
<UserControl.DataTemplates>
|
||||
<DataTemplate DataType="vm:ChangeCollectionAsTree">
|
||||
<v:ChangeCollectionContainer ItemsSource="{Binding Rows}"
|
||||
SelectedItems="{Binding SelectedRows, Mode=TwoWay}"
|
||||
SelectionMode="{Binding #ThisControl.SelectionMode}"
|
||||
SelectionChanged="OnRowSelectionChanged">
|
||||
<ListBox.ItemTemplate>
|
||||
|
@ -50,11 +51,12 @@
|
|||
Classes="folder"
|
||||
Focusable="False"
|
||||
Width="14" Height="14"
|
||||
Margin="0,1,0,0"
|
||||
Foreground="Goldenrod"
|
||||
IsChecked="{Binding IsExpanded}"
|
||||
IsVisible="{Binding IsFolder}"/>
|
||||
|
||||
<v:ChangeStatusIcon Grid.Column="1" Width="14" Height="14" IsWorkingCopyChange="{Binding #ThisControl.IsWorkingCopyChange}" Change="{Binding Change}" IsVisible="{Binding !IsFolder}"/>
|
||||
<v:ChangeStatusIcon Grid.Column="1" Width="14" Height="14" IsUnstagedChange="{Binding #ThisControl.IsUnstagedChange}" Change="{Binding Change}" IsVisible="{Binding !IsFolder}"/>
|
||||
<TextBlock Grid.Column="2" Classes="monospace" Text="{Binding FullPath, Converter={x:Static c:PathConverters.PureFileName}}" Margin="6,0,0,0"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
@ -64,6 +66,7 @@
|
|||
|
||||
<DataTemplate DataType="vm:ChangeCollectionAsGrid">
|
||||
<v:ChangeCollectionContainer ItemsSource="{Binding Changes}"
|
||||
SelectedItems="{Binding SelectedChanges, Mode=TwoWay}"
|
||||
SelectionMode="{Binding #ThisControl.SelectionMode}"
|
||||
SelectionChanged="OnRowSelectionChanged">
|
||||
<ListBox.ItemTemplate>
|
||||
|
@ -72,7 +75,7 @@
|
|||
<v:ChangeStatusIcon Grid.Column="0"
|
||||
Width="14" Height="14"
|
||||
Margin="4,0,0,0"
|
||||
IsWorkingCopyChange="{Binding #ThisControl.IsWorkingCopyChange}"
|
||||
IsUnstagedChange="{Binding #ThisControl.IsUnstagedChange}"
|
||||
Change="{Binding}" />
|
||||
|
||||
<TextBlock Grid.Column="1"
|
||||
|
@ -92,6 +95,7 @@
|
|||
|
||||
<DataTemplate DataType="vm:ChangeCollectionAsList">
|
||||
<v:ChangeCollectionContainer ItemsSource="{Binding Changes}"
|
||||
SelectedItems="{Binding SelectedChanges, Mode=TwoWay}"
|
||||
SelectionMode="{Binding #ThisControl.SelectionMode}"
|
||||
SelectionChanged="OnRowSelectionChanged">
|
||||
<ListBox.ItemTemplate>
|
||||
|
@ -100,7 +104,7 @@
|
|||
<v:ChangeStatusIcon Grid.Column="0"
|
||||
Width="14" Height="14"
|
||||
Margin="4,0,0,0"
|
||||
IsWorkingCopyChange="{Binding #ThisControl.IsWorkingCopyChange}"
|
||||
IsUnstagedChange="{Binding #ThisControl.IsUnstagedChange}"
|
||||
Change="{Binding}" />
|
||||
|
||||
<TextBlock Grid.Column="1"
|
||||
|
|
|
@ -40,13 +40,13 @@ namespace SourceGit.Views
|
|||
|
||||
public partial class ChangeCollectionView : UserControl
|
||||
{
|
||||
public static readonly StyledProperty<bool> IsWorkingCopyChangeProperty =
|
||||
AvaloniaProperty.Register<ChangeCollectionView, bool>(nameof(IsWorkingCopyChange));
|
||||
public static readonly StyledProperty<bool> IsUnstagedChangeProperty =
|
||||
AvaloniaProperty.Register<ChangeCollectionView, bool>(nameof(IsUnstagedChange));
|
||||
|
||||
public bool IsWorkingCopyChange
|
||||
public bool IsUnstagedChange
|
||||
{
|
||||
get => GetValue(IsWorkingCopyChangeProperty);
|
||||
set => SetValue(IsWorkingCopyChangeProperty, value);
|
||||
get => GetValue(IsUnstagedChangeProperty);
|
||||
set => SetValue(IsUnstagedChangeProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<SelectionMode> SelectionModeProperty =
|
||||
|
@ -101,7 +101,7 @@ namespace SourceGit.Views
|
|||
|
||||
public void ToggleNodeIsExpanded(ViewModels.ChangeTreeNode node)
|
||||
{
|
||||
if (_displayContext is ViewModels.ChangeCollectionAsTree tree)
|
||||
if (Content is ViewModels.ChangeCollectionAsTree tree)
|
||||
{
|
||||
node.IsExpanded = !node.IsExpanded;
|
||||
|
||||
|
@ -136,92 +136,12 @@ namespace SourceGit.Views
|
|||
{
|
||||
base.OnPropertyChanged(change);
|
||||
|
||||
if (change.Property == ViewModeProperty || change.Property == ChangesProperty)
|
||||
{
|
||||
_disableSelectionChangingEvent = change.Property == ChangesProperty;
|
||||
var changes = Changes;
|
||||
if (changes == null || changes.Count == 0)
|
||||
{
|
||||
Content = null;
|
||||
_displayContext = null;
|
||||
_disableSelectionChangingEvent = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ViewMode == Models.ChangeViewMode.Tree)
|
||||
{
|
||||
HashSet<string> oldFolded = new HashSet<string>();
|
||||
if (_displayContext is ViewModels.ChangeCollectionAsTree oldTree)
|
||||
{
|
||||
foreach (var row in oldTree.Rows)
|
||||
{
|
||||
if (row.IsFolder && !row.IsExpanded)
|
||||
oldFolded.Add(row.FullPath);
|
||||
}
|
||||
}
|
||||
|
||||
var tree = new ViewModels.ChangeCollectionAsTree();
|
||||
tree.Tree = ViewModels.ChangeTreeNode.Build(changes, oldFolded);
|
||||
|
||||
var rows = new List<ViewModels.ChangeTreeNode>();
|
||||
MakeTreeRows(rows, tree.Tree);
|
||||
tree.Rows.AddRange(rows);
|
||||
_displayContext = tree;
|
||||
}
|
||||
else if (ViewMode == Models.ChangeViewMode.Grid)
|
||||
{
|
||||
var grid = new ViewModels.ChangeCollectionAsGrid();
|
||||
grid.Changes.AddRange(changes);
|
||||
_displayContext = grid;
|
||||
}
|
||||
else
|
||||
{
|
||||
var list = new ViewModels.ChangeCollectionAsList();
|
||||
list.Changes.AddRange(changes);
|
||||
_displayContext = list;
|
||||
}
|
||||
|
||||
Content = _displayContext;
|
||||
_disableSelectionChangingEvent = false;
|
||||
}
|
||||
if (change.Property == ViewModeProperty)
|
||||
UpdateDataSource(false);
|
||||
else if (change.Property == ChangesProperty)
|
||||
UpdateDataSource(true);
|
||||
else if (change.Property == SelectedChangesProperty)
|
||||
{
|
||||
if (_disableSelectionChangingEvent)
|
||||
return;
|
||||
|
||||
var list = this.FindDescendantOfType<ChangeCollectionContainer>();
|
||||
if (list == null)
|
||||
return;
|
||||
|
||||
_disableSelectionChangingEvent = true;
|
||||
|
||||
var selected = SelectedChanges;
|
||||
if (selected == null || selected.Count == 0)
|
||||
{
|
||||
list.SelectedItem = null;
|
||||
}
|
||||
else if (_displayContext is ViewModels.ChangeCollectionAsTree tree)
|
||||
{
|
||||
var sets = new HashSet<Models.Change>();
|
||||
foreach (var c in selected)
|
||||
sets.Add(c);
|
||||
|
||||
var nodes = new List<ViewModels.ChangeTreeNode>();
|
||||
foreach (var row in tree.Rows)
|
||||
{
|
||||
if (row.Change != null && sets.Contains(row.Change))
|
||||
nodes.Add(row);
|
||||
}
|
||||
|
||||
list.SelectedItems = nodes;
|
||||
}
|
||||
else
|
||||
{
|
||||
list.SelectedItems = selected;
|
||||
}
|
||||
|
||||
_disableSelectionChangingEvent = false;
|
||||
}
|
||||
UpdateSelection();
|
||||
}
|
||||
|
||||
private void OnRowDoubleTapped(object sender, TappedEventArgs e)
|
||||
|
@ -283,6 +203,120 @@ namespace SourceGit.Views
|
|||
}
|
||||
}
|
||||
|
||||
private void UpdateDataSource(bool disableEvents)
|
||||
{
|
||||
_disableSelectionChangingEvent = disableEvents;
|
||||
|
||||
var changes = Changes;
|
||||
if (changes == null || changes.Count == 0)
|
||||
{
|
||||
Content = null;
|
||||
_disableSelectionChangingEvent = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var selected = SelectedChanges ?? [];
|
||||
if (ViewMode == Models.ChangeViewMode.Tree)
|
||||
{
|
||||
HashSet<string> oldFolded = new HashSet<string>();
|
||||
if (Content is ViewModels.ChangeCollectionAsTree oldTree)
|
||||
{
|
||||
foreach (var row in oldTree.Rows)
|
||||
{
|
||||
if (row.IsFolder && !row.IsExpanded)
|
||||
oldFolded.Add(row.FullPath);
|
||||
}
|
||||
}
|
||||
|
||||
var tree = new ViewModels.ChangeCollectionAsTree();
|
||||
tree.Tree = ViewModels.ChangeTreeNode.Build(changes, oldFolded);
|
||||
|
||||
var rows = new List<ViewModels.ChangeTreeNode>();
|
||||
MakeTreeRows(rows, tree.Tree);
|
||||
tree.Rows.AddRange(rows);
|
||||
|
||||
if (selected.Count > 0)
|
||||
{
|
||||
var sets = new HashSet<Models.Change>();
|
||||
foreach (var c in selected)
|
||||
sets.Add(c);
|
||||
|
||||
var nodes = new List<ViewModels.ChangeTreeNode>();
|
||||
foreach (var row in tree.Rows)
|
||||
{
|
||||
if (row.Change != null && sets.Contains(row.Change))
|
||||
nodes.Add(row);
|
||||
}
|
||||
|
||||
tree.SelectedRows.AddRange(nodes);
|
||||
}
|
||||
|
||||
Content = tree;
|
||||
}
|
||||
else if (ViewMode == Models.ChangeViewMode.Grid)
|
||||
{
|
||||
var grid = new ViewModels.ChangeCollectionAsGrid();
|
||||
grid.Changes.AddRange(changes);
|
||||
if (selected.Count > 0)
|
||||
grid.SelectedChanges.AddRange(selected);
|
||||
Content = grid;
|
||||
}
|
||||
else
|
||||
{
|
||||
var list = new ViewModels.ChangeCollectionAsList();
|
||||
list.Changes.AddRange(changes);
|
||||
if (selected.Count > 0)
|
||||
list.SelectedChanges.AddRange(selected);
|
||||
Content = list;
|
||||
}
|
||||
|
||||
_disableSelectionChangingEvent = false;
|
||||
}
|
||||
|
||||
private void UpdateSelection()
|
||||
{
|
||||
if (_disableSelectionChangingEvent)
|
||||
return;
|
||||
|
||||
_disableSelectionChangingEvent = true;
|
||||
|
||||
var selected = SelectedChanges ?? [];
|
||||
if (Content is ViewModels.ChangeCollectionAsTree tree)
|
||||
{
|
||||
tree.SelectedRows.Clear();
|
||||
|
||||
if (selected.Count > 0)
|
||||
{
|
||||
var sets = new HashSet<Models.Change>();
|
||||
foreach (var c in selected)
|
||||
sets.Add(c);
|
||||
|
||||
var nodes = new List<ViewModels.ChangeTreeNode>();
|
||||
foreach (var row in tree.Rows)
|
||||
{
|
||||
if (row.Change != null && sets.Contains(row.Change))
|
||||
nodes.Add(row);
|
||||
}
|
||||
|
||||
tree.SelectedRows.AddRange(nodes);
|
||||
}
|
||||
}
|
||||
else if (Content is ViewModels.ChangeCollectionAsGrid grid)
|
||||
{
|
||||
grid.SelectedChanges.Clear();
|
||||
if (selected.Count > 0)
|
||||
grid.SelectedChanges.AddRange(selected);
|
||||
}
|
||||
else if (Content is ViewModels.ChangeCollectionAsList list)
|
||||
{
|
||||
list.SelectedChanges.Clear();
|
||||
if (selected.Count > 0)
|
||||
list.SelectedChanges.AddRange(selected);
|
||||
}
|
||||
|
||||
_disableSelectionChangingEvent = false;
|
||||
}
|
||||
|
||||
private void CollectChangesInNode(List<Models.Change> outs, ViewModels.ChangeTreeNode node)
|
||||
{
|
||||
if (node.IsFolder)
|
||||
|
@ -324,6 +358,5 @@ namespace SourceGit.Views
|
|||
}
|
||||
|
||||
private bool _disableSelectionChangingEvent = false;
|
||||
private object _displayContext = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,13 +57,13 @@ namespace SourceGit.Views
|
|||
|
||||
private static readonly string[] INDICATOR = ["?", "±", "+", "−", "➜", "❏", "U", "★"];
|
||||
|
||||
public static readonly StyledProperty<bool> IsWorkingCopyChangeProperty =
|
||||
AvaloniaProperty.Register<ChangeStatusIcon, bool>(nameof(IsWorkingCopyChange));
|
||||
public static readonly StyledProperty<bool> IsUnstagedChangeProperty =
|
||||
AvaloniaProperty.Register<ChangeStatusIcon, bool>(nameof(IsUnstagedChange));
|
||||
|
||||
public bool IsWorkingCopyChange
|
||||
public bool IsUnstagedChange
|
||||
{
|
||||
get => GetValue(IsWorkingCopyChangeProperty);
|
||||
set => SetValue(IsWorkingCopyChangeProperty, value);
|
||||
get => GetValue(IsUnstagedChangeProperty);
|
||||
set => SetValue(IsUnstagedChangeProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<Models.Change> ChangeProperty =
|
||||
|
@ -77,7 +77,7 @@ namespace SourceGit.Views
|
|||
|
||||
static ChangeStatusIcon()
|
||||
{
|
||||
AffectsRender<ChangeStatusIcon>(IsWorkingCopyChangeProperty, ChangeProperty);
|
||||
AffectsRender<ChangeStatusIcon>(IsUnstagedChangeProperty, ChangeProperty);
|
||||
}
|
||||
|
||||
public override void Render(DrawingContext context)
|
||||
|
@ -89,7 +89,7 @@ namespace SourceGit.Views
|
|||
|
||||
IBrush background;
|
||||
string indicator;
|
||||
if (IsWorkingCopyChange)
|
||||
if (IsUnstagedChange)
|
||||
{
|
||||
if (Change.IsConflit)
|
||||
{
|
||||
|
|
|
@ -45,8 +45,7 @@
|
|||
|
||||
<!-- Changes -->
|
||||
<Border Grid.Row="1" Margin="0,4,0,0" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}">
|
||||
<v:ChangeCollectionView IsWorkingCopyChange="False"
|
||||
SelectionMode="Single"
|
||||
<v:ChangeCollectionView SelectionMode="Single"
|
||||
ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=CommitChangeViewMode}"
|
||||
Changes="{Binding VisibleChanges}"
|
||||
SelectedChanges="{Binding SelectedChanges, Mode=TwoWay}"
|
||||
|
|
|
@ -51,7 +51,6 @@
|
|||
Width="14" Height="14"
|
||||
HorizontalAlignment="Left"
|
||||
Margin="16,0,0,0"
|
||||
IsWorkingCopyChange="False"
|
||||
Change="{Binding}"/>
|
||||
<TextBlock Grid.Column="1" Classes="monospace" Text="{Binding Path}" Margin="8,0" TextTrimming="CharacterEllipsis"/>
|
||||
</Grid>
|
||||
|
|
|
@ -25,8 +25,6 @@
|
|||
<StackPanel Grid.Column="1" Orientation="Horizontal">
|
||||
<Path Width="12" Height="12" Margin="0,0,8,0"
|
||||
Fill="{Binding Node.Bookmark, Converter={x:Static c:BookmarkConverters.ToBrush}}"
|
||||
StrokeThickness="{Binding Node.Bookmark, Converter={x:Static c:BookmarkConverters.ToStrokeThickness}}"
|
||||
Stroke="{DynamicResource Brush.FG1}"
|
||||
HorizontalAlignment="Left" VerticalAlignment="Center"
|
||||
Data="{StaticResource Icons.Bookmark}"
|
||||
IsVisible="{Binding Node.IsRepository}"/>
|
||||
|
|
|
@ -41,8 +41,6 @@
|
|||
<Border Height="20" VerticalAlignment="Center">
|
||||
<Path Width="12" Height="12"
|
||||
Fill="{Binding Converter={x:Static c:BookmarkConverters.ToBrush}}"
|
||||
StrokeThickness="{Binding Converter={x:Static c:BookmarkConverters.ToStrokeThickness}}"
|
||||
Stroke="{DynamicResource Brush.FG1}"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
Data="{StaticResource Icons.Bookmark}"/>
|
||||
</Border>
|
||||
|
|
|
@ -69,6 +69,16 @@
|
|||
<DataTemplate x:DataType="{x:Type m:Commit}">
|
||||
<Border Margin="{Binding Margin}">
|
||||
<StackPanel Orientation="Horizontal" Margin="2,0,0,0">
|
||||
<Ellipse Width="5" Height="5"
|
||||
Margin="0,0,4,0"
|
||||
Fill="{DynamicResource Brush.Accent}"
|
||||
IsVisible="{Binding CanPushToUpstream}"/>
|
||||
|
||||
<Ellipse Width="5" Height="5"
|
||||
Margin="0,0,4,0"
|
||||
Fill="{DynamicResource Brush.FG1}"
|
||||
IsVisible="{Binding CanPullFromUpstream}"/>
|
||||
|
||||
<ItemsControl ItemsSource="{Binding Decorators}" IsVisible="{Binding HasDecorators}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
|
@ -103,8 +113,8 @@
|
|||
|
||||
<TextBlock Classes="monospace"
|
||||
Text="{Binding Subject}"
|
||||
Opacity="{Binding IsMerged, Converter={x:Static c:BoolConverters.HalfIfFalse}}"
|
||||
FontWeight="{Binding IsCurrentHead, Converter={x:Static c:BoolConverters.BoldIfTrue}}"/>
|
||||
Opacity="{Binding Opacity}"
|
||||
FontWeight="{Binding FontWeight}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
|
@ -124,13 +134,13 @@
|
|||
VerticalAlignment="Center"
|
||||
IsHitTestVisible="False"
|
||||
User="{Binding Author}"
|
||||
Opacity="{Binding IsMerged, Converter={x:Static c:BoolConverters.HalfIfFalse}}"/>
|
||||
Opacity="{Binding Opacity}"/>
|
||||
<TextBlock Grid.Column="1"
|
||||
Classes="monospace"
|
||||
Text="{Binding Author.Name}"
|
||||
Margin="8,0,0,0"
|
||||
Opacity="{Binding IsMerged, Converter={x:Static c:BoolConverters.HalfIfFalse}}"
|
||||
FontWeight="{Binding IsCurrentHead, Converter={x:Static c:BoolConverters.BoldIfTrue}}"/>
|
||||
Opacity="{Binding Opacity}"
|
||||
FontWeight="{Binding FontWeight}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
|
@ -147,24 +157,32 @@
|
|||
Text="{Binding SHA, Converter={x:Static c:StringConverters.ToShortSHA}}"
|
||||
Margin="8,0"
|
||||
HorizontalAlignment="Center"
|
||||
Opacity="{Binding IsMerged, Converter={x:Static c:BoolConverters.HalfIfFalse}}"
|
||||
FontWeight="{Binding IsCurrentHead, Converter={x:Static c:BoolConverters.BoldIfTrue}}"/>
|
||||
Opacity="{Binding Opacity}"
|
||||
FontWeight="{Binding FontWeight}"/>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
|
||||
<DataGridTemplateColumn CanUserResize="False" MinWidth="156">
|
||||
<DataGridTemplateColumn.Header>
|
||||
<TextBlock Classes="table_header" Text="{DynamicResource Text.Histories.Header.Time}"/>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<ToggleButton Classes="time_display_mode"
|
||||
Width="10" Height="10"
|
||||
IsChecked="{Binding Source={x:Static vm:Preference.Instance}, Path=DisplayTimeAsPeriodInHistories, Mode=TwoWay}"/>
|
||||
<TextBlock Classes="table_header" Margin="4,0,0,0" Text="{DynamicResource Text.Histories.Header.Time}"/>
|
||||
</StackPanel>
|
||||
|
||||
</DataGridTemplateColumn.Header>
|
||||
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate x:DataType="{x:Type m:Commit}">
|
||||
<TextBlock Classes="monospace"
|
||||
Text="{Binding CommitterTimeStr}"
|
||||
Margin="8,0"
|
||||
Opacity="{Binding IsMerged, Converter={x:Static c:BoolConverters.HalfIfFalse}}"
|
||||
FontWeight="{Binding IsCurrentHead, Converter={x:Static c:BoolConverters.BoldIfTrue}}"/>
|
||||
<v:CommitTimeTextBlock Classes="monospace"
|
||||
Margin="8,0"
|
||||
HorizontalAlignment="Center"
|
||||
Opacity="{Binding Opacity}"
|
||||
FontWeight="{Binding FontWeight}"
|
||||
Timestamp="{Binding CommitterTime}"
|
||||
ShowAsDateTime="{Binding Source={x:Static vm:Preference.Instance}, Path=!DisplayTimeAsPeriodInHistories}"/>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
|
|
|
@ -3,7 +3,9 @@ using System;
|
|||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Threading;
|
||||
using Avalonia.VisualTree;
|
||||
|
||||
namespace SourceGit.Views
|
||||
|
@ -67,6 +69,123 @@ namespace SourceGit.Views
|
|||
}
|
||||
}
|
||||
|
||||
public class CommitTimeTextBlock : TextBlock
|
||||
{
|
||||
public static readonly StyledProperty<bool> ShowAsDateTimeProperty =
|
||||
AvaloniaProperty.Register<CommitTimeTextBlock, bool>(nameof(ShowAsDateTime), true);
|
||||
|
||||
public bool ShowAsDateTime
|
||||
{
|
||||
get => GetValue(ShowAsDateTimeProperty);
|
||||
set => SetValue(ShowAsDateTimeProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<ulong> TimestampProperty =
|
||||
AvaloniaProperty.Register<CommitTimeTextBlock, ulong>(nameof(Timestamp));
|
||||
|
||||
public ulong Timestamp
|
||||
{
|
||||
get => GetValue(TimestampProperty);
|
||||
set => SetValue(TimestampProperty, value);
|
||||
}
|
||||
|
||||
protected override Type StyleKeyOverride => typeof(TextBlock);
|
||||
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||
{
|
||||
base.OnPropertyChanged(change);
|
||||
|
||||
if (change.Property == TimestampProperty)
|
||||
{
|
||||
SetCurrentValue(TextProperty, GetDisplayText());
|
||||
}
|
||||
else if (change.Property == ShowAsDateTimeProperty)
|
||||
{
|
||||
SetCurrentValue(TextProperty, GetDisplayText());
|
||||
|
||||
if (ShowAsDateTime)
|
||||
StopTimer();
|
||||
else
|
||||
StartTimer();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnLoaded(RoutedEventArgs e)
|
||||
{
|
||||
base.OnLoaded(e);
|
||||
|
||||
if (!ShowAsDateTime)
|
||||
StartTimer();
|
||||
}
|
||||
|
||||
protected override void OnUnloaded(RoutedEventArgs e)
|
||||
{
|
||||
base.OnUnloaded(e);
|
||||
StopTimer();
|
||||
}
|
||||
|
||||
private void StartTimer()
|
||||
{
|
||||
if (_refreshTimer != null)
|
||||
return;
|
||||
|
||||
_refreshTimer = DispatcherTimer.Run(() =>
|
||||
{
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
var text = GetDisplayText();
|
||||
if (!text.Equals(Text, StringComparison.Ordinal))
|
||||
Text = text;
|
||||
});
|
||||
|
||||
return true;
|
||||
}, TimeSpan.FromSeconds(10));
|
||||
}
|
||||
|
||||
private void StopTimer()
|
||||
{
|
||||
if (_refreshTimer != null)
|
||||
{
|
||||
_refreshTimer.Dispose();
|
||||
_refreshTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
private string GetDisplayText()
|
||||
{
|
||||
if (ShowAsDateTime)
|
||||
return DateTime.UnixEpoch.AddSeconds(Timestamp).ToLocalTime().ToString("yyyy/MM/dd HH:mm:ss");
|
||||
|
||||
var today = DateTime.Today;
|
||||
var committerTime = DateTime.UnixEpoch.AddSeconds(Timestamp).ToLocalTime();
|
||||
|
||||
if (committerTime >= today)
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
var timespan = now - committerTime;
|
||||
if (timespan.TotalHours > 1)
|
||||
return App.Text("Period.HoursAgo", (int)timespan.TotalHours);
|
||||
|
||||
return timespan.TotalMinutes < 1 ? App.Text("Period.JustNow") : App.Text("Period.MinutesAgo", (int)timespan.TotalMinutes);
|
||||
}
|
||||
|
||||
var diffYear = today.Year - committerTime.Year;
|
||||
if (diffYear == 0)
|
||||
{
|
||||
var diffMonth = today.Month - committerTime.Month;
|
||||
if (diffMonth > 0)
|
||||
return diffMonth == 1 ? App.Text("Period.LastMonth") : App.Text("Period.MonthsAgo", diffMonth);
|
||||
|
||||
var diffDay = today.Day - committerTime.Day;
|
||||
return diffDay == 1 ? App.Text("Period.Yesterday") : App.Text("Period.DaysAgo", diffDay);
|
||||
}
|
||||
|
||||
return diffYear == 1 ? App.Text("Period.LastYear") : App.Text("Period.YearsAgo", diffYear);
|
||||
}
|
||||
|
||||
private IDisposable _refreshTimer = null;
|
||||
}
|
||||
|
||||
public class CommitGraph : Control
|
||||
{
|
||||
public static readonly StyledProperty<Models.CommitGraph> GraphProperty =
|
||||
|
@ -158,7 +277,7 @@ namespace SourceGit.Views
|
|||
if (line.Points[size - 1].Y < top)
|
||||
continue;
|
||||
if (last.Y > bottom)
|
||||
continue;
|
||||
break;
|
||||
|
||||
var geo = new StreamGeometry();
|
||||
var pen = Models.CommitGraph.Pens[line.Color];
|
||||
|
|
|
@ -46,8 +46,6 @@
|
|||
<Path Grid.Column="0"
|
||||
Width="12" Height="12" Margin="12,0"
|
||||
Fill="{Binding Node.Bookmark, Converter={x:Static c:BookmarkConverters.ToBrush}}"
|
||||
StrokeThickness="{Binding Node.Bookmark, Converter={x:Static c:BookmarkConverters.ToStrokeThickness}}"
|
||||
Stroke="{DynamicResource Brush.FG1}"
|
||||
Data="{StaticResource Icons.Bookmark}"
|
||||
IsVisible="{Binding Node.IsRepository}"
|
||||
IsHitTestVisible="False"/>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<Grid ColumnDefinitions="*,Auto,*">
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal" Margin="4,0,0,0">
|
||||
<Button Classes="icon_button" Width="32" Command="{Binding OpenInFileManager}" ToolTip.Tip="{DynamicResource Text.Repository.Explore}">
|
||||
<Path Width="15" Height="13" Data="{StaticResource Icons.Folder.Open}" Margin="0,1,0,0"/>
|
||||
<Path Width="15" Height="15" Data="{StaticResource Icons.Explore}" Margin="0,2,0,0"/>
|
||||
</Button>
|
||||
|
||||
<Button Classes="icon_button" Width="32" Command="{Binding OpenInTerminal}" ToolTip.Tip="{DynamicResource Text.Repository.Terminal}">
|
||||
|
|
|
@ -103,8 +103,7 @@
|
|||
|
||||
<!-- Changes -->
|
||||
<Border Grid.Row="1" Margin="0,4,0,0" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}">
|
||||
<v:ChangeCollectionView IsWorkingCopyChange="False"
|
||||
ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=CommitChangeViewMode}"
|
||||
<v:ChangeCollectionView ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=CommitChangeViewMode}"
|
||||
Changes="{Binding VisibleChanges}"
|
||||
SelectedChanges="{Binding SelectedChanges, Mode=TwoWay}"
|
||||
ContextRequested="OnChangeContextRequested"/>
|
||||
|
|
|
@ -74,7 +74,7 @@ namespace SourceGit.Views
|
|||
CreateContent("Icons.Submodule", new Thickness(0, 0, 0, 0));
|
||||
break;
|
||||
default:
|
||||
CreateContent(node.IsExpanded ? "Icons.Folder.Open" : "Icons.Folder.Fill", new Thickness(0, 2, 0, 0), Brushes.Goldenrod);
|
||||
CreateContent(node.IsExpanded ? "Icons.Folder.Open" : "Icons.Folder", new Thickness(0, 2, 0, 0), Brushes.Goldenrod);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -90,6 +90,7 @@ namespace SourceGit.Views
|
|||
Width = 14,
|
||||
Height = 14,
|
||||
Margin = margin,
|
||||
Stretch = Stretch.Uniform,
|
||||
HorizontalAlignment = HorizontalAlignment.Left,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Data = geo,
|
||||
|
|
|
@ -128,7 +128,7 @@
|
|||
<DataGridTemplateColumn Header="ICON">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<v:ChangeStatusIcon Width="14" Height="14" IsWorkingCopyChange="False" Change="{Binding}"/>
|
||||
<v:ChangeStatusIcon Width="14" Height="14" Change="{Binding}"/>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
|
|
|
@ -9,29 +9,11 @@
|
|||
x:Class="SourceGit.Views.TextDiffView"
|
||||
x:Name="ThisControl"
|
||||
Background="{DynamicResource Brush.Contents}">
|
||||
<UserControl.DataTemplates>
|
||||
<DataTemplate DataType="m:TextDiff">
|
||||
<v:CombinedTextDiffPresenter FileName="{Binding File}"
|
||||
Foreground="{DynamicResource Brush.FG1}"
|
||||
LineBrush="{DynamicResource Brush.Border2}"
|
||||
EmptyContentBackground="{DynamicResource Brush.Diff.EmptyBG}"
|
||||
AddedContentBackground="{DynamicResource Brush.Diff.AddedBG}"
|
||||
DeletedContentBackground="{DynamicResource Brush.Diff.DeletedBG}"
|
||||
AddedHighlightBrush="{DynamicResource Brush.Diff.AddedHighlight}"
|
||||
DeletedHighlightBrush="{DynamicResource Brush.Diff.DeletedHighlight}"
|
||||
IndicatorForeground="{DynamicResource Brush.FG2}"
|
||||
FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}"
|
||||
UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting}"
|
||||
WordWrap="{Binding Source={x:Static vm:Preference.Instance}, Path=EnableDiffViewWordWrap}"
|
||||
ShowHiddenSymbols="{Binding Source={x:Static vm:Preference.Instance}, Path=ShowHiddenSymbolsInDiffView}"
|
||||
/>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate DataType="vm:TwoSideTextDiff">
|
||||
<Grid ColumnDefinitions="*,1,*">
|
||||
<v:SingleSideTextDiffPresenter Grid.Column="0"
|
||||
IsOld="True"
|
||||
FileName="{Binding File}"
|
||||
<Grid>
|
||||
<ContentControl x:Name="Editor">
|
||||
<ContentControl.DataTemplates>
|
||||
<DataTemplate DataType="m:TextDiff">
|
||||
<v:CombinedTextDiffPresenter FileName="{Binding File}"
|
||||
Foreground="{DynamicResource Brush.FG1}"
|
||||
LineBrush="{DynamicResource Brush.Border2}"
|
||||
EmptyContentBackground="{DynamicResource Brush.Diff.EmptyBG}"
|
||||
|
@ -44,27 +26,58 @@
|
|||
UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting}"
|
||||
WordWrap="{Binding Source={x:Static vm:Preference.Instance}, Path=EnableDiffViewWordWrap}"
|
||||
ShowHiddenSymbols="{Binding Source={x:Static vm:Preference.Instance}, Path=ShowHiddenSymbolsInDiffView}"
|
||||
/>
|
||||
EnableChunkSelection="{Binding #ThisControl.EnableChunkSelection}"
|
||||
SelectedChunk="{Binding #ThisControl.SelectedChunk, Mode=TwoWay}"/>
|
||||
</DataTemplate>
|
||||
|
||||
<Rectangle Grid.Column="1" Fill="{DynamicResource Brush.Border2}" Width="1" HorizontalAlignment="Center" VerticalAlignment="Stretch"/>
|
||||
<DataTemplate DataType="vm:TwoSideTextDiff">
|
||||
<Grid ColumnDefinitions="*,1,*">
|
||||
<v:SingleSideTextDiffPresenter Grid.Column="0"
|
||||
IsOld="True"
|
||||
FileName="{Binding File}"
|
||||
Foreground="{DynamicResource Brush.FG1}"
|
||||
LineBrush="{DynamicResource Brush.Border2}"
|
||||
EmptyContentBackground="{DynamicResource Brush.Diff.EmptyBG}"
|
||||
AddedContentBackground="{DynamicResource Brush.Diff.AddedBG}"
|
||||
DeletedContentBackground="{DynamicResource Brush.Diff.DeletedBG}"
|
||||
AddedHighlightBrush="{DynamicResource Brush.Diff.AddedHighlight}"
|
||||
DeletedHighlightBrush="{DynamicResource Brush.Diff.DeletedHighlight}"
|
||||
IndicatorForeground="{DynamicResource Brush.FG2}"
|
||||
FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}"
|
||||
UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting}"
|
||||
WordWrap="{Binding Source={x:Static vm:Preference.Instance}, Path=EnableDiffViewWordWrap}"
|
||||
ShowHiddenSymbols="{Binding Source={x:Static vm:Preference.Instance}, Path=ShowHiddenSymbolsInDiffView}"
|
||||
EnableChunkSelection="{Binding #ThisControl.EnableChunkSelection}"
|
||||
SelectedChunk="{Binding #ThisControl.SelectedChunk, Mode=TwoWay}"/>
|
||||
|
||||
<v:SingleSideTextDiffPresenter Grid.Column="2"
|
||||
IsOld="False"
|
||||
FileName="{Binding File}"
|
||||
Foreground="{DynamicResource Brush.FG1}"
|
||||
LineBrush="{DynamicResource Brush.Border2}"
|
||||
EmptyContentBackground="{DynamicResource Brush.Diff.EmptyBG}"
|
||||
AddedContentBackground="{DynamicResource Brush.Diff.AddedBG}"
|
||||
DeletedContentBackground="{DynamicResource Brush.Diff.DeletedBG}"
|
||||
AddedHighlightBrush="{DynamicResource Brush.Diff.AddedHighlight}"
|
||||
DeletedHighlightBrush="{DynamicResource Brush.Diff.DeletedHighlight}"
|
||||
IndicatorForeground="{DynamicResource Brush.FG2}"
|
||||
FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}"
|
||||
UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting}"
|
||||
WordWrap="{Binding Source={x:Static vm:Preference.Instance}, Path=EnableDiffViewWordWrap}"
|
||||
ShowHiddenSymbols="{Binding Source={x:Static vm:Preference.Instance}, Path=ShowHiddenSymbolsInDiffView}"
|
||||
/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</UserControl.DataTemplates>
|
||||
<Rectangle Grid.Column="1" Fill="{DynamicResource Brush.Border2}" Width="1" HorizontalAlignment="Center" VerticalAlignment="Stretch"/>
|
||||
|
||||
<v:SingleSideTextDiffPresenter Grid.Column="2"
|
||||
IsOld="False"
|
||||
FileName="{Binding File}"
|
||||
Foreground="{DynamicResource Brush.FG1}"
|
||||
LineBrush="{DynamicResource Brush.Border2}"
|
||||
EmptyContentBackground="{DynamicResource Brush.Diff.EmptyBG}"
|
||||
AddedContentBackground="{DynamicResource Brush.Diff.AddedBG}"
|
||||
DeletedContentBackground="{DynamicResource Brush.Diff.DeletedBG}"
|
||||
AddedHighlightBrush="{DynamicResource Brush.Diff.AddedHighlight}"
|
||||
DeletedHighlightBrush="{DynamicResource Brush.Diff.DeletedHighlight}"
|
||||
IndicatorForeground="{DynamicResource Brush.FG2}"
|
||||
FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}"
|
||||
UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting}"
|
||||
WordWrap="{Binding Source={x:Static vm:Preference.Instance}, Path=EnableDiffViewWordWrap}"
|
||||
ShowHiddenSymbols="{Binding Source={x:Static vm:Preference.Instance}, Path=ShowHiddenSymbolsInDiffView}"
|
||||
EnableChunkSelection="{Binding #ThisControl.EnableChunkSelection}"
|
||||
SelectedChunk="{Binding #ThisControl.SelectedChunk, Mode=TwoWay}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ContentControl.DataTemplates>
|
||||
</ContentControl>
|
||||
|
||||
<StackPanel x:Name="Popup" IsVisible="False" Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Right" Effect="drop-shadow(0 0 6 #40000000)">
|
||||
<Button Classes="flat" Content="{DynamicResource Text.Hunk.Stage}" Click="OnStageChunk" IsVisible="{Binding #ThisControl.IsUnstagedChange}"/>
|
||||
<Button Classes="flat" Content="{DynamicResource Text.Hunk.Unstage}" Click="OnUnstageChunk" IsVisible="{Binding #ThisControl.IsUnstagedChange, Converter={x:Static BoolConverters.Not}}"/>
|
||||
<Button Classes="flat" Content="{DynamicResource Text.Hunk.Discard}" Margin="8,0,0,0" Click="OnDiscardChunk"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
VerticalContentAlignment="Center"
|
||||
Text="{Binding SearchFilter, Mode=TwoWay}">
|
||||
<TextBox.InnerLeftContent>
|
||||
<Path Width="16" Height="16" Margin="6,0,3,0" Data="{StaticResource Icons.Search}" Fill="{DynamicResource Brush.FG1}"/>
|
||||
<Path Width="16" Height="16" Margin="6,0,3,0" Data="{StaticResource Icons.Search}" Fill="{DynamicResource Brush.FG2}"/>
|
||||
</TextBox.InnerLeftContent>
|
||||
|
||||
<TextBox.InnerRightContent>
|
||||
|
@ -59,7 +59,7 @@
|
|||
<TreeView.ItemTemplate>
|
||||
<TreeDataTemplate ItemsSource="{Binding SubNodes}">
|
||||
<Grid Height="30"
|
||||
ColumnDefinitions="Auto,Auto,*"
|
||||
ColumnDefinitions="18,Auto,*"
|
||||
Background="Transparent"
|
||||
Loaded="SetupTreeNodeDragAndDrop"
|
||||
ContextRequested="OnTreeNodeContextRequested"
|
||||
|
@ -68,19 +68,18 @@
|
|||
PointerReleased="OnPointerReleasedOnTreeNode"
|
||||
DoubleTapped="OnDoubleTappedTreeNode"
|
||||
ClipToBounds="True">
|
||||
<Path Grid.Column="0" Width="12" Height="12" Margin="0,0,4,0"
|
||||
<Path Grid.Column="0"
|
||||
Width="14" Height="14"
|
||||
Fill="{Binding Bookmark, Converter={x:Static c:BookmarkConverters.ToBrush}}"
|
||||
StrokeThickness="{Binding Bookmark, Converter={x:Static c:BookmarkConverters.ToStrokeThickness}}"
|
||||
Stroke="{DynamicResource Brush.FG1}"
|
||||
HorizontalAlignment="Left"
|
||||
HorizontalAlignment="Center"
|
||||
Data="{StaticResource Icons.Bookmark}"
|
||||
IsVisible="{Binding IsRepository}"/>
|
||||
|
||||
<ToggleButton Grid.Column="0"
|
||||
Classes="folder"
|
||||
Focusable="False"
|
||||
Width="12" Height="12"
|
||||
Margin="0,0,4,0"
|
||||
Width="14" Height="14"
|
||||
HorizontalAlignment="Left"
|
||||
Foreground="{DynamicResource Brush.FG1}"
|
||||
IsChecked="{Binding IsExpanded}"
|
||||
IsVisible="{Binding !IsRepository}"/>
|
||||
|
|
|
@ -6,27 +6,24 @@
|
|||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="SourceGit.Views.WelcomeToolbar"
|
||||
x:DataType="vm:Welcome">
|
||||
<StackPanel Orientation="Horizontal" Margin="4,0,0,0">
|
||||
<Button Classes="icon_button" Width="32" Command="{Binding Clone}" ToolTip.Tip="{DynamicResource Text.Welcome.Clone}">
|
||||
<Path Width="13" Height="13" Data="{StaticResource Icons.Pull}"/>
|
||||
</Button>
|
||||
<Grid ColumnDefinitions="Auto,*,Auto" Margin="4,0">
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal">
|
||||
<Button Classes="icon_button" Width="32" Command="{Binding Clone}" ToolTip.Tip="{DynamicResource Text.Welcome.Clone}">
|
||||
<Path Width="13" Height="13" Data="{StaticResource Icons.Pull}"/>
|
||||
</Button>
|
||||
|
||||
<Button Classes="icon_button" Width="32" Click="OpenLocalRepository" ToolTip.Tip="{DynamicResource Text.Welcome.OpenOrInit}">
|
||||
<Path Width="16" Height="16" Data="{StaticResource Icons.Folder.Add2}" Margin="0,4,0,0"/>
|
||||
</Button>
|
||||
<Button Classes="icon_button" Width="32" Click="OpenLocalRepository" ToolTip.Tip="{DynamicResource Text.Welcome.OpenOrInit}">
|
||||
<Path Width="14" Height="14" Data="{StaticResource Icons.Folder.Open}" Margin="0,2,0,0"/>
|
||||
</Button>
|
||||
|
||||
<Button Classes="icon_button" Width="32" Command="{Binding OpenTerminal}" ToolTip.Tip="{DynamicResource Text.Welcome.OpenTerminal}">
|
||||
<Path Width="13" Height="13" Data="{StaticResource Icons.Terminal}"/>
|
||||
</Button>
|
||||
<Button Classes="icon_button" Width="32" Command="{Binding OpenTerminal}" ToolTip.Tip="{DynamicResource Text.Welcome.OpenTerminal}">
|
||||
<Path Width="13" Height="13" Data="{StaticResource Icons.Terminal}"/>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<Rectangle Width="1" Height="16"
|
||||
Margin="4,0"
|
||||
VerticalAlignment="Center"
|
||||
Fill="{DynamicResource Brush.Border2}"/>
|
||||
|
||||
<Button Classes="icon_button" Width="32" Command="{Binding AddRootNode}" ToolTip.Tip="{DynamicResource Text.Welcome.AddRootFolder}">
|
||||
<Button Grid.Column="2" Classes="icon_button" Width="32" Command="{Binding AddRootNode}" ToolTip.Tip="{DynamicResource Text.Welcome.AddRootFolder}">
|
||||
<Path Width="14" Height="14" Margin="0,2,0,0" Data="{StaticResource Icons.Folder.Add}"/>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
|
||||
<!-- Unstaged Changes -->
|
||||
<v:ChangeCollectionView Grid.Row="1"
|
||||
IsWorkingCopyChange="True"
|
||||
IsUnstagedChange="True"
|
||||
SelectionMode="Multiple"
|
||||
Background="{DynamicResource Brush.Contents}"
|
||||
ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=UnstagedChangeViewMode}"
|
||||
|
@ -98,7 +98,6 @@
|
|||
|
||||
<!-- Staged Changes -->
|
||||
<v:ChangeCollectionView Grid.Row="3"
|
||||
IsWorkingCopyChange="False"
|
||||
SelectionMode="Multiple"
|
||||
Background="{DynamicResource Brush.Contents}"
|
||||
ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=StagedChangeViewMode}"
|
||||
|
|