diff --git a/README.md b/README.md index cbba9388..023b1c96 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Opensource Git GUI client. * Supports Windows/macOS/Linux * Opensource/Free * Fast -* English/German/Português/简体中文/繁體中文 +* English/Français/Deutsch/Português/简体中文/繁體中文 * Built-in light/dark themes * Customize theme * Visual commit graph diff --git a/VERSION b/VERSION index eeb14e9b..87641a66 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.27 \ No newline at end of file +8.28 \ No newline at end of file diff --git a/screenshots/theme_dark.png b/screenshots/theme_dark.png index 8541b747..5954be00 100644 Binary files a/screenshots/theme_dark.png and b/screenshots/theme_dark.png differ diff --git a/screenshots/theme_light.png b/screenshots/theme_light.png index a7fd1947..cc68ccb3 100644 Binary files a/screenshots/theme_light.png and b/screenshots/theme_light.png differ diff --git a/src/App.Commands.cs b/src/App.Commands.cs index 55241afc..4a594766 100644 --- a/src/App.Commands.cs +++ b/src/App.Commands.cs @@ -43,7 +43,7 @@ namespace SourceGit private Action _action = null; } - + public static readonly SimpleCommand OpenPreferenceCommand = new SimpleCommand(() => OpenDialog(new Views.Preference())); public static readonly SimpleCommand OpenHotkeysCommand = new SimpleCommand(() => OpenDialog(new Views.Hotkeys())); public static readonly SimpleCommand OpenAppDataDirCommand = new SimpleCommand(() => Native.OS.OpenInFileManager(Native.OS.DataDir)); diff --git a/src/App.axaml b/src/App.axaml index 1831f59c..56d81615 100644 --- a/src/App.axaml +++ b/src/App.axaml @@ -22,7 +22,6 @@ - diff --git a/src/App.axaml.cs b/src/App.axaml.cs index 5dab4836..98d671d3 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -98,7 +98,7 @@ namespace SourceGit public static void OpenDialog(Window window) { - if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner}) + if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner }) window.ShowDialog(owner); } diff --git a/src/Commands/Checkout.cs b/src/Commands/Checkout.cs index a1a151aa..25f93e89 100644 --- a/src/Commands/Checkout.cs +++ b/src/Commands/Checkout.cs @@ -58,7 +58,7 @@ namespace SourceGit.Commands public bool FileWithRevision(string file, string revision) { - Args = $"checkout {revision} -- \"{file}\""; + Args = $"checkout --no-overlay {revision} -- \"{file}\""; return Exec(); } diff --git a/src/Commands/CherryPick.cs b/src/Commands/CherryPick.cs index 504769c0..95b56655 100644 --- a/src/Commands/CherryPick.cs +++ b/src/Commands/CherryPick.cs @@ -2,12 +2,12 @@ { public class CherryPick : Command { - public CherryPick(string repo, string commit, bool noCommit) + public CherryPick(string repo, string commits, bool noCommit) { var mode = noCommit ? "-n" : "--ff"; WorkingDirectory = repo; Context = repo; - Args = $"cherry-pick {mode} {commit}"; + Args = $"cherry-pick {mode} {commits}"; } } } diff --git a/src/Commands/Command.cs b/src/Commands/Command.cs index 26083ef9..b347dc37 100644 --- a/src/Commands/Command.cs +++ b/src/Commands/Command.cs @@ -17,8 +17,9 @@ namespace SourceGit.Commands public class ReadToEndResult { - public bool IsSuccess { get; set; } - public string StdOut { get; set; } + public bool IsSuccess { get; set; } = false; + public string StdOut { get; set; } = ""; + public string StdErr { get; set; } = ""; } public enum EditorType @@ -198,18 +199,20 @@ namespace SourceGit.Commands { proc.Start(); } - catch + catch (Exception e) { return new ReadToEndResult() { IsSuccess = false, StdOut = string.Empty, + StdErr = e.Message, }; } var rs = new ReadToEndResult() { StdOut = proc.StandardOutput.ReadToEnd(), + StdErr = proc.StandardError.ReadToEnd(), }; proc.WaitForExit(); diff --git a/src/Commands/QueryCommits.cs b/src/Commands/QueryCommits.cs index ef80566e..8a48eff3 100644 --- a/src/Commands/QueryCommits.cs +++ b/src/Commands/QueryCommits.cs @@ -14,17 +14,22 @@ namespace SourceGit.Commands _findFirstMerged = needFindHead; } - public QueryCommits(string repo, int maxCount, string messageFilter, bool isFile) + public QueryCommits(string repo, string filter, Models.CommitSearchMethod method) { string search; - if (isFile) + + if (method == Models.CommitSearchMethod.ByUser) { - search = $"-- \"{messageFilter}\""; + search = $"-i --author=\"{filter}\" --committer=\"{filter}\""; + } + else if (method == Models.CommitSearchMethod.ByFile) + { + search = $"-- \"{filter}\""; } else { var argsBuilder = new StringBuilder(); - var words = messageFilter.Split(new[] { ' ', '\t', '\r' }, StringSplitOptions.RemoveEmptyEntries); + var words = filter.Split(new[] { ' ', '\t', '\r' }, StringSplitOptions.RemoveEmptyEntries); foreach (var word in words) { var escaped = word.Trim().Replace("\"", "\\\"", StringComparison.Ordinal); @@ -36,7 +41,7 @@ namespace SourceGit.Commands 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 --branches --remotes " + search; + Args = $"log -1000 --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; } diff --git a/src/Commands/QueryRepositoryRootPath.cs b/src/Commands/QueryRepositoryRootPath.cs index 1eef5af8..016621c8 100644 --- a/src/Commands/QueryRepositoryRootPath.cs +++ b/src/Commands/QueryRepositoryRootPath.cs @@ -6,15 +6,6 @@ { WorkingDirectory = path; Args = "rev-parse --show-toplevel"; - RaiseError = false; - } - - public string Result() - { - var rs = ReadToEnd().StdOut; - if (string.IsNullOrEmpty(rs)) - return null; - return rs.Trim(); } } } diff --git a/src/Commands/QuerySingleCommit.cs b/src/Commands/QuerySingleCommit.cs index 6c242631..1e1c9ea4 100644 --- a/src/Commands/QuerySingleCommit.cs +++ b/src/Commands/QuerySingleCommit.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; namespace SourceGit.Commands { diff --git a/src/Commands/Statistics.cs b/src/Commands/Statistics.cs index b9f33367..6b2296ca 100644 --- a/src/Commands/Statistics.cs +++ b/src/Commands/Statistics.cs @@ -10,7 +10,7 @@ namespace SourceGit.Commands WorkingDirectory = repo; Context = repo; - Args = $"log --date-order --branches --remotes --since=\"{_statistics.Since()}\" --pretty=format:\"%ct$%cn\""; + Args = $"log --date-order --branches --remotes --since=\"{_statistics.Since()}\" --pretty=format:\"%ct$%an\""; } public Models.Statistics Result() diff --git a/src/Models/Commit.cs b/src/Models/Commit.cs index 6a199478..9549e4a4 100644 --- a/src/Models/Commit.cs +++ b/src/Models/Commit.cs @@ -6,6 +6,13 @@ using Avalonia.Media; namespace SourceGit.Models { + public enum CommitSearchMethod + { + ByUser, + ByMessage, + ByFile, + } + public class Commit { public static double OpacityForNotMerged diff --git a/src/Models/CommitGraph.cs b/src/Models/CommitGraph.cs index 55746a43..fde8011c 100644 --- a/src/Models/CommitGraph.cs +++ b/src/Models/CommitGraph.cs @@ -191,6 +191,7 @@ namespace SourceGit.Models } isMerged = isMerged || l.IsMerged; + major.IsMerged = isMerged; } else { @@ -202,12 +203,17 @@ namespace SourceGit.Models } // Create new curve for branch head - if (major == null && commit.Parents.Count > 0) + if (major == null) { offsetX += UNIT_WIDTH; - major = new PathHelper(commit.Parents[0], isMerged, colorIdx, new Point(offsetX, offsetY)); - unsolved.Add(major); - temp.Paths.Add(major.Path); + + if (commit.Parents.Count > 0) + { + major = new PathHelper(commit.Parents[0], isMerged, colorIdx, new Point(offsetX, offsetY)); + unsolved.Add(major); + temp.Paths.Add(major.Path); + } + colorIdx = (colorIdx + 1) % _penCount; } @@ -216,11 +222,9 @@ namespace SourceGit.Models int dotColor = 0; if (major != null) { - major.IsMerged = isMerged; position = new Point(major.LastX, offsetY); dotColor = major.Path.Color; } - Dot anchor = new Dot() { Center = position, Color = dotColor }; if (commit.IsCurrentHead) anchor.Type = DotType.Head; @@ -271,7 +275,7 @@ namespace SourceGit.Models unsolved.Remove(l); } - // Margins & merge state (used by datagrid). + // Margins & merge state (used by Views.Histories). commit.IsMerged = isMerged; commit.Margin = new Thickness(Math.Max(offsetX + HALF_WIDTH, oldCount * UNIT_WIDTH + H_MARGIN) + H_MARGIN, 0, 0, 0); diff --git a/src/Models/Statistics.cs b/src/Models/Statistics.cs index abdd24df..60517e59 100644 --- a/src/Models/Statistics.cs +++ b/src/Models/Statistics.cs @@ -3,42 +3,44 @@ using System.Collections.Generic; namespace SourceGit.Models { - public class StatisticsSample + public class StatisticsSample(string name) { - public string Name { get; set; } - public int Count { get; set; } + public string Name { get; set; } = name; + public int Count { get; set; } = 0; } public class StatisticsReport { public int Total { get; set; } = 0; public List Samples { get; set; } = new List(); - public List ByCommitter { get; set; } = new List(); + public List ByAuthor { get; set; } = new List(); - public void AddCommit(int index, string committer) + public void AddCommit(int index, string author) { Total++; Samples[index].Count++; - if (_mapByCommitter.TryGetValue(committer, out var value)) + if (_mapUsers.TryGetValue(author, out var value)) { value.Count++; } else { - var sample = new StatisticsSample() { Name = committer, Count = 1 }; - _mapByCommitter.Add(committer, sample); - ByCommitter.Add(sample); + var sample = new StatisticsSample(author); + sample.Count++; + + _mapUsers.Add(author, sample); + ByAuthor.Add(sample); } } public void Complete() { - ByCommitter.Sort((l, r) => r.Count - l.Count); - _mapByCommitter.Clear(); + ByAuthor.Sort((l, r) => r.Count - l.Count); + _mapUsers.Clear(); } - private readonly Dictionary _mapByCommitter = new Dictionary(); + private readonly Dictionary _mapUsers = new Dictionary(); } public class Statistics @@ -53,79 +55,33 @@ namespace SourceGit.Models _thisWeekStart = _today.AddSeconds(-(int)_today.DayOfWeek * 3600 * 24 - _today.Hour * 3600 - _today.Minute * 60 - _today.Second); _thisWeekEnd = _thisWeekStart.AddDays(7); - string[] monthNames = [ - "Jan", - "Feb", - "Mar", - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec", - ]; - - for (int i = 0; i < monthNames.Length; i++) - { - Year.Samples.Add(new StatisticsSample - { - Name = monthNames[i], - Count = 0, - }); - } + for (int i = 0; i < 12; i++) + Year.Samples.Add(new StatisticsSample("")); var monthDays = DateTime.DaysInMonth(_today.Year, _today.Month); for (int i = 0; i < monthDays; i++) - { - Month.Samples.Add(new StatisticsSample - { - Name = $"{i + 1}", - Count = 0, - }); - } - - string[] weekDayNames = [ - "SUN", - "MON", - "TUE", - "WED", - "THU", - "FRI", - "SAT", - ]; + Month.Samples.Add(new StatisticsSample($"{i + 1}")); + string[] weekDayNames = [ "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" ]; for (int i = 0; i < weekDayNames.Length; i++) - { - Week.Samples.Add(new StatisticsSample - { - Name = weekDayNames[i], - Count = 0, - }); - } + Week.Samples.Add(new StatisticsSample(weekDayNames[i])); } public string Since() { - return _today.ToString("yyyy-01-01 00:00:00"); + return _today.AddMonths(-11).ToString("yyyy-MM-01 00:00:00"); } - public void AddCommit(string committer, double timestamp) + public void AddCommit(string author, double timestamp) { var time = DateTime.UnixEpoch.AddSeconds(timestamp).ToLocalTime(); if (time.CompareTo(_thisWeekStart) >= 0 && time.CompareTo(_thisWeekEnd) < 0) - { - Week.AddCommit((int)time.DayOfWeek, committer); - } + Week.AddCommit((int)time.DayOfWeek, author); if (time.Month == _today.Month) - { - Month.AddCommit(time.Day - 1, committer); - } + Month.AddCommit(time.Day - 1, author); - Year.AddCommit(time.Month - 1, committer); + Year.AddCommit(time.Month - 1, author); } public void Complete() @@ -133,6 +89,30 @@ namespace SourceGit.Models Year.Complete(); Month.Complete(); Week.Complete(); + + // Year is start from 11 months ago from now. + var thisYear = _today.Year; + var start = _today.AddMonths(-11); + if (start.Month == 1) + { + for (int i = 0; i < 12; i++) + Year.Samples[i].Name = $"{thisYear}/{i + 1:00}"; + } + else + { + var lastYearIdx = start.Month - 1; + var lastYearMonths = Year.Samples.GetRange(lastYearIdx, 12 - lastYearIdx); + for (int i = 0; i < lastYearMonths.Count; i++) + lastYearMonths[i].Name = $"{thisYear - 1}/{lastYearIdx + i + 1:00}"; + + var thisYearMonths = Year.Samples.GetRange(0, lastYearIdx); + for (int i = 0; i < thisYearMonths.Count; i++) + thisYearMonths[i].Name = $"{thisYear}/{i + 1:00}"; + + Year.Samples.Clear(); + Year.Samples.AddRange(lastYearMonths); + Year.Samples.AddRange(thisYearMonths); + } } private readonly DateTime _today; diff --git a/src/Native/Linux.cs b/src/Native/Linux.cs index bd973f5f..1b33cc5d 100644 --- a/src/Native/Linux.cs +++ b/src/Native/Linux.cs @@ -91,7 +91,7 @@ namespace SourceGit.Native { var dir = string.IsNullOrEmpty(workdir) ? "~" : workdir; if (_terminal == null) - App.RaiseException(dir, $"Only supports gnome-terminal/konsole/xfce4-terminal/lxterminal/deepin-terminal!"); + App.RaiseException(dir, $"Only supports gnome-terminal/konsole/xfce4-terminal/lxterminal/deepin-terminal/mate-terminal/foot!"); else _terminal.Open(dir); } @@ -159,6 +159,10 @@ namespace SourceGit.Native test = Path.Combine(path, "mate-terminal"); if (File.Exists(test)) return new Terminal(test, "--working-directory=\"{0}\""); + + test = Path.Combine(path, "foot"); + if (File.Exists(test)) + return new Terminal(test, "--working-directory=\"{0}\""); } return null; diff --git a/src/Resources/Icons.axaml b/src/Resources/Icons.axaml index bbef234a..8090e576 100644 --- a/src/Resources/Icons.axaml +++ b/src/Resources/Icons.axaml @@ -88,6 +88,7 @@ M960 146v91C960 318 759 384 512 384S64 318 64 238V146C64 66 265 0 512 0s448 66 448 146zM960 352v206C960 638 759 704 512 704S64 638 64 558V352c96 66 272 97 448 97S864 418 960 352zm0 320v206C960 958 759 1024 512 1024S64 958 64 878V672c96 66 272 97 448 97S864 738 960 672z M883 567l-128-128c-17-17-43-17-60 0l-128 128c-17 17-17 43 0 60 17 17 43 17 60 0l55-55V683c0 21-21 43-43 43H418c-13-38-43-64-77-77V375c51-17 85-64 85-119 0-73-60-128-128-128-73 0-128 55-128 128 0 55 34 102 85 119v269c-51 17-85 64-85 119 0 73 55 128 128 128 55 0 102-34 119-85H640c73 0 128-55 128-128v-111l55 55c9 9 17 13 30 13 13 0 21-4 30-13 17-13 17-43 0-55zM299 213c26 0 43 17 43 43 0 21-21 43-43 43-26 0-43-21-43-43 0-26 17-43 43-43zm0 597c-26 0-43-21-43-43 0-26 17-43 43-43s43 17 43 43c0 21-17 43-43 43zM725 384c-73 0-128-60-128-128 0-73 55-128 128-128s128 55 128 128c0 68-55 128-128 128zm0-171c-26 0-43 17-43 43s17 43 43 43 43-17 43-43-17-43-43-43z M293 122v244h439V146l171 175V829a73 73 0 01-73 73h-98V536H293v366H195a73 73 0 01-73-73V195a73 73 0 0173-73h98zm366 512v268H366V634h293zm-49 49h-195v73h195v-73zm49-561v171H366V122h293z + M0 551V472c0-11 9-19 19-19h984c11 0 19 9 19 19v79c0 11-9 19-19 19H19c-11 0-19-9-19-19zM114 154v240c0 11-9 19-19 19H19C9 413 0 404 0 394V79C0 35 35 0 79 0h315c11 0 19 9 19 19v75c0 11-9 19-19 19H154c-21 0-39 18-39 39zm795 0c0-22-17-39-39-39h-240c-11 0-19-9-19-19V19c0-11 9-19 19-19h315c43 0 79 35 79 79v315c0 11-9 19-19 19h-75c-11 0-19-9-19-19l-1-240zm0 716v-240c0-11 9-19 19-19h75c11 0 19 9 19 19v315c0 43-35 79-79 79h-315c-11 0-19-9-19-19v-75c0-11 9-19 19-19H870c21-1 39-18 39-40zm-795 0c0 21 18 39 39 39h240c11 0 19 9 19 19v75c0 11-9 19-19 19H79C35 1023 0 988 0 945v-315c0-11 9-19 19-19h75c11 0 19 9 19 19V870z M702 677 590 565a148 148 0 10-25 27L676 703zm-346-200a115 115 0 11115 115A115 115 0 01355 478z M928 500a21 21 0 00-19-20L858 472a11 11 0 01-9-9c-1-6-2-13-3-19a11 11 0 015-12l46-25a21 21 0 0010-26l-8-22a21 21 0 00-24-13l-51 10a11 11 0 01-12-6c-3-6-6-11-10-17a11 11 0 011-13l34-39a21 21 0 001-28l-15-18a20 20 0 00-27-4l-45 27a11 11 0 01-13-1c-5-4-10-9-15-12a11 11 0 01-3-12l19-49a21 21 0 00-9-26l-20-12a21 21 0 00-27 6L650 193a9 9 0 01-11 3c-1-1-12-5-20-7a11 11 0 01-7-10l1-52a21 21 0 00-17-22l-23-4a21 21 0 00-24 14L532 164a11 11 0 01-11 7h-20a11 11 0 01-11-7l-17-49a21 21 0 00-24-15l-23 4a21 21 0 00-17 22l1 52a11 11 0 01-8 11c-5 2-15 6-19 7c-4 1-8 0-12-4l-33-40A21 21 0 00313 146l-20 12A21 21 0 00285 184l19 49a11 11 0 01-3 12c-5 4-10 8-15 12a11 11 0 01-13 1L228 231a21 21 0 00-27 4L186 253a21 21 0 001 28L221 320a11 11 0 011 13c-3 5-7 11-10 17a11 11 0 01-12 6l-51-10a21 21 0 00-24 13l-8 22a21 21 0 0010 26l46 25a11 11 0 015 12l0 3c-1 6-2 11-3 16a11 11 0 01-9 9l-51 8A21 21 0 0096 500v23A21 21 0 00114 544l51 8a11 11 0 019 9c1 6 2 13 3 19a11 11 0 01-5 12l-46 25a21 21 0 00-10 26l8 22a21 21 0 0024 13l51-10a11 11 0 0112 6c3 6 6 11 10 17a11 11 0 01-1 13l-34 39a21 21 0 00-1 28l15 18a20 20 0 0027 4l45-27a11 11 0 0113 1c5 4 10 9 15 12a11 11 0 013 12l-19 49a21 21 0 009 26l20 12a21 21 0 0027-6L374 832c3-3 7-5 10-4c7 3 12 5 20 7a11 11 0 018 10l-1 52a21 21 0 0017 22l23 4a21 21 0 0024-14l17-50a11 11 0 0111-7h20a11 11 0 0111 7l17 49a21 21 0 0020 15a19 19 0 004 0l23-4a21 21 0 0017-22l-1-52a11 11 0 018-10c8-3 13-5 18-7l1 0c6-2 9 0 11 3l34 41A21 21 0 00710 878l20-12a21 21 0 009-26l-18-49a11 11 0 013-12c5-4 10-8 15-12a11 11 0 0113-1l45 27a21 21 0 0027-4l15-18a21 21 0 00-1-28l-34-39a11 11 0 01-1-13c3-5 7-11 10-17a11 11 0 0112-6l51 10a21 21 0 0024-13l8-22a21 21 0 00-10-26l-46-25a11 11 0 01-5-12l0-3c1-6 2-11 3-16a11 11 0 019-9l51-8a21 21 0 0018-21v-23zm-565 188a32 32 0 01-51 5a270 270 0 011-363a32 32 0 0151 6l91 161a32 32 0 010 31zM512 782a270 270 0 01-57-6a32 32 0 01-20-47l92-160a32 32 0 0127-16h184a32 32 0 0130 41c-35 109-137 188-257 188zm15-328L436 294a32 32 0 0121-47a268 268 0 0155-6c120 0 222 79 257 188a32 32 0 01-30 41h-184a32 32 0 01-28-16z M900 287c40 69 60 144 60 225s-20 156-60 225c-40 69-94 123-163 163-69 40-144 60-225 60s-156-20-225-60c-69-40-123-94-163-163C84 668 64 593 64 512s20-156 60-225 94-123 163-163c69-40 144-60 225-60s156 20 225 60 123 94 163 163zM762 512c0-9-3-16-9-22L578 315l-44-44c-6-6-13-9-22-9s-16 3-22 9l-44 44-176 176c-6 6-9 13-9 22s3 16 9 22l44 44c6 6 13 9 22 9s16-3 22-9l92-92v269c0 9 3 16 9 22 6 6 13 9 22 9h62c8 0 16-3 22-9 6-6 9-13 9-22V486l92 92c6 6 13 9 22 9 8 0 16-3 22-9l44-44c6-6 9-13 9-22z diff --git a/src/Resources/Locales/de_DE.axaml b/src/Resources/Locales/de_DE.axaml index 3752841a..7f6a6a18 100644 --- a/src/Resources/Locales/de_DE.axaml +++ b/src/Resources/Locales/de_DE.axaml @@ -82,10 +82,9 @@ Verwerfen Nichts tun Stashen & wieder anwenden - Diesen Commit cherry-picken - Commit: + Cherry Pick + Commit(s): Alle Änderungen committen - Cherry Pick Stashes löschen Du versuchst alle Stashes zu löschen. Möchtest du wirklich fortfahren? Remote Repository klonen @@ -109,7 +108,8 @@ Commit rückgängig machen Umformulieren Als Patch speichern... - Squash Commits + Squash in den Vorgänger + Squash Nachfolger Commits bis hier ÄNDERUNGEN Änderungen durchsuchen... DATEIEN @@ -148,6 +148,7 @@ Benutzername Benutzername für dieses Repository Kopieren + Kopiere gesamten Text COMMIT-NACHRICHT KOPIEREN Pfad kopieren Dateinamen kopieren @@ -246,6 +247,8 @@ "Ihre" verwenden (checkout --theirs) "Meine" verwenden (checkout --ours) Datei Historie + INHALT + ÄNDERUNGEN FILTER Git-Flow Development-Branch: @@ -334,7 +337,6 @@ Verwerfen Initialisiere Repository Pfad: - Ungültiges Repository erkannt. `git init` auf diesem Pfad ausführen? Cherry-Pick wird durchgeführt. Drücke 'Abbrechen' um den originalen HEAD wiederherzustellen. Merge request wird durchgeführt. Drücke 'Abbrechen' um den originalen HEAD wiederherzustellen. Rebase wird durchgeführt. Drücke 'Abbrechen' um den originalen HEAD wiederherzustellen. @@ -381,7 +383,7 @@ Standardschriftart Standardschriftgröße Monospace-Schriftart - Verwende nur die Monospace-Schriftart im Texteditor + Verwende die Monospace-Schriftart nur im Texteditor Design Design-Anpassungen Fixe Tab-Breite in Titelleiste @@ -531,7 +533,7 @@ Diese Version überspringen Software Update Es sind momentan kein Updates verfügbar. - Squash HEAD in Vorgänger + Squash Commits SSH privater Schlüssel: Pfad zum privaten SSH Schlüssel START @@ -553,9 +555,9 @@ COMMITTER MONAT WOCHE - JAHR + JAHR COMMITS: - COMMITTERS: + AUTOREN: SUBMODULE Submodul hinzufügen Relativen Pfad kopieren diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 67f42eb2..ab29d6eb 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -79,10 +79,9 @@ Discard Do Nothing Stash & Reapply - Cherry-Pick This Commit - Commit: + Cherry Pick + Commit(s): Commit all changes - Cherry Pick Clear Stashes You are trying to clear all stashes. Are you sure to continue? Clone Remote Repository @@ -95,6 +94,7 @@ CLOSE Editor Cherry-Pick This Commit + Cherry-Pick ... Checkout Commit Compare with HEAD Compare with Worktree @@ -124,6 +124,7 @@ PARENTS REFS SHA + Open in Browser Enter commit subject Description Repository Configure @@ -318,6 +319,7 @@ REPOSITORY Commit staged changes Commit and push staged changes + Discard selected changes Dashboard mode (Default) Force to reload this repository Stage/Unstage selected changes @@ -335,7 +337,6 @@ Discard Initialize Repository Path: - Invalid repository detected. Run `git init` under this path? Cherry-Pick in progress. Press 'Abort' to restore original HEAD. Merge request in progress. Press 'Abort' to restore original HEAD. Rebase in progress. Press 'Abort' to restore original HEAD. @@ -525,6 +526,8 @@ SAVE Save As... Patch has been saved successfully! + Scan Repositories + Root Dir: Check for Updates... New version of this software is available: Check for updates failed! @@ -555,9 +558,9 @@ COMMITTER MONTH WEEK - YEAR + YEAR COMMITS: - COMMITTERS: + AUTHORS: SUBMODULES Add Submodule Copy Relative Path @@ -588,6 +591,7 @@ Open All Repositories Open Repository Open Terminal + Rescan Repositories in Default Clone Dir Search Repositories... Sort Changes diff --git a/src/Resources/Locales/fr_FR.axaml b/src/Resources/Locales/fr_FR.axaml index ac7265c5..6209a100 100644 --- a/src/Resources/Locales/fr_FR.axaml +++ b/src/Resources/Locales/fr_FR.axaml @@ -83,10 +83,9 @@ Rejeter Ne rien faire Stash et Réappliquer - Cherry-Pick ce commit - Commit : + Cherry-Pick + Commit(s) : Commit tous les changements - Cherry Pick Vider les Stashes Voulez-vous vider tous les stashes. Êtes-vous sûr de vouloir continuer ? Cloner le dépôt distant @@ -337,7 +336,6 @@ Rejeter Initialize Repository Path: - Invalid repository detected. Run `git init` under this path? Cherry-Pick in progress. Press 'Abort' to restore original HEAD. Merge request in progress. Press 'Abort' to restore original HEAD. Rebase in progress. Press 'Abort' to restore original HEAD. @@ -556,9 +554,9 @@ COMMITTER MONTH WEEK - YEAR + YEAR COMMITS: - COMMITTERS: + AUTHORS: SUBMODULES Add Submodule Copy Relative Path diff --git a/src/Resources/Locales/pt_BR.axaml b/src/Resources/Locales/pt_BR.axaml index 7d6f8cee..1b3dc48f 100644 --- a/src/Resources/Locales/pt_BR.axaml +++ b/src/Resources/Locales/pt_BR.axaml @@ -82,10 +82,9 @@ Descartar Nada Stash & Reaplicar - Cherry-Pick Este Commit - Commit: + Cherry-Pick + Commit(s): Commitar todas as alterações - Cherry-Pick Limpar Stashes Você está tentando limpar todas as stashes. Tem certeza que deseja continuar? Clonar Repositório Remoto @@ -332,7 +331,6 @@ Descartar Inicializar Repositório Caminho: - Repositório inválido detectado. Executar `git init` neste caminho? Cherry-Pick em andamento. Pressione 'Abort' para restaurar o HEAD original. Merge em andamento. Pressione 'Abort' para restaurar o HEAD original. Rebase em andamento. Pressione 'Abort' para restaurar o HEAD original. @@ -549,9 +547,9 @@ COMMITTER MÊS SEMANA - ANO + ANO COMMITS: - COMMITTERS: + AUTORES: SUBMÓDULOS Adicionar Submódulo Copiar Caminho Relativo diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 2fcb2bde..3986e358 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -82,10 +82,9 @@ 丢弃更改 不做处理 贮藏并自动恢复 - 挑选(cherry-pick)此提交 - 提交ID : + 挑选提交 + 提交列表 : 提交变化 - 挑选提交 丢弃贮藏确认 您正在丢弃所有的贮藏,一经操作,无法回退,是否继续? 克隆远程仓库 @@ -98,6 +97,7 @@ 关闭 提交信息编辑器 挑选(cherry-pick)此提交 + 挑选(cherry-pick)... 检出此提交 与当前HEAD比较 与本地工作树比较 @@ -127,6 +127,7 @@ 父提交 相关引用 提交指纹 + 浏览器中查看 填写提交信息主题 详细描述 仓库配置 @@ -321,6 +322,7 @@ 仓库页面快捷键 提交暂存区更改 提交暂存区更改并推送 + 丢弃选中的更改 切换左边栏为分支/标签等显示模式(默认) 重新加载仓库状态 将选中的变更暂存或从暂存列表中移除 @@ -338,7 +340,6 @@ 丢弃 初始化新仓库 路径 : - 选择目录不是有效的Git仓库。是否需要在此目录执行`git init`操作? 挑选(Cherry-Pick)操作进行中。点击【终止】回滚到操作前的状态。 合并操作进行中。点击【终止】回滚到操作前的状态。 变基(Rebase)操作进行中。点击【终止】回滚到操作前的状态。 @@ -527,6 +528,8 @@ 保 存 另存为... 补丁已成功保存! + 扫描仓库 + 根路径 : 检测更新... 检测到软件有版本更新: 获取最新版本信息失败! @@ -557,9 +560,9 @@ 提交者 本月 本周 - 本年 + 最近一年 提交次数: - 提交者: + 贡献者人数: 子模块 添加子模块 复制路径 @@ -590,6 +593,7 @@ 打开所有包含仓库 打开本地仓库 打开终端 + 重新扫描默认克隆路径下的仓库 快速查找仓库... 排序 本地更改 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index aad55faf..8d7a4bf1 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -82,10 +82,9 @@ 捨棄變更 不做處理 擱置變更並自動復原 - 揀選 (cherry-pick) 此提交 - 提交編號: + 揀選提交 + 提交列表: 提交變更 - 揀選提交 捨棄擱置變更確認 您正在捨棄所有的擱置變更,一經操作便無法復原,是否繼續? 複製 (clone) 遠端存放庫 @@ -98,6 +97,7 @@ 關閉 提交訊息編輯器 揀選 (cherry-pick) 此提交 + 揀選 (cherry-pick)... 簽出 (checkout) 此提交 與目前 HEAD 比較 與本機工作區比較 @@ -127,6 +127,7 @@ 前次提交 相關參照 提交編號 + 在瀏覽器中檢視 填寫提交訊息標題 詳細描述 存放庫設定 @@ -137,7 +138,7 @@ 電子郵件地址 Git 設定 Issue 追蹤 - 新增符合 Github Issue 規則 + 新增符合 GitHub Issue 規則 新增符合 Jira 規則 新增自訂規則 符合 Issue 的正則表達式: @@ -165,7 +166,7 @@ 建立本機分支 新增標籤... 標籤位於: - 使用 GPG 簽名 + 使用 GPG 簽章 標籤描述: 選填。 標籤名稱: @@ -321,9 +322,10 @@ 存放庫頁面快速鍵 提交暫存區變更 提交暫存區變更並推送 + 捨棄選取的變更 切換左邊欄為分支/標籤等顯示模式 (預設) 強制重新載入存放庫 - 暫存選取的變更或從暫存列表中移除 + 暫存或取消暫存選取的變更 切換左邊欄為歷史搜尋模式 顯示本機變更 顯示歷史記錄 @@ -338,7 +340,6 @@ 捨棄 初始化存放庫 路徑: - 選擇目錄並非有效的 Git 存放庫。是否要在此目錄執行 `git init` 以進行初始化? 揀選 (cherry-pick) 操作進行中。點選 [中止] 復原到操作前的狀態。 合併操作進行中。點選 [中止] 復原到操作前的狀態。 重定基底 (rebase) 操作進行中。點選 [中止] 復原到操作前的狀態。 @@ -410,14 +411,14 @@ 預設 Git 使用者名稱 Git 版本 本軟體要求 Git 最低版本為 2.23.0 - GPG 簽名 - 啟用提交簽名 - 啟用標籤簽名 - GPG 簽名格式 + GPG 簽章 + 啟用提交簽章 + 啟用標籤簽章 + GPG 簽章格式 可執行檔案路徑 填寫 gpg.exe 所在路徑 - 使用者簽名金鑰 - 填寫簽名提交所使用的金鑰 + 使用者簽章金鑰 + 填寫簽章提交所使用的金鑰 對比/合併工具 安裝路徑 填寫可執行檔案所在路徑 @@ -528,9 +529,11 @@ 儲存 另存新檔... 修補檔已成功儲存! + 掃描存放庫 + 頂層目錄: 檢查更新... 軟體有版本更新: - 獲取最新版本資訊失敗! + 取得最新版本資訊失敗! 下載 忽略此版本 軟體更新 @@ -544,7 +547,7 @@ 包含未追蹤的檔案 擱置變更訊息: 選填,用於命名此擱置變更 - 擱置變更本機變更 + 擱置本機變更 套用 (apply) 刪除 (drop) 套用並刪除 (pop) @@ -558,9 +561,9 @@ 提交者 本月 本週 - 本年 + 最近一年 提交次數: - 提交者: + 貢獻者人數: 子模組 新增子模組 複製路徑 @@ -592,6 +595,7 @@ 開啟本機存放庫 開啟終端機 快速搜尋存放庫... + 重新掃描預設複製 (clone) 目錄下的存放庫 排序 本機變更 加入至 .gitignore 忽略清單 diff --git a/src/Resources/Styles.axaml b/src/Resources/Styles.axaml index 497e9f49..91563527 100644 --- a/src/Resources/Styles.axaml +++ b/src/Resources/Styles.axaml @@ -191,6 +191,38 @@ - - - - - - - - - - - - - - + - - - - - - - - - + + + + + + + + + + + + + + + + + + + FontWeight="{Binding NameFontWeight}" + TextTrimming="CharacterEllipsis"/> + x:Class="SourceGit.Views.CaptionButtons" + x:Name="ThisControl"> - - - - diff --git a/src/Views/CaptionButtonsMacOS.axaml.cs b/src/Views/CaptionButtonsMacOS.axaml.cs index a520ad9e..ec1f2b2c 100644 --- a/src/Views/CaptionButtonsMacOS.axaml.cs +++ b/src/Views/CaptionButtonsMacOS.axaml.cs @@ -1,3 +1,4 @@ +using Avalonia; using Avalonia.Controls; using Avalonia.Interactivity; using Avalonia.VisualTree; @@ -6,6 +7,15 @@ namespace SourceGit.Views { public partial class CaptionButtonsMacOS : UserControl { + public static readonly StyledProperty IsCloseButtonOnlyProperty = + AvaloniaProperty.Register(nameof(IsCloseButtonOnly)); + + public bool IsCloseButtonOnly + { + get => GetValue(IsCloseButtonOnlyProperty); + set => SetValue(IsCloseButtonOnlyProperty, value); + } + public CaptionButtonsMacOS() { InitializeComponent(); @@ -32,9 +42,7 @@ namespace SourceGit.Views private void CloseWindow(object _, RoutedEventArgs e) { var window = this.FindAncestorOfType(); - if (window != null) - window.Close(); - + window?.Close(); e.Handled = true; } } diff --git a/src/Views/CherryPick.axaml b/src/Views/CherryPick.axaml index e3634bd2..ba672b13 100644 --- a/src/Views/CherryPick.axaml +++ b/src/Views/CherryPick.axaml @@ -2,6 +2,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:m="using:SourceGit.Models" xmlns:vm="using:SourceGit.ViewModels" xmlns:c="using:SourceGit.Converters" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" @@ -11,17 +12,51 @@ - + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + - + - - + + @@ -114,6 +117,7 @@ Classes="primary" Message="{Binding #ThisControl.Message}" IssueTrackerRules="{Binding #ThisControl.IssueTrackerRules}" + HorizontalAlignment="Stretch" TextWrapping="Wrap"/> diff --git a/src/Views/CommitBaseInfo.axaml.cs b/src/Views/CommitBaseInfo.axaml.cs index 9806860d..ad137147 100644 --- a/src/Views/CommitBaseInfo.axaml.cs +++ b/src/Views/CommitBaseInfo.axaml.cs @@ -49,6 +49,14 @@ namespace SourceGit.Views InitializeComponent(); } + private void OnCopyCommitSHA(object sender, RoutedEventArgs e) + { + if (sender is Button { DataContext: Models.Commit commit }) + App.CopyText(commit.SHA); + + e.Handled = true; + } + private void OnOpenWebLink(object sender, RoutedEventArgs e) { if (DataContext is ViewModels.CommitDetail detail) diff --git a/src/Views/CommitMessagePresenter.cs b/src/Views/CommitMessagePresenter.cs index bf19ecc7..55e1dfb1 100644 --- a/src/Views/CommitMessagePresenter.cs +++ b/src/Views/CommitMessagePresenter.cs @@ -89,21 +89,24 @@ namespace SourceGit.Views matches.Sort((l, r) => l.Start - r.Start); _matches = matches; - int pos = 0; + var inlines = new List(); + var pos = 0; foreach (var match in matches) { if (match.Start > pos) - Inlines.Add(new Run(message.Substring(pos, match.Start - pos))); + inlines.Add(new Run(message.Substring(pos, match.Start - pos))); var link = new Run(message.Substring(match.Start, match.Length)); link.Classes.Add(match.IsCommitSHA ? "commit_link" : "issue_link"); - Inlines.Add(link); + inlines.Add(link); pos = match.Start + match.Length; } if (pos < message.Length) - Inlines.Add(new Run(message.Substring(pos))); + inlines.Add(new Run(message.Substring(pos))); + + Inlines.AddRange(inlines); } } @@ -111,7 +114,23 @@ namespace SourceGit.Views { base.OnPointerMoved(e); - if (e.Pointer.Captured == null && _matches != null) + if (e.Pointer.Captured == this) + { + var relativeSelfY = e.GetPosition(this).Y; + if (relativeSelfY <= 0 || relativeSelfY > Bounds.Height) + return; + + var scrollViewer = this.FindAncestorOfType(); + if (scrollViewer != null) + { + var relativeY = e.GetPosition(scrollViewer).Y; + if (relativeY <= 8) + scrollViewer.LineUp(); + else if (relativeY >= scrollViewer.Bounds.Height - 8) + scrollViewer.LineDown(); + } + } + else if (_matches != null) { var point = e.GetPosition(this) - new Point(Padding.Left, Padding.Top); var x = Math.Min(Math.Max(point.X, 0), Math.Max(TextLayout.WidthIncludingTrailingWhitespace, 0)); diff --git a/src/Views/CommitRefsPresenter.cs b/src/Views/CommitRefsPresenter.cs index 92be9b98..ecb2c982 100644 --- a/src/Views/CommitRefsPresenter.cs +++ b/src/Views/CommitRefsPresenter.cs @@ -172,11 +172,8 @@ namespace SourceGit.Views switch (decorator.Type) { case Models.DecoratorType.CurrentBranchHead: - item.LabelBG = headBG; - geo = this.FindResource("Icons.Check") as StreamGeometry; - break; case Models.DecoratorType.CurrentCommitHead: - item.LabelBG = branchBG; + item.LabelBG = headBG; geo = this.FindResource("Icons.Check") as StreamGeometry; break; case Models.DecoratorType.RemoteBranchHead: diff --git a/src/Views/ConfirmRestart.axaml b/src/Views/ConfirmRestart.axaml index 4e0ac386..9aafda7e 100644 --- a/src/Views/ConfirmRestart.axaml +++ b/src/Views/ConfirmRestart.axaml @@ -25,14 +25,10 @@ Data="{StaticResource Icons.Info}" IsVisible="{OnPlatform True, macOS=False}"/> - - - + - + diff --git a/src/Views/DeleteMultipleBranches.axaml b/src/Views/DeleteMultipleBranches.axaml index 748616ba..2a888118 100644 --- a/src/Views/DeleteMultipleBranches.axaml +++ b/src/Views/DeleteMultipleBranches.axaml @@ -2,6 +2,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:m="using:SourceGit.Models" xmlns:vm="using:SourceGit.ViewModels" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="SourceGit.Views.DeleteMultipleBranches" @@ -17,56 +18,37 @@ BorderBrush="{DynamicResource Brush.Border1}" CornerRadius="4" Padding="4"> - - - + - + + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + - - - - - - - - - - - - - + + + + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + x:DataType="vm:Histories" + x:Name="ThisControl"> - - - - - - - - - - - - - - - - + + + + + + + + + + + - + + + + + + + + + + + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + diff --git a/src/Views/Histories.axaml.cs b/src/Views/Histories.axaml.cs index a0828459..46c73300 100644 --- a/src/Views/Histories.axaml.cs +++ b/src/Views/Histories.axaml.cs @@ -6,7 +6,6 @@ using Avalonia; using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Controls.Documents; -using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Media; @@ -212,23 +211,24 @@ namespace SourceGit.Views matches.Sort((l, r) => l.Start - r.Start); _matches = matches; - int pos = 0; + var inlines = new List(); + var pos = 0; foreach (var match in matches) { if (match.Start > pos) - Inlines.Add(new Run(subject.Substring(pos, match.Start - pos))); + inlines.Add(new Run(subject.Substring(pos, match.Start - pos))); var link = new Run(subject.Substring(match.Start, match.Length)); link.Classes.Add("issue_link"); - Inlines.Add(link); + inlines.Add(link); pos = match.Start + match.Length; } if (pos < subject.Length) - Inlines.Add(new Run(subject.Substring(pos))); + inlines.Add(new Run(subject.Substring(pos))); - InvalidateTextLayout(); + Inlines.AddRange(inlines); } } @@ -438,45 +438,32 @@ namespace SourceGit.Views { base.Render(context); - var grid = this.FindAncestorOfType()?.CommitDataGrid; - if (grid == null) - return; - var graph = Graph; if (graph == null) return; - var rowsPresenter = grid.FindDescendantOfType(); - if (rowsPresenter == null) + var histories = this.FindAncestorOfType(); + if (histories == null) return; - // Find the content display offset Y of binding DataGrid. - double rowHeight = grid.RowHeight; - double startY = 0; - foreach (var child in rowsPresenter.Children) + var list = histories.CommitListContainer; + if (list == null) + return; + + // Calculate drawing area. + double width = Bounds.Width - 156 - 96 - histories.AuthorNameColumnWidth.Value; + double height = Bounds.Height; + double startY = list.Scroll?.Offset.Y ?? 0; + double endY = startY + height + 28; + + // Apply scroll offset and clip. + using (context.PushClip(new Rect(0, 0, width, height))) + using (context.PushTransform(Matrix.CreateTranslation(0, -startY))) { - if (child is DataGridRow { IsVisible: true, Bounds.Top: <= 0 } row && row.Bounds.Top > -rowHeight) - { - var test = rowHeight * row.GetIndex() - row.Bounds.Top; - if (startY < test) - startY = test; - } + // Draw contents + DrawCurves(context, graph, startY, endY); + DrawAnchors(context, graph, startY, endY); } - - var headerHeight = grid.ColumnHeaderHeight; - startY -= headerHeight; - - // Apply scroll offset. - context.PushClip(new Rect(Bounds.Left, Bounds.Top + headerHeight, grid.Columns[0].ActualWidth, Bounds.Height)); - context.PushTransform(Matrix.CreateTranslation(0, -startY)); - - // Calculate bounds. - var top = startY; - var bottom = startY + grid.Bounds.Height + rowHeight * 2; - - // Draw contents - DrawCurves(context, graph, top, bottom); - DrawAnchors(context, graph, top, bottom); } private void DrawCurves(DrawingContext context, Models.CommitGraph graph, double top, double bottom) @@ -601,6 +588,15 @@ namespace SourceGit.Views public partial class Histories : UserControl { + public static readonly StyledProperty AuthorNameColumnWidthProperty = + AvaloniaProperty.Register(nameof(AuthorNameColumnWidth), new GridLength(120)); + + public GridLength AuthorNameColumnWidth + { + get => GetValue(AuthorNameColumnWidthProperty); + set => SetValue(AuthorNameColumnWidthProperty, value); + } + public static readonly StyledProperty CurrentBranchProperty = AvaloniaProperty.Register(nameof(CurrentBranch)); @@ -636,9 +632,14 @@ namespace SourceGit.Views return; // Force scroll selected item (current head) into view. see issue #58 - var datagrid = h.CommitDataGrid; - if (datagrid != null && datagrid.SelectedItems.Count == 1) - datagrid.ScrollIntoView(datagrid.SelectedItems[0], null); + var list = h.CommitListContainer; + if (list != null && list.SelectedItems.Count == 1) + list.ScrollIntoView(list.SelectedIndex); + }); + + AuthorNameColumnWidthProperty.Changed.AddClassHandler((h, _) => + { + h.CommitGraph.InvalidateVisual(); }); } @@ -647,43 +648,47 @@ namespace SourceGit.Views InitializeComponent(); } - private void OnCommitDataGridLayoutUpdated(object _1, EventArgs _2) + private void OnCommitListLayoutUpdated(object _1, EventArgs _2) { - CommitGraph.InvalidateVisual(); + var y = CommitListContainer.Scroll?.Offset.Y ?? 0; + if (y != _lastScrollY) + { + _lastScrollY = y; + CommitGraph.InvalidateVisual(); + } } - private void OnCommitDataGridSelectionChanged(object _, SelectionChangedEventArgs e) + private void OnCommitListSelectionChanged(object _, SelectionChangedEventArgs e) { if (DataContext is ViewModels.Histories histories) { - histories.Select(CommitDataGrid.SelectedItems); + histories.Select(CommitListContainer.SelectedItems); } e.Handled = true; } - private void OnCommitDataGridContextRequested(object sender, ContextRequestedEventArgs e) + private void OnCommitListContextRequested(object sender, ContextRequestedEventArgs e) { - if (DataContext is ViewModels.Histories histories && sender is DataGrid datagrid) + if (DataContext is ViewModels.Histories histories && sender is ListBox { SelectedItems: { Count: > 0 } } list) { - var menu = histories.MakeContextMenu(datagrid); - datagrid.OpenContextMenu(menu); + var menu = histories.MakeContextMenu(list); + list.OpenContextMenu(menu); } e.Handled = true; } - private void OnCommitDataGridDoubleTapped(object sender, TappedEventArgs e) + private void OnCommitListDoubleTapped(object sender, TappedEventArgs e) { - if (DataContext is ViewModels.Histories histories && sender is DataGrid datagrid && datagrid.SelectedItems is { Count: 1 } selectedItems) + if (DataContext is ViewModels.Histories histories && sender is ListBox { SelectedItems: { Count: 1 } selected }) { - histories.DoubleTapped(selectedItems[0] as Models.Commit); + histories.DoubleTapped(selected[0] as Models.Commit); } e.Handled = true; } - private void OnCommitDataGridKeyDown(object sender, KeyEventArgs e) + private void OnCommitListKeyDown(object sender, KeyEventArgs e) { - if (sender is DataGrid grid && - grid.SelectedItems is { Count: > 0 } selected && + if (sender is ListBox { SelectedItems: { Count: > 0 } selected } && e.Key == Key.C && e.KeyModifiers.HasFlag(KeyModifiers.Control)) { @@ -698,5 +703,7 @@ namespace SourceGit.Views e.Handled = true; } } + + private double _lastScrollY = 0; } } diff --git a/src/Views/Hotkeys.axaml b/src/Views/Hotkeys.axaml index 1b2c2a33..cd7f9c4a 100644 --- a/src/Views/Hotkeys.axaml +++ b/src/Views/Hotkeys.axaml @@ -27,14 +27,10 @@ Data="{StaticResource Icons.Hotkeys}" IsVisible="{OnPlatform True, macOS=False}"/> - - - + - + @@ -85,7 +78,7 @@ FontSize="{Binding Source={x:Static vm:Preference.Instance}, Path=DefaultFontSize, Converter={x:Static c:DoubleConverters.Increase}}" Margin="0,8"/> - + @@ -101,17 +94,20 @@ - + + + + - - + + - - + + - - + + - + + + + + + + Text="{DynamicResource Text.Init.Path}" + HorizontalAlignment="Right" VerticalAlignment="Center" + Margin="0,0,8,0"/> + Text="{Binding TargetPath}"/> + Foreground="{DynamicResource Brush.FG2}" + Text="{Binding Reason}" + TextWrapping="Wrap"/> diff --git a/src/Views/InteractiveRebase.axaml b/src/Views/InteractiveRebase.axaml index 706b6cf0..a8f9d360 100644 --- a/src/Views/InteractiveRebase.axaml +++ b/src/Views/InteractiveRebase.axaml @@ -28,14 +28,10 @@ Data="{StaticResource Icons.InteractiveRebase}" IsVisible="{OnPlatform True, macOS=False}"/> - - - + - + @@ -66,203 +59,172 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/InteractiveRebase.axaml.cs b/src/Views/InteractiveRebase.axaml.cs index 0660f7c9..14375217 100644 --- a/src/Views/InteractiveRebase.axaml.cs +++ b/src/Views/InteractiveRebase.axaml.cs @@ -43,6 +43,7 @@ namespace SourceGit.Views private void OnRowHeaderDragOver(object sender, DragEventArgs e) { if (DataContext is ViewModels.InteractiveRebase vm && + e.Data.Contains("InteractiveRebaseItem") && e.Data.Get("InteractiveRebaseItem") is ViewModels.InteractiveRebaseItem src && sender is Border { DataContext: ViewModels.InteractiveRebaseItem dst } border && src != dst) @@ -88,9 +89,9 @@ namespace SourceGit.Views } } - private void OnDataGridKeyDown(object sender, KeyEventArgs e) + private void OnItemsListBoxKeyDown(object sender, KeyEventArgs e) { - var item = (sender as DataGrid)?.SelectedItem as ViewModels.InteractiveRebaseItem; + var item = (sender as ListBox)?.SelectedItem as ViewModels.InteractiveRebaseItem; if (item == null) return; diff --git a/src/Views/LFSLocks.axaml b/src/Views/LFSLocks.axaml index 5d51e25e..53392043 100644 --- a/src/Views/LFSLocks.axaml +++ b/src/Views/LFSLocks.axaml @@ -2,6 +2,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:m="using:SourceGit.Models" xmlns:vm="using:SourceGit.ViewModels" xmlns:v="using:SourceGit.Views" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" @@ -26,14 +27,10 @@ Data="{StaticResource Icons.Lock}" IsVisible="{OnPlatform True, macOS=False}"/> - - - + - + - - - - - - - - - - - - + + + + - - - - - - - + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - + - + diff --git a/src/Views/Preference.axaml.cs b/src/Views/Preference.axaml.cs index 28bdeeaa..2621bcaf 100644 --- a/src/Views/Preference.axaml.cs +++ b/src/Views/Preference.axaml.cs @@ -125,12 +125,7 @@ namespace SourceGit.Views } } - private void BeginMoveWindow(object _, PointerPressedEventArgs e) - { - BeginMoveDrag(e); - } - - private void CloseWindow(object _1, RoutedEventArgs _2) + protected override void OnClosing(WindowClosingEventArgs e) { var config = new Commands.Config(null).ListAll(); SetIfChanged(config, "user.name", DefaultUser); @@ -143,8 +138,13 @@ namespace SourceGit.Views if (!GPGFormat.Value.Equals("ssh", StringComparison.Ordinal)) SetIfChanged(config, $"gpg.{GPGFormat.Value}.program", GPGExecutableFile); + + base.OnClosing(e); + } - Close(); + private void BeginMoveWindow(object _, PointerPressedEventArgs e) + { + BeginMoveDrag(e); } private async void SelectThemeOverrideFile(object _, RoutedEventArgs e) @@ -186,10 +186,17 @@ namespace SourceGit.Views private async void SelectDefaultCloneDir(object _1, RoutedEventArgs _2) { var options = new FolderPickerOpenOptions() { AllowMultiple = false }; - var selected = await StorageProvider.OpenFolderPickerAsync(options); - if (selected.Count == 1) + try { - ViewModels.Preference.Instance.GitDefaultCloneDir = selected[0].Path.LocalPath; + var selected = await StorageProvider.OpenFolderPickerAsync(options); + if (selected.Count == 1) + { + ViewModels.Preference.Instance.GitDefaultCloneDir = selected[0].Path.LocalPath; + } + } + catch (Exception e) + { + App.RaiseException(string.Empty, $"Failed to select default clone directory: {e.Message}"); } } diff --git a/src/Views/Repository.axaml b/src/Views/Repository.axaml index 15819142..7a8bd2a1 100644 --- a/src/Views/Repository.axaml +++ b/src/Views/Repository.axaml @@ -257,7 +257,7 @@ SelectionMode="Single" ContextRequested="OnSubmoduleContextRequested" DoubleTapped="OnDoubleTappedSubmodule" - PropertyChanged="OnLeftSidebarDataGridPropertyChanged" + PropertyChanged="OnLeftSidebarListBoxPropertyChanged" IsVisible="{Binding IsSubmoduleGroupExpanded, Mode=OneWay}"> + + + - - - - - - - - - - - - - + + + + + - - - - - - - - + + + + + + + + + + + + + + + + + diff --git a/src/Views/Repository.axaml.cs b/src/Views/Repository.axaml.cs index eb3158cd..ff13b8c8 100644 --- a/src/Views/Repository.axaml.cs +++ b/src/Views/Repository.axaml.cs @@ -154,16 +154,6 @@ namespace SourceGit.Views } } - private void OnSearchResultDataGridSelectionChanged(object sender, SelectionChangedEventArgs e) - { - if (sender is DataGrid { SelectedItem: Models.Commit commit } && DataContext is ViewModels.Repository repo) - { - repo.NavigateToCommit(commit.SHA); - } - - e.Handled = true; - } - private void OnBranchTreeRowsChanged(object _, RoutedEventArgs e) { UpdateLeftSidebarLayout(); @@ -236,7 +226,7 @@ namespace SourceGit.Views e.Handled = true; } - private void OnLeftSidebarDataGridPropertyChanged(object _, AvaloniaPropertyChangedEventArgs e) + private void OnLeftSidebarListBoxPropertyChanged(object _, AvaloniaPropertyChangedEventArgs e) { if (e.Property == ListBox.ItemsSourceProperty || e.Property == ListBox.IsVisibleProperty) UpdateLeftSidebarLayout(); diff --git a/src/Views/RepositoryConfigure.axaml b/src/Views/RepositoryConfigure.axaml index f5d16fd4..44ffe4b6 100644 --- a/src/Views/RepositoryConfigure.axaml +++ b/src/Views/RepositoryConfigure.axaml @@ -27,15 +27,11 @@ Data="{StaticResource Icons.Settings}" Margin="10,0,0,0" IsVisible="{OnPlatform True, macOS=False}"/> - - - - + + - + diff --git a/src/Views/RepositoryConfigure.axaml.cs b/src/Views/RepositoryConfigure.axaml.cs index b309453d..7e559cc2 100644 --- a/src/Views/RepositoryConfigure.axaml.cs +++ b/src/Views/RepositoryConfigure.axaml.cs @@ -1,5 +1,5 @@ +using Avalonia.Controls; using Avalonia.Input; -using Avalonia.Interactivity; namespace SourceGit.Views { @@ -10,15 +10,15 @@ namespace SourceGit.Views InitializeComponent(); } + protected override void OnClosing(WindowClosingEventArgs e) + { + (DataContext as ViewModels.RepositoryConfigure)?.Save(); + base.OnClosing(e); + } + private void BeginMoveWindow(object _, PointerPressedEventArgs e) { BeginMoveDrag(e); } - - private void CloseWindow(object _1, RoutedEventArgs _2) - { - (DataContext as ViewModels.RepositoryConfigure)?.Save(); - Close(); - } } } diff --git a/src/Views/RevisionFileContentViewer.axaml b/src/Views/RevisionFileContentViewer.axaml index eef7605c..67ba5913 100644 --- a/src/Views/RevisionFileContentViewer.axaml +++ b/src/Views/RevisionFileContentViewer.axaml @@ -48,10 +48,12 @@ - - - - + + + + + + diff --git a/src/Views/ScanRepositories.axaml b/src/Views/ScanRepositories.axaml new file mode 100644 index 00000000..90274700 --- /dev/null +++ b/src/Views/ScanRepositories.axaml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + diff --git a/src/Views/ScanRepositories.axaml.cs b/src/Views/ScanRepositories.axaml.cs new file mode 100644 index 00000000..4848112a --- /dev/null +++ b/src/Views/ScanRepositories.axaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace SourceGit.Views +{ + public partial class ScanRepositories : UserControl + { + public ScanRepositories() + { + InitializeComponent(); + } + } +} diff --git a/src/Views/SelfUpdate.axaml b/src/Views/SelfUpdate.axaml index c768a891..0015a957 100644 --- a/src/Views/SelfUpdate.axaml +++ b/src/Views/SelfUpdate.axaml @@ -29,14 +29,10 @@ Data="{StaticResource Icons.SoftwareUpdate}" IsVisible="{OnPlatform True, macOS=False}"/> - - - + - + diff --git a/src/Views/StandaloneCommitMessageEditor.axaml b/src/Views/StandaloneCommitMessageEditor.axaml index d6b961d5..f59d3e84 100644 --- a/src/Views/StandaloneCommitMessageEditor.axaml +++ b/src/Views/StandaloneCommitMessageEditor.axaml @@ -26,14 +26,10 @@ Data="{StaticResource Icons.Edit}" IsVisible="{OnPlatform True, macOS=False}"/> - - - + - + diff --git a/src/Views/StandaloneCommitMessageEditor.axaml.cs b/src/Views/StandaloneCommitMessageEditor.axaml.cs index dd4c0fcf..08b0d6f4 100644 --- a/src/Views/StandaloneCommitMessageEditor.axaml.cs +++ b/src/Views/StandaloneCommitMessageEditor.axaml.cs @@ -1,3 +1,4 @@ +using System; using System.IO; using Avalonia.Input; @@ -33,22 +34,25 @@ namespace SourceGit.Views } } + protected override void OnClosed(EventArgs e) + { + base.OnClosed(e); + App.Quit(_exitCode); + } + private void BeginMoveWindow(object _, PointerPressedEventArgs e) { BeginMoveDrag(e); } - private void CloseWindow(object _1, RoutedEventArgs _2) - { - App.Quit(-1); - } - private void SaveAndClose(object _1, RoutedEventArgs _2) { File.WriteAllText(_file, Editor.Text); - App.Quit(0); + _exitCode = 0; + Close(); } private readonly string _file; + private int _exitCode = -1; } } diff --git a/src/Views/StashesPage.axaml b/src/Views/StashesPage.axaml index 2d859941..d0d257c1 100644 --- a/src/Views/StashesPage.axaml +++ b/src/Views/StashesPage.axaml @@ -66,39 +66,42 @@ - - - - - - - - - - - + + + + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + @@ -110,38 +113,36 @@ - - - - - - - - - + + + + - - - - - - - - - + + + + + + + + + + + + + + + @@ -27,14 +27,10 @@ Data="{StaticResource Icons.Statistics}" IsVisible="{OnPlatform True, macOS=False}"/> - - - + - + @@ -110,7 +103,7 @@ - + @@ -134,53 +127,50 @@ - - - - - + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - + + @@ -193,7 +183,7 @@ - + + + + + diff --git a/src/Views/WelcomeToolbar.axaml.cs b/src/Views/WelcomeToolbar.axaml.cs index 6c5e21d0..fc36e050 100644 --- a/src/Views/WelcomeToolbar.axaml.cs +++ b/src/Views/WelcomeToolbar.axaml.cs @@ -1,3 +1,4 @@ +using System; using System.IO; using Avalonia.Controls; @@ -30,9 +31,16 @@ namespace SourceGit.Views options.SuggestedStartLocation = folder; } - var selected = await topLevel.StorageProvider.OpenFolderPickerAsync(options); - if (selected.Count == 1) - OpenOrInitRepository(selected[0].Path.LocalPath); + try + { + var selected = await topLevel.StorageProvider.OpenFolderPickerAsync(options); + if (selected.Count == 1) + OpenOrInitRepository(selected[0].Path.LocalPath); + } + catch (Exception exception) + { + App.RaiseException(string.Empty, $"Failed to open repository: {exception.Message}"); + } e.Handled = true; } @@ -47,14 +55,14 @@ namespace SourceGit.Views return; } - var root = new Commands.QueryRepositoryRootPath(path).Result(); - if (string.IsNullOrEmpty(root)) + var test = new Commands.QueryRepositoryRootPath(path).ReadToEnd(); + if (!test.IsSuccess || string.IsNullOrEmpty(test.StdOut)) { - ViewModels.Welcome.Instance.InitRepository(path, parent); + ViewModels.Welcome.Instance.InitRepository(path, parent, test.StdErr); return; } - var normalizedPath = root.Replace("\\", "/"); + var normalizedPath = test.StdOut.Trim().Replace("\\", "/"); var node = ViewModels.Preference.Instance.FindOrAddNodeByRepositoryPath(normalizedPath, parent, false); ViewModels.Welcome.Instance.Refresh(); diff --git a/src/Views/WorkingCopy.axaml b/src/Views/WorkingCopy.axaml index b8cab0e8..7997e64a 100644 --- a/src/Views/WorkingCopy.axaml +++ b/src/Views/WorkingCopy.axaml @@ -46,7 +46,7 @@ - + @@ -85,7 +85,7 @@ - + diff --git a/src/Views/WorkingCopy.axaml.cs b/src/Views/WorkingCopy.axaml.cs index 9cd6aa27..58a5b536 100644 --- a/src/Views/WorkingCopy.axaml.cs +++ b/src/Views/WorkingCopy.axaml.cs @@ -62,16 +62,27 @@ namespace SourceGit.Views private void OnUnstagedKeyDown(object _, KeyEventArgs e) { - if (DataContext is ViewModels.WorkingCopy vm && e.Key == Key.Space) + if (DataContext is ViewModels.WorkingCopy vm) { - vm.StageSelected(); - e.Handled = true; + if (e.Key is Key.Space or Key.Enter) + { + vm.StageSelected(); + e.Handled = true; + return; + } + + if (e.Key is Key.Delete or Key.Back && vm.SelectedUnstaged is { Count: > 0 } selected) + { + vm.Discard(selected); + e.Handled = true; + return; + } } } private void OnStagedKeyDown(object _, KeyEventArgs e) { - if (DataContext is ViewModels.WorkingCopy vm && e.Key == Key.Space) + if (DataContext is ViewModels.WorkingCopy vm && e.Key is Key.Space or Key.Enter) { vm.UnstageSelected(); e.Handled = true;