From 750ca8ec61ad7a4600f9622caa178d92223e4d88 Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 21 Apr 2025 14:44:02 +0800 Subject: [PATCH 001/392] refactor: use custom view locator to create new window/dialog (#1216) Signed-off-by: leo (cherry picked from commit 3e6f2b25f15b263e2b84922abc5cf6d621d62a83) --- src/App.Commands.cs | 6 +- src/App.axaml.cs | 38 +++++++++--- src/ViewModels/AIAssistant.cs | 80 +++++++++++++++++++++++++ src/ViewModels/CommitDetail.cs | 12 ++-- src/ViewModels/Histories.cs | 6 +- src/ViewModels/Launcher.cs | 2 +- src/ViewModels/Repository.cs | 16 ++--- src/ViewModels/WorkingCopy.cs | 41 +++---------- src/Views/AIAssistant.axaml | 13 ++-- src/Views/AIAssistant.axaml.cs | 72 ++-------------------- src/Views/CommitMessageTextBox.axaml.cs | 7 +-- src/Views/Launcher.axaml.cs | 2 +- src/Views/LauncherPage.axaml.cs | 2 +- src/Views/Preferences.axaml.cs | 4 +- src/Views/RepositoryToolbar.axaml.cs | 21 +++---- 15 files changed, 158 insertions(+), 164 deletions(-) create mode 100644 src/ViewModels/AIAssistant.cs diff --git a/src/App.Commands.cs b/src/App.Commands.cs index 85a75829..22e9fb51 100644 --- a/src/App.Commands.cs +++ b/src/App.Commands.cs @@ -37,10 +37,10 @@ namespace SourceGit } } - public static readonly Command OpenPreferencesCommand = new Command(_ => OpenDialog(new Views.Preferences())); - public static readonly Command OpenHotkeysCommand = new Command(_ => OpenDialog(new Views.Hotkeys())); + public static readonly Command OpenPreferencesCommand = new Command(_ => ShowWindow(new Views.Preferences(), false)); + public static readonly Command OpenHotkeysCommand = new Command(_ => ShowWindow(new Views.Hotkeys(), false)); public static readonly Command OpenAppDataDirCommand = new Command(_ => Native.OS.OpenInFileManager(Native.OS.DataDir)); - public static readonly Command OpenAboutCommand = new Command(_ => OpenDialog(new Views.About())); + public static readonly Command OpenAboutCommand = new Command(_ => ShowWindow(new Views.About(), false)); public static readonly Command CheckForUpdateCommand = new Command(_ => (Current as App)?.Check4Update(true)); public static readonly Command QuitCommand = new Command(_ => Quit(0)); public static readonly Command CopyTextBlockCommand = new Command(p => diff --git a/src/App.axaml.cs b/src/App.axaml.cs index c659388a..de6e94ff 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -105,10 +105,36 @@ namespace SourceGit #endregion #region Utility Functions - public static void OpenDialog(Window window) + public static void ShowWindow(object data, bool showAsDialog) { - if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner }) - window.ShowDialog(owner); + if (data is Views.ChromelessWindow window) + { + if (showAsDialog && Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner }) + window.ShowDialog(owner); + else + window.Show(); + + return; + } + + var dataTypeName = data.GetType().FullName; + if (string.IsNullOrEmpty(dataTypeName) || !dataTypeName.Contains(".ViewModels.", StringComparison.Ordinal)) + return; + + var viewTypeName = dataTypeName.Replace(".ViewModels.", ".Views."); + var viewType = Type.GetType(viewTypeName); + if (viewType == null || !viewType.IsSubclassOf(typeof(Views.ChromelessWindow))) + return; + + window = Activator.CreateInstance(viewType) as Views.ChromelessWindow; + if (window != null) + { + window.DataContext = data; + if (showAsDialog && Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner }) + window.ShowDialog(owner); + else + window.Show(); + } } public static void RaiseException(string context, string message) @@ -598,11 +624,7 @@ namespace SourceGit { Dispatcher.UIThread.Post(() => { - if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner }) - { - var dialog = new Views.SelfUpdate() { DataContext = new ViewModels.SelfUpdate() { Data = data } }; - dialog.ShowDialog(owner); - } + ShowWindow(new ViewModels.SelfUpdate() { Data = data }, true); }); } diff --git a/src/ViewModels/AIAssistant.cs b/src/ViewModels/AIAssistant.cs new file mode 100644 index 00000000..8756a30b --- /dev/null +++ b/src/ViewModels/AIAssistant.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +using Avalonia.Threading; + +using CommunityToolkit.Mvvm.ComponentModel; + +namespace SourceGit.ViewModels +{ + public class AIAssistant : ObservableObject + { + public bool IsGenerating + { + get => _isGenerating; + private set => SetProperty(ref _isGenerating, value); + } + + public string Text + { + get => _text; + private set => SetProperty(ref _text, value); + } + + public AIAssistant(Repository repo, Models.OpenAIService service, List changes, Action onApply) + { + _repo = repo; + _service = service; + _changes = changes; + _onApply = onApply; + _cancel = new CancellationTokenSource(); + + Gen(); + } + + public void Regen() + { + if (_cancel is { IsCancellationRequested: false }) + _cancel.Cancel(); + + Gen(); + } + + public void Apply() + { + _onApply?.Invoke(Text); + } + + public void Cancel() + { + _cancel?.Cancel(); + } + + private void Gen() + { + Text = string.Empty; + IsGenerating = true; + + _cancel = new CancellationTokenSource(); + Task.Run(() => + { + new Commands.GenerateCommitMessage(_service, _repo.FullPath, _changes, _cancel.Token, message => + { + Dispatcher.UIThread.Invoke(() => Text = message); + }).Exec(); + + Dispatcher.UIThread.Invoke(() => IsGenerating = false); + }, _cancel.Token); + } + + private readonly Repository _repo = null; + private Models.OpenAIService _service = null; + private List _changes = null; + private Action _onApply = null; + private CancellationTokenSource _cancel = null; + private bool _isGenerating = false; + private string _text = string.Empty; + } +} diff --git a/src/ViewModels/CommitDetail.cs b/src/ViewModels/CommitDetail.cs index 81c09472..0c3ad23f 100644 --- a/src/ViewModels/CommitDetail.cs +++ b/src/ViewModels/CommitDetail.cs @@ -332,8 +332,7 @@ namespace SourceGit.ViewModels history.Icon = App.CreateMenuIcon("Icons.Histories"); history.Click += (_, ev) => { - var window = new Views.FileHistories() { DataContext = new FileHistories(_repo, change.Path, _commit.SHA) }; - window.Show(); + App.ShowWindow(new FileHistories(_repo, change.Path, _commit.SHA), false); ev.Handled = true; }; @@ -343,8 +342,7 @@ namespace SourceGit.ViewModels blame.IsEnabled = change.Index != Models.ChangeState.Deleted; blame.Click += (_, ev) => { - var window = new Views.Blame() { DataContext = new Blame(_repo.FullPath, change.Path, _commit.SHA) }; - window.Show(); + App.ShowWindow(new Blame(_repo.FullPath, change.Path, _commit.SHA), false); ev.Handled = true; }; @@ -508,8 +506,7 @@ namespace SourceGit.ViewModels history.Icon = App.CreateMenuIcon("Icons.Histories"); history.Click += (_, ev) => { - var window = new Views.FileHistories() { DataContext = new FileHistories(_repo, file.Path, _commit.SHA) }; - window.Show(); + App.ShowWindow(new FileHistories(_repo, file.Path, _commit.SHA), false); ev.Handled = true; }; @@ -519,8 +516,7 @@ namespace SourceGit.ViewModels blame.IsEnabled = file.Type == Models.ObjectType.Blob; blame.Click += (_, ev) => { - var window = new Views.Blame() { DataContext = new Blame(_repo.FullPath, file.Path, _commit.SHA) }; - window.Show(); + App.ShowWindow(new Blame(_repo.FullPath, file.Path, _commit.SHA), false); ev.Handled = true; }; diff --git a/src/ViewModels/Histories.cs b/src/ViewModels/Histories.cs index a4a3c515..27457959 100644 --- a/src/ViewModels/Histories.cs +++ b/src/ViewModels/Histories.cs @@ -570,11 +570,7 @@ namespace SourceGit.ViewModels return; } - App.OpenDialog(new Views.InteractiveRebase() - { - DataContext = new InteractiveRebase(_repo, current, commit) - }); - + App.ShowWindow(new InteractiveRebase(_repo, current, commit), true); e.Handled = true; }; diff --git a/src/ViewModels/Launcher.cs b/src/ViewModels/Launcher.cs index 9ae99b33..84ba96e4 100644 --- a/src/ViewModels/Launcher.cs +++ b/src/ViewModels/Launcher.cs @@ -380,7 +380,7 @@ namespace SourceGit.ViewModels configure.Header = App.Text("Workspace.Configure"); configure.Click += (_, e) => { - App.OpenDialog(new Views.ConfigureWorkspace() { DataContext = new ConfigureWorkspace() }); + App.ShowWindow(new ConfigureWorkspace(), true); e.Handled = true; }; menu.Items.Add(configure); diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index 3ce40751..04f938ed 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -1405,8 +1405,7 @@ namespace SourceGit.ViewModels { locks.Click += (_, e) => { - var dialog = new Views.LFSLocks() { DataContext = new LFSLocks(this, _remotes[0].Name) }; - App.OpenDialog(dialog); + App.ShowWindow(new LFSLocks(this, _remotes[0].Name), true); e.Handled = true; }; } @@ -1419,8 +1418,7 @@ namespace SourceGit.ViewModels lockRemote.Header = remoteName; lockRemote.Click += (_, e) => { - var dialog = new Views.LFSLocks() { DataContext = new LFSLocks(this, remoteName) }; - App.OpenDialog(dialog); + App.ShowWindow(new LFSLocks(this, remoteName), true); e.Handled = true; }; locks.Items.Add(lockRemote); @@ -1706,10 +1704,7 @@ namespace SourceGit.ViewModels compareWithHead.Icon = App.CreateMenuIcon("Icons.Compare"); compareWithHead.Click += (_, _) => { - App.OpenDialog(new Views.BranchCompare() - { - DataContext = new BranchCompare(_fullpath, branch, _currentBranch) - }); + App.ShowWindow(new BranchCompare(_fullpath, branch, _currentBranch), false); }; menu.Items.Add(new MenuItem() { Header = "-" }); menu.Items.Add(compareWithHead); @@ -1989,10 +1984,7 @@ namespace SourceGit.ViewModels compareWithHead.Icon = App.CreateMenuIcon("Icons.Compare"); compareWithHead.Click += (_, _) => { - App.OpenDialog(new Views.BranchCompare() - { - DataContext = new BranchCompare(_fullpath, branch, _currentBranch) - }); + App.ShowWindow(new BranchCompare(_fullpath, branch, _currentBranch), false); }; menu.Items.Add(compareWithHead); diff --git a/src/ViewModels/WorkingCopy.cs b/src/ViewModels/WorkingCopy.cs index 65759412..29c8a13f 100644 --- a/src/ViewModels/WorkingCopy.cs +++ b/src/ViewModels/WorkingCopy.cs @@ -323,10 +323,7 @@ namespace SourceGit.ViewModels public void OpenAssumeUnchanged() { - App.OpenDialog(new Views.AssumeUnchangedManager() - { - DataContext = new AssumeUnchangedManager(_repo) - }); + App.ShowWindow(new AssumeUnchangedManager(_repo), true); } public void StashAll(bool autoStart) @@ -726,8 +723,7 @@ namespace SourceGit.ViewModels history.Icon = App.CreateMenuIcon("Icons.Histories"); history.Click += (_, e) => { - var window = new Views.FileHistories() { DataContext = new FileHistories(_repo, change.Path) }; - window.Show(); + App.ShowWindow(new FileHistories(_repo, change.Path), false); e.Handled = true; }; @@ -1093,8 +1089,7 @@ namespace SourceGit.ViewModels { ai.Click += (_, e) => { - var dialog = new Views.AIAssistant(services[0], _repo.FullPath, this, _selectedStaged); - App.OpenDialog(dialog); + App.ShowWindow(new AIAssistant(_repo, services[0], _selectedStaged, t => CommitMessage = t), true); e.Handled = true; }; } @@ -1108,8 +1103,7 @@ namespace SourceGit.ViewModels item.Header = service.Name; item.Click += (_, e) => { - var dialog = new Views.AIAssistant(dup, _repo.FullPath, this, _selectedStaged); - App.OpenDialog(dialog); + App.ShowWindow(new AIAssistant(_repo, dup, _selectedStaged, t => CommitMessage = t), true); e.Handled = true; }; @@ -1193,8 +1187,7 @@ namespace SourceGit.ViewModels history.Icon = App.CreateMenuIcon("Icons.Histories"); history.Click += (_, e) => { - var window = new Views.FileHistories() { DataContext = new FileHistories(_repo, change.Path) }; - window.Show(); + App.ShowWindow(new FileHistories(_repo, change.Path), false); e.Handled = true; }; @@ -1490,8 +1483,7 @@ namespace SourceGit.ViewModels if (services.Count == 1) { - var dialog = new Views.AIAssistant(services[0], _repo.FullPath, this, _staged); - App.OpenDialog(dialog); + App.ShowWindow(new AIAssistant(_repo, services[0], _staged, t => CommitMessage = t), true); return null; } @@ -1503,8 +1495,7 @@ namespace SourceGit.ViewModels item.Header = service.Name; item.Click += (_, e) => { - var dialog = new Views.AIAssistant(dup, _repo.FullPath, this, _staged); - App.OpenDialog(dialog); + App.ShowWindow(new AIAssistant(_repo, dup, _staged, t => CommitMessage = t), true); e.Handled = true; }; @@ -1705,14 +1696,7 @@ namespace SourceGit.ViewModels if (!string.IsNullOrEmpty(_filter) && _staged.Count > _visibleStaged.Count && !confirmWithFilter) { var confirmMessage = App.Text("WorkingCopy.ConfirmCommitWithFilter", _staged.Count, _visibleStaged.Count, _staged.Count - _visibleStaged.Count); - App.OpenDialog(new Views.ConfirmCommit() - { - DataContext = new ConfirmCommit(confirmMessage, () => - { - DoCommit(autoStage, autoPush, allowEmpty, true); - }) - }); - + App.ShowWindow(new ConfirmCommit(confirmMessage, () => DoCommit(autoStage, autoPush, allowEmpty, true)), true); return; } @@ -1720,14 +1704,7 @@ namespace SourceGit.ViewModels { if ((autoStage && _count == 0) || (!autoStage && _staged.Count == 0)) { - App.OpenDialog(new Views.ConfirmEmptyCommit() - { - DataContext = new ConfirmEmptyCommit(_count > 0, stageAll => - { - DoCommit(stageAll, autoPush, true, confirmWithFilter); - }) - }); - + App.ShowWindow(new ConfirmEmptyCommit(_count > 0, stageAll => DoCommit(stageAll, autoPush, true, confirmWithFilter)), true); return; } } diff --git a/src/Views/AIAssistant.axaml b/src/Views/AIAssistant.axaml index e07c3a3e..c9a37f3b 100644 --- a/src/Views/AIAssistant.axaml +++ b/src/Views/AIAssistant.axaml @@ -7,6 +7,7 @@ xmlns:c="using:SourceGit.Converters" mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="120" x:Class="SourceGit.Views.AIAssistant" + x:DataType="vm:AIAssistant" x:Name="ThisControl" Icon="/App.ico" Title="{DynamicResource Text.AIAssistant}" @@ -48,20 +49,22 @@ - + + + diff --git a/src/Views/RepositoryToolbar.axaml.cs b/src/Views/RepositoryToolbar.axaml.cs index 9ddc64c8..80b5544d 100644 --- a/src/Views/RepositoryToolbar.axaml.cs +++ b/src/Views/RepositoryToolbar.axaml.cs @@ -116,6 +116,21 @@ namespace SourceGit.Views e.Handled = true; } + private void StartBisect(object sender, RoutedEventArgs e) + { + if (DataContext is ViewModels.Repository { IsBisectCommandRunning: false } repo && + repo.InProgressContext == null && + repo.CanCreatePopup()) + { + if (repo.LocalChangesCount > 0) + App.RaiseException(repo.FullPath, "You have un-committed local changes. Please discard or stash them first."); + else + repo.Bisect("start"); + } + + e.Handled = true; + } + private void OpenCustomActionMenu(object sender, RoutedEventArgs e) { if (DataContext is ViewModels.Repository repo && sender is Control control) From a42df87b9c5e8549bc46d8f1a2056f0e2f7d07bb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 22 Apr 2025 07:45:38 +0000 Subject: [PATCH 009/392] doc: Update translation status and sort locale files --- TRANSLATION.md | 96 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 86 insertions(+), 10 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index 866aac65..3fe63403 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -6,11 +6,18 @@ This document shows the translation status of each locale file in the repository ### ![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen) -### ![de__DE](https://img.shields.io/badge/de__DE-96.19%25-yellow) +### ![de__DE](https://img.shields.io/badge/de__DE-95.32%25-yellow)
Missing keys in de_DE.axaml +- Text.Bisect +- Text.Bisect.Abort +- Text.Bisect.Bad +- Text.Bisect.Detecting +- Text.Bisect.Good +- Text.Bisect.Skip +- Text.Bisect.WaitingForRange - Text.BranchUpstreamInvalid - Text.CommitCM.CopyAuthor - Text.CommitCM.CopyCommitter @@ -43,11 +50,18 @@ This document shows the translation status of each locale file in the repository
-### ![es__ES](https://img.shields.io/badge/es__ES-98.95%25-yellow) +### ![es__ES](https://img.shields.io/badge/es__ES-98.05%25-yellow)
Missing keys in es_ES.axaml +- Text.Bisect +- Text.Bisect.Abort +- Text.Bisect.Bad +- Text.Bisect.Detecting +- Text.Bisect.Good +- Text.Bisect.Skip +- Text.Bisect.WaitingForRange - Text.CommitCM.CopyAuthor - Text.CommitCM.CopyCommitter - Text.CommitCM.CopySubject @@ -59,11 +73,18 @@ This document shows the translation status of each locale file in the repository
-### ![fr__FR](https://img.shields.io/badge/fr__FR-97.51%25-yellow) +### ![fr__FR](https://img.shields.io/badge/fr__FR-96.62%25-yellow)
Missing keys in fr_FR.axaml +- Text.Bisect +- Text.Bisect.Abort +- Text.Bisect.Bad +- Text.Bisect.Detecting +- Text.Bisect.Good +- Text.Bisect.Skip +- Text.Bisect.WaitingForRange - Text.CommitCM.CopyAuthor - Text.CommitCM.CopyCommitter - Text.CommitCM.CopySubject @@ -86,11 +107,18 @@ This document shows the translation status of each locale file in the repository
-### ![it__IT](https://img.shields.io/badge/it__IT-97.24%25-yellow) +### ![it__IT](https://img.shields.io/badge/it__IT-96.36%25-yellow)
Missing keys in it_IT.axaml +- Text.Bisect +- Text.Bisect.Abort +- Text.Bisect.Bad +- Text.Bisect.Detecting +- Text.Bisect.Good +- Text.Bisect.Skip +- Text.Bisect.WaitingForRange - Text.CommitCM.CopyAuthor - Text.CommitCM.CopyCommitter - Text.CommitCM.CopySubject @@ -115,11 +143,18 @@ This document shows the translation status of each locale file in the repository
-### ![ja__JP](https://img.shields.io/badge/ja__JP-97.24%25-yellow) +### ![ja__JP](https://img.shields.io/badge/ja__JP-96.36%25-yellow)
Missing keys in ja_JP.axaml +- Text.Bisect +- Text.Bisect.Abort +- Text.Bisect.Bad +- Text.Bisect.Detecting +- Text.Bisect.Good +- Text.Bisect.Skip +- Text.Bisect.WaitingForRange - Text.CommitCM.CopyAuthor - Text.CommitCM.CopyCommitter - Text.CommitCM.CopySubject @@ -144,7 +179,7 @@ This document shows the translation status of each locale file in the repository
-### ![pt__BR](https://img.shields.io/badge/pt__BR-88.71%25-yellow) +### ![pt__BR](https://img.shields.io/badge/pt__BR-87.91%25-yellow)
Missing keys in pt_BR.axaml @@ -155,6 +190,13 @@ This document shows the translation status of each locale file in the repository - Text.ApplyStash.DropAfterApply - Text.ApplyStash.RestoreIndex - Text.ApplyStash.Stash +- Text.Bisect +- Text.Bisect.Abort +- Text.Bisect.Bad +- Text.Bisect.Detecting +- Text.Bisect.Good +- Text.Bisect.Skip +- Text.Bisect.WaitingForRange - Text.BranchCM.CustomAction - Text.BranchCM.MergeMultiBranches - Text.BranchUpstreamInvalid @@ -238,11 +280,18 @@ This document shows the translation status of each locale file in the repository
-### ![ru__RU](https://img.shields.io/badge/ru__RU-98.82%25-yellow) +### ![ru__RU](https://img.shields.io/badge/ru__RU-97.92%25-yellow)
Missing keys in ru_RU.axaml +- Text.Bisect +- Text.Bisect.Abort +- Text.Bisect.Bad +- Text.Bisect.Detecting +- Text.Bisect.Good +- Text.Bisect.Skip +- Text.Bisect.WaitingForRange - Text.CommitCM.CopyAuthor - Text.CommitCM.CopyCommitter - Text.CommitCM.CopySubject @@ -255,11 +304,18 @@ This document shows the translation status of each locale file in the repository
-### ![ta__IN](https://img.shields.io/badge/ta__IN-97.51%25-yellow) +### ![ta__IN](https://img.shields.io/badge/ta__IN-96.62%25-yellow)
Missing keys in ta_IN.axaml +- Text.Bisect +- Text.Bisect.Abort +- Text.Bisect.Bad +- Text.Bisect.Detecting +- Text.Bisect.Good +- Text.Bisect.Skip +- Text.Bisect.WaitingForRange - Text.CommitCM.CopyAuthor - Text.CommitCM.CopyCommitter - Text.CommitCM.CopySubject @@ -282,11 +338,18 @@ This document shows the translation status of each locale file in the repository
-### ![uk__UA](https://img.shields.io/badge/uk__UA-98.69%25-yellow) +### ![uk__UA](https://img.shields.io/badge/uk__UA-97.79%25-yellow)
Missing keys in uk_UA.axaml +- Text.Bisect +- Text.Bisect.Abort +- Text.Bisect.Bad +- Text.Bisect.Detecting +- Text.Bisect.Good +- Text.Bisect.Skip +- Text.Bisect.WaitingForRange - Text.CommitCM.CopyAuthor - Text.CommitCM.CopyCommitter - Text.CommitCM.CopySubject @@ -302,4 +365,17 @@ This document shows the translation status of each locale file in the repository ### ![zh__CN](https://img.shields.io/badge/zh__CN-%E2%88%9A-brightgreen) -### ![zh__TW](https://img.shields.io/badge/zh__TW-%E2%88%9A-brightgreen) \ No newline at end of file +### ![zh__TW](https://img.shields.io/badge/zh__TW-99.09%25-yellow) + +
+Missing keys in zh_TW.axaml + +- Text.Bisect +- Text.Bisect.Abort +- Text.Bisect.Bad +- Text.Bisect.Detecting +- Text.Bisect.Good +- Text.Bisect.Skip +- Text.Bisect.WaitingForRange + +
\ No newline at end of file From 7be37424e10f596d61049d114e5e5e0b73474169 Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 22 Apr 2025 16:02:10 +0800 Subject: [PATCH 010/392] fix: modal dialog did not take focus after show (#1225) Co-authored-by: Gadfly Signed-off-by: leo --- src/Views/ChromelessWindow.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Views/ChromelessWindow.cs b/src/Views/ChromelessWindow.cs index 647c657e..dd2485d4 100644 --- a/src/Views/ChromelessWindow.cs +++ b/src/Views/ChromelessWindow.cs @@ -18,6 +18,8 @@ namespace SourceGit.Views public ChromelessWindow() { + Focusable = true; + if (OperatingSystem.IsLinux()) { if (UseSystemWindowFrame) From 34e0ea3bcbb74f9ad3b15f32f656f159c1dca7dc Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 22 Apr 2025 16:07:23 +0800 Subject: [PATCH 011/392] enhance: raise bisect error manually Signed-off-by: leo --- src/Commands/Bisect.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Commands/Bisect.cs b/src/Commands/Bisect.cs index 20beeab9..a3bf1a97 100644 --- a/src/Commands/Bisect.cs +++ b/src/Commands/Bisect.cs @@ -6,6 +6,7 @@ { WorkingDirectory = repo; Context = repo; + RaiseError = false; Args = $"bisect {subcmd}"; } } From 9a6c671a964f19df393ab223a5e4429a351bddd5 Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 22 Apr 2025 16:50:46 +0800 Subject: [PATCH 012/392] refactor: `--ignore-cr-at-eol` is not necessary when `--ignore-all-space` is enabled Signed-off-by: leo --- src/Commands/Diff.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Commands/Diff.cs b/src/Commands/Diff.cs index 8df8cbaa..1434c1a5 100644 --- a/src/Commands/Diff.cs +++ b/src/Commands/Diff.cs @@ -28,7 +28,7 @@ namespace SourceGit.Commands Context = repo; if (ignoreWhitespace) - Args = $"-c core.autocrlf=false diff --no-ext-diff --patch --ignore-cr-at-eol --ignore-all-space --unified={unified} {opt}"; + Args = $"-c core.autocrlf=false diff --no-ext-diff --patch --ignore-all-space --unified={unified} {opt}"; else Args = $"-c core.autocrlf=false diff --no-ext-diff --patch --unified={unified} {opt}"; } From f2000b4a845373e057658cffb623c9ee15e08c19 Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 22 Apr 2025 17:51:55 +0800 Subject: [PATCH 013/392] enhance: show git log without command itself for `git bisect` Signed-off-by: leo --- src/ViewModels/Repository.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index ddc967fd..1db1d6f3 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -965,9 +965,9 @@ namespace SourceGit.ViewModels Dispatcher.UIThread.Invoke(() => { if (!succ) - App.RaiseException(_fullpath, log.Content); + App.RaiseException(_fullpath, log.Content.Substring(log.Content.IndexOf('\n')).Trim()); else if (log.Content.Contains("is the first bad commit")) - App.SendNotification(_fullpath, log.Content); + App.SendNotification(_fullpath, log.Content.Substring(log.Content.IndexOf('\n')).Trim()); MarkBranchesDirtyManually(); SetWatcherEnabled(true); From 87ebe3741df33375ae380c28bfcd838d2f973d5b Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 22 Apr 2025 18:45:14 +0800 Subject: [PATCH 014/392] fix: for init-commit, app will crash with `COMMIT & PUSH` due to local branch has not been updated (#1229) Signed-off-by: leo --- src/ViewModels/Push.cs | 3 +++ src/ViewModels/WorkingCopy.cs | 13 ++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/ViewModels/Push.cs b/src/ViewModels/Push.cs index 5bbb9858..917935b0 100644 --- a/src/ViewModels/Push.cs +++ b/src/ViewModels/Push.cs @@ -114,6 +114,9 @@ namespace SourceGit.ViewModels // Set default selected local branch. if (localBranch != null) { + if (LocalBranches.Count == 0) + LocalBranches.Add(localBranch); + _selectedLocalBranch = localBranch; HasSpecifiedLocalBranch = true; } diff --git a/src/ViewModels/WorkingCopy.cs b/src/ViewModels/WorkingCopy.cs index 29c8a13f..ef6f0996 100644 --- a/src/ViewModels/WorkingCopy.cs +++ b/src/ViewModels/WorkingCopy.cs @@ -1733,7 +1733,18 @@ namespace SourceGit.ViewModels UseAmend = false; if (autoPush && _repo.Remotes.Count > 0) - _repo.ShowAndStartPopup(new Push(_repo, null)); + { + if (_repo.CurrentBranch == null) + { + var currentBranchName = Commands.Branch.ShowCurrent(_repo.FullPath); + var tmp = new Models.Branch() { Name = currentBranchName }; + _repo.ShowAndStartPopup(new Push(_repo, tmp)); + } + else + { + _repo.ShowAndStartPopup(new Push(_repo, null)); + } + } } _repo.MarkBranchesDirtyManually(); From 78f480987544001cbb4459a6901929d49a7b7e8c Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 22 Apr 2025 19:04:40 +0800 Subject: [PATCH 015/392] fix: no changes were displayed when try to amend a commit without parent (branch first commit) (#1231) Signed-off-by: leo --- src/Commands/QueryStagedChangesWithAmend.cs | 4 ++-- src/ViewModels/WorkingCopy.cs | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Commands/QueryStagedChangesWithAmend.cs b/src/Commands/QueryStagedChangesWithAmend.cs index cfea5e35..93dcdb37 100644 --- a/src/Commands/QueryStagedChangesWithAmend.cs +++ b/src/Commands/QueryStagedChangesWithAmend.cs @@ -11,11 +11,11 @@ namespace SourceGit.Commands [GeneratedRegex(@"^:[\d]{6} ([\d]{6}) ([0-9a-f]{40}) [0-9a-f]{40} R\d{0,6}\t(.*\t.*)$")] private static partial Regex REG_FORMAT2(); - public QueryStagedChangesWithAmend(string repo) + public QueryStagedChangesWithAmend(string repo, string parent) { WorkingDirectory = repo; Context = repo; - Args = "diff-index --cached -M HEAD^"; + Args = $"diff-index --cached -M {parent}"; } public List Result() diff --git a/src/ViewModels/WorkingCopy.cs b/src/ViewModels/WorkingCopy.cs index ef6f0996..906248eb 100644 --- a/src/ViewModels/WorkingCopy.cs +++ b/src/ViewModels/WorkingCopy.cs @@ -1524,7 +1524,10 @@ namespace SourceGit.ViewModels private List GetStagedChanges() { if (_useAmend) - return new Commands.QueryStagedChangesWithAmend(_repo.FullPath).Result(); + { + var head = new Commands.QuerySingleCommit(_repo.FullPath, "HEAD").Result(); + return new Commands.QueryStagedChangesWithAmend(_repo.FullPath, head.Parents.Count == 0 ? "4b825dc642cb6eb9a060e54bf8d69288fbee4904" : "HEAD^").Result(); + } var rs = new List(); foreach (var c in _cached) From 345ad06aba29053db643f6afeb5790fe99fe8023 Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 22 Apr 2025 19:20:27 +0800 Subject: [PATCH 016/392] refactor: diff for staged file with `--amend` enabled (#1231) Signed-off-by: leo --- src/Commands/QueryStagedChangesWithAmend.cs | 5 +++++ src/Models/Change.cs | 1 + src/Models/DiffOption.cs | 2 +- src/ViewModels/WorkingCopy.cs | 4 ++-- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Commands/QueryStagedChangesWithAmend.cs b/src/Commands/QueryStagedChangesWithAmend.cs index 93dcdb37..8f8456c9 100644 --- a/src/Commands/QueryStagedChangesWithAmend.cs +++ b/src/Commands/QueryStagedChangesWithAmend.cs @@ -16,6 +16,7 @@ namespace SourceGit.Commands WorkingDirectory = repo; Context = repo; Args = $"diff-index --cached -M {parent}"; + _parent = parent; } public List Result() @@ -37,6 +38,7 @@ namespace SourceGit.Commands { FileMode = match.Groups[1].Value, ObjectHash = match.Groups[2].Value, + ParentSHA = _parent, }, }; change.Set(Models.ChangeState.Renamed); @@ -54,6 +56,7 @@ namespace SourceGit.Commands { FileMode = match.Groups[1].Value, ObjectHash = match.Groups[2].Value, + ParentSHA = _parent, }, }; @@ -88,5 +91,7 @@ namespace SourceGit.Commands return []; } + + private string _parent = string.Empty; } } diff --git a/src/Models/Change.cs b/src/Models/Change.cs index e9d07181..0c96ec95 100644 --- a/src/Models/Change.cs +++ b/src/Models/Change.cs @@ -26,6 +26,7 @@ namespace SourceGit.Models { public string FileMode { get; set; } = ""; public string ObjectHash { get; set; } = ""; + public string ParentSHA { get; set; } = ""; } public class Change diff --git a/src/Models/DiffOption.cs b/src/Models/DiffOption.cs index 98387e7f..31d83cea 100644 --- a/src/Models/DiffOption.cs +++ b/src/Models/DiffOption.cs @@ -40,7 +40,7 @@ namespace SourceGit.Models else { if (change.DataForAmend != null) - _extra = "--cached HEAD^"; + _extra = $"--cached {change.DataForAmend.ParentSHA}"; else _extra = "--cached"; diff --git a/src/ViewModels/WorkingCopy.cs b/src/ViewModels/WorkingCopy.cs index 906248eb..6402878a 100644 --- a/src/ViewModels/WorkingCopy.cs +++ b/src/ViewModels/WorkingCopy.cs @@ -1526,8 +1526,8 @@ namespace SourceGit.ViewModels if (_useAmend) { var head = new Commands.QuerySingleCommit(_repo.FullPath, "HEAD").Result(); - return new Commands.QueryStagedChangesWithAmend(_repo.FullPath, head.Parents.Count == 0 ? "4b825dc642cb6eb9a060e54bf8d69288fbee4904" : "HEAD^").Result(); - } + return new Commands.QueryStagedChangesWithAmend(_repo.FullPath, head.Parents.Count == 0 ? "4b825dc642cb6eb9a060e54bf8d69288fbee4904" : $"{head.SHA}^").Result(); + } var rs = new List(); foreach (var c in _cached) From 7890f7abbf38f356722e548cec549163c1c71927 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 23 Apr 2025 10:17:14 +0800 Subject: [PATCH 017/392] refactor: use `PointerPressed` event instead of `ListBox.SelectionChanged` event to navigate to commit (#1230) Signed-off-by: leo --- src/Views/BranchTree.axaml | 96 ++++++++++++++++++----------------- src/Views/BranchTree.axaml.cs | 28 ++++++++-- src/Views/TagsView.axaml | 83 +++++++++++++++--------------- src/Views/TagsView.axaml.cs | 27 ++++++---- 4 files changed, 133 insertions(+), 101 deletions(-) diff --git a/src/Views/BranchTree.axaml b/src/Views/BranchTree.axaml index 0ac09e6c..c1fecf63 100644 --- a/src/Views/BranchTree.axaml +++ b/src/Views/BranchTree.axaml @@ -32,61 +32,63 @@ - + + - - + + - - + + - - + + - - + + - - - - + + + + - - + + - - + + + - + diff --git a/src/Views/BranchTree.axaml.cs b/src/Views/BranchTree.axaml.cs index 51bb01e0..8542157d 100644 --- a/src/Views/BranchTree.axaml.cs +++ b/src/Views/BranchTree.axaml.cs @@ -318,6 +318,31 @@ namespace SourceGit.Views } } + private void OnNodePointerPressed(object sender, PointerPressedEventArgs e) + { + var p = e.GetCurrentPoint(this); + if (!p.Properties.IsLeftButtonPressed) + return; + + if (DataContext is not ViewModels.Repository repo) + return; + + if (sender is not Border { DataContext: ViewModels.BranchTreeNode node }) + return; + + if (node.Backend is not Models.Branch branch) + return; + + if (BranchesPresenter.SelectedItems is { Count: > 0 }) + { + var ctrl = OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control; + if (e.KeyModifiers.HasFlag(ctrl) || e.KeyModifiers.HasFlag(KeyModifiers.Shift)) + return; + } + + repo.NavigateToCommit(branch.Head); + } + private void OnNodesSelectionChanged(object _, SelectionChangedEventArgs e) { if (_disableSelectionChangingEvent) @@ -343,9 +368,6 @@ namespace SourceGit.Views if (selected == null || selected.Count == 0) return; - if (selected.Count == 1 && selected[0] is ViewModels.BranchTreeNode { Backend: Models.Branch branch }) - repo.NavigateToCommit(branch.Head); - var prev = null as ViewModels.BranchTreeNode; foreach (var row in Rows) { diff --git a/src/Views/TagsView.axaml b/src/Views/TagsView.axaml index b5384c8f..2a575cb3 100644 --- a/src/Views/TagsView.axaml +++ b/src/Views/TagsView.axaml @@ -26,36 +26,36 @@ SelectionChanged="OnRowSelectionChanged"> - - + + + - + - + - - - - - - - - + + + + + + + + + @@ -69,23 +69,22 @@ SelectionChanged="OnRowSelectionChanged"> - - + + + - + - - + + + diff --git a/src/Views/TagsView.axaml.cs b/src/Views/TagsView.axaml.cs index c83cfd28..ba6740c0 100644 --- a/src/Views/TagsView.axaml.cs +++ b/src/Views/TagsView.axaml.cs @@ -199,15 +199,27 @@ namespace SourceGit.Views private void OnDoubleTappedNode(object sender, TappedEventArgs e) { - if (sender is Grid { DataContext: ViewModels.TagTreeNode node }) - { - if (node.IsFolder) - ToggleNodeIsExpanded(node); - } + if (sender is Control { DataContext: ViewModels.TagTreeNode { IsFolder: true } node }) + ToggleNodeIsExpanded(node); e.Handled = true; } + private void OnRowPointerPressed(object sender, PointerPressedEventArgs e) + { + var p = e.GetCurrentPoint(this); + if (!p.Properties.IsLeftButtonPressed) + return; + + if (DataContext is not ViewModels.Repository repo) + return; + + if (sender is Control { DataContext: Models.Tag tag }) + repo.NavigateToCommit(tag.SHA); + else if (sender is Control { DataContext: ViewModels.TagTreeNode { Tag: { } nodeTag } }) + repo.NavigateToCommit(nodeTag.SHA); + } + private void OnRowContextRequested(object sender, ContextRequestedEventArgs e) { var control = sender as Control; @@ -240,11 +252,8 @@ namespace SourceGit.Views else if (selected is Models.Tag tag) selectedTag = tag; - if (selectedTag != null && DataContext is ViewModels.Repository repo) - { + if (selectedTag != null) RaiseEvent(new RoutedEventArgs(SelectionChangedEvent)); - repo.NavigateToCommit(selectedTag.SHA); - } } private void MakeTreeRows(List rows, List nodes) From fafa2a53aece00da9749524446d31afa57d8560d Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 23 Apr 2025 10:33:49 +0800 Subject: [PATCH 018/392] enhance: show commit full message tooltip when hover commit subject in `FileHistories` view (#1232) Signed-off-by: leo --- src/ViewModels/FileHistories.cs | 12 ++++++++++++ src/Views/FileHistories.axaml | 7 ++++++- src/Views/FileHistories.axaml.cs | 19 +++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/ViewModels/FileHistories.cs b/src/ViewModels/FileHistories.cs index 417b816d..9f91205e 100644 --- a/src/ViewModels/FileHistories.cs +++ b/src/ViewModels/FileHistories.cs @@ -305,11 +305,23 @@ namespace SourceGit.ViewModels _repo.NavigateToCommit(commit.SHA); } + public string GetCommitFullMessage(Models.Commit commit) + { + var sha = commit.SHA; + if (_fullCommitMessages.TryGetValue(sha, out var msg)) + return msg; + + msg = new Commands.QueryCommitFullMessage(_repo.FullPath, sha).Result(); + _fullCommitMessages[sha] = msg; + return msg; + } + private readonly Repository _repo = null; private readonly string _file = null; private bool _isLoading = true; private bool _prevIsDiffMode = true; private List _commits = null; + private Dictionary _fullCommitMessages = new Dictionary(); private object _viewContent = null; } } diff --git a/src/Views/FileHistories.axaml b/src/Views/FileHistories.axaml index e33156fb..f597fc04 100644 --- a/src/Views/FileHistories.axaml +++ b/src/Views/FileHistories.axaml @@ -93,7 +93,12 @@ - + diff --git a/src/Views/FileHistories.axaml.cs b/src/Views/FileHistories.axaml.cs index be5affc3..a183182e 100644 --- a/src/Views/FileHistories.axaml.cs +++ b/src/Views/FileHistories.axaml.cs @@ -1,3 +1,5 @@ +using System; + using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; @@ -57,5 +59,22 @@ namespace SourceGit.Views e.Handled = true; } } + + private void OnCommitSubjectDataContextChanged(object sender, EventArgs e) + { + if (sender is TextBlock textBlock) + ToolTip.SetTip(textBlock, null); + } + + private void OnCommitSubjectPointerMoved(object sender, PointerEventArgs e) + { + if (sender is TextBlock { DataContext: Models.Commit commit } textBlock && + DataContext is ViewModels.FileHistories vm) + { + var tooltip = ToolTip.GetTip(textBlock); + if (tooltip == null) + ToolTip.SetTip(textBlock, vm.GetCommitFullMessage(commit)); + } + } } } From 17c08d42a0ea721a35b99393e10a245578483a72 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 23 Apr 2025 10:38:01 +0800 Subject: [PATCH 019/392] enhance: ignore all sub-directories those names start with '.' (#1234) Signed-off-by: leo --- src/ViewModels/ScanRepositories.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/ViewModels/ScanRepositories.cs b/src/ViewModels/ScanRepositories.cs index 322c2cda..09c3559f 100644 --- a/src/ViewModels/ScanRepositories.cs +++ b/src/ViewModels/ScanRepositories.cs @@ -87,11 +87,8 @@ namespace SourceGit.ViewModels var subdirs = dir.GetDirectories("*", opts); foreach (var subdir in subdirs) { - if (subdir.Name.Equals("node_modules", StringComparison.Ordinal) || - subdir.Name.Equals(".svn", StringComparison.Ordinal) || - subdir.Name.Equals(".vs", StringComparison.Ordinal) || - subdir.Name.Equals(".vscode", StringComparison.Ordinal) || - subdir.Name.Equals(".idea", StringComparison.Ordinal)) + if (subdir.Name.StartsWith(".", StringComparison.Ordinal) || + subdir.Name.Equals("node_modules", StringComparison.Ordinal)) continue; CallUIThread(() => ProgressDescription = $"Scanning {subdir.FullName}..."); From 9bdbf475220cd9cfb148d00c2459d1f41e4d2b5c Mon Sep 17 00:00:00 2001 From: AquariusStar <48148723+AquariusStar@users.noreply.github.com> Date: Wed, 23 Apr 2025 05:39:30 +0300 Subject: [PATCH 020/392] localization: update russian localization (#1233) --- src/Resources/Locales/ru_RU.axaml | 40 +++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index b8c86415..35125369 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -14,8 +14,8 @@ Отслеживание ветки: Отслеживание внешней ветки Переключиться на: - создать новую ветку - ветку из списка + Создать новую ветку + Ветку из списка Помощник OpenAI ПЕРЕСОЗДАТЬ Использовать OpenAI для создания сообщения о ревизии @@ -40,9 +40,16 @@ СПИСОК ПУСТ УДАЛИТЬ ДВОИЧНЫЙ ФАЙЛ НЕ ПОДДЕРЖИВАЕТСЯ!!! + Раздвоить + О + Плохая + Раздвоение. Текущая ГОЛОВА (HEAD) хорошая или плохая? + Хорошая + Пропустить + Раздвоение. Сделать текущую ревизию хорошей или плохой и переключиться на другой. Расследование РАССЛЕДОВАНИЕ В ЭТОМ ФАЙЛЕ НЕ ПОДДЕРЖИВАЕТСЯ!!! - Проверить ${0}$... + Переключиться на ${0}$... Сравнить с ГОЛОВОЙ (HEAD) Сравнить с рабочим каталогом Копировать имя ветки @@ -55,8 +62,8 @@ Поток Git - Завершение ${0}$ Влить ${0}$ в ${1}$... Влить {0} выделенных веток в текущую - Забрать ${0}$ - Забрать ${0}$ в ${1}$... + Загрузить ${0}$ + Загрузить ${0}$ в ${1}$... Выложить ${0}$ Переместить ${0}$ на ${1}$... Переименовать ${0}$... @@ -103,8 +110,11 @@ Применить несколько ревизий ... Сравнить c ГОЛОВОЙ (HEAD) Сравнить с рабочим каталогом + Автор + Ревизор Информацию SHA + Субъект Пользовательское действие Интерактивное перемещение (rebase -i) ${0}$ сюда Влить в ${0}$ @@ -136,6 +146,7 @@ SHA Открыть в браузере Описание + СУБЪЕКТ Введите тему ревизии Настройка репозитория ШАБЛОН РЕВИЗИИ @@ -199,7 +210,7 @@ Копировать путь Создать ветку... Основан на: - Проверить созданную ветку + Переключиться на созданную ветку Локальные изменения: Отклонить Отложить и применить повторно @@ -348,9 +359,9 @@ Принудительно разблокировать Обрезать Запустить (git lfs prune), чтобы удалить старые файлы LFS из локального хранилища - Забрать + Загрузить Запустить (git lfs pull), чтобы загрузить все файлы LFS Git для текущей ссылки и проверить - Забрать объекты LFS + Загрузить объекты LFS Выложить Отправляйте большие файлы, помещенные в очередь, в конечную точку LFS Git Выложить объекты LFS @@ -385,7 +396,7 @@ Извлечение, запускается сразу Режим доски (по умолчанию) Режим поиска ревизий - Забрать, запускается сразу + Загрузить, запускается сразу Выложить, запускается сразу Принудительно перезагрузить репозиторий Подготовленные/Неподготовленные выбранные изменения @@ -512,16 +523,16 @@ Цель: Удалить рабочий каталог Информация об обрезке рабочего каталога в «$GIT_COMMON_DIR/worktrees» - Забрать + Загрузить Ветка внешнего репозитория: Извлечь все ветки В: Локальные изменения: Отклонить Отложить и применить повторно - Забрать без меток + Загрузить без меток Внешний репозиторий: - Забрать (Получить и слить) + Загрузить (Получить и слить) Использовать перемещение вместо слияния Выложить Убедитесь, что подмодули были вставлены @@ -615,6 +626,7 @@ Сортировать Открыть в терминале Использовать относительное время в историях + Просмотр логов РАБОЧИЕ КАТАЛОГИ ДОБАВИТЬ РАБОЧИЙ КАТАЛОГ ОБРЕЗАТЬ @@ -700,6 +712,10 @@ Подмодуль: Использовать опцию (--remote) Сетевой адрес: + Логи + ОЧИСТИТЬ ВСЁ + Копировать + Удалить Предупреждение Приветствие Создать группу From 586ff39da1d51d79fa4d53ec2fcb15ed8762842c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 23 Apr 2025 02:39:41 +0000 Subject: [PATCH 021/392] doc: Update translation status and sort locale files --- TRANSLATION.md | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index 3fe63403..fb96afdb 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -280,29 +280,7 @@ This document shows the translation status of each locale file in the repository
-### ![ru__RU](https://img.shields.io/badge/ru__RU-97.92%25-yellow) - -
-Missing keys in ru_RU.axaml - -- Text.Bisect -- Text.Bisect.Abort -- Text.Bisect.Bad -- Text.Bisect.Detecting -- Text.Bisect.Good -- Text.Bisect.Skip -- Text.Bisect.WaitingForRange -- Text.CommitCM.CopyAuthor -- Text.CommitCM.CopyCommitter -- Text.CommitCM.CopySubject -- Text.CommitMessageTextBox.SubjectCount -- Text.Repository.ViewLogs -- Text.ViewLogs -- Text.ViewLogs.Clear -- Text.ViewLogs.CopyLog -- Text.ViewLogs.Delete - -
+### ![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen) ### ![ta__IN](https://img.shields.io/badge/ta__IN-96.62%25-yellow) From f72f1894c3f5bd15d3ab1a22f8e1706149fbbd76 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 23 Apr 2025 15:34:21 +0800 Subject: [PATCH 022/392] feature: supports to enable `--ignore-cr-at-eol` in diff by default Signed-off-by: leo --- src/Commands/Diff.cs | 2 ++ src/Models/DiffOption.cs | 9 +++++++++ src/Resources/Locales/en_US.axaml | 1 + src/Resources/Locales/zh_CN.axaml | 1 + src/ViewModels/Preferences.cs | 13 +++++++++++++ src/Views/Preferences.axaml | 7 ++++++- 6 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/Commands/Diff.cs b/src/Commands/Diff.cs index 1434c1a5..a60f4cc5 100644 --- a/src/Commands/Diff.cs +++ b/src/Commands/Diff.cs @@ -29,6 +29,8 @@ namespace SourceGit.Commands if (ignoreWhitespace) Args = $"-c core.autocrlf=false diff --no-ext-diff --patch --ignore-all-space --unified={unified} {opt}"; + else if (Models.DiffOption.IgnoreCRAtEOL) + Args = $"-c core.autocrlf=false diff --no-ext-diff --patch --ignore-cr-at-eol --unified={unified} {opt}"; else Args = $"-c core.autocrlf=false diff --no-ext-diff --patch --unified={unified} {opt}"; } diff --git a/src/Models/DiffOption.cs b/src/Models/DiffOption.cs index 31d83cea..5f491d58 100644 --- a/src/Models/DiffOption.cs +++ b/src/Models/DiffOption.cs @@ -5,6 +5,15 @@ namespace SourceGit.Models { public class DiffOption { + /// + /// Enable `--ignore-cr-at-eol` by default? + /// + public static bool IgnoreCRAtEOL + { + get; + set; + } = false; + public Change WorkingCopyChange => _workingCopyChange; public bool IsUnstaged => _isUnstaged; public List Revisions => _revisions; diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 3c710727..538ac1f1 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -497,6 +497,7 @@ User Email Global git user email Enable --prune on fetch + Enable --ignore-cr-at-eol in diff Git (>= 2.23.0) is required by this app Install Path Enable HTTP SSL Verify diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 843c3735..db77a3ed 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -501,6 +501,7 @@ 邮箱 默认GIT用户邮箱 拉取更新时启用修剪(--prune) + 文件对比时默认启用 --ignore-cr-at-eol 选项 本软件要求GIT最低版本为2.23.0 安装路径 启用HTTP SSL验证 diff --git a/src/ViewModels/Preferences.cs b/src/ViewModels/Preferences.cs index a1830d07..d1e13f38 100644 --- a/src/ViewModels/Preferences.cs +++ b/src/ViewModels/Preferences.cs @@ -212,6 +212,19 @@ namespace SourceGit.ViewModels set => SetProperty(ref _useSyntaxHighlighting, value); } + public bool IgnoreCRAtEOLInDiff + { + get => Models.DiffOption.IgnoreCRAtEOL; + set + { + if (Models.DiffOption.IgnoreCRAtEOL != value) + { + Models.DiffOption.IgnoreCRAtEOL = value; + OnPropertyChanged(); + } + } + } + public bool IgnoreWhitespaceChangesInDiff { get => _ignoreWhitespaceChangesInDiff; diff --git a/src/Views/Preferences.axaml b/src/Views/Preferences.axaml index 702ec20f..6742bcfc 100644 --- a/src/Views/Preferences.axaml +++ b/src/Views/Preferences.axaml @@ -273,7 +273,7 @@ - + + + From 210767605818dc46f4f5f5c7a5b3c3c24e1c805e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 23 Apr 2025 07:34:37 +0000 Subject: [PATCH 023/392] doc: Update translation status and sort locale files --- TRANSLATION.md | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index fb96afdb..9e590d21 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -6,7 +6,7 @@ This document shows the translation status of each locale file in the repository ### ![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen) -### ![de__DE](https://img.shields.io/badge/de__DE-95.32%25-yellow) +### ![de__DE](https://img.shields.io/badge/de__DE-95.19%25-yellow)
Missing keys in de_DE.axaml @@ -36,6 +36,7 @@ This document shows the translation status of each locale file in the repository - Text.Preferences.AI.Streaming - Text.Preferences.Appearance.EditorTabWidth - Text.Preferences.General.ShowTagsInGraph +- Text.Preferences.Git.IgnoreCRAtEOLInDiff - Text.Repository.ViewLogs - Text.StashCM.SaveAsPatch - Text.ViewLogs @@ -50,7 +51,7 @@ This document shows the translation status of each locale file in the repository
-### ![es__ES](https://img.shields.io/badge/es__ES-98.05%25-yellow) +### ![es__ES](https://img.shields.io/badge/es__ES-97.92%25-yellow)
Missing keys in es_ES.axaml @@ -65,6 +66,7 @@ This document shows the translation status of each locale file in the repository - Text.CommitCM.CopyAuthor - Text.CommitCM.CopyCommitter - Text.CommitCM.CopySubject +- Text.Preferences.Git.IgnoreCRAtEOLInDiff - Text.Repository.ViewLogs - Text.ViewLogs - Text.ViewLogs.Clear @@ -73,7 +75,7 @@ This document shows the translation status of each locale file in the repository
-### ![fr__FR](https://img.shields.io/badge/fr__FR-96.62%25-yellow) +### ![fr__FR](https://img.shields.io/badge/fr__FR-96.49%25-yellow)
Missing keys in fr_FR.axaml @@ -94,6 +96,7 @@ This document shows the translation status of each locale file in the repository - Text.ConfirmEmptyCommit.NoLocalChanges - Text.ConfirmEmptyCommit.StageAllThenCommit - Text.ConfirmEmptyCommit.WithLocalChanges +- Text.Preferences.Git.IgnoreCRAtEOLInDiff - Text.Repository.ViewLogs - Text.ViewLogs - Text.ViewLogs.Clear @@ -107,7 +110,7 @@ This document shows the translation status of each locale file in the repository
-### ![it__IT](https://img.shields.io/badge/it__IT-96.36%25-yellow) +### ![it__IT](https://img.shields.io/badge/it__IT-96.23%25-yellow)
Missing keys in it_IT.axaml @@ -130,6 +133,7 @@ This document shows the translation status of each locale file in the repository - Text.ConfirmEmptyCommit.WithLocalChanges - Text.CopyFullPath - Text.Preferences.General.ShowTagsInGraph +- Text.Preferences.Git.IgnoreCRAtEOLInDiff - Text.Repository.ViewLogs - Text.ViewLogs - Text.ViewLogs.Clear @@ -143,7 +147,7 @@ This document shows the translation status of each locale file in the repository
-### ![ja__JP](https://img.shields.io/badge/ja__JP-96.36%25-yellow) +### ![ja__JP](https://img.shields.io/badge/ja__JP-96.23%25-yellow)
Missing keys in ja_JP.axaml @@ -164,6 +168,7 @@ This document shows the translation status of each locale file in the repository - Text.ConfirmEmptyCommit.NoLocalChanges - Text.ConfirmEmptyCommit.StageAllThenCommit - Text.ConfirmEmptyCommit.WithLocalChanges +- Text.Preferences.Git.IgnoreCRAtEOLInDiff - Text.Repository.FilterCommits - Text.Repository.Tags.OrderByNameDes - Text.Repository.ViewLogs @@ -179,7 +184,7 @@ This document shows the translation status of each locale file in the repository
-### ![pt__BR](https://img.shields.io/badge/pt__BR-87.91%25-yellow) +### ![pt__BR](https://img.shields.io/badge/pt__BR-87.79%25-yellow)
Missing keys in pt_BR.axaml @@ -243,6 +248,7 @@ This document shows the translation status of each locale file in the repository - Text.Preferences.General.DateFormat - Text.Preferences.General.ShowChildren - Text.Preferences.General.ShowTagsInGraph +- Text.Preferences.Git.IgnoreCRAtEOLInDiff - Text.Preferences.Git.SSLVerify - Text.Repository.FilterCommits - Text.Repository.HistoriesLayout @@ -280,9 +286,16 @@ This document shows the translation status of each locale file in the repository
-### ![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen) +### ![ru__RU](https://img.shields.io/badge/ru__RU-99.87%25-yellow) -### ![ta__IN](https://img.shields.io/badge/ta__IN-96.62%25-yellow) +
+Missing keys in ru_RU.axaml + +- Text.Preferences.Git.IgnoreCRAtEOLInDiff + +
+ +### ![ta__IN](https://img.shields.io/badge/ta__IN-96.49%25-yellow)
Missing keys in ta_IN.axaml @@ -303,6 +316,7 @@ This document shows the translation status of each locale file in the repository - Text.ConfirmEmptyCommit.NoLocalChanges - Text.ConfirmEmptyCommit.StageAllThenCommit - Text.ConfirmEmptyCommit.WithLocalChanges +- Text.Preferences.Git.IgnoreCRAtEOLInDiff - Text.Repository.ViewLogs - Text.UpdateSubmodules.Target - Text.ViewLogs @@ -316,7 +330,7 @@ This document shows the translation status of each locale file in the repository
-### ![uk__UA](https://img.shields.io/badge/uk__UA-97.79%25-yellow) +### ![uk__UA](https://img.shields.io/badge/uk__UA-97.66%25-yellow)
Missing keys in uk_UA.axaml @@ -333,6 +347,7 @@ This document shows the translation status of each locale file in the repository - Text.CommitCM.CopySubject - Text.CommitMessageTextBox.SubjectCount - Text.ConfigureWorkspace.Name +- Text.Preferences.Git.IgnoreCRAtEOLInDiff - Text.Repository.ViewLogs - Text.ViewLogs - Text.ViewLogs.Clear @@ -343,7 +358,7 @@ This document shows the translation status of each locale file in the repository ### ![zh__CN](https://img.shields.io/badge/zh__CN-%E2%88%9A-brightgreen) -### ![zh__TW](https://img.shields.io/badge/zh__TW-99.09%25-yellow) +### ![zh__TW](https://img.shields.io/badge/zh__TW-98.96%25-yellow)
Missing keys in zh_TW.axaml @@ -355,5 +370,6 @@ This document shows the translation status of each locale file in the repository - Text.Bisect.Good - Text.Bisect.Skip - Text.Bisect.WaitingForRange +- Text.Preferences.Git.IgnoreCRAtEOLInDiff
\ No newline at end of file From 1386ca30e3bfb14e55e426eb466e3cc1b2b0938e Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 23 Apr 2025 20:52:41 +0800 Subject: [PATCH 024/392] fix: typo in conventional commit type (#1239) Signed-off-by: leo --- src/Models/ConventionalCommitType.cs | 35 ++++++++++++++-------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/Models/ConventionalCommitType.cs b/src/Models/ConventionalCommitType.cs index cd09453a..531a16c0 100644 --- a/src/Models/ConventionalCommitType.cs +++ b/src/Models/ConventionalCommitType.cs @@ -4,25 +4,24 @@ namespace SourceGit.Models { public class ConventionalCommitType { - public string Name { get; set; } = string.Empty; - public string Type { get; set; } = string.Empty; - public string Description { get; set; } = string.Empty; + public string Name { get; set; } + public string Type { get; set; } + public string Description { get; set; } - public static readonly List Supported = new List() - { - new ConventionalCommitType("Features", "feat", "Adding a new feature"), - new ConventionalCommitType("Bug Fixes", "fix", "Fixing a bug"), - new ConventionalCommitType("Work In Progress", "wip", "Still being developed and not yet complete"), - new ConventionalCommitType("Reverts", "revert", "Undoing a previous commit"), - new ConventionalCommitType("Code Refactoring", "refactor", "Restructuring code without changing its external behavior"), - new ConventionalCommitType("Performance Improvements", "pref", "Improves performance"), - new ConventionalCommitType("Builds", "build", "Changes that affect the build system or external dependencies"), - new ConventionalCommitType("Continuous Integrations", "ci", "Changes to CI configuration files and scripts"), - new ConventionalCommitType("Documentations", "docs", "Updating documentation"), - new ConventionalCommitType("Styles", "style", "Elements or code styles without changing the code logic"), - new ConventionalCommitType("Tests", "test", "Adding or updating tests"), - new ConventionalCommitType("Chores", "chore", "Other changes that don't modify src or test files"), - }; + public static readonly List Supported = [ + new("Features", "feat", "Adding a new feature"), + new("Bug Fixes", "fix", "Fixing a bug"), + new("Work In Progress", "wip", "Still being developed and not yet complete"), + new("Reverts", "revert", "Undoing a previous commit"), + new("Code Refactoring", "refactor", "Restructuring code without changing its external behavior"), + new("Performance Improvements", "perf", "Improves performance"), + new("Builds", "build", "Changes that affect the build system or external dependencies"), + new("Continuous Integrations", "ci", "Changes to CI configuration files and scripts"), + new("Documentations", "docs", "Updating documentation"), + new("Styles", "style", "Elements or code styles without changing the code logic"), + new("Tests", "test", "Adding or updating tests"), + new("Chores", "chore", "Other changes that don't modify src or test files"), + ]; public ConventionalCommitType(string name, string type, string description) { From 7e282b13fa30d2d9737e5261f3c9e05744bc3f92 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 23 Apr 2025 20:59:39 +0800 Subject: [PATCH 025/392] code_style: run `dotnet format` Signed-off-by: leo --- src/Models/Bisect.cs | 2 +- src/ViewModels/ScanRepositories.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Models/Bisect.cs b/src/Models/Bisect.cs index 8ef5ec78..3dd28681 100644 --- a/src/Models/Bisect.cs +++ b/src/Models/Bisect.cs @@ -13,7 +13,7 @@ namespace SourceGit.Models { None = 0, Good = 1, - Bad = 2, + Bad = 2, } public class Bisect diff --git a/src/ViewModels/ScanRepositories.cs b/src/ViewModels/ScanRepositories.cs index 09c3559f..307694ea 100644 --- a/src/ViewModels/ScanRepositories.cs +++ b/src/ViewModels/ScanRepositories.cs @@ -87,7 +87,7 @@ namespace SourceGit.ViewModels var subdirs = dir.GetDirectories("*", opts); foreach (var subdir in subdirs) { - if (subdir.Name.StartsWith(".", StringComparison.Ordinal) || + if (subdir.Name.StartsWith(".", StringComparison.Ordinal) || subdir.Name.Equals("node_modules", StringComparison.Ordinal)) continue; From ea680782fee8816924f179a20d4f3f76ed4d979b Mon Sep 17 00:00:00 2001 From: qiufengshe <172344058@qq.com> Date: Wed, 23 Apr 2025 21:14:41 +0800 Subject: [PATCH 026/392] perf: minimize temporary strings for better performance (#1240) (cherry picked from commit f4dad2bf551ead5640a500297a4a6f408aef1350) --- src/ViewModels/WorkingCopy.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ViewModels/WorkingCopy.cs b/src/ViewModels/WorkingCopy.cs index 6402878a..63d6df47 100644 --- a/src/ViewModels/WorkingCopy.cs +++ b/src/ViewModels/WorkingCopy.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; @@ -635,7 +635,7 @@ namespace SourceGit.ViewModels } else if (_inProgressContext is RevertInProgress revert) { - useTheirs.Header = App.Text("FileCM.ResolveUsing", revert.Head.SHA.Substring(0, 10) + " (revert)"); + useTheirs.Header = App.Text("FileCM.ResolveUsing", $"{revert.Head.SHA.AsSpan().Slice(0, 10)} (revert)"); useMine.Header = App.Text("FileCM.ResolveUsing", _repo.CurrentBranch.Name); } else if (_inProgressContext is MergeInProgress merge) @@ -771,7 +771,7 @@ namespace SourceGit.ViewModels byExtension.Header = App.Text("WorkingCopy.AddToGitIgnore.Extension", extension); byExtension.Click += (_, e) => { - Commands.GitIgnore.Add(_repo.FullPath, "*" + extension); + Commands.GitIgnore.Add(_repo.FullPath, $"*{extension}"); e.Handled = true; }; addToIgnore.Items.Add(byExtension); @@ -782,7 +782,7 @@ namespace SourceGit.ViewModels byExtensionInSameFolder.Click += (_, e) => { var dir = Path.GetDirectoryName(change.Path).Replace("\\", "/"); - Commands.GitIgnore.Add(_repo.FullPath, dir + "/*" + extension); + Commands.GitIgnore.Add(_repo.FullPath, $"{dir}/*{extension}"); e.Handled = true; }; addToIgnore.Items.Add(byExtensionInSameFolder); @@ -824,7 +824,7 @@ namespace SourceGit.ViewModels lfsTrackByExtension.Click += async (_, e) => { var log = _repo.CreateLog("Track LFS"); - var succ = await Task.Run(() => new Commands.LFS(_repo.FullPath).Track("*" + extension, false, log)); + var succ = await Task.Run(() => new Commands.LFS(_repo.FullPath).Track($"*{extension}", false, log)); if (succ) App.SendNotification(_repo.FullPath, $"Tracking all *{extension} files successfully!"); @@ -993,7 +993,7 @@ namespace SourceGit.ViewModels } else if (_inProgressContext is RevertInProgress revert) { - useTheirs.Header = App.Text("FileCM.ResolveUsing", revert.Head.SHA.Substring(0, 10) + " (revert)"); + useTheirs.Header = App.Text("FileCM.ResolveUsing", $"{revert.Head.SHA.AsSpan().Slice(0, 10)} (revert)"); useMine.Header = App.Text("FileCM.ResolveUsing", _repo.CurrentBranch.Name); } else if (_inProgressContext is MergeInProgress merge) @@ -1417,7 +1417,7 @@ namespace SourceGit.ViewModels var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); var prefixLen = home.EndsWith('/') ? home.Length - 1 : home.Length; if (gitTemplate.StartsWith(home, StringComparison.Ordinal)) - friendlyName = "~" + gitTemplate.Substring(prefixLen); + friendlyName = $"~{gitTemplate.AsSpan().Slice(prefixLen)}"; } var gitTemplateItem = new MenuItem(); From f73e0687a112046c6d0b455940823e5fad31415b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20J=2E=20Mart=C3=ADnez=20M=2E?= <56406225+jjesus-dev@users.noreply.github.com> Date: Wed, 23 Apr 2025 19:22:32 -0600 Subject: [PATCH 027/392] localization: update spanish translations (#1241) add missing translations. `Bisect`/`Bisecting` stays the same because they reference command names. --- src/Resources/Locales/es_ES.axaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml index 204cfc1a..82913d24 100644 --- a/src/Resources/Locales/es_ES.axaml +++ b/src/Resources/Locales/es_ES.axaml @@ -40,6 +40,13 @@ NO HAY ARCHIVOS ASUMIDOS COMO SIN CAMBIOS REMOVER ¡ARCHIVO BINARIO NO SOPORTADO! + Bisect + Abortar + Malo + Bisecting. ¿Es el HEAD actual bueno o malo? + Bueno + Saltar + Bisecting. Marcar el commit actual cómo bueno o malo y revisar otro. Blame ¡BLAME EN ESTE ARCHIVO NO SOPORTADO! Checkout ${0}$... @@ -103,8 +110,11 @@ Cherry-Pick ... Comparar con HEAD Comparar con Worktree + Autor + Committer Información SHA + Asunto Acción personalizada Rebase Interactivo ${0}$ hasta Aquí Merge a ${0}$ @@ -491,6 +501,7 @@ Email de usuario Email global del usuario git Habilitar --prune para fetch + Habilitar --ignore-cr-at-eol en diff Se requiere Git (>= 2.23.0) para esta aplicación Ruta de instalación Habilitar verificación HTTP SSL @@ -616,6 +627,7 @@ Ordenar Abrir en Terminal Usar tiempo relativo en las historias + Ver Logs WORKTREES AÑADIR WORKTREE PRUNE @@ -701,6 +713,10 @@ Submódulo: Usar opción --remote URL: + Logs + LIMPIAR TODO + Copiar + Borrar Advertencia Página de Bienvenida Crear Grupo From ad6ed1512b4c9f6411d73170807fb6f74d797e09 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 24 Apr 2025 01:22:43 +0000 Subject: [PATCH 028/392] doc: Update translation status and sort locale files --- TRANSLATION.md | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index 9e590d21..a81ae006 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -51,29 +51,7 @@ This document shows the translation status of each locale file in the repository
-### ![es__ES](https://img.shields.io/badge/es__ES-97.92%25-yellow) - -
-Missing keys in es_ES.axaml - -- Text.Bisect -- Text.Bisect.Abort -- Text.Bisect.Bad -- Text.Bisect.Detecting -- Text.Bisect.Good -- Text.Bisect.Skip -- Text.Bisect.WaitingForRange -- Text.CommitCM.CopyAuthor -- Text.CommitCM.CopyCommitter -- Text.CommitCM.CopySubject -- Text.Preferences.Git.IgnoreCRAtEOLInDiff -- Text.Repository.ViewLogs -- Text.ViewLogs -- Text.ViewLogs.Clear -- Text.ViewLogs.CopyLog -- Text.ViewLogs.Delete - -
+### ![es__ES](https://img.shields.io/badge/es__ES-%E2%88%9A-brightgreen) ### ![fr__FR](https://img.shields.io/badge/fr__FR-96.49%25-yellow) From 65908126349a44de5aba0b79874ecd43321e659c Mon Sep 17 00:00:00 2001 From: leo Date: Thu, 24 Apr 2025 10:00:07 +0800 Subject: [PATCH 029/392] localization: update translations for Chinese Signed-off-by: leo --- src/Resources/Locales/zh_CN.axaml | 6 +++--- src/Resources/Locales/zh_TW.axaml | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index db77a3ed..39bf38e4 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -43,9 +43,9 @@ 二分定位(bisect) 终止 标记错误 - 二分定位进行中。当前提交是 '正确' 还是 '错误'? + 二分定位进行中。当前提交是 '正确' 还是 '错误' ? 标记正确 - 该提交无法判定 + 无法判定 二分定位进行中。请标记当前的提交是 '正确' 还是 '错误',然后检出另一个提交。 逐行追溯(blame) 选中文件不支持该操作!!! @@ -501,7 +501,7 @@ 邮箱 默认GIT用户邮箱 拉取更新时启用修剪(--prune) - 文件对比时默认启用 --ignore-cr-at-eol 选项 + 对比文件时,默认忽略换行符变更 (--ignore-cr-at-eol) 本软件要求GIT最低版本为2.23.0 安装路径 启用HTTP SSL验证 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 3a10f6ca..4e2b9e20 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -40,6 +40,13 @@ 沒有不追蹤變更的檔案 移除 二進位檔案不支援該操作! + 二元搜尋(bisect) + 中止 + 標記為錯誤 + 二元搜尋進行中。目前的提交是「良好」是「錯誤」? + 標記為良好 + 無法確認 + 二元搜尋進行中。請標記目前的提交為「良好」或「錯誤」,然後檢查另一個。。 逐行溯源 (blame) 所選擇的檔案不支援該操作! 簽出 (checkout) ${0}$... @@ -494,6 +501,7 @@ 電子郵件 預設 Git 使用者電子郵件 拉取變更時進行清理 (--prune) + 對比檔案時,預設忽略行尾的 CR 變更 (--ignore-cr-at-eol) 本軟體要求 Git 最低版本為 2.23.0 安裝路徑 啟用 HTTP SSL 驗證 From c51938164565d2ef265e2a11668a56a164f7d435 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 24 Apr 2025 02:00:26 +0000 Subject: [PATCH 030/392] doc: Update translation status and sort locale files --- TRANSLATION.md | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index a81ae006..e2693e64 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -336,18 +336,4 @@ This document shows the translation status of each locale file in the repository ### ![zh__CN](https://img.shields.io/badge/zh__CN-%E2%88%9A-brightgreen) -### ![zh__TW](https://img.shields.io/badge/zh__TW-98.96%25-yellow) - -
-Missing keys in zh_TW.axaml - -- Text.Bisect -- Text.Bisect.Abort -- Text.Bisect.Bad -- Text.Bisect.Detecting -- Text.Bisect.Good -- Text.Bisect.Skip -- Text.Bisect.WaitingForRange -- Text.Preferences.Git.IgnoreCRAtEOLInDiff - -
\ No newline at end of file +### ![zh__TW](https://img.shields.io/badge/zh__TW-%E2%88%9A-brightgreen) \ No newline at end of file From 9efbc7dd7a6181626c14b4e4bc75ad5f0be67d4a Mon Sep 17 00:00:00 2001 From: leo Date: Thu, 24 Apr 2025 10:01:57 +0800 Subject: [PATCH 031/392] localization: update translations for Chinese Signed-off-by: leo --- src/Resources/Locales/zh_TW.axaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 4e2b9e20..63052d35 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -40,13 +40,13 @@ 沒有不追蹤變更的檔案 移除 二進位檔案不支援該操作! - 二元搜尋(bisect) + 二元搜尋 (bisect) 中止 標記為錯誤 二元搜尋進行中。目前的提交是「良好」是「錯誤」? 標記為良好 無法確認 - 二元搜尋進行中。請標記目前的提交為「良好」或「錯誤」,然後檢查另一個。。 + 二元搜尋進行中。請標記目前的提交為「良好」或「錯誤」,然後簽出另一個提交。 逐行溯源 (blame) 所選擇的檔案不支援該操作! 簽出 (checkout) ${0}$... From 8c4362a98d757c4dab53a1a4f48a1b09e39c2448 Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 25 Apr 2025 13:24:13 +0800 Subject: [PATCH 032/392] feature: subject presenter supports inline codeblock Signed-off-by: leo --- src/Models/Commit.cs | 2 +- src/Models/{Hyperlink.cs => InlineElement.cs} | 17 +- src/Models/IssueTrackerRule.cs | 5 +- src/ViewModels/CommitDetail.cs | 20 +- src/Views/CommitMessagePresenter.cs | 14 +- src/Views/CommitSubjectPresenter.cs | 345 +++++++++++++----- src/Views/Histories.axaml | 5 +- 7 files changed, 289 insertions(+), 119 deletions(-) rename src/Models/{Hyperlink.cs => InlineElement.cs} (60%) diff --git a/src/Models/Commit.cs b/src/Models/Commit.cs index 0bad8376..1980e622 100644 --- a/src/Models/Commit.cs +++ b/src/Models/Commit.cs @@ -117,6 +117,6 @@ namespace SourceGit.Models public class CommitFullMessage { public string Message { get; set; } = string.Empty; - public List Links { get; set; } = []; + public List Inlines { get; set; } = []; } } diff --git a/src/Models/Hyperlink.cs b/src/Models/InlineElement.cs similarity index 60% rename from src/Models/Hyperlink.cs rename to src/Models/InlineElement.cs index 81dc980e..53761403 100644 --- a/src/Models/Hyperlink.cs +++ b/src/Models/InlineElement.cs @@ -1,18 +1,27 @@ namespace SourceGit.Models { - public class Hyperlink + public enum InlineElementType { + None = 0, + Keyword, + Link, + CommitSHA, + Code, + } + + public class InlineElement + { + public InlineElementType Type { get; set; } = InlineElementType.None; public int Start { get; set; } = 0; public int Length { get; set; } = 0; public string Link { get; set; } = ""; - public bool IsCommitSHA { get; set; } = false; - public Hyperlink(int start, int length, string link, bool isCommitSHA = false) + public InlineElement(InlineElementType type, int start, int length, string link) { + Type = type; Start = start; Length = length; Link = link; - IsCommitSHA = isCommitSHA; } public bool Intersect(int start, int length) diff --git a/src/Models/IssueTrackerRule.cs b/src/Models/IssueTrackerRule.cs index 29487a16..fe0fe8e0 100644 --- a/src/Models/IssueTrackerRule.cs +++ b/src/Models/IssueTrackerRule.cs @@ -46,7 +46,7 @@ namespace SourceGit.Models set => SetProperty(ref _urlTemplate, value); } - public void Matches(List outs, string message) + public void Matches(List outs, string message) { if (_regex == null || string.IsNullOrEmpty(_urlTemplate)) return; @@ -81,8 +81,7 @@ namespace SourceGit.Models link = link.Replace($"${j}", group.Value); } - var range = new Hyperlink(start, len, link); - outs.Add(range); + outs.Add(new InlineElement(InlineElementType.Link, start, len, link)); } } diff --git a/src/ViewModels/CommitDetail.cs b/src/ViewModels/CommitDetail.cs index 69b2c53d..6581d7bb 100644 --- a/src/ViewModels/CommitDetail.cs +++ b/src/ViewModels/CommitDetail.cs @@ -578,10 +578,10 @@ namespace SourceGit.ViewModels Task.Run(() => { var message = new Commands.QueryCommitFullMessage(_repo.FullPath, _commit.SHA).Result(); - var links = ParseLinksInMessage(message); + var inlines = ParseInlinesInMessage(message); if (!token.IsCancellationRequested) - Dispatcher.UIThread.Invoke(() => FullMessage = new Models.CommitFullMessage { Message = message, Links = links }); + Dispatcher.UIThread.Invoke(() => FullMessage = new Models.CommitFullMessage { Message = message, Inlines = inlines }); }); Task.Run(() => @@ -633,13 +633,13 @@ namespace SourceGit.ViewModels }); } - private List ParseLinksInMessage(string message) + private List ParseInlinesInMessage(string message) { - var links = new List(); + var inlines = new List(); if (_repo.Settings.IssueTrackerRules is { Count: > 0 } rules) { foreach (var rule in rules) - rule.Matches(links, message); + rule.Matches(inlines, message); } var matches = REG_SHA_FORMAT().Matches(message); @@ -652,7 +652,7 @@ namespace SourceGit.ViewModels var start = match.Index; var len = match.Length; var intersect = false; - foreach (var link in links) + foreach (var link in inlines) { if (link.Intersect(start, len)) { @@ -667,13 +667,13 @@ namespace SourceGit.ViewModels var sha = match.Groups[1].Value; var isCommitSHA = new Commands.IsCommitSHA(_repo.FullPath, sha).Result(); if (isCommitSHA) - links.Add(new Models.Hyperlink(start, len, sha, true)); + inlines.Add(new Models.InlineElement(Models.InlineElementType.CommitSHA, start, len, sha)); } - if (links.Count > 0) - links.Sort((l, r) => l.Start - r.Start); + if (inlines.Count > 0) + inlines.Sort((l, r) => l.Start - r.Start); - return links; + return inlines; } private void RefreshVisibleChanges() diff --git a/src/Views/CommitMessagePresenter.cs b/src/Views/CommitMessagePresenter.cs index c71c9687..0858640b 100644 --- a/src/Views/CommitMessagePresenter.cs +++ b/src/Views/CommitMessagePresenter.cs @@ -39,7 +39,7 @@ namespace SourceGit.Views if (string.IsNullOrEmpty(message)) return; - var links = FullMessage?.Links; + var links = FullMessage?.Inlines; if (links == null || links.Count == 0) { Inlines.Add(new Run(message)); @@ -54,7 +54,7 @@ namespace SourceGit.Views inlines.Add(new Run(message.Substring(pos, link.Start - pos))); var run = new Run(message.Substring(link.Start, link.Length)); - run.Classes.Add(link.IsCommitSHA ? "commit_link" : "issue_link"); + run.Classes.Add(link.Type == Models.InlineElementType.CommitSHA ? "commit_link" : "issue_link"); inlines.Add(run); pos = link.Start + link.Length; @@ -87,7 +87,7 @@ namespace SourceGit.Views scrollViewer.LineDown(); } } - else if (FullMessage is { Links: { Count: > 0 } links }) + else if (FullMessage is { Inlines: { Count: > 0 } links }) { 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)); @@ -106,7 +106,7 @@ namespace SourceGit.Views SetCurrentValue(CursorProperty, Cursor.Parse("Hand")); _lastHover = link; - if (!link.IsCommitSHA) + if (link.Type == Models.InlineElementType.Link) ToolTip.SetTip(this, link.Link); else ProcessHoverCommitLink(link); @@ -127,7 +127,7 @@ namespace SourceGit.Views var link = _lastHover.Link; e.Pointer.Capture(null); - if (_lastHover.IsCommitSHA) + if (_lastHover.Type == Models.InlineElementType.CommitSHA) { var parentView = this.FindAncestorOfType(); if (parentView is { DataContext: ViewModels.CommitDetail detail }) @@ -252,7 +252,7 @@ namespace SourceGit.Views ClearHoveredIssueLink(); } - private void ProcessHoverCommitLink(Models.Hyperlink link) + private void ProcessHoverCommitLink(Models.InlineElement link) { var sha = link.Link; @@ -301,7 +301,7 @@ namespace SourceGit.Views } } - private Models.Hyperlink _lastHover = null; + private Models.InlineElement _lastHover = null; private Dictionary _inlineCommits = new(); } } diff --git a/src/Views/CommitSubjectPresenter.cs b/src/Views/CommitSubjectPresenter.cs index 83d79fe4..bfa2d9ea 100644 --- a/src/Views/CommitSubjectPresenter.cs +++ b/src/Views/CommitSubjectPresenter.cs @@ -1,18 +1,72 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Text.RegularExpressions; + using Avalonia; using Avalonia.Collections; using Avalonia.Controls; -using Avalonia.Controls.Documents; using Avalonia.Input; using Avalonia.Media; -using Avalonia.Media.TextFormatting; namespace SourceGit.Views { - public partial class CommitSubjectPresenter : TextBlock + public partial class CommitSubjectPresenter : Control { + public static readonly StyledProperty FontFamilyProperty = + AvaloniaProperty.Register(nameof(FontFamily)); + + public FontFamily FontFamily + { + get => GetValue(FontFamilyProperty); + set => SetValue(FontFamilyProperty, value); + } + + public static readonly StyledProperty CodeFontFamilyProperty = + AvaloniaProperty.Register(nameof(CodeFontFamily)); + + public FontFamily CodeFontFamily + { + get => GetValue(CodeFontFamilyProperty); + set => SetValue(CodeFontFamilyProperty, value); + } + + public static readonly StyledProperty FontSizeProperty = + TextBlock.FontSizeProperty.AddOwner(); + + public double FontSize + { + get => GetValue(FontSizeProperty); + set => SetValue(FontSizeProperty, value); + } + + public static readonly StyledProperty FontWeightProperty = + TextBlock.FontWeightProperty.AddOwner(); + + public FontWeight FontWeight + { + get => GetValue(FontWeightProperty); + set => SetValue(FontWeightProperty, value); + } + + public static readonly StyledProperty ForegroundProperty = + AvaloniaProperty.Register(nameof(Foreground), Brushes.White); + + public IBrush Foreground + { + get => GetValue(ForegroundProperty); + set => SetValue(ForegroundProperty, value); + } + + public static readonly StyledProperty LinkForegroundProperty = + AvaloniaProperty.Register(nameof(LinkForeground), Brushes.White); + + public IBrush LinkForeground + { + get => GetValue(LinkForegroundProperty); + set => SetValue(LinkForegroundProperty, value); + } + public static readonly StyledProperty SubjectProperty = AvaloniaProperty.Register(nameof(Subject)); @@ -31,7 +85,33 @@ namespace SourceGit.Views set => SetValue(IssueTrackerRulesProperty, value); } - protected override Type StyleKeyOverride => typeof(TextBlock); + public override void Render(DrawingContext context) + { + if (_needRebuildInlines) + { + _needRebuildInlines = false; + GenerateFormattedTextElements(); + } + + if (_inlines.Count == 0) + return; + + var height = Bounds.Height; + foreach (var inline in _inlines) + { + if (inline.Element is { Type: Models.InlineElementType.Code}) + { + var rect = new Rect(inline.X, (height - inline.Text.Height - 2) * 0.5, inline.Text.WidthIncludingTrailingWhitespace + 8, inline.Text.Height + 2); + var roundedRect = new RoundedRect(rect, new CornerRadius(4)); + context.DrawRectangle(new SolidColorBrush(new Color(52, 101, 108, 118)), null, roundedRect); + context.DrawText(inline.Text, new Point(inline.X + 4, (height - inline.Text.Height) * 0.5)); + } + else + { + context.DrawText(inline.Text, new Point(inline.X, (height - inline.Text.Height) * 0.5)); + } + } + } protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { @@ -39,85 +119,65 @@ namespace SourceGit.Views if (change.Property == SubjectProperty || change.Property == IssueTrackerRulesProperty) { - Inlines!.Clear(); - _matches = null; + _elements.Clear(); ClearHoveredIssueLink(); var subject = Subject; if (string.IsNullOrEmpty(subject)) + { + _inlines.Clear(); + InvalidateVisual(); return; + } var keywordMatch = REG_KEYWORD_FORMAT1().Match(subject); if (!keywordMatch.Success) keywordMatch = REG_KEYWORD_FORMAT2().Match(subject); + if (keywordMatch.Success) + _elements.Add(new Models.InlineElement(Models.InlineElementType.Keyword, 0, keywordMatch.Length, string.Empty)); + + var codeMatches = REG_INLINECODE_FORMAT().Matches(subject); + for (var i = 0; i < codeMatches.Count; i++) + { + var match = codeMatches[i]; + if (!match.Success) + continue; + + var start = match.Index; + var len = match.Length; + var intersect = false; + foreach (var exist in _elements) + { + if (exist.Intersect(start, len)) + { + intersect = true; + break; + } + } + + if (intersect) + continue; + + _elements.Add(new Models.InlineElement(Models.InlineElementType.Code, start, len, string.Empty)); + } + var rules = IssueTrackerRules ?? []; - var matches = new List(); foreach (var rule in rules) - rule.Matches(matches, subject); + rule.Matches(_elements, subject); - if (matches.Count == 0) - { - if (keywordMatch.Success) - { - Inlines.Add(new Run(subject.Substring(0, keywordMatch.Length)) { FontWeight = FontWeight.Bold }); - Inlines.Add(new Run(subject.Substring(keywordMatch.Length))); - } - else - { - Inlines.Add(new Run(subject)); - } - return; - } - - matches.Sort((l, r) => l.Start - r.Start); - _matches = matches; - - var inlines = new List(); - var pos = 0; - foreach (var match in matches) - { - if (match.Start > pos) - { - if (keywordMatch.Success && pos < keywordMatch.Length) - { - if (keywordMatch.Length < match.Start) - { - inlines.Add(new Run(subject.Substring(pos, keywordMatch.Length - pos)) { FontWeight = FontWeight.Bold }); - inlines.Add(new Run(subject.Substring(keywordMatch.Length, match.Start - keywordMatch.Length))); - } - else - { - inlines.Add(new Run(subject.Substring(pos, match.Start - pos)) { FontWeight = FontWeight.Bold }); - } - } - else - { - 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); - - pos = match.Start + match.Length; - } - - if (pos < subject.Length) - { - if (keywordMatch.Success && pos < keywordMatch.Length) - { - inlines.Add(new Run(subject.Substring(pos, keywordMatch.Length - pos)) { FontWeight = FontWeight.Bold }); - inlines.Add(new Run(subject.Substring(keywordMatch.Length))); - } - else - { - inlines.Add(new Run(subject.Substring(pos))); - } - } - - Inlines.AddRange(inlines); + _needRebuildInlines = true; + InvalidateVisual(); + } + else if (change.Property == FontFamilyProperty || + change.Property == CodeFontFamilyProperty || + change.Property == FontSizeProperty || + change.Property == FontWeightProperty || + change.Property == ForegroundProperty || + change.Property == LinkForegroundProperty) + { + _needRebuildInlines = true; + InvalidateVisual(); } } @@ -125,31 +185,23 @@ namespace SourceGit.Views { base.OnPointerMoved(e); - if (_matches != null) + var point = e.GetPosition(this); + foreach (var inline in _inlines) { - 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)); - var y = Math.Min(Math.Max(point.Y, 0), Math.Max(TextLayout.Height, 0)); - point = new Point(x, y); + if (inline.Element is not { Type: Models.InlineElementType.Link } link) + continue; - var textPosition = TextLayout.HitTestPoint(point).TextPosition; - foreach (var match in _matches) - { - if (!match.Intersect(textPosition, 1)) - continue; + if (inline.X > point.X || inline.X + inline.Text.WidthIncludingTrailingWhitespace < point.X) + continue; - if (match == _lastHover) - return; - - _lastHover = match; - SetCurrentValue(CursorProperty, Cursor.Parse("Hand")); - ToolTip.SetTip(this, match.Link); - e.Handled = true; - return; - } - - ClearHoveredIssueLink(); + _lastHover = link; + SetCurrentValue(CursorProperty, Cursor.Parse("Hand")); + ToolTip.SetTip(this, link.Link); + e.Handled = true; + return; } + + ClearHoveredIssueLink(); } protected override void OnPointerPressed(PointerPressedEventArgs e) @@ -166,6 +218,94 @@ namespace SourceGit.Views ClearHoveredIssueLink(); } + private void GenerateFormattedTextElements() + { + _inlines.Clear(); + + var subject = Subject; + if (string.IsNullOrEmpty(subject)) + return; + + var fontFamily = FontFamily; + var codeFontFamily = CodeFontFamily; + var fontSize = FontSize; + var foreground = Foreground; + var linkForeground = LinkForeground; + var typeface = new Typeface(fontFamily, FontStyle.Normal, FontWeight); + var codeTypeface = new Typeface(codeFontFamily, FontStyle.Normal, FontWeight); + var pos = 0; + var x = 0.0; + foreach (var elem in _elements) + { + if (elem.Start > pos) + { + var normal = new FormattedText( + subject.Substring(pos, elem.Start - pos), + CultureInfo.CurrentCulture, + FlowDirection.LeftToRight, + typeface, + fontSize, + foreground); + + _inlines.Add(new Inline(x, normal, null)); + x += normal.WidthIncludingTrailingWhitespace; + } + + if (elem.Type == Models.InlineElementType.Keyword) + { + var keyword = new FormattedText( + subject.Substring(elem.Start, elem.Length), + CultureInfo.CurrentCulture, + FlowDirection.LeftToRight, + new Typeface(fontFamily, FontStyle.Normal, FontWeight.Bold), + fontSize, + foreground); + _inlines.Add(new Inline(x, keyword, elem)); + x += keyword.WidthIncludingTrailingWhitespace; + } + else if (elem.Type == Models.InlineElementType.Link) + { + var link = new FormattedText( + subject.Substring(elem.Start, elem.Length), + CultureInfo.CurrentCulture, + FlowDirection.LeftToRight, + typeface, + fontSize, + linkForeground); + _inlines.Add(new Inline(x, link, elem)); + x += link.WidthIncludingTrailingWhitespace; + } + else if (elem.Type == Models.InlineElementType.Code) + { + var link = new FormattedText( + subject.Substring(elem.Start + 1, elem.Length - 2), + CultureInfo.CurrentCulture, + FlowDirection.LeftToRight, + codeTypeface, + fontSize, + foreground); + _inlines.Add(new Inline(x, link, elem)); + x += link.WidthIncludingTrailingWhitespace + 8; + } + + pos = elem.Start + elem.Length; + } + + if (pos < subject.Length) + { + var normal = new FormattedText( + subject.Substring(pos), + CultureInfo.CurrentCulture, + FlowDirection.LeftToRight, + typeface, + fontSize, + foreground); + + _inlines.Add(new Inline(x, normal, null)); + x += normal.WidthIncludingTrailingWhitespace; + } + } + private void ClearHoveredIssueLink() { if (_lastHover != null) @@ -176,13 +316,32 @@ namespace SourceGit.Views } } + [GeneratedRegex(@"`.*?`")] + private static partial Regex REG_INLINECODE_FORMAT(); + [GeneratedRegex(@"^\[[\w\s]+\]")] private static partial Regex REG_KEYWORD_FORMAT1(); [GeneratedRegex(@"^\S+([\<\(][\w\s_\-\*,]+[\>\)])?\!?\s?:\s")] private static partial Regex REG_KEYWORD_FORMAT2(); - private List _matches = null; - private Models.Hyperlink _lastHover = null; + private class Inline + { + public double X { get; set; } = 0; + public FormattedText Text { get; set; } = null; + public Models.InlineElement Element { get; set; } = null; + + public Inline(double x, FormattedText text, Models.InlineElement elem) + { + X = x; + Text = text; + Element = elem; + } + } + + private List _elements = []; + private List _inlines = []; + private Models.InlineElement _lastHover = null; + private bool _needRebuildInlines = false; } } diff --git a/src/Views/Histories.axaml b/src/Views/Histories.axaml index c6c43aa9..2f5e2ffd 100644 --- a/src/Views/Histories.axaml +++ b/src/Views/Histories.axaml @@ -160,7 +160,10 @@ Date: Fri, 25 Apr 2025 13:30:00 +0800 Subject: [PATCH 033/392] enhance: stop render next inline elements when it is out of bounds Signed-off-by: leo --- src/Views/CommitSubjectPresenter.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Views/CommitSubjectPresenter.cs b/src/Views/CommitSubjectPresenter.cs index bfa2d9ea..d1aeab3f 100644 --- a/src/Views/CommitSubjectPresenter.cs +++ b/src/Views/CommitSubjectPresenter.cs @@ -97,8 +97,12 @@ namespace SourceGit.Views return; var height = Bounds.Height; + var width = Bounds.Width; foreach (var inline in _inlines) { + if (inline.X > width) + return; + if (inline.Element is { Type: Models.InlineElementType.Code}) { var rect = new Rect(inline.X, (height - inline.Text.Height - 2) * 0.5, inline.Text.WidthIncludingTrailingWhitespace + 8, inline.Text.Height + 2); From a94c7f55cea48962374948f7557aafa30952fbb8 Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 25 Apr 2025 13:37:11 +0800 Subject: [PATCH 034/392] ux: remove tips in commit list Signed-off-by: leo --- src/Views/Histories.axaml | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/Views/Histories.axaml b/src/Views/Histories.axaml index 2f5e2ffd..e58d8349 100644 --- a/src/Views/Histories.axaml +++ b/src/Views/Histories.axaml @@ -126,11 +126,7 @@ - + - + Date: Fri, 25 Apr 2025 14:31:14 +0800 Subject: [PATCH 035/392] code_style: run `dotnet format` Signed-off-by: leo --- src/Views/CommitSubjectPresenter.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Views/CommitSubjectPresenter.cs b/src/Views/CommitSubjectPresenter.cs index d1aeab3f..d00d3951 100644 --- a/src/Views/CommitSubjectPresenter.cs +++ b/src/Views/CommitSubjectPresenter.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; @@ -91,7 +90,7 @@ namespace SourceGit.Views { _needRebuildInlines = false; GenerateFormattedTextElements(); - } + } if (_inlines.Count == 0) return; @@ -103,7 +102,7 @@ namespace SourceGit.Views if (inline.X > width) return; - if (inline.Element is { Type: Models.InlineElementType.Code}) + if (inline.Element is { Type: Models.InlineElementType.Code }) { var rect = new Rect(inline.X, (height - inline.Text.Height - 2) * 0.5, inline.Text.WidthIncludingTrailingWhitespace + 8, inline.Text.Height + 2); var roundedRect = new RoundedRect(rect, new CornerRadius(4)); @@ -129,7 +128,7 @@ namespace SourceGit.Views var subject = Subject; if (string.IsNullOrEmpty(subject)) { - _inlines.Clear(); + _needRebuildInlines = true; InvalidateVisual(); return; } From 00e56ce9d139da58493f3193eb70ec3d510f2577 Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 25 Apr 2025 15:56:37 +0800 Subject: [PATCH 036/392] fix: `System.NullReferenceException` raised after popup stop (success or not) running Signed-off-by: leo --- src/Views/PopupRunningStatus.axaml | 3 +-- src/Views/PopupRunningStatus.axaml.cs | 14 ++++---------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/Views/PopupRunningStatus.axaml b/src/Views/PopupRunningStatus.axaml index a8467415..6a0dbdb4 100644 --- a/src/Views/PopupRunningStatus.axaml +++ b/src/Views/PopupRunningStatus.axaml @@ -15,8 +15,7 @@
- Date: Fri, 25 Apr 2025 10:56:28 +0200 Subject: [PATCH 037/392] fix: Append UserName to the SourceGitIPCChannel NamedPipeServerStream to allow multiple users usage on the same server (#1244) (#1246) --- src/Models/IpcChannel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Models/IpcChannel.cs b/src/Models/IpcChannel.cs index 2ecfb771..d47a46bd 100644 --- a/src/Models/IpcChannel.cs +++ b/src/Models/IpcChannel.cs @@ -22,7 +22,7 @@ namespace SourceGit.Models _singletoneLock = File.Open(Path.Combine(Native.OS.DataDir, "process.lock"), FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); _isFirstInstance = true; _server = new NamedPipeServerStream( - "SourceGitIPCChannel", + "SourceGitIPCChannel" + Environment.UserName, PipeDirection.In, -1, PipeTransmissionMode.Byte, @@ -40,7 +40,7 @@ namespace SourceGit.Models { try { - using (var client = new NamedPipeClientStream(".", "SourceGitIPCChannel", PipeDirection.Out)) + using (var client = new NamedPipeClientStream(".", "SourceGitIPCChannel" + Environment.UserName, PipeDirection.Out, PipeOptions.Asynchronous | PipeOptions.CurrentUserOnly)) { client.Connect(1000); if (!client.IsConnected) From d44d2b977042ad2e1ded55b6ed52202fe5703a35 Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 25 Apr 2025 18:50:56 +0800 Subject: [PATCH 038/392] ux: use a outer border to hold tooltip of commit message in `FileHistories` view (#1232) Signed-off-by: leo --- src/Views/FileHistories.axaml | 9 +++------ src/Views/FileHistories.axaml.cs | 10 +++++----- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/Views/FileHistories.axaml b/src/Views/FileHistories.axaml index f597fc04..e7a5c072 100644 --- a/src/Views/FileHistories.axaml +++ b/src/Views/FileHistories.axaml @@ -93,12 +93,9 @@ - + + +
diff --git a/src/Views/FileHistories.axaml.cs b/src/Views/FileHistories.axaml.cs index a183182e..3631eb71 100644 --- a/src/Views/FileHistories.axaml.cs +++ b/src/Views/FileHistories.axaml.cs @@ -62,18 +62,18 @@ namespace SourceGit.Views private void OnCommitSubjectDataContextChanged(object sender, EventArgs e) { - if (sender is TextBlock textBlock) - ToolTip.SetTip(textBlock, null); + if (sender is Border border) + ToolTip.SetTip(border, null); } private void OnCommitSubjectPointerMoved(object sender, PointerEventArgs e) { - if (sender is TextBlock { DataContext: Models.Commit commit } textBlock && + if (sender is Border { DataContext: Models.Commit commit } border && DataContext is ViewModels.FileHistories vm) { - var tooltip = ToolTip.GetTip(textBlock); + var tooltip = ToolTip.GetTip(border); if (tooltip == null) - ToolTip.SetTip(textBlock, vm.GetCommitFullMessage(commit)); + ToolTip.SetTip(border, vm.GetCommitFullMessage(commit)); } } } From 91acf0a32a9cee70ef125cb5c8a88a8e4979a8b7 Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 25 Apr 2025 20:55:11 +0800 Subject: [PATCH 039/392] enhance: fore invalidate measure after data context of `BisectStateIndicator` changed Signed-off-by: leo --- src/Models/Bisect.cs | 4 +++- src/Views/BisectStateIndicator.cs | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Models/Bisect.cs b/src/Models/Bisect.cs index 3dd28681..d1021113 100644 --- a/src/Models/Bisect.cs +++ b/src/Models/Bisect.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; namespace SourceGit.Models { @@ -9,6 +10,7 @@ namespace SourceGit.Models Detecting, } + [Flags] public enum BisectCommitFlag { None = 0, diff --git a/src/Views/BisectStateIndicator.cs b/src/Views/BisectStateIndicator.cs index c93192a6..0a581f53 100644 --- a/src/Views/BisectStateIndicator.cs +++ b/src/Views/BisectStateIndicator.cs @@ -65,6 +65,12 @@ namespace SourceGit.Views RenderImpl(context, Brushes.Red, _bad, x); } + protected override void OnDataContextChanged(EventArgs e) + { + base.OnDataContextChanged(e); + InvalidateMeasure(); + } + protected override Size MeasureOverride(Size availableSize) { var desiredFlags = Models.BisectCommitFlag.None; From d2e688908c107e3daf5246be8bf2f6532a84eada Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 25 Apr 2025 21:26:21 +0800 Subject: [PATCH 040/392] ux: use different inline code background for different themes Signed-off-by: leo --- src/Resources/Themes.axaml | 3 +++ src/Views/CommitSubjectPresenter.cs | 15 ++++++++++++++- src/Views/Histories.axaml | 1 + 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/Resources/Themes.axaml b/src/Resources/Themes.axaml index ff4c0374..f33e71d2 100644 --- a/src/Resources/Themes.axaml +++ b/src/Resources/Themes.axaml @@ -25,6 +25,7 @@ #A7E1A7 #F19B9D #0000EE + #FFE5E5E5 @@ -51,6 +52,7 @@ #A0308D3C #A09F4247 #4DAAFC + #FF2E2E2E @@ -79,6 +81,7 @@ + fonts:Inter#Inter fonts:SourceGit#JetBrains Mono diff --git a/src/Views/CommitSubjectPresenter.cs b/src/Views/CommitSubjectPresenter.cs index d00d3951..abb08a3b 100644 --- a/src/Views/CommitSubjectPresenter.cs +++ b/src/Views/CommitSubjectPresenter.cs @@ -47,6 +47,15 @@ namespace SourceGit.Views get => GetValue(FontWeightProperty); set => SetValue(FontWeightProperty, value); } + + public static readonly StyledProperty InlineCodeBackgroundProperty = + AvaloniaProperty.Register(nameof(InlineCodeBackground), Brushes.Transparent); + + public IBrush InlineCodeBackground + { + get => GetValue(InlineCodeBackgroundProperty); + set => SetValue(InlineCodeBackgroundProperty, value); + } public static readonly StyledProperty ForegroundProperty = AvaloniaProperty.Register(nameof(Foreground), Brushes.White); @@ -106,7 +115,7 @@ namespace SourceGit.Views { var rect = new Rect(inline.X, (height - inline.Text.Height - 2) * 0.5, inline.Text.WidthIncludingTrailingWhitespace + 8, inline.Text.Height + 2); var roundedRect = new RoundedRect(rect, new CornerRadius(4)); - context.DrawRectangle(new SolidColorBrush(new Color(52, 101, 108, 118)), null, roundedRect); + context.DrawRectangle(InlineCodeBackground, null, roundedRect); context.DrawText(inline.Text, new Point(inline.X + 4, (height - inline.Text.Height) * 0.5)); } else @@ -182,6 +191,10 @@ namespace SourceGit.Views _needRebuildInlines = true; InvalidateVisual(); } + else if (change.Property == InlineCodeBackgroundProperty) + { + InvalidateVisual(); + } } protected override void OnPointerMoved(PointerEventArgs e) diff --git a/src/Views/Histories.axaml b/src/Views/Histories.axaml index e58d8349..568854fd 100644 --- a/src/Views/Histories.axaml +++ b/src/Views/Histories.axaml @@ -158,6 +158,7 @@ Date: Sun, 27 Apr 2025 04:38:09 +0300 Subject: [PATCH 041/392] locallization: update russian translate (#1248) --- src/Resources/Locales/ru_RU.axaml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index 35125369..a3fe2b3a 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -501,6 +501,7 @@ Электроная почта пользователя Общая электроная почта пользователя git Разрешить (--prune) при скачивании + Разрешить (--ignore-cr-at-eol) в различии Для работы программы требуется версия Git (>= 2.23.0) Путь установки Разрешить верификацию HTTP SSL From f39048df773b5f01dee85f153642bf1240ebb44a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 27 Apr 2025 01:38:20 +0000 Subject: [PATCH 042/392] doc: Update translation status and sort locale files --- TRANSLATION.md | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index e2693e64..21706eb2 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -264,14 +264,7 @@ This document shows the translation status of each locale file in the repository -### ![ru__RU](https://img.shields.io/badge/ru__RU-99.87%25-yellow) - -
-Missing keys in ru_RU.axaml - -- Text.Preferences.Git.IgnoreCRAtEOLInDiff - -
+### ![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen) ### ![ta__IN](https://img.shields.io/badge/ta__IN-96.49%25-yellow) From 21cb87cec5c27c0c4627379f44235ff27d08a076 Mon Sep 17 00:00:00 2001 From: Chiahong <36815907+ChiahongHong@users.noreply.github.com> Date: Sun, 27 Apr 2025 09:38:28 +0800 Subject: [PATCH 043/392] localization: update zh_TW.axaml (#1249) --- src/Resources/Locales/zh_TW.axaml | 38 +++++++++++++++---------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 63052d35..181adb69 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -40,13 +40,13 @@ 沒有不追蹤變更的檔案 移除 二進位檔案不支援該操作! - 二元搜尋 (bisect) + 二分搜尋 (bisect) 中止 標記為錯誤 - 二元搜尋進行中。目前的提交是「良好」是「錯誤」? + 二分搜尋進行中。目前的提交是「良好」是「錯誤」? 標記為良好 無法確認 - 二元搜尋進行中。請標記目前的提交為「良好」或「錯誤」,然後簽出另一個提交。 + 二分搜尋進行中。請標記目前的提交為「良好」或「錯誤」,然後簽出另一個提交。 逐行溯源 (blame) 所選擇的檔案不支援該操作! 簽出 (checkout) ${0}$... @@ -69,7 +69,7 @@ 重新命名 ${0}$... 切換上游分支... 分支比較 - 追蹤上游分支不存在或已刪除! + 追蹤上游分支不存在或已刪除! 位元組 取 消 重設檔案到上一版本 @@ -168,7 +168,7 @@ 啟用定時自動提取 (fetch) 遠端更新 分鐘 預設遠端存放庫 - 首選合併模式 + 預設合併模式 Issue 追蹤 新增符合 Azure DevOps 規則 新增符合 Gitee 議題規則 @@ -193,10 +193,10 @@ 顏色 名稱 啟動時還原上次開啟的存放庫 - 确认继续 + 確認繼續 未包含任何檔案變更! 您是否仍要提交 (--allow-empty)? - 自动暂存并提交 - 未包含任何檔案變更! 您是否仍要提交 (--allow-empty)或者自動暫存全部變更並提交? + 自動暫存並提交 + 未包含任何檔案變更! 您是否仍要提交 (--allow-empty) 或者自動暫存全部變更並提交? 產生約定式提交訊息 破壞性變更: 關閉的 Issue: @@ -257,7 +257,7 @@ 複製 檔案權限已變更 第一個差異 - 忽略空白符號變化和EOL + 忽略空白符號變化和 EOL 最後一個差異 LFS 物件變更 下一個差異 @@ -313,8 +313,8 @@ 取消暫存 從暫存中移除 {0} 個檔案 取消暫存選取的變更 - 使用我方版本 (checkout --ours) - 使用對方版本 (checkout --theirs) + 使用我方版本 (ours) + 使用對方版本 (theirs) 檔案歷史 檔案變更 檔案内容 @@ -472,7 +472,7 @@ 啟用串流輸出 外觀設定 預設字型 - 編輯器制表符寬度 + 編輯器 Tab 寬度 字型大小 預設 程式碼 @@ -627,7 +627,7 @@ 排序 在終端機中開啟 在提交列表中使用相對時間 - 檢視 GIT 指令的日誌 + 檢視 Git 指令記錄 工作區列表 新增工作區 清理 @@ -713,8 +713,8 @@ 子模組: 啟用 [--remote] 選項 存放庫網址: - 日誌清單 - 清除所有日誌 + 記錄 + 清除所有記錄 複製 刪除 警告 @@ -746,13 +746,13 @@ 觸發點擊事件 提交 (修改原始提交) 自動暫存全部變更並提交 - 您已暫存 {0} 檔案,但只顯示 {1} 檔案 ({2} 檔案被篩選器隱藏)。您要繼續嗎? - 檢測到衝突 + 您已暫存 {0} 個檔案,但只顯示 {1} 個檔案 ({2} 個檔案被篩選器隱藏)。您確定要繼續提交嗎? + 偵測到衝突 使用外部合併工具開啟 使用外部合併工具開啟 檔案衝突已解決 - 使用 MINE - 使用 THEIRS + 使用我方版本 (ours) + 使用對方版本 (theirs) 顯示未追蹤檔案 沒有提交訊息記錄 沒有可套用的提交訊息範本 From 951ea8f0887e9653279721dbb948f07926aa2a48 Mon Sep 17 00:00:00 2001 From: leo Date: Sun, 27 Apr 2025 11:20:44 +0800 Subject: [PATCH 044/392] fix: use subject as context menu item header to fix vertical alignment (#1251) Signed-off-by: leo --- src/ViewModels/WorkingCopy.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ViewModels/WorkingCopy.cs b/src/ViewModels/WorkingCopy.cs index 63d6df47..4d35b5c2 100644 --- a/src/ViewModels/WorkingCopy.cs +++ b/src/ViewModels/WorkingCopy.cs @@ -1449,9 +1449,11 @@ namespace SourceGit.ViewModels { for (int i = 0; i < historiesCount; i++) { - var message = _repo.Settings.CommitMessages[i]; + var message = _repo.Settings.CommitMessages[i].Trim().ReplaceLineEndings("\n"); + var subjectEndIdx = message.IndexOf('\n'); + var subject = subjectEndIdx > 0 ? message.Substring(0, subjectEndIdx) : message; var item = new MenuItem(); - item.Header = message; + item.Header = subject; item.Icon = App.CreateMenuIcon("Icons.Histories"); item.Click += (_, e) => { From 2e1cf76c8295b6f325348522fe850f183b166108 Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 28 Apr 2025 09:16:37 +0800 Subject: [PATCH 045/392] version: Release 2025.15 Signed-off-by: leo --- VERSION | 2 +- src/Views/CommitSubjectPresenter.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 89f9e970..70f2a59f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2025.14 \ No newline at end of file +2025.15 \ No newline at end of file diff --git a/src/Views/CommitSubjectPresenter.cs b/src/Views/CommitSubjectPresenter.cs index abb08a3b..f64323c0 100644 --- a/src/Views/CommitSubjectPresenter.cs +++ b/src/Views/CommitSubjectPresenter.cs @@ -47,7 +47,7 @@ namespace SourceGit.Views get => GetValue(FontWeightProperty); set => SetValue(FontWeightProperty, value); } - + public static readonly StyledProperty InlineCodeBackgroundProperty = AvaloniaProperty.Register(nameof(InlineCodeBackground), Brushes.Transparent); From 2698427cd03d52044822b63a2ad97ed2ced79908 Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 28 Apr 2025 11:57:36 +0800 Subject: [PATCH 046/392] fix: popup running animation did not update after switch back from another page Signed-off-by: leo --- src/ViewModels/ScanRepositories.cs | 12 ++++++------ src/Views/PopupRunningStatus.axaml.cs | 9 +++++++++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/ViewModels/ScanRepositories.cs b/src/ViewModels/ScanRepositories.cs index 307694ea..cec932eb 100644 --- a/src/ViewModels/ScanRepositories.cs +++ b/src/ViewModels/ScanRepositories.cs @@ -38,6 +38,12 @@ namespace SourceGit.ViewModels IgnoreInaccessible = true, }); + // Make sure this task takes at least 0.5s to avoid that the popup panel do not disappear very quickly. + var remain = 500 - (int)watch.Elapsed.TotalMilliseconds; + watch.Stop(); + if (remain > 0) + Task.Delay(remain).Wait(); + Dispatcher.UIThread.Invoke(() => { var normalizedRoot = rootDir.FullName.Replace("\\", "/"); @@ -61,12 +67,6 @@ namespace SourceGit.ViewModels Welcome.Instance.Refresh(); }); - // Make sure this task takes at least 0.5s to avoid that the popup panel do not disappear very quickly. - var remain = 500 - (int)watch.Elapsed.TotalMilliseconds; - watch.Stop(); - if (remain > 0) - Task.Delay(remain).Wait(); - return true; }); } diff --git a/src/Views/PopupRunningStatus.axaml.cs b/src/Views/PopupRunningStatus.axaml.cs index 8a59d87b..05258799 100644 --- a/src/Views/PopupRunningStatus.axaml.cs +++ b/src/Views/PopupRunningStatus.axaml.cs @@ -21,6 +21,15 @@ namespace SourceGit.Views InitializeComponent(); } + protected override void OnLoaded(RoutedEventArgs e) + { + base.OnLoaded(e); + + _isUnloading = false; + if (IsVisible) + StartAnim(); + } + protected override void OnUnloaded(RoutedEventArgs e) { _isUnloading = true; From 30d42b11e27de4c3b2ed0ad2e10d1f51447eb2f6 Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 28 Apr 2025 16:19:40 +0800 Subject: [PATCH 047/392] enhance: wait a while after branch changed (#1254) Signed-off-by: leo --- src/ViewModels/Checkout.cs | 3 +++ src/ViewModels/CreateBranch.cs | 6 ++++++ src/ViewModels/RenameBranch.cs | 7 +++++++ 3 files changed, 16 insertions(+) diff --git a/src/ViewModels/Checkout.cs b/src/ViewModels/Checkout.cs index 5a51e2d1..a96ac79a 100644 --- a/src/ViewModels/Checkout.cs +++ b/src/ViewModels/Checkout.cs @@ -62,6 +62,8 @@ namespace SourceGit.ViewModels CallUIThread(() => { + ProgressDescription = "Waiting for branch updated..."; + var b = _repo.Branches.Find(x => x.IsLocal && x.Name == Branch); if (b != null && _repo.HistoriesFilterMode == Models.FilterMode.Included) _repo.SetBranchFilterMode(b, Models.FilterMode.Included, true, false); @@ -70,6 +72,7 @@ namespace SourceGit.ViewModels _repo.SetWatcherEnabled(true); }); + Task.Delay(400).Wait(); return rs; }); } diff --git a/src/ViewModels/CreateBranch.cs b/src/ViewModels/CreateBranch.cs index d51a709d..dd190e18 100644 --- a/src/ViewModels/CreateBranch.cs +++ b/src/ViewModels/CreateBranch.cs @@ -150,6 +150,12 @@ namespace SourceGit.ViewModels _repo.SetWatcherEnabled(true); }); + if (CheckoutAfterCreated) + { + CallUIThread(() => ProgressDescription = "Waiting for branch updated..."); + Task.Delay(400).Wait(); + } + return true; }); } diff --git a/src/ViewModels/RenameBranch.cs b/src/ViewModels/RenameBranch.cs index d730c83e..22cd2688 100644 --- a/src/ViewModels/RenameBranch.cs +++ b/src/ViewModels/RenameBranch.cs @@ -58,12 +58,15 @@ namespace SourceGit.ViewModels return Task.Run(() => { + var isCurrent = Target.IsCurrent; var oldName = Target.FullName; var succ = Commands.Branch.Rename(_repo.FullPath, Target.Name, fixedName, log); log.Complete(); CallUIThread(() => { + ProgressDescription = "Waiting for branch updated..."; + if (succ) { foreach (var filter in _repo.Settings.HistoriesFilters) @@ -80,6 +83,10 @@ namespace SourceGit.ViewModels _repo.MarkBranchesDirtyManually(); _repo.SetWatcherEnabled(true); }); + + if (isCurrent) + Task.Delay(400).Wait(); + return succ; }); } From 226bc434f5fd75f4c82f7ed11c9b9a3d04db3663 Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 28 Apr 2025 16:53:51 +0800 Subject: [PATCH 048/392] ux: make log window resizable (#1253) Signed-off-by: leo --- src/ViewModels/CommandLog.cs | 7 +------ src/Views/ViewLogs.axaml | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/ViewModels/CommandLog.cs b/src/ViewModels/CommandLog.cs index 4286b93f..15f48e70 100644 --- a/src/ViewModels/CommandLog.cs +++ b/src/ViewModels/CommandLog.cs @@ -13,16 +13,11 @@ namespace SourceGit.ViewModels private set; } = string.Empty; - public DateTime Time + public DateTime StartTime { get; } = DateTime.Now; - public string TimeStr - { - get => Time.ToString("T"); - } - public bool IsComplete { get; diff --git a/src/Views/ViewLogs.axaml b/src/Views/ViewLogs.axaml index ccdd1f4d..ed66f9a9 100644 --- a/src/Views/ViewLogs.axaml +++ b/src/Views/ViewLogs.axaml @@ -12,7 +12,7 @@ Title="{DynamicResource Text.ViewLogs}" Icon="/App.ico" Width="800" Height="500" - CanResize="False" + CanResize="True" WindowStartupLocation="CenterOwner"> @@ -38,7 +38,13 @@ - + + + + + + + @@ -103,6 +109,12 @@ Fill="{DynamicResource Brush.FG2}" IsVisible="{Binding Logs.Count, Converter={x:Static c:IntConverters.IsZero}}"/> + + Date: Tue, 29 Apr 2025 09:14:24 +0800 Subject: [PATCH 049/392] fix: empty dialog when generating commit message with AI (#1257) Signed-off-by: leo --- src/Views/AIAssistant.axaml | 4 ++-- src/Views/AIAssistant.axaml.cs | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/Views/AIAssistant.axaml b/src/Views/AIAssistant.axaml index c9a37f3b..96ce56ec 100644 --- a/src/Views/AIAssistant.axaml +++ b/src/Views/AIAssistant.axaml @@ -39,12 +39,12 @@ + Background="{DynamicResource Brush.Contents}" + Content="{Binding Text}"/> diff --git a/src/Views/AIAssistant.axaml.cs b/src/Views/AIAssistant.axaml.cs index f8519b7c..f865cabe 100644 --- a/src/Views/AIAssistant.axaml.cs +++ b/src/Views/AIAssistant.axaml.cs @@ -15,6 +15,15 @@ namespace SourceGit.Views { public class AIResponseView : TextEditor { + public static readonly StyledProperty ContentProperty = + AvaloniaProperty.Register(nameof(Content), string.Empty); + + public string Content + { + get => GetValue(ContentProperty); + set => SetValue(ContentProperty, value); + } + protected override Type StyleKeyOverride => typeof(TextEditor); public AIResponseView() : base(new TextArea(), new TextDocument()) @@ -58,6 +67,16 @@ namespace SourceGit.Views GC.Collect(); } + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == ContentProperty) + { + Text = Content; + } + } + private void OnTextViewContextRequested(object sender, ContextRequestedEventArgs e) { var selected = SelectedText; From 53a55467f1196daadaf4f536359724cfc408d283 Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 29 Apr 2025 09:27:09 +0800 Subject: [PATCH 050/392] enhance: ignore submodule changes when deal with local changes before pull/checkout/create branch (#1256) Signed-off-by: leo --- src/Commands/CountLocalChangesWithoutUntracked.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Commands/CountLocalChangesWithoutUntracked.cs b/src/Commands/CountLocalChangesWithoutUntracked.cs index 0ba508f8..a704f313 100644 --- a/src/Commands/CountLocalChangesWithoutUntracked.cs +++ b/src/Commands/CountLocalChangesWithoutUntracked.cs @@ -8,7 +8,7 @@ namespace SourceGit.Commands { WorkingDirectory = repo; Context = repo; - Args = "--no-optional-locks status -uno --ignore-submodules=dirty --porcelain"; + Args = "--no-optional-locks status -uno --ignore-submodules=all --porcelain"; } public int Result() From 48bb8e91dea42f005c7841d3c4d2c30131e647f1 Mon Sep 17 00:00:00 2001 From: qiufengshe <172344058@qq.com> Date: Tue, 29 Apr 2025 09:33:14 +0800 Subject: [PATCH 051/392] perf: minimize temporary strings for better performance (#1255) --- src/Converters/PathConverters.cs | 4 ++-- src/Models/DiffResult.cs | 7 ++++--- src/Models/Worktree.cs | 9 +++++---- src/ViewModels/Archive.cs | 5 +++-- src/ViewModels/Blame.cs | 5 +++-- src/ViewModels/Launcher.cs | 4 ++-- src/Views/Histories.axaml.cs | 2 +- src/Views/TextDiffView.axaml.cs | 22 +++++++++++++++------- 8 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/Converters/PathConverters.cs b/src/Converters/PathConverters.cs index dd7cfa49..44fd7188 100644 --- a/src/Converters/PathConverters.cs +++ b/src/Converters/PathConverters.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using Avalonia.Data.Converters; @@ -22,7 +22,7 @@ namespace SourceGit.Converters var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); var prefixLen = home.EndsWith('/') ? home.Length - 1 : home.Length; if (v.StartsWith(home, StringComparison.Ordinal)) - return "~" + v.Substring(prefixLen); + return $"~{v.AsSpan().Slice(prefixLen)}"; return v; }); diff --git a/src/Models/DiffResult.cs b/src/Models/DiffResult.cs index e7d17994..ffb977bf 100644 --- a/src/Models/DiffResult.cs +++ b/src/Models/DiffResult.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; @@ -147,9 +148,9 @@ namespace SourceGit.Models public void GenerateNewPatchFromSelection(Change change, string fileBlobGuid, TextDiffSelection selection, bool revert, string output) { var isTracked = !string.IsNullOrEmpty(fileBlobGuid); - var fileGuid = isTracked ? fileBlobGuid.Substring(0, 8) : "00000000"; + var fileGuid = isTracked ? fileBlobGuid.AsSpan().Slice(0, 8) : "00000000".AsSpan(); - var builder = new StringBuilder(); + var builder = new StringBuilder(512); builder.Append("diff --git a/").Append(change.Path).Append(" b/").Append(change.Path).Append('\n'); if (!revert && !isTracked) builder.Append("new file mode 100644\n"); diff --git a/src/Models/Worktree.cs b/src/Models/Worktree.cs index bc40e320..81f58545 100644 --- a/src/Models/Worktree.cs +++ b/src/Models/Worktree.cs @@ -1,4 +1,5 @@ -using CommunityToolkit.Mvvm.ComponentModel; +using System; +using CommunityToolkit.Mvvm.ComponentModel; namespace SourceGit.Models { @@ -22,12 +23,12 @@ namespace SourceGit.Models get { if (IsDetached) - return $"deteched HEAD at {Head.Substring(10)}"; + return $"deteched HEAD at {Head.AsSpan().Slice(10)}"; - if (Branch.StartsWith("refs/heads/", System.StringComparison.Ordinal)) + if (Branch.StartsWith("refs/heads/", StringComparison.Ordinal)) return Branch.Substring(11); - if (Branch.StartsWith("refs/remotes/", System.StringComparison.Ordinal)) + if (Branch.StartsWith("refs/remotes/", StringComparison.Ordinal)) return Branch.Substring(13); return Branch; diff --git a/src/ViewModels/Archive.cs b/src/ViewModels/Archive.cs index 7ce0d968..a8a06e2c 100644 --- a/src/ViewModels/Archive.cs +++ b/src/ViewModels/Archive.cs @@ -1,4 +1,5 @@ -using System.ComponentModel.DataAnnotations; +using System; +using System.ComponentModel.DataAnnotations; using System.IO; using System.Threading.Tasks; @@ -31,7 +32,7 @@ namespace SourceGit.ViewModels { _repo = repo; _revision = commit.SHA; - _saveFile = $"archive-{commit.SHA.Substring(0, 10)}.zip"; + _saveFile = $"archive-{commit.SHA.AsSpan().Slice(0, 10)}.zip"; BasedOn = commit; } diff --git a/src/ViewModels/Blame.cs b/src/ViewModels/Blame.cs index 7cfa8eac..b76db66d 100644 --- a/src/ViewModels/Blame.cs +++ b/src/ViewModels/Blame.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Threading.Tasks; using Avalonia.Threading; @@ -30,7 +31,7 @@ namespace SourceGit.ViewModels { _repo = repo; - Title = $"{file} @ {revision.Substring(0, 10)}"; + Title = $"{file} @ {revision.AsSpan().Slice(0, 10)}"; Task.Run(() => { var result = new Commands.Blame(repo, file, revision).Result(); diff --git a/src/ViewModels/Launcher.cs b/src/ViewModels/Launcher.cs index 84ba96e4..0a465cc4 100644 --- a/src/ViewModels/Launcher.cs +++ b/src/ViewModels/Launcher.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using Avalonia.Collections; @@ -584,7 +584,7 @@ namespace SourceGit.ViewModels var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); var prefixLen = home.EndsWith('/') ? home.Length - 1 : home.Length; if (path.StartsWith(home, StringComparison.Ordinal)) - path = "~" + path.Substring(prefixLen); + path = $"~{path.AsSpan().Slice(prefixLen)}"; } Title = $"[{workspace}] {name} ({path})"; diff --git a/src/Views/Histories.axaml.cs b/src/Views/Histories.axaml.cs index 89c68934..5584b106 100644 --- a/src/Views/Histories.axaml.cs +++ b/src/Views/Histories.axaml.cs @@ -205,7 +205,7 @@ namespace SourceGit.Views foreach (var item in selected) { if (item is Models.Commit commit) - builder.AppendLine($"{commit.SHA.Substring(0, 10)} - {commit.Subject}"); + builder.AppendLine($"{commit.SHA.AsSpan().Slice(0, 10)} - {commit.Subject}"); } App.CopyText(builder.ToString()); diff --git a/src/Views/TextDiffView.axaml.cs b/src/Views/TextDiffView.axaml.cs index ad2f8cea..d2d9622d 100644 --- a/src/Views/TextDiffView.axaml.cs +++ b/src/Views/TextDiffView.axaml.cs @@ -126,7 +126,7 @@ namespace SourceGit.Views typeface, presenter.FontSize, presenter.Foreground); - context.DrawText(txt, new Point(Bounds.Width - txt.Width, y - txt.Height * 0.5)); + context.DrawText(txt, new Point(Bounds.Width - txt.Width, y - (txt.Height * 0.5))); } } } @@ -212,7 +212,7 @@ namespace SourceGit.Views } if (indicator != null) - context.DrawText(indicator, new Point(0, y - indicator.Height * 0.5)); + context.DrawText(indicator, new Point(0, y - (indicator.Height * 0.5))); } } } @@ -1047,7 +1047,8 @@ namespace SourceGit.Views // The first selected line (partial selection) if (i == startIdx && startPosition.Column > 1) { - builder.AppendLine(line.Content.Substring(startPosition.Column - 1)); + builder.Append(line.Content.AsSpan().Slice(startPosition.Column - 1)); + builder.Append(Environment.NewLine); continue; } @@ -1061,7 +1062,14 @@ namespace SourceGit.Views // For the last line (selection range is within original source) if (i == endIdx) { - builder.Append(endPosition.Column - 1 < line.Content.Length ? line.Content.Substring(0, endPosition.Column - 1) : line.Content); + if (endPosition.Column - 1 < line.Content.Length) + { + builder.Append(line.Content.AsSpan().Slice(0, endPosition.Column - 1)); + } + else + { + builder.Append(line.Content); + } break; } @@ -1246,12 +1254,12 @@ namespace SourceGit.Views var textDiff = DataContext as Models.TextDiff; if (textDiff != null) { - var builder = new StringBuilder(); + var builder = new StringBuilder(512); foreach (var line in textDiff.Lines) { if (line.Content.Length > 10000) { - builder.Append(line.Content.Substring(0, 1000)); + builder.Append(line.Content.AsSpan().Slice(0, 1000)); builder.Append($"...({line.Content.Length - 1000} character trimmed)"); } else @@ -1741,7 +1749,7 @@ namespace SourceGit.Views } var top = chunk.Y + (chunk.Height >= 36 ? 16 : 4); - var right = (chunk.Combined || !chunk.IsOldSide) ? 16 : v.Bounds.Width * 0.5f + 16; + var right = (chunk.Combined || !chunk.IsOldSide) ? 16 : (v.Bounds.Width * 0.5f) + 16; v.Popup.Margin = new Thickness(0, top, right, 0); v.Popup.IsVisible = true; }); From 825b74c2a3fbd49d595fdfc905ebceb1f93e6f91 Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 29 Apr 2025 09:44:06 +0800 Subject: [PATCH 052/392] refactor: use `String.AsSpan(int, int)` instead of `String.AsSpan().Slice(int, int)` Signed-off-by: leo --- src/Commands/Diff.cs | 6 +++--- src/Commands/Statistics.cs | 2 +- src/Converters/PathConverters.cs | 2 +- src/Models/AvatarManager.cs | 2 +- src/Models/DiffResult.cs | 4 ++-- src/Models/Worktree.cs | 2 +- src/ViewModels/Archive.cs | 2 +- src/ViewModels/Blame.cs | 2 +- src/ViewModels/Launcher.cs | 2 +- src/ViewModels/WorkingCopy.cs | 6 +++--- src/Views/Histories.axaml.cs | 2 +- src/Views/TextDiffView.axaml.cs | 8 ++++---- 12 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/Commands/Diff.cs b/src/Commands/Diff.cs index a60f4cc5..a354b816 100644 --- a/src/Commands/Diff.cs +++ b/src/Commands/Diff.cs @@ -105,7 +105,7 @@ namespace SourceGit.Commands } else if (line.StartsWith("-size ", StringComparison.Ordinal)) { - _result.LFSDiff.Old.Size = long.Parse(line.AsSpan().Slice(6)); + _result.LFSDiff.Old.Size = long.Parse(line.AsSpan(6)); } } else if (ch == '+') @@ -116,12 +116,12 @@ namespace SourceGit.Commands } else if (line.StartsWith("+size ", StringComparison.Ordinal)) { - _result.LFSDiff.New.Size = long.Parse(line.AsSpan().Slice(6)); + _result.LFSDiff.New.Size = long.Parse(line.AsSpan(6)); } } else if (line.StartsWith(" size ", StringComparison.Ordinal)) { - _result.LFSDiff.New.Size = _result.LFSDiff.Old.Size = long.Parse(line.AsSpan().Slice(6)); + _result.LFSDiff.New.Size = _result.LFSDiff.Old.Size = long.Parse(line.AsSpan(6)); } return; } diff --git a/src/Commands/Statistics.cs b/src/Commands/Statistics.cs index ea0b86de..e11c1740 100644 --- a/src/Commands/Statistics.cs +++ b/src/Commands/Statistics.cs @@ -40,7 +40,7 @@ namespace SourceGit.Commands if (dateEndIdx == -1) return; - var dateStr = line.AsSpan().Slice(0, dateEndIdx); + var dateStr = line.AsSpan(0, dateEndIdx); if (double.TryParse(dateStr, out var date)) statistics.AddCommit(line.Substring(dateEndIdx + 1), date); } diff --git a/src/Converters/PathConverters.cs b/src/Converters/PathConverters.cs index 44fd7188..ac1e61e5 100644 --- a/src/Converters/PathConverters.cs +++ b/src/Converters/PathConverters.cs @@ -22,7 +22,7 @@ namespace SourceGit.Converters var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); var prefixLen = home.EndsWith('/') ? home.Length - 1 : home.Length; if (v.StartsWith(home, StringComparison.Ordinal)) - return $"~{v.AsSpan().Slice(prefixLen)}"; + return $"~{v.AsSpan(prefixLen)}"; return v; }); diff --git a/src/Models/AvatarManager.cs b/src/Models/AvatarManager.cs index a506d886..a9ef3eb4 100644 --- a/src/Models/AvatarManager.cs +++ b/src/Models/AvatarManager.cs @@ -196,7 +196,7 @@ namespace SourceGit.Models private string GetEmailHash(string email) { var lowered = email.ToLower(CultureInfo.CurrentCulture).Trim(); - var hash = MD5.HashData(Encoding.Default.GetBytes(lowered).AsSpan()); + var hash = MD5.HashData(Encoding.Default.GetBytes(lowered)); var builder = new StringBuilder(hash.Length * 2); foreach (var c in hash) builder.Append(c.ToString("x2")); diff --git a/src/Models/DiffResult.cs b/src/Models/DiffResult.cs index ffb977bf..75294057 100644 --- a/src/Models/DiffResult.cs +++ b/src/Models/DiffResult.cs @@ -148,9 +148,9 @@ namespace SourceGit.Models public void GenerateNewPatchFromSelection(Change change, string fileBlobGuid, TextDiffSelection selection, bool revert, string output) { var isTracked = !string.IsNullOrEmpty(fileBlobGuid); - var fileGuid = isTracked ? fileBlobGuid.AsSpan().Slice(0, 8) : "00000000".AsSpan(); + var fileGuid = isTracked ? fileBlobGuid : "00000000"; - var builder = new StringBuilder(512); + var builder = new StringBuilder(); builder.Append("diff --git a/").Append(change.Path).Append(" b/").Append(change.Path).Append('\n'); if (!revert && !isTracked) builder.Append("new file mode 100644\n"); diff --git a/src/Models/Worktree.cs b/src/Models/Worktree.cs index 81f58545..3f954650 100644 --- a/src/Models/Worktree.cs +++ b/src/Models/Worktree.cs @@ -23,7 +23,7 @@ namespace SourceGit.Models get { if (IsDetached) - return $"deteched HEAD at {Head.AsSpan().Slice(10)}"; + return $"deteched HEAD at {Head.AsSpan(10)}"; if (Branch.StartsWith("refs/heads/", StringComparison.Ordinal)) return Branch.Substring(11); diff --git a/src/ViewModels/Archive.cs b/src/ViewModels/Archive.cs index a8a06e2c..a4a4f6eb 100644 --- a/src/ViewModels/Archive.cs +++ b/src/ViewModels/Archive.cs @@ -32,7 +32,7 @@ namespace SourceGit.ViewModels { _repo = repo; _revision = commit.SHA; - _saveFile = $"archive-{commit.SHA.AsSpan().Slice(0, 10)}.zip"; + _saveFile = $"archive-{commit.SHA.AsSpan(0, 10)}.zip"; BasedOn = commit; } diff --git a/src/ViewModels/Blame.cs b/src/ViewModels/Blame.cs index b76db66d..c04842e5 100644 --- a/src/ViewModels/Blame.cs +++ b/src/ViewModels/Blame.cs @@ -31,7 +31,7 @@ namespace SourceGit.ViewModels { _repo = repo; - Title = $"{file} @ {revision.AsSpan().Slice(0, 10)}"; + Title = $"{file} @ {revision.AsSpan(0, 10)}"; Task.Run(() => { var result = new Commands.Blame(repo, file, revision).Result(); diff --git a/src/ViewModels/Launcher.cs b/src/ViewModels/Launcher.cs index 0a465cc4..d9425059 100644 --- a/src/ViewModels/Launcher.cs +++ b/src/ViewModels/Launcher.cs @@ -584,7 +584,7 @@ namespace SourceGit.ViewModels var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); var prefixLen = home.EndsWith('/') ? home.Length - 1 : home.Length; if (path.StartsWith(home, StringComparison.Ordinal)) - path = $"~{path.AsSpan().Slice(prefixLen)}"; + path = $"~{path.AsSpan(prefixLen)}"; } Title = $"[{workspace}] {name} ({path})"; diff --git a/src/ViewModels/WorkingCopy.cs b/src/ViewModels/WorkingCopy.cs index 4d35b5c2..762f1176 100644 --- a/src/ViewModels/WorkingCopy.cs +++ b/src/ViewModels/WorkingCopy.cs @@ -635,7 +635,7 @@ namespace SourceGit.ViewModels } else if (_inProgressContext is RevertInProgress revert) { - useTheirs.Header = App.Text("FileCM.ResolveUsing", $"{revert.Head.SHA.AsSpan().Slice(0, 10)} (revert)"); + useTheirs.Header = App.Text("FileCM.ResolveUsing", $"{revert.Head.SHA.AsSpan(0, 10)} (revert)"); useMine.Header = App.Text("FileCM.ResolveUsing", _repo.CurrentBranch.Name); } else if (_inProgressContext is MergeInProgress merge) @@ -993,7 +993,7 @@ namespace SourceGit.ViewModels } else if (_inProgressContext is RevertInProgress revert) { - useTheirs.Header = App.Text("FileCM.ResolveUsing", $"{revert.Head.SHA.AsSpan().Slice(0, 10)} (revert)"); + useTheirs.Header = App.Text("FileCM.ResolveUsing", $"{revert.Head.SHA.AsSpan(0, 10)} (revert)"); useMine.Header = App.Text("FileCM.ResolveUsing", _repo.CurrentBranch.Name); } else if (_inProgressContext is MergeInProgress merge) @@ -1417,7 +1417,7 @@ namespace SourceGit.ViewModels var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); var prefixLen = home.EndsWith('/') ? home.Length - 1 : home.Length; if (gitTemplate.StartsWith(home, StringComparison.Ordinal)) - friendlyName = $"~{gitTemplate.AsSpan().Slice(prefixLen)}"; + friendlyName = $"~{gitTemplate.AsSpan(prefixLen)}"; } var gitTemplateItem = new MenuItem(); diff --git a/src/Views/Histories.axaml.cs b/src/Views/Histories.axaml.cs index 5584b106..d49d9735 100644 --- a/src/Views/Histories.axaml.cs +++ b/src/Views/Histories.axaml.cs @@ -205,7 +205,7 @@ namespace SourceGit.Views foreach (var item in selected) { if (item is Models.Commit commit) - builder.AppendLine($"{commit.SHA.AsSpan().Slice(0, 10)} - {commit.Subject}"); + builder.AppendLine($"{commit.SHA.AsSpan(0, 10)} - {commit.Subject}"); } App.CopyText(builder.ToString()); diff --git a/src/Views/TextDiffView.axaml.cs b/src/Views/TextDiffView.axaml.cs index d2d9622d..78a67bae 100644 --- a/src/Views/TextDiffView.axaml.cs +++ b/src/Views/TextDiffView.axaml.cs @@ -1047,7 +1047,7 @@ namespace SourceGit.Views // The first selected line (partial selection) if (i == startIdx && startPosition.Column > 1) { - builder.Append(line.Content.AsSpan().Slice(startPosition.Column - 1)); + builder.Append(line.Content.AsSpan(startPosition.Column - 1)); builder.Append(Environment.NewLine); continue; } @@ -1064,7 +1064,7 @@ namespace SourceGit.Views { if (endPosition.Column - 1 < line.Content.Length) { - builder.Append(line.Content.AsSpan().Slice(0, endPosition.Column - 1)); + builder.Append(line.Content.AsSpan(0, endPosition.Column - 1)); } else { @@ -1254,12 +1254,12 @@ namespace SourceGit.Views var textDiff = DataContext as Models.TextDiff; if (textDiff != null) { - var builder = new StringBuilder(512); + var builder = new StringBuilder(); foreach (var line in textDiff.Lines) { if (line.Content.Length > 10000) { - builder.Append(line.Content.AsSpan().Slice(0, 1000)); + builder.Append(line.Content.AsSpan(0, 1000)); builder.Append($"...({line.Content.Length - 1000} character trimmed)"); } else From 63803c9b88c768fd89316429ffc9ceed148f244f Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 29 Apr 2025 16:36:05 +0800 Subject: [PATCH 053/392] feature: show command running time in logs window (#1253) Signed-off-by: leo --- src/ViewModels/CommandLog.cs | 8 ++++ src/Views/CommandLogTime.cs | 80 ++++++++++++++++++++++++++++++++++++ src/Views/ViewLogs.axaml | 14 +++---- 3 files changed, 95 insertions(+), 7 deletions(-) create mode 100644 src/Views/CommandLogTime.cs diff --git a/src/ViewModels/CommandLog.cs b/src/ViewModels/CommandLog.cs index 15f48e70..e73bc1ec 100644 --- a/src/ViewModels/CommandLog.cs +++ b/src/ViewModels/CommandLog.cs @@ -18,6 +18,12 @@ namespace SourceGit.ViewModels get; } = DateTime.Now; + public DateTime EndTime + { + get; + private set; + } = DateTime.Now; + public bool IsComplete { get; @@ -64,6 +70,8 @@ namespace SourceGit.ViewModels _builder.Clear(); _builder = null; + EndTime = DateTime.Now; + OnPropertyChanged(nameof(IsComplete)); if (_onNewLineReceived != null) diff --git a/src/Views/CommandLogTime.cs b/src/Views/CommandLogTime.cs new file mode 100644 index 00000000..2f24d890 --- /dev/null +++ b/src/Views/CommandLogTime.cs @@ -0,0 +1,80 @@ +using System; +using System.Threading; + +using Avalonia; +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Threading; + +namespace SourceGit.Views +{ + public class CommandLogTime : TextBlock + { + public static readonly StyledProperty LogProperty = + AvaloniaProperty.Register(nameof(Log), null); + + public ViewModels.CommandLog Log + { + get => GetValue(LogProperty); + set => SetValue(LogProperty, value); + } + + protected override Type StyleKeyOverride => typeof(TextBlock); + + protected override void OnUnloaded(RoutedEventArgs e) + { + base.OnUnloaded(e); + StopTimer(); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == LogProperty) + { + StopTimer(); + + if (change.NewValue is ViewModels.CommandLog log) + SetupCommandLog(log); + else + Text = string.Empty; + } + } + + private void SetupCommandLog(ViewModels.CommandLog log) + { + Text = GetDisplayText(log); + if (log.IsComplete) + return; + + _refreshTimer = new Timer(_ => + { + Dispatcher.UIThread.Invoke(() => + { + Text = GetDisplayText(log); + if (log.IsComplete) + StopTimer(); + }); + }, null, 0, 100); + } + + private void StopTimer() + { + if (_refreshTimer is { }) + { + _refreshTimer.Dispose(); + _refreshTimer = null; + } + } + + private string GetDisplayText(ViewModels.CommandLog log) + { + var endTime = log.IsComplete ? log.EndTime : DateTime.Now; + var duration = (endTime - log.StartTime).ToString(@"hh\:mm\:ss\.fff"); + return $"{log.StartTime:T} ({duration})"; + } + + private Timer _refreshTimer = null; + } +} diff --git a/src/Views/ViewLogs.axaml b/src/Views/ViewLogs.axaml index ed66f9a9..1fb44d66 100644 --- a/src/Views/ViewLogs.axaml +++ b/src/Views/ViewLogs.axaml @@ -77,7 +77,7 @@ - + - + From b9dc5a81643548c66d925b88b7dde2d91f1c053d Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 29 Apr 2025 18:08:35 +0800 Subject: [PATCH 054/392] feature: parse url in commit message (#1133) Signed-off-by: leo --- src/ViewModels/CommitDetail.cs | 36 +++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/src/ViewModels/CommitDetail.cs b/src/ViewModels/CommitDetail.cs index 6581d7bb..fdaa29f1 100644 --- a/src/ViewModels/CommitDetail.cs +++ b/src/ViewModels/CommitDetail.cs @@ -642,10 +642,37 @@ namespace SourceGit.ViewModels rule.Matches(inlines, message); } - var matches = REG_SHA_FORMAT().Matches(message); - for (int i = 0; i < matches.Count; i++) + var urlMatches = REG_URL_FORMAT().Matches(message); + for (int i = 0; i < urlMatches.Count; i++) { - var match = matches[i]; + var match = urlMatches[i]; + if (!match.Success) + continue; + + var start = match.Index; + var len = match.Length; + var intersect = false; + foreach (var link in inlines) + { + if (link.Intersect(start, len)) + { + intersect = true; + break; + } + } + + if (intersect) + continue; + + var url = message.Substring(start, len); + if (Uri.IsWellFormedUriString(url, UriKind.Absolute)) + inlines.Add(new Models.InlineElement(Models.InlineElementType.Link, start, len, url)); + } + + var shaMatches = REG_SHA_FORMAT().Matches(message); + for (int i = 0; i < shaMatches.Count; i++) + { + var match = shaMatches[i]; if (!match.Success) continue; @@ -840,6 +867,9 @@ namespace SourceGit.ViewModels RevisionFileSearchSuggestion = suggestion; } + [GeneratedRegex(@"\b(https?://|ftp://)[\w\d\._/\-~%@()+:?&=#!]*[\w\d/]")] + private static partial Regex REG_URL_FORMAT(); + [GeneratedRegex(@"\b([0-9a-fA-F]{6,40})\b")] private static partial Regex REG_SHA_FORMAT(); From 95ea0a6ba615e4c1f3a88825c2650e55ab5ba0a0 Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 29 Apr 2025 22:21:38 +0800 Subject: [PATCH 055/392] ux: `Ignore Whitespace and EOL Changes` should always be visible (#1260) Signed-off-by: leo --- src/Resources/Locales/en_US.axaml | 2 +- src/Resources/Locales/zh_CN.axaml | 2 +- src/Resources/Locales/zh_TW.axaml | 2 +- src/Views/DiffView.axaml | 1 - 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 538ac1f1..d5ac457f 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -253,7 +253,7 @@ Copy File Mode Changed First Difference - Ignore Whitespace Change and EOL + Ignore Whitespace & EOL Changes Last Difference LFS OBJECT CHANGE Next Difference diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 39bf38e4..4adf7ebf 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -257,7 +257,7 @@ 复制 文件权限已变化 首个差异 - 忽略空白符号变化和EOL + 忽略空白符号和EOL变化 最后一个差异 LFS对象变更 下一个差异 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 181adb69..fcab3c7a 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -257,7 +257,7 @@ 複製 檔案權限已變更 第一個差異 - 忽略空白符號變化和 EOL + 忽略空白符號和 EOL 變化 最後一個差異 LFS 物件變更 下一個差異 diff --git a/src/Views/DiffView.axaml b/src/Views/DiffView.axaml index c4da158d..9ca99abe 100644 --- a/src/Views/DiffView.axaml +++ b/src/Views/DiffView.axaml @@ -155,7 +155,6 @@ From 98dd37a9bc268b8bb7c44a4918b50baa9eea6609 Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 29 Apr 2025 22:49:20 +0800 Subject: [PATCH 056/392] localization: update tranlation for `Text.Diff.IgnoreWhitespace` This is because that in `git diff` command the `--ignore-all-space` option will also ignore line-ending changes (`--ignore-cr-at-eol`) Signed-off-by: leo --- build.command | 12 ++++++++++++ src/Resources/Locales/de_DE.axaml | 2 +- src/Resources/Locales/en_US.axaml | 2 +- src/Resources/Locales/es_ES.axaml | 2 +- src/Resources/Locales/fr_FR.axaml | 2 +- src/Resources/Locales/it_IT.axaml | 2 +- src/Resources/Locales/ja_JP.axaml | 2 +- src/Resources/Locales/pt_BR.axaml | 2 +- src/Resources/Locales/ru_RU.axaml | 2 +- src/Resources/Locales/ta_IN.axaml | 2 +- src/Resources/Locales/zh_CN.axaml | 2 +- src/Resources/Locales/zh_TW.axaml | 2 +- 12 files changed, 23 insertions(+), 11 deletions(-) create mode 100755 build.command diff --git a/build.command b/build.command new file mode 100755 index 00000000..c2e87b67 --- /dev/null +++ b/build.command @@ -0,0 +1,12 @@ +#!/bin/sh + +version=`cat VERSION` + +rm -rf SourceGit.app + +mkdir -p SourceGit.app/Contents/Resources +mkdir -p SourceGit.app/Contents/MacOS +cp build/resources/app/App.icns SourceGit.app/Contents/Resources/App.icns +sed "s/SOURCE_GIT_VERSION/${version}/g" build/resources/app/App.plist > SourceGit.app/Contents/Info.plist +dotnet publish src/SourceGit.csproj -c Release -r osx-arm64 -o SourceGit.app/Contents/MacOS +rm -rf SourceGit.app/Contents/MacOS/SourceGit.dsym \ No newline at end of file diff --git a/src/Resources/Locales/de_DE.axaml b/src/Resources/Locales/de_DE.axaml index 7a3da294..8eb49a3e 100644 --- a/src/Resources/Locales/de_DE.axaml +++ b/src/Resources/Locales/de_DE.axaml @@ -236,7 +236,7 @@ ALT Kopieren Dateimodus geändert - Ignoriere Leerzeichenänderungen und EOL + Ignoriere Leerzeichenänderungen LFS OBJEKT ÄNDERUNG Nächste Änderung KEINE ÄNDERUNG ODER NUR ZEILEN-ENDE ÄNDERUNGEN diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index d5ac457f..108eb50e 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -253,7 +253,7 @@ Copy File Mode Changed First Difference - Ignore Whitespace & EOL Changes + Ignore All Whitespace Changes Last Difference LFS OBJECT CHANGE Next Difference diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml index 82913d24..1c275d67 100644 --- a/src/Resources/Locales/es_ES.axaml +++ b/src/Resources/Locales/es_ES.axaml @@ -257,7 +257,7 @@ Copiar Modo de Archivo Cambiado Primera Diferencia - Ignorar Cambio de Espacios en Blanco y EOL + Ignorar Cambio de Espacios en Blanco Última Diferencia CAMBIO DE OBJETO LFS Siguiente Diferencia diff --git a/src/Resources/Locales/fr_FR.axaml b/src/Resources/Locales/fr_FR.axaml index f45a83d9..0529702d 100644 --- a/src/Resources/Locales/fr_FR.axaml +++ b/src/Resources/Locales/fr_FR.axaml @@ -241,7 +241,7 @@ Copier Mode de fichier changé Première différence - Ignorer les changements d'espaces et EOL + Ignorer les changements d'espaces Dernière différence CHANGEMENT D'OBJET LFS Différence suivante diff --git a/src/Resources/Locales/it_IT.axaml b/src/Resources/Locales/it_IT.axaml index b7d2568a..545261e9 100644 --- a/src/Resources/Locales/it_IT.axaml +++ b/src/Resources/Locales/it_IT.axaml @@ -240,7 +240,7 @@ Copia Modalità File Modificata Prima differenza - Ignora Modifiche agli Spazi e EOL + Ignora Modifiche agli Spazi Ultima differenza MODIFICA OGGETTO LFS Differenza Successiva diff --git a/src/Resources/Locales/ja_JP.axaml b/src/Resources/Locales/ja_JP.axaml index c50504e1..48d55500 100644 --- a/src/Resources/Locales/ja_JP.axaml +++ b/src/Resources/Locales/ja_JP.axaml @@ -241,7 +241,7 @@ コピー ファイルモードが変更されました 先頭の差分 - 空白の変更とEOLを無視 + 空白の変更を無視 最後の差分 LFSオブジェクトの変更 次の差分 diff --git a/src/Resources/Locales/pt_BR.axaml b/src/Resources/Locales/pt_BR.axaml index a4e9d883..2a693151 100644 --- a/src/Resources/Locales/pt_BR.axaml +++ b/src/Resources/Locales/pt_BR.axaml @@ -217,7 +217,7 @@ ANTIGO Copiar Modo de Arquivo Alterado - Ignorar mudanças de espaço em branco e EOL + Ignorar mudanças de espaço em branco MUDANÇA DE OBJETO LFS Próxima Diferença SEM MUDANÇAS OU APENAS MUDANÇAS DE EOL diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index a3fe2b3a..b69945e4 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -257,7 +257,7 @@ Копировать Режим файла изменён Первое различие - Игнорировать изменение пробелов и EOL + Игнорировать изменение пробелов Последнее различие ИЗМЕНЕНИЕ ОБЪЕКТА LFS Следующее различие diff --git a/src/Resources/Locales/ta_IN.axaml b/src/Resources/Locales/ta_IN.axaml index b4e4d939..02c138c6 100644 --- a/src/Resources/Locales/ta_IN.axaml +++ b/src/Resources/Locales/ta_IN.axaml @@ -241,7 +241,7 @@ நகல் கோப்பு முறை மாற்றப்பட்டது முதல் வேறுபாடு - வெள்ளைவெளி மாற்றத்தை மற்றும் EOL புறக்கணி + வெள்ளைவெளி மாற்றத்தை புறக்கணி கடைசி வேறுபாடு பெகோஅ பொருள் மாற்றம் அடுத்த வேறுபாடு diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 4adf7ebf..4da8c3d7 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -257,7 +257,7 @@ 复制 文件权限已变化 首个差异 - 忽略空白符号和EOL变化 + 忽略空白符号变化 最后一个差异 LFS对象变更 下一个差异 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index fcab3c7a..049fc379 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -257,7 +257,7 @@ 複製 檔案權限已變更 第一個差異 - 忽略空白符號和 EOL 變化 + 忽略空白符號變化 最後一個差異 LFS 物件變更 下一個差異 From 847a1e727b077b593c8f922982ae592a85583f9e Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 30 Apr 2025 09:26:34 +0800 Subject: [PATCH 057/392] refactor: enable `--ignore-cr-at-eol` in diff by default Signed-off-by: leo --- src/Models/DiffOption.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Models/DiffOption.cs b/src/Models/DiffOption.cs index 5f491d58..5e2aec8d 100644 --- a/src/Models/DiffOption.cs +++ b/src/Models/DiffOption.cs @@ -12,7 +12,7 @@ namespace SourceGit.Models { get; set; - } = false; + } = true; public Change WorkingCopyChange => _workingCopyChange; public bool IsUnstaged => _isUnstaged; From 80aead3a1758f13b34851b9890ad140f70ea62ba Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 30 Apr 2025 11:01:39 +0800 Subject: [PATCH 058/392] feature: add dirty state indicator icon to repository tab (#1227) Signed-off-by: leo --- src/Models/Bisect.cs | 4 ++-- src/Models/DirtyState.cs | 12 ++++++++++++ src/ViewModels/LauncherPage.cs | 31 +++++++++++++++++++++++++++++++ src/ViewModels/Repository.cs | 3 +++ src/Views/LauncherTabBar.axaml | 31 +++++++++++++++++++++++-------- 5 files changed, 71 insertions(+), 10 deletions(-) create mode 100644 src/Models/DirtyState.cs diff --git a/src/Models/Bisect.cs b/src/Models/Bisect.cs index d1021113..2ed8beb2 100644 --- a/src/Models/Bisect.cs +++ b/src/Models/Bisect.cs @@ -14,8 +14,8 @@ namespace SourceGit.Models public enum BisectCommitFlag { None = 0, - Good = 1, - Bad = 2, + Good = 1 << 0, + Bad = 1 << 1, } public class Bisect diff --git a/src/Models/DirtyState.cs b/src/Models/DirtyState.cs new file mode 100644 index 00000000..2b9d898d --- /dev/null +++ b/src/Models/DirtyState.cs @@ -0,0 +1,12 @@ +using System; + +namespace SourceGit.Models +{ + [Flags] + public enum DirtyState + { + None = 0, + HasLocalChanges = 1 << 0, + HasPendingPullOrPush = 1 << 1, + } +} diff --git a/src/ViewModels/LauncherPage.cs b/src/ViewModels/LauncherPage.cs index 288770bc..b97ef6f7 100644 --- a/src/ViewModels/LauncherPage.cs +++ b/src/ViewModels/LauncherPage.cs @@ -1,5 +1,8 @@ using System; + using Avalonia.Collections; +using Avalonia.Media; + using CommunityToolkit.Mvvm.ComponentModel; namespace SourceGit.ViewModels @@ -18,6 +21,12 @@ namespace SourceGit.ViewModels set => SetProperty(ref _data, value); } + public IBrush DirtyBrush + { + get => _dirtyBrush; + private set => SetProperty(ref _dirtyBrush, value); + } + public Popup Popup { get => _popup; @@ -56,6 +65,26 @@ namespace SourceGit.ViewModels App.CopyText(_node.Id); } + public void ChangeDirtyState(Models.DirtyState flag, bool remove) + { + if (remove) + { + if (_dirtyState.HasFlag(flag)) + _dirtyState -= flag; + } + else + { + _dirtyState |= flag; + } + + if (_dirtyState.HasFlag(Models.DirtyState.HasLocalChanges)) + DirtyBrush = Brushes.Gray; + else if (_dirtyState.HasFlag(Models.DirtyState.HasPendingPullOrPush)) + DirtyBrush = Brushes.RoyalBlue; + else + DirtyBrush = null; + } + public bool CanCreatePopup() { return _popup == null || !_popup.InProgress; @@ -104,6 +133,8 @@ namespace SourceGit.ViewModels private RepositoryNode _node = null; private object _data = null; + private IBrush _dirtyBrush = null; + private Models.DirtyState _dirtyState = Models.DirtyState.None; private Popup _popup = null; } } diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index 1db1d6f3..289d890f 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -994,6 +994,8 @@ namespace SourceGit.ViewModels if (_workingCopy != null) _workingCopy.HasRemotes = remotes.Count > 0; + + GetOwnerPage()?.ChangeDirtyState(Models.DirtyState.HasPendingPullOrPush, !CurrentBranch.TrackStatus.IsVisible); }); } @@ -1101,6 +1103,7 @@ namespace SourceGit.ViewModels { LocalChangesCount = changes.Count; OnPropertyChanged(nameof(InProgressContext)); + GetOwnerPage()?.ChangeDirtyState(Models.DirtyState.HasLocalChanges, changes.Count == 0); }); } diff --git a/src/Views/LauncherTabBar.axaml b/src/Views/LauncherTabBar.axaml index 73b48f58..0376e259 100644 --- a/src/Views/LauncherTabBar.axaml +++ b/src/Views/LauncherTabBar.axaml @@ -68,14 +68,29 @@ Data="{StaticResource Icons.Repositories}" IsVisible="{Binding !Node.IsRepository}" IsHitTestVisible="False"/> - + + + + + + + Date: Wed, 30 Apr 2025 15:02:54 +0800 Subject: [PATCH 059/392] enhance: quit application after main window has been closed Signed-off-by: leo --- src/App.axaml.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/App.axaml.cs b/src/App.axaml.cs index 822084dc..c54b616d 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -552,6 +552,7 @@ namespace SourceGit _launcher = new ViewModels.Launcher(startupRepo); desktop.MainWindow = new Views.Launcher() { DataContext = _launcher }; + desktop.ShutdownMode = ShutdownMode.OnMainWindowClose; #if !DISABLE_UPDATE_DETECTION if (pref.ShouldCheck4UpdateOnStartup()) From 9bde797b2448cabcf6669cd02355554d776a8cfc Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 30 Apr 2025 20:41:00 +0800 Subject: [PATCH 060/392] fix: make sure the new pattern is appended as a new line (#1264) Signed-off-by: leo --- src/Commands/GitIgnore.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Commands/GitIgnore.cs b/src/Commands/GitIgnore.cs index e666eba6..8b351f5e 100644 --- a/src/Commands/GitIgnore.cs +++ b/src/Commands/GitIgnore.cs @@ -8,7 +8,14 @@ namespace SourceGit.Commands { var file = Path.Combine(repo, ".gitignore"); if (!File.Exists(file)) + { File.WriteAllLines(file, [pattern]); + return; + } + + var org = File.ReadAllText(file); + if (!org.EndsWith('\n')) + File.AppendAllLines(file, ["", pattern]); else File.AppendAllLines(file, [pattern]); } From fe677d40c1a0d4999b2a9ae5c7784a71270f184a Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 30 Apr 2025 21:05:53 +0800 Subject: [PATCH 061/392] feature: supports search commits by change content (#1263) Signed-off-by: leo --- src/Commands/QueryCommits.cs | 16 ++++++++++------ src/Models/Commit.cs | 1 + src/Resources/Locales/en_US.axaml | 1 + src/Resources/Locales/zh_CN.axaml | 1 + src/Resources/Locales/zh_TW.axaml | 1 + src/Views/Repository.axaml | 1 + 6 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/Commands/QueryCommits.cs b/src/Commands/QueryCommits.cs index dd3c39b4..9e1d9918 100644 --- a/src/Commands/QueryCommits.cs +++ b/src/Commands/QueryCommits.cs @@ -26,11 +26,7 @@ namespace SourceGit.Commands { search += $"-i --committer=\"{filter}\""; } - else if (method == Models.CommitSearchMethod.ByFile) - { - search += $"-- \"{filter}\""; - } - else + else if (method == Models.CommitSearchMethod.ByMessage) { var argsBuilder = new StringBuilder(); argsBuilder.Append(search); @@ -45,10 +41,18 @@ namespace SourceGit.Commands search = argsBuilder.ToString(); } + else if (method == Models.CommitSearchMethod.ByFile) + { + search += $"-- \"{filter}\""; + } + else + { + search = $"-G\"{filter}\""; + } WorkingDirectory = repo; Context = repo; - Args = $"log -1000 --date-order --no-show-signature --decorate=full --format=%H%n%P%n%D%n%aN±%aE%n%at%n%cN±%cE%n%ct%n%s " + search; + Args = $"log -1000 --date-order --no-show-signature --decorate=full --format=%H%n%P%n%D%n%aN±%aE%n%at%n%cN±%cE%n%ct%n%s {search}"; _findFirstMerged = false; } diff --git a/src/Models/Commit.cs b/src/Models/Commit.cs index 1980e622..ef5a34bc 100644 --- a/src/Models/Commit.cs +++ b/src/Models/Commit.cs @@ -13,6 +13,7 @@ namespace SourceGit.Models ByCommitter, ByMessage, ByFile, + ByContent, } public class Commit diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 108eb50e..f4c62a90 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -605,6 +605,7 @@ Search Commit Author Committer + Content File Message SHA diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 4da8c3d7..8ee210e9 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -609,6 +609,7 @@ 查找提交 作者 提交者 + 变更内容 文件 提交信息 提交指纹 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 049fc379..7430d609 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -609,6 +609,7 @@ 搜尋提交 作者 提交者 + 變更內容 檔案 提交訊息 提交編號 diff --git a/src/Views/Repository.axaml b/src/Views/Repository.axaml index 16908f81..a06432d4 100644 --- a/src/Views/Repository.axaml +++ b/src/Views/Repository.axaml @@ -495,6 +495,7 @@ + From 61bc42612eed09d80b49adba26a70acf168b54c9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 30 Apr 2025 13:06:53 +0000 Subject: [PATCH 062/392] doc: Update translation status and sort locale files --- TRANSLATION.md | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index 21706eb2..fe60e9a3 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -6,7 +6,7 @@ This document shows the translation status of each locale file in the repository ### ![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen) -### ![de__DE](https://img.shields.io/badge/de__DE-95.19%25-yellow) +### ![de__DE](https://img.shields.io/badge/de__DE-95.07%25-yellow)
Missing keys in de_DE.axaml @@ -37,6 +37,7 @@ This document shows the translation status of each locale file in the repository - Text.Preferences.Appearance.EditorTabWidth - Text.Preferences.General.ShowTagsInGraph - Text.Preferences.Git.IgnoreCRAtEOLInDiff +- Text.Repository.Search.ByContent - Text.Repository.ViewLogs - Text.StashCM.SaveAsPatch - Text.ViewLogs @@ -51,9 +52,16 @@ This document shows the translation status of each locale file in the repository
-### ![es__ES](https://img.shields.io/badge/es__ES-%E2%88%9A-brightgreen) +### ![es__ES](https://img.shields.io/badge/es__ES-99.87%25-yellow) -### ![fr__FR](https://img.shields.io/badge/fr__FR-96.49%25-yellow) +
+Missing keys in es_ES.axaml + +- Text.Repository.Search.ByContent + +
+ +### ![fr__FR](https://img.shields.io/badge/fr__FR-96.37%25-yellow)
Missing keys in fr_FR.axaml @@ -75,6 +83,7 @@ This document shows the translation status of each locale file in the repository - Text.ConfirmEmptyCommit.StageAllThenCommit - Text.ConfirmEmptyCommit.WithLocalChanges - Text.Preferences.Git.IgnoreCRAtEOLInDiff +- Text.Repository.Search.ByContent - Text.Repository.ViewLogs - Text.ViewLogs - Text.ViewLogs.Clear @@ -88,7 +97,7 @@ This document shows the translation status of each locale file in the repository
-### ![it__IT](https://img.shields.io/badge/it__IT-96.23%25-yellow) +### ![it__IT](https://img.shields.io/badge/it__IT-96.11%25-yellow)
Missing keys in it_IT.axaml @@ -112,6 +121,7 @@ This document shows the translation status of each locale file in the repository - Text.CopyFullPath - Text.Preferences.General.ShowTagsInGraph - Text.Preferences.Git.IgnoreCRAtEOLInDiff +- Text.Repository.Search.ByContent - Text.Repository.ViewLogs - Text.ViewLogs - Text.ViewLogs.Clear @@ -125,7 +135,7 @@ This document shows the translation status of each locale file in the repository
-### ![ja__JP](https://img.shields.io/badge/ja__JP-96.23%25-yellow) +### ![ja__JP](https://img.shields.io/badge/ja__JP-96.11%25-yellow)
Missing keys in ja_JP.axaml @@ -148,6 +158,7 @@ This document shows the translation status of each locale file in the repository - Text.ConfirmEmptyCommit.WithLocalChanges - Text.Preferences.Git.IgnoreCRAtEOLInDiff - Text.Repository.FilterCommits +- Text.Repository.Search.ByContent - Text.Repository.Tags.OrderByNameDes - Text.Repository.ViewLogs - Text.ViewLogs @@ -162,7 +173,7 @@ This document shows the translation status of each locale file in the repository
-### ![pt__BR](https://img.shields.io/badge/pt__BR-87.79%25-yellow) +### ![pt__BR](https://img.shields.io/badge/pt__BR-87.68%25-yellow)
Missing keys in pt_BR.axaml @@ -235,6 +246,7 @@ This document shows the translation status of each locale file in the repository - Text.Repository.HistoriesOrder - Text.Repository.Notifications.Clear - Text.Repository.OnlyHighlightCurrentBranchInHistories +- Text.Repository.Search.ByContent - Text.Repository.Skip - Text.Repository.Tags.OrderByCreatorDate - Text.Repository.Tags.OrderByNameAsc @@ -264,9 +276,16 @@ This document shows the translation status of each locale file in the repository
-### ![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen) +### ![ru__RU](https://img.shields.io/badge/ru__RU-99.87%25-yellow) -### ![ta__IN](https://img.shields.io/badge/ta__IN-96.49%25-yellow) +
+Missing keys in ru_RU.axaml + +- Text.Repository.Search.ByContent + +
+ +### ![ta__IN](https://img.shields.io/badge/ta__IN-96.37%25-yellow)
Missing keys in ta_IN.axaml @@ -288,6 +307,7 @@ This document shows the translation status of each locale file in the repository - Text.ConfirmEmptyCommit.StageAllThenCommit - Text.ConfirmEmptyCommit.WithLocalChanges - Text.Preferences.Git.IgnoreCRAtEOLInDiff +- Text.Repository.Search.ByContent - Text.Repository.ViewLogs - Text.UpdateSubmodules.Target - Text.ViewLogs @@ -301,7 +321,7 @@ This document shows the translation status of each locale file in the repository
-### ![uk__UA](https://img.shields.io/badge/uk__UA-97.66%25-yellow) +### ![uk__UA](https://img.shields.io/badge/uk__UA-97.54%25-yellow)
Missing keys in uk_UA.axaml @@ -319,6 +339,7 @@ This document shows the translation status of each locale file in the repository - Text.CommitMessageTextBox.SubjectCount - Text.ConfigureWorkspace.Name - Text.Preferences.Git.IgnoreCRAtEOLInDiff +- Text.Repository.Search.ByContent - Text.Repository.ViewLogs - Text.ViewLogs - Text.ViewLogs.Clear From 3b0c57be8468500c34e6b44c8a80dab98969c9cf Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 30 Apr 2025 21:38:46 +0800 Subject: [PATCH 063/392] feature: supports to re-order workspaces (#1261) Signed-off-by: leo --- src/ViewModels/ConfigureWorkspace.cs | 31 ++++++++++++++++++++++- src/Views/ConfigureWorkspace.axaml | 38 +++++++++++++++++++++------- 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/src/ViewModels/ConfigureWorkspace.cs b/src/ViewModels/ConfigureWorkspace.cs index 6dc0f82e..047a0cc7 100644 --- a/src/ViewModels/ConfigureWorkspace.cs +++ b/src/ViewModels/ConfigureWorkspace.cs @@ -9,7 +9,6 @@ namespace SourceGit.ViewModels public AvaloniaList Workspaces { get; - private set; } public Workspace Selected @@ -51,6 +50,36 @@ namespace SourceGit.ViewModels Workspaces.Remove(_selected); } + public void MoveSelectedUp() + { + if (_selected == null) + return; + + var idx = Workspaces.IndexOf(_selected); + if (idx == 0) + return; + + Workspaces.Move(idx - 1, idx); + + Preferences.Instance.Workspaces.RemoveAt(idx); + Preferences.Instance.Workspaces.Insert(idx - 1, _selected); + } + + public void MoveSelectedDown() + { + if (_selected == null) + return; + + var idx = Workspaces.IndexOf(_selected); + if (idx == Workspaces.Count - 1) + return; + + Workspaces.Move(idx + 1, idx); + + Preferences.Instance.Workspaces.RemoveAt(idx); + Preferences.Instance.Workspaces.Insert(idx + 1, _selected); + } + private Workspace _selected = null; private bool _canDeleteSelected = false; } diff --git a/src/Views/ConfigureWorkspace.axaml b/src/Views/ConfigureWorkspace.axaml index 169df154..c65b8879 100644 --- a/src/Views/ConfigureWorkspace.axaml +++ b/src/Views/ConfigureWorkspace.axaml @@ -79,19 +79,39 @@ - - - - - - - - - + + + From 007acb3fa61624e6437cc9990e69d2aafe3017d5 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 30 Apr 2025 21:40:01 +0800 Subject: [PATCH 064/392] project: remove unused scripts Signed-off-by: leo --- build.command | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100755 build.command diff --git a/build.command b/build.command deleted file mode 100755 index c2e87b67..00000000 --- a/build.command +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - -version=`cat VERSION` - -rm -rf SourceGit.app - -mkdir -p SourceGit.app/Contents/Resources -mkdir -p SourceGit.app/Contents/MacOS -cp build/resources/app/App.icns SourceGit.app/Contents/Resources/App.icns -sed "s/SOURCE_GIT_VERSION/${version}/g" build/resources/app/App.plist > SourceGit.app/Contents/Info.plist -dotnet publish src/SourceGit.csproj -c Release -r osx-arm64 -o SourceGit.app/Contents/MacOS -rm -rf SourceGit.app/Contents/MacOS/SourceGit.dsym \ No newline at end of file From 8c1d3974809559c69db97613a76493bb2a613d81 Mon Sep 17 00:00:00 2001 From: leo Date: Sat, 3 May 2025 20:51:07 +0800 Subject: [PATCH 065/392] fix: inline blocks is not sorted in order (#1274) Signed-off-by: leo --- src/Views/CommitSubjectPresenter.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Views/CommitSubjectPresenter.cs b/src/Views/CommitSubjectPresenter.cs index f64323c0..6ff72bb8 100644 --- a/src/Views/CommitSubjectPresenter.cs +++ b/src/Views/CommitSubjectPresenter.cs @@ -177,7 +177,8 @@ namespace SourceGit.Views var rules = IssueTrackerRules ?? []; foreach (var rule in rules) rule.Matches(_elements, subject); - + + _elements.Sort((l, r) => l.Start - r.Start); _needRebuildInlines = true; InvalidateVisual(); } From 4b2983b330181c96745d5b046431aa88ce9786e0 Mon Sep 17 00:00:00 2001 From: leo Date: Sat, 3 May 2025 21:09:43 +0800 Subject: [PATCH 066/392] fix: commit detail panel is overlapping history when resizing (#1273) Signed-off-by: leo --- src/Views/Histories.axaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Views/Histories.axaml b/src/Views/Histories.axaml index 568854fd..86a96528 100644 --- a/src/Views/Histories.axaml +++ b/src/Views/Histories.axaml @@ -24,7 +24,7 @@ - + - + - - + + @@ -121,8 +121,8 @@ - - + + From c529fab86945dc22b9badb5c75a89e2f92b116ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alen=20=C5=A0iljak?= <462445+alensiljak@users.noreply.github.com> Date: Sat, 3 May 2025 15:18:24 +0200 Subject: [PATCH 067/392] feature: close repository configuration dialog when user pressed `Esc` (#1269) --- src/Views/RepositoryConfigure.axaml.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Views/RepositoryConfigure.axaml.cs b/src/Views/RepositoryConfigure.axaml.cs index 455731aa..2c80dd45 100644 --- a/src/Views/RepositoryConfigure.axaml.cs +++ b/src/Views/RepositoryConfigure.axaml.cs @@ -1,4 +1,5 @@ using Avalonia.Controls; +using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Platform.Storage; @@ -33,5 +34,13 @@ namespace SourceGit.Views e.Handled = true; } + + protected override void OnKeyDown(KeyEventArgs e) + { + base.OnKeyDown(e); + + if (!e.Handled && e.Key == Key.Escape) + Close(); + } } } From c0c52695a35dfb3129dfa6c9c77de7226d20df19 Mon Sep 17 00:00:00 2001 From: leo Date: Sat, 3 May 2025 21:31:10 +0800 Subject: [PATCH 068/392] code_style: remove unused code Signed-off-by: leo --- src/Models/DiffResult.cs | 1 - src/ViewModels/ConfigureWorkspace.cs | 12 ++++++------ src/Views/CommitSubjectPresenter.cs | 3 +-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/Models/DiffResult.cs b/src/Models/DiffResult.cs index 75294057..b2d91310 100644 --- a/src/Models/DiffResult.cs +++ b/src/Models/DiffResult.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; diff --git a/src/ViewModels/ConfigureWorkspace.cs b/src/ViewModels/ConfigureWorkspace.cs index 047a0cc7..ec885b2f 100644 --- a/src/ViewModels/ConfigureWorkspace.cs +++ b/src/ViewModels/ConfigureWorkspace.cs @@ -54,13 +54,13 @@ namespace SourceGit.ViewModels { if (_selected == null) return; - + var idx = Workspaces.IndexOf(_selected); if (idx == 0) return; - + Workspaces.Move(idx - 1, idx); - + Preferences.Instance.Workspaces.RemoveAt(idx); Preferences.Instance.Workspaces.Insert(idx - 1, _selected); } @@ -69,13 +69,13 @@ namespace SourceGit.ViewModels { if (_selected == null) return; - + var idx = Workspaces.IndexOf(_selected); if (idx == Workspaces.Count - 1) return; - + Workspaces.Move(idx + 1, idx); - + Preferences.Instance.Workspaces.RemoveAt(idx); Preferences.Instance.Workspaces.Insert(idx + 1, _selected); } diff --git a/src/Views/CommitSubjectPresenter.cs b/src/Views/CommitSubjectPresenter.cs index 6ff72bb8..86e0fd54 100644 --- a/src/Views/CommitSubjectPresenter.cs +++ b/src/Views/CommitSubjectPresenter.cs @@ -177,7 +177,7 @@ namespace SourceGit.Views var rules = IssueTrackerRules ?? []; foreach (var rule in rules) rule.Matches(_elements, subject); - + _elements.Sort((l, r) => l.Start - r.Start); _needRebuildInlines = true; InvalidateVisual(); @@ -319,7 +319,6 @@ namespace SourceGit.Views foreground); _inlines.Add(new Inline(x, normal, null)); - x += normal.WidthIncludingTrailingWhitespace; } } From 666275c747da8d0a57dd53a5ffc4850137d7a97d Mon Sep 17 00:00:00 2001 From: broknecho Date: Sat, 3 May 2025 21:24:11 -0600 Subject: [PATCH 069/392] feature: add `Meld` as an option for external merge tool on Windows (#1275) --- src/Models/ExternalMerger.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Models/ExternalMerger.cs b/src/Models/ExternalMerger.cs index d97d3933..fe67ad6a 100644 --- a/src/Models/ExternalMerger.cs +++ b/src/Models/ExternalMerger.cs @@ -43,6 +43,7 @@ namespace SourceGit.Models new ExternalMerger(8, "codium", "VSCodium", "VSCodium.exe", "-n --wait \"$MERGED\"", "-n --wait --diff \"$LOCAL\" \"$REMOTE\""), new ExternalMerger(9, "p4merge", "P4Merge", "p4merge.exe", "-tw 4 \"$BASE\" \"$LOCAL\" \"$REMOTE\" \"$MERGED\"", "-tw 4 \"$LOCAL\" \"$REMOTE\""), new ExternalMerger(10, "plastic_merge", "Plastic SCM", "mergetool.exe", "-s=\"$REMOTE\" -b=\"$BASE\" -d=\"$LOCAL\" -r=\"$MERGED\" --automatic", "-s=\"$LOCAL\" -d=\"$REMOTE\""), + new ExternalMerger(11, "meld", "Meld", "Meld.exe", "\"$LOCAL\" \"$BASE\" \"$REMOTE\" --output \"$MERGED\"", "\"$LOCAL\" \"$REMOTE\""), }; } else if (OperatingSystem.IsMacOS()) From 704c6f589d34ab3d4011803cb31d39b0ef2d79d6 Mon Sep 17 00:00:00 2001 From: AquariusStar <48148723+AquariusStar@users.noreply.github.com> Date: Sun, 4 May 2025 12:36:30 +0300 Subject: [PATCH 070/392] localization: update and fix translation russian (#1276) --- src/Resources/Locales/ru_RU.axaml | 93 ++++++++++++++++--------------- 1 file changed, 47 insertions(+), 46 deletions(-) diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index b69945e4..4c8ea4bf 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -59,7 +59,7 @@ Отклонить все изменения. Перемотать вперёд к ${0}$ Извлечь ${0}$ в ${1}$... - Поток Git - Завершение ${0}$ + Git-процесс - Завершение ${0}$ Влить ${0}$ в ${1}$... Влить {0} выделенных веток в текущую Загрузить ${0}$ @@ -195,8 +195,8 @@ Восстанавливать вкладки при запуске ПРОДОЛЖИТЬ Обнаружена пустая ревизия! Вы хотите продолжить (--allow-empty)? - Подготовить все и зафиксировать ревизию - Обнаружена пустая ревизия! Вы хотите продолжить (--allow-empty) или отложить все, затем зафиксировать ревизию? + Сформировать всё и зафиксировать ревизию + Обнаружена пустая ревизия! Вы хотите продолжить (--allow-empty) или отложить всё, затем зафиксировать ревизию? Общепринятый помощник по ревизии Кардинальные изменения: Закрытая тема: @@ -251,21 +251,21 @@ Удалить метку Метка: Удалить из внешнего репозитория - РАЗНИЦА БИНАРНИКОВ + СРАВНЕНИЕ БИНАРНИКОВ НОВЫЙ СТАРЫЙ Копировать Режим файла изменён - Первое различие - Игнорировать изменение пробелов - Последнее различие + Первое сравнение + Игнорировать изменения пробелов + Последнее сравнение ИЗМЕНЕНИЕ ОБЪЕКТА LFS - Следующее различие + Следующее сравнение НИКАКИХ ИЗМЕНЕНИЙ ИЛИ МЕНЯЕТСЯ ТОЛЬКО EOL - Предыдущее различие + Предыдущее сравнение Сохранить как заплатку Показывать скрытые символы - Различие рядом + Сравнение рядом ПОДМОДУЛЬ НОВЫЙ Обмен @@ -305,41 +305,41 @@ Открыть расширенный инструмент слияния Взять версию ${0}$ Сохранить как файл заплатки... - Подготовить - Подготовленные {0} файлы - Подготовленные изменения в выбранной(ых) строке(ах) + Сформировать + Сформированные {0} файлы + Сформированные изменения в выбранной(ых) строке(ах) Отложить... Отложить {0} файлов... - Снять подготовленный - Неподготовленные {0} файлы - Неподготовленные изменения в выбранной(ых) строке(ах) + Расформировать + Несформированные {0} файлы + Несформированные изменения в выбранной(ых) строке(ах) Использовать мой (checkout --ours) Использовать их (checkout --theirs) История файлов ИЗМЕНИТЬ СОДЕРЖИМОЕ - Git-поток + Git-процесс Ветка разработчика: Свойство: Свойство префикса: - ПОТОК - Свойства завершения - ПОТОК - Закончить исправление - ПОТОК - Завершить выпуск + ПРОЦЕСС - Свойства завершения + ПРОЦЕСС - Закончить исправление + ПРОЦЕСС - Завершить выпуск Цель: Исправление: Префикс исправлений: - Создать Git-поток + Создать Git-процесс Держать ветку Производственная ветка: Выпуск: Префикс выпуска: Свойство запуска... - ПОТОК - Свойство запуска + ПРОЦЕСС - Свойство запуска Запуск исправлений... - ПОТОК - Запуск исправлений + ПРОЦЕСС - Запуск исправлений Ввести имя Запуск выпуска... - ПОТОК - Запуск выпуска + ПРОЦЕСС - Запуск выпуска Префикс метки версии: Git LFS (хранилище больших файлов) Добавить шаблон отслеживания... @@ -388,9 +388,9 @@ Создать новую вкладку Открыть диалоговое окно настроек РЕПОЗИТОРИЙ - Зафиксировать подготовленные изменения - Зафиксировать и выложить подготовленные изменения - Подготовить все изменения и зафиксировать + Зафиксировать сформированные изменения + Зафиксировать и выложить сформированные изменения + Сформировать все изменения и зафиксировать Создать новую ветку на основе выбранной ветки Отклонить выбранные изменения Извлечение, запускается сразу @@ -399,7 +399,7 @@ Загрузить, запускается сразу Выложить, запускается сразу Принудительно перезагрузить репозиторий - Подготовленные/Неподготовленные выбранные изменения + Сформированные/Несформированные выбранные изменения Переключить на «Изменения» Переключить на «Истории» Переключить на «Отложенные» @@ -409,8 +409,8 @@ Найти предыдущее совпадение Открыть панель поиска Отклонить - Подготовить - Снять из подготовленных + Сформировать + Расформировать Создать репозиторий Путь: Выполняется частичный перенос ревизий (cherry-pick). @@ -463,9 +463,9 @@ Вчера Параметры ОТКРЫТЬ ИИ - Запрос на анализ различий + Запрос на анализ сравнения Ключ API - Произвести запрос на тему + Создать запрос на тему Модель Имя: Сервер @@ -482,9 +482,9 @@ Переопределение темы Использовать фиксированную ширину табуляции в строке заголовка. Использовать системное окно - ИНСТРУМЕНТ РАЗЛИЧИЙ/СЛИЯНИЯ + ИНСТРУМЕНТ СРАВНЕНИЙ/СЛИЯНИЯ Путь установки - Введите путь для инструмента различия/слияния + Введите путь для инструмента сравнения/слияния Инструмент ОСНОВНЫЕ Проверить обновления при старте @@ -501,7 +501,7 @@ Электроная почта пользователя Общая электроная почта пользователя git Разрешить (--prune) при скачивании - Разрешить (--ignore-cr-at-eol) в различии + Разрешить (--ignore-cr-at-eol) в сравнении Для работы программы требуется версия Git (>= 2.23.0) Путь установки Разрешить верификацию HTTP SSL @@ -609,6 +609,7 @@ Поиск ревизии Автор Ревизор + Содержимое Файл Сообщение SHA @@ -673,8 +674,8 @@ Хранить отложенные файлы Сообщение: Имя тайника (необязательно) - Только подготовленные изменения - Подготовленные так и неподготовленные изменения выбранных файлов будут сохранены!!! + Только сформированные изменения + Сформированные так и несформированные изменения выбранных файлов будут сохранены!!! Отложить локальные изменения Принять Отбросить @@ -739,14 +740,14 @@ Игнорировать файлы в том же каталоге Игнорировать только эти файлы Изменить - Теперь вы можете подготовить этот файл. + Теперь вы можете сформировать этот файл. ЗАФИКСИРОВАТЬ ЗАФИКСИРОВАТЬ и ОТПРАВИТЬ Шаблон/Истории Запустить событие щелчка Зафиксировать (Редактировать) - Подготовить все изменения и зафиксировать - Вы подготовили {0} файл(ов), но отображается только {1} файл(ов) ({2} файл(ов) отфильтровано). Вы хотите продолжить? + Сформировать все изменения и зафиксировать + Вы сформировали {0} файл(ов), но отображается только {1} файл(ов) ({2} файл(ов) отфильтровано). Вы хотите продолжить? ОБНАРУЖЕНЫ КОНФЛИКТЫ ОТКРЫТЬ ВНЕШНИЙ ИНСТРУМЕНТ СЛИЯНИЯ ОТКРЫТЬ ВСЕ КОНФЛИКТЫ ВО ВНЕШНЕМ ИНСТРУМЕНТЕ СЛИЯНИЯ @@ -758,12 +759,12 @@ НЕТ ШАБЛОНОВ РЕВИЗИИ Щёлкните правой кнопкой мыши выбранный файл(ы) и разрешите конфликты. Завершение работы - ПОДГОТОВЛЕННЫЕ - СНЯТЬ ПОДГОТОВЛЕННЫЙ - СНЯТЬ ВСЕ ПОДГОТОВЛЕННЫЕ - НЕПОДГОТОВЛЕННЫЕ - ПОДГОТОВИТЬ - ВСЕ ПОДГОТОВИТЬ + СФОРМИРОВАННЫЕ + РАСФОРМИРОВАТЬ + РАСФОРМИРОВАТЬ ВСЁ + НЕСФОРМИРОВАННЫЕ + СФОРМИРОВАТЬ + СФОРМИРОВАТЬ ВСЁ ОТКРЫТЬ СПИСОК НЕОТСЛЕЖИВАЕМЫХ ФАЙЛОВ Шаблон: ${0}$ РАБОЧЕЕ ПРОСТРАНСТВО: From 5e080279cee305638b29f04a18d565b9f5e416fe Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 4 May 2025 09:36:42 +0000 Subject: [PATCH 071/392] doc: Update translation status and sort locale files --- TRANSLATION.md | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index fe60e9a3..27c77e7b 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -276,14 +276,7 @@ This document shows the translation status of each locale file in the repository
-### ![ru__RU](https://img.shields.io/badge/ru__RU-99.87%25-yellow) - -
-Missing keys in ru_RU.axaml - -- Text.Repository.Search.ByContent - -
+### ![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen) ### ![ta__IN](https://img.shields.io/badge/ta__IN-96.37%25-yellow) From d92d279fbe96bd115255ab6baca3c58a387b4d57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20J=2E=20Mart=C3=ADnez=20M=2E?= <56406225+jjesus-dev@users.noreply.github.com> Date: Mon, 5 May 2025 19:17:45 -0600 Subject: [PATCH 072/392] localization: update spanish translations (#1279) add missing translations --- src/Resources/Locales/es_ES.axaml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml index 1c275d67..c9978b2a 100644 --- a/src/Resources/Locales/es_ES.axaml +++ b/src/Resources/Locales/es_ES.axaml @@ -609,6 +609,7 @@ Buscar Commit Autor Committer + Contenido Archivo Mensaje SHA From aee4ce6387c913ed3e3807c0bf228243c9eb4604 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 6 May 2025 01:17:55 +0000 Subject: [PATCH 073/392] doc: Update translation status and sort locale files --- TRANSLATION.md | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index 27c77e7b..facab0ab 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -52,14 +52,7 @@ This document shows the translation status of each locale file in the repository -### ![es__ES](https://img.shields.io/badge/es__ES-99.87%25-yellow) - -
-Missing keys in es_ES.axaml - -- Text.Repository.Search.ByContent - -
+### ![es__ES](https://img.shields.io/badge/es__ES-%E2%88%9A-brightgreen) ### ![fr__FR](https://img.shields.io/badge/fr__FR-96.37%25-yellow) From 867edd9453e92e8aea23fd8db8004b6145f5ea1a Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 6 May 2025 09:22:49 +0800 Subject: [PATCH 074/392] version: Release 2025.16 Signed-off-by: leo --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 70f2a59f..fe39b2a2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2025.15 \ No newline at end of file +2025.16 \ No newline at end of file From 054bbf7e0c683d67610a445d338c22d191f40ff2 Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 6 May 2025 09:39:03 +0800 Subject: [PATCH 075/392] enhance: do not override `core.autocrlf` configure while reading file diff (#1278) Signed-off-by: leo --- src/Commands/Diff.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Commands/Diff.cs b/src/Commands/Diff.cs index a354b816..6af0a3cc 100644 --- a/src/Commands/Diff.cs +++ b/src/Commands/Diff.cs @@ -28,11 +28,11 @@ namespace SourceGit.Commands Context = repo; if (ignoreWhitespace) - Args = $"-c core.autocrlf=false diff --no-ext-diff --patch --ignore-all-space --unified={unified} {opt}"; + Args = $"diff --no-ext-diff --patch --ignore-all-space --unified={unified} {opt}"; else if (Models.DiffOption.IgnoreCRAtEOL) - Args = $"-c core.autocrlf=false diff --no-ext-diff --patch --ignore-cr-at-eol --unified={unified} {opt}"; + Args = $"diff --no-ext-diff --patch --ignore-cr-at-eol --unified={unified} {opt}"; else - Args = $"-c core.autocrlf=false diff --no-ext-diff --patch --unified={unified} {opt}"; + Args = $"diff --no-ext-diff --patch --unified={unified} {opt}"; } public Models.DiffResult Result() From df29edd8f05867b1e6688c98819094626ad3bde4 Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 6 May 2025 11:12:57 +0800 Subject: [PATCH 076/392] feature: make `--recurse-submdoules` an option while trying to checkout branch with submodules (#1272) Signed-off-by: leo --- src/Commands/Checkout.cs | 10 ++++++---- src/Models/RepositorySettings.cs | 6 ++++++ src/Resources/Locales/en_US.axaml | 1 + src/Resources/Locales/zh_CN.axaml | 1 + src/Resources/Locales/zh_TW.axaml | 1 + src/ViewModels/Checkout.cs | 16 +++++++++++++++- src/ViewModels/CreateBranch.cs | 27 +++++++++++++++++++++++++-- src/ViewModels/Repository.cs | 2 +- src/Views/Checkout.axaml | 12 ++++++++++-- src/Views/CreateBranch.axaml | 15 ++++++++++++++- 10 files changed, 80 insertions(+), 11 deletions(-) diff --git a/src/Commands/Checkout.cs b/src/Commands/Checkout.cs index c39c28ae..b0274e7d 100644 --- a/src/Commands/Checkout.cs +++ b/src/Commands/Checkout.cs @@ -11,15 +11,17 @@ namespace SourceGit.Commands Context = repo; } - public bool Branch(string branch) + public bool Branch(string branch, bool recurseSubmodules) { - Args = $"checkout --recurse-submodules --progress {branch}"; + var options = recurseSubmodules ? "--recurse-submodules" : string.Empty; + Args = $"checkout {options} --progress {branch}"; return Exec(); } - public bool Branch(string branch, string basedOn) + public bool Branch(string branch, string basedOn, bool recurseSubmodules) { - Args = $"checkout --recurse-submodules --progress -b {branch} {basedOn}"; + var options = recurseSubmodules ? "--recurse-submodules" : string.Empty; + Args = $"checkout {options} --progress -b {branch} {basedOn}"; return Exec(); } diff --git a/src/Models/RepositorySettings.cs b/src/Models/RepositorySettings.cs index 34a72033..3df463b3 100644 --- a/src/Models/RepositorySettings.cs +++ b/src/Models/RepositorySettings.cs @@ -110,6 +110,12 @@ namespace SourceGit.Models set; } = true; + public bool UpdateSubmodulesOnCheckoutBranch + { + get; + set; + } = true; + public AvaloniaList HistoriesFilters { get; diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index f4c62a90..640cb8f3 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -82,6 +82,7 @@ Local Changes: Discard Stash & Reapply + Update all submodules Branch: Cherry Pick Append source to commit message diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 8ee210e9..5db542b1 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -86,6 +86,7 @@ 未提交更改 : 丢弃更改 贮藏并自动恢复 + 同时更新所有子模块 目标分支 : 挑选提交 提交信息中追加来源信息 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 7430d609..6fd380ec 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -86,6 +86,7 @@ 未提交變更: 捨棄變更 擱置變更並自動復原 + 同時更新所有子模組 目標分支: 揀選提交 提交資訊中追加來源資訊 diff --git a/src/ViewModels/Checkout.cs b/src/ViewModels/Checkout.cs index a96ac79a..67db08c9 100644 --- a/src/ViewModels/Checkout.cs +++ b/src/ViewModels/Checkout.cs @@ -15,11 +15,24 @@ namespace SourceGit.ViewModels set; } + public bool IsRecurseSubmoduleVisible + { + get; + private set; + } + + public bool RecurseSubmodules + { + get => _repo.Settings.UpdateSubmodulesOnCheckoutBranch; + set => _repo.Settings.UpdateSubmodulesOnCheckoutBranch = value; + } + public Checkout(Repository repo, string branch) { _repo = repo; Branch = branch; DiscardLocalChanges = false; + IsRecurseSubmoduleVisible = repo.Submodules.Count > 0; } public override Task Sure() @@ -30,6 +43,7 @@ namespace SourceGit.ViewModels var log = _repo.CreateLog($"Checkout '{Branch}'"); Use(log); + var updateSubmodules = IsRecurseSubmoduleVisible && RecurseSubmodules; return Task.Run(() => { var changes = new Commands.CountLocalChangesWithoutUntracked(_repo.FullPath).Result(); @@ -54,7 +68,7 @@ namespace SourceGit.ViewModels } } - var rs = new Commands.Checkout(_repo.FullPath).Use(log).Branch(Branch); + var rs = new Commands.Checkout(_repo.FullPath).Use(log).Branch(Branch, updateSubmodules); if (needPopStash) rs = new Commands.Stash(_repo.FullPath).Use(log).Pop("stash@{0}"); diff --git a/src/ViewModels/CreateBranch.cs b/src/ViewModels/CreateBranch.cs index dd190e18..3a99d88c 100644 --- a/src/ViewModels/CreateBranch.cs +++ b/src/ViewModels/CreateBranch.cs @@ -28,7 +28,14 @@ namespace SourceGit.ViewModels public bool CheckoutAfterCreated { get => _repo.Settings.CheckoutBranchOnCreateBranch; - set => _repo.Settings.CheckoutBranchOnCreateBranch = value; + set + { + if (_repo.Settings.CheckoutBranchOnCreateBranch != value) + { + _repo.Settings.CheckoutBranchOnCreateBranch = value; + OnPropertyChanged(); + } + } } public bool IsBareRepository @@ -36,6 +43,18 @@ namespace SourceGit.ViewModels get => _repo.IsBare; } + public bool IsRecurseSubmoduleVisible + { + get; + private set; + } + + public bool RecurseSubmodules + { + get => _repo.Settings.UpdateSubmodulesOnCheckoutBranch; + set => _repo.Settings.UpdateSubmodulesOnCheckoutBranch = value; + } + public CreateBranch(Repository repo, Models.Branch branch) { _repo = repo; @@ -48,6 +67,7 @@ namespace SourceGit.ViewModels BasedOn = branch; DiscardLocalChanges = false; + IsRecurseSubmoduleVisible = repo.Submodules.Count > 0; } public CreateBranch(Repository repo, Models.Commit commit) @@ -57,6 +77,7 @@ namespace SourceGit.ViewModels BasedOn = commit; DiscardLocalChanges = false; + IsRecurseSubmoduleVisible = repo.Submodules.Count > 0; } public CreateBranch(Repository repo, Models.Tag tag) @@ -66,6 +87,7 @@ namespace SourceGit.ViewModels BasedOn = tag; DiscardLocalChanges = false; + IsRecurseSubmoduleVisible = repo.Submodules.Count > 0; } public static ValidationResult ValidateBranchName(string name, ValidationContext ctx) @@ -92,6 +114,7 @@ namespace SourceGit.ViewModels var log = _repo.CreateLog($"Create Branch '{fixedName}'"); Use(log); + var updateSubmodules = IsRecurseSubmoduleVisible && RecurseSubmodules; return Task.Run(() => { bool succ; @@ -119,7 +142,7 @@ namespace SourceGit.ViewModels } } - succ = new Commands.Checkout(_repo.FullPath).Use(log).Branch(fixedName, _baseOnRevision); + succ = new Commands.Checkout(_repo.FullPath).Use(log).Branch(fixedName, _baseOnRevision, updateSubmodules); if (needPopStash) new Commands.Stash(_repo.FullPath).Use(log).Pop("stash@{0}"); } diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index 289d890f..5e8bd40d 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -1154,7 +1154,7 @@ namespace SourceGit.ViewModels if (branch.IsLocal) { - if (_localChangesCount > 0) + if (_localChangesCount > 0 || _submodules.Count > 0) ShowPopup(new Checkout(this, branch.Name)); else ShowAndStartPopup(new Checkout(this, branch.Name)); diff --git a/src/Views/Checkout.axaml b/src/Views/Checkout.axaml index 3cdfd94e..42b9cec5 100644 --- a/src/Views/Checkout.axaml +++ b/src/Views/Checkout.axaml @@ -17,6 +17,7 @@ + + Content="{DynamicResource Text.Checkout.LocalChanges.Discard}"/> + + diff --git a/src/Views/CreateBranch.axaml b/src/Views/CreateBranch.axaml index a5f1f212..e77f8751 100644 --- a/src/Views/CreateBranch.axaml +++ b/src/Views/CreateBranch.axaml @@ -14,7 +14,7 @@ - + + + + + + + + + + + From 4bc5b90e6b721c562fa1f57a35e9f002962eeb2c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 6 May 2025 03:13:13 +0000 Subject: [PATCH 077/392] doc: Update translation status and sort locale files (cherry picked from commit 15445d02379020144239886bc87380ae38c2018a) --- TRANSLATION.md | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index facab0ab..242b950a 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -6,7 +6,7 @@ This document shows the translation status of each locale file in the repository ### ![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen) -### ![de__DE](https://img.shields.io/badge/de__DE-95.07%25-yellow) +### ![de__DE](https://img.shields.io/badge/de__DE-94.95%25-yellow)
Missing keys in de_DE.axaml @@ -19,6 +19,7 @@ This document shows the translation status of each locale file in the repository - Text.Bisect.Skip - Text.Bisect.WaitingForRange - Text.BranchUpstreamInvalid +- Text.Checkout.RecurseSubmodules - Text.CommitCM.CopyAuthor - Text.CommitCM.CopyCommitter - Text.CommitCM.CopySubject @@ -52,9 +53,16 @@ This document shows the translation status of each locale file in the repository
-### ![es__ES](https://img.shields.io/badge/es__ES-%E2%88%9A-brightgreen) +### ![es__ES](https://img.shields.io/badge/es__ES-99.87%25-yellow) -### ![fr__FR](https://img.shields.io/badge/fr__FR-96.37%25-yellow) +
+Missing keys in es_ES.axaml + +- Text.Checkout.RecurseSubmodules + +
+ +### ![fr__FR](https://img.shields.io/badge/fr__FR-96.24%25-yellow)
Missing keys in fr_FR.axaml @@ -66,6 +74,7 @@ This document shows the translation status of each locale file in the repository - Text.Bisect.Good - Text.Bisect.Skip - Text.Bisect.WaitingForRange +- Text.Checkout.RecurseSubmodules - Text.CommitCM.CopyAuthor - Text.CommitCM.CopyCommitter - Text.CommitCM.CopySubject @@ -90,7 +99,7 @@ This document shows the translation status of each locale file in the repository
-### ![it__IT](https://img.shields.io/badge/it__IT-96.11%25-yellow) +### ![it__IT](https://img.shields.io/badge/it__IT-95.98%25-yellow)
Missing keys in it_IT.axaml @@ -102,6 +111,7 @@ This document shows the translation status of each locale file in the repository - Text.Bisect.Good - Text.Bisect.Skip - Text.Bisect.WaitingForRange +- Text.Checkout.RecurseSubmodules - Text.CommitCM.CopyAuthor - Text.CommitCM.CopyCommitter - Text.CommitCM.CopySubject @@ -128,7 +138,7 @@ This document shows the translation status of each locale file in the repository
-### ![ja__JP](https://img.shields.io/badge/ja__JP-96.11%25-yellow) +### ![ja__JP](https://img.shields.io/badge/ja__JP-95.98%25-yellow)
Missing keys in ja_JP.axaml @@ -140,6 +150,7 @@ This document shows the translation status of each locale file in the repository - Text.Bisect.Good - Text.Bisect.Skip - Text.Bisect.WaitingForRange +- Text.Checkout.RecurseSubmodules - Text.CommitCM.CopyAuthor - Text.CommitCM.CopyCommitter - Text.CommitCM.CopySubject @@ -166,7 +177,7 @@ This document shows the translation status of each locale file in the repository
-### ![pt__BR](https://img.shields.io/badge/pt__BR-87.68%25-yellow) +### ![pt__BR](https://img.shields.io/badge/pt__BR-87.56%25-yellow)
Missing keys in pt_BR.axaml @@ -187,6 +198,7 @@ This document shows the translation status of each locale file in the repository - Text.BranchCM.CustomAction - Text.BranchCM.MergeMultiBranches - Text.BranchUpstreamInvalid +- Text.Checkout.RecurseSubmodules - Text.Clone.RecurseSubmodules - Text.CommitCM.CopyAuthor - Text.CommitCM.CopyCommitter @@ -269,9 +281,16 @@ This document shows the translation status of each locale file in the repository
-### ![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen) +### ![ru__RU](https://img.shields.io/badge/ru__RU-99.87%25-yellow) -### ![ta__IN](https://img.shields.io/badge/ta__IN-96.37%25-yellow) +
+Missing keys in ru_RU.axaml + +- Text.Checkout.RecurseSubmodules + +
+ +### ![ta__IN](https://img.shields.io/badge/ta__IN-96.24%25-yellow)
Missing keys in ta_IN.axaml @@ -283,6 +302,7 @@ This document shows the translation status of each locale file in the repository - Text.Bisect.Good - Text.Bisect.Skip - Text.Bisect.WaitingForRange +- Text.Checkout.RecurseSubmodules - Text.CommitCM.CopyAuthor - Text.CommitCM.CopyCommitter - Text.CommitCM.CopySubject @@ -307,7 +327,7 @@ This document shows the translation status of each locale file in the repository
-### ![uk__UA](https://img.shields.io/badge/uk__UA-97.54%25-yellow) +### ![uk__UA](https://img.shields.io/badge/uk__UA-97.41%25-yellow)
Missing keys in uk_UA.axaml @@ -319,6 +339,7 @@ This document shows the translation status of each locale file in the repository - Text.Bisect.Good - Text.Bisect.Skip - Text.Bisect.WaitingForRange +- Text.Checkout.RecurseSubmodules - Text.CommitCM.CopyAuthor - Text.CommitCM.CopyCommitter - Text.CommitCM.CopySubject From eae6d1078487dce25f160b630b0fc7f7645595c8 Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 6 May 2025 12:17:17 +0800 Subject: [PATCH 078/392] enhance: only log exception in popup task (#1281) Signed-off-by: leo --- src/App.axaml.cs | 2 +- src/ViewModels/LauncherPage.cs | 23 ++++++++++++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/App.axaml.cs b/src/App.axaml.cs index c54b616d..6e45164d 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -78,7 +78,7 @@ namespace SourceGit return builder; } - private static void LogException(Exception ex) + public static void LogException(Exception ex) { if (ex == null) return; diff --git a/src/ViewModels/LauncherPage.cs b/src/ViewModels/LauncherPage.cs index b97ef6f7..3affe71d 100644 --- a/src/ViewModels/LauncherPage.cs +++ b/src/ViewModels/LauncherPage.cs @@ -100,23 +100,32 @@ namespace SourceGit.ViewModels public async void ProcessPopup() { - if (_popup is { InProgress: false }) + if (_popup is { InProgress: false } dump) { - if (!_popup.Check()) + if (!dump.Check()) return; - _popup.InProgress = true; - var task = _popup.Sure(); + dump.InProgress = true; + var task = dump.Sure(); + var finished = false; if (task != null) { - var finished = await task; - _popup.InProgress = false; + try + { + finished = await task; + } + catch (Exception e) + { + App.LogException(e); + } + + dump.InProgress = false; if (finished) Popup = null; } else { - _popup.InProgress = false; + dump.InProgress = false; Popup = null; } } From e4e2f7b3a7cbad3e1203f0b0fd7a9696e05a2235 Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 6 May 2025 14:49:54 +0800 Subject: [PATCH 079/392] ux: use smaller font size for inline code in commit subject Signed-off-by: leo --- src/Views/CommitSubjectPresenter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Views/CommitSubjectPresenter.cs b/src/Views/CommitSubjectPresenter.cs index 86e0fd54..38e964be 100644 --- a/src/Views/CommitSubjectPresenter.cs +++ b/src/Views/CommitSubjectPresenter.cs @@ -299,7 +299,7 @@ namespace SourceGit.Views CultureInfo.CurrentCulture, FlowDirection.LeftToRight, codeTypeface, - fontSize, + fontSize - 0.5, foreground); _inlines.Add(new Inline(x, link, elem)); x += link.WidthIncludingTrailingWhitespace + 8; From 93a5d7baea923483fe10fbae8414e59a934f7c11 Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 6 May 2025 15:51:57 +0800 Subject: [PATCH 080/392] feature: supports to visit remote repository in web browser (#1265) - combine `Open in File Manager`, `Open in Terminal` and `Open with external editor` into one dropdown menu - add `Visit $REMOTE in Browser` Signed-off-by: leo --- src/Resources/Locales/en_US.axaml | 1 + src/Resources/Locales/zh_CN.axaml | 1 + src/Resources/Locales/zh_TW.axaml | 1 + src/ViewModels/Repository.cs | 96 ++++++++++++++++++++++--------- src/Views/RepositoryToolbar.axaml | 10 +--- 5 files changed, 73 insertions(+), 36 deletions(-) diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 640cb8f3..0ebeeeb0 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -626,6 +626,7 @@ Open in Terminal Use relative time in histories View Logs + Visit '{0}' in Browser WORKTREES ADD WORKTREE PRUNE diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 5db542b1..6bf7f5d1 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -630,6 +630,7 @@ 在终端中打开 在提交列表中使用相对时间 查看命令日志 + 访问远程仓库 '{0}' 工作树列表 新增工作树 清理 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 6fd380ec..9ed35c8a 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -630,6 +630,7 @@ 在終端機中開啟 在提交列表中使用相對時間 檢視 Git 指令記錄 + 檢視遠端存放庫 '{0}' 工作區列表 新增工作區 清理 diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index 5e8bd40d..bf084fb0 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -583,45 +583,87 @@ namespace SourceGit.ViewModels Task.Run(RefreshStashes); } - public void OpenInFileManager() - { - Native.OS.OpenInFileManager(_fullpath); - } - - public void OpenInTerminal() - { - Native.OS.OpenTerminal(_fullpath); - } - public ContextMenu CreateContextMenuForExternalTools() { - var tools = Native.OS.ExternalTools; - if (tools.Count == 0) - { - App.RaiseException(_fullpath, "No available external editors found!"); - return null; - } - var menu = new ContextMenu(); menu.Placement = PlacementMode.BottomEdgeAlignedLeft; + RenderOptions.SetBitmapInterpolationMode(menu, BitmapInterpolationMode.HighQuality); + RenderOptions.SetEdgeMode(menu, EdgeMode.Antialias); + RenderOptions.SetTextRenderingMode(menu, TextRenderingMode.Antialias); - foreach (var tool in tools) + var explore = new MenuItem(); + explore.Header = App.Text("Repository.Explore"); + explore.Icon = App.CreateMenuIcon("Icons.Explore"); + explore.Click += (_, e) => { - var dupTool = tool; + Native.OS.OpenInFileManager(_fullpath); + e.Handled = true; + }; - var item = new MenuItem(); - item.Header = App.Text("Repository.OpenIn", dupTool.Name); - item.Icon = new Image { Width = 16, Height = 16, Source = dupTool.IconImage }; - item.Click += (_, e) => + var terminal = new MenuItem(); + terminal.Header = App.Text("Repository.Terminal"); + terminal.Icon = App.CreateMenuIcon("Icons.Terminal"); + terminal.Click += (_, e) => + { + Native.OS.OpenTerminal(_fullpath); + e.Handled = true; + }; + + menu.Items.Add(explore); + menu.Items.Add(terminal); + + var tools = Native.OS.ExternalTools; + if (tools.Count > 0) + { + menu.Items.Add(new MenuItem() { Header = "-" }); + + foreach (var tool in Native.OS.ExternalTools) { - dupTool.Open(_fullpath); - e.Handled = true; - }; + var dupTool = tool; - menu.Items.Add(item); + var item = new MenuItem(); + item.Header = App.Text("Repository.OpenIn", dupTool.Name); + item.Icon = new Image { Width = 16, Height = 16, Source = dupTool.IconImage }; + item.Click += (_, e) => + { + dupTool.Open(_fullpath); + e.Handled = true; + }; + + menu.Items.Add(item); + } } + var urls = new Dictionary(); + foreach (var r in _remotes) + { + if (r.TryGetVisitURL(out var visit)) + urls.Add(r.Name, visit); + } + + if (urls.Count > 0) + { + menu.Items.Add(new MenuItem() { Header = "-" }); + + foreach (var url in urls) + { + var name = url.Key; + var addr = url.Value; + + var item = new MenuItem(); + item.Header = App.Text("Repository.Visit", name); + item.Icon = App.CreateMenuIcon("Icons.Remotes"); + item.Click += (_, e) => + { + Native.OS.OpenBrowser(addr); + e.Handled = true; + }; + + menu.Items.Add(item); + } + } + return menu; } diff --git a/src/Views/RepositoryToolbar.axaml b/src/Views/RepositoryToolbar.axaml index 85b2ad76..208ff725 100644 --- a/src/Views/RepositoryToolbar.axaml +++ b/src/Views/RepositoryToolbar.axaml @@ -9,16 +9,8 @@ x:DataType="vm:Repository"> - - - - + - + - + diff --git a/src/Views/Repository.axaml.cs b/src/Views/Repository.axaml.cs index a9f5bfee..3e30d161 100644 --- a/src/Views/Repository.axaml.cs +++ b/src/Views/Repository.axaml.cs @@ -403,6 +403,28 @@ namespace SourceGit.Views e.Handled = true; } + private void OnOpenSortLocalBranchMenu(object sender, RoutedEventArgs e) + { + if (sender is Button button && DataContext is ViewModels.Repository repo) + { + var menu = repo.CreateContextMenuForBranchSortMode(true); + menu?.Open(button); + } + + e.Handled = true; + } + + private void OnOpenSortRemoteBranchMenu(object sender, RoutedEventArgs e) + { + if (sender is Button button && DataContext is ViewModels.Repository repo) + { + var menu = repo.CreateContextMenuForBranchSortMode(false); + menu?.Open(button); + } + + e.Handled = true; + } + private void OnOpenSortTagMenu(object sender, RoutedEventArgs e) { if (sender is Button button && DataContext is ViewModels.Repository repo) From c8e21673e48e79f551ef4fd61635a9dde3bbabcb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 6 May 2025 10:24:59 +0000 Subject: [PATCH 083/392] doc: Update translation status and sort locale files --- TRANSLATION.md | 45 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index 2632bc4f..9b499ce2 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -6,7 +6,7 @@ This document shows the translation status of each locale file in the repository ### ![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen) -### ![de__DE](https://img.shields.io/badge/de__DE-94.83%25-yellow) +### ![de__DE](https://img.shields.io/badge/de__DE-94.46%25-yellow)
Missing keys in de_DE.axaml @@ -38,6 +38,9 @@ This document shows the translation status of each locale file in the repository - Text.Preferences.Appearance.EditorTabWidth - Text.Preferences.General.ShowTagsInGraph - Text.Preferences.Git.IgnoreCRAtEOLInDiff +- Text.Repository.BranchSort +- Text.Repository.BranchSort.ByCommitterDate +- Text.Repository.BranchSort.ByName - Text.Repository.Search.ByContent - Text.Repository.ViewLogs - Text.Repository.Visit @@ -54,17 +57,20 @@ This document shows the translation status of each locale file in the repository
-### ![es__ES](https://img.shields.io/badge/es__ES-99.74%25-yellow) +### ![es__ES](https://img.shields.io/badge/es__ES-99.36%25-yellow)
Missing keys in es_ES.axaml - Text.Checkout.RecurseSubmodules +- Text.Repository.BranchSort +- Text.Repository.BranchSort.ByCommitterDate +- Text.Repository.BranchSort.ByName - Text.Repository.Visit
-### ![fr__FR](https://img.shields.io/badge/fr__FR-96.12%25-yellow) +### ![fr__FR](https://img.shields.io/badge/fr__FR-95.75%25-yellow)
Missing keys in fr_FR.axaml @@ -87,6 +93,9 @@ This document shows the translation status of each locale file in the repository - Text.ConfirmEmptyCommit.StageAllThenCommit - Text.ConfirmEmptyCommit.WithLocalChanges - Text.Preferences.Git.IgnoreCRAtEOLInDiff +- Text.Repository.BranchSort +- Text.Repository.BranchSort.ByCommitterDate +- Text.Repository.BranchSort.ByName - Text.Repository.Search.ByContent - Text.Repository.ViewLogs - Text.Repository.Visit @@ -102,7 +111,7 @@ This document shows the translation status of each locale file in the repository
-### ![it__IT](https://img.shields.io/badge/it__IT-95.86%25-yellow) +### ![it__IT](https://img.shields.io/badge/it__IT-95.49%25-yellow)
Missing keys in it_IT.axaml @@ -127,6 +136,9 @@ This document shows the translation status of each locale file in the repository - Text.CopyFullPath - Text.Preferences.General.ShowTagsInGraph - Text.Preferences.Git.IgnoreCRAtEOLInDiff +- Text.Repository.BranchSort +- Text.Repository.BranchSort.ByCommitterDate +- Text.Repository.BranchSort.ByName - Text.Repository.Search.ByContent - Text.Repository.ViewLogs - Text.Repository.Visit @@ -142,7 +154,7 @@ This document shows the translation status of each locale file in the repository
-### ![ja__JP](https://img.shields.io/badge/ja__JP-95.86%25-yellow) +### ![ja__JP](https://img.shields.io/badge/ja__JP-95.49%25-yellow)
Missing keys in ja_JP.axaml @@ -165,6 +177,9 @@ This document shows the translation status of each locale file in the repository - Text.ConfirmEmptyCommit.StageAllThenCommit - Text.ConfirmEmptyCommit.WithLocalChanges - Text.Preferences.Git.IgnoreCRAtEOLInDiff +- Text.Repository.BranchSort +- Text.Repository.BranchSort.ByCommitterDate +- Text.Repository.BranchSort.ByName - Text.Repository.FilterCommits - Text.Repository.Search.ByContent - Text.Repository.Tags.OrderByNameDes @@ -182,7 +197,7 @@ This document shows the translation status of each locale file in the repository
-### ![pt__BR](https://img.shields.io/badge/pt__BR-87.45%25-yellow) +### ![pt__BR](https://img.shields.io/badge/pt__BR-87.11%25-yellow)
Missing keys in pt_BR.axaml @@ -249,6 +264,9 @@ This document shows the translation status of each locale file in the repository - Text.Preferences.General.ShowTagsInGraph - Text.Preferences.Git.IgnoreCRAtEOLInDiff - Text.Preferences.Git.SSLVerify +- Text.Repository.BranchSort +- Text.Repository.BranchSort.ByCommitterDate +- Text.Repository.BranchSort.ByName - Text.Repository.FilterCommits - Text.Repository.HistoriesLayout - Text.Repository.HistoriesLayout.Horizontal @@ -287,17 +305,20 @@ This document shows the translation status of each locale file in the repository
-### ![ru__RU](https://img.shields.io/badge/ru__RU-99.74%25-yellow) +### ![ru__RU](https://img.shields.io/badge/ru__RU-99.36%25-yellow)
Missing keys in ru_RU.axaml - Text.Checkout.RecurseSubmodules +- Text.Repository.BranchSort +- Text.Repository.BranchSort.ByCommitterDate +- Text.Repository.BranchSort.ByName - Text.Repository.Visit
-### ![ta__IN](https://img.shields.io/badge/ta__IN-96.12%25-yellow) +### ![ta__IN](https://img.shields.io/badge/ta__IN-95.75%25-yellow)
Missing keys in ta_IN.axaml @@ -320,6 +341,9 @@ This document shows the translation status of each locale file in the repository - Text.ConfirmEmptyCommit.StageAllThenCommit - Text.ConfirmEmptyCommit.WithLocalChanges - Text.Preferences.Git.IgnoreCRAtEOLInDiff +- Text.Repository.BranchSort +- Text.Repository.BranchSort.ByCommitterDate +- Text.Repository.BranchSort.ByName - Text.Repository.Search.ByContent - Text.Repository.ViewLogs - Text.Repository.Visit @@ -335,7 +359,7 @@ This document shows the translation status of each locale file in the repository
-### ![uk__UA](https://img.shields.io/badge/uk__UA-97.28%25-yellow) +### ![uk__UA](https://img.shields.io/badge/uk__UA-96.91%25-yellow)
Missing keys in uk_UA.axaml @@ -354,6 +378,9 @@ This document shows the translation status of each locale file in the repository - Text.CommitMessageTextBox.SubjectCount - Text.ConfigureWorkspace.Name - Text.Preferences.Git.IgnoreCRAtEOLInDiff +- Text.Repository.BranchSort +- Text.Repository.BranchSort.ByCommitterDate +- Text.Repository.BranchSort.ByName - Text.Repository.Search.ByContent - Text.Repository.ViewLogs - Text.Repository.Visit From bbc840a5cb98c53dd4d831a34b1ad999b4a3642c Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 6 May 2025 19:26:06 +0800 Subject: [PATCH 084/392] perf: set/update `TimeToSort` while creating branch nodes Signed-off-by: leo --- src/ViewModels/BranchTreeNode.cs | 40 +++++--------------------------- 1 file changed, 6 insertions(+), 34 deletions(-) diff --git a/src/ViewModels/BranchTreeNode.cs b/src/ViewModels/BranchTreeNode.cs index 005d8b90..d0289f6a 100644 --- a/src/ViewModels/BranchTreeNode.cs +++ b/src/ViewModels/BranchTreeNode.cs @@ -120,28 +120,19 @@ namespace SourceGit.ViewModels folders.Clear(); if (_localSortMode == Models.BranchSortMode.Name) - { SortNodesByName(_locals); - } else - { - SetTimeToSortRecusive(_locals); SortNodesByTime(_locals); - } if (_remoteSortMode == Models.BranchSortMode.Name) - { SortNodesByName(_remotes); - } else - { - SetTimeToSortRecusive(_remotes); SortNodesByTime(_remotes); - } } private void MakeBranchNode(Models.Branch branch, List roots, Dictionary folders, string prefix, bool bForceExpanded) { + var time = branch.CommitterDate; var fullpath = $"{prefix}/{branch.Name}"; var sepIdx = branch.Name.IndexOf('/', StringComparison.Ordinal); if (sepIdx == -1 || branch.IsDetachedHead) @@ -152,7 +143,7 @@ namespace SourceGit.ViewModels Path = fullpath, Backend = branch, IsExpanded = false, - TimeToSort = branch.CommitterDate, + TimeToSort = time, }); return; } @@ -167,6 +158,7 @@ namespace SourceGit.ViewModels if (folders.TryGetValue(folder, out var val)) { lastFolder = val; + lastFolder.TimeToSort = Math.Max(lastFolder.TimeToSort, time); if (!lastFolder.IsExpanded) lastFolder.IsExpanded |= (branch.IsCurrent || _expanded.Contains(folder)); } @@ -177,6 +169,7 @@ namespace SourceGit.ViewModels Name = name, Path = folder, IsExpanded = bForceExpanded || branch.IsCurrent || _expanded.Contains(folder), + TimeToSort = time, }; roots.Add(lastFolder); folders.Add(folder, lastFolder); @@ -188,6 +181,7 @@ namespace SourceGit.ViewModels Name = name, Path = folder, IsExpanded = bForceExpanded || branch.IsCurrent || _expanded.Contains(folder), + TimeToSort = time, }; lastFolder.Children.Add(cur); folders.Add(folder, cur); @@ -204,7 +198,7 @@ namespace SourceGit.ViewModels Path = fullpath, Backend = branch, IsExpanded = false, - TimeToSort = branch.CommitterDate, + TimeToSort = time, }); } @@ -253,28 +247,6 @@ namespace SourceGit.ViewModels SortNodesByTime(node.Children); } - private ulong SetTimeToSortRecusive(List nodes) - { - var recent = (ulong)0; - - foreach (var node in nodes) - { - if (node.Backend is Models.Branch) - { - recent = Math.Max(recent, node.TimeToSort); - continue; - } - - var time = SetTimeToSortRecusive(node.Children); - recent = Math.Max(recent, time); - - if (node.Backend is not Models.Remote) - node.TimeToSort = time; - } - - return recent; - } - private readonly Models.BranchSortMode _localSortMode = Models.BranchSortMode.Name; private readonly Models.BranchSortMode _remoteSortMode = Models.BranchSortMode.Name; private readonly List _locals = new List(); From ddf643c081791002fbea743a3e1a3fb911769660 Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 6 May 2025 20:52:43 +0800 Subject: [PATCH 085/392] ux: new style for revision/branch compare targets Signed-off-by: leo --- src/Views/BranchCompare.axaml | 6 +++--- src/Views/RevisionCompare.axaml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Views/BranchCompare.axaml b/src/Views/BranchCompare.axaml index 4f6f312e..1fe66e9b 100644 --- a/src/Views/BranchCompare.axaml +++ b/src/Views/BranchCompare.axaml @@ -46,7 +46,7 @@ - + - + - + @@ -47,7 +47,7 @@ - + From a413df6f89841a93320b3b657368cfd7793b391f Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 6 May 2025 20:56:45 +0800 Subject: [PATCH 086/392] code_style: run `dotnet format` Signed-off-by: leo --- src/ViewModels/LauncherPage.cs | 2 +- src/ViewModels/Repository.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ViewModels/LauncherPage.cs b/src/ViewModels/LauncherPage.cs index 3affe71d..8a59d246 100644 --- a/src/ViewModels/LauncherPage.cs +++ b/src/ViewModels/LauncherPage.cs @@ -118,7 +118,7 @@ namespace SourceGit.ViewModels { App.LogException(e); } - + dump.InProgress = false; if (finished) Popup = null; diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index 54070cf5..8661cae2 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -662,7 +662,7 @@ namespace SourceGit.ViewModels menu.Items.Add(item); } - } + } return menu; } From 417ab3ecc276547e5e7ef34d0d4861cb64a20b50 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 7 May 2025 09:52:26 +0800 Subject: [PATCH 087/392] ux: layout for revision compare targets Signed-off-by: leo --- src/Views/RevisionCompare.axaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Views/RevisionCompare.axaml b/src/Views/RevisionCompare.axaml index 8f5ebeee..6367c866 100644 --- a/src/Views/RevisionCompare.axaml +++ b/src/Views/RevisionCompare.axaml @@ -28,12 +28,12 @@ - + - + From 6b050fa55741652e0fcfeb6d7fc4049dedbcf611 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20G=C3=B6ttfert?= <45085620+ChrisGoettfert@users.noreply.github.com> Date: Wed, 7 May 2025 13:08:39 +0200 Subject: [PATCH 088/392] localization: updated german translations (#1284) --- TRANSLATION.md | 44 ------------------------------- src/Resources/Locales/de_DE.axaml | 43 ++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 44 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index 9b499ce2..29a9b46f 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -11,50 +11,6 @@ This document shows the translation status of each locale file in the repository
Missing keys in de_DE.axaml -- Text.Bisect -- Text.Bisect.Abort -- Text.Bisect.Bad -- Text.Bisect.Detecting -- Text.Bisect.Good -- Text.Bisect.Skip -- Text.Bisect.WaitingForRange -- Text.BranchUpstreamInvalid -- Text.Checkout.RecurseSubmodules -- Text.CommitCM.CopyAuthor -- Text.CommitCM.CopyCommitter -- Text.CommitCM.CopySubject -- Text.CommitMessageTextBox.SubjectCount -- Text.Configure.CustomAction.WaitForExit -- Text.Configure.Git.PreferredMergeMode -- Text.Configure.IssueTracker.AddSampleAzure -- Text.ConfirmEmptyCommit.Continue -- Text.ConfirmEmptyCommit.NoLocalChanges -- Text.ConfirmEmptyCommit.StageAllThenCommit -- Text.ConfirmEmptyCommit.WithLocalChanges -- Text.CopyFullPath -- Text.Diff.First -- Text.Diff.Last -- Text.Preferences.AI.Streaming -- Text.Preferences.Appearance.EditorTabWidth -- Text.Preferences.General.ShowTagsInGraph -- Text.Preferences.Git.IgnoreCRAtEOLInDiff -- Text.Repository.BranchSort -- Text.Repository.BranchSort.ByCommitterDate -- Text.Repository.BranchSort.ByName -- Text.Repository.Search.ByContent -- Text.Repository.ViewLogs -- Text.Repository.Visit -- Text.StashCM.SaveAsPatch -- Text.ViewLogs -- Text.ViewLogs.Clear -- Text.ViewLogs.CopyLog -- Text.ViewLogs.Delete -- Text.WorkingCopy.ConfirmCommitWithFilter -- Text.WorkingCopy.Conflicts.OpenExternalMergeTool -- Text.WorkingCopy.Conflicts.OpenExternalMergeToolAllConflicts -- Text.WorkingCopy.Conflicts.UseMine -- Text.WorkingCopy.Conflicts.UseTheirs -
### ![es__ES](https://img.shields.io/badge/es__ES-99.36%25-yellow) diff --git a/src/Resources/Locales/de_DE.axaml b/src/Resources/Locales/de_DE.axaml index 8eb49a3e..30750931 100644 --- a/src/Resources/Locales/de_DE.axaml +++ b/src/Resources/Locales/de_DE.axaml @@ -40,6 +40,13 @@ KEINE ALS UNVERÄNDERT ANGENOMMENEN DATEIEN ENTFERNEN BINÄRE DATEI NICHT UNTERSTÜTZT!!! + Bisect + Abbrechen + Schlecht + Bisecting. Ist der aktuelle HEAD gut oder fehlerhaft? + Gut + Überspringen + Bisecting. Aktuellen Commit als gut oder schlecht markieren und einen anderen auschecken. Blame BLAME WIRD BEI DIESER DATEI NICHT UNTERSTÜTZT!!! Auschecken von ${0}$... @@ -62,6 +69,7 @@ Benenne ${0}$ um... Setze verfolgten Branch... Branch Vergleich + Ungültiger upstream! Bytes ABBRECHEN Auf Vorgänger-Revision zurücksetzen @@ -78,6 +86,7 @@ Lokale Änderungen: Verwerfen Stashen & wieder anwenden + Alle Submodule updaten Branch: Cherry Pick Quelle an Commit-Nachricht anhängen @@ -102,8 +111,11 @@ Mehrere cherry-picken Mit HEAD vergleichen Mit Worktree vergleichen + Author + Committer Information SHA + Betreff Benutzerdefinierte Aktion Interactives Rebase von ${0}$ auf diesen Commit Merge in ${0}$ hinein @@ -135,6 +147,7 @@ SHA Im Browser öffnen Details + Betreff Commit-Nachricht Repository Einstellungen COMMIT TEMPLATE @@ -149,13 +162,16 @@ Branch Commit Repository + Auf Beenden der Aktion warten Email Adresse Email Adresse GIT Remotes automatisch fetchen Minute(n) Standard Remote + Bevorzugter Merge Modus TICKETSYSTEM + Beispiel Azure DevOps Rule hinzufügen Beispiel für Gitee Issue Regel einfügen Beispiel für Gitee Pull Request Regel einfügen Beispiel für Github-Regel hinzufügen @@ -178,6 +194,10 @@ Farbe Name Zuletzt geöffnete Tabs beim Starten wiederherstellen + WEITER + Leerer Commit erkannt! Möchtest du trotzdem fortfahren (--allow-empty)? + ALLES STAGEN & COMMITTEN + Leerer Commit erkannt! Möchtest du trotzdem fortfahren (--allow-empty) oder alle Änderungen stagen und dann committen? Konventionelle Commit-Hilfe Breaking Change: Geschlossenes Ticket: @@ -187,6 +207,7 @@ Typ der Änderung: Kopieren Kopiere gesamten Text + Ganzen Pfad kopieren Pfad kopieren Branch erstellen... Basierend auf: @@ -236,7 +257,9 @@ ALT Kopieren Dateimodus geändert + Erste Differenz Ignoriere Leerzeichenänderungen + Letzte Differenz LFS OBJEKT ÄNDERUNG Nächste Änderung KEINE ÄNDERUNG ODER NUR ZEILEN-ENDE ÄNDERUNGEN @@ -447,8 +470,10 @@ Modell Name Server + Streaming aktivieren DARSTELLUNG Standardschriftart + Editor Tab Breite Schriftgröße Standard Texteditor @@ -469,6 +494,7 @@ Commit-Historie Zeige Autor Zeitpunkt anstatt Commit Zeitpunkt Zeige Nachfolger in den Commit Details + Zeige Tags im Commit Graph Längenvorgabe für Commit-Nachrichten GIT Aktiviere Auto-CRLF @@ -476,6 +502,7 @@ Benutzer Email Globale Git Benutzer Email Aktivere --prune beim fetchen + Aktiviere --ignore-cr-at-eol beim Unterschied Diese App setzt Git (>= 2.23.0) voraus Installationspfad Aktiviere HTTP SSL Verifizierung @@ -549,6 +576,9 @@ Branch: ABBRECHEN Änderungen automatisch von Remote fetchen... + Sortieren + Nach Commit Datum + Nach Name Aufräumen (GC & Prune) Führt `git gc` auf diesem Repository aus. Filter aufheben @@ -583,6 +613,7 @@ Commit suchen Autor Committer + Inhalt Dateiname Commit-Nachricht SHA @@ -601,6 +632,8 @@ Sortiere Öffne im Terminal Verwende relative Zeitangaben in Verlauf + Logs ansehen + Öffne '{0}' im Browser WORKTREES WORKTREE HINZUFÜGEN PRUNE @@ -651,6 +684,7 @@ Lokale Änderungen stashen Anwenden Entfernen + Als Path speichern... Stash entfernen Entfernen: Stashes @@ -685,6 +719,10 @@ Submodul: Verwende `--remote` Option URL: + Logs + ALLES LÖSCHEN + Kopieren + Löschen Warnung Willkommensseite Erstelle Gruppe @@ -714,8 +752,13 @@ Klick-Ereignis auslösen Commit (Bearbeitung) Alle Änderungen stagen und committen + Du hast {0} Datei(en) gestaged, aber nur {1} werden angezeigt ({2} sind herausgefiltert). Willst du trotzdem fortfahren? KONFLIKTE ERKANNT + EXTERNES MERGE-TOOL ÖFFNEN + ALLE KONFLIKTE IN EXTERNEM MERGE-TOOL ÖFFNEN DATEI KONFLIKTE GELÖST + MEINE VERSION VERWENDEN + DEREN VERSION VERWENDEN NICHT-VERFOLGTE DATEIEN INKLUDIEREN KEINE BISHERIGEN COMMIT-NACHRICHTEN KEINE COMMIT TEMPLATES From 0a7b9733886d035b45c180c1211c4346bb3b6689 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 7 May 2025 11:08:51 +0000 Subject: [PATCH 089/392] doc: Update translation status and sort locale files --- TRANSLATION.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index 29a9b46f..7b7bf212 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -6,12 +6,7 @@ This document shows the translation status of each locale file in the repository ### ![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen) -### ![de__DE](https://img.shields.io/badge/de__DE-94.46%25-yellow) - -
-Missing keys in de_DE.axaml - -
+### ![de__DE](https://img.shields.io/badge/de__DE-%E2%88%9A-brightgreen) ### ![es__ES](https://img.shields.io/badge/es__ES-99.36%25-yellow) From 6df38ad97043dc2babee6f1709e56297d0d3629b Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 7 May 2025 20:23:06 +0800 Subject: [PATCH 090/392] ux: new style for inline code in commit subject Signed-off-by: leo --- src/Resources/Themes.axaml | 4 ++-- src/Views/CommitSubjectPresenter.cs | 37 ++++++++++++++++++----------- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/Resources/Themes.axaml b/src/Resources/Themes.axaml index f33e71d2..3194c8c3 100644 --- a/src/Resources/Themes.axaml +++ b/src/Resources/Themes.axaml @@ -25,7 +25,7 @@ #A7E1A7 #F19B9D #0000EE - #FFE5E5E5 + #FFE4E4E4 @@ -52,7 +52,7 @@ #A0308D3C #A09F4247 #4DAAFC - #FF2E2E2E + #FF383838 diff --git a/src/Views/CommitSubjectPresenter.cs b/src/Views/CommitSubjectPresenter.cs index 38e964be..18902462 100644 --- a/src/Views/CommitSubjectPresenter.cs +++ b/src/Views/CommitSubjectPresenter.cs @@ -104,23 +104,32 @@ namespace SourceGit.Views if (_inlines.Count == 0) return; - var height = Bounds.Height; - var width = Bounds.Width; - foreach (var inline in _inlines) + var ro = new RenderOptions() { - if (inline.X > width) - return; + TextRenderingMode = TextRenderingMode.SubpixelAntialias, + EdgeMode = EdgeMode.Antialias + }; - if (inline.Element is { Type: Models.InlineElementType.Code }) + using (context.PushRenderOptions(ro)) + { + var height = Bounds.Height; + var width = Bounds.Width; + foreach (var inline in _inlines) { - var rect = new Rect(inline.X, (height - inline.Text.Height - 2) * 0.5, inline.Text.WidthIncludingTrailingWhitespace + 8, inline.Text.Height + 2); - var roundedRect = new RoundedRect(rect, new CornerRadius(4)); - context.DrawRectangle(InlineCodeBackground, null, roundedRect); - context.DrawText(inline.Text, new Point(inline.X + 4, (height - inline.Text.Height) * 0.5)); - } - else - { - context.DrawText(inline.Text, new Point(inline.X, (height - inline.Text.Height) * 0.5)); + if (inline.X > width) + return; + + if (inline.Element is { Type: Models.InlineElementType.Code }) + { + var rect = new Rect(inline.X, (height - inline.Text.Height - 2) * 0.5, inline.Text.WidthIncludingTrailingWhitespace + 8, inline.Text.Height + 2); + var roundedRect = new RoundedRect(rect, new CornerRadius(4)); + context.DrawRectangle(InlineCodeBackground, null, roundedRect); + context.DrawText(inline.Text, new Point(inline.X + 4, (height - inline.Text.Height) * 0.5)); + } + else + { + context.DrawText(inline.Text, new Point(inline.X, (height - inline.Text.Height) * 0.5)); + } } } } From 832fcd7487c4ac384eaa09e50825487286b67bf5 Mon Sep 17 00:00:00 2001 From: leo Date: Thu, 8 May 2025 12:22:23 +0800 Subject: [PATCH 091/392] fix: offset of commit graph does not look quite right (#1287) This is because that when using `VirtualizingStackPanel`, the `Bounds.Height` of `ListBoxItem` may not be the same with its `Height` setted in axaml. Signed-off-by: leo --- src/Models/CommitGraph.cs | 4 +-- src/Views/CommitGraph.cs | 76 +++++++++++++++++++++++++-------------- src/Views/Histories.axaml | 2 +- 3 files changed, 52 insertions(+), 30 deletions(-) diff --git a/src/Models/CommitGraph.cs b/src/Models/CommitGraph.cs index 77209751..01488656 100644 --- a/src/Models/CommitGraph.cs +++ b/src/Models/CommitGraph.cs @@ -64,8 +64,8 @@ namespace SourceGit.Models { const double unitWidth = 12; const double halfWidth = 6; - const double unitHeight = 28; - const double halfHeight = 14; + const double unitHeight = 1; + const double halfHeight = 0.5; var temp = new CommitGraph(); var unsolved = new List(); diff --git a/src/Views/CommitGraph.cs b/src/Views/CommitGraph.cs index 015eaca5..faf883c6 100644 --- a/src/Views/CommitGraph.cs +++ b/src/Views/CommitGraph.cs @@ -56,22 +56,33 @@ namespace SourceGit.Views return; // Calculate drawing area. - double width = Bounds.Width - 273 - histories.AuthorNameColumnWidth.Value; - double height = Bounds.Height; - double startY = list.Scroll?.Offset.Y ?? 0; - double endY = startY + height + 28; + var width = Bounds.Width - 273 - histories.AuthorNameColumnWidth.Value; + var height = Bounds.Height; + + // Calculate row height + var container = list.ItemsPanelRoot as VirtualizingStackPanel; + if (container == null) + return; + + var item = list.ContainerFromIndex(container.FirstRealizedIndex); + if (item == null) + return; + + var rowHeight = item.Bounds.Height; + var startY = container.FirstRealizedIndex * rowHeight - item.TranslatePoint(new Point(0, 0), list).Value!.Y; + var 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))) { // Draw contents - DrawCurves(context, graph, startY, endY); - DrawAnchors(context, graph, startY, endY); + DrawCurves(context, graph, startY, endY, rowHeight); + DrawAnchors(context, graph, startY, endY, rowHeight); } } - private void DrawCurves(DrawingContext context, Models.CommitGraph graph, double top, double bottom) + private void DrawCurves(DrawingContext context, Models.CommitGraph graph, double top, double bottom, double rowHeight) { var grayedPen = new Pen(new SolidColorBrush(Colors.Gray, 0.4), Models.CommitGraph.Pens[0].Thickness); var onlyHighlightCurrentBranch = OnlyHighlightCurrentBranch; @@ -82,16 +93,20 @@ namespace SourceGit.Views { if (link.IsMerged) continue; - if (link.End.Y < top) + + var startY = link.Start.Y * rowHeight; + var endY = link.End.Y * rowHeight; + + if (endY < top) continue; - if (link.Start.Y > bottom) + if (startY > bottom) break; var geo = new StreamGeometry(); using (var ctx = geo.Open()) { - ctx.BeginFigure(link.Start, false); - ctx.QuadraticBezierTo(link.Control, link.End); + ctx.BeginFigure(new Point(link.Start.X, startY), false); + ctx.QuadraticBezierTo(new Point(link.Control.X, link.Control.Y * rowHeight), new Point(link.End.X, endY)); } context.DrawGeometry(null, grayedPen, geo); @@ -100,10 +115,11 @@ namespace SourceGit.Views foreach (var line in graph.Paths) { - var last = line.Points[0]; + var last = new Point(line.Points[0].X, line.Points[0].Y * rowHeight); var size = line.Points.Count; + var endY = line.Points[size - 1].Y * rowHeight; - if (line.Points[size - 1].Y < top) + if (endY < top) continue; if (last.Y > bottom) break; @@ -117,7 +133,7 @@ namespace SourceGit.Views var ended = false; for (int i = 1; i < size; i++) { - var cur = line.Points[i]; + var cur = new Point(line.Points[i].X, line.Points[i].Y * rowHeight); if (cur.Y < top) { last = cur; @@ -173,23 +189,27 @@ namespace SourceGit.Views { if (onlyHighlightCurrentBranch && !link.IsMerged) continue; - if (link.End.Y < top) + + var startY = link.Start.Y * rowHeight; + var endY = link.End.Y * rowHeight; + + if (endY < top) continue; - if (link.Start.Y > bottom) + if (startY > bottom) break; var geo = new StreamGeometry(); using (var ctx = geo.Open()) { - ctx.BeginFigure(link.Start, false); - ctx.QuadraticBezierTo(link.Control, link.End); + ctx.BeginFigure(new Point(link.Start.X, startY), false); + ctx.QuadraticBezierTo(new Point(link.Control.X, link.Control.Y * rowHeight), new Point(link.End.X, endY)); } context.DrawGeometry(null, Models.CommitGraph.Pens[link.Color], geo); } } - private void DrawAnchors(DrawingContext context, Models.CommitGraph graph, double top, double bottom) + private void DrawAnchors(DrawingContext context, Models.CommitGraph graph, double top, double bottom, double rowHeight) { var dotFill = DotBrush; var dotFillPen = new Pen(dotFill, 2); @@ -198,9 +218,11 @@ namespace SourceGit.Views foreach (var dot in graph.Dots) { - if (dot.Center.Y < top) + var center = new Point(dot.Center.X, dot.Center.Y * rowHeight); + + if (center.Y < top) continue; - if (dot.Center.Y > bottom) + if (center.Y > bottom) break; var pen = Models.CommitGraph.Pens[dot.Color]; @@ -210,16 +232,16 @@ namespace SourceGit.Views switch (dot.Type) { case Models.CommitGraph.DotType.Head: - context.DrawEllipse(dotFill, pen, dot.Center, 6, 6); - context.DrawEllipse(pen.Brush, null, dot.Center, 3, 3); + context.DrawEllipse(dotFill, pen, center, 6, 6); + context.DrawEllipse(pen.Brush, null, center, 3, 3); break; case Models.CommitGraph.DotType.Merge: - context.DrawEllipse(pen.Brush, null, dot.Center, 6, 6); - context.DrawLine(dotFillPen, new Point(dot.Center.X, dot.Center.Y - 3), new Point(dot.Center.X, dot.Center.Y + 3)); - context.DrawLine(dotFillPen, new Point(dot.Center.X - 3, dot.Center.Y), new Point(dot.Center.X + 3, dot.Center.Y)); + context.DrawEllipse(pen.Brush, null, center, 6, 6); + context.DrawLine(dotFillPen, new Point(center.X, center.Y - 3), new Point(center.X, center.Y + 3)); + context.DrawLine(dotFillPen, new Point(center.X - 3, center.Y), new Point(center.X + 3, center.Y)); break; default: - context.DrawEllipse(dotFill, pen, dot.Center, 3, 3); + context.DrawEllipse(dotFill, pen, center, 3, 3); break; } } diff --git a/src/Views/Histories.axaml b/src/Views/Histories.axaml index 86a96528..9b5e6514 100644 --- a/src/Views/Histories.axaml +++ b/src/Views/Histories.axaml @@ -76,7 +76,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -461,7 +375,7 @@ SelectionMode="Single" ContextRequested="OnWorktreeContextRequested" DoubleTapped="OnDoubleTappedWorktree" - PropertyChanged="OnLeftSidebarListBoxPropertyChanged" + PropertyChanged="OnWorktreeListPropertyChanged" IsVisible="{Binding IsWorktreeGroupExpanded, Mode=OneWay}"> + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/SubmodulesView.axaml.cs b/src/Views/SubmodulesView.axaml.cs new file mode 100644 index 00000000..116cbe9f --- /dev/null +++ b/src/Views/SubmodulesView.axaml.cs @@ -0,0 +1,174 @@ +using System; + +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Input; +using Avalonia.Interactivity; +using Avalonia.Layout; +using Avalonia.Media; +using Avalonia.VisualTree; + +namespace SourceGit.Views +{ + public class SubmoduleTreeNodeToggleButton : ToggleButton + { + protected override Type StyleKeyOverride => typeof(ToggleButton); + + protected override void OnPointerPressed(PointerPressedEventArgs e) + { + if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed && + DataContext is ViewModels.SubmoduleTreeNode { IsFolder: true } node) + { + var view = this.FindAncestorOfType(); + view?.ToggleNodeIsExpanded(node); + } + + e.Handled = true; + } + } + + public class SubmoduleTreeNodeIcon : UserControl + { + public static readonly StyledProperty IsExpandedProperty = + AvaloniaProperty.Register(nameof(IsExpanded)); + + public bool IsExpanded + { + get => GetValue(IsExpandedProperty); + set => SetValue(IsExpandedProperty, value); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == IsExpandedProperty) + UpdateContent(); + } + + protected override void OnDataContextChanged(EventArgs e) + { + base.OnDataContextChanged(e); + UpdateContent(); + } + + private void UpdateContent() + { + if (DataContext is not ViewModels.SubmoduleTreeNode node) + { + Content = null; + return; + } + + if (node.Module != null) + CreateContent(new Thickness(0, 0, 0, 0), "Icons.Submodule"); + else if (node.IsExpanded) + CreateContent(new Thickness(0, 2, 0, 0), "Icons.Folder.Open"); + else + CreateContent(new Thickness(0, 2, 0, 0), "Icons.Folder"); + } + + private void CreateContent(Thickness margin, string iconKey) + { + var geo = this.FindResource(iconKey) as StreamGeometry; + if (geo == null) + return; + + Content = new Avalonia.Controls.Shapes.Path() + { + Width = 12, + Height = 12, + HorizontalAlignment = HorizontalAlignment.Left, + VerticalAlignment = VerticalAlignment.Center, + Margin = margin, + Data = geo, + }; + } + } + + public partial class SubmodulesView : UserControl + { + public static readonly StyledProperty SubmodulesProperty = + AvaloniaProperty.Register(nameof(Submodules)); + + public ViewModels.SubmoduleCollection Submodules + { + get => GetValue(SubmodulesProperty); + set => SetValue(SubmodulesProperty, value); + } + + public static readonly RoutedEvent RowsChangedEvent = + RoutedEvent.Register(nameof(RowsChanged), RoutingStrategies.Tunnel | RoutingStrategies.Bubble); + + public event EventHandler RowsChanged + { + add { AddHandler(RowsChangedEvent, value); } + remove { RemoveHandler(RowsChangedEvent, value); } + } + + public int Rows + { + get; + private set; + } + + public SubmodulesView() + { + InitializeComponent(); + } + + public void ToggleNodeIsExpanded(ViewModels.SubmoduleTreeNode node) + { + var submodules = Submodules; + if (submodules != null) + { + submodules.ToggleExpand(node); + Rows = submodules.Rows.Count; + RaiseEvent(new RoutedEventArgs(RowsChangedEvent)); + } + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == SubmodulesProperty) + { + Rows = Submodules?.Rows.Count ?? 0; + RaiseEvent(new RoutedEventArgs(RowsChangedEvent)); + } + else if (change.Property == IsVisibleProperty) + { + RaiseEvent(new RoutedEventArgs(RowsChangedEvent)); + } + } + + private void OnDoubleTappedNode(object sender, TappedEventArgs e) + { + if (sender is Control { DataContext: ViewModels.SubmoduleTreeNode node } && + DataContext is ViewModels.Repository repo) + { + if (node.IsFolder) + ToggleNodeIsExpanded(node); + else if (node.Module.Status != Models.SubmoduleStatus.NotInited) + repo.OpenSubmodule(node.Module.Path); + } + + e.Handled = true; + } + + private void OnRowContextRequested(object sender, ContextRequestedEventArgs e) + { + if (sender is Control { DataContext: ViewModels.SubmoduleTreeNode node } control && + node.Module != null && + DataContext is ViewModels.Repository repo) + { + var menu = repo.CreateContextMenuForSubmodule(node.Module); + menu?.Open(control); + } + + e.Handled = true; + } + } +} From 85e08f5eea483818f4756a177b5be534c5b665c3 Mon Sep 17 00:00:00 2001 From: AquariusStar <48148723+AquariusStar@users.noreply.github.com> Date: Fri, 16 May 2025 04:20:31 +0300 Subject: [PATCH 140/392] localization: update russian localization (#1319) --- src/Resources/Locales/ru_RU.axaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index 1c45f22f..ca5cc384 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -410,6 +410,7 @@ Закрыть панель поиска Найти следующее совпадение Найти предыдущее совпадение + Открыть с внешним инструментом сравнения/слияние Открыть панель поиска Отклонить Сформировать @@ -708,6 +709,12 @@ Каталог: Относительный путь для хранения подмодуля. Удалить подмодуль + СОСТОЯНИЕ + изменён + не создан + ревизия изменена + не слита + URL-адрес ОК Копировать имя метки Копировать сообщение с метки From 6c62789c4cf13fd668c8e0d18066a850c3ccfbbd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 16 May 2025 01:20:43 +0000 Subject: [PATCH 141/392] doc: Update translation status and sort locale files --- TRANSLATION.md | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index 16649d1f..2e1e8d64 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -309,20 +309,7 @@ This document shows the translation status of each locale file in the repository
-### ![ru__RU](https://img.shields.io/badge/ru__RU-99.11%25-yellow) - -
-Missing keys in ru_RU.axaml - -- Text.Hotkeys.TextEditor.OpenExternalMergeTool -- Text.Submodule.Status -- Text.Submodule.Status.Modified -- Text.Submodule.Status.NotInited -- Text.Submodule.Status.RevisionChanged -- Text.Submodule.Status.Unmerged -- Text.Submodule.URL - -
+### ![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen) ### ![ta__IN](https://img.shields.io/badge/ta__IN-94.65%25-yellow) From 85b223a3d058d84a868e96963a56fecd8c1f20ac Mon Sep 17 00:00:00 2001 From: Martin Smith Date: Fri, 16 May 2025 02:27:42 +0100 Subject: [PATCH 142/392] Task/UI usability tweaks (#1314) * Allow About to center in parent * About closes on Escape * ConfirmEmptyCommit dialog closes on Escape * Ignore Dev file * Missed condition --------- Co-authored-by: Martin Smith --- .gitignore | 1 + src/App.axaml.cs | 9 +++++++-- src/Views/About.axaml | 7 ++++++- src/Views/ConfirmEmptyCommit.axaml | 2 ++ 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 498c7e32..e686a534 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,4 @@ build/*.rpm build/*.AppImage SourceGit.app/ build.command +src/Properties/launchSettings.json diff --git a/src/App.axaml.cs b/src/App.axaml.cs index 6e45164d..ec0f79c9 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -109,8 +109,13 @@ namespace SourceGit { if (data is Views.ChromelessWindow window) { - if (showAsDialog && Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner }) - window.ShowDialog(owner); + if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner }) + { + if (showAsDialog) + window.ShowDialog(owner); + else + window.Show(owner); + } else window.Show(); diff --git a/src/Views/About.axaml b/src/Views/About.axaml index d0dd7b82..bcc133e4 100644 --- a/src/Views/About.axaml +++ b/src/Views/About.axaml @@ -10,7 +10,7 @@ Title="{DynamicResource Text.About}" Width="520" Height="230" CanResize="False" - WindowStartupLocation="CenterScreen"> + WindowStartupLocation="CenterOwner"> @@ -67,4 +67,9 @@ + + + + + diff --git a/src/Views/ConfirmEmptyCommit.axaml b/src/Views/ConfirmEmptyCommit.axaml index 32a1f2cd..bfa42ef0 100644 --- a/src/Views/ConfirmEmptyCommit.axaml +++ b/src/Views/ConfirmEmptyCommit.axaml @@ -12,6 +12,7 @@ Title="{DynamicResource Text.Warn}" SizeToContent="WidthAndHeight" CanResize="False" + ShowInTaskbar="False" WindowStartupLocation="CenterOwner"> @@ -64,6 +65,7 @@ Height="30" Margin="4,0" Click="CloseWindow" + IsCancel="True" Content="{DynamicResource Text.Cancel}" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"/> From e4490d87dc6c1974fd3b8c0d72325a253a0089d0 Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 16 May 2025 09:44:36 +0800 Subject: [PATCH 143/392] code_review: PR #1314 Signed-off-by: leo --- src/App.axaml.cs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/App.axaml.cs b/src/App.axaml.cs index ec0f79c9..45ab0b8c 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -107,18 +107,24 @@ namespace SourceGit #region Utility Functions public static void ShowWindow(object data, bool showAsDialog) { - if (data is Views.ChromelessWindow window) + var impl = (Views.ChromelessWindow target, bool isDialog) => { if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner }) { - if (showAsDialog) - window.ShowDialog(owner); + if (isDialog) + target.ShowDialog(owner); else - window.Show(owner); + target.Show(owner); } else - window.Show(); + { + target.Show(); + } + }; + if (data is Views.ChromelessWindow window) + { + impl(window, showAsDialog); return; } @@ -135,10 +141,7 @@ namespace SourceGit if (window != null) { window.DataContext = data; - if (showAsDialog && Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner }) - window.ShowDialog(owner); - else - window.Show(); + impl(window, showAsDialog); } } From d2994696130613e49331c5f9f352bf6b6523dddf Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 16 May 2025 10:47:38 +0800 Subject: [PATCH 144/392] ux: show tooltip at right of hovered item in repository's left side bar (#1317) Signed-off-by: leo --- src/Views/BranchTree.axaml | 8 +++++--- src/Views/SubmodulesView.axaml | 7 ++++++- src/Views/TagsView.axaml | 14 ++++++++++++-- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/Views/BranchTree.axaml b/src/Views/BranchTree.axaml index 61a08613..a90b327e 100644 --- a/src/Views/BranchTree.axaml +++ b/src/Views/BranchTree.axaml @@ -32,11 +32,13 @@ - + + ColumnDefinitions="16,*"> - + diff --git a/src/Views/TagsView.axaml b/src/Views/TagsView.axaml index 9bf1e653..2795c403 100644 --- a/src/Views/TagsView.axaml +++ b/src/Views/TagsView.axaml @@ -26,7 +26,13 @@ SelectionChanged="OnRowSelectionChanged"> - + @@ -86,7 +92,11 @@ SelectionChanged="OnRowSelectionChanged"> - + From ed1351b1f789e7550be3b8b7857953edf2a84fec Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 16 May 2025 11:31:53 +0800 Subject: [PATCH 145/392] feature: supports to show submodules as tree or list (#1307) Signed-off-by: leo --- src/Resources/Locales/en_US.axaml | 1 + src/Resources/Locales/zh_CN.axaml | 1 + src/Resources/Locales/zh_TW.axaml | 1 + src/Resources/Styles.axaml | 2 +- src/ViewModels/Preferences.cs | 6 + src/ViewModels/Repository.cs | 27 +- ...duleTreeNode.cs => SubmoduleCollection.cs} | 24 +- src/Views/Repository.axaml | 16 +- src/Views/SubmodulesView.axaml | 312 ++++++++++++------ src/Views/SubmodulesView.axaml.cs | 64 ++-- 10 files changed, 302 insertions(+), 152 deletions(-) rename src/ViewModels/{SubmoduleTreeNode.cs => SubmoduleCollection.cs} (90%) diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index cee9ad2c..2f7c5c43 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -617,6 +617,7 @@ Message SHA Current Branch + Show Submodules as Tree Show Tags as Tree SKIP Statistics diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 3b0237da..56227607 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -621,6 +621,7 @@ 提交信息 提交指纹 仅在当前分支中查找 + 以树型结构展示 以树型结构展示 跳过此提交 提交统计 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 6b3ce423..204e1cf7 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -621,6 +621,7 @@ 提交訊息 提交編號 僅搜尋目前分支 + 以樹型結構展示 以樹型結構展示 跳過此提交 提交統計 diff --git a/src/Resources/Styles.axaml b/src/Resources/Styles.axaml index 882a14f4..923ef22b 100644 --- a/src/Resources/Styles.axaml +++ b/src/Resources/Styles.axaml @@ -1235,7 +1235,7 @@ - + - - - - + + + + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Views/SubmodulesView.axaml.cs b/src/Views/SubmodulesView.axaml.cs index 116cbe9f..81ccdc5d 100644 --- a/src/Views/SubmodulesView.axaml.cs +++ b/src/Views/SubmodulesView.axaml.cs @@ -89,15 +89,6 @@ namespace SourceGit.Views public partial class SubmodulesView : UserControl { - public static readonly StyledProperty SubmodulesProperty = - AvaloniaProperty.Register(nameof(Submodules)); - - public ViewModels.SubmoduleCollection Submodules - { - get => GetValue(SubmodulesProperty); - set => SetValue(SubmodulesProperty, value); - } - public static readonly RoutedEvent RowsChangedEvent = RoutedEvent.Register(nameof(RowsChanged), RoutingStrategies.Tunnel | RoutingStrategies.Bubble); @@ -120,11 +111,10 @@ namespace SourceGit.Views public void ToggleNodeIsExpanded(ViewModels.SubmoduleTreeNode node) { - var submodules = Submodules; - if (submodules != null) + if (Content is ViewModels.SubmoduleCollectionAsTree tree) { - submodules.ToggleExpand(node); - Rows = submodules.Rows.Count; + tree.ToggleExpand(node); + Rows = tree.Rows.Count; RaiseEvent(new RoutedEventArgs(RowsChangedEvent)); } } @@ -133,9 +123,15 @@ namespace SourceGit.Views { base.OnPropertyChanged(change); - if (change.Property == SubmodulesProperty) + if (change.Property == ContentProperty) { - Rows = Submodules?.Rows.Count ?? 0; + if (Content is ViewModels.SubmoduleCollectionAsTree tree) + Rows = tree.Rows.Count; + else if (Content is ViewModels.SubmoduleCollectionAsList list) + Rows = list.Submodules.Count; + else + Rows = 0; + RaiseEvent(new RoutedEventArgs(RowsChangedEvent)); } else if (change.Property == IsVisibleProperty) @@ -144,28 +140,40 @@ namespace SourceGit.Views } } - private void OnDoubleTappedNode(object sender, TappedEventArgs e) + private void OnItemDoubleTapped(object sender, TappedEventArgs e) { - if (sender is Control { DataContext: ViewModels.SubmoduleTreeNode node } && - DataContext is ViewModels.Repository repo) + if (sender is Control control && DataContext is ViewModels.Repository repo) { - if (node.IsFolder) - ToggleNodeIsExpanded(node); - else if (node.Module.Status != Models.SubmoduleStatus.NotInited) - repo.OpenSubmodule(node.Module.Path); + if (control.DataContext is ViewModels.SubmoduleTreeNode node) + { + if (node.IsFolder) + ToggleNodeIsExpanded(node); + else if (node.Module.Status != Models.SubmoduleStatus.NotInited) + repo.OpenSubmodule(node.Module.Path); + } + else if (control.DataContext is Models.Submodule m && m.Status != Models.SubmoduleStatus.NotInited) + { + repo.OpenSubmodule(m.Path); + } } e.Handled = true; } - private void OnRowContextRequested(object sender, ContextRequestedEventArgs e) + private void OnItemContextRequested(object sender, ContextRequestedEventArgs e) { - if (sender is Control { DataContext: ViewModels.SubmoduleTreeNode node } control && - node.Module != null && - DataContext is ViewModels.Repository repo) + if (sender is Control control && DataContext is ViewModels.Repository repo) { - var menu = repo.CreateContextMenuForSubmodule(node.Module); - menu?.Open(control); + if (control.DataContext is ViewModels.SubmoduleTreeNode node && node.Module != null) + { + var menu = repo.CreateContextMenuForSubmodule(node.Module); + menu?.Open(control); + } + else if (control.DataContext is Models.Submodule m) + { + var menu = repo.CreateContextMenuForSubmodule(m); + menu?.Open(control); + } } e.Handled = true; From f46bbd01cdf1489a8978b655bef8f4de06d6ecbc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 16 May 2025 03:32:09 +0000 Subject: [PATCH 146/392] doc: Update translation status and sort locale files --- TRANSLATION.md | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index 2e1e8d64..7bad5859 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -6,7 +6,7 @@ This document shows the translation status of each locale file in the repository ### ![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen) -### ![de__DE](https://img.shields.io/badge/de__DE-98.85%25-yellow) +### ![de__DE](https://img.shields.io/badge/de__DE-98.73%25-yellow)
Missing keys in de_DE.axaml @@ -14,6 +14,7 @@ This document shows the translation status of each locale file in the repository - Text.GitFlow.FinishWithPush - Text.GitFlow.FinishWithSquash - Text.Hotkeys.TextEditor.OpenExternalMergeTool +- Text.Repository.ShowSubmodulesAsTree - Text.Submodule.Status - Text.Submodule.Status.Modified - Text.Submodule.Status.NotInited @@ -23,12 +24,13 @@ This document shows the translation status of each locale file in the repository
-### ![es__ES](https://img.shields.io/badge/es__ES-99.11%25-yellow) +### ![es__ES](https://img.shields.io/badge/es__ES-98.98%25-yellow)
Missing keys in es_ES.axaml - Text.Hotkeys.TextEditor.OpenExternalMergeTool +- Text.Repository.ShowSubmodulesAsTree - Text.Submodule.Status - Text.Submodule.Status.Modified - Text.Submodule.Status.NotInited @@ -38,7 +40,7 @@ This document shows the translation status of each locale file in the repository
-### ![fr__FR](https://img.shields.io/badge/fr__FR-94.65%25-yellow) +### ![fr__FR](https://img.shields.io/badge/fr__FR-94.53%25-yellow)
Missing keys in fr_FR.axaml @@ -68,6 +70,7 @@ This document shows the translation status of each locale file in the repository - Text.Repository.BranchSort.ByCommitterDate - Text.Repository.BranchSort.ByName - Text.Repository.Search.ByContent +- Text.Repository.ShowSubmodulesAsTree - Text.Repository.ViewLogs - Text.Repository.Visit - Text.Submodule.Status @@ -88,7 +91,7 @@ This document shows the translation status of each locale file in the repository
-### ![it__IT](https://img.shields.io/badge/it__IT-94.39%25-yellow) +### ![it__IT](https://img.shields.io/badge/it__IT-94.27%25-yellow)
Missing keys in it_IT.axaml @@ -120,6 +123,7 @@ This document shows the translation status of each locale file in the repository - Text.Repository.BranchSort.ByCommitterDate - Text.Repository.BranchSort.ByName - Text.Repository.Search.ByContent +- Text.Repository.ShowSubmodulesAsTree - Text.Repository.ViewLogs - Text.Repository.Visit - Text.Submodule.Status @@ -140,7 +144,7 @@ This document shows the translation status of each locale file in the repository
-### ![ja__JP](https://img.shields.io/badge/ja__JP-94.39%25-yellow) +### ![ja__JP](https://img.shields.io/badge/ja__JP-94.27%25-yellow)
Missing keys in ja_JP.axaml @@ -171,6 +175,7 @@ This document shows the translation status of each locale file in the repository - Text.Repository.BranchSort.ByName - Text.Repository.FilterCommits - Text.Repository.Search.ByContent +- Text.Repository.ShowSubmodulesAsTree - Text.Repository.Tags.OrderByNameDes - Text.Repository.ViewLogs - Text.Repository.Visit @@ -192,7 +197,7 @@ This document shows the translation status of each locale file in the repository
-### ![pt__BR](https://img.shields.io/badge/pt__BR-86.11%25-yellow) +### ![pt__BR](https://img.shields.io/badge/pt__BR-86.01%25-yellow)
Missing keys in pt_BR.axaml @@ -273,6 +278,7 @@ This document shows the translation status of each locale file in the repository - Text.Repository.Notifications.Clear - Text.Repository.OnlyHighlightCurrentBranchInHistories - Text.Repository.Search.ByContent +- Text.Repository.ShowSubmodulesAsTree - Text.Repository.Skip - Text.Repository.Tags.OrderByCreatorDate - Text.Repository.Tags.OrderByNameAsc @@ -309,9 +315,16 @@ This document shows the translation status of each locale file in the repository
-### ![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen) +### ![ru__RU](https://img.shields.io/badge/ru__RU-99.87%25-yellow) -### ![ta__IN](https://img.shields.io/badge/ta__IN-94.65%25-yellow) +
+Missing keys in ru_RU.axaml + +- Text.Repository.ShowSubmodulesAsTree + +
+ +### ![ta__IN](https://img.shields.io/badge/ta__IN-94.53%25-yellow)
Missing keys in ta_IN.axaml @@ -341,6 +354,7 @@ This document shows the translation status of each locale file in the repository - Text.Repository.BranchSort.ByCommitterDate - Text.Repository.BranchSort.ByName - Text.Repository.Search.ByContent +- Text.Repository.ShowSubmodulesAsTree - Text.Repository.ViewLogs - Text.Repository.Visit - Text.Submodule.Status @@ -361,7 +375,7 @@ This document shows the translation status of each locale file in the repository
-### ![uk__UA](https://img.shields.io/badge/uk__UA-95.80%25-yellow) +### ![uk__UA](https://img.shields.io/badge/uk__UA-95.67%25-yellow)
Missing keys in uk_UA.axaml @@ -387,6 +401,7 @@ This document shows the translation status of each locale file in the repository - Text.Repository.BranchSort.ByCommitterDate - Text.Repository.BranchSort.ByName - Text.Repository.Search.ByContent +- Text.Repository.ShowSubmodulesAsTree - Text.Repository.ViewLogs - Text.Repository.Visit - Text.Submodule.Status From fd935259aafa01e584801cbf84903f8767349eb3 Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 16 May 2025 12:22:37 +0800 Subject: [PATCH 147/392] refactor: build tags view data in viewmodels instead of views Signed-off-by: leo --- src/ViewModels/Preferences.cs | 7 +- src/ViewModels/Repository.cs | 28 ++++- src/ViewModels/SubmoduleCollection.cs | 10 +- src/ViewModels/TagCollection.cs | 72 ++++++++++++- src/Views/Repository.axaml | 5 +- src/Views/TagsView.axaml | 53 +++++---- src/Views/TagsView.axaml.cs | 148 +++++--------------------- 7 files changed, 151 insertions(+), 172 deletions(-) diff --git a/src/ViewModels/Preferences.cs b/src/ViewModels/Preferences.cs index 5ce6f769..2698067e 100644 --- a/src/ViewModels/Preferences.cs +++ b/src/ViewModels/Preferences.cs @@ -178,9 +178,9 @@ namespace SourceGit.ViewModels public bool ShowTagsAsTree { - get => _showTagsAsTree; - set => SetProperty(ref _showTagsAsTree, value); - } + get; + set; + } = false; public bool ShowTagsInGraph { @@ -677,7 +677,6 @@ namespace SourceGit.ViewModels private double _lastCheckUpdateTime = 0; private string _ignoreUpdateTag = string.Empty; - private bool _showTagsAsTree = false; private bool _showTagsInGraph = true; private bool _useTwoColumnsLayoutInHistories = false; private bool _displayTimeAsPeriodInHistories = false; diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index c3e7c478..d24f6fbf 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -198,7 +198,21 @@ namespace SourceGit.ViewModels private set => SetProperty(ref _tags, value); } - public List VisibleTags + public bool ShowTagsAsTree + { + get => Preferences.Instance.ShowTagsAsTree; + set + { + if (value != Preferences.Instance.ShowTagsAsTree) + { + Preferences.Instance.ShowTagsAsTree = value; + VisibleTags = BuildVisibleTags(); + OnPropertyChanged(); + } + } + } + + public object VisibleTags { get => _visibleTags; private set => SetProperty(ref _visibleTags, value); @@ -548,7 +562,7 @@ namespace SourceGit.ViewModels _localBranchTrees.Clear(); _remoteBranchTrees.Clear(); _tags.Clear(); - _visibleTags.Clear(); + _visibleTags = null; _submodules.Clear(); _visibleSubmodules = null; _searchedCommits.Clear(); @@ -2492,7 +2506,7 @@ namespace SourceGit.ViewModels return builder; } - private List BuildVisibleTags() + private object BuildVisibleTags() { switch (_settings.TagSortMode) { @@ -2523,7 +2537,11 @@ namespace SourceGit.ViewModels var historiesFilters = _settings.CollectHistoriesFilters(); UpdateTagFilterMode(historiesFilters); - return visible; + + if (Preferences.Instance.ShowTagsAsTree) + return TagCollectionAsTree.Build(visible, _visibleTags as TagCollectionAsTree); + else + return new TagCollectionAsList() { Tags = visible }; } private object BuildVisibleSubmodules() @@ -2775,7 +2793,7 @@ namespace SourceGit.ViewModels private List _remoteBranchTrees = new List(); private List _worktrees = new List(); private List _tags = new List(); - private List _visibleTags = new List(); + private object _visibleTags = null; private List _submodules = new List(); private object _visibleSubmodules = null; diff --git a/src/ViewModels/SubmoduleCollection.cs b/src/ViewModels/SubmoduleCollection.cs index e2ebf634..4600496e 100644 --- a/src/ViewModels/SubmoduleCollection.cs +++ b/src/ViewModels/SubmoduleCollection.cs @@ -150,18 +150,12 @@ namespace SourceGit.ViewModels collection.Tree = SubmoduleTreeNode.Build(submodules, oldExpanded); var rows = new List(); - collection.MakeTreeRows(rows, collection.Tree); + MakeTreeRows(rows, collection.Tree); collection.Rows.AddRange(rows); return collection; } - public void Clear() - { - Tree.Clear(); - Rows.Clear(); - } - public void ToggleExpand(SubmoduleTreeNode node) { node.IsExpanded = !node.IsExpanded; @@ -193,7 +187,7 @@ namespace SourceGit.ViewModels } } - private void MakeTreeRows(List rows, List nodes) + private static void MakeTreeRows(List rows, List nodes) { foreach (var node in nodes) { diff --git a/src/ViewModels/TagCollection.cs b/src/ViewModels/TagCollection.cs index f05a727e..ce9c9508 100644 --- a/src/ViewModels/TagCollection.cs +++ b/src/ViewModels/TagCollection.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; + using Avalonia.Collections; + using CommunityToolkit.Mvvm.ComponentModel; namespace SourceGit.ViewModels @@ -61,7 +63,7 @@ namespace SourceGit.ViewModels Counter = 1; } - public static List Build(IList tags, HashSet expaneded) + public static List Build(List tags, HashSet expaneded) { var nodes = new List(); var folders = new Dictionary(); @@ -131,7 +133,7 @@ namespace SourceGit.ViewModels public class TagCollectionAsList { - public AvaloniaList Tags + public List Tags { get; set; @@ -151,5 +153,71 @@ namespace SourceGit.ViewModels get; set; } = []; + + public static TagCollectionAsTree Build(List tags, TagCollectionAsTree old) + { + var oldExpanded = new HashSet(); + if (old != null) + { + foreach (var row in old.Rows) + { + if (row.IsFolder && row.IsExpanded) + oldExpanded.Add(row.FullPath); + } + } + + var collection = new TagCollectionAsTree(); + collection.Tree = TagTreeNode.Build(tags, oldExpanded); + + var rows = new List(); + MakeTreeRows(rows, collection.Tree); + collection.Rows.AddRange(rows); + + return collection; + } + + public void ToggleExpand(TagTreeNode node) + { + node.IsExpanded = !node.IsExpanded; + + var rows = Rows; + var depth = node.Depth; + var idx = rows.IndexOf(node); + if (idx == -1) + return; + + if (node.IsExpanded) + { + var subrows = new List(); + MakeTreeRows(subrows, node.Children); + rows.InsertRange(idx + 1, subrows); + } + else + { + var removeCount = 0; + for (int i = idx + 1; i < rows.Count; i++) + { + var row = rows[i]; + if (row.Depth <= depth) + break; + + removeCount++; + } + rows.RemoveRange(idx + 1, removeCount); + } + } + + private static void MakeTreeRows(List rows, List nodes) + { + foreach (var node in nodes) + { + rows.Add(node); + + if (!node.IsExpanded || !node.IsFolder) + continue; + + MakeTreeRows(rows, node.Children); + } + } } } diff --git a/src/Views/Repository.axaml b/src/Views/Repository.axaml index 99bdd6ab..4eb303fe 100644 --- a/src/Views/Repository.axaml +++ b/src/Views/Repository.axaml @@ -271,7 +271,7 @@
-### ![it__IT](https://img.shields.io/badge/it__IT-94.27%25-yellow) - -
-Missing keys in it_IT.axaml - -- Text.Bisect -- Text.Bisect.Abort -- Text.Bisect.Bad -- Text.Bisect.Detecting -- Text.Bisect.Good -- Text.Bisect.Skip -- Text.Bisect.WaitingForRange -- Text.Checkout.RecurseSubmodules -- Text.CommitCM.CopyAuthor -- Text.CommitCM.CopyCommitter -- Text.CommitCM.CopySubject -- Text.CommitMessageTextBox.SubjectCount -- Text.Configure.Git.PreferredMergeMode -- Text.ConfirmEmptyCommit.Continue -- Text.ConfirmEmptyCommit.NoLocalChanges -- Text.ConfirmEmptyCommit.StageAllThenCommit -- Text.ConfirmEmptyCommit.WithLocalChanges -- Text.CopyFullPath -- Text.GitFlow.FinishWithPush -- Text.GitFlow.FinishWithSquash -- Text.Hotkeys.TextEditor.OpenExternalMergeTool -- Text.Preferences.General.ShowTagsInGraph -- Text.Preferences.Git.IgnoreCRAtEOLInDiff -- Text.Repository.BranchSort -- Text.Repository.BranchSort.ByCommitterDate -- Text.Repository.BranchSort.ByName -- Text.Repository.Search.ByContent -- Text.Repository.ShowSubmodulesAsTree -- Text.Repository.ViewLogs -- Text.Repository.Visit -- Text.Submodule.Status -- Text.Submodule.Status.Modified -- Text.Submodule.Status.NotInited -- Text.Submodule.Status.RevisionChanged -- Text.Submodule.Status.Unmerged -- Text.Submodule.URL -- Text.ViewLogs -- Text.ViewLogs.Clear -- Text.ViewLogs.CopyLog -- Text.ViewLogs.Delete -- Text.WorkingCopy.ConfirmCommitWithFilter -- Text.WorkingCopy.Conflicts.OpenExternalMergeTool -- Text.WorkingCopy.Conflicts.OpenExternalMergeToolAllConflicts -- Text.WorkingCopy.Conflicts.UseMine -- Text.WorkingCopy.Conflicts.UseTheirs - -
+### ![it__IT](https://img.shields.io/badge/it__IT-%E2%88%9A-brightgreen) ### ![ja__JP](https://img.shields.io/badge/ja__JP-94.27%25-yellow) From 879b84ac2040a68eeb437844800e9db2023572f7 Mon Sep 17 00:00:00 2001 From: Gadfly Date: Sat, 17 May 2025 07:58:47 +0800 Subject: [PATCH 152/392] enhance: Show the stderr content from `QueryLocalChanges` (#1327) --- src/Commands/QueryLocalChanges.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Commands/QueryLocalChanges.cs b/src/Commands/QueryLocalChanges.cs index 4e626a79..404f5be6 100644 --- a/src/Commands/QueryLocalChanges.cs +++ b/src/Commands/QueryLocalChanges.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text.RegularExpressions; +using Avalonia.Threading; namespace SourceGit.Commands { @@ -22,7 +23,10 @@ namespace SourceGit.Commands var outs = new List(); var rs = ReadToEnd(); if (!rs.IsSuccess) + { + Dispatcher.UIThread.Post(() => App.RaiseException(Context, rs.StdErr)); return outs; + } var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries); foreach (var line in lines) From d3d0e7b15c4b80ec11d9743dbe03e9e1572cf5fd Mon Sep 17 00:00:00 2001 From: leo Date: Sat, 17 May 2025 08:13:19 +0800 Subject: [PATCH 153/392] ux: thinner border for default avatar Signed-off-by: leo --- src/Views/Avatar.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Views/Avatar.cs b/src/Views/Avatar.cs index 9a61c99a..87bc7ce3 100644 --- a/src/Views/Avatar.cs +++ b/src/Views/Avatar.cs @@ -54,8 +54,8 @@ namespace SourceGit.Views { context.DrawRectangle(Brushes.White, new Pen(new SolidColorBrush(Colors.Black, 0.3f), 0.65f), rect, corner, corner); - var offsetX = Bounds.Width / 8.0; - var offsetY = Bounds.Height / 8.0; + var offsetX = Bounds.Width / 10.0; + var offsetY = Bounds.Height / 10.0; var stepX = (Bounds.Width - offsetX * 2) / 5.0; var stepY = (Bounds.Height - offsetY * 2) / 5.0; From d3a740fb95702e0bf05701a86896ebc1e8cfe938 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20J=2E=20Mart=C3=ADnez=20M=2E?= <56406225+jjesus-dev@users.noreply.github.com> Date: Fri, 16 May 2025 23:12:01 -0600 Subject: [PATCH 154/392] localization: update spanish translations (#1329) add missing translations --- src/Resources/Locales/es_ES.axaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml index b11d0e98..e2b16329 100644 --- a/src/Resources/Locales/es_ES.axaml +++ b/src/Resources/Locales/es_ES.axaml @@ -410,6 +410,7 @@ Cerrar panel de búsqueda Buscar siguiente coincidencia Buscar coincidencia anterior + Abrir con herramienta diff/merge externa Abrir panel de búsqueda Descartar Stage @@ -620,6 +621,7 @@ Mensaje SHA Rama Actual + Mostrar Submódulos como Árbol Mostrar Etiquetas como Árbol OMITIR Estadísticas @@ -708,6 +710,12 @@ Ruta Relativa: Carpeta relativa para almacenar este módulo. Eliminar Submódulo + ESTADO + modificado + no inicializado + revisión cambiada + unmerged + URL OK Copiar Nombre de la Etiqueta Copiar Mensaje de la Etiqueta From 506dbc218c13b5f560a57e843b326f17ee01cb1a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 17 May 2025 05:12:20 +0000 Subject: [PATCH 155/392] doc: Update translation status and sort locale files --- TRANSLATION.md | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index b604acca..08d3bb06 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -24,21 +24,7 @@ This document shows the translation status of each locale file in the repository
-### ![es__ES](https://img.shields.io/badge/es__ES-98.98%25-yellow) - -
-Missing keys in es_ES.axaml - -- Text.Hotkeys.TextEditor.OpenExternalMergeTool -- Text.Repository.ShowSubmodulesAsTree -- Text.Submodule.Status -- Text.Submodule.Status.Modified -- Text.Submodule.Status.NotInited -- Text.Submodule.Status.RevisionChanged -- Text.Submodule.Status.Unmerged -- Text.Submodule.URL - -
+### ![es__ES](https://img.shields.io/badge/es__ES-%E2%88%9A-brightgreen) ### ![fr__FR](https://img.shields.io/badge/fr__FR-94.53%25-yellow) From 01945f231ed8f98e49cdd3de848c922c157c1053 Mon Sep 17 00:00:00 2001 From: popara <31167073+popara96@users.noreply.github.com> Date: Sat, 17 May 2025 07:17:10 +0200 Subject: [PATCH 156/392] Added workspaces shortcuts (#1328) - added Alt+Space for opening Workspaces context menu (which can then be navigated normally with arrows) - added Alt+1 through Alt+9 for switching to corresponding workspace --- src/Resources/Locales/en_US.axaml | 2 ++ src/ViewModels/Launcher.cs | 10 ++++++- src/Views/Hotkeys.axaml | 8 +++++- src/Views/Launcher.axaml | 2 +- src/Views/Launcher.axaml.cs | 44 +++++++++++++++++++++++++++++++ 5 files changed, 63 insertions(+), 3 deletions(-) diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 2f7c5c43..63c9815d 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -386,6 +386,8 @@ Go to previous page Create new page Open Preferences dialog + Open Workspaces dialog + Switch to corresponding workspace REPOSITORY Commit staged changes Commit and push staged changes diff --git a/src/ViewModels/Launcher.cs b/src/ViewModels/Launcher.cs index d9425059..5eaca763 100644 --- a/src/ViewModels/Launcher.cs +++ b/src/ViewModels/Launcher.cs @@ -463,6 +463,14 @@ namespace SourceGit.ViewModels return menu; } + public void SwitchWorkspace(int idx) + { + var pref = Preferences.Instance; + if (idx >= pref.Workspaces.Count || pref.Workspaces[idx].IsActive) return; + + SwitchWorkspace(pref.Workspaces[idx]); + } + private string GetRepositoryGitDir(string repo) { var fullpath = Path.Combine(repo, ".git"); @@ -493,7 +501,7 @@ namespace SourceGit.ViewModels return new Commands.QueryGitDir(repo).Result(); } - + private void SwitchWorkspace(Workspace to) { foreach (var one in Pages) diff --git a/src/Views/Hotkeys.axaml b/src/Views/Hotkeys.axaml index 87242793..b1d436a4 100644 --- a/src/Views/Hotkeys.axaml +++ b/src/Views/Hotkeys.axaml @@ -45,7 +45,7 @@ FontSize="{Binding Source={x:Static vm:Preferences.Instance}, Path=DefaultFontSize, Converter={x:Static c:DoubleConverters.Increase}}" Margin="0,0,0,8"/> - + @@ -69,6 +69,12 @@ + + + + + + - - + - + - + @@ -102,5 +102,22 @@ + + + + + + + + + + + + + + + diff --git a/src/Views/Launcher.axaml.cs b/src/Views/Launcher.axaml.cs index 24fd6dcc..02cc4f08 100644 --- a/src/Views/Launcher.axaml.cs +++ b/src/Views/Launcher.axaml.cs @@ -157,6 +157,12 @@ namespace SourceGit.Views if (e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control)) { + if (e.Key == Key.P) + { + vm.OpenWorkspaceSwitcher(); + e.Handled = true; + } + if (e.Key == Key.W) { vm.CloseTab(null); @@ -248,17 +254,10 @@ namespace SourceGit.Views } } } - else if (e.KeyModifiers.HasFlag(KeyModifiers.Alt)) - { - if (SwitchWorkspace(e.Key)) - { - e.Handled = true; - return; - } - } else if (e.Key == Key.Escape) { vm.ActivePage.CancelPopup(); + vm.CancelWorkspaceSwitcher(); e.Handled = true; return; } @@ -314,44 +313,6 @@ namespace SourceGit.Views e.Handled = true; } - - private bool SwitchWorkspace(Key eKey) - { - var exec = (ViewModels.Launcher l, int idx) => - { - var pref = ViewModels.Preferences.Instance; - if (idx < pref.Workspaces.Count) - l.SwitchWorkspace(pref.Workspaces[idx]); - return true; // Alt+1..9 (or Option+1..9) always mark handled - }; - - if (DataContext is ViewModels.Launcher launcher) - { - switch (eKey) - { - case Key.D1 or Key.NumPad1: - return exec(launcher, 0); - case Key.D2 or Key.NumPad2: - return exec(launcher, 1); - case Key.D3 or Key.NumPad3: - return exec(launcher, 2); - case Key.D4 or Key.NumPad4: - return exec(launcher, 3); - case Key.D5 or Key.NumPad5: - return exec(launcher, 4); - case Key.D6 or Key.NumPad6: - return exec(launcher, 5); - case Key.D7 or Key.NumPad7: - return exec(launcher, 6); - case Key.D8 or Key.NumPad8: - return exec(launcher, 7); - case Key.D9 or Key.NumPad9: - return exec(launcher, 8); - } - } - - return false; - } private KeyModifiers _unhandledModifiers = KeyModifiers.None; private WindowState _lastWindowState = WindowState.Normal; diff --git a/src/Views/WorkspaceSwitcher.axaml b/src/Views/WorkspaceSwitcher.axaml new file mode 100644 index 00000000..49fed451 --- /dev/null +++ b/src/Views/WorkspaceSwitcher.axaml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/WorkspaceSwitcher.axaml.cs b/src/Views/WorkspaceSwitcher.axaml.cs new file mode 100644 index 00000000..04bdae1a --- /dev/null +++ b/src/Views/WorkspaceSwitcher.axaml.cs @@ -0,0 +1,49 @@ +using Avalonia.Controls; +using Avalonia.Input; + +namespace SourceGit.Views +{ + public partial class WorkspaceSwitcher : UserControl + { + public WorkspaceSwitcher() + { + InitializeComponent(); + } + + protected override void OnKeyDown(KeyEventArgs e) + { + base.OnKeyDown(e); + + if (e.Key == Key.Enter && DataContext is ViewModels.WorkspaceSwitcher switcher) + { + switcher.Switch(); + e.Handled = true; + } + } + + private void OnItemDoubleTapped(object sender, TappedEventArgs e) + { + if (DataContext is ViewModels.WorkspaceSwitcher switcher) + { + switcher.Switch(); + e.Handled = true; + } + } + + private void OnSearchBoxKeyDown(object sender, KeyEventArgs e) + { + if (e.Key == Key.Down && WorkspaceListBox.ItemCount > 0) + { + WorkspaceListBox.Focus(NavigationMethod.Directional); + + if (WorkspaceListBox.SelectedIndex < 0) + WorkspaceListBox.SelectedIndex = 0; + else if (WorkspaceListBox.SelectedIndex < WorkspaceListBox.ItemCount) + WorkspaceListBox.SelectedIndex++; + + e.Handled = true; + } + } + } +} + From d429a6426a99d441869d9727929b879dd4037717 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 17 May 2025 12:14:32 +0000 Subject: [PATCH 161/392] doc: Update translation status and sort locale files --- TRANSLATION.md | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index 2795c890..aee0e2a0 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -6,7 +6,7 @@ This document shows the translation status of each locale file in the repository ### ![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen) -### ![de__DE](https://img.shields.io/badge/de__DE-98.60%25-yellow) +### ![de__DE](https://img.shields.io/badge/de__DE-98.48%25-yellow)
Missing keys in de_DE.axaml @@ -16,6 +16,7 @@ This document shows the translation status of each locale file in the repository - Text.Hotkeys.Global.SwitchWorkspace - Text.Hotkeys.TextEditor.OpenExternalMergeTool - Text.Repository.ShowSubmodulesAsTree +- Text.Repository.WorkspaceSwitcher - Text.Submodule.Status - Text.Submodule.Status.Modified - Text.Submodule.Status.NotInited @@ -25,16 +26,17 @@ This document shows the translation status of each locale file in the repository
-### ![es__ES](https://img.shields.io/badge/es__ES-99.87%25-yellow) +### ![es__ES](https://img.shields.io/badge/es__ES-99.75%25-yellow)
Missing keys in es_ES.axaml - Text.Hotkeys.Global.SwitchWorkspace +- Text.Repository.WorkspaceSwitcher
-### ![fr__FR](https://img.shields.io/badge/fr__FR-94.41%25-yellow) +### ![fr__FR](https://img.shields.io/badge/fr__FR-94.29%25-yellow)
Missing keys in fr_FR.axaml @@ -68,6 +70,7 @@ This document shows the translation status of each locale file in the repository - Text.Repository.ShowSubmodulesAsTree - Text.Repository.ViewLogs - Text.Repository.Visit +- Text.Repository.WorkspaceSwitcher - Text.Submodule.Status - Text.Submodule.Status.Modified - Text.Submodule.Status.NotInited @@ -86,16 +89,17 @@ This document shows the translation status of each locale file in the repository
-### ![it__IT](https://img.shields.io/badge/it__IT-99.87%25-yellow) +### ![it__IT](https://img.shields.io/badge/it__IT-99.75%25-yellow)
Missing keys in it_IT.axaml - Text.Hotkeys.Global.SwitchWorkspace +- Text.Repository.WorkspaceSwitcher
-### ![ja__JP](https://img.shields.io/badge/ja__JP-94.16%25-yellow) +### ![ja__JP](https://img.shields.io/badge/ja__JP-94.04%25-yellow)
Missing keys in ja_JP.axaml @@ -131,6 +135,7 @@ This document shows the translation status of each locale file in the repository - Text.Repository.Tags.OrderByNameDes - Text.Repository.ViewLogs - Text.Repository.Visit +- Text.Repository.WorkspaceSwitcher - Text.Submodule.Status - Text.Submodule.Status.Modified - Text.Submodule.Status.NotInited @@ -149,7 +154,7 @@ This document shows the translation status of each locale file in the repository
-### ![pt__BR](https://img.shields.io/badge/pt__BR-85.90%25-yellow) +### ![pt__BR](https://img.shields.io/badge/pt__BR-85.79%25-yellow)
Missing keys in pt_BR.axaml @@ -240,6 +245,7 @@ This document shows the translation status of each locale file in the repository - Text.Repository.UseRelativeTimeInHistories - Text.Repository.ViewLogs - Text.Repository.Visit +- Text.Repository.WorkspaceSwitcher - Text.SetUpstream - Text.SetUpstream.Local - Text.SetUpstream.Unset @@ -268,17 +274,18 @@ This document shows the translation status of each locale file in the repository
-### ![ru__RU](https://img.shields.io/badge/ru__RU-99.75%25-yellow) +### ![ru__RU](https://img.shields.io/badge/ru__RU-99.62%25-yellow)
Missing keys in ru_RU.axaml - Text.Hotkeys.Global.SwitchWorkspace - Text.Repository.ShowSubmodulesAsTree +- Text.Repository.WorkspaceSwitcher
-### ![ta__IN](https://img.shields.io/badge/ta__IN-94.41%25-yellow) +### ![ta__IN](https://img.shields.io/badge/ta__IN-94.29%25-yellow)
Missing keys in ta_IN.axaml @@ -312,6 +319,7 @@ This document shows the translation status of each locale file in the repository - Text.Repository.ShowSubmodulesAsTree - Text.Repository.ViewLogs - Text.Repository.Visit +- Text.Repository.WorkspaceSwitcher - Text.Submodule.Status - Text.Submodule.Status.Modified - Text.Submodule.Status.NotInited @@ -330,7 +338,7 @@ This document shows the translation status of each locale file in the repository
-### ![uk__UA](https://img.shields.io/badge/uk__UA-95.55%25-yellow) +### ![uk__UA](https://img.shields.io/badge/uk__UA-95.43%25-yellow)
Missing keys in uk_UA.axaml @@ -360,6 +368,7 @@ This document shows the translation status of each locale file in the repository - Text.Repository.ShowSubmodulesAsTree - Text.Repository.ViewLogs - Text.Repository.Visit +- Text.Repository.WorkspaceSwitcher - Text.Submodule.Status - Text.Submodule.Status.Modified - Text.Submodule.Status.NotInited From fd35e0817da331a444a83b718494b33a1b700a27 Mon Sep 17 00:00:00 2001 From: AquariusStar <48148723+AquariusStar@users.noreply.github.com> Date: Sun, 18 May 2025 10:02:30 +0300 Subject: [PATCH 162/392] localization: update russian translate (#1331) --- src/Resources/Locales/ru_RU.axaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index ca5cc384..1fabffda 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -390,6 +390,7 @@ Перейти на предыдущую вкладку Создать новую вкладку Открыть диалоговое окно настроек + Переключить активное рабочее место РЕПОЗИТОРИЙ Зафиксировать сформированные изменения Зафиксировать и выложить сформированные изменения @@ -621,6 +622,7 @@ Сообщение SHA Текущая ветка + Показывать подмодули как дерево Показывать метки как катлог ПРОПУСТИТЬ Статистикa @@ -637,6 +639,7 @@ Использовать относительное время в историях Просмотр журналов Посетить '{0}' в браузере + Переключить рабочее место РАБОЧИЕ КАТАЛОГИ ДОБАВИТЬ РАБОЧИЙ КАТАЛОГ ОБРЕЗАТЬ From 36c2e083cc087a2a0007cf44abd35df2f455fb69 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 18 May 2025 07:02:46 +0000 Subject: [PATCH 163/392] doc: Update translation status and sort locale files --- TRANSLATION.md | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index aee0e2a0..d810b5b2 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -274,16 +274,7 @@ This document shows the translation status of each locale file in the repository
-### ![ru__RU](https://img.shields.io/badge/ru__RU-99.62%25-yellow) - -
-Missing keys in ru_RU.axaml - -- Text.Hotkeys.Global.SwitchWorkspace -- Text.Repository.ShowSubmodulesAsTree -- Text.Repository.WorkspaceSwitcher - -
+### ![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen) ### ![ta__IN](https://img.shields.io/badge/ta__IN-94.29%25-yellow) From 9614b995d85e93dc891e6da9b0a276d4dfc02c83 Mon Sep 17 00:00:00 2001 From: leo Date: Sun, 18 May 2025 19:36:17 +0800 Subject: [PATCH 164/392] refactor: workspace/page switcher (#1330) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - add `Switch Tab` popup - change hotkey to open `Preferences` to `Ctrl+,/⌘+,` - change hotkey to open `Switch Workspace` to `Ctrl+Shift+P/⌘+⇧+P` - change hotkey to open `Switch Tab` to `Ctrl+P/⌘+P` Signed-off-by: leo --- src/Resources/Locales/en_US.axaml | 4 +- src/Resources/Locales/ru_RU.axaml | 2 +- src/Resources/Locales/zh_CN.axaml | 4 +- src/Resources/Locales/zh_TW.axaml | 4 +- src/ViewModels/Launcher.cs | 25 ++-- src/ViewModels/LauncherPageSwitcher.cs | 77 +++++++++++ src/ViewModels/Repository.cs | 2 +- src/ViewModels/WorkspaceSwitcher.cs | 2 +- src/Views/Hotkeys.axaml | 11 +- src/Views/Launcher.axaml | 8 +- src/Views/Launcher.axaml.cs | 18 ++- ...ector.axaml => LauncherPageSwitcher.axaml} | 53 ++++---- src/Views/LauncherPageSwitcher.axaml.cs | 49 +++++++ src/Views/LauncherTabBar.axaml | 104 ++++++++++++++- src/Views/LauncherTabBar.axaml.cs | 111 +++++++++++++++- src/Views/LauncherTabsSelector.axaml.cs | 120 ------------------ src/Views/WorkspaceSwitcher.axaml | 4 +- 17 files changed, 418 insertions(+), 180 deletions(-) create mode 100644 src/ViewModels/LauncherPageSwitcher.cs rename src/Views/{LauncherTabsSelector.axaml => LauncherPageSwitcher.axaml} (70%) create mode 100644 src/Views/LauncherPageSwitcher.axaml.cs delete mode 100644 src/Views/LauncherTabsSelector.axaml.cs diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 4ce0a1ad..65b9d325 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -387,6 +387,7 @@ Create new page Open Preferences dialog Switch active workspace + Switch active page REPOSITORY Commit staged changes Commit and push staged changes @@ -429,6 +430,8 @@ Open in Browser ERROR NOTICE + Switch Workspace + Switch Tab Merge Branch Into: Merge Option: @@ -635,7 +638,6 @@ Use relative time in histories View Logs Visit '{0}' in Browser - Switch Workspace WORKTREES Add Worktree Prune diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index 1fabffda..a569cd97 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -433,6 +433,7 @@ Открыть в браузере ОШИБКА УВЕДОМЛЕНИЕ + Переключить рабочее место Влить ветку В: Опции слияния: @@ -639,7 +640,6 @@ Использовать относительное время в историях Просмотр журналов Посетить '{0}' в браузере - Переключить рабочее место РАБОЧИЕ КАТАЛОГИ ДОБАВИТЬ РАБОЧИЙ КАТАЛОГ ОБРЕЗАТЬ diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 1fd1a610..1344dfed 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -391,6 +391,7 @@ 新建页面 打开偏好设置面板 切换工作区 + 切换显示页面 仓库页面快捷键 提交暂存区更改 提交暂存区更改并推送 @@ -433,6 +434,8 @@ 在浏览器中访问 出错了 系统提示 + 切换工作区 + 切换页面 合并分支 目标分支 : 合并方式 : @@ -639,7 +642,6 @@ 在提交列表中使用相对时间 查看命令日志 访问远程仓库 '{0}' - 切换工作区 工作树列表 新增工作树 清理 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index eca8214c..a95f7211 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -391,6 +391,7 @@ 新增頁面 開啟偏好設定面板 切換工作區 + 切換目前頁面 存放庫頁面快速鍵 提交暫存區變更 提交暫存區變更並推送 @@ -433,6 +434,8 @@ 在瀏覽器中開啟連結 發生錯誤 系統提示 + 切換工作區 + 切換目前頁面 合併分支 目標分支: 合併方式: @@ -639,7 +642,6 @@ 在提交列表中使用相對時間 檢視 Git 指令記錄 檢視遠端存放庫 '{0}' - 切換工作區 工作區列表 新增工作區 清理 diff --git a/src/ViewModels/Launcher.cs b/src/ViewModels/Launcher.cs index 9a54bb32..3b6a4dd8 100644 --- a/src/ViewModels/Launcher.cs +++ b/src/ViewModels/Launcher.cs @@ -23,12 +23,6 @@ namespace SourceGit.ViewModels private set; } - public WorkspaceSwitcher WorkspaceSwitcher - { - get => _workspaceSwitcher; - set => SetProperty(ref _workspaceSwitcher, value); - } - public Workspace ActiveWorkspace { get => _activeWorkspace; @@ -50,6 +44,12 @@ namespace SourceGit.ViewModels } } + public object Switcher + { + get => _switcher; + set => SetProperty(ref _switcher, value); + } + public Launcher(string startupRepo) { _ignoreIndexChange = true; @@ -138,12 +138,17 @@ namespace SourceGit.ViewModels public void OpenWorkspaceSwitcher() { - WorkspaceSwitcher = new WorkspaceSwitcher(this); + Switcher = new WorkspaceSwitcher(this); } - public void CancelWorkspaceSwitcher() + public void OpenTabSwitcher() { - WorkspaceSwitcher = null; + Switcher = new LauncherPageSwitcher(this); + } + + public void CancelSwitcher() + { + Switcher = null; } public void SwitchWorkspace(Workspace to) @@ -618,6 +623,6 @@ namespace SourceGit.ViewModels private LauncherPage _activePage = null; private bool _ignoreIndexChange = false; private string _title = string.Empty; - private WorkspaceSwitcher _workspaceSwitcher = null; + private object _switcher = null; } } diff --git a/src/ViewModels/LauncherPageSwitcher.cs b/src/ViewModels/LauncherPageSwitcher.cs new file mode 100644 index 00000000..b0dfaca3 --- /dev/null +++ b/src/ViewModels/LauncherPageSwitcher.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using CommunityToolkit.Mvvm.ComponentModel; + +namespace SourceGit.ViewModels +{ + public class LauncherPageSwitcher : ObservableObject + { + public List VisiblePages + { + get => _visiblePages; + private set => SetProperty(ref _visiblePages, value); + } + + public string SearchFilter + { + get => _searchFilter; + set + { + if (SetProperty(ref _searchFilter, value)) + UpdateVisiblePages(); + } + } + + public LauncherPage SelectedPage + { + get => _selectedPage; + set => SetProperty(ref _selectedPage, value); + } + + public LauncherPageSwitcher(Launcher launcher) + { + _launcher = launcher; + UpdateVisiblePages(); + } + + public void ClearFilter() + { + SearchFilter = string.Empty; + } + + public void Switch() + { + if (_selectedPage is { }) + _launcher.ActivePage = _selectedPage; + + _launcher.CancelSwitcher(); + } + + private void UpdateVisiblePages() + { + var visible = new List(); + if (string.IsNullOrEmpty(_searchFilter)) + { + visible.AddRange(_launcher.Pages); + } + else + { + foreach (var page in _launcher.Pages) + { + if (page.Node.Name.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase) || + (page.Node.IsRepository && page.Node.Id.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase))) + { + visible.Add(page); + } + } + } + + VisiblePages = visible; + } + + private Launcher _launcher = null; + private List _visiblePages = []; + private string _searchFilter = string.Empty; + private LauncherPage _selectedPage = null; + } +} diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index d24f6fbf..a3f63251 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -2632,7 +2632,7 @@ namespace SourceGit.ViewModels if (node.Path.Equals(path, StringComparison.Ordinal)) return node; - if (path!.StartsWith(node.Path, StringComparison.Ordinal)) + if (path.StartsWith(node.Path, StringComparison.Ordinal)) { var founded = FindBranchNode(node.Children, path); if (founded != null) diff --git a/src/ViewModels/WorkspaceSwitcher.cs b/src/ViewModels/WorkspaceSwitcher.cs index 01d62744..41f47631 100644 --- a/src/ViewModels/WorkspaceSwitcher.cs +++ b/src/ViewModels/WorkspaceSwitcher.cs @@ -44,7 +44,7 @@ namespace SourceGit.ViewModels if (_selectedWorkspace is { }) _launcher.SwitchWorkspace(_selectedWorkspace); - _launcher.CancelWorkspaceSwitcher(); + _launcher.CancelSwitcher(); } private void UpdateVisibleWorkspaces() diff --git a/src/Views/Hotkeys.axaml b/src/Views/Hotkeys.axaml index 5d9a6f9f..5275f264 100644 --- a/src/Views/Hotkeys.axaml +++ b/src/Views/Hotkeys.axaml @@ -45,8 +45,8 @@ FontSize="{Binding Source={x:Static vm:Preferences.Instance}, Path=DefaultFontSize, Converter={x:Static c:DoubleConverters.Increase}}" Margin="0,0,0,8"/> - - + + @@ -55,7 +55,7 @@ - + @@ -70,8 +70,11 @@ - + + + + + IsVisible="{Binding Switcher, Converter={x:Static ObjectConverters.IsNotNull}}"> - + + + + + diff --git a/src/Views/Launcher.axaml.cs b/src/Views/Launcher.axaml.cs index 02cc4f08..abcbaba9 100644 --- a/src/Views/Launcher.axaml.cs +++ b/src/Views/Launcher.axaml.cs @@ -133,8 +133,8 @@ namespace SourceGit.Views return; } - // Ctrl+Shift+P opens preference dialog (macOS use hotkeys in system menu bar) - if (!OperatingSystem.IsMacOS() && e is { KeyModifiers: (KeyModifiers.Control | KeyModifiers.Shift), Key: Key.P }) + // Ctrl+, opens preference dialog (macOS use hotkeys in system menu bar) + if (!OperatingSystem.IsMacOS() && e is { KeyModifiers: KeyModifiers.Control, Key: Key.OemComma }) { App.ShowWindow(new Preferences(), true); e.Handled = true; @@ -149,7 +149,7 @@ namespace SourceGit.Views } // Ctrl+Q quits the application (macOS use hotkeys in system menu bar) - if (!OperatingSystem.IsMacOS() && e.KeyModifiers == KeyModifiers.Control && e.Key == Key.Q) + if (!OperatingSystem.IsMacOS() && e is { KeyModifiers: KeyModifiers.Control, Key: Key.Q }) { App.Quit(0); return; @@ -157,10 +157,18 @@ namespace SourceGit.Views if (e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control)) { - if (e.Key == Key.P) + if (e.KeyModifiers.HasFlag(KeyModifiers.Shift) && e.Key == Key.P) { vm.OpenWorkspaceSwitcher(); e.Handled = true; + return; + } + + if (e.Key == Key.P) + { + vm.OpenTabSwitcher(); + e.Handled = true; + return; } if (e.Key == Key.W) @@ -257,7 +265,7 @@ namespace SourceGit.Views else if (e.Key == Key.Escape) { vm.ActivePage.CancelPopup(); - vm.CancelWorkspaceSwitcher(); + vm.CancelSwitcher(); e.Handled = true; return; } diff --git a/src/Views/LauncherTabsSelector.axaml b/src/Views/LauncherPageSwitcher.axaml similarity index 70% rename from src/Views/LauncherTabsSelector.axaml rename to src/Views/LauncherPageSwitcher.axaml index 109a2ce7..09f42038 100644 --- a/src/Views/LauncherTabsSelector.axaml +++ b/src/Views/LauncherPageSwitcher.axaml @@ -3,19 +3,27 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:vm="using:SourceGit.ViewModels" + xmlns:v="using:SourceGit.Views" xmlns:c="using:SourceGit.Converters" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" - x:Class="SourceGit.Views.LauncherTabsSelector" - x:Name="ThisControl"> - - + + + + + VerticalContentAlignment="Center" + v:AutoFocusBehaviour.IsEnabled="True"> - + ItemsSource="{Binding VisiblePages, Mode=OneWay}" + SelectedItem="{Binding SelectedPage, Mode=TwoWay}"> @@ -72,30 +83,28 @@ - + - - diff --git a/src/Views/LauncherPageSwitcher.axaml.cs b/src/Views/LauncherPageSwitcher.axaml.cs new file mode 100644 index 00000000..277eb549 --- /dev/null +++ b/src/Views/LauncherPageSwitcher.axaml.cs @@ -0,0 +1,49 @@ +using Avalonia.Controls; +using Avalonia.Input; + +namespace SourceGit.Views +{ + public partial class LauncherPageSwitcher : UserControl + { + public LauncherPageSwitcher() + { + InitializeComponent(); + } + + protected override void OnKeyDown(KeyEventArgs e) + { + base.OnKeyDown(e); + + if (e.Key == Key.Enter && DataContext is ViewModels.LauncherPageSwitcher switcher) + { + switcher.Switch(); + e.Handled = true; + } + } + + private void OnItemDoubleTapped(object sender, TappedEventArgs e) + { + if (DataContext is ViewModels.LauncherPageSwitcher switcher) + { + switcher.Switch(); + e.Handled = true; + } + } + + private void OnSearchBoxKeyDown(object sender, KeyEventArgs e) + { + if (e.Key == Key.Down && PagesListBox.ItemCount > 0) + { + PagesListBox.Focus(NavigationMethod.Directional); + + if (PagesListBox.SelectedIndex < 0) + PagesListBox.SelectedIndex = 0; + else if (PagesListBox.SelectedIndex < PagesListBox.ItemCount) + PagesListBox.SelectedIndex++; + + e.Handled = true; + } + } + } +} + diff --git a/src/Views/LauncherTabBar.axaml b/src/Views/LauncherTabBar.axaml index 0376e259..f770a5d9 100644 --- a/src/Views/LauncherTabBar.axaml +++ b/src/Views/LauncherTabBar.axaml @@ -40,7 +40,7 @@ - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/LauncherTabBar.axaml.cs b/src/Views/LauncherTabBar.axaml.cs index 12bca91f..b75d93d6 100644 --- a/src/Views/LauncherTabBar.axaml.cs +++ b/src/Views/LauncherTabBar.axaml.cs @@ -1,6 +1,7 @@ using System; using Avalonia; +using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; @@ -18,6 +19,20 @@ namespace SourceGit.Views get => GetValue(IsScrollerVisibleProperty); set => SetValue(IsScrollerVisibleProperty, value); } + + public static readonly StyledProperty SearchFilterProperty = + AvaloniaProperty.Register(nameof(SearchFilter)); + + public string SearchFilter + { + get => GetValue(SearchFilterProperty); + set => SetValue(SearchFilterProperty, value); + } + + public AvaloniaList SelectablePages + { + get; + } = []; public LauncherTabBar() { @@ -125,7 +140,15 @@ namespace SourceGit.Views var stroke = new Pen(this.FindResource("Brush.Border0") as IBrush); context.DrawGeometry(fill, stroke, geo); } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + if (change.Property == SearchFilterProperty) + UpdateSelectablePages(); + } + private void ScrollTabs(object _, PointerWheelEventArgs e) { if (!e.KeyModifiers.HasFlag(KeyModifiers.Shift)) @@ -247,16 +270,92 @@ namespace SourceGit.Views e.Handled = true; } - - private void OnGotoSelectedPage(object sender, LauncherTabSelectedEventArgs e) + + private void OnTabsDropdownOpened(object sender, EventArgs e) { - if (DataContext is ViewModels.Launcher vm) - vm.ActivePage = e.Page; + UpdateSelectablePages(); + } + + private void OnTabsDropdownKeyDown(object sender, KeyEventArgs e) + { + if (e.Key == Key.Escape) + { + PageSelector.Flyout?.Hide(); + e.Handled = true; + } + else if (e.Key == Key.Enter) + { + if (TabsDropdownList.SelectedItem is ViewModels.LauncherPage page && + DataContext is ViewModels.Launcher vm) + { + vm.ActivePage = page; + PageSelector.Flyout?.Hide(); + e.Handled = true; + } + } + } + + private void OnTabsDropdownSearchBoxKeyDown(object sender, KeyEventArgs e) + { + if (e.Key == Key.Down && TabsDropdownList.ItemCount > 0) + { + TabsDropdownList.Focus(NavigationMethod.Directional); - PageSelector.Flyout?.Hide(); - e.Handled = true; + if (TabsDropdownList.SelectedIndex < 0) + TabsDropdownList.SelectedIndex = 0; + else if (TabsDropdownList.SelectedIndex < TabsDropdownList.ItemCount) + TabsDropdownList.SelectedIndex++; + + e.Handled = true; + } } + private void OnTabsDropdownLostFocus(object sender, RoutedEventArgs e) + { + if (sender is Control { IsFocused: false, IsKeyboardFocusWithin: false }) + PageSelector.Flyout?.Hide(); + } + + private void OnClearSearchFilter(object sender, RoutedEventArgs e) + { + SearchFilter = string.Empty; + } + + private void OnTabsDropdownItemDoubleTapped(object sender, TappedEventArgs e) + { + if (sender is Control { DataContext: ViewModels.LauncherPage page } && + DataContext is ViewModels.Launcher vm) + { + vm.ActivePage = page; + PageSelector.Flyout?.Hide(); + e.Handled = true; + } + } + + private void UpdateSelectablePages() + { + if (DataContext is not ViewModels.Launcher vm) + return; + + SelectablePages.Clear(); + + var pages = vm.Pages; + var filter = SearchFilter?.Trim() ?? ""; + if (string.IsNullOrEmpty(filter)) + { + SelectablePages.AddRange(pages); + return; + } + + foreach (var page in pages) + { + var node = page.Node; + if (node.Name.Contains(filter, StringComparison.OrdinalIgnoreCase) || + (node.IsRepository && node.Id.Contains(filter, StringComparison.OrdinalIgnoreCase))) + SelectablePages.Add(page); + } + } + private bool _pressedTab = false; private Point _pressedTabPosition = new Point(); private bool _startDragTab = false; diff --git a/src/Views/LauncherTabsSelector.axaml.cs b/src/Views/LauncherTabsSelector.axaml.cs deleted file mode 100644 index 61d7a966..00000000 --- a/src/Views/LauncherTabsSelector.axaml.cs +++ /dev/null @@ -1,120 +0,0 @@ -using System; - -using Avalonia; -using Avalonia.Collections; -using Avalonia.Controls; -using Avalonia.Interactivity; - -namespace SourceGit.Views -{ - public class LauncherTabSelectedEventArgs : RoutedEventArgs - { - public ViewModels.LauncherPage Page { get; } - - public LauncherTabSelectedEventArgs(ViewModels.LauncherPage page) - { - RoutedEvent = LauncherTabsSelector.PageSelectedEvent; - Page = page; - } - } - - public partial class LauncherTabsSelector : UserControl - { - public static readonly StyledProperty> PagesProperty = - AvaloniaProperty.Register>(nameof(Pages)); - - public AvaloniaList Pages - { - get => GetValue(PagesProperty); - set => SetValue(PagesProperty, value); - } - - public static readonly StyledProperty SearchFilterProperty = - AvaloniaProperty.Register(nameof(SearchFilter)); - - public string SearchFilter - { - get => GetValue(SearchFilterProperty); - set => SetValue(SearchFilterProperty, value); - } - - public static readonly RoutedEvent PageSelectedEvent = - RoutedEvent.Register(nameof(PageSelected), RoutingStrategies.Tunnel | RoutingStrategies.Bubble); - - public event EventHandler PageSelected - { - add { AddHandler(PageSelectedEvent, value); } - remove { RemoveHandler(PageSelectedEvent, value); } - } - - public AvaloniaList VisiblePages - { - get; - private set; - } - - public LauncherTabsSelector() - { - VisiblePages = new AvaloniaList(); - InitializeComponent(); - } - - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) - { - base.OnPropertyChanged(change); - - if (change.Property == PagesProperty || change.Property == SearchFilterProperty) - UpdateVisiblePages(); - } - - private void OnClearSearchFilter(object sender, RoutedEventArgs e) - { - SearchFilter = string.Empty; - } - - private void OnPageSelectionChanged(object sender, SelectionChangedEventArgs e) - { - if (sender is ListBox { SelectedItem: ViewModels.LauncherPage page }) - { - _isProcessingSelection = true; - RaiseEvent(new LauncherTabSelectedEventArgs(page)); - _isProcessingSelection = false; - } - - e.Handled = true; - } - - private void UpdateVisiblePages() - { - if (_isProcessingSelection) - return; - - VisiblePages.Clear(); - - if (Pages == null) - return; - - var filter = SearchFilter?.Trim() ?? ""; - if (string.IsNullOrEmpty(filter)) - { - foreach (var p in Pages) - VisiblePages.Add(p); - - return; - } - - foreach (var page in Pages) - { - if (!page.Node.IsRepository) - continue; - - if (page.Node.Name.Contains(filter, StringComparison.OrdinalIgnoreCase) || - page.Node.Id.Contains(filter, StringComparison.OrdinalIgnoreCase)) - VisiblePages.Add(page); - } - } - - private bool _isProcessingSelection = false; - } -} - diff --git a/src/Views/WorkspaceSwitcher.axaml b/src/Views/WorkspaceSwitcher.axaml index 49fed451..aa621b73 100644 --- a/src/Views/WorkspaceSwitcher.axaml +++ b/src/Views/WorkspaceSwitcher.axaml @@ -9,7 +9,7 @@ x:DataType="vm:WorkspaceSwitcher"> @@ -82,7 +82,7 @@ - + Date: Sun, 18 May 2025 11:36:39 +0000 Subject: [PATCH 165/392] doc: Update translation status and sort locale files --- TRANSLATION.md | 58 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index d810b5b2..26e21624 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -6,7 +6,7 @@ This document shows the translation status of each locale file in the repository ### ![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen) -### ![de__DE](https://img.shields.io/badge/de__DE-98.48%25-yellow) +### ![de__DE](https://img.shields.io/badge/de__DE-98.23%25-yellow)
Missing keys in de_DE.axaml @@ -14,9 +14,11 @@ This document shows the translation status of each locale file in the repository - Text.GitFlow.FinishWithPush - Text.GitFlow.FinishWithSquash - Text.Hotkeys.Global.SwitchWorkspace +- Text.Hotkeys.Global.SwitchTab - Text.Hotkeys.TextEditor.OpenExternalMergeTool +- Text.Launcher.SwitchWorkspace +- Text.Launcher.SwitchTab - Text.Repository.ShowSubmodulesAsTree -- Text.Repository.WorkspaceSwitcher - Text.Submodule.Status - Text.Submodule.Status.Modified - Text.Submodule.Status.NotInited @@ -26,17 +28,19 @@ This document shows the translation status of each locale file in the repository
-### ![es__ES](https://img.shields.io/badge/es__ES-99.75%25-yellow) +### ![es__ES](https://img.shields.io/badge/es__ES-99.49%25-yellow)
Missing keys in es_ES.axaml - Text.Hotkeys.Global.SwitchWorkspace -- Text.Repository.WorkspaceSwitcher +- Text.Hotkeys.Global.SwitchTab +- Text.Launcher.SwitchWorkspace +- Text.Launcher.SwitchTab
-### ![fr__FR](https://img.shields.io/badge/fr__FR-94.29%25-yellow) +### ![fr__FR](https://img.shields.io/badge/fr__FR-94.05%25-yellow)
Missing keys in fr_FR.axaml @@ -61,7 +65,10 @@ This document shows the translation status of each locale file in the repository - Text.GitFlow.FinishWithPush - Text.GitFlow.FinishWithSquash - Text.Hotkeys.Global.SwitchWorkspace +- Text.Hotkeys.Global.SwitchTab - Text.Hotkeys.TextEditor.OpenExternalMergeTool +- Text.Launcher.SwitchWorkspace +- Text.Launcher.SwitchTab - Text.Preferences.Git.IgnoreCRAtEOLInDiff - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate @@ -70,7 +77,6 @@ This document shows the translation status of each locale file in the repository - Text.Repository.ShowSubmodulesAsTree - Text.Repository.ViewLogs - Text.Repository.Visit -- Text.Repository.WorkspaceSwitcher - Text.Submodule.Status - Text.Submodule.Status.Modified - Text.Submodule.Status.NotInited @@ -89,17 +95,19 @@ This document shows the translation status of each locale file in the repository
-### ![it__IT](https://img.shields.io/badge/it__IT-99.75%25-yellow) +### ![it__IT](https://img.shields.io/badge/it__IT-99.49%25-yellow)
Missing keys in it_IT.axaml - Text.Hotkeys.Global.SwitchWorkspace -- Text.Repository.WorkspaceSwitcher +- Text.Hotkeys.Global.SwitchTab +- Text.Launcher.SwitchWorkspace +- Text.Launcher.SwitchTab
-### ![ja__JP](https://img.shields.io/badge/ja__JP-94.04%25-yellow) +### ![ja__JP](https://img.shields.io/badge/ja__JP-93.80%25-yellow)
Missing keys in ja_JP.axaml @@ -124,7 +132,10 @@ This document shows the translation status of each locale file in the repository - Text.GitFlow.FinishWithPush - Text.GitFlow.FinishWithSquash - Text.Hotkeys.Global.SwitchWorkspace +- Text.Hotkeys.Global.SwitchTab - Text.Hotkeys.TextEditor.OpenExternalMergeTool +- Text.Launcher.SwitchWorkspace +- Text.Launcher.SwitchTab - Text.Preferences.Git.IgnoreCRAtEOLInDiff - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate @@ -135,7 +146,6 @@ This document shows the translation status of each locale file in the repository - Text.Repository.Tags.OrderByNameDes - Text.Repository.ViewLogs - Text.Repository.Visit -- Text.Repository.WorkspaceSwitcher - Text.Submodule.Status - Text.Submodule.Status.Modified - Text.Submodule.Status.NotInited @@ -154,7 +164,7 @@ This document shows the translation status of each locale file in the repository
-### ![pt__BR](https://img.shields.io/badge/pt__BR-85.79%25-yellow) +### ![pt__BR](https://img.shields.io/badge/pt__BR-85.57%25-yellow)
Missing keys in pt_BR.axaml @@ -208,11 +218,14 @@ This document shows the translation status of each locale file in the repository - Text.GitFlow.FinishWithSquash - Text.Hotkeys.Global.Clone - Text.Hotkeys.Global.SwitchWorkspace +- Text.Hotkeys.Global.SwitchTab - Text.Hotkeys.TextEditor.OpenExternalMergeTool - Text.InProgress.CherryPick.Head - Text.InProgress.Merge.Operating - Text.InProgress.Rebase.StoppedAt - Text.InProgress.Revert.Head +- Text.Launcher.SwitchWorkspace +- Text.Launcher.SwitchTab - Text.Merge.Source - Text.MergeMultiple - Text.MergeMultiple.CommitChanges @@ -245,7 +258,6 @@ This document shows the translation status of each locale file in the repository - Text.Repository.UseRelativeTimeInHistories - Text.Repository.ViewLogs - Text.Repository.Visit -- Text.Repository.WorkspaceSwitcher - Text.SetUpstream - Text.SetUpstream.Local - Text.SetUpstream.Unset @@ -274,9 +286,17 @@ This document shows the translation status of each locale file in the repository
-### ![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen) +### ![ru__RU](https://img.shields.io/badge/ru__RU-99.75%25-yellow) -### ![ta__IN](https://img.shields.io/badge/ta__IN-94.29%25-yellow) +
+Missing keys in ru_RU.axaml + +- Text.Hotkeys.Global.SwitchTab +- Text.Launcher.SwitchTab + +
+ +### ![ta__IN](https://img.shields.io/badge/ta__IN-94.05%25-yellow)
Missing keys in ta_IN.axaml @@ -301,7 +321,10 @@ This document shows the translation status of each locale file in the repository - Text.GitFlow.FinishWithPush - Text.GitFlow.FinishWithSquash - Text.Hotkeys.Global.SwitchWorkspace +- Text.Hotkeys.Global.SwitchTab - Text.Hotkeys.TextEditor.OpenExternalMergeTool +- Text.Launcher.SwitchWorkspace +- Text.Launcher.SwitchTab - Text.Preferences.Git.IgnoreCRAtEOLInDiff - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate @@ -310,7 +333,6 @@ This document shows the translation status of each locale file in the repository - Text.Repository.ShowSubmodulesAsTree - Text.Repository.ViewLogs - Text.Repository.Visit -- Text.Repository.WorkspaceSwitcher - Text.Submodule.Status - Text.Submodule.Status.Modified - Text.Submodule.Status.NotInited @@ -329,7 +351,7 @@ This document shows the translation status of each locale file in the repository
-### ![uk__UA](https://img.shields.io/badge/uk__UA-95.43%25-yellow) +### ![uk__UA](https://img.shields.io/badge/uk__UA-95.19%25-yellow)
Missing keys in uk_UA.axaml @@ -350,7 +372,10 @@ This document shows the translation status of each locale file in the repository - Text.GitFlow.FinishWithPush - Text.GitFlow.FinishWithSquash - Text.Hotkeys.Global.SwitchWorkspace +- Text.Hotkeys.Global.SwitchTab - Text.Hotkeys.TextEditor.OpenExternalMergeTool +- Text.Launcher.SwitchWorkspace +- Text.Launcher.SwitchTab - Text.Preferences.Git.IgnoreCRAtEOLInDiff - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate @@ -359,7 +384,6 @@ This document shows the translation status of each locale file in the repository - Text.Repository.ShowSubmodulesAsTree - Text.Repository.ViewLogs - Text.Repository.Visit -- Text.Repository.WorkspaceSwitcher - Text.Submodule.Status - Text.Submodule.Status.Modified - Text.Submodule.Status.NotInited From 4b849d9d5ccdab6d97ce0671cc4d29e63c45d3cf Mon Sep 17 00:00:00 2001 From: leo Date: Sun, 18 May 2025 20:33:55 +0800 Subject: [PATCH 166/392] ux: update workspace/page switcher popup layout Signed-off-by: leo --- src/Resources/Locales/en_US.axaml | 4 ++-- src/Resources/Locales/ru_RU.axaml | 2 +- src/Resources/Locales/zh_CN.axaml | 4 ++-- src/Resources/Locales/zh_TW.axaml | 4 ++-- src/Views/Launcher.axaml | 4 ++-- src/Views/LauncherPageSwitcher.axaml | 10 +++++----- src/Views/WorkspaceSwitcher.axaml | 10 +++++----- 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 65b9d325..bb259272 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -430,8 +430,8 @@ Open in Browser ERROR NOTICE - Switch Workspace - Switch Tab + Workspaces + Pages Merge Branch Into: Merge Option: diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index a569cd97..77a7ba4f 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -433,7 +433,7 @@ Открыть в браузере ОШИБКА УВЕДОМЛЕНИЕ - Переключить рабочее место + Рабочие места Влить ветку В: Опции слияния: diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 1344dfed..18569a14 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -434,8 +434,8 @@ 在浏览器中访问 出错了 系统提示 - 切换工作区 - 切换页面 + 工作区列表 + 页面列表 合并分支 目标分支 : 合并方式 : diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index a95f7211..ded99a14 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -434,8 +434,8 @@ 在瀏覽器中開啟連結 發生錯誤 系統提示 - 切換工作區 - 切換目前頁面 + 工作區列表 + 頁面列表 合併分支 目標分支: 合併方式: diff --git a/src/Views/Launcher.axaml b/src/Views/Launcher.axaml index c9565452..18e307fe 100644 --- a/src/Views/Launcher.axaml +++ b/src/Views/Launcher.axaml @@ -103,13 +103,13 @@ - + - + diff --git a/src/Views/LauncherPageSwitcher.axaml b/src/Views/LauncherPageSwitcher.axaml index 09f42038..4d785b55 100644 --- a/src/Views/LauncherPageSwitcher.axaml +++ b/src/Views/LauncherPageSwitcher.axaml @@ -8,15 +8,15 @@ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="SourceGit.Views.LauncherPageSwitcher" x:DataType="vm:LauncherPageSwitcher"> - + + HorizontalAlignment="Center"/> - + + HorizontalAlignment="Center"/> Date: Sun, 18 May 2025 12:34:17 +0000 Subject: [PATCH 167/392] doc: Update translation status and sort locale files --- TRANSLATION.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index 26e21624..1a9d00d1 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -16,8 +16,8 @@ This document shows the translation status of each locale file in the repository - Text.Hotkeys.Global.SwitchWorkspace - Text.Hotkeys.Global.SwitchTab - Text.Hotkeys.TextEditor.OpenExternalMergeTool -- Text.Launcher.SwitchWorkspace -- Text.Launcher.SwitchTab +- Text.Launcher.Workspaces +- Text.Launcher.Pages - Text.Repository.ShowSubmodulesAsTree - Text.Submodule.Status - Text.Submodule.Status.Modified @@ -35,8 +35,8 @@ This document shows the translation status of each locale file in the repository - Text.Hotkeys.Global.SwitchWorkspace - Text.Hotkeys.Global.SwitchTab -- Text.Launcher.SwitchWorkspace -- Text.Launcher.SwitchTab +- Text.Launcher.Workspaces +- Text.Launcher.Pages
@@ -67,8 +67,8 @@ This document shows the translation status of each locale file in the repository - Text.Hotkeys.Global.SwitchWorkspace - Text.Hotkeys.Global.SwitchTab - Text.Hotkeys.TextEditor.OpenExternalMergeTool -- Text.Launcher.SwitchWorkspace -- Text.Launcher.SwitchTab +- Text.Launcher.Workspaces +- Text.Launcher.Pages - Text.Preferences.Git.IgnoreCRAtEOLInDiff - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate @@ -102,8 +102,8 @@ This document shows the translation status of each locale file in the repository - Text.Hotkeys.Global.SwitchWorkspace - Text.Hotkeys.Global.SwitchTab -- Text.Launcher.SwitchWorkspace -- Text.Launcher.SwitchTab +- Text.Launcher.Workspaces +- Text.Launcher.Pages @@ -134,8 +134,8 @@ This document shows the translation status of each locale file in the repository - Text.Hotkeys.Global.SwitchWorkspace - Text.Hotkeys.Global.SwitchTab - Text.Hotkeys.TextEditor.OpenExternalMergeTool -- Text.Launcher.SwitchWorkspace -- Text.Launcher.SwitchTab +- Text.Launcher.Workspaces +- Text.Launcher.Pages - Text.Preferences.Git.IgnoreCRAtEOLInDiff - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate @@ -224,8 +224,8 @@ This document shows the translation status of each locale file in the repository - Text.InProgress.Merge.Operating - Text.InProgress.Rebase.StoppedAt - Text.InProgress.Revert.Head -- Text.Launcher.SwitchWorkspace -- Text.Launcher.SwitchTab +- Text.Launcher.Workspaces +- Text.Launcher.Pages - Text.Merge.Source - Text.MergeMultiple - Text.MergeMultiple.CommitChanges @@ -292,7 +292,7 @@ This document shows the translation status of each locale file in the repository Missing keys in ru_RU.axaml - Text.Hotkeys.Global.SwitchTab -- Text.Launcher.SwitchTab +- Text.Launcher.Pages @@ -323,8 +323,8 @@ This document shows the translation status of each locale file in the repository - Text.Hotkeys.Global.SwitchWorkspace - Text.Hotkeys.Global.SwitchTab - Text.Hotkeys.TextEditor.OpenExternalMergeTool -- Text.Launcher.SwitchWorkspace -- Text.Launcher.SwitchTab +- Text.Launcher.Workspaces +- Text.Launcher.Pages - Text.Preferences.Git.IgnoreCRAtEOLInDiff - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate @@ -374,8 +374,8 @@ This document shows the translation status of each locale file in the repository - Text.Hotkeys.Global.SwitchWorkspace - Text.Hotkeys.Global.SwitchTab - Text.Hotkeys.TextEditor.OpenExternalMergeTool -- Text.Launcher.SwitchWorkspace -- Text.Launcher.SwitchTab +- Text.Launcher.Workspaces +- Text.Launcher.Pages - Text.Preferences.Git.IgnoreCRAtEOLInDiff - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate From 5e85f6fefed553c71fb4ddb9b2dacd333f93aab6 Mon Sep 17 00:00:00 2001 From: leo Date: Sun, 18 May 2025 20:47:04 +0800 Subject: [PATCH 168/392] enhance: auto-select the first page by default Signed-off-by: leo --- src/ViewModels/LauncherPageSwitcher.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ViewModels/LauncherPageSwitcher.cs b/src/ViewModels/LauncherPageSwitcher.cs index b0dfaca3..a73a216f 100644 --- a/src/ViewModels/LauncherPageSwitcher.cs +++ b/src/ViewModels/LauncherPageSwitcher.cs @@ -67,6 +67,7 @@ namespace SourceGit.ViewModels } VisiblePages = visible; + SelectedPage = visible.Count > 0 ? visible[0] : null; } private Launcher _launcher = null; From b78f6b0ea8da7aa857aa27326f683d7995ebcf7a Mon Sep 17 00:00:00 2001 From: qiufengshe <172344058@qq.com> Date: Sun, 18 May 2025 20:52:05 +0800 Subject: [PATCH 169/392] perf: minimize temporary strings for better performance (#1332) --- src/ViewModels/Histories.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ViewModels/Histories.cs b/src/ViewModels/Histories.cs index 555954d9..f21d2636 100644 --- a/src/ViewModels/Histories.cs +++ b/src/ViewModels/Histories.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using System.IO; @@ -387,7 +387,7 @@ namespace SourceGit.ViewModels { var builder = new StringBuilder(); foreach (var c in selected) - builder.AppendLine($"{c.SHA.Substring(0, 10)} - {c.Subject}"); + builder.AppendLine($"{c.SHA.AsSpan(0, 10)} - {c.Subject}"); App.CopyText(builder.ToString()); e.Handled = true; @@ -780,7 +780,7 @@ namespace SourceGit.ViewModels copyInfo.Icon = App.CreateMenuIcon("Icons.Info"); copyInfo.Click += (_, e) => { - App.CopyText($"{commit.SHA.Substring(0, 10)} - {commit.Subject}"); + App.CopyText($"{commit.SHA.AsSpan(0, 10)} - {commit.Subject}"); e.Handled = true; }; From aff003fd6dab3e181cddcf53d2a61c823c2d41bb Mon Sep 17 00:00:00 2001 From: leo Date: Sun, 18 May 2025 22:00:35 +0800 Subject: [PATCH 170/392] enhance: cleanup unused resources Signed-off-by: leo --- src/ViewModels/Launcher.cs | 9 +++++---- src/ViewModels/LauncherPageSwitcher.cs | 19 ++++++++++++------- src/ViewModels/WorkspaceSwitcher.cs | 13 +++++++++---- src/Views/Launcher.axaml | 5 +++-- src/Views/Launcher.axaml.cs | 7 +++++++ src/Views/LauncherPageSwitcher.axaml.cs | 2 +- src/Views/LauncherTabBar.axaml | 2 +- src/Views/LauncherTabBar.axaml.cs | 20 +++++++++++++------- 8 files changed, 51 insertions(+), 26 deletions(-) diff --git a/src/ViewModels/Launcher.cs b/src/ViewModels/Launcher.cs index 3b6a4dd8..4c0714df 100644 --- a/src/ViewModels/Launcher.cs +++ b/src/ViewModels/Launcher.cs @@ -44,10 +44,10 @@ namespace SourceGit.ViewModels } } - public object Switcher + public IDisposable Switcher { get => _switcher; - set => SetProperty(ref _switcher, value); + private set => SetProperty(ref _switcher, value); } public Launcher(string startupRepo) @@ -148,12 +148,13 @@ namespace SourceGit.ViewModels public void CancelSwitcher() { + Switcher?.Dispose(); Switcher = null; } public void SwitchWorkspace(Workspace to) { - if (to.IsActive) + if (to == null || to.IsActive) return; foreach (var one in Pages) @@ -623,6 +624,6 @@ namespace SourceGit.ViewModels private LauncherPage _activePage = null; private bool _ignoreIndexChange = false; private string _title = string.Empty; - private object _switcher = null; + private IDisposable _switcher = null; } } diff --git a/src/ViewModels/LauncherPageSwitcher.cs b/src/ViewModels/LauncherPageSwitcher.cs index a73a216f..5f53021d 100644 --- a/src/ViewModels/LauncherPageSwitcher.cs +++ b/src/ViewModels/LauncherPageSwitcher.cs @@ -4,14 +4,14 @@ using CommunityToolkit.Mvvm.ComponentModel; namespace SourceGit.ViewModels { - public class LauncherPageSwitcher : ObservableObject + public class LauncherPageSwitcher : ObservableObject, IDisposable { public List VisiblePages { get => _visiblePages; private set => SetProperty(ref _visiblePages, value); } - + public string SearchFilter { get => _searchFilter; @@ -27,13 +27,13 @@ namespace SourceGit.ViewModels get => _selectedPage; set => SetProperty(ref _selectedPage, value); } - + public LauncherPageSwitcher(Launcher launcher) { _launcher = launcher; UpdateVisiblePages(); } - + public void ClearFilter() { SearchFilter = string.Empty; @@ -41,12 +41,17 @@ namespace SourceGit.ViewModels public void Switch() { - if (_selectedPage is { }) - _launcher.ActivePage = _selectedPage; - + _launcher.ActivePage = _selectedPage ?? _launcher.ActivePage; _launcher.CancelSwitcher(); } + public void Dispose() + { + _visiblePages.Clear(); + _selectedPage = null; + _searchFilter = string.Empty; + } + private void UpdateVisiblePages() { var visible = new List(); diff --git a/src/ViewModels/WorkspaceSwitcher.cs b/src/ViewModels/WorkspaceSwitcher.cs index 41f47631..7a2da9be 100644 --- a/src/ViewModels/WorkspaceSwitcher.cs +++ b/src/ViewModels/WorkspaceSwitcher.cs @@ -4,7 +4,7 @@ using CommunityToolkit.Mvvm.ComponentModel; namespace SourceGit.ViewModels { - public class WorkspaceSwitcher : ObservableObject + public class WorkspaceSwitcher : ObservableObject, IDisposable { public List VisibleWorkspaces { @@ -41,12 +41,17 @@ namespace SourceGit.ViewModels public void Switch() { - if (_selectedWorkspace is { }) - _launcher.SwitchWorkspace(_selectedWorkspace); - + _launcher.SwitchWorkspace(_selectedWorkspace); _launcher.CancelSwitcher(); } + public void Dispose() + { + _visibleWorkspaces.Clear(); + _selectedWorkspace = null; + _searchFilter = string.Empty; + } + private void UpdateVisibleWorkspaces() { var visible = new List(); diff --git a/src/Views/Launcher.axaml b/src/Views/Launcher.axaml index 18e307fe..bd115fef 100644 --- a/src/Views/Launcher.axaml +++ b/src/Views/Launcher.axaml @@ -104,9 +104,10 @@ - + IsVisible="{Binding Switcher, Converter={x:Static ObjectConverters.IsNotNull}}" + PointerPressed="OnCancelSwitcher"> diff --git a/src/Views/Launcher.axaml.cs b/src/Views/Launcher.axaml.cs index abcbaba9..be4cdf5b 100644 --- a/src/Views/Launcher.axaml.cs +++ b/src/Views/Launcher.axaml.cs @@ -322,6 +322,13 @@ namespace SourceGit.Views e.Handled = true; } + private void OnCancelSwitcher(object sender, PointerPressedEventArgs e) + { + if (e.Source == sender) + (DataContext as ViewModels.Launcher)?.CancelSwitcher(); + e.Handled = true; + } + private KeyModifiers _unhandledModifiers = KeyModifiers.None; private WindowState _lastWindowState = WindowState.Normal; } diff --git a/src/Views/LauncherPageSwitcher.axaml.cs b/src/Views/LauncherPageSwitcher.axaml.cs index 277eb549..1effb93c 100644 --- a/src/Views/LauncherPageSwitcher.axaml.cs +++ b/src/Views/LauncherPageSwitcher.axaml.cs @@ -9,7 +9,7 @@ namespace SourceGit.Views { InitializeComponent(); } - + protected override void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); diff --git a/src/Views/LauncherTabBar.axaml b/src/Views/LauncherTabBar.axaml index f770a5d9..a56da2b0 100644 --- a/src/Views/LauncherTabBar.axaml +++ b/src/Views/LauncherTabBar.axaml @@ -141,7 +141,7 @@ -
- + +
diff --git a/src/Views/StashesPage.axaml b/src/Views/StashesPage.axaml index 27305aac..15427b93 100644 --- a/src/Views/StashesPage.axaml +++ b/src/Views/StashesPage.axaml @@ -19,20 +19,12 @@ - + - From 3437f5f4a95dfded9fb8d721aa9f3763058ada67 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 28 May 2025 02:19:23 +0000 Subject: [PATCH 243/392] doc: Update translation status and sort locale files --- TRANSLATION.md | 27 ++++++++++++++++++--------- src/Resources/Locales/ta_IN.axaml | 2 +- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index 9df04a52..a6789484 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -6,7 +6,7 @@ This document shows the translation status of each locale file in the repository ### ![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen) -### ![de__DE](https://img.shields.io/badge/de__DE-96.63%25-yellow) +### ![de__DE](https://img.shields.io/badge/de__DE-96.50%25-yellow)
Missing keys in de_DE.axaml @@ -26,6 +26,7 @@ This document shows the translation status of each locale file in the repository - Text.Launcher.Workspaces - Text.Launcher.Pages - Text.Pull.RecurseSubmodules +- Text.Repository.ClearStashes - Text.Repository.ShowSubmodulesAsTree - Text.ResetWithoutCheckout - Text.ResetWithoutCheckout.MoveTo @@ -41,17 +42,18 @@ This document shows the translation status of each locale file in the repository
-### ![es__ES](https://img.shields.io/badge/es__ES-99.75%25-yellow) +### ![es__ES](https://img.shields.io/badge/es__ES-99.63%25-yellow)
Missing keys in es_ES.axaml - Text.CreateBranch.OverwriteExisting +- Text.Repository.ClearStashes - Text.WorkingCopy.ResetAuthor
-### ![fr__FR](https://img.shields.io/badge/fr__FR-92.50%25-yellow) +### ![fr__FR](https://img.shields.io/badge/fr__FR-92.38%25-yellow)
Missing keys in fr_FR.axaml @@ -92,6 +94,7 @@ This document shows the translation status of each locale file in the repository - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate - Text.Repository.BranchSort.ByName +- Text.Repository.ClearStashes - Text.Repository.Search.ByContent - Text.Repository.ShowSubmodulesAsTree - Text.Repository.ViewLogs @@ -119,7 +122,7 @@ This document shows the translation status of each locale file in the repository
-### ![it__IT](https://img.shields.io/badge/it__IT-97.88%25-yellow) +### ![it__IT](https://img.shields.io/badge/it__IT-97.75%25-yellow)
Missing keys in it_IT.axaml @@ -136,6 +139,7 @@ This document shows the translation status of each locale file in the repository - Text.Launcher.Workspaces - Text.Launcher.Pages - Text.Pull.RecurseSubmodules +- Text.Repository.ClearStashes - Text.ResetWithoutCheckout - Text.ResetWithoutCheckout.MoveTo - Text.ResetWithoutCheckout.Target @@ -144,7 +148,7 @@ This document shows the translation status of each locale file in the repository
-### ![ja__JP](https://img.shields.io/badge/ja__JP-92.25%25-yellow) +### ![ja__JP](https://img.shields.io/badge/ja__JP-92.13%25-yellow)
Missing keys in ja_JP.axaml @@ -185,6 +189,7 @@ This document shows the translation status of each locale file in the repository - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate - Text.Repository.BranchSort.ByName +- Text.Repository.ClearStashes - Text.Repository.FilterCommits - Text.Repository.Search.ByContent - Text.Repository.ShowSubmodulesAsTree @@ -214,7 +219,7 @@ This document shows the translation status of each locale file in the repository
-### ![pt__BR](https://img.shields.io/badge/pt__BR-84.13%25-yellow) +### ![pt__BR](https://img.shields.io/badge/pt__BR-84.02%25-yellow)
Missing keys in pt_BR.axaml @@ -299,6 +304,7 @@ This document shows the translation status of each locale file in the repository - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate - Text.Repository.BranchSort.ByName +- Text.Repository.ClearStashes - Text.Repository.FilterCommits - Text.Repository.HistoriesLayout - Text.Repository.HistoriesLayout.Horizontal @@ -349,16 +355,17 @@ This document shows the translation status of each locale file in the repository
-### ![ru__RU](https://img.shields.io/badge/ru__RU-99.88%25-yellow) +### ![ru__RU](https://img.shields.io/badge/ru__RU-99.75%25-yellow)
Missing keys in ru_RU.axaml +- Text.Repository.ClearStashes - Text.WorkingCopy.ResetAuthor
-### ![ta__IN](https://img.shields.io/badge/ta__IN-92.50%25-yellow) +### ![ta__IN](https://img.shields.io/badge/ta__IN-92.38%25-yellow)
Missing keys in ta_IN.axaml @@ -399,6 +406,7 @@ This document shows the translation status of each locale file in the repository - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate - Text.Repository.BranchSort.ByName +- Text.Repository.ClearStashes - Text.Repository.Search.ByContent - Text.Repository.ShowSubmodulesAsTree - Text.Repository.ViewLogs @@ -426,7 +434,7 @@ This document shows the translation status of each locale file in the repository
-### ![uk__UA](https://img.shields.io/badge/uk__UA-93.63%25-yellow) +### ![uk__UA](https://img.shields.io/badge/uk__UA-93.51%25-yellow)
Missing keys in uk_UA.axaml @@ -463,6 +471,7 @@ This document shows the translation status of each locale file in the repository - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate - Text.Repository.BranchSort.ByName +- Text.Repository.ClearStashes - Text.Repository.Search.ByContent - Text.Repository.ShowSubmodulesAsTree - Text.Repository.ViewLogs diff --git a/src/Resources/Locales/ta_IN.axaml b/src/Resources/Locales/ta_IN.axaml index 685434a1..f7d7f405 100644 --- a/src/Resources/Locales/ta_IN.axaml +++ b/src/Resources/Locales/ta_IN.axaml @@ -561,9 +561,9 @@ தொடர்க தனிப்பயன் செயல்கள் தனிப்பயன் செயல்கள் இல்லை + எல்லா மாற்றங்களையும் நிராகரி '--குறிபதிவு' விருப்பத்தை இயக்கு கோப்பு உலாவியில் திற - எல்லா மாற்றங்களையும் நிராகரி கிளைகள்/குறிச்சொற்கள்/துணைத் தொகுதிகளைத் தேடு வரைபடத்தில் தெரிவுநிலை அமைவை நீக்கு From fbc8edcc13a336a674d2c46077462de5e1eda3f0 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 28 May 2025 14:20:31 +0800 Subject: [PATCH 244/392] feature: show conflict reason Signed-off-by: leo --- src/Commands/QueryLocalChanges.cs | 20 ++++++++++ src/Models/Change.cs | 14 +++++++ src/ViewModels/Conflict.cs | 66 ++++++++++++++++++++++++++++++- src/Views/Conflict.axaml | 12 +++++- 4 files changed, 108 insertions(+), 4 deletions(-) diff --git a/src/Commands/QueryLocalChanges.cs b/src/Commands/QueryLocalChanges.cs index 9abf433e..db29531a 100644 --- a/src/Commands/QueryLocalChanges.cs +++ b/src/Commands/QueryLocalChanges.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text.RegularExpressions; + using Avalonia.Threading; namespace SourceGit.Commands @@ -128,12 +129,31 @@ namespace SourceGit.Commands change.Set(Models.ChangeState.Deleted, Models.ChangeState.Copied); break; case "DD": + change.ConflictReason = Models.ConflictReason.BothDeleted; + change.Set(Models.ChangeState.None, Models.ChangeState.Conflicted); + break; case "AU": + change.ConflictReason = Models.ConflictReason.AddedByUs; + change.Set(Models.ChangeState.None, Models.ChangeState.Conflicted); + break; case "UD": + change.ConflictReason = Models.ConflictReason.DeletedByThem; + change.Set(Models.ChangeState.None, Models.ChangeState.Conflicted); + break; case "UA": + change.ConflictReason = Models.ConflictReason.AddedByThem; + change.Set(Models.ChangeState.None, Models.ChangeState.Conflicted); + break; case "DU": + change.ConflictReason = Models.ConflictReason.DeletedByUs; + change.Set(Models.ChangeState.None, Models.ChangeState.Conflicted); + break; case "AA": + change.ConflictReason = Models.ConflictReason.BothAdded; + change.Set(Models.ChangeState.None, Models.ChangeState.Conflicted); + break; case "UU": + change.ConflictReason = Models.ConflictReason.BothModified; change.Set(Models.ChangeState.None, Models.ChangeState.Conflicted); break; case "??": diff --git a/src/Models/Change.cs b/src/Models/Change.cs index 7d772d3e..4e0e23ae 100644 --- a/src/Models/Change.cs +++ b/src/Models/Change.cs @@ -22,6 +22,18 @@ namespace SourceGit.Models Conflicted, } + public enum ConflictReason + { + None, + BothDeleted, + AddedByUs, + DeletedByThem, + AddedByThem, + DeletedByUs, + BothAdded, + BothModified, + } + public class ChangeDataForAmend { public string FileMode { get; set; } = ""; @@ -36,6 +48,8 @@ namespace SourceGit.Models public string Path { get; set; } = ""; public string OriginalPath { get; set; } = ""; public ChangeDataForAmend DataForAmend { get; set; } = null; + public ConflictReason ConflictReason { get; set; } = ConflictReason.None; + public bool IsSubmodule { get; set; } = false; public bool IsConflicted => WorkTree == ChangeState.Conflicted; public void Set(ChangeState index, ChangeState workTree = ChangeState.None) diff --git a/src/ViewModels/Conflict.cs b/src/ViewModels/Conflict.cs index add365a3..db6492cc 100644 --- a/src/ViewModels/Conflict.cs +++ b/src/ViewModels/Conflict.cs @@ -25,6 +25,18 @@ namespace SourceGit.ViewModels public class Conflict { + public string Marker + { + get; + private set; + } + + public string Description + { + get; + private set; + } + public object Theirs { get; @@ -41,7 +53,13 @@ namespace SourceGit.ViewModels { get; private set; - } + } = false; + + public bool CanUseExternalMergeTool + { + get; + private set; + } = false; public Conflict(Repository repo, WorkingCopy wc, Models.Change change) { @@ -49,7 +67,51 @@ namespace SourceGit.ViewModels _change = change; var isSubmodule = repo.Submodules.Find(x => x.Path.Equals(change.Path, StringComparison.Ordinal)) != null; - IsResolved = !isSubmodule && new Commands.IsConflictResolved(repo.FullPath, change).Result(); + switch (change.ConflictReason) + { + case Models.ConflictReason.BothDeleted: + Marker = "DD"; + Description = "Both deleted"; + break; + case Models.ConflictReason.AddedByUs: + Marker = "AU"; + Description = "Added by us"; + break; + case Models.ConflictReason.DeletedByThem: + Marker = "UD"; + Description = "Deleted by them"; + break; + case Models.ConflictReason.AddedByThem: + Marker = "UA"; + Description = "Added by them"; + break; + case Models.ConflictReason.DeletedByUs: + Marker = "DU"; + Description = "Deleted by us"; + break; + case Models.ConflictReason.BothAdded: + Marker = "AA"; + Description = "Both added"; + if (!isSubmodule) + { + CanUseExternalMergeTool = true; + IsResolved = new Commands.IsConflictResolved(repo.FullPath, change).Result(); + } + break; + case Models.ConflictReason.BothModified: + Marker = "UU"; + Description = "Both modified"; + if (!isSubmodule) + { + CanUseExternalMergeTool = true; + IsResolved = new Commands.IsConflictResolved(repo.FullPath, change).Result(); + } + break; + default: + Marker = string.Empty; + Description = string.Empty; + break; + } var context = wc.InProgressContext; if (context is CherryPickInProgress cherryPick) diff --git a/src/Views/Conflict.axaml b/src/Views/Conflict.axaml index 6e8aceb3..a324b50f 100644 --- a/src/Views/Conflict.axaml +++ b/src/Views/Conflict.axaml @@ -14,7 +14,15 @@ - + + + + + + + + + @@ -117,7 +125,7 @@ -
-### ![ja__JP](https://img.shields.io/badge/ja__JP-92.13%25-yellow) +### ![ja__JP](https://img.shields.io/badge/ja__JP-92.01%25-yellow)
Missing keys in ja_JP.axaml @@ -160,6 +160,7 @@ This document shows the translation status of each locale file in the repository - Text.Bisect.Good - Text.Bisect.Skip - Text.Bisect.WaitingForRange +- Text.BranchCM.CompareWithCurrent - Text.BranchCM.ResetToSelectedCommit - Text.Checkout.RecurseSubmodules - Text.CommitCM.CopyAuthor @@ -355,17 +356,18 @@ This document shows the translation status of each locale file in the repository
-### ![ru__RU](https://img.shields.io/badge/ru__RU-99.75%25-yellow) +### ![ru__RU](https://img.shields.io/badge/ru__RU-99.63%25-yellow)
Missing keys in ru_RU.axaml +- Text.BranchCM.CompareWithCurrent - Text.Repository.ClearStashes - Text.WorkingCopy.ResetAuthor
-### ![ta__IN](https://img.shields.io/badge/ta__IN-92.38%25-yellow) +### ![ta__IN](https://img.shields.io/badge/ta__IN-92.26%25-yellow)
Missing keys in ta_IN.axaml @@ -377,6 +379,7 @@ This document shows the translation status of each locale file in the repository - Text.Bisect.Good - Text.Bisect.Skip - Text.Bisect.WaitingForRange +- Text.BranchCM.CompareWithCurrent - Text.BranchCM.ResetToSelectedCommit - Text.Checkout.RecurseSubmodules - Text.CommitCM.CopyAuthor From a9734ea8e961de4cf5100b4f5cad692299c6eeee Mon Sep 17 00:00:00 2001 From: leo Date: Sat, 31 May 2025 11:33:22 +0800 Subject: [PATCH 256/392] code_style: remove unused code Signed-off-by: leo --- src/Commands/QueryLocalChanges.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Commands/QueryLocalChanges.cs b/src/Commands/QueryLocalChanges.cs index db29531a..788ed617 100644 --- a/src/Commands/QueryLocalChanges.cs +++ b/src/Commands/QueryLocalChanges.cs @@ -122,12 +122,6 @@ namespace SourceGit.Commands case "CD": change.Set(Models.ChangeState.Copied, Models.ChangeState.Deleted); break; - case "DR": - change.Set(Models.ChangeState.Deleted, Models.ChangeState.Renamed); - break; - case "DC": - change.Set(Models.ChangeState.Deleted, Models.ChangeState.Copied); - break; case "DD": change.ConflictReason = Models.ConflictReason.BothDeleted; change.Set(Models.ChangeState.None, Models.ChangeState.Conflicted); From 8e5d5b946e49e201b91a0394f635ea97d305f072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20J=2E=20Mart=C3=ADnez=20M=2E?= <56406225+jjesus-dev@users.noreply.github.com> Date: Fri, 30 May 2025 22:15:06 -0600 Subject: [PATCH 257/392] localization: update spanish translations (#1379) add missing translations --- src/Resources/Locales/es_ES.axaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml index fcbefcd8..d2d7c071 100644 --- a/src/Resources/Locales/es_ES.axaml +++ b/src/Resources/Locales/es_ES.axaml @@ -220,6 +220,7 @@ Introduzca el nombre de la rama. Los espacios serán reemplazados con guiones. Crear Rama Local + Sobrescribir la rama existente Crear Etiqueta... Nueva Etiqueta En: Firma GPG @@ -592,6 +593,7 @@ Limpiar (GC & Prune) Ejecutar comando `git gc` para este repositorio. Limpiar todo + Limpiar Configurar este repositorio CONTINUAR Acciones Personalizadas @@ -784,6 +786,7 @@ INCLUIR ARCHIVOS NO RASTREADOS NO HAY MENSAJES DE ENTRADA RECIENTES NO HAY PLANTILLAS DE COMMIT + Restablecer Autor Haz clic derecho en el(los) archivo(s) seleccionado(s) y elige tu opción para resolver conflictos. Firmar STAGED From b94f26a9372b4b67ff229a034d7afac198181200 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 31 May 2025 04:15:23 +0000 Subject: [PATCH 258/392] doc: Update translation status and sort locale files --- TRANSLATION.md | 11 +---------- src/Resources/Locales/es_ES.axaml | 2 +- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index ae66ddf5..94ff684b 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -42,16 +42,7 @@ This document shows the translation status of each locale file in the repository
-### ![es__ES](https://img.shields.io/badge/es__ES-99.63%25-yellow) - -
-Missing keys in es_ES.axaml - -- Text.CreateBranch.OverwriteExisting -- Text.Repository.ClearStashes -- Text.WorkingCopy.ResetAuthor - -
+### ![es__ES](https://img.shields.io/badge/es__ES-%E2%88%9A-brightgreen) ### ![fr__FR](https://img.shields.io/badge/fr__FR-92.38%25-yellow) diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml index d2d7c071..0a558dfa 100644 --- a/src/Resources/Locales/es_ES.axaml +++ b/src/Resources/Locales/es_ES.axaml @@ -220,7 +220,7 @@ Introduzca el nombre de la rama. Los espacios serán reemplazados con guiones. Crear Rama Local - Sobrescribir la rama existente + Sobrescribir la rama existente Crear Etiqueta... Nueva Etiqueta En: Firma GPG From dd432c63e85eada01f8afb7c55bf8217568b51b8 Mon Sep 17 00:00:00 2001 From: leo Date: Sat, 31 May 2025 18:52:15 +0800 Subject: [PATCH 259/392] enhance: when counting commits in `Statistics`, if the authors have the same e-mail address, the commits are considered to be from the same person (#1380) Signed-off-by: leo --- src/Models/Statistics.cs | 53 +++++++++++++++++++++----------------- src/Views/Statistics.axaml | 4 ++- 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/src/Models/Statistics.cs b/src/Models/Statistics.cs index d982a3ed..d6f5870d 100644 --- a/src/Models/Statistics.cs +++ b/src/Models/Statistics.cs @@ -26,25 +26,23 @@ namespace SourceGit.Models public class StatisticsReport { - public static readonly string[] WEEKDAYS = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"]; - public int Total { get; set; } = 0; - public List Authors { get; set; } = new List(); - public List Series { get; set; } = new List(); - public List XAxes { get; set; } = new List(); - public List YAxes { get; set; } = new List(); + public List Authors { get; set; } = new(); + public List Series { get; set; } = new(); + public List XAxes { get; set; } = new(); + public List YAxes { get; set; } = new(); public StatisticsAuthor SelectedAuthor { get => _selectedAuthor; set => ChangeAuthor(value); } public StatisticsReport(StatisticsMode mode, DateTime start) { _mode = mode; - YAxes = [new Axis() + YAxes.Add(new Axis() { TextSize = 10, MinLimit = 0, SeparatorsPaint = new SolidColorPaint(new SKColor(0x40808080)) { StrokeThickness = 1 } - }]; + }); if (mode == StatisticsMode.ThisWeek) { @@ -72,7 +70,7 @@ namespace SourceGit.Models { Total++; - var normalized = DateTime.MinValue; + DateTime normalized; if (_mode == StatisticsMode.ThisWeek || _mode == StatisticsMode.ThisMonth) normalized = time.Date; else @@ -172,26 +170,27 @@ namespace SourceGit.Models ChangeColor(_fillColor); } - private StatisticsMode _mode = StatisticsMode.All; - private Dictionary _mapUsers = new Dictionary(); - private Dictionary _mapSamples = new Dictionary(); - private Dictionary> _mapUserSamples = new Dictionary>(); + private static readonly string[] WEEKDAYS = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"]; + private StatisticsMode _mode; + private Dictionary _mapUsers = new(); + private Dictionary _mapSamples = new(); + private Dictionary> _mapUserSamples = new(); private StatisticsAuthor _selectedAuthor = null; private uint _fillColor = 255; } public class Statistics { - public StatisticsReport All { get; set; } - public StatisticsReport Month { get; set; } - public StatisticsReport Week { get; set; } + public StatisticsReport All { get; } + public StatisticsReport Month { get; } + public StatisticsReport Week { get; } public Statistics() { - _today = DateTime.Now.ToLocalTime().Date; - var weekOffset = (7 + (int)_today.DayOfWeek - (int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek) % 7; - _thisWeekStart = _today.AddDays(-weekOffset); - _thisMonthStart = _today.AddDays(1 - _today.Day); + var today = DateTime.Now.ToLocalTime().Date; + var weekOffset = (7 + (int)today.DayOfWeek - (int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek) % 7; + _thisWeekStart = today.AddDays(-weekOffset); + _thisMonthStart = today.AddDays(1 - today.Day); All = new StatisticsReport(StatisticsMode.All, DateTime.MinValue); Month = new StatisticsReport(StatisticsMode.ThisMonth, _thisMonthStart); @@ -200,7 +199,13 @@ namespace SourceGit.Models public void AddCommit(string author, double timestamp) { - var user = User.FindOrAdd(author); + var emailIdx = author.IndexOf('±', StringComparison.Ordinal); + var email = author.Substring(emailIdx + 1).ToLower(CultureInfo.CurrentCulture); + if (!_users.TryGetValue(email, out var user)) + { + user = User.FindOrAdd(author); + _users.Add(email, user); + } var time = DateTime.UnixEpoch.AddSeconds(timestamp).ToLocalTime(); if (time >= _thisWeekStart) @@ -214,13 +219,15 @@ namespace SourceGit.Models public void Complete() { + _users.Clear(); + All.Complete(); Month.Complete(); Week.Complete(); } - - private readonly DateTime _today; + private readonly DateTime _thisMonthStart; private readonly DateTime _thisWeekStart; + private readonly Dictionary _users = new(); } } diff --git a/src/Views/Statistics.axaml b/src/Views/Statistics.axaml index 577849df..a2d18393 100644 --- a/src/Views/Statistics.axaml +++ b/src/Views/Statistics.axaml @@ -162,7 +162,9 @@ - + Date: Sat, 31 May 2025 21:26:01 +0800 Subject: [PATCH 260/392] fix: must convert the relative path to absolute before send it to first instance (#1376) Signed-off-by: leo --- src/App.axaml.cs | 12 +++++++++++- src/Models/Statistics.cs | 4 ++-- src/Views/ChangeStatusIcon.cs | 2 +- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/App.axaml.cs b/src/App.axaml.cs index 0664ee25..411f5cfb 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -393,7 +393,17 @@ namespace SourceGit _ipcChannel = new Models.IpcChannel(); if (!_ipcChannel.IsFirstInstance) { - _ipcChannel.SendToFirstInstance(desktop.Args is { Length: 1 } ? desktop.Args[0] : string.Empty); + var arg = desktop.Args is { Length: > 0 } ? desktop.Args[0].Trim() : string.Empty; + if (!string.IsNullOrEmpty(arg)) + { + if (arg.StartsWith('"') && arg.EndsWith('"')) + arg = arg.Substring(1, arg.Length - 2).Trim(); + + if (arg.Length > 0 && !Path.IsPathFullyQualified(arg)) + arg = Path.GetFullPath(arg); + } + + _ipcChannel.SendToFirstInstance(arg); Environment.Exit(0); } else diff --git a/src/Models/Statistics.cs b/src/Models/Statistics.cs index d6f5870d..a86380c3 100644 --- a/src/Models/Statistics.cs +++ b/src/Models/Statistics.cs @@ -220,12 +220,12 @@ namespace SourceGit.Models public void Complete() { _users.Clear(); - + All.Complete(); Month.Complete(); Week.Complete(); } - + private readonly DateTime _thisMonthStart; private readonly DateTime _thisWeekStart; private readonly Dictionary _users = new(); diff --git a/src/Views/ChangeStatusIcon.cs b/src/Views/ChangeStatusIcon.cs index ade46537..0f0e8da1 100644 --- a/src/Views/ChangeStatusIcon.cs +++ b/src/Views/ChangeStatusIcon.cs @@ -135,7 +135,7 @@ namespace SourceGit.Views else { ToolTip.SetTip(this, TIPS[(int)c.Index]); - } + } InvalidateVisual(); } From 26307e2343b82939247553cfbe39ec8889c016a1 Mon Sep 17 00:00:00 2001 From: leo Date: Sun, 1 Jun 2025 10:34:24 +0800 Subject: [PATCH 261/392] refactor: new tooltip for change Signed-off-by: leo --- src/Models/Change.cs | 17 +++++++++- src/Views/ChangeCollectionView.axaml | 6 ++-- src/Views/ChangeCollectionView.axaml.cs | 41 ++++++++++++++++++++++++- src/Views/ChangeStatusIcon.cs | 26 +--------------- 4 files changed, 60 insertions(+), 30 deletions(-) diff --git a/src/Models/Change.cs b/src/Models/Change.cs index a7be1f64..aaa2633f 100644 --- a/src/Models/Change.cs +++ b/src/Models/Change.cs @@ -53,6 +53,9 @@ namespace SourceGit.Models public bool IsConflicted => WorkTree == ChangeState.Conflicted; public string ConflictMarker => CONFLICT_MARKERS[(int)ConflictReason]; public string ConflictDesc => CONFLICT_DESCS[(int)ConflictReason]; + + public string WorkTreeDesc => TYPE_DESCS[(int)WorkTree]; + public string IndexDesc => TYPE_DESCS[(int)Index]; public void Set(ChangeState index, ChangeState workTree = ChangeState.None) { @@ -84,7 +87,19 @@ namespace SourceGit.Models if (!string.IsNullOrEmpty(OriginalPath) && OriginalPath[0] == '"') OriginalPath = OriginalPath.Substring(1, OriginalPath.Length - 2); } - + + private static readonly string[] TYPE_DESCS = + [ + "Unknown", + "Modified", + "Type Changed", + "Added", + "Deleted", + "Renamed", + "Copied", + "Untracked", + "Conflict" + ]; private static readonly string[] CONFLICT_MARKERS = [ string.Empty, diff --git a/src/Views/ChangeCollectionView.axaml b/src/Views/ChangeCollectionView.axaml index 11c9e706..43af3a9a 100644 --- a/src/Views/ChangeCollectionView.axaml +++ b/src/Views/ChangeCollectionView.axaml @@ -41,7 +41,7 @@ Margin="{Binding Depth, Converter={x:Static c:IntConverters.ToTreeMargin}}" Background="Transparent" DoubleTapped="OnRowDoubleTapped" - ToolTip.Tip="{Binding FullPath}"> + DataContextChanged="OnRowDataContextChanged"> + DataContextChanged="OnRowDataContextChanged"> + DataContextChanged="OnRowDataContextChanged"> AutoSelectFirstChangeProperty = - AvaloniaProperty.Register(nameof(AutoSelectFirstChange), false); + AvaloniaProperty.Register(nameof(AutoSelectFirstChange)); public bool AutoSelectFirstChange { @@ -229,6 +231,28 @@ namespace SourceGit.Views UpdateSelection(); } + private void OnRowDataContextChanged(object sender, EventArgs e) + { + if (sender is not Control control) + return; + + if (control.DataContext is ViewModels.ChangeTreeNode node) + { + if (node.Change is {} c) + UpdateRowTips(control, c); + else + ToolTip.SetTip(control, node.FullPath); + } + else if (control.DataContext is Models.Change change) + { + UpdateRowTips(control, change); + } + else + { + ToolTip.SetTip(control, null); + } + } + private void OnRowDoubleTapped(object sender, TappedEventArgs e) { var grid = sender as Grid; @@ -466,6 +490,21 @@ namespace SourceGit.Views } } + private void UpdateRowTips(Control control, Models.Change change) + { + var tip = new TextBlock() { TextWrapping = TextWrapping.Wrap }; + tip.Inlines!.Add(new Run(change.Path)); + tip.Inlines!.Add(new Run(" • ") { Foreground = Brushes.Gray }); + tip.Inlines!.Add(new Run(IsUnstagedChange ? change.WorkTreeDesc : change.IndexDesc) { Foreground = Brushes.Gray }); + if (change.IsConflicted) + { + tip.Inlines!.Add(new Run(" • ") { Foreground = Brushes.Gray }); + tip.Inlines!.Add(new Run(change.ConflictDesc) { Foreground = Brushes.Gray }); + } + + ToolTip.SetTip(control, tip); + } + private bool _disableSelectionChangingEvent = false; } } diff --git a/src/Views/ChangeStatusIcon.cs b/src/Views/ChangeStatusIcon.cs index 0f0e8da1..d66ac11d 100644 --- a/src/Views/ChangeStatusIcon.cs +++ b/src/Views/ChangeStatusIcon.cs @@ -9,6 +9,7 @@ namespace SourceGit.Views { public class ChangeStatusIcon : Control { + private static readonly string[] INDICATOR = ["?", "±", "T", "+", "−", "➜", "❏", "★", "!"]; private static readonly IBrush[] BACKGROUNDS = [ Brushes.Transparent, new LinearGradientBrush @@ -56,9 +57,6 @@ namespace SourceGit.Views Brushes.OrangeRed, ]; - private static readonly string[] INDICATOR = ["?", "±", "T", "+", "−", "➜", "❏", "★", "!"]; - private static readonly string[] TIPS = ["Unknown", "Modified", "Type Changed", "Added", "Deleted", "Renamed", "Copied", "Untracked", "Conflict"]; - public static readonly StyledProperty IsUnstagedChangeProperty = AvaloniaProperty.Register(nameof(IsUnstagedChange)); @@ -116,29 +114,7 @@ namespace SourceGit.Views base.OnPropertyChanged(change); if (change.Property == IsUnstagedChangeProperty || change.Property == ChangeProperty) - { - var isUnstaged = IsUnstagedChange; - var c = Change; - if (c == null) - { - ToolTip.SetTip(this, null); - return; - } - - if (isUnstaged) - { - if (c.IsConflicted) - ToolTip.SetTip(this, $"Conflict ({c.ConflictDesc})"); - else - ToolTip.SetTip(this, TIPS[(int)c.WorkTree]); - } - else - { - ToolTip.SetTip(this, TIPS[(int)c.Index]); - } - InvalidateVisual(); - } } } } From 6620bd193e65328d0a69abf888d599dc05030991 Mon Sep 17 00:00:00 2001 From: leo Date: Sun, 1 Jun 2025 11:09:31 +0800 Subject: [PATCH 262/392] ux: remove tooltip for `USE THEIRS` and `USE MINE` button Signed-off-by: leo --- src/Views/Conflict.axaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Views/Conflict.axaml b/src/Views/Conflict.axaml index d074bb72..71bb0b41 100644 --- a/src/Views/Conflict.axaml +++ b/src/Views/Conflict.axaml @@ -113,13 +113,13 @@ - - From a2ca071f082518862df5dd8fc6aa326b9245ce92 Mon Sep 17 00:00:00 2001 From: Henrik Andersson <138666275+henrik-andersson-sus@users.noreply.github.com> Date: Fri, 6 Jun 2025 10:44:40 +0200 Subject: [PATCH 299/392] feature: `.dds` image support (#1392) * Added Pfim as 3rdparty lib * Added support for parsing showing dds and tga images using Pfim --------- Co-authored-by: Snimax --- src/Models/ImageDecoder.cs | 1 + src/SourceGit.csproj | 1 + src/ViewModels/ImageSource.cs | 89 +++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+) diff --git a/src/Models/ImageDecoder.cs b/src/Models/ImageDecoder.cs index 3a758882..ce3a44c1 100644 --- a/src/Models/ImageDecoder.cs +++ b/src/Models/ImageDecoder.cs @@ -4,5 +4,6 @@ { None = 0, Builtin, + Pfim } } diff --git a/src/SourceGit.csproj b/src/SourceGit.csproj index 3fe21b1a..62ec6255 100644 --- a/src/SourceGit.csproj +++ b/src/SourceGit.csproj @@ -50,6 +50,7 @@ + diff --git a/src/ViewModels/ImageSource.cs b/src/ViewModels/ImageSource.cs index ec6b6a8d..2be09809 100644 --- a/src/ViewModels/ImageSource.cs +++ b/src/ViewModels/ImageSource.cs @@ -1,5 +1,7 @@ using System.IO; +using System.Runtime.InteropServices; using Avalonia.Media.Imaging; +using Pfim; namespace SourceGit.ViewModels { @@ -27,6 +29,9 @@ namespace SourceGit.ViewModels case ".png": case ".webp": return Models.ImageDecoder.Builtin; + case ".tga": + case ".dds": + return Models.ImageDecoder.Pfim; default: return Models.ImageDecoder.None; } @@ -70,9 +75,93 @@ namespace SourceGit.ViewModels // Just ignore. } } + else if (decoder == Models.ImageDecoder.Pfim) + { + return new ImageSource(LoadWithPfim(stream), size); + } } return new ImageSource(null, 0); } + + private static Bitmap LoadWithPfim(Stream stream) + { + var image = Pfim.Pfimage.FromStream(stream); + byte[] data; + int stride; + if (image.Format == ImageFormat.Rgba32) + { + data = image.Data; + stride = image.Stride; + } + else + { + int pixels = image.Width * image.Height; + data = new byte[pixels * 4]; + stride = image.Width * 4; + + switch (image.Format) + { + case ImageFormat.Rgba16: + case ImageFormat.R5g5b5a1: + { + for (int i = 0; i < pixels; i++) + { + data[i * 4 + 0] = image.Data[i * 4 + 2]; // B + data[i * 4 + 1] = image.Data[i * 4 + 1]; // G + data[i * 4 + 2] = image.Data[i * 4 + 0]; // R + data[i * 4 + 3] = image.Data[i * 4 + 3]; // A + } + } + break; + case ImageFormat.R5g5b5: + case ImageFormat.R5g6b5: + case ImageFormat.Rgb24: + { + for (int i = 0; i < pixels; i++) + { + data[i * 4 + 0] = image.Data[i * 3 + 2]; // B + data[i * 4 + 1] = image.Data[i * 3 + 1]; // G + data[i * 4 + 2] = image.Data[i * 3 + 0]; // R + data[i * 4 + 3] = 255; // A + } + } + break; + case ImageFormat.Rgb8: + { + for (int i = 0; i < pixels; i++) + { + var color = image.Data[i]; + data[i * 4 + 0] = color; + data[i * 4 + 1] = color; + data[i * 4 + 2] = color; + data[i * 4 + 3] = 255; + } + } + break; + default: + return null; + } + } + + // Pin the array and pass the pointer to Bitmap + var handle = GCHandle.Alloc(data, GCHandleType.Pinned); + try + { + var ptr = Marshal.UnsafeAddrOfPinnedArrayElement(data, 0); + var bitmap = new Bitmap( + Avalonia.Platform.PixelFormat.Bgra8888, + Avalonia.Platform.AlphaFormat.Unpremul, + ptr, + new Avalonia.PixelSize(image.Width, image.Height), + new Avalonia.Vector(96, 96), + stride); + return bitmap; + } + finally + { + handle.Free(); + } + } } } From 8db033be991836c7be7eb8373a8c8a651bbd9f3a Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 6 Jun 2025 18:21:04 +0800 Subject: [PATCH 300/392] code_review: PR #1392 - fix the issue that not all channel takes 8 bits - if `PixelFormatTranscoder.Transcode` supports the same pixel formats, let it converts pixels automatically Signed-off-by: leo --- src/ViewModels/ImageSource.cs | 174 +++++++++++++++++----------------- 1 file changed, 87 insertions(+), 87 deletions(-) diff --git a/src/ViewModels/ImageSource.cs b/src/ViewModels/ImageSource.cs index 2be09809..4993e2cf 100644 --- a/src/ViewModels/ImageSource.cs +++ b/src/ViewModels/ImageSource.cs @@ -1,6 +1,11 @@ -using System.IO; +using System; +using System.IO; using System.Runtime.InteropServices; + +using Avalonia; using Avalonia.Media.Imaging; +using Avalonia.Platform; + using Pfim; namespace SourceGit.ViewModels @@ -64,103 +69,98 @@ namespace SourceGit.ViewModels if (size > 0) { if (decoder == Models.ImageDecoder.Builtin) - { - try - { - var bitmap = new Bitmap(stream); - return new ImageSource(bitmap, size); - } - catch - { - // Just ignore. - } - } + return DecodeWithAvalonia(stream, size); else if (decoder == Models.ImageDecoder.Pfim) - { - return new ImageSource(LoadWithPfim(stream), size); - } + return DecodeWithPfim(stream, size); } return new ImageSource(null, 0); } - private static Bitmap LoadWithPfim(Stream stream) + private static ImageSource DecodeWithAvalonia(Stream stream, long size) { - var image = Pfim.Pfimage.FromStream(stream); - byte[] data; - int stride; - if (image.Format == ImageFormat.Rgba32) - { - data = image.Data; - stride = image.Stride; - } - else - { - int pixels = image.Width * image.Height; - data = new byte[pixels * 4]; - stride = image.Width * 4; - - switch (image.Format) - { - case ImageFormat.Rgba16: - case ImageFormat.R5g5b5a1: - { - for (int i = 0; i < pixels; i++) - { - data[i * 4 + 0] = image.Data[i * 4 + 2]; // B - data[i * 4 + 1] = image.Data[i * 4 + 1]; // G - data[i * 4 + 2] = image.Data[i * 4 + 0]; // R - data[i * 4 + 3] = image.Data[i * 4 + 3]; // A - } - } - break; - case ImageFormat.R5g5b5: - case ImageFormat.R5g6b5: - case ImageFormat.Rgb24: - { - for (int i = 0; i < pixels; i++) - { - data[i * 4 + 0] = image.Data[i * 3 + 2]; // B - data[i * 4 + 1] = image.Data[i * 3 + 1]; // G - data[i * 4 + 2] = image.Data[i * 3 + 0]; // R - data[i * 4 + 3] = 255; // A - } - } - break; - case ImageFormat.Rgb8: - { - for (int i = 0; i < pixels; i++) - { - var color = image.Data[i]; - data[i * 4 + 0] = color; - data[i * 4 + 1] = color; - data[i * 4 + 2] = color; - data[i * 4 + 3] = 255; - } - } - break; - default: - return null; - } - } - - // Pin the array and pass the pointer to Bitmap - var handle = GCHandle.Alloc(data, GCHandleType.Pinned); try { - var ptr = Marshal.UnsafeAddrOfPinnedArrayElement(data, 0); - var bitmap = new Bitmap( - Avalonia.Platform.PixelFormat.Bgra8888, - Avalonia.Platform.AlphaFormat.Unpremul, - ptr, - new Avalonia.PixelSize(image.Width, image.Height), - new Avalonia.Vector(96, 96), - stride); - return bitmap; + var bitmap = new Bitmap(stream); + return new ImageSource(bitmap, size); } - finally + catch { - handle.Free(); + return new ImageSource(null, 0); + } + } + + private static ImageSource DecodeWithPfim(Stream stream, long size) + { + using (var pfiImage = Pfimage.FromStream(stream)) + { + try + { + var data = pfiImage.Data; + var stride = pfiImage.Stride; + + var pixelFormat = PixelFormats.Rgba8888; + var alphaFormat = AlphaFormat.Opaque; + switch (pfiImage.Format) + { + case ImageFormat.Rgb8: + pixelFormat = PixelFormats.Gray8; + break; + case ImageFormat.R5g6b5: + pixelFormat = PixelFormats.Rgb565; + break; + case ImageFormat.Rgba16: + var pixels = pfiImage.DataLen / 2; + var newSize = pfiImage.DataLen * 2; + data = new byte[newSize]; + stride = 4 * pfiImage.Width; + for (int i = 0; i < pixels; i++) + { + var rg = pfiImage.Data[i * 2]; + var ba = pfiImage.Data[i * 2 + 1]; + data[i * 4 + 0] = (byte)Math.Round((rg >> 4) / 15.0 * 255); + data[i * 4 + 1] = (byte)Math.Round((rg & 0xF) / 15.0 * 255); + data[i * 4 + 2] = (byte)Math.Round((ba >> 4) / 15.0 * 255); + data[i * 4 + 3] = (byte)Math.Round((ba & 0xF) / 15.0 * 255); + } + alphaFormat = AlphaFormat.Premul; + break; + case ImageFormat.R5g5b5a1: + var pixels2 = pfiImage.DataLen / 2; + var newSize2 = pfiImage.DataLen * 2; + data = new byte[newSize2]; + stride = 4 * pfiImage.Width; + for (int i = 0; i < pixels2; i++) + { + var v = (int)pfiImage.Data[i * 2] << 8 + pfiImage.Data[i * 2 + 1]; + data[i * 4 + 0] = (byte)Math.Round(((v & 0b1111100000000000) >> 11) / 31.0 * 255); + data[i * 4 + 1] = (byte)Math.Round(((v & 0b11111000000) >> 6) / 31.0 * 255); + data[i * 4 + 2] = (byte)Math.Round(((v & 0b111110) >> 1) / 31.0 * 255); + data[i * 4 + 3] = (byte)((v & 1) == 1 ? 255 : 0); + } + alphaFormat = AlphaFormat.Premul; + break; + case ImageFormat.Rgb24: + pixelFormat = PixelFormats.Rgb24; + break; + case ImageFormat.Rgba32: + pixelFormat = PixelFormat.Rgba8888; + alphaFormat = AlphaFormat.Premul; + break; + default: + return new ImageSource(null, 0); + } + + var ptr = Marshal.UnsafeAddrOfPinnedArrayElement(pfiImage.Data, 0); + var pixelSize = new PixelSize(pfiImage.Width, pfiImage.Height); + var dpi = new Vector(96, 96); + var bitmap = new Bitmap(pixelFormat, alphaFormat, ptr, pixelSize, dpi, stride); + return new ImageSource(bitmap, size); + } + catch + { + return new ImageSource(null, 0); + } } } } From 47012e29dc4076cee2e01b3a1111ef88bcb5269d Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 6 Jun 2025 18:47:36 +0800 Subject: [PATCH 301/392] fix: file extensions are case-insensitive Signed-off-by: leo --- src/ViewModels/ImageSource.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/ViewModels/ImageSource.cs b/src/ViewModels/ImageSource.cs index 4993e2cf..be8ffc0f 100644 --- a/src/ViewModels/ImageSource.cs +++ b/src/ViewModels/ImageSource.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.IO; using System.Runtime.InteropServices; @@ -23,7 +24,7 @@ namespace SourceGit.ViewModels public static Models.ImageDecoder GetDecoder(string file) { - var ext = Path.GetExtension(file) ?? ".invalid_img"; + var ext = (Path.GetExtension(file) ?? ".invalid_img").ToLower(CultureInfo.CurrentCulture); switch (ext) { @@ -92,9 +93,9 @@ namespace SourceGit.ViewModels private static ImageSource DecodeWithPfim(Stream stream, long size) { - using (var pfiImage = Pfimage.FromStream(stream)) + try { - try + using (var pfiImage = Pfimage.FromStream(stream)) { var data = pfiImage.Data; var stride = pfiImage.Stride; @@ -136,7 +137,7 @@ namespace SourceGit.ViewModels data[i * 4 + 0] = (byte)Math.Round(((v & 0b1111100000000000) >> 11) / 31.0 * 255); data[i * 4 + 1] = (byte)Math.Round(((v & 0b11111000000) >> 6) / 31.0 * 255); data[i * 4 + 2] = (byte)Math.Round(((v & 0b111110) >> 1) / 31.0 * 255); - data[i * 4 + 3] = (byte)((v & 1) == 1 ? 255 : 0); + data[i * 4 + 3] = (byte)((v & 1) == 1 ? 255 : 0); } alphaFormat = AlphaFormat.Premul; break; @@ -157,10 +158,10 @@ namespace SourceGit.ViewModels var bitmap = new Bitmap(pixelFormat, alphaFormat, ptr, pixelSize, dpi, stride); return new ImageSource(bitmap, size); } - catch - { - return new ImageSource(null, 0); - } + } + catch (Exception e) + { + return new ImageSource(null, 0); } } } From 203c50350e1a2296338500250cf887e63930b64c Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 6 Jun 2025 20:50:37 +0800 Subject: [PATCH 302/392] fix: wrong pfim image format Signed-off-by: leo --- src/ViewModels/ImageSource.cs | 44 +++++++---------------------------- 1 file changed, 8 insertions(+), 36 deletions(-) diff --git a/src/ViewModels/ImageSource.cs b/src/ViewModels/ImageSource.cs index be8ffc0f..22740dc7 100644 --- a/src/ViewModels/ImageSource.cs +++ b/src/ViewModels/ImageSource.cs @@ -100,59 +100,31 @@ namespace SourceGit.ViewModels var data = pfiImage.Data; var stride = pfiImage.Stride; - var pixelFormat = PixelFormats.Rgba8888; + var pixelFormat = PixelFormats.Bgra8888; var alphaFormat = AlphaFormat.Opaque; switch (pfiImage.Format) { case ImageFormat.Rgb8: pixelFormat = PixelFormats.Gray8; break; - case ImageFormat.R5g6b5: - pixelFormat = PixelFormats.Rgb565; - break; - case ImageFormat.Rgba16: - var pixels = pfiImage.DataLen / 2; - var newSize = pfiImage.DataLen * 2; - data = new byte[newSize]; - stride = 4 * pfiImage.Width; - for (int i = 0; i < pixels; i++) - { - var rg = pfiImage.Data[i * 2]; - var ba = pfiImage.Data[i * 2 + 1]; - data[i * 4 + 0] = (byte)Math.Round((rg >> 4) / 15.0 * 255); - data[i * 4 + 1] = (byte)Math.Round((rg & 0xF) / 15.0 * 255); - data[i * 4 + 2] = (byte)Math.Round((ba >> 4) / 15.0 * 255); - data[i * 4 + 3] = (byte)Math.Round((ba & 0xF) / 15.0 * 255); - } - alphaFormat = AlphaFormat.Premul; - break; + case ImageFormat.R5g5b5: case ImageFormat.R5g5b5a1: - var pixels2 = pfiImage.DataLen / 2; - var newSize2 = pfiImage.DataLen * 2; - data = new byte[newSize2]; - stride = 4 * pfiImage.Width; - for (int i = 0; i < pixels2; i++) - { - var v = (int)pfiImage.Data[i * 2] << 8 + pfiImage.Data[i * 2 + 1]; - data[i * 4 + 0] = (byte)Math.Round(((v & 0b1111100000000000) >> 11) / 31.0 * 255); - data[i * 4 + 1] = (byte)Math.Round(((v & 0b11111000000) >> 6) / 31.0 * 255); - data[i * 4 + 2] = (byte)Math.Round(((v & 0b111110) >> 1) / 31.0 * 255); - data[i * 4 + 3] = (byte)((v & 1) == 1 ? 255 : 0); - } - alphaFormat = AlphaFormat.Premul; + pixelFormat = PixelFormats.Bgr555; + break; + case ImageFormat.R5g6b5: + pixelFormat = PixelFormats.Bgr565; break; case ImageFormat.Rgb24: - pixelFormat = PixelFormats.Rgb24; + pixelFormat = PixelFormats.Bgr24; break; case ImageFormat.Rgba32: - pixelFormat = PixelFormat.Rgba8888; alphaFormat = AlphaFormat.Premul; break; default: return new ImageSource(null, 0); } - var ptr = Marshal.UnsafeAddrOfPinnedArrayElement(pfiImage.Data, 0); + var ptr = Marshal.UnsafeAddrOfPinnedArrayElement(data, 0); var pixelSize = new PixelSize(pfiImage.Width, pfiImage.Height); var dpi = new Vector(96, 96); var bitmap = new Bitmap(pixelFormat, alphaFormat, ptr, pixelSize, dpi, stride); From d323a2064ef02adc759129e3984d3907d0be694d Mon Sep 17 00:00:00 2001 From: leo Date: Sat, 7 Jun 2025 12:00:16 +0800 Subject: [PATCH 303/392] feature: supports RGBA16 pixel format Signed-off-by: leo --- src/ViewModels/ImageSource.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/ViewModels/ImageSource.cs b/src/ViewModels/ImageSource.cs index 22740dc7..634fbefe 100644 --- a/src/ViewModels/ImageSource.cs +++ b/src/ViewModels/ImageSource.cs @@ -117,6 +117,21 @@ namespace SourceGit.ViewModels case ImageFormat.Rgb24: pixelFormat = PixelFormats.Bgr24; break; + case ImageFormat.Rgba16: + var pixels2 = pfiImage.DataLen / 2; + data = new byte[pixels2 * 4]; + stride = pfiImage.Width * 4; + for (var i = 0; i < pixels2; i++) + { + var src = BitConverter.ToUInt16(pfiImage.Data, i * 2); + data[i * 4 + 0] = (byte)Math.Round((src & 0x0F) / 15F * 255); // B + data[i * 4 + 1] = (byte)Math.Round(((src >> 4) & 0x0F) / 15F * 255); // G + data[i * 4 + 2] = (byte)Math.Round(((src >> 8) & 0x0F) / 15F * 255); // R + data[i * 4 + 3] = (byte)Math.Round(((src >> 12) & 0x0F) / 15F * 255); // A + } + + alphaFormat = AlphaFormat.Premul; + break; case ImageFormat.Rgba32: alphaFormat = AlphaFormat.Premul; break; @@ -131,7 +146,7 @@ namespace SourceGit.ViewModels return new ImageSource(bitmap, size); } } - catch (Exception e) + catch { return new ImageSource(null, 0); } From f830b68f6a7e50a8b92ed0e4541627b4e4082af0 Mon Sep 17 00:00:00 2001 From: leo Date: Sat, 7 Jun 2025 12:20:09 +0800 Subject: [PATCH 304/392] ux: change foreground for some labels Signed-off-by: leo --- src/Views/DiffView.axaml | 8 ++++---- src/Views/ImageDiffView.axaml | 6 +++--- src/Views/RevisionFileContentViewer.axaml | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Views/DiffView.axaml b/src/Views/DiffView.axaml index c9291a6b..d1a3966d 100644 --- a/src/Views/DiffView.axaml +++ b/src/Views/DiffView.axaml @@ -213,7 +213,7 @@ - + @@ -240,7 +240,7 @@ - + @@ -274,7 +274,7 @@ - + @@ -290,7 +290,7 @@
- + diff --git a/src/Views/ImageDiffView.axaml b/src/Views/ImageDiffView.axaml index c3e0f772..d0b79f57 100644 --- a/src/Views/ImageDiffView.axaml +++ b/src/Views/ImageDiffView.axaml @@ -44,7 +44,7 @@ - + @@ -79,7 +79,7 @@ - + @@ -115,7 +115,7 @@ - + diff --git a/src/Views/RevisionFileContentViewer.axaml b/src/Views/RevisionFileContentViewer.axaml index 422e9169..7dc6d384 100644 --- a/src/Views/RevisionFileContentViewer.axaml +++ b/src/Views/RevisionFileContentViewer.axaml @@ -39,7 +39,7 @@ - + From 74f52fb26636509f79c0dc0bdb33a75acd1e31a6 Mon Sep 17 00:00:00 2001 From: leo Date: Sat, 7 Jun 2025 20:27:52 +0800 Subject: [PATCH 305/392] enhance: only show syntax-highlighting toggle if current revision content is a text file Signed-off-by: leo --- src/Converters/ObjectConverters.cs | 27 +++++++++++++++++++++++++++ src/Views/FileHistories.axaml | 16 +++++++++++++++- src/Views/RevisionFiles.axaml | 7 +++++++ 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 src/Converters/ObjectConverters.cs diff --git a/src/Converters/ObjectConverters.cs b/src/Converters/ObjectConverters.cs new file mode 100644 index 00000000..f7c57764 --- /dev/null +++ b/src/Converters/ObjectConverters.cs @@ -0,0 +1,27 @@ +using System; +using System.Globalization; +using Avalonia.Data.Converters; + +namespace SourceGit.Converters +{ + public static class ObjectConverters + { + public class IsTypeOfConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value == null || parameter == null) + return false; + + return value.GetType().IsAssignableTo((Type)parameter); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return new NotImplementedException(); + } + } + + public static readonly IsTypeOfConverter IsTypeOf = new IsTypeOfConverter(); + } +} diff --git a/src/Views/FileHistories.axaml b/src/Views/FileHistories.axaml index e7a5c072..be0c91a0 100644 --- a/src/Views/FileHistories.axaml +++ b/src/Views/FileHistories.axaml @@ -139,7 +139,7 @@ - + + + + + + + diff --git a/src/Views/RevisionFiles.axaml b/src/Views/RevisionFiles.axaml index cfc20198..b4bd3354 100644 --- a/src/Views/RevisionFiles.axaml +++ b/src/Views/RevisionFiles.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" xmlns:c="using:SourceGit.Converters" @@ -133,6 +134,12 @@ Background="Transparent" IsChecked="{Binding Source={x:Static vm:Preferences.Instance}, Path=UseSyntaxHighlighting, Mode=TwoWay}" ToolTip.Tip="{DynamicResource Text.Diff.SyntaxHighlight}"> + + + From 2478d2953b3c652fc278f66694af8ecfe6904019 Mon Sep 17 00:00:00 2001 From: leo Date: Sat, 7 Jun 2025 20:42:45 +0800 Subject: [PATCH 306/392] code_style: remove unnecessary code in `DiffContext` Signed-off-by: leo --- src/ViewModels/DiffContext.cs | 39 ++++++++++++++--------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/src/ViewModels/DiffContext.cs b/src/ViewModels/DiffContext.cs index 828a2d59..9bb3c710 100644 --- a/src/ViewModels/DiffContext.cs +++ b/src/ViewModels/DiffContext.cs @@ -12,7 +12,7 @@ namespace SourceGit.ViewModels { public string Title { - get => _title; + get; } public bool IgnoreWhitespace @@ -68,9 +68,9 @@ namespace SourceGit.ViewModels } if (string.IsNullOrEmpty(_option.OrgPath) || _option.OrgPath == "/dev/null") - _title = _option.Path; + Title = _option.Path; else - _title = $"{_option.OrgPath} → {_option.Path}"; + Title = $"{_option.OrgPath} → {_option.Path}"; LoadDiffContent(); } @@ -112,9 +112,9 @@ namespace SourceGit.ViewModels Task.Run(() => { var numLines = Preferences.Instance.UseFullTextDiff ? 999999999 : _unifiedLines; - var ignoreWS = Preferences.Instance.IgnoreWhitespaceChangesInDiff; - var latest = new Commands.Diff(_repo, _option, numLines, ignoreWS).Result(); - var info = new Info(_option, numLines, ignoreWS, latest); + var ignoreWhitespace = Preferences.Instance.IgnoreWhitespaceChangesInDiff; + var latest = new Commands.Diff(_repo, _option, numLines, ignoreWhitespace).Result(); + var info = new Info(_option, numLines, ignoreWhitespace, latest); if (_info != null && info.IsSame(_info)) return; @@ -239,30 +239,24 @@ namespace SourceGit.ViewModels private Models.RevisionSubmodule QuerySubmoduleRevision(string repo, string sha) { var commit = new Commands.QuerySingleCommit(repo, sha).Result(); - if (commit != null) - { - var body = new Commands.QueryCommitFullMessage(repo, sha).Result(); - return new Models.RevisionSubmodule() - { - Commit = commit, - FullMessage = new Models.CommitFullMessage { Message = body } - }; - } + if (commit == null) + return new Models.RevisionSubmodule() { Commit = new Models.Commit() { SHA = sha } }; + var body = new Commands.QueryCommitFullMessage(repo, sha).Result(); return new Models.RevisionSubmodule() { - Commit = new Models.Commit() { SHA = sha }, - FullMessage = null, + Commit = commit, + FullMessage = new Models.CommitFullMessage { Message = body } }; } private class Info { - public string Argument { get; set; } - public int UnifiedLines { get; set; } - public bool IgnoreWhitespace { get; set; } - public string OldHash { get; set; } - public string NewHash { get; set; } + public string Argument { get; } + public int UnifiedLines { get; } + public bool IgnoreWhitespace { get; } + public string OldHash { get; } + public string NewHash { get; } public Info(Models.DiffOption option, int unifiedLines, bool ignoreWhitespace, Models.DiffResult result) { @@ -285,7 +279,6 @@ namespace SourceGit.ViewModels private readonly string _repo; private readonly Models.DiffOption _option = null; - private string _title; private string _fileModeChange = string.Empty; private int _unifiedLines = 4; private bool _isTextDiff = false; From ba4c0f0cd2596946b6d7356cda83f068e5043f00 Mon Sep 17 00:00:00 2001 From: leo Date: Sat, 7 Jun 2025 20:53:34 +0800 Subject: [PATCH 307/392] enhance: scroll to home when active revision file changed Signed-off-by: leo --- src/Views/RevisionFileContentViewer.axaml.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Views/RevisionFileContentViewer.axaml.cs b/src/Views/RevisionFileContentViewer.axaml.cs index 59f2d33d..4a6f69b0 100644 --- a/src/Views/RevisionFileContentViewer.axaml.cs +++ b/src/Views/RevisionFileContentViewer.axaml.cs @@ -83,6 +83,7 @@ namespace SourceGit.Views { Text = source.Content; Models.TextMateHelper.SetGrammarByFileName(_textMate, source.FileName); + ScrollToHome(); } else { From fe54d30b7010f7e10ebcc1f7b92dc032cb4a7bbb Mon Sep 17 00:00:00 2001 From: Sina Hinderks Date: Sun, 8 Jun 2025 02:47:03 +0200 Subject: [PATCH 308/392] refactor: collecting inlines for subjects (#1402) Instead of checking intersections of inline elements yourself before adding an inline element, the new class `InlineElementCollector` prevents intersections internally. Additionally the inline elements are sorted by the new class, so it's no longer necessary to do this after adding the inline elements. --- src/Models/Commit.cs | 2 +- src/Models/InlineElementCollector.cs | 85 ++++++++++++++++++++++++++++ src/Models/IssueTrackerRule.cs | 17 +----- src/ViewModels/CommitDetail.cs | 31 +--------- src/Views/CommitMessagePresenter.cs | 37 ++++++------ src/Views/CommitSubjectPresenter.cs | 16 +----- 6 files changed, 109 insertions(+), 79 deletions(-) create mode 100644 src/Models/InlineElementCollector.cs diff --git a/src/Models/Commit.cs b/src/Models/Commit.cs index ced7597e..6bf2655a 100644 --- a/src/Models/Commit.cs +++ b/src/Models/Commit.cs @@ -121,6 +121,6 @@ namespace SourceGit.Models public class CommitFullMessage { public string Message { get; set; } = string.Empty; - public List Inlines { get; set; } = []; + public InlineElementCollector Inlines { get; set; } = []; } } diff --git a/src/Models/InlineElementCollector.cs b/src/Models/InlineElementCollector.cs new file mode 100644 index 00000000..b4b2e9e1 --- /dev/null +++ b/src/Models/InlineElementCollector.cs @@ -0,0 +1,85 @@ +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; + +namespace SourceGit.Models +{ + public class InlineElementCollector : IEnumerable + { + private readonly List _implementation = []; + + public void Clear() + { + _implementation.Clear(); + + AssertInvariant(); + } + + public int Count => _implementation.Count; + + public void Add(InlineElement element) + { + + var index = FindIndex(element.Start); + if (!IsIntersection(index, element.Start, element.Length)) + _implementation.Insert(index, element); + + AssertInvariant(); + } + + [Conditional("DEBUG")] + private void AssertInvariant() + { + if (_implementation.Count == 0) + return; + + for (var index = 1; index < _implementation.Count; index++) + { + var prev = _implementation[index - 1]; + var curr = _implementation[index]; + + Debug.Assert(prev.Start + prev.Length <= curr.Start); + } + } + + public InlineElement Lookup(int position) + { + var index = FindIndex(position); + return IsIntersection(index, position, 1) + ? _implementation[index] + : null; + } + + private int FindIndex(int start) + { + var index = 0; + while (index < _implementation.Count && _implementation[index].Start <= start) + index++; + + return index; + } + + private bool IsIntersection(int index, int start, int length) + { + if (index > 0) + { + var predecessor = _implementation[index - 1]; + if (predecessor.Start + predecessor.Length >= start) + return true; + } + + if (index < _implementation.Count) + { + var successor = _implementation[index]; + if (start + length >= successor.Start) + return true; + } + + return false; + } + + public IEnumerator GetEnumerator() => _implementation.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } +} diff --git a/src/Models/IssueTrackerRule.cs b/src/Models/IssueTrackerRule.cs index fe0fe8e0..30bce596 100644 --- a/src/Models/IssueTrackerRule.cs +++ b/src/Models/IssueTrackerRule.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using System.Text.RegularExpressions; +using System.Text.RegularExpressions; using CommunityToolkit.Mvvm.ComponentModel; @@ -46,7 +45,7 @@ namespace SourceGit.Models set => SetProperty(ref _urlTemplate, value); } - public void Matches(List outs, string message) + public void Matches(InlineElementCollector outs, string message) { if (_regex == null || string.IsNullOrEmpty(_urlTemplate)) return; @@ -60,18 +59,6 @@ namespace SourceGit.Models var start = match.Index; var len = match.Length; - var intersect = false; - foreach (var exist in outs) - { - if (exist.Intersect(start, len)) - { - intersect = true; - break; - } - } - - if (intersect) - continue; var link = _urlTemplate; for (var j = 1; j < match.Groups.Count; j++) diff --git a/src/ViewModels/CommitDetail.cs b/src/ViewModels/CommitDetail.cs index 02db9b4f..8cc18152 100644 --- a/src/ViewModels/CommitDetail.cs +++ b/src/ViewModels/CommitDetail.cs @@ -620,9 +620,9 @@ namespace SourceGit.ViewModels }); } - private List ParseInlinesInMessage(string message) + private Models.InlineElementCollector ParseInlinesInMessage(string message) { - var inlines = new List(); + var inlines = new Models.InlineElementCollector(); if (_repo.Settings.IssueTrackerRules is { Count: > 0 } rules) { foreach (var rule in rules) @@ -638,18 +638,6 @@ namespace SourceGit.ViewModels var start = match.Index; var len = match.Length; - var intersect = false; - foreach (var link in inlines) - { - if (link.Intersect(start, len)) - { - intersect = true; - break; - } - } - - if (intersect) - continue; var url = message.Substring(start, len); if (Uri.IsWellFormedUriString(url, UriKind.Absolute)) @@ -665,18 +653,6 @@ namespace SourceGit.ViewModels var start = match.Index; var len = match.Length; - var intersect = false; - foreach (var link in inlines) - { - if (link.Intersect(start, len)) - { - intersect = true; - break; - } - } - - if (intersect) - continue; var sha = match.Groups[1].Value; var isCommitSHA = new Commands.IsCommitSHA(_repo.FullPath, sha).Result(); @@ -684,9 +660,6 @@ namespace SourceGit.ViewModels inlines.Add(new Models.InlineElement(Models.InlineElementType.CommitSHA, start, len, sha)); } - if (inlines.Count > 0) - inlines.Sort((l, r) => l.Start - r.Start); - return inlines; } diff --git a/src/Views/CommitMessagePresenter.cs b/src/Views/CommitMessagePresenter.cs index 0858640b..87433c45 100644 --- a/src/Views/CommitMessagePresenter.cs +++ b/src/Views/CommitMessagePresenter.cs @@ -95,26 +95,11 @@ namespace SourceGit.Views point = new Point(x, y); var pos = TextLayout.HitTestPoint(point).TextPosition; - foreach (var link in links) - { - if (!link.Intersect(pos, 1)) - continue; - if (link == _lastHover) - return; - - SetCurrentValue(CursorProperty, Cursor.Parse("Hand")); - - _lastHover = link; - if (link.Type == Models.InlineElementType.Link) - ToolTip.SetTip(this, link.Link); - else - ProcessHoverCommitLink(link); - - return; - } - - ClearHoveredIssueLink(); + if (links.Lookup(pos) is { } link) + SetHoveredIssueLink(link); + else + ClearHoveredIssueLink(); } } @@ -291,6 +276,20 @@ namespace SourceGit.Views } } + private void SetHoveredIssueLink(Models.InlineElement link) + { + if (link == _lastHover) + return; + + SetCurrentValue(CursorProperty, Cursor.Parse("Hand")); + + _lastHover = link; + if (link.Type == Models.InlineElementType.Link) + ToolTip.SetTip(this, link.Link); + else + ProcessHoverCommitLink(link); + } + private void ClearHoveredIssueLink() { if (_lastHover != null) diff --git a/src/Views/CommitSubjectPresenter.cs b/src/Views/CommitSubjectPresenter.cs index 18902462..b860ec1d 100644 --- a/src/Views/CommitSubjectPresenter.cs +++ b/src/Views/CommitSubjectPresenter.cs @@ -167,19 +167,6 @@ namespace SourceGit.Views var start = match.Index; var len = match.Length; - var intersect = false; - foreach (var exist in _elements) - { - if (exist.Intersect(start, len)) - { - intersect = true; - break; - } - } - - if (intersect) - continue; - _elements.Add(new Models.InlineElement(Models.InlineElementType.Code, start, len, string.Empty)); } @@ -187,7 +174,6 @@ namespace SourceGit.Views foreach (var rule in rules) rule.Matches(_elements, subject); - _elements.Sort((l, r) => l.Start - r.Start); _needRebuildInlines = true; InvalidateVisual(); } @@ -364,7 +350,7 @@ namespace SourceGit.Views } } - private List _elements = []; + private Models.InlineElementCollector _elements = []; private List _inlines = []; private Models.InlineElement _lastHover = null; private bool _needRebuildInlines = false; From 84fb39f97a23eb15c42460d5df8d990674664804 Mon Sep 17 00:00:00 2001 From: leo Date: Sun, 8 Jun 2025 11:09:20 +0800 Subject: [PATCH 309/392] code_review: PR #1402 - it's unnecessary to implement `IEnumerable` interface - we should check `IsIntersecting` before creating `InlineElement` to avoid unnecessary works suck as running `git cat-file -t ` - sort whold list after all elements have been added to avoid unnecessary memmove in `Insert` Signed-off-by: leo --- src/Models/Commit.cs | 8 +-- src/Models/InlineElement.cs | 13 ++-- src/Models/InlineElementCollector.cs | 97 +++++++--------------------- src/Models/IssueTrackerRule.cs | 2 + src/ViewModels/CommitDetail.cs | 5 ++ src/Views/CommitMessagePresenter.cs | 6 +- src/Views/CommitSubjectPresenter.cs | 9 ++- 7 files changed, 52 insertions(+), 88 deletions(-) diff --git a/src/Models/Commit.cs b/src/Models/Commit.cs index 6bf2655a..865b3ac1 100644 --- a/src/Models/Commit.cs +++ b/src/Models/Commit.cs @@ -33,8 +33,8 @@ namespace SourceGit.Models public User Committer { get; set; } = User.Invalid; public ulong CommitterTime { get; set; } = 0; public string Subject { get; set; } = string.Empty; - public List Parents { get; set; } = new List(); - public List Decorators { get; set; } = new List(); + public List Parents { get; set; } = new(); + public List Decorators { get; set; } = new(); public bool HasDecorators => Decorators.Count > 0; public string AuthorTimeStr => DateTime.UnixEpoch.AddSeconds(AuthorTime).ToLocalTime().ToString(DateTimeFormat.Active.DateTime); @@ -49,7 +49,7 @@ namespace SourceGit.Models public int Color { get; set; } = 0; public double Opacity => IsMerged ? 1 : OpacityForNotMerged; public FontWeight FontWeight => IsCurrentHead ? FontWeight.Bold : FontWeight.Regular; - public Thickness Margin { get; set; } = new Thickness(0); + public Thickness Margin { get; set; } = new(0); public IBrush Brush => CommitGraph.Pens[Color].Brush; public void ParseDecorators(string data) @@ -121,6 +121,6 @@ namespace SourceGit.Models public class CommitFullMessage { public string Message { get; set; } = string.Empty; - public InlineElementCollector Inlines { get; set; } = []; + public InlineElementCollector Inlines { get; set; } = new(); } } diff --git a/src/Models/InlineElement.cs b/src/Models/InlineElement.cs index 53761403..ea7bcee8 100644 --- a/src/Models/InlineElement.cs +++ b/src/Models/InlineElement.cs @@ -2,8 +2,7 @@ { public enum InlineElementType { - None = 0, - Keyword, + Keyword = 0, Link, CommitSHA, Code, @@ -11,10 +10,10 @@ public class InlineElement { - public InlineElementType Type { get; set; } = InlineElementType.None; - public int Start { get; set; } = 0; - public int Length { get; set; } = 0; - public string Link { get; set; } = ""; + public InlineElementType Type { get; } + public int Start { get; } + public int Length { get; } + public string Link { get; } public InlineElement(InlineElementType type, int start, int length, string link) { @@ -24,7 +23,7 @@ Link = link; } - public bool Intersect(int start, int length) + public bool IsIntersecting(int start, int length) { if (start == Start) return true; diff --git a/src/Models/InlineElementCollector.cs b/src/Models/InlineElementCollector.cs index b4b2e9e1..d81aaf8d 100644 --- a/src/Models/InlineElementCollector.cs +++ b/src/Models/InlineElementCollector.cs @@ -1,85 +1,38 @@ -using System.Collections; using System.Collections.Generic; -using System.Diagnostics; namespace SourceGit.Models { - public class InlineElementCollector : IEnumerable + public class InlineElementCollector { - private readonly List _implementation = []; + public int Count => _implementation.Count; + public InlineElement this[int index] => _implementation[index]; + + public InlineElement Intersect(int start, int length) + { + foreach (var elem in _implementation) + { + if (elem.IsIntersecting(start, length)) + return elem; + } + + return null; + } + + public void Add(InlineElement element) + { + _implementation.Add(element); + } + + public void Sort() + { + _implementation.Sort((l, r) => l.Start.CompareTo(r.Start)); + } public void Clear() { _implementation.Clear(); - - AssertInvariant(); } - public int Count => _implementation.Count; - - public void Add(InlineElement element) - { - - var index = FindIndex(element.Start); - if (!IsIntersection(index, element.Start, element.Length)) - _implementation.Insert(index, element); - - AssertInvariant(); - } - - [Conditional("DEBUG")] - private void AssertInvariant() - { - if (_implementation.Count == 0) - return; - - for (var index = 1; index < _implementation.Count; index++) - { - var prev = _implementation[index - 1]; - var curr = _implementation[index]; - - Debug.Assert(prev.Start + prev.Length <= curr.Start); - } - } - - public InlineElement Lookup(int position) - { - var index = FindIndex(position); - return IsIntersection(index, position, 1) - ? _implementation[index] - : null; - } - - private int FindIndex(int start) - { - var index = 0; - while (index < _implementation.Count && _implementation[index].Start <= start) - index++; - - return index; - } - - private bool IsIntersection(int index, int start, int length) - { - if (index > 0) - { - var predecessor = _implementation[index - 1]; - if (predecessor.Start + predecessor.Length >= start) - return true; - } - - if (index < _implementation.Count) - { - var successor = _implementation[index]; - if (start + length >= successor.Start) - return true; - } - - return false; - } - - public IEnumerator GetEnumerator() => _implementation.GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + private readonly List _implementation = []; } } diff --git a/src/Models/IssueTrackerRule.cs b/src/Models/IssueTrackerRule.cs index 30bce596..40c84b9e 100644 --- a/src/Models/IssueTrackerRule.cs +++ b/src/Models/IssueTrackerRule.cs @@ -59,6 +59,8 @@ namespace SourceGit.Models var start = match.Index; var len = match.Length; + if (outs.Intersect(start, len) != null) + continue; var link = _urlTemplate; for (var j = 1; j < match.Groups.Count; j++) diff --git a/src/ViewModels/CommitDetail.cs b/src/ViewModels/CommitDetail.cs index 8cc18152..8d2ade09 100644 --- a/src/ViewModels/CommitDetail.cs +++ b/src/ViewModels/CommitDetail.cs @@ -638,6 +638,8 @@ namespace SourceGit.ViewModels var start = match.Index; var len = match.Length; + if (inlines.Intersect(start, len) != null) + continue; var url = message.Substring(start, len); if (Uri.IsWellFormedUriString(url, UriKind.Absolute)) @@ -653,6 +655,8 @@ namespace SourceGit.ViewModels var start = match.Index; var len = match.Length; + if (inlines.Intersect(start, len) != null) + continue; var sha = match.Groups[1].Value; var isCommitSHA = new Commands.IsCommitSHA(_repo.FullPath, sha).Result(); @@ -660,6 +664,7 @@ namespace SourceGit.ViewModels inlines.Add(new Models.InlineElement(Models.InlineElementType.CommitSHA, start, len, sha)); } + inlines.Sort(); return inlines; } diff --git a/src/Views/CommitMessagePresenter.cs b/src/Views/CommitMessagePresenter.cs index 87433c45..d7c7ee3f 100644 --- a/src/Views/CommitMessagePresenter.cs +++ b/src/Views/CommitMessagePresenter.cs @@ -48,8 +48,9 @@ namespace SourceGit.Views var inlines = new List(); var pos = 0; - foreach (var link in links) + for (var i = 0; i < links.Count; i++) { + var link = links[i]; if (link.Start > pos) inlines.Add(new Run(message.Substring(pos, link.Start - pos))); @@ -95,8 +96,7 @@ namespace SourceGit.Views point = new Point(x, y); var pos = TextLayout.HitTestPoint(point).TextPosition; - - if (links.Lookup(pos) is { } link) + if (links.Intersect(pos, 1) is { } link) SetHoveredIssueLink(link); else ClearHoveredIssueLink(); diff --git a/src/Views/CommitSubjectPresenter.cs b/src/Views/CommitSubjectPresenter.cs index b860ec1d..361c1184 100644 --- a/src/Views/CommitSubjectPresenter.cs +++ b/src/Views/CommitSubjectPresenter.cs @@ -167,6 +167,9 @@ namespace SourceGit.Views var start = match.Index; var len = match.Length; + if (_elements.Intersect(start, len) != null) + continue; + _elements.Add(new Models.InlineElement(Models.InlineElementType.Code, start, len, string.Empty)); } @@ -174,6 +177,7 @@ namespace SourceGit.Views foreach (var rule in rules) rule.Matches(_elements, subject); + _elements.Sort(); _needRebuildInlines = true; InvalidateVisual(); } @@ -247,8 +251,9 @@ namespace SourceGit.Views var codeTypeface = new Typeface(codeFontFamily, FontStyle.Normal, FontWeight); var pos = 0; var x = 0.0; - foreach (var elem in _elements) + for (var i = 0; i < _elements.Count; i++) { + var elem = _elements[i]; if (elem.Start > pos) { var normal = new FormattedText( @@ -350,7 +355,7 @@ namespace SourceGit.Views } } - private Models.InlineElementCollector _elements = []; + private Models.InlineElementCollector _elements = new(); private List _inlines = []; private Models.InlineElement _lastHover = null; private bool _needRebuildInlines = false; From a22c39519f1769e56be967cd4abaecf0ff570212 Mon Sep 17 00:00:00 2001 From: leo Date: Sun, 8 Jun 2025 11:54:54 +0800 Subject: [PATCH 310/392] code_style: remove unnecessary code Signed-off-by: leo --- src/ViewModels/ScanRepositories.cs | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/ViewModels/ScanRepositories.cs b/src/ViewModels/ScanRepositories.cs index f97066b1..21cd9bf8 100644 --- a/src/ViewModels/ScanRepositories.cs +++ b/src/ViewModels/ScanRepositories.cs @@ -31,7 +31,7 @@ namespace SourceGit.ViewModels watch.Start(); var rootDir = new DirectoryInfo(RootDir); - var found = new List(); + var found = new List(); GetUnmanagedRepositories(rootDir, found, new EnumerationOptions() { AttributesToSkip = FileAttributes.Hidden | FileAttributes.System, @@ -50,20 +50,21 @@ namespace SourceGit.ViewModels foreach (var f in found) { - var parent = new DirectoryInfo(f.Path).Parent!.FullName.Replace('\\', '/').TrimEnd('/'); + var parent = new DirectoryInfo(f).Parent!.FullName.Replace('\\', '/').TrimEnd('/'); if (parent.Equals(normalizedRoot, StringComparison.Ordinal)) { - Preferences.Instance.FindOrAddNodeByRepositoryPath(f.Path, null, false, false); + Preferences.Instance.FindOrAddNodeByRepositoryPath(f, null, false, false); } else if (parent.StartsWith(normalizedRoot, StringComparison.Ordinal)) { var relative = parent.Substring(normalizedRoot.Length).TrimStart('/'); var group = FindOrCreateGroupRecursive(Preferences.Instance.RepositoryNodes, relative); - Preferences.Instance.FindOrAddNodeByRepositoryPath(f.Path, group, false, false); + Preferences.Instance.FindOrAddNodeByRepositoryPath(f, group, false, false); } } Preferences.Instance.AutoRemoveInvalidNode(); + Preferences.Instance.SortNodes(Preferences.Instance.RepositoryNodes); Preferences.Instance.Save(); Welcome.Instance.Refresh(); @@ -84,7 +85,7 @@ namespace SourceGit.ViewModels } } - private void GetUnmanagedRepositories(DirectoryInfo dir, List outs, EnumerationOptions opts, int depth = 0) + private void GetUnmanagedRepositories(DirectoryInfo dir, List outs, EnumerationOptions opts, int depth = 0) { var subdirs = dir.GetDirectories("*", opts); foreach (var subdir in subdirs) @@ -107,7 +108,7 @@ namespace SourceGit.ViewModels { var normalized = test.StdOut.Trim().Replace('\\', '/').TrimEnd('/'); if (!_managed.Contains(normalized)) - outs.Add(new FoundRepository(normalized, false)); + outs.Add(normalized); } continue; @@ -116,7 +117,7 @@ namespace SourceGit.ViewModels var isBare = new Commands.IsBareRepository(subdir.FullName).Result(); if (isBare) { - outs.Add(new FoundRepository(normalizedSelf, true)); + outs.Add(normalizedSelf); continue; } @@ -158,12 +159,6 @@ namespace SourceGit.ViewModels return added; } - private record FoundRepository(string path, bool isBare) - { - public string Path { get; set; } = path; - public bool IsBare { get; set; } = isBare; - } - - private HashSet _managed = new HashSet(); + private HashSet _managed = new(); } } From d55f19586f6a9578c9ff26d5a7926a9119fc3025 Mon Sep 17 00:00:00 2001 From: Sina Hinderks Date: Mon, 9 Jun 2025 03:26:27 +0200 Subject: [PATCH 311/392] fix: issue tracker rule over keyword in subject (#1403) Some teams use issue tracker numbers in front of the commit message subject, followed by a colon. It was not possible to use an issue tracker rule in such cases, since the issue tracker number would be interpreted as a keyword due to the colon and therefore displayed in bold face instead of as a link into the issue tracker. --- src/Views/CommitSubjectPresenter.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Views/CommitSubjectPresenter.cs b/src/Views/CommitSubjectPresenter.cs index 361c1184..bfeab34f 100644 --- a/src/Views/CommitSubjectPresenter.cs +++ b/src/Views/CommitSubjectPresenter.cs @@ -151,11 +151,15 @@ namespace SourceGit.Views return; } + var rules = IssueTrackerRules ?? []; + foreach (var rule in rules) + rule.Matches(_elements, subject); + var keywordMatch = REG_KEYWORD_FORMAT1().Match(subject); if (!keywordMatch.Success) keywordMatch = REG_KEYWORD_FORMAT2().Match(subject); - if (keywordMatch.Success) + if (keywordMatch.Success && _elements.Intersect(0, keywordMatch.Length) == null) _elements.Add(new Models.InlineElement(Models.InlineElementType.Keyword, 0, keywordMatch.Length, string.Empty)); var codeMatches = REG_INLINECODE_FORMAT().Matches(subject); @@ -173,10 +177,6 @@ namespace SourceGit.Views _elements.Add(new Models.InlineElement(Models.InlineElementType.Code, start, len, string.Empty)); } - var rules = IssueTrackerRules ?? []; - foreach (var rule in rules) - rule.Matches(_elements, subject); - _elements.Sort(); _needRebuildInlines = true; InvalidateVisual(); From a8541a780e235c60fde494c8a5a7593566c257d5 Mon Sep 17 00:00:00 2001 From: AquariusStar <48148723+AquariusStar@users.noreply.github.com> Date: Mon, 9 Jun 2025 04:27:10 +0300 Subject: [PATCH 312/392] localization: update translate Russian (#1404) --- src/Resources/Locales/ru_RU.axaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index 80766130..a625df98 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -39,6 +39,7 @@ НЕОТСЛЕЖИВАЕМЫЕ ФАЙЛЫ СПИСОК ПУСТ УДАЛИТЬ + Загрузить картинку... Обновить ДВОИЧНЫЙ ФАЙЛ НЕ ПОДДЕРЖИВАЕТСЯ!!! Раздвоить @@ -51,6 +52,7 @@ Расследование РАССЛЕДОВАНИЕ В ЭТОМ ФАЙЛЕ НЕ ПОДДЕРЖИВАЕТСЯ!!! Переключиться на ${0}$... + Сравнить с ${0}$ Сравнить с рабочим каталогом Копировать имя ветки Изменить действие @@ -592,6 +594,7 @@ Очистить (Сбор мусора и удаление) Запустить команду (git gc) для данного репозитория. Очистить всё + Очистить Настройка репозитория ПРОДОЛЖИТЬ Изменить действия @@ -783,6 +786,7 @@ ВКЛЮЧИТЬ НЕОТСЛЕЖИВАЕМЫЕ ФАЙЛЫ НЕТ ПОСЛЕДНИХ ВХОДНЫХ СООБЩЕНИЙ НЕТ ШАБЛОНОВ РЕВИЗИИ + Сбросить автора Щёлкните правой кнопкой мыши выбранный файл(ы) и разрешите конфликты. Завершение работы СФОРМИРОВАННЫЕ From a1e76e9bea938e4aea9a68675a55f5f68dc5faea Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 9 Jun 2025 01:27:28 +0000 Subject: [PATCH 313/392] doc: Update translation status and sort locale files --- TRANSLATION.md | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index 78d1e843..ba51b82c 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -357,17 +357,7 @@ This document shows the translation status of each locale file in the repository -### ![ru__RU](https://img.shields.io/badge/ru__RU-99.50%25-yellow) - -
-Missing keys in ru_RU.axaml - -- Text.Avatar.Load -- Text.BranchCM.CompareWithCurrent -- Text.Repository.ClearStashes -- Text.WorkingCopy.ResetAuthor - -
+### ![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen) ### ![ta__IN](https://img.shields.io/badge/ta__IN-92.13%25-yellow) From 637e133f478d1c4f30b033cca50ec1ffb971bb32 Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 9 Jun 2025 09:30:36 +0800 Subject: [PATCH 314/392] version: Release 2025.21 Signed-off-by: leo --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 67a3d7e0..b89504d0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2025.20 \ No newline at end of file +2025.21 \ No newline at end of file From 1b1dc2f6661cd890df4041463b60a2a9ba8727b4 Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 9 Jun 2025 15:42:02 +0800 Subject: [PATCH 315/392] code_style: remove unnecessary code Signed-off-by: leo --- src/Views/LauncherTabBar.axaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Views/LauncherTabBar.axaml b/src/Views/LauncherTabBar.axaml index f078a598..ef7cbb93 100644 --- a/src/Views/LauncherTabBar.axaml +++ b/src/Views/LauncherTabBar.axaml @@ -87,7 +87,6 @@ FontSize="{Binding Source={x:Static vm:Preferences.Instance}, Path=DefaultFontSize, Converter={x:Static c:DoubleConverters.Decrease}}" TextAlignment="Center" Text="{Binding Node.Name}" - IsVisible="{Binding Node.IsRepository}" IsHitTestVisible="False"/>
@@ -99,6 +98,7 @@ Text="{DynamicResource Text.PageTabBar.Welcome.Title}" IsVisible="{Binding !Node.IsRepository}" IsHitTestVisible="False"/> + + + +
diff --git a/src/Views/Blame.axaml.cs b/src/Views/Blame.axaml.cs index 00342b0b..47f3809d 100644 --- a/src/Views/Blame.axaml.cs +++ b/src/Views/Blame.axaml.cs @@ -225,7 +225,7 @@ namespace SourceGit.Views { if (DataContext is ViewModels.Blame blame) { - blame.NavigateToCommit(info.CommitSHA); + blame.NavigateToCommit(info.CommitSHA, true); } e.Handled = true; @@ -433,6 +433,8 @@ namespace SourceGit.Views public Blame() { InitializeComponent(); + + AddHandler(PointerReleasedEvent, MouseUpHandler, handledEventsToo: true); } protected override void OnClosed(EventArgs e) @@ -440,5 +442,32 @@ namespace SourceGit.Views base.OnClosed(e); GC.Collect(); } + + private void HistoryBack(object _, RoutedEventArgs e) + { + if (DataContext is ViewModels.Blame blame) + { + blame.Back(); + } + } + private void HistoryForward(object _, RoutedEventArgs e) + { + if (DataContext is ViewModels.Blame blame) + { + blame.Forward(); + } + } + + private void MouseUpHandler(object sender, PointerReleasedEventArgs e) + { + if (e.InitialPressMouseButton == MouseButton.XButton1) + { + HistoryBack(null, null); + } + else if (e.InitialPressMouseButton == MouseButton.XButton2) + { + HistoryForward(null, null); + } + } } } From ee4d8a6a0e50a63bc3700541023db442830c0b89 Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 10 Jun 2025 11:18:20 +0800 Subject: [PATCH 319/392] code_review: PR #1408 Signed-off-by: leo --- src/ViewModels/Blame.cs | 213 ++++++++++++++++++++------------- src/ViewModels/CommitDetail.cs | 4 +- src/Views/Blame.axaml | 55 ++++++--- src/Views/Blame.axaml.cs | 40 +++---- 4 files changed, 182 insertions(+), 130 deletions(-) diff --git a/src/ViewModels/Blame.cs b/src/ViewModels/Blame.cs index 4b57575b..e9917aaf 100644 --- a/src/ViewModels/Blame.cs +++ b/src/ViewModels/Blame.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using Avalonia.Threading; @@ -10,10 +11,25 @@ namespace SourceGit.ViewModels { public class Blame : ObservableObject { - public string Title + public string FilePath { - get => _title; - private set => SetProperty(ref _title, value); + get; + } + + public Models.Commit Revision + { + get => _revision; + private set => SetProperty(ref _revision, value); + } + + public Models.BlameData Data + { + get => _data; + private set + { + if (SetProperty(ref _data, value)) + OnPropertyChanged(nameof(IsBinary)); + } } public bool IsBinary @@ -21,92 +37,27 @@ namespace SourceGit.ViewModels get => _data != null && _data.IsBinary; } - public bool CanMoveBack + public bool CanBack { - get => _shaHistoryIndex > 0 && _shaHistory.Count > 1; - } - public bool CanMoveForward - { - get => _shaHistoryIndex < _shaHistory.Count - 1; + get => _navigationActiveIndex > 0; } - public Models.BlameData Data + public bool CanForward { - get => _data; - private set => SetProperty(ref _data, value); + get => _navigationActiveIndex < _navigationHistory.Count - 1; } - public Blame(string repo, string file, string revision) + public Blame(string repo, string file, Models.Commit commit) { + var sha = commit.SHA.Substring(0, 10); + + FilePath = file; + Revision = commit; + _repo = repo; - _file = file; - - SetBlameData($"{revision.AsSpan(0, 10)}", true); - } - - private void SetBlameData(string commitSHA, bool resetHistoryForward) - { - Title = $"{_file} @ {commitSHA}"; - - Task.Run(() => - { - var result = new Commands.Blame(_repo, _file, commitSHA).Result(); - Dispatcher.UIThread.Invoke(() => - { - Data = result; - OnPropertyChanged(nameof(IsBinary)); - }); - }); - - if (resetHistoryForward) - { - if (_shaHistoryIndex < _shaHistory.Count - 1) - _shaHistory.RemoveRange(_shaHistoryIndex + 1, _shaHistory.Count - _shaHistoryIndex - 1); - - if (_shaHistory.Count == 0 || _shaHistory[_shaHistoryIndex] != commitSHA) - { - _shaHistory.Add(commitSHA); - _shaHistoryIndex = _shaHistory.Count - 1; - } - } - - OnPropertyChanged(nameof(CanMoveBack)); - OnPropertyChanged(nameof(CanMoveForward)); - } - - public void Back() - { - --_shaHistoryIndex; - if (_shaHistoryIndex < 0) - _shaHistoryIndex = 0; - - NavigateToCommit(_shaHistory[_shaHistoryIndex], false); - } - - public void Forward() - { - ++_shaHistoryIndex; - if (_shaHistoryIndex >= _shaHistory.Count) - _shaHistoryIndex = _shaHistory.Count - 1; - - NavigateToCommit(_shaHistory[_shaHistoryIndex], false); - } - - public void NavigateToCommit(string commitSHA, bool resetHistoryForward) - { - var launcher = App.GetLauncher(); - if (launcher == null) - return; - - foreach (var page in launcher.Pages) - { - if (page.Data is Repository repo && repo.FullPath.Equals(_repo)) - { - repo.NavigateToCommit(commitSHA); - SetBlameData(commitSHA, resetHistoryForward); - break; - } - } + _navigationHistory.Add(sha); + _commits.Add(sha, commit); + SetBlameData(sha); } public string GetCommitMessage(string sha) @@ -119,12 +70,102 @@ namespace SourceGit.ViewModels return msg; } + public void Back() + { + if (_navigationActiveIndex <= 0) + return; + + _navigationActiveIndex--; + OnPropertyChanged(nameof(CanBack)); + OnPropertyChanged(nameof(CanForward)); + NavigateToCommit(_navigationHistory[_navigationActiveIndex]); + } + + public void Forward() + { + if (_navigationActiveIndex >= _navigationHistory.Count - 1) + return; + + _navigationActiveIndex++; + OnPropertyChanged(nameof(CanBack)); + OnPropertyChanged(nameof(CanForward)); + NavigateToCommit(_navigationHistory[_navigationActiveIndex]); + } + + public void NavigateToCommit(string commitSHA) + { + if (!_navigationHistory[_navigationActiveIndex].Equals(commitSHA, StringComparison.Ordinal)) + { + _navigationHistory.Add(commitSHA); + _navigationActiveIndex = _navigationHistory.Count - 1; + OnPropertyChanged(nameof(CanBack)); + OnPropertyChanged(nameof(CanForward)); + } + + if (!Revision.SHA.StartsWith(commitSHA, StringComparison.Ordinal)) + SetBlameData(commitSHA); + + if (App.GetLauncher() is { Pages: { } pages }) + { + foreach (var page in pages) + { + if (page.Data is Repository repo && repo.FullPath.Equals(_repo)) + { + repo.NavigateToCommit(commitSHA); + break; + } + } + } + } + + private void SetBlameData(string commitSHA) + { + if (_cancellationSource is { IsCancellationRequested: false }) + _cancellationSource.Cancel(); + + _cancellationSource = new CancellationTokenSource(); + var token = _cancellationSource.Token; + + if (_commits.TryGetValue(commitSHA, out var c)) + { + Revision = c; + } + else + { + Task.Run(() => + { + var result = new Commands.QuerySingleCommit(_repo, commitSHA).Result(); + + Dispatcher.UIThread.Invoke(() => + { + if (!token.IsCancellationRequested) + { + _commits.Add(commitSHA, result); + Revision = result ?? new Models.Commit() { SHA = commitSHA }; + } + }); + }, token); + } + + Task.Run(() => + { + var result = new Commands.Blame(_repo, FilePath, commitSHA).Result(); + + Dispatcher.UIThread.Invoke(() => + { + if (!token.IsCancellationRequested) + Data = result; + }); + }, token); + } + private string _repo; - private string _file; - private string _title; - private int _shaHistoryIndex = 0; - private List _shaHistory = []; + private Models.Commit _revision; + private CancellationTokenSource _cancellationSource = null; + private int _navigationActiveIndex = 0; + private List _navigationHistory = []; private Models.BlameData _data = null; - private Dictionary _commitMessages = new Dictionary(); + private Dictionary _commits = new(); + private Dictionary _commitMessages = new(); } } diff --git a/src/ViewModels/CommitDetail.cs b/src/ViewModels/CommitDetail.cs index 8d2ade09..3ec168db 100644 --- a/src/ViewModels/CommitDetail.cs +++ b/src/ViewModels/CommitDetail.cs @@ -313,7 +313,7 @@ namespace SourceGit.ViewModels blame.IsEnabled = change.Index != Models.ChangeState.Deleted; blame.Click += (_, ev) => { - App.ShowWindow(new Blame(_repo.FullPath, change.Path, _commit.SHA), false); + App.ShowWindow(new Blame(_repo.FullPath, change.Path, _commit), false); ev.Handled = true; }; @@ -481,7 +481,7 @@ namespace SourceGit.ViewModels blame.IsEnabled = file.Type == Models.ObjectType.Blob; blame.Click += (_, ev) => { - App.ShowWindow(new Blame(_repo.FullPath, file.Path, _commit.SHA), false); + App.ShowWindow(new Blame(_repo.FullPath, file.Path, _commit), false); ev.Handled = true; }; diff --git a/src/Views/Blame.axaml b/src/Views/Blame.axaml index e89bb2ec..6f299f8e 100644 --- a/src/Views/Blame.axaml +++ b/src/Views/Blame.axaml @@ -4,6 +4,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:vm="using:SourceGit.ViewModels" xmlns:v="using:SourceGit.Views" + xmlns:c="using:SourceGit.Converters" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="SourceGit.Views.Blame" x:DataType="vm:Blame" @@ -43,22 +44,44 @@ - - - - - - + + + + + + + + + + + + + + + + diff --git a/src/Views/Blame.axaml.cs b/src/Views/Blame.axaml.cs index 47f3809d..72dd605d 100644 --- a/src/Views/Blame.axaml.cs +++ b/src/Views/Blame.axaml.cs @@ -224,9 +224,7 @@ namespace SourceGit.Views if (rect.Contains(pos)) { if (DataContext is ViewModels.Blame blame) - { - blame.NavigateToCommit(info.CommitSHA, true); - } + blame.NavigateToCommit(info.CommitSHA); e.Handled = true; break; @@ -433,8 +431,6 @@ namespace SourceGit.Views public Blame() { InitializeComponent(); - - AddHandler(PointerReleasedEvent, MouseUpHandler, handledEventsToo: true); } protected override void OnClosed(EventArgs e) @@ -443,30 +439,22 @@ namespace SourceGit.Views GC.Collect(); } - private void HistoryBack(object _, RoutedEventArgs e) + protected override void OnPointerReleased(PointerReleasedEventArgs e) { - if (DataContext is ViewModels.Blame blame) - { - blame.Back(); - } - } - private void HistoryForward(object _, RoutedEventArgs e) - { - if (DataContext is ViewModels.Blame blame) - { - blame.Forward(); - } - } + base.OnPointerReleased(e); - private void MouseUpHandler(object sender, PointerReleasedEventArgs e) - { - if (e.InitialPressMouseButton == MouseButton.XButton1) + if (!e.Handled && DataContext is ViewModels.Blame blame) { - HistoryBack(null, null); - } - else if (e.InitialPressMouseButton == MouseButton.XButton2) - { - HistoryForward(null, null); + if (e.InitialPressMouseButton == MouseButton.XButton1) + { + blame.Back(); + e.Handled = true; + } + else if (e.InitialPressMouseButton == MouseButton.XButton2) + { + blame.Forward(); + e.Handled = true; + } } } } From 6c04f5390a28fab8f2327f13ba24d6edd4d78ab0 Mon Sep 17 00:00:00 2001 From: Nathan Baulch Date: Tue, 10 Jun 2025 17:58:57 +1000 Subject: [PATCH 320/392] feature: double tap commit with tracked remote branch checks out local tracking branch (#1409) --- src/ViewModels/Histories.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/ViewModels/Histories.cs b/src/ViewModels/Histories.cs index 6b2208f9..044d436e 100644 --- a/src/ViewModels/Histories.cs +++ b/src/ViewModels/Histories.cs @@ -231,9 +231,24 @@ namespace SourceGit.ViewModels return; } } - else if (d.Type == Models.DecoratorType.RemoteBranchHead && firstRemoteBranch == null) + else if (d.Type == Models.DecoratorType.RemoteBranchHead) { - firstRemoteBranch = _repo.Branches.Find(x => x.FriendlyName == d.Name); + var remoteBranch = _repo.Branches.Find(x => x.FriendlyName == d.Name); + if (remoteBranch != null) + { + var localBranch = _repo.Branches.Find(x => x.IsLocal && x.Upstream == remoteBranch.FullName); + if (localBranch != null) + { + if (!localBranch.IsCurrent) + _repo.CheckoutBranch(localBranch); + return; + } + } + + if (firstRemoteBranch == null) + { + firstRemoteBranch = remoteBranch; + } } } From 7d0536d94b83c64a3d9ff16a5cbe689ee49e36dd Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 10 Jun 2025 16:56:22 +0800 Subject: [PATCH 321/392] feature: when trying to checkout a local branch from its tracking upstream and it is behind the upstream, show `Checkout & Fast-Forward` popup Signed-off-by: leo --- src/Resources/Locales/en_US.axaml | 2 + src/Resources/Locales/zh_CN.axaml | 2 + src/Resources/Locales/zh_TW.axaml | 2 + src/ViewModels/CheckoutAndFastForward.cs | 111 ++++++++++++++++++++++ src/ViewModels/Histories.cs | 8 +- src/ViewModels/Repository.cs | 10 +- src/Views/CheckoutAndFastForward.axaml | 62 ++++++++++++ src/Views/CheckoutAndFastForward.axaml.cs | 12 +++ 8 files changed, 203 insertions(+), 6 deletions(-) create mode 100644 src/ViewModels/CheckoutAndFastForward.cs create mode 100644 src/Views/CheckoutAndFastForward.axaml create mode 100644 src/Views/CheckoutAndFastForward.axaml.cs diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index afdff5da..ee035551 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -86,6 +86,8 @@ Stash & Reapply Update all submodules Branch: + Checkout & Fast-Forward + Fast-Forward to: Cherry Pick Append source to commit message Commit(s): diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index c7e57c97..2a117fb8 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -90,6 +90,8 @@ 贮藏并自动恢复 同时更新所有子模块 目标分支 : + 检出分支并快进 + 上游分支 : 挑选提交 提交信息中追加来源信息 提交列表 : diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 18a3b04e..29a5346d 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -90,6 +90,8 @@ 擱置變更並自動復原 同時更新所有子模組 目標分支: + 簽出分支並快轉 + 上游分支 : 揀選提交 提交資訊中追加來源資訊 提交列表: diff --git a/src/ViewModels/CheckoutAndFastForward.cs b/src/ViewModels/CheckoutAndFastForward.cs new file mode 100644 index 00000000..8e62a452 --- /dev/null +++ b/src/ViewModels/CheckoutAndFastForward.cs @@ -0,0 +1,111 @@ +using System.Threading.Tasks; + +namespace SourceGit.ViewModels +{ + public class CheckoutAndFastForward : Popup + { + public Models.Branch LocalBranch + { + get; + } + + public Models.Branch RemoteBrach + { + get; + } + + public bool DiscardLocalChanges + { + get; + set; + } + + public bool IsRecurseSubmoduleVisible + { + get => _repo.Submodules.Count > 0; + } + + public bool RecurseSubmodules + { + get => _repo.Settings.UpdateSubmodulesOnCheckoutBranch; + set => _repo.Settings.UpdateSubmodulesOnCheckoutBranch = value; + } + + public CheckoutAndFastForward(Repository repo, Models.Branch localBranch, Models.Branch remoteBranch) + { + _repo = repo; + LocalBranch = localBranch; + RemoteBrach = remoteBranch; + } + + public override Task Sure() + { + _repo.SetWatcherEnabled(false); + ProgressDescription = $"Checkout and Fast-Forward '{LocalBranch.Name}' ..."; + + var log = _repo.CreateLog($"Checkout and Fast-Forward '{LocalBranch.Name}' ..."); + Use(log); + + var updateSubmodules = IsRecurseSubmoduleVisible && RecurseSubmodules; + return Task.Run(() => + { + var succ = false; + var needPopStash = false; + + if (DiscardLocalChanges) + { + succ = new Commands.Checkout(_repo.FullPath).Use(log).Branch(LocalBranch.Name, RemoteBrach.Head, true, true); + } + else + { + var changes = new Commands.CountLocalChangesWithoutUntracked(_repo.FullPath).Result(); + if (changes > 0) + { + succ = new Commands.Stash(_repo.FullPath).Use(log).Push("CHECKOUT_AND_FASTFORWARD_AUTO_STASH"); + if (!succ) + { + log.Complete(); + CallUIThread(() => _repo.SetWatcherEnabled(true)); + return false; + } + + needPopStash = true; + } + + succ = new Commands.Checkout(_repo.FullPath).Use(log).Branch(LocalBranch.Name, RemoteBrach.Head, false, true); + } + + if (succ) + { + if (updateSubmodules) + { + var submodules = new Commands.QueryUpdatableSubmodules(_repo.FullPath).Result(); + if (submodules.Count > 0) + new Commands.Submodule(_repo.FullPath).Use(log).Update(submodules, true, true); + } + + if (needPopStash) + new Commands.Stash(_repo.FullPath).Use(log).Pop("stash@{0}"); + } + + log.Complete(); + + CallUIThread(() => + { + ProgressDescription = "Waiting for branch updated..."; + + if (_repo.HistoriesFilterMode == Models.FilterMode.Included) + _repo.SetBranchFilterMode(LocalBranch, Models.FilterMode.Included, true, false); + + _repo.MarkBranchesDirtyManually(); + _repo.SetWatcherEnabled(true); + }); + + Task.Delay(400).Wait(); + return succ; + }); + } + + private Repository _repo; + } +} diff --git a/src/ViewModels/Histories.cs b/src/ViewModels/Histories.cs index 044d436e..43f060d7 100644 --- a/src/ViewModels/Histories.cs +++ b/src/ViewModels/Histories.cs @@ -236,11 +236,13 @@ namespace SourceGit.ViewModels var remoteBranch = _repo.Branches.Find(x => x.FriendlyName == d.Name); if (remoteBranch != null) { + // If there's a local branch that is tracking on this remote branch and it does not ahead of + // its upstream, show `Create and Fast-Forward` popup. var localBranch = _repo.Branches.Find(x => x.IsLocal && x.Upstream == remoteBranch.FullName); - if (localBranch != null) + if (localBranch is { TrackStatus: { Ahead: { Count: 0 } } }) { - if (!localBranch.IsCurrent) - _repo.CheckoutBranch(localBranch); + if (_repo.CanCreatePopup()) + _repo.ShowPopup(new CheckoutAndFastForward(_repo, localBranch, remoteBranch)); return; } } diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index 43a67ff4..af72e72a 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -1316,7 +1316,7 @@ namespace SourceGit.ViewModels { if (branch.IsLocal) { - var worktree = _worktrees.Find(x => x.Branch == branch.FullName); + var worktree = _worktrees.Find(x => x.Branch.Equals(branch.FullName, StringComparison.Ordinal)); if (worktree != null) { OpenWorktree(worktree); @@ -1341,9 +1341,13 @@ namespace SourceGit.ViewModels { foreach (var b in _branches) { - if (b.IsLocal && b.Upstream == branch.FullName) + if (b.IsLocal && + b.Upstream.Equals(branch.FullName, StringComparison.Ordinal) && + b.TrackStatus.Ahead.Count == 0) { - if (!b.IsCurrent) + if (b.TrackStatus.Behind.Count > 0) + ShowPopup(new CheckoutAndFastForward(this, b, branch)); + else if (!b.IsCurrent) CheckoutBranch(b); return; diff --git a/src/Views/CheckoutAndFastForward.axaml b/src/Views/CheckoutAndFastForward.axaml new file mode 100644 index 00000000..725c081e --- /dev/null +++ b/src/Views/CheckoutAndFastForward.axaml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/CheckoutAndFastForward.axaml.cs b/src/Views/CheckoutAndFastForward.axaml.cs new file mode 100644 index 00000000..c54f5a1f --- /dev/null +++ b/src/Views/CheckoutAndFastForward.axaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace SourceGit.Views +{ + public partial class CheckoutAndFastForward : UserControl + { + public CheckoutAndFastForward() + { + InitializeComponent(); + } + } +} From 5e303d43d43a2b5cfcf680bb7b72387e5e105ab4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 10 Jun 2025 09:04:35 +0000 Subject: [PATCH 322/392] doc: Update translation status and sort locale files --- TRANSLATION.md | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index ba51b82c..40f81e80 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -6,13 +6,15 @@ This document shows the translation status of each locale file in the repository ### ![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen) -### ![de__DE](https://img.shields.io/badge/de__DE-96.38%25-yellow) +### ![de__DE](https://img.shields.io/badge/de__DE-96.14%25-yellow)
Missing keys in de_DE.axaml - Text.Avatar.Load - Text.BranchCM.ResetToSelectedCommit +- Text.Checkout.WithFastForward +- Text.Checkout.WithFastForward.Upstream - Text.CommitDetail.Changes.Count - Text.CreateBranch.OverwriteExisting - Text.DeinitSubmodule @@ -43,16 +45,18 @@ This document shows the translation status of each locale file in the repository
-### ![es__ES](https://img.shields.io/badge/es__ES-99.88%25-yellow) +### ![es__ES](https://img.shields.io/badge/es__ES-99.63%25-yellow)
Missing keys in es_ES.axaml - Text.Avatar.Load +- Text.Checkout.WithFastForward +- Text.Checkout.WithFastForward.Upstream
-### ![fr__FR](https://img.shields.io/badge/fr__FR-92.26%25-yellow) +### ![fr__FR](https://img.shields.io/badge/fr__FR-92.03%25-yellow)
Missing keys in fr_FR.axaml @@ -67,6 +71,8 @@ This document shows the translation status of each locale file in the repository - Text.Bisect.WaitingForRange - Text.BranchCM.ResetToSelectedCommit - Text.Checkout.RecurseSubmodules +- Text.Checkout.WithFastForward +- Text.Checkout.WithFastForward.Upstream - Text.CommitCM.CopyAuthor - Text.CommitCM.CopyCommitter - Text.CommitCM.CopySubject @@ -122,13 +128,15 @@ This document shows the translation status of each locale file in the repository
-### ![it__IT](https://img.shields.io/badge/it__IT-97.63%25-yellow) +### ![it__IT](https://img.shields.io/badge/it__IT-97.38%25-yellow)
Missing keys in it_IT.axaml - Text.Avatar.Load - Text.BranchCM.ResetToSelectedCommit +- Text.Checkout.WithFastForward +- Text.Checkout.WithFastForward.Upstream - Text.CommitDetail.Changes.Count - Text.CreateBranch.OverwriteExisting - Text.DeinitSubmodule @@ -149,7 +157,7 @@ This document shows the translation status of each locale file in the repository
-### ![ja__JP](https://img.shields.io/badge/ja__JP-92.01%25-yellow) +### ![ja__JP](https://img.shields.io/badge/ja__JP-91.78%25-yellow)
Missing keys in ja_JP.axaml @@ -165,6 +173,8 @@ This document shows the translation status of each locale file in the repository - Text.BranchCM.CompareWithCurrent - Text.BranchCM.ResetToSelectedCommit - Text.Checkout.RecurseSubmodules +- Text.Checkout.WithFastForward +- Text.Checkout.WithFastForward.Upstream - Text.CommitCM.CopyAuthor - Text.CommitCM.CopyCommitter - Text.CommitCM.CopySubject @@ -221,7 +231,7 @@ This document shows the translation status of each locale file in the repository
-### ![pt__BR](https://img.shields.io/badge/pt__BR-84.02%25-yellow) +### ![pt__BR](https://img.shields.io/badge/pt__BR-83.81%25-yellow)
Missing keys in pt_BR.axaml @@ -245,6 +255,8 @@ This document shows the translation status of each locale file in the repository - Text.BranchCM.ResetToSelectedCommit - Text.BranchUpstreamInvalid - Text.Checkout.RecurseSubmodules +- Text.Checkout.WithFastForward +- Text.Checkout.WithFastForward.Upstream - Text.Clone.RecurseSubmodules - Text.CommitCM.CopyAuthor - Text.CommitCM.CopyCommitter @@ -357,9 +369,17 @@ This document shows the translation status of each locale file in the repository
-### ![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen) +### ![ru__RU](https://img.shields.io/badge/ru__RU-99.75%25-yellow) -### ![ta__IN](https://img.shields.io/badge/ta__IN-92.13%25-yellow) +
+Missing keys in ru_RU.axaml + +- Text.Checkout.WithFastForward +- Text.Checkout.WithFastForward.Upstream + +
+ +### ![ta__IN](https://img.shields.io/badge/ta__IN-91.91%25-yellow)
Missing keys in ta_IN.axaml @@ -375,6 +395,8 @@ This document shows the translation status of each locale file in the repository - Text.BranchCM.CompareWithCurrent - Text.BranchCM.ResetToSelectedCommit - Text.Checkout.RecurseSubmodules +- Text.Checkout.WithFastForward +- Text.Checkout.WithFastForward.Upstream - Text.CommitCM.CopyAuthor - Text.CommitCM.CopyCommitter - Text.CommitCM.CopySubject @@ -430,7 +452,7 @@ This document shows the translation status of each locale file in the repository
-### ![uk__UA](https://img.shields.io/badge/uk__UA-93.38%25-yellow) +### ![uk__UA](https://img.shields.io/badge/uk__UA-93.15%25-yellow)
Missing keys in uk_UA.axaml @@ -445,6 +467,8 @@ This document shows the translation status of each locale file in the repository - Text.Bisect.WaitingForRange - Text.BranchCM.ResetToSelectedCommit - Text.Checkout.RecurseSubmodules +- Text.Checkout.WithFastForward +- Text.Checkout.WithFastForward.Upstream - Text.CommitCM.CopyAuthor - Text.CommitCM.CopyCommitter - Text.CommitCM.CopySubject From aa1c8b1cc1fe5a5ed7d4db9925e39406de51630b Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 10 Jun 2025 17:09:57 +0800 Subject: [PATCH 323/392] code_style: use combined expr Signed-off-by: leo --- src/ViewModels/Histories.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ViewModels/Histories.cs b/src/ViewModels/Histories.cs index 43f060d7..448e3f10 100644 --- a/src/ViewModels/Histories.cs +++ b/src/ViewModels/Histories.cs @@ -237,9 +237,9 @@ namespace SourceGit.ViewModels if (remoteBranch != null) { // If there's a local branch that is tracking on this remote branch and it does not ahead of - // its upstream, show `Create and Fast-Forward` popup. + // its upstream, show `Checkout and Fast-Forward` popup. var localBranch = _repo.Branches.Find(x => x.IsLocal && x.Upstream == remoteBranch.FullName); - if (localBranch is { TrackStatus: { Ahead: { Count: 0 } } }) + if (localBranch is { TrackStatus.Ahead.Count: 0 }) { if (_repo.CanCreatePopup()) _repo.ShowPopup(new CheckoutAndFastForward(_repo, localBranch, remoteBranch)); From bcefb773c1d53e981bb934e63c6e2ab71b6c2d6c Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 10 Jun 2025 17:19:37 +0800 Subject: [PATCH 324/392] code_style: remove comment Signed-off-by: leo --- src/ViewModels/Histories.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/ViewModels/Histories.cs b/src/ViewModels/Histories.cs index 448e3f10..a1f71409 100644 --- a/src/ViewModels/Histories.cs +++ b/src/ViewModels/Histories.cs @@ -236,8 +236,6 @@ namespace SourceGit.ViewModels var remoteBranch = _repo.Branches.Find(x => x.FriendlyName == d.Name); if (remoteBranch != null) { - // If there's a local branch that is tracking on this remote branch and it does not ahead of - // its upstream, show `Checkout and Fast-Forward` popup. var localBranch = _repo.Branches.Find(x => x.IsLocal && x.Upstream == remoteBranch.FullName); if (localBranch is { TrackStatus.Ahead.Count: 0 }) { @@ -248,9 +246,7 @@ namespace SourceGit.ViewModels } if (firstRemoteBranch == null) - { firstRemoteBranch = remoteBranch; - } } } From 7c1a8945254cffe6f6b29a95a4cf3a0a95c993ce Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 11 Jun 2025 10:12:03 +0800 Subject: [PATCH 325/392] refactor: do not change original image aspect ratio and do not upscale image in `SWIPE` mode Signed-off-by: leo --- src/Views/ImageContainer.cs | 38 +++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/Views/ImageContainer.cs b/src/Views/ImageContainer.cs index 2c37d12a..45acfcd0 100644 --- a/src/Views/ImageContainer.cs +++ b/src/Views/ImageContainer.cs @@ -137,21 +137,12 @@ namespace SourceGit.Views var w = Bounds.Width; var h = Bounds.Height; var x = w * alpha; - var left = OldImage; - if (left != null && alpha > 0) - { - var src = new Rect(0, 0, left.Size.Width * alpha, left.Size.Height); - var dst = new Rect(0, 0, x, h); - context.DrawImage(left, src, dst); - } - var right = NewImage; - if (right != null && alpha < 1) - { - var src = new Rect(right.Size.Width * alpha, 0, right.Size.Width * (1 - alpha), right.Size.Height); - var dst = new Rect(x, 0, w - x, h); - context.DrawImage(right, src, dst); - } + if (OldImage is { } left && alpha > 0) + RenderSingleSide(context, left, new Rect(0, 0, x, h)); + + if (NewImage is { } right && alpha < 1) + RenderSingleSide(context, right, new Rect(x, 0, w - x, h)); context.DrawLine(new Pen(Brushes.DarkGreen, 2), new Point(x, 0), new Point(x, Bounds.Height)); } @@ -233,6 +224,25 @@ namespace SourceGit.Views return new Size(scale * img.Width, scale * img.Height); } + private void RenderSingleSide(DrawingContext context, Bitmap img, Rect clip) + { + var w = Bounds.Width; + var h = Bounds.Height; + + var imgW = img.Size.Width; + var imgH = img.Size.Height; + var scale = Math.Min(1, Math.Min(w / imgW, h / imgH)); + + var scaledW = img.Size.Width * scale; + var scaledH = img.Size.Height * scale; + + var src = new Rect(0, 0, imgW, imgH); + var dst = new Rect((w - scaledW) * 0.5, (h - scaledH) * 0.5, scaledW, scaledH); + + using (context.PushClip(clip)) + context.DrawImage(img, src, dst); + } + private bool _pressedOnSlider = false; private bool _lastInSlider = false; } From c3c7d321671d251df39b5556cbd6dbe0c71229fa Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 11 Jun 2025 10:25:24 +0800 Subject: [PATCH 326/392] refactor: do not change original image aspect ratio and do not up-scale image in `BLEND` image-diff mode Signed-off-by: leo --- src/Views/ImageContainer.cs | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/Views/ImageContainer.cs b/src/Views/ImageContainer.cs index 45acfcd0..995f269b 100644 --- a/src/Views/ImageContainer.cs +++ b/src/Views/ImageContainer.cs @@ -286,7 +286,6 @@ namespace SourceGit.Views { base.Render(context); - var rect = new Rect(0, 0, Bounds.Width, Bounds.Height); var alpha = Alpha; var left = OldImage; var right = NewImage; @@ -295,32 +294,27 @@ namespace SourceGit.Views if (drawLeft && drawRight) { - using (var rt = new RenderTargetBitmap(right.PixelSize, right.Dpi)) + using (var rt = new RenderTargetBitmap(new PixelSize((int)Bounds.Width, (int)Bounds.Height), right.Dpi)) { - var rtRect = new Rect(rt.Size); using (var dc = rt.CreateDrawingContext()) { using (dc.PushRenderOptions(RO_SRC)) - using (dc.PushOpacity(1 - alpha)) - dc.DrawImage(left, rtRect); + RenderSingleSide(dc, left, rt.Size.Width, rt.Size.Height, 1 - alpha); using (dc.PushRenderOptions(RO_DST)) - using (dc.PushOpacity(alpha)) - dc.DrawImage(right, rtRect); + RenderSingleSide(dc, right, rt.Size.Width, rt.Size.Height, alpha); } - context.DrawImage(rt, rtRect, rect); + context.DrawImage(rt, new Rect(0, 0, Bounds.Width, Bounds.Height)); } } else if (drawLeft) { - using (context.PushOpacity(1 - alpha)) - context.DrawImage(left, rect); + RenderSingleSide(context, left, Bounds.Width, Bounds.Height, 1 - alpha); } else if (drawRight) { - using (context.PushOpacity(alpha)) - context.DrawImage(right, rect); + RenderSingleSide(context, right, Bounds.Width, Bounds.Height, alpha); } } @@ -348,6 +342,22 @@ namespace SourceGit.Views return new Size(scale * img.Width, scale * img.Height); } + private void RenderSingleSide(DrawingContext context, Bitmap img, double w, double h, double alpha) + { + var imgW = img.Size.Width; + var imgH = img.Size.Height; + var scale = Math.Min(1, Math.Min(w / imgW, h / imgH)); + + var scaledW = img.Size.Width * scale; + var scaledH = img.Size.Height * scale; + + var src = new Rect(0, 0, imgW, imgH); + var dst = new Rect((w - scaledW) * 0.5, (h - scaledH) * 0.5, scaledW, scaledH); + + using (context.PushOpacity(alpha)) + context.DrawImage(img, src, dst); + } + private static readonly RenderOptions RO_SRC = new RenderOptions() { BitmapBlendingMode = BitmapBlendingMode.Source, BitmapInterpolationMode = BitmapInterpolationMode.HighQuality }; private static readonly RenderOptions RO_DST = new RenderOptions() { BitmapBlendingMode = BitmapBlendingMode.Plus, BitmapInterpolationMode = BitmapInterpolationMode.HighQuality }; } From 549409326175952369cd356736fa75c7b8f8ab71 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 11 Jun 2025 15:20:50 +0800 Subject: [PATCH 327/392] refactor: now all filesystem related trees/lists are sorted in case-insensitive mode Signed-off-by: leo --- src/Commands/CompareRevisions.cs | 2 +- src/Models/NumericSort.cs | 9 ++++++--- src/ViewModels/Preferences.cs | 2 +- src/ViewModels/Repository.cs | 1 + src/ViewModels/StashesPage.cs | 2 +- src/Views/RevisionFileTreeView.axaml.cs | 25 ++++++++++++------------- 6 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/Commands/CompareRevisions.cs b/src/Commands/CompareRevisions.cs index 7b4a496d..c88e087a 100644 --- a/src/Commands/CompareRevisions.cs +++ b/src/Commands/CompareRevisions.cs @@ -39,7 +39,7 @@ namespace SourceGit.Commands foreach (var line in lines) ParseLine(line); - _changes.Sort((l, r) => string.Compare(l.Path, r.Path, StringComparison.Ordinal)); + _changes.Sort((l, r) => Models.NumericSort.Compare(l.Path, r.Path)); return _changes; } diff --git a/src/Models/NumericSort.cs b/src/Models/NumericSort.cs index ed5002e6..3bb69fe2 100644 --- a/src/Models/NumericSort.cs +++ b/src/Models/NumericSort.cs @@ -1,4 +1,7 @@ -namespace SourceGit.Models +using System; +using System.Globalization; + +namespace SourceGit.Models { public static class NumericSort { @@ -23,7 +26,7 @@ bool isDigit1 = char.IsDigit(c1); bool isDigit2 = char.IsDigit(c2); if (isDigit1 != isDigit2) - return c1.CompareTo(c2); + return char.ToUpper(c1, CultureInfo.CurrentCulture).CompareTo(char.ToUpper(c2, CultureInfo.CurrentCulture)); do { @@ -55,7 +58,7 @@ if (isDigit1) result = loc1 == loc2 ? string.CompareOrdinal(sub1, sub2) : loc1 - loc2; else - result = string.CompareOrdinal(sub1, sub2); + result = string.Compare(sub1, sub2, StringComparison.OrdinalIgnoreCase); if (result != 0) return result; diff --git a/src/ViewModels/Preferences.cs b/src/ViewModels/Preferences.cs index e41e046e..665b7604 100644 --- a/src/ViewModels/Preferences.cs +++ b/src/ViewModels/Preferences.cs @@ -449,7 +449,7 @@ namespace SourceGit.ViewModels if (l.IsRepository != r.IsRepository) return l.IsRepository ? 1 : -1; - return string.Compare(l.Name, r.Name, StringComparison.Ordinal); + return Models.NumericSort.Compare(l.Name, r.Name); }); } diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index af72e72a..44fbffc5 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -1275,6 +1275,7 @@ namespace SourceGit.ViewModels if (_workingCopy == null) return; + changes.Sort((l, r) => Models.NumericSort.Compare(l.Path, r.Path)); _workingCopy.SetData(changes); Dispatcher.UIThread.Invoke(() => diff --git a/src/ViewModels/StashesPage.cs b/src/ViewModels/StashesPage.cs index dec8ea6b..eba8ac28 100644 --- a/src/ViewModels/StashesPage.cs +++ b/src/ViewModels/StashesPage.cs @@ -71,7 +71,7 @@ namespace SourceGit.ViewModels changes.Add(c); if (needSort) - changes.Sort((l, r) => string.Compare(l.Path, r.Path, StringComparison.Ordinal)); + changes.Sort((l, r) => Models.NumericSort.Compare(l.Path, r.Path)); } Dispatcher.UIThread.Invoke(() => diff --git a/src/Views/RevisionFileTreeView.axaml.cs b/src/Views/RevisionFileTreeView.axaml.cs index 410f747e..98f32c25 100644 --- a/src/Views/RevisionFileTreeView.axaml.cs +++ b/src/Views/RevisionFileTreeView.axaml.cs @@ -270,12 +270,7 @@ namespace SourceGit.Views foreach (var obj in objects) _tree.Add(new ViewModels.RevisionFileTreeNode { Backend = obj }); - _tree.Sort((l, r) => - { - if (l.IsFolder == r.IsFolder) - return string.Compare(l.Name, r.Name, StringComparison.Ordinal); - return l.IsFolder ? -1 : 1; - }); + SortNodes(_tree); var topTree = new List(); MakeRows(topTree, _tree, 0); @@ -341,13 +336,7 @@ namespace SourceGit.Views foreach (var obj in objects) node.Children.Add(new ViewModels.RevisionFileTreeNode() { Backend = obj }); - node.Children.Sort((l, r) => - { - if (l.IsFolder == r.IsFolder) - return Models.NumericSort.Compare(l.Name, r.Name); - return l.IsFolder ? -1 : 1; - }); - + SortNodes(node.Children); return node.Children; } @@ -365,6 +354,16 @@ namespace SourceGit.Views } } + private void SortNodes(List nodes) + { + nodes.Sort((l, r) => + { + if (l.IsFolder == r.IsFolder) + return Models.NumericSort.Compare(l.Name, r.Name); + return l.IsFolder ? -1 : 1; + }); + } + private List _tree = []; private AvaloniaList _rows = []; private bool _disableSelectionChangingEvent = false; From 196b454ae8751f5b158c4aa5e96d2836eb012c0f Mon Sep 17 00:00:00 2001 From: Nathan Baulch Date: Wed, 11 Jun 2025 17:35:43 +1000 Subject: [PATCH 328/392] feature: support delete key everywhere (#1412) --- src/ViewModels/Repository.cs | 18 ++++++++++++++++ src/ViewModels/StashesPage.cs | 6 ++++++ src/Views/BranchTree.axaml | 1 + src/Views/BranchTree.axaml.cs | 38 ++++++++++++++++++++++++++++++++++ src/Views/StashesPage.axaml | 1 + src/Views/StashesPage.axaml.cs | 16 ++++++++++++++ src/Views/TagsView.axaml | 2 ++ src/Views/TagsView.axaml.cs | 12 +++++++++++ src/Views/ViewLogs.axaml | 1 + src/Views/ViewLogs.axaml.cs | 13 ++++++++++++ src/Views/Welcome.axaml.cs | 30 +++++++++++++++++---------- 11 files changed, 127 insertions(+), 11 deletions(-) diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index 44fbffc5..7f76dc2a 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -1359,6 +1359,18 @@ namespace SourceGit.ViewModels } } + public void DeleteBranch(Models.Branch branch) + { + if (CanCreatePopup()) + ShowPopup(new DeleteBranch(this, branch)); + } + + public void DeleteRemote(Models.Remote remote) + { + if (CanCreatePopup()) + ShowPopup(new DeleteRemote(this, remote)); + } + public void DeleteMultipleBranches(List branches, bool isLocal) { if (CanCreatePopup()) @@ -1383,6 +1395,12 @@ namespace SourceGit.ViewModels ShowPopup(new CreateTag(this, _currentBranch)); } + public void DeleteTag(Models.Tag tag) + { + if (CanCreatePopup()) + ShowPopup(new DeleteTag(this, tag)); + } + public void AddRemote() { if (CanCreatePopup()) diff --git a/src/ViewModels/StashesPage.cs b/src/ViewModels/StashesPage.cs index eba8ac28..6cdf9f45 100644 --- a/src/ViewModels/StashesPage.cs +++ b/src/ViewModels/StashesPage.cs @@ -314,6 +314,12 @@ namespace SourceGit.ViewModels } } + public void Drop(Models.Stash stash) + { + if (_repo.CanCreatePopup()) + _repo.ShowPopup(new DropStash(_repo, stash)); + } + private Repository _repo = null; private List _stashes = []; private List _visibleStashes = []; diff --git a/src/Views/BranchTree.axaml b/src/Views/BranchTree.axaml index dad4f820..151b41bf 100644 --- a/src/Views/BranchTree.axaml +++ b/src/Views/BranchTree.axaml @@ -13,6 +13,7 @@ ItemsSource="{Binding #ThisControl.Rows}" SelectionMode="Multiple" SelectionChanged="OnNodesSelectionChanged" + KeyDown="OnListKeyDown" ContextRequested="OnTreeContextRequested"> diff --git a/src/Views/BranchTree.axaml.cs b/src/Views/BranchTree.axaml.cs index d8fe7903..45cc8608 100644 --- a/src/Views/BranchTree.axaml.cs +++ b/src/Views/BranchTree.axaml.cs @@ -450,6 +450,44 @@ namespace SourceGit.Views } } + private void OnListKeyDown(object _, KeyEventArgs e) + { + if (e.Key is not (Key.Delete or Key.Back)) + return; + + var repo = DataContext as ViewModels.Repository; + if (repo?.Settings == null) + return; + + var selected = BranchesPresenter.SelectedItems; + if (selected == null || selected.Count == 0) + return; + + if (selected is [ViewModels.BranchTreeNode { Backend: Models.Remote remote }]) + { + repo.DeleteRemote(remote); + e.Handled = true; + return; + } + + var branches = new List(); + foreach (var item in selected) + { + if (item is ViewModels.BranchTreeNode node) + CollectBranchesInNode(branches, node); + } + + if (branches.Find(x => x.IsCurrent) != null) + return; + + if (branches.Count == 1) + repo.DeleteBranch(branches[0]); + else + repo.DeleteMultipleBranches(branches, branches[0].IsLocal); + + e.Handled = true; + } + private void OnDoubleTappedBranchNode(object sender, TappedEventArgs _) { if (sender is Grid { DataContext: ViewModels.BranchTreeNode node }) diff --git a/src/Views/StashesPage.axaml b/src/Views/StashesPage.axaml index 15427b93..34913973 100644 --- a/src/Views/StashesPage.axaml +++ b/src/Views/StashesPage.axaml @@ -65,6 +65,7 @@ ItemsSource="{Binding VisibleStashes}" SelectedItem="{Binding SelectedStash, Mode=TwoWay}" SelectionMode="Single" + KeyDown="OnStashKeyDown" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto"> diff --git a/src/Views/StashesPage.axaml.cs b/src/Views/StashesPage.axaml.cs index 461f9d5d..f95d0b98 100644 --- a/src/Views/StashesPage.axaml.cs +++ b/src/Views/StashesPage.axaml.cs @@ -1,4 +1,5 @@ using Avalonia.Controls; +using Avalonia.Input; namespace SourceGit.Views { @@ -33,6 +34,21 @@ namespace SourceGit.Views e.Handled = true; } + private void OnStashKeyDown(object sender, KeyEventArgs e) + { + if (e.Key is not (Key.Delete or Key.Back)) + return; + + if (DataContext is not ViewModels.StashesPage vm) + return; + + if (sender is not ListBox { SelectedValue: Models.Stash stash }) + return; + + vm.Drop(stash); + e.Handled = true; + } + private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e) { if (DataContext is ViewModels.StashesPage vm && sender is Grid grid) diff --git a/src/Views/TagsView.axaml b/src/Views/TagsView.axaml index 0dbe6dae..7dadbaad 100644 --- a/src/Views/TagsView.axaml +++ b/src/Views/TagsView.axaml @@ -23,6 +23,7 @@ @@ -88,6 +89,7 @@ Margin="8,0,0,0" ItemsSource="{Binding Tags}" SelectionMode="Single" + KeyDown="OnKeyDown" SelectionChanged="OnSelectionChanged"> diff --git a/src/Views/TagsView.axaml.cs b/src/Views/TagsView.axaml.cs index 6764c7ea..6bd312f3 100644 --- a/src/Views/TagsView.axaml.cs +++ b/src/Views/TagsView.axaml.cs @@ -214,6 +214,18 @@ namespace SourceGit.Views if (selectedTag != null) RaiseEvent(new RoutedEventArgs(SelectionChangedEvent)); } + + private void OnKeyDown(object sender, KeyEventArgs e) + { + if (sender is not ListBox { SelectedValue: Models.Tag tag }) + return; + + if (DataContext is not ViewModels.Repository repo) + return; + + repo.DeleteTag(tag); + e.Handled = true; + } } } diff --git a/src/Views/ViewLogs.axaml b/src/Views/ViewLogs.axaml index 1fb44d66..07d0048e 100644 --- a/src/Views/ViewLogs.axaml +++ b/src/Views/ViewLogs.axaml @@ -53,6 +53,7 @@ ItemsSource="{Binding Logs}" SelectedItem="{Binding SelectedLog, Mode=TwoWay}" SelectionMode="Single" + KeyDown="OnLogKeyDown" Grid.IsSharedSizeScope="True" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto"> diff --git a/src/Views/ViewLogs.axaml.cs b/src/Views/ViewLogs.axaml.cs index f0a27884..0ed24449 100644 --- a/src/Views/ViewLogs.axaml.cs +++ b/src/Views/ViewLogs.axaml.cs @@ -1,4 +1,5 @@ using Avalonia.Controls; +using Avalonia.Input; namespace SourceGit.Views { @@ -39,5 +40,17 @@ namespace SourceGit.Views e.Handled = true; } + + private void OnLogKeyDown(object _, KeyEventArgs e) + { + if (e.Key is not (Key.Delete or Key.Back)) + return; + + if (DataContext is not ViewModels.ViewLogs vm) + return; + + vm.Logs.Remove(vm.SelectedLog); + e.Handled = true; + } } } diff --git a/src/Views/Welcome.axaml.cs b/src/Views/Welcome.axaml.cs index 3ab11782..3a69247a 100644 --- a/src/Views/Welcome.axaml.cs +++ b/src/Views/Welcome.axaml.cs @@ -94,20 +94,28 @@ namespace SourceGit.Views private void OnTreeViewKeyDown(object _, KeyEventArgs e) { - if (TreeContainer.SelectedItem is ViewModels.RepositoryNode node && e.Key == Key.Enter) + if (TreeContainer.SelectedItem is ViewModels.RepositoryNode node) { - if (node.IsRepository) + if (e.Key == Key.Enter) { - var parent = this.FindAncestorOfType(); - if (parent is { DataContext: ViewModels.Launcher launcher }) - launcher.OpenRepositoryInTab(node, null); - } - else - { - ViewModels.Welcome.Instance.ToggleNodeIsExpanded(node); - } + if (node.IsRepository) + { + var parent = this.FindAncestorOfType(); + if (parent is { DataContext: ViewModels.Launcher launcher }) + launcher.OpenRepositoryInTab(node, null); + } + else + { + ViewModels.Welcome.Instance.ToggleNodeIsExpanded(node); + } - e.Handled = true; + e.Handled = true; + } + else if (e.Key is Key.Delete or Key.Back) + { + node.Delete(); + e.Handled = true; + } } } From a128b67bd4e8dfbdc679c4fa32617acdb478a952 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 11 Jun 2025 16:13:47 +0800 Subject: [PATCH 329/392] code_review: PR #1412 - Use `ViewModels.StashesPage.SelectedStash` instead of `sender is not ListBox { SelectedValue: Models.Stash stash }` - In tags view, `SelectedItem` can be `Models.Tag` or `ViewModels.TagTreeNode` - In logs window, `vm.SelectedLog` may be null Signed-off-by: leo --- src/ViewModels/Repository.cs | 12 ++++----- src/ViewModels/StashesPage.cs | 12 ++++----- src/Views/BranchTree.axaml | 2 +- src/Views/BranchTree.axaml.cs | 4 +-- src/Views/StashesPage.axaml | 2 +- src/Views/StashesPage.axaml.cs | 25 ++++++++---------- src/Views/TagsView.axaml.cs | 12 +++++---- src/Views/ViewLogs.axaml.cs | 7 +++-- src/Views/Welcome.axaml | 3 +-- src/Views/Welcome.axaml.cs | 47 +++++++++++++--------------------- 10 files changed, 56 insertions(+), 70 deletions(-) diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index 7f76dc2a..b97bbf5d 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -1365,12 +1365,6 @@ namespace SourceGit.ViewModels ShowPopup(new DeleteBranch(this, branch)); } - public void DeleteRemote(Models.Remote remote) - { - if (CanCreatePopup()) - ShowPopup(new DeleteRemote(this, remote)); - } - public void DeleteMultipleBranches(List branches, bool isLocal) { if (CanCreatePopup()) @@ -1407,6 +1401,12 @@ namespace SourceGit.ViewModels ShowPopup(new AddRemote(this)); } + public void DeleteRemote(Models.Remote remote) + { + if (CanCreatePopup()) + ShowPopup(new DeleteRemote(this, remote)); + } + public void AddSubmodule() { if (CanCreatePopup()) diff --git a/src/ViewModels/StashesPage.cs b/src/ViewModels/StashesPage.cs index 6cdf9f45..f039d54e 100644 --- a/src/ViewModels/StashesPage.cs +++ b/src/ViewModels/StashesPage.cs @@ -295,6 +295,12 @@ namespace SourceGit.ViewModels SearchFilter = string.Empty; } + public void Drop(Models.Stash stash) + { + if (stash != null && _repo.CanCreatePopup()) + _repo.ShowPopup(new DropStash(_repo, stash)); + } + private void RefreshVisible() { if (string.IsNullOrEmpty(_searchFilter)) @@ -314,12 +320,6 @@ namespace SourceGit.ViewModels } } - public void Drop(Models.Stash stash) - { - if (_repo.CanCreatePopup()) - _repo.ShowPopup(new DropStash(_repo, stash)); - } - private Repository _repo = null; private List _stashes = []; private List _visibleStashes = []; diff --git a/src/Views/BranchTree.axaml b/src/Views/BranchTree.axaml index 151b41bf..1783ffe0 100644 --- a/src/Views/BranchTree.axaml +++ b/src/Views/BranchTree.axaml @@ -13,7 +13,7 @@ ItemsSource="{Binding #ThisControl.Rows}" SelectionMode="Multiple" SelectionChanged="OnNodesSelectionChanged" - KeyDown="OnListKeyDown" + KeyDown="OnTreeKeyDown" ContextRequested="OnTreeContextRequested"> diff --git a/src/Views/BranchTree.axaml.cs b/src/Views/BranchTree.axaml.cs index 45cc8608..0579c0da 100644 --- a/src/Views/BranchTree.axaml.cs +++ b/src/Views/BranchTree.axaml.cs @@ -450,7 +450,7 @@ namespace SourceGit.Views } } - private void OnListKeyDown(object _, KeyEventArgs e) + private void OnTreeKeyDown(object _, KeyEventArgs e) { if (e.Key is not (Key.Delete or Key.Back)) return; @@ -463,7 +463,7 @@ namespace SourceGit.Views if (selected == null || selected.Count == 0) return; - if (selected is [ViewModels.BranchTreeNode { Backend: Models.Remote remote }]) + if (selected.Count == 1 && selected[0] is ViewModels.BranchTreeNode { Backend: Models.Remote remote }) { repo.DeleteRemote(remote); e.Handled = true; diff --git a/src/Views/StashesPage.axaml b/src/Views/StashesPage.axaml index 34913973..30c6e183 100644 --- a/src/Views/StashesPage.axaml +++ b/src/Views/StashesPage.axaml @@ -65,7 +65,7 @@ ItemsSource="{Binding VisibleStashes}" SelectedItem="{Binding SelectedStash, Mode=TwoWay}" SelectionMode="Single" - KeyDown="OnStashKeyDown" + KeyDown="OnStashListKeyDown" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto"> diff --git a/src/Views/StashesPage.axaml.cs b/src/Views/StashesPage.axaml.cs index f95d0b98..d152a12f 100644 --- a/src/Views/StashesPage.axaml.cs +++ b/src/Views/StashesPage.axaml.cs @@ -24,17 +24,7 @@ namespace SourceGit.Views layout.StashesLeftWidth = new GridLength(maxLeft, GridUnitType.Pixel); } - private void OnStashContextRequested(object sender, ContextRequestedEventArgs e) - { - if (DataContext is ViewModels.StashesPage vm && sender is Border border) - { - var menu = vm.MakeContextMenu(border.DataContext as Models.Stash); - menu?.Open(border); - } - e.Handled = true; - } - - private void OnStashKeyDown(object sender, KeyEventArgs e) + private void OnStashListKeyDown(object sender, KeyEventArgs e) { if (e.Key is not (Key.Delete or Key.Back)) return; @@ -42,10 +32,17 @@ namespace SourceGit.Views if (DataContext is not ViewModels.StashesPage vm) return; - if (sender is not ListBox { SelectedValue: Models.Stash stash }) - return; + vm.Drop(vm.SelectedStash); + e.Handled = true; + } - vm.Drop(stash); + private void OnStashContextRequested(object sender, ContextRequestedEventArgs e) + { + if (DataContext is ViewModels.StashesPage vm && sender is Border border) + { + var menu = vm.MakeContextMenu(border.DataContext as Models.Stash); + menu?.Open(border); + } e.Handled = true; } diff --git a/src/Views/TagsView.axaml.cs b/src/Views/TagsView.axaml.cs index 6bd312f3..390063ee 100644 --- a/src/Views/TagsView.axaml.cs +++ b/src/Views/TagsView.axaml.cs @@ -217,13 +217,15 @@ namespace SourceGit.Views private void OnKeyDown(object sender, KeyEventArgs e) { - if (sender is not ListBox { SelectedValue: Models.Tag tag }) - return; - if (DataContext is not ViewModels.Repository repo) return; - - repo.DeleteTag(tag); + + var selected = (sender as ListBox)?.SelectedItem; + if (selected is ViewModels.TagTreeNode { Tag: { } tagInNode }) + repo.DeleteTag(tagInNode); + else if (selected is Models.Tag tag) + repo.DeleteTag(tag); + e.Handled = true; } } diff --git a/src/Views/ViewLogs.axaml.cs b/src/Views/ViewLogs.axaml.cs index 0ed24449..b9c71882 100644 --- a/src/Views/ViewLogs.axaml.cs +++ b/src/Views/ViewLogs.axaml.cs @@ -45,11 +45,10 @@ namespace SourceGit.Views { if (e.Key is not (Key.Delete or Key.Back)) return; - - if (DataContext is not ViewModels.ViewLogs vm) - return; - vm.Logs.Remove(vm.SelectedLog); + if (DataContext is ViewModels.ViewLogs { SelectedLog: { } log } vm) + vm.Logs.Remove(log); + e.Handled = true; } } diff --git a/src/Views/Welcome.axaml b/src/Views/Welcome.axaml index 1e300653..d249e5ef 100644 --- a/src/Views/Welcome.axaml +++ b/src/Views/Welcome.axaml @@ -61,8 +61,7 @@ ItemsSource="{Binding Rows}" SelectionMode="Single" Loaded="SetupTreeViewDragAndDrop" - LostFocus="OnTreeViewLostFocus" - KeyDown="OnTreeViewKeyDown"> + LostFocus="OnTreeViewLostFocus"> @@ -38,7 +38,7 @@ - + @@ -206,7 +206,7 @@ - + - + - + - + - + @@ -737,7 +737,7 @@ - + @@ -956,7 +956,7 @@ - + - + - +