diff --git a/README.md b/README.md index 67ee5258..3829cf54 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ ## Translation Status -[![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen)](TRANSLATION.md) [![de__DE](https://img.shields.io/badge/de__DE-98.13%25-yellow)](TRANSLATION.md) [![es__ES](https://img.shields.io/badge/es__ES-98.13%25-yellow)](TRANSLATION.md) [![fr__FR](https://img.shields.io/badge/fr__FR-92.91%25-yellow)](TRANSLATION.md) [![it__IT](https://img.shields.io/badge/it__IT-98.40%25-yellow)](TRANSLATION.md) [![pt__BR](https://img.shields.io/badge/pt__BR-92.65%25-yellow)](TRANSLATION.md) [![ru__RU](https://img.shields.io/badge/ru__RU-98.13%25-yellow)](TRANSLATION.md) [![zh__CN](https://img.shields.io/badge/zh__CN-%E2%88%9A-brightgreen)](TRANSLATION.md) [![zh__TW](https://img.shields.io/badge/zh__TW-%E2%88%9A-brightgreen)](TRANSLATION.md) +[![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen)](TRANSLATION.md) [![de__DE](https://img.shields.io/badge/de__DE-99.47%25-yellow)](TRANSLATION.md) [![es__ES](https://img.shields.io/badge/es__ES-97.61%25-yellow)](TRANSLATION.md) [![fr__FR](https://img.shields.io/badge/fr__FR-92.42%25-yellow)](TRANSLATION.md) [![it__IT](https://img.shields.io/badge/it__IT-97.87%25-yellow)](TRANSLATION.md) [![pt__BR](https://img.shields.io/badge/pt__BR-92.15%25-yellow)](TRANSLATION.md) [![ru__RU](https://img.shields.io/badge/ru__RU-99.73%25-yellow)](TRANSLATION.md) [![zh__CN](https://img.shields.io/badge/zh__CN-%E2%88%9A-brightgreen)](TRANSLATION.md) [![zh__TW](https://img.shields.io/badge/zh__TW-%E2%88%9A-brightgreen)](TRANSLATION.md) > [!NOTE] > You can find the missing keys in [TRANSLATION.md](TRANSLATION.md) diff --git a/TRANSLATION.md b/TRANSLATION.md index 9f7a1c80..84411404 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -1,4 +1,17 @@ -### de_DE.axaml: 98.13% +### de_DE.axaml: 99.47% + + +
+Missing Keys + +- Text.BranchCM.CustomAction +- Text.Configure.CustomAction.Scope.Branch +- Text.Configure.CustomAction.WaitForExit +- Text.Repository.Notifications.Clear + +
+ +### es_ES.axaml: 97.61%
@@ -10,41 +23,22 @@ - Text.ApplyStash.DropAfterApply - Text.ApplyStash.RestoreIndex - Text.ApplyStash.Stash +- Text.BranchCM.CustomAction - Text.Clone.RecurseSubmodules +- Text.Configure.CustomAction.Scope.Branch +- Text.Configure.CustomAction.WaitForExit - Text.CreateBranch.Name.WarnSpace - Text.DeleteRepositoryNode.Path - Text.DeleteRepositoryNode.TipForGroup - Text.DeleteRepositoryNode.TipForRepository +- Text.Repository.Notifications.Clear - Text.Stash.AutoRestore - Text.Stash.AutoRestore.Tip - Text.WorkingCopy.SignOff
-### es_ES.axaml: 98.13% - - -
-Missing Keys - -- Text.AIAssistant.Regen -- Text.AIAssistant.Use -- Text.ApplyStash -- Text.ApplyStash.DropAfterApply -- Text.ApplyStash.RestoreIndex -- Text.ApplyStash.Stash -- Text.Clone.RecurseSubmodules -- Text.CreateBranch.Name.WarnSpace -- Text.DeleteRepositoryNode.Path -- Text.DeleteRepositoryNode.TipForGroup -- Text.DeleteRepositoryNode.TipForRepository -- Text.Stash.AutoRestore -- Text.Stash.AutoRestore.Tip -- Text.WorkingCopy.SignOff - -
- -### fr_FR.axaml: 92.91% +### fr_FR.axaml: 92.42%
@@ -56,7 +50,10 @@ - Text.ApplyStash.DropAfterApply - Text.ApplyStash.RestoreIndex - Text.ApplyStash.Stash +- Text.BranchCM.CustomAction - Text.Clone.RecurseSubmodules +- Text.Configure.CustomAction.Scope.Branch +- Text.Configure.CustomAction.WaitForExit - Text.CreateBranch.Name.WarnSpace - Text.DeleteRepositoryNode.Path - Text.DeleteRepositoryNode.TipForGroup @@ -87,6 +84,7 @@ - Text.Repository.HistoriesOrder - Text.Repository.HistoriesOrder.ByDate - Text.Repository.HistoriesOrder.Topo +- Text.Repository.Notifications.Clear - Text.Repository.Skip - Text.Repository.Tags.OrderByCreatorDate - Text.Repository.Tags.OrderByNameAsc @@ -106,7 +104,7 @@
-### it_IT.axaml: 98.40% +### it_IT.axaml: 97.87%
@@ -118,16 +116,20 @@ - Text.ApplyStash.DropAfterApply - Text.ApplyStash.RestoreIndex - Text.ApplyStash.Stash +- Text.BranchCM.CustomAction - Text.Clone.RecurseSubmodules +- Text.Configure.CustomAction.Scope.Branch +- Text.Configure.CustomAction.WaitForExit - Text.DeleteRepositoryNode.Path - Text.DeleteRepositoryNode.TipForGroup - Text.DeleteRepositoryNode.TipForRepository +- Text.Repository.Notifications.Clear - Text.Stash.AutoRestore - Text.Stash.AutoRestore.Tip
-### pt_BR.axaml: 92.65% +### pt_BR.axaml: 92.15%
@@ -139,12 +141,15 @@ - Text.ApplyStash.DropAfterApply - Text.ApplyStash.RestoreIndex - Text.ApplyStash.Stash +- Text.BranchCM.CustomAction - Text.BranchCM.MergeMultiBranches - Text.Clone.RecurseSubmodules - Text.CommitCM.Merge - Text.CommitCM.MergeMultiple - Text.CommitDetail.Files.Search - Text.CommitDetail.Info.Children +- Text.Configure.CustomAction.Scope.Branch +- Text.Configure.CustomAction.WaitForExit - Text.Configure.IssueTracker.AddSampleGiteeIssue - Text.Configure.IssueTracker.AddSampleGiteePullRequest - Text.CreateBranch.Name.WarnSpace @@ -172,6 +177,7 @@ - Text.Repository.HistoriesLayout.Horizontal - Text.Repository.HistoriesLayout.Vertical - Text.Repository.HistoriesOrder +- Text.Repository.Notifications.Clear - Text.Repository.OnlyHighlightCurrentBranchInHistories - Text.Repository.Skip - Text.Repository.Tags.OrderByCreatorDate @@ -191,26 +197,14 @@
-### ru_RU.axaml: 98.13% +### ru_RU.axaml: 99.73%
Missing Keys -- Text.AIAssistant.Regen -- Text.AIAssistant.Use -- Text.ApplyStash -- Text.ApplyStash.DropAfterApply -- Text.ApplyStash.RestoreIndex -- Text.ApplyStash.Stash -- Text.Clone.RecurseSubmodules -- Text.CreateBranch.Name.WarnSpace -- Text.DeleteRepositoryNode.Path -- Text.DeleteRepositoryNode.TipForGroup -- Text.DeleteRepositoryNode.TipForRepository -- Text.Stash.AutoRestore -- Text.Stash.AutoRestore.Tip -- Text.WorkingCopy.SignOff +- Text.BranchCM.CustomAction +- Text.Configure.CustomAction.Scope.Branch
diff --git a/VERSION b/VERSION index 4ee298df..15689348 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2025.04 \ No newline at end of file +2025.05 \ No newline at end of file diff --git a/src/Commands/ExecuteCustomAction.cs b/src/Commands/ExecuteCustomAction.cs index a10f5387..3a77ec01 100644 --- a/src/Commands/ExecuteCustomAction.cs +++ b/src/Commands/ExecuteCustomAction.cs @@ -8,7 +8,37 @@ namespace SourceGit.Commands { public static class ExecuteCustomAction { - public static void Run(string repo, string file, string args, Action outputHandler) + public static void Run(string repo, string file, string args) + { + var start = new ProcessStartInfo(); + start.FileName = file; + start.Arguments = args; + start.UseShellExecute = false; + start.CreateNoWindow = true; + start.WorkingDirectory = repo; + + // Force using en_US.UTF-8 locale to avoid GCM crash + if (OperatingSystem.IsLinux()) + start.Environment.Add("LANG", "en_US.UTF-8"); + + // Fix macOS `PATH` env + if (OperatingSystem.IsMacOS() && !string.IsNullOrEmpty(Native.OS.CustomPathEnv)) + start.Environment.Add("PATH", Native.OS.CustomPathEnv); + + try + { + Process.Start(start); + } + catch (Exception e) + { + Dispatcher.UIThread.Invoke(() => + { + App.RaiseException(repo, e.Message); + }); + } + } + + public static void RunAndWait(string repo, string file, string args, Action outputHandler) { var start = new ProcessStartInfo(); start.FileName = file; diff --git a/src/Commands/Worktree.cs b/src/Commands/Worktree.cs index 27c0e28e..b73b8573 100644 --- a/src/Commands/Worktree.cs +++ b/src/Commands/Worktree.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; namespace SourceGit.Commands { @@ -26,6 +27,7 @@ namespace SourceGit.Commands if (line.StartsWith("worktree ", StringComparison.Ordinal)) { last = new Models.Worktree() { FullPath = line.Substring(9).Trim() }; + last.RelativePath = Path.GetRelativePath(WorkingDirectory, last.FullPath); worktrees.Add(last); } else if (line.StartsWith("bare", StringComparison.Ordinal)) diff --git a/src/Models/CustomAction.cs b/src/Models/CustomAction.cs index 8452a42d..a614961a 100644 --- a/src/Models/CustomAction.cs +++ b/src/Models/CustomAction.cs @@ -6,6 +6,7 @@ namespace SourceGit.Models { Repository, Commit, + Branch, } public class CustomAction : ObservableObject @@ -34,9 +35,16 @@ namespace SourceGit.Models set => SetProperty(ref _arguments, value); } + public bool WaitForExit + { + get => _waitForExit; + set => SetProperty(ref _waitForExit, value); + } + private string _name = string.Empty; private CustomActionScope _scope = CustomActionScope.Repository; private string _executable = string.Empty; private string _arguments = string.Empty; + private bool _waitForExit = true; } } diff --git a/src/Models/Watcher.cs b/src/Models/Watcher.cs index 7a6c5163..d0ccdea5 100644 --- a/src/Models/Watcher.cs +++ b/src/Models/Watcher.cs @@ -114,6 +114,7 @@ namespace SourceGit.Models { _updateBranch = 0; _updateWC = 0; + _updateSubmodules = 0; if (_updateTags > 0) { @@ -124,6 +125,7 @@ namespace SourceGit.Models Task.Run(_repo.RefreshBranches); Task.Run(_repo.RefreshCommits); Task.Run(_repo.RefreshWorkingCopyChanges); + Task.Run(_repo.RefreshSubmodules); Task.Run(_repo.RefreshWorktrees); } @@ -136,20 +138,20 @@ namespace SourceGit.Models if (_updateSubmodules > 0 && now > _updateSubmodules) { _updateSubmodules = 0; - _repo.RefreshSubmodules(); + Task.Run(_repo.RefreshSubmodules); } if (_updateStashes > 0 && now > _updateStashes) { _updateStashes = 0; - _repo.RefreshStashes(); + Task.Run(_repo.RefreshStashes); } if (_updateTags > 0 && now > _updateTags) { _updateTags = 0; - _repo.RefreshTags(); - _repo.RefreshCommits(); + Task.Run(_repo.RefreshTags); + Task.Run(_repo.RefreshCommits); } } @@ -178,12 +180,6 @@ namespace SourceGit.Models (name.StartsWith("worktrees/", StringComparison.Ordinal) && name.EndsWith("/HEAD", StringComparison.Ordinal))) { _updateBranch = DateTime.Now.AddSeconds(.5).ToFileTime(); - - lock (_lockSubmodule) - { - if (_submodules.Count > 0) - _updateSubmodules = DateTime.Now.AddSeconds(1).ToFileTime(); - } } else if (name.StartsWith("objects/", StringComparison.Ordinal) || name.Equals("index", StringComparison.Ordinal)) { diff --git a/src/Models/Worktree.cs b/src/Models/Worktree.cs index f9ba14e4..bc40e320 100644 --- a/src/Models/Worktree.cs +++ b/src/Models/Worktree.cs @@ -6,6 +6,7 @@ namespace SourceGit.Models { public string Branch { get; set; } = string.Empty; public string FullPath { get; set; } = string.Empty; + public string RelativePath { get; set; } = string.Empty; public string Head { get; set; } = string.Empty; public bool IsBare { get; set; } = false; public bool IsDetached { get; set; } = false; @@ -21,15 +22,15 @@ namespace SourceGit.Models get { if (IsDetached) - return $"(deteched HEAD at {Head.Substring(10)})"; + return $"deteched HEAD at {Head.Substring(10)}"; if (Branch.StartsWith("refs/heads/", System.StringComparison.Ordinal)) - return $"({Branch.Substring(11)})"; + return Branch.Substring(11); if (Branch.StartsWith("refs/remotes/", System.StringComparison.Ordinal)) - return $"({Branch.Substring(13)})"; + return Branch.Substring(13); - return $"({Branch})"; + return Branch; } } diff --git a/src/Resources/Icons.axaml b/src/Resources/Icons.axaml index c7e02431..8e865660 100644 --- a/src/Resources/Icons.axaml +++ b/src/Resources/Icons.axaml @@ -10,7 +10,9 @@ M512 597m-1 0a1 1 0 103 0a1 1 0 10-3 0ZM810 393 732 315 448 600 293 444 214 522l156 156 78 78 362-362z M747 467c29 0 56 4 82 12v-363c0-47-38-84-84-84H125c-47 0-84 38-84 84v707c0 47 38 84 84 84h375a287 287 0 01-43-152c0-160 129-289 289-289zm-531-250h438c19 0 34 15 34 34s-15 34-34 34H216c-19 0-34-15-34-34s15-34 34-34zm0 179h263c19 0 34 15 34 34s-15 34-34 34H216c-19 0-34-15-34-34s15-34 34-34zm131 247h-131c-19 0-34-15-34-34s15-34 34-34h131c19 0 34 15 34 34s-15 34-34 34zM747 521c-130 0-236 106-236 236S617 992 747 992s236-106 236-236S877 521 747 521zm11 386v-65h-130c-12 0-22-10-22-22s10-22 22-22h260l-130 108zm108-192H606l130-108v65h130c12 0 22 10 22 22s-10 22-22 22z M529 511c115 0 212 79 239 185h224a62 62 0 017 123l-7 0-224 0a247 247 0 01-479 0H65a62 62 0 01-7-123l7-0h224a247 247 0 01239-185zm0 124a124 124 0 100 247 124 124 0 000-247zm0-618c32 0 58 24 61 55l0 7V206c89 11 165 45 225 103a74 74 0 0122 45l0 9v87a62 62 0 01-123 7l-0-7v-65l-6-4c-43-33-97-51-163-53l-17-0c-74 0-133 18-180 54l-6 4v65a62 62 0 01-55 61l-7 0a62 62 0 01-61-55l-0-7V362c0-20 8-39 23-53 60-58 135-92 224-103V79c0-34 28-62 62-62z + M512 926c-229 0-414-186-414-414S283 98 512 98s414 186 414 414-186 414-414 414zm0-73c189 0 341-153 341-341S701 171 512 171 171 323 171 512s153 341 341 341zm-6-192L284 439l52-52 171 171 171-171L728 439l-222 222z M512 57c251 0 455 204 455 455S763 967 512 967 57 763 57 512 261 57 512 57zm181 274c-11-11-29-11-40 0L512 472 371 331c-11-11-29-11-40 0-11 11-11 29 0 40L471 512 331 653c-11 11-11 29 0 40 11 11 29 11 40 0l141-141 141 141c11 11 29 11 40 0 11-11 11-29 0-40L552 512l141-141c11-11 11-29 0-40z + M591 907A85 85 0 01427 875h114a299 299 0 0050 32zM725 405c130 0 235 105 235 235s-105 235-235 235-235-105-235-235 105-235 235-235zM512 64a43 43 0 0143 43v24c126 17 229 107 264 225A298 298 0 00725 341l-4 0A235 235 0 00512 213l-5 0c-125 4-224 104-228 229l-0 6v167a211 211 0 01-26 101l-4 7-14 23h211a298 298 0 0050 85l-276-0a77 77 0 01-66-39c-13-22-14-50-2-73l2-4 22-36c10-17 16-37 17-57l0-7v-167C193 287 313 153 469 131V107a43 43 0 0139-43zm345 505L654 771a149 149 0 00202-202zM725 491a149 149 0 00-131 220l202-202A149 149 0 00725 491z M797 829a49 49 0 1049 49 49 49 0 00-49-49zm147-114A49 49 0 10992 764a49 49 0 00-49-49zM928 861a49 49 0 1049 49A49 49 0 00928 861zm-5-586L992 205 851 64l-71 71a67 67 0 00-94 0l235 235a67 67 0 000-94zm-853 128a32 32 0 00-32 50 1291 1291 0 0075 112L288 552c20 0 25 21 8 37l-93 86a1282 1282 0 00120 114l100-32c19-6 28 15 14 34l-40 55c26 19 53 36 82 53a89 89 0 00115-20 1391 1391 0 00256-485l-188-188s-306 224-595 198z M1280 704c0 141-115 256-256 256H288C129 960 0 831 0 672c0-126 80-232 192-272A327 327 0 01192 384c0-177 143-320 320-320 119 0 222 64 277 160C820 204 857 192 896 192c106 0 192 86 192 192 0 24-5 48-13 69C1192 477 1280 580 1280 704zm-493-128H656V352c0-18-14-32-32-32h-96c-18 0-32 14-32 32v224h-131c-29 0-43 34-23 55l211 211c12 12 33 12 45 0l211-211c20-20 6-55-23-55z M853 102H171C133 102 102 133 102 171v683C102 891 133 922 171 922h683C891 922 922 891 922 853V171C922 133 891 102 853 102zM390 600l-48 48L205 512l137-137 48 48L301 512l88 88zM465 819l-66-18L559 205l66 18L465 819zm218-171L634 600 723 512l-88-88 48-48L819 512 683 649z diff --git a/src/Resources/Locales/de_DE.axaml b/src/Resources/Locales/de_DE.axaml index fe961d14..b6458822 100644 --- a/src/Resources/Locales/de_DE.axaml +++ b/src/Resources/Locales/de_DE.axaml @@ -22,7 +22,9 @@ Branch verfolgen: Remote-Branch verfolgen OpenAI Assistent + Neu generieren Verwende OpenAI, um Commit-Nachrichten zu generieren + Als Commit-Nachricht verwenden Patch Fehler Fehler werfen und anwenden des Patches verweigern @@ -37,6 +39,10 @@ Warnen Gibt eine Warnung für ein paar solcher Fehler aus, aber wendet es an Leerzeichen: + Stash anwenden + Nach dem Anwenden löschen + Änderungen des Index wiederherstellen + Stash: Archivieren... Speichere Archiv in: Wähle Archivpfad aus @@ -100,6 +106,7 @@ Lokaler Name: Repository-Name. Optional. Übergeordneter Ordner: + Submodule initialisieren und aktualisieren Repository URL: SCHLIESSEN Editor @@ -202,6 +209,7 @@ Stashen & wieder anwenden Neuer Branch-Name: Branch-Namen eingeben. + Leerzeichen werden durch Bindestriche ersetzt. Lokalen Branch erstellen Tag erstellen... Neuer Tag auf: @@ -225,8 +233,11 @@ Du versuchst mehrere Branches auf einmal zu löschen. Kontrolliere noch einmal vor dem Fortfahren! Remote löschen Remote: + Pfad: Ziel: Bestätige löschen von Gruppe + Alle Nachfolger werden aus der Liste entfernt. + Dadurch wird es nur aus der Liste entfernt, nicht von der Festplatte! Bestätige löschen von Repository Lösche Submodul Submodul Pfad: @@ -565,6 +576,7 @@ Aufheben Im Graph ausblenden Im Graph filtern + Aktiviere '--first-parent' Option LAYOUT Horizontal Vertikal @@ -573,7 +585,6 @@ Topologie LOKALE BRANCHES Zum HEAD wechseln - Aktiviere '--first-parent' Option Erstelle Branch Nur aktuellen Branch im Graphen hervorheben Öffne in {0} @@ -641,6 +652,8 @@ Pfad zum privaten SSH Schlüssel START Stash + Automatisch wiederherstellen nach dem Stashen + Die Arbeitsdateien bleiben unverändert, aber ein Stash wird gespeichert. Inklusive nicht-verfolgter Dateien Behalte gestagte Dateien Name: @@ -720,6 +733,7 @@ NICHT-VERFOLGTE DATEIEN INKLUDIEREN KEINE BISHERIGEN COMMIT-NACHRICHTEN KEINE COMMIT TEMPLATES + SignOff GESTAGED UNSTAGEN ALLES UNSTAGEN diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 018b54d7..cd266666 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -56,6 +56,7 @@ Compare with HEAD Compare with Worktree Copy Branch Name + Custom Action Delete ${0}$... Delete selected {0} branches Discard all changes @@ -152,12 +153,14 @@ Template Content: CUSTOM ACTION Arguments: - ${REPO} - Repository's path; ${SHA} - Selected commit's SHA + ${REPO} - Repository's path; ${BRANCH} - Selected branch; ${SHA} - Selected commit's SHA Executable File: Name: Scope: + Branch Commit Repository + Wait for action exit Email Address Email address GIT @@ -462,7 +465,7 @@ Default Editor Monospace Font - Only use monospace font in text editor + Use monospace font only in text editor Theme Theme Overrides Use fixed tab width in titlebar @@ -573,6 +576,7 @@ Unset Hide in commit graph Filter in commit graph + Enable '--first-parent' Option LAYOUT Horizontal Vertical @@ -581,8 +585,8 @@ Topologically LOCAL BRANCHES Navigate to HEAD - Enable '--first-parent' Option Create Branch + CLEAR NOTIFICATIONS Only highlight current branch in graph Open in {0} Open in External Tools diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml index 3e926c63..a0f35814 100644 --- a/src/Resources/Locales/es_ES.axaml +++ b/src/Resources/Locales/es_ES.axaml @@ -566,6 +566,7 @@ Desestablecer Ocultar en el Gráfico de Commits Filtrar en el Gráfico de Commits + Habilitar Opción '--first-parent' DISPOSICIÓN Horizontal Vertical @@ -574,7 +575,6 @@ Topológicamente RAMAS LOCALES Navegar a HEAD - Habilitar Opción '--first-parent' Crear Rama Resaltar solo la rama actual en el gráfico Abrir en {0} diff --git a/src/Resources/Locales/fr_FR.axaml b/src/Resources/Locales/fr_FR.axaml index da20a5ee..b269424a 100644 --- a/src/Resources/Locales/fr_FR.axaml +++ b/src/Resources/Locales/fr_FR.axaml @@ -548,9 +548,9 @@ Activer l'option '--reflog' Ouvrir dans l'explorateur de fichiers Rechercher Branches/Tags/Submodules + Activer l'option '--first-parent' BRANCHES LOCALES Naviguer vers le HEAD - Activer l'option '--first-parent' Créer une branche Mettre la branche courante en surbrillance dans le graph Ouvrir dans {0} diff --git a/src/Resources/Locales/it_IT.axaml b/src/Resources/Locales/it_IT.axaml index 95f7bd1d..1d0d353e 100644 --- a/src/Resources/Locales/it_IT.axaml +++ b/src/Resources/Locales/it_IT.axaml @@ -568,6 +568,7 @@ Non impostato Nascondi nel grafico dei commit Filtra nel grafico dei commit + Abilita opzione '--first-parent' LAYOUT Orizzontale Verticale @@ -576,7 +577,6 @@ Topologicamente BRANCH LOCALI Vai a HEAD - Abilita opzione '--first-parent' Crea Branch Evidenzia nel grafico solo il branch corrente Apri in {0} diff --git a/src/Resources/Locales/pt_BR.axaml b/src/Resources/Locales/pt_BR.axaml index dee8565b..a318cfa8 100644 --- a/src/Resources/Locales/pt_BR.axaml +++ b/src/Resources/Locales/pt_BR.axaml @@ -566,11 +566,11 @@ Desfazer Esconder no gráfico de commit Incluir no gráfico de commit + Habilitar opção '--first-parent' Data do Commit Topologicamente BRANCHES LOCAIS Navegar para HEAD - Habilitar opção '--first-parent' Criar Branch Abrir em {0} Abrir em Ferramentas Externas diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index 27a2d360..a1c40764 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -22,7 +22,9 @@ Отслеживание ветки: Отслеживание внешней ветки OpenAI Ассистент + ПЕРЕСОЗДАТЬ Использовать OpenAI для создания сообщения о ревизии + ПРИМЕНИТЬ КАК СООБЩЕНИЕ РЕВИЗИИ Исправить Ошибка Выдает ошибки и отказывается применять исправление @@ -37,6 +39,10 @@ Предупреждать Выдавать предупреждения о нескольких таких ошибках, но применять Пробел: + Отложить + Удалить после применения + Восстановить изменения индекса + Отложенный: Архивировать... Сохранить архив в: Выберите путь к архивному файлу @@ -100,6 +106,7 @@ Локальное имя: Имя репозитория. (необязательно). Родительский каталог: + Инициализировать и обновить подмодуль Адрес репозитория: ЗАКРЫТЬ Редактор @@ -131,7 +138,7 @@ АВТОР ИЗМЕНЁННЫЙ ДОЧЕРНИЙ - ИСПОЛНИТЕЛЬ + РЕВИЗОР(ИСПОЛНИТЕЛЬ) Найти все ветки с этой ревизией ВЕТКИ С ЭТОЙ РЕВИЗИЕЙ Отображаются только первые 100 изменений. Смотрите все изменения на вкладке ИЗМЕНЕНИЯ. @@ -154,15 +161,16 @@ Диапазон: Ревизия Репозиторий + Ждать для выполения выхода Адрес электронной почты Адрес электронной почты GIT - Автоматическое скачивание изменений + Автоматическая загрузка изменений Минут(а/ы) Внешний репозиторий по умолчанию ОТСЛЕЖИВАНИЕ ПРОБЛЕМ - Добавить пример правила для тем в Gitee - Добавить пример правила запроса скачивания из Gitee + Добавить пример правила для тем в Gitea + Добавить пример правила запроса скачивания из Gitea Добавить пример правила для Git Добавить пример правила Jira Добавить пример правила выдачи GitLab @@ -203,6 +211,7 @@ Отложить и применить повторно Имя новой ветки: Введите имя ветки. + Пробелы будут заменены на тире. Создать локальную ветку Создать метку... Новая метка у: @@ -226,8 +235,11 @@ Вы пытаетесь удалить несколько веток одновременно. Обязательно перепроверьте, прежде чем предпринимать какие-либо действия! Удалить внешний репозиторий Внешний репозиторий: + Path: Цель: + Все дочерние элементы будут удалены из списка. Подтвердите удаление группы + Будет удалён из списка. На диске останется. Подтвердите удаление репозитория Удалить подмодуль Путь подмодуля: @@ -567,6 +579,7 @@ Скрыть в графе ревизии Фильтр в графе ревизии ОТФИЛЬТРОВАНО: + Включить опцию --first-parent РАСПОЛОЖЕНИЕ Горизонтально Вертикально @@ -575,8 +588,8 @@ Топологически ЛОКАЛЬНЫЕ ВЕТКИ Навигация по ГОЛОВЕ (HEAD) - Включить опцию --first-parent Создать ветку + ОЧИСТКА УВЕДОМЛЕНИЙ Выделять только текущую ветку на графике Открыть в {0} Открыть в расширенном инструменте @@ -585,7 +598,7 @@ ДОБАВИТЬ ВНЕШНИЙ РЕПОЗИТОРИЙ Поиск ревизии Автор - исполнитель + Ревизор Файл Сообщение SHA @@ -645,6 +658,8 @@ Подготовленные так и неподготовленные изменения выбранных файлов будут сохранены!!! ЗАПУСК Отложить + Автоматически восстанавливать после откладывания + Ваши рабочие файлы остаются неизменными, но отложенные сохранятся. Включить неотслеживаемые файлы Хранить отложенные файлы Сообщение: @@ -660,7 +675,7 @@ ОТЛОЖЕННЫЕ Статистика РЕВИЗИИ - ИСПОЛНИТЕЛИ + РЕВИЗОРЫ(ИСПОЛНИТЕЛИ) МЕСЯЦ НЕДЕЛЯ РЕВИЗИИ: @@ -722,6 +737,7 @@ ВКЛЮЧИТЬ НЕОТСЛЕЖИВАЕМЫЕ ФАЙЛЫ НЕТ ПОСЛЕДНИХ ВХОДНЫХ СООБЩЕНИЙ НЕТ ШАБЛОНОВ РЕВИЗИИ + Завершение работы ПОДГОТОВЛЕННЫЕ СНЯТЬ ПОДГОТОВЛЕННЫЙ СНЯТЬ ВСЕ ПОДГОТОВЛЕННЫЕ diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 66d1f37e..4db909c9 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -59,6 +59,7 @@ 与当前HEAD比较 与本地工作树比较 复制分支名 + 自定义操作 删除 ${0}$... 删除选中的 {0} 个分支 放弃所有更改 @@ -155,12 +156,14 @@ 模板内容 : 自定义操作 命令行参数 : - 请使用${REPO}代替仓库路径,${SHA}代替提交哈希 + 请使用${REPO}代替仓库路径,${BRANCH}代替选中的分支,${SHA}代替提交哈希 可执行文件路径 : 名称 : 作用目标 : + 选中的分支 选中的提交 仓库 + 等待操作执行完成 电子邮箱 邮箱地址 GIT配置 @@ -577,6 +580,7 @@ 不指定 在提交列表中隐藏 使用其对提交列表过滤 + 启用 --first-parent 过滤选项 布局方式 水平排布 竖直排布 @@ -585,8 +589,8 @@ 按拓扑排序 本地分支 定位HEAD - 启用 --first-parent 过滤选项 新建分支 + 清空通知列表 提交路线图中仅高亮显示当前分支 在 {0} 中打开 使用外部工具打开 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index f8255947..a3fe5c19 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -39,10 +39,10 @@ 警告 套用修補檔,輸出關於空白字元的警告 空白字元處理: - 套用擱置 - 在成功套用后捨棄擱置 - 恢復索引中已暫存的變更 - 已選擇擱置 : + 套用擱置變更 + 套用擱置變更後刪除 + 還原索引中已暫存的變更 (--index) + 已選擇擱置變更: 封存 (archive)... 封存檔案路徑: 選擇封存檔案的儲存路徑 @@ -59,6 +59,7 @@ 與目前 HEAD 比較 與本機工作區比較 複製分支名稱 + 自訂動作 刪除 ${0}$... 刪除所選的 {0} 個分支 捨棄所有變更 @@ -106,7 +107,7 @@ 本機存放庫名稱: 本機存放庫目錄的名稱,選填。 父級目錄: - 初始化並複製子模組 + 初始化並更新子模組 遠端存放庫: 關閉 提交訊息編輯器 @@ -155,12 +156,14 @@ 範本內容: 自訂動作 指令參數: - 使用 ${REPO} 表示存放庫路徑、${SHA} 表示所選的提交編號 + 使用 ${REPO} 表示存放庫路徑、${BRANCH} 表示所選的分支、${SHA} 表示所選的提交編號 可執行檔案路徑: 名稱: 執行範圍: + 選取的分支 選取的提交 存放庫 + 等待自訂動作執行結束 電子郵件 電子郵件地址 Git 設定 @@ -237,7 +240,7 @@ 目標: 所有子節點都會從清單中移除。 刪除群組確認 - 只會從清單中移除,而不會刪除磁碟中的檔案! + 只會從清單中移除,而不會刪除磁碟中的檔案! 刪除存放庫確認 刪除子模組確認 子模組路徑: @@ -262,7 +265,7 @@ 交換比對雙方 語法上色 自動換行 - 啟用基於變更區塊的導航 + 區塊切換上/下一個差異 使用外部合併工具檢視 顯示檔案的全部內容 減少可見的行數 @@ -409,7 +412,7 @@ 合併操作進行中。 正在處理 重定基底 (rebase) 操作進行中。 - 当前停止于 + 目前停止於 復原提交操作進行中。 正在復原提交 互動式重定基底 @@ -422,8 +425,8 @@ 合併分支 目標分支: 合併方式: - 合併目標: - 合併(多目標) + 合併來源: + 合併 (多個來源) 提交變更 合併策略: 目標列表: @@ -576,17 +579,18 @@ 取消指定 在提交列表中隱藏 以其篩選提交列表 - 佈局方式 + 啟用 [--first-parent] 選項 + 版面配置 橫向顯示 縱向顯示 提交顯示順序 - 依提交時間排序 + 依時間排序 依拓撲排序 本機分支 回到 HEAD - 啟用 [--first-parent] 選項 新增分支 - 提交圖表中僅高亮顯示目前分支 + 清除所有通知 + 在提交路線圖中僅對目前分支上色 在 {0} 中開啟 使用外部工具開啟 重新載入 @@ -612,7 +616,7 @@ 依名稱降序 排序 在終端機中開啟 - 在提交清單中使用相對時間 + 在提交列表中使用相對時間 工作區列表 新增工作區 清理 @@ -652,8 +656,8 @@ SSH 金鑰檔案 開 始 擱置變更 (stash) - 暫存後自動復原工作區 - 工作區檔案保持未修改,但暫存內容已儲存。 + 擱置變更後自動復原工作區 + 工作區檔案保持未修改,但擱置內容已儲存。 包含未追蹤的檔案 保留已暫存的變更 擱置變更訊息: @@ -725,7 +729,7 @@ 提交並推送 歷史輸入/範本 觸發點擊事件 - 提交(修改現有提交) + 提交 (修改原始提交) 自動暫存全部變更並提交 未包含任何檔案變更! 您是否仍要提交 (--allow-empty)? 檢測到衝突 diff --git a/src/SourceGit.csproj b/src/SourceGit.csproj index af8fa5b1..7cceee7e 100644 --- a/src/SourceGit.csproj +++ b/src/SourceGit.csproj @@ -41,19 +41,19 @@ - - - - - + + + + + - - + + diff --git a/src/ViewModels/BranchTreeNode.cs b/src/ViewModels/BranchTreeNode.cs index 698dc7ad..5c42f729 100644 --- a/src/ViewModels/BranchTreeNode.cs +++ b/src/ViewModels/BranchTreeNode.cs @@ -40,9 +40,9 @@ namespace SourceGit.ViewModels get => Backend is Models.Branch; } - public FontWeight NameFontWeight + public bool IsCurrent { - get => Backend is Models.Branch { IsCurrent: true } ? FontWeight.Bold : FontWeight.Regular; + get => Backend is Models.Branch { IsCurrent: true }; } public string Tooltip diff --git a/src/ViewModels/ExecuteCustomAction.cs b/src/ViewModels/ExecuteCustomAction.cs index 920b9f43..8e34379f 100644 --- a/src/ViewModels/ExecuteCustomAction.cs +++ b/src/ViewModels/ExecuteCustomAction.cs @@ -10,13 +10,26 @@ namespace SourceGit.ViewModels private set; } - public ExecuteCustomAction(Repository repo, Models.CustomAction action, Models.Commit commit) + public ExecuteCustomAction(Repository repo, Models.CustomAction action) { _repo = repo; _args = action.Arguments.Replace("${REPO}", _repo.FullPath); - if (commit != null) - _args = _args.Replace("${SHA}", commit.SHA); + CustomAction = action; + View = new Views.ExecuteCustomAction() { DataContext = this }; + } + public ExecuteCustomAction(Repository repo, Models.CustomAction action, Models.Branch branch) + { + _repo = repo; + _args = action.Arguments.Replace("${REPO}", _repo.FullPath).Replace("${BRANCH}", branch.FriendlyName); + CustomAction = action; + View = new Views.ExecuteCustomAction() { DataContext = this }; + } + + public ExecuteCustomAction(Repository repo, Models.CustomAction action, Models.Commit commit) + { + _repo = repo; + _args = action.Arguments.Replace("${REPO}", _repo.FullPath).Replace("${SHA}", commit.SHA); CustomAction = action; View = new Views.ExecuteCustomAction() { DataContext = this }; } @@ -28,7 +41,11 @@ namespace SourceGit.ViewModels return Task.Run(() => { - Commands.ExecuteCustomAction.Run(_repo.FullPath, CustomAction.Executable, _args, SetProgressDescription); + if (CustomAction.WaitForExit) + Commands.ExecuteCustomAction.RunAndWait(_repo.FullPath, CustomAction.Executable, _args, SetProgressDescription); + else + Commands.ExecuteCustomAction.Run(_repo.FullPath, CustomAction.Executable, _args); + CallUIThread(() => _repo.SetWatcherEnabled(true)); return true; }); diff --git a/src/ViewModels/LauncherPage.cs b/src/ViewModels/LauncherPage.cs index b8138aca..63cb52af 100644 --- a/src/ViewModels/LauncherPage.cs +++ b/src/ViewModels/LauncherPage.cs @@ -45,6 +45,11 @@ namespace SourceGit.ViewModels _data = repo; } + public void ClearNotifications() + { + Notifications.Clear(); + } + public void CopyPath() { if (_node.IsRepository) diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index 44de8c35..19e48b13 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -826,6 +826,15 @@ namespace SourceGit.ViewModels Task.Run(RefreshCommits); } + public void RemoveHistoriesFilter(Models.Filter filter) + { + if (_settings.HistoriesFilters.Remove(filter)) + { + HistoriesFilterMode = _settings.HistoriesFilters.Count > 0 ? _settings.HistoriesFilters[0].Mode : Models.FilterMode.None; + RefreshHistoriesFilters(true); + } + } + public void UpdateBranchNodeIsExpanded(BranchTreeNode node) { if (_settings == null || !string.IsNullOrWhiteSpace(_filter)) @@ -1439,7 +1448,7 @@ namespace SourceGit.ViewModels item.Click += (_, e) => { if (CanCreatePopup()) - ShowAndStartPopup(new ExecuteCustomAction(this, dup, null)); + ShowAndStartPopup(new ExecuteCustomAction(this, dup)); e.Handled = true; }; @@ -1698,6 +1707,7 @@ namespace SourceGit.ViewModels menu.Items.Add(createBranch); menu.Items.Add(createTag); menu.Items.Add(new MenuItem() { Header = "-" }); + TryToAddCustomActionsToBranchContextMenu(menu, branch); if (!IsBare) { @@ -1968,7 +1978,9 @@ namespace SourceGit.ViewModels menu.Items.Add(new MenuItem() { Header = "-" }); menu.Items.Add(archive); menu.Items.Add(new MenuItem() { Header = "-" }); + TryToAddCustomActionsToBranchContextMenu(menu, branch); menu.Items.Add(copy); + return menu; } @@ -2319,6 +2331,43 @@ namespace SourceGit.ViewModels return null; } + private void TryToAddCustomActionsToBranchContextMenu(ContextMenu menu, Models.Branch branch) + { + var actions = new List(); + foreach (var action in Settings.CustomActions) + { + if (action.Scope == Models.CustomActionScope.Branch) + actions.Add(action); + } + + if (actions.Count == 0) + return; + + var custom = new MenuItem(); + custom.Header = App.Text("BranchCM.CustomAction"); + custom.Icon = App.CreateMenuIcon("Icons.Action"); + + foreach (var action in actions) + { + var dup = action; + var item = new MenuItem(); + item.Icon = App.CreateMenuIcon("Icons.Action"); + item.Header = dup.Name; + item.Click += (_, e) => + { + if (CanCreatePopup()) + ShowAndStartPopup(new ExecuteCustomAction(this, dup, branch)); + + e.Handled = true; + }; + + custom.Items.Add(item); + } + + menu.Items.Add(custom); + menu.Items.Add(new MenuItem() { Header = "-" }); + } + private void UpdateCurrentRevisionFilesForSearchSuggestion() { _revisionFiles.Clear(); diff --git a/src/ViewModels/WorkingCopy.cs b/src/ViewModels/WorkingCopy.cs index ecb3c0d6..7ac13dc2 100644 --- a/src/ViewModels/WorkingCopy.cs +++ b/src/ViewModels/WorkingCopy.cs @@ -431,10 +431,7 @@ namespace SourceGit.ViewModels { var toolType = Preferences.Instance.ExternalMergeToolType; var toolPath = Preferences.Instance.ExternalMergeToolPath; - - _repo.SetWatcherEnabled(false); await Task.Run(() => Commands.MergeTool.OpenForMerge(_repo.FullPath, toolType, toolPath, change.Path)); - _repo.SetWatcherEnabled(true); } public void ContinueMerge() diff --git a/src/Views/BranchTree.axaml b/src/Views/BranchTree.axaml index 0ebcdf20..b7ac725b 100644 --- a/src/Views/BranchTree.axaml +++ b/src/Views/BranchTree.axaml @@ -57,11 +57,17 @@ IsExpanded="{Binding IsExpanded}"/> - + + + + + + + + - + - + diff --git a/src/Views/Launcher.axaml.cs b/src/Views/Launcher.axaml.cs index b5f09d79..e3140191 100644 --- a/src/Views/Launcher.axaml.cs +++ b/src/Views/Launcher.axaml.cs @@ -120,6 +120,13 @@ namespace SourceGit.Views return; } + // Ctrl+Q quits the application (macOS use hotkeys in system menu bar) + if (!OperatingSystem.IsMacOS() && e.KeyModifiers == KeyModifiers.Control && e.Key == Key.Q) + { + App.Quit(0); + return; + } + if (e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control)) { if (e.Key == Key.W) @@ -146,13 +153,6 @@ namespace SourceGit.Views return; } - if (e.Key == Key.Q) - { - App.Quit(0); - e.Handled = true; - return; - } - if ((OperatingSystem.IsMacOS() && e.KeyModifiers.HasFlag(KeyModifiers.Alt) && e.Key == Key.Right) || (!OperatingSystem.IsMacOS() && !e.KeyModifiers.HasFlag(KeyModifiers.Shift) && e.Key == Key.Tab)) { diff --git a/src/Views/LauncherPage.axaml b/src/Views/LauncherPage.axaml index ee4c77eb..81f504c1 100644 --- a/src/Views/LauncherPage.axaml +++ b/src/Views/LauncherPage.axaml @@ -53,7 +53,7 @@ PointerPressed="OnMaskClicked" IsVisible="{Binding Popup, Converter={x:Static ObjectConverters.IsNotNull}}"/> - + + + + - + diff --git a/src/Views/LauncherTabBar.axaml b/src/Views/LauncherTabBar.axaml index c957d134..9b748e91 100644 --- a/src/Views/LauncherTabBar.axaml +++ b/src/Views/LauncherTabBar.axaml @@ -4,11 +4,17 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:vm="using:SourceGit.ViewModels" xmlns:c="using:SourceGit.Converters" + xmlns:v="using:SourceGit.Views" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="SourceGit.Views.LauncherTabBar" - x:DataType="vm:Launcher"> - - + x:DataType="vm:Launcher" + x:Name="ThisControl"> + + @@ -96,11 +102,11 @@ - - - + + diff --git a/src/Views/LauncherTabBar.axaml.cs b/src/Views/LauncherTabBar.axaml.cs index 129ce892..12bca91f 100644 --- a/src/Views/LauncherTabBar.axaml.cs +++ b/src/Views/LauncherTabBar.axaml.cs @@ -10,6 +10,15 @@ namespace SourceGit.Views { public partial class LauncherTabBar : UserControl { + public static readonly StyledProperty IsScrollerVisibleProperty = + AvaloniaProperty.Register(nameof(IsScrollerVisible)); + + public bool IsScrollerVisible + { + get => GetValue(IsScrollerVisibleProperty); + set => SetValue(IsScrollerVisibleProperty, value); + } + public LauncherTabBar() { InitializeComponent(); @@ -43,7 +52,7 @@ namespace SourceGit.Views if (containerEndX < startX || containerEndX > endX) continue; - if (OuterNewTabBtn.IsVisible && i == count - 1) + if (IsScrollerVisible && i == count - 1) break; var separatorX = containerEndX - startX + LauncherTabsScroller.Bounds.X; @@ -143,23 +152,7 @@ namespace SourceGit.Views private void OnTabsLayoutUpdated(object _1, EventArgs _2) { - if (LauncherTabsScroller.Extent.Width > LauncherTabsScroller.Viewport.Width) - { - LeftScrollIndicator.IsVisible = true; - LeftScrollIndicator.IsEnabled = LauncherTabsScroller.Offset.X > 0; - RightScrollIndicator.IsVisible = true; - RightScrollIndicator.IsEnabled = LauncherTabsScroller.Offset.X < LauncherTabsScroller.Extent.Width - LauncherTabsScroller.Viewport.Width; - InnerNewTabBtn.IsVisible = false; - OuterNewTabBtn.IsVisible = true; - } - else - { - LeftScrollIndicator.IsVisible = false; - RightScrollIndicator.IsVisible = false; - InnerNewTabBtn.IsVisible = true; - OuterNewTabBtn.IsVisible = false; - } - + SetCurrentValue(IsScrollerVisibleProperty, LauncherTabsScroller.Extent.Width > LauncherTabsScroller.Viewport.Width); InvalidateVisual(); } @@ -255,6 +248,15 @@ namespace SourceGit.Views e.Handled = true; } + private void OnGotoSelectedPage(object sender, LauncherTabSelectedEventArgs e) + { + if (DataContext is ViewModels.Launcher vm) + vm.ActivePage = e.Page; + + PageSelector.Flyout?.Hide(); + e.Handled = true; + } + private bool _pressedTab = false; private Point _pressedTabPosition = new Point(); private bool _startDragTab = false; diff --git a/src/Views/LauncherTabsSelector.axaml b/src/Views/LauncherTabsSelector.axaml new file mode 100644 index 00000000..109a2ce7 --- /dev/null +++ b/src/Views/LauncherTabsSelector.axaml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/LauncherTabsSelector.axaml.cs b/src/Views/LauncherTabsSelector.axaml.cs new file mode 100644 index 00000000..01c5fa0d --- /dev/null +++ b/src/Views/LauncherTabsSelector.axaml.cs @@ -0,0 +1,120 @@ +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/RemoveWorktree.axaml b/src/Views/RemoveWorktree.axaml index 6d7ea914..736e6e40 100644 --- a/src/Views/RemoveWorktree.axaml +++ b/src/Views/RemoveWorktree.axaml @@ -18,8 +18,8 @@ - - + + diff --git a/src/Views/Repository.axaml b/src/Views/Repository.axaml index 74f628c6..30180f7d 100644 --- a/src/Views/Repository.axaml +++ b/src/Views/Repository.axaml @@ -372,8 +372,8 @@ - - + + @@ -685,7 +685,11 @@ - + + + diff --git a/src/Views/Repository.axaml.cs b/src/Views/Repository.axaml.cs index bb359040..53bb2d53 100644 --- a/src/Views/Repository.axaml.cs +++ b/src/Views/Repository.axaml.cs @@ -519,5 +519,13 @@ namespace SourceGit.Views e.Handled = true; } + + private void OnRemoveSelectedHistoriesFilter(object sender, RoutedEventArgs e) + { + if (DataContext is ViewModels.Repository repo && sender is Button { DataContext: Models.Filter filter}) + repo.RemoveHistoriesFilter(filter); + + e.Handled = true; + } } } diff --git a/src/Views/RepositoryConfigure.axaml b/src/Views/RepositoryConfigure.axaml index 5e9374f9..6b7cdc12 100644 --- a/src/Views/RepositoryConfigure.axaml +++ b/src/Views/RepositoryConfigure.axaml @@ -5,7 +5,6 @@ xmlns:m="using:SourceGit.Models" xmlns:vm="using:SourceGit.ViewModels" xmlns:v="using:SourceGit.Views" - xmlns:ac="using:Avalonia.Controls.Converters" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="SourceGit.Views.RepositoryConfigure" x:DataType="vm:RepositoryConfigure" @@ -341,7 +340,7 @@ - + @@ -412,20 +411,12 @@ - - - - - - - - - + + + + + + @@ -439,6 +430,8 @@ + + diff --git a/src/Views/TextDiffView.axaml.cs b/src/Views/TextDiffView.axaml.cs index 15a2a212..28fe81a8 100644 --- a/src/Views/TextDiffView.axaml.cs +++ b/src/Views/TextDiffView.axaml.cs @@ -970,8 +970,15 @@ namespace SourceGit.Views } var lines = GetLines(); - var startIdx = Math.Min(selection.StartPosition.Line - 1, lines.Count - 1); - var endIdx = Math.Min(selection.EndPosition.Line - 1, lines.Count - 1); + + var startPosition = selection.StartPosition; + var endPosition = selection.EndPosition; + + if (startPosition.Location > endPosition.Location) + (startPosition, endPosition) = (endPosition, startPosition); + + var startIdx = Math.Min(startPosition.Line - 1, lines.Count - 1); + var endIdx = Math.Min(endPosition.Line - 1, lines.Count - 1); if (startIdx == endIdx) { @@ -995,15 +1002,15 @@ namespace SourceGit.Views line.Type == Models.TextDiffLineType.None) continue; - if (i == startIdx && selection.StartPosition.Column > 1) + if (i == startIdx && startPosition.Column > 1) { - builder.AppendLine(line.Content.Substring(selection.StartPosition.Column - 1)); + builder.AppendLine(line.Content.Substring(startPosition.Column - 1)); continue; } - if (i == endIdx && selection.EndPosition.Column < line.Content.Length) + if (i == endIdx && endPosition.Column < line.Content.Length) { - builder.AppendLine(line.Content.Substring(0, selection.EndPosition.Column)); + builder.AppendLine(line.Content.Substring(0, endPosition.Column)); continue; }