From 24dde77548ef07432e16665e6d5a7eee1164e06f Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 19 Aug 2024 12:49:04 +0800 Subject: [PATCH 01/93] refactor: rewrite SourceGit.App --- src/App.axaml.cs | 223 ++++++++++++++------------------- src/ViewModels/CommitDetail.cs | 6 +- src/ViewModels/Histories.cs | 6 +- src/ViewModels/Repository.cs | 33 ++--- src/ViewModels/WorkingCopy.cs | 37 +++--- src/Views/Launcher.axaml.cs | 2 +- 6 files changed, 121 insertions(+), 186 deletions(-) diff --git a/src/App.axaml.cs b/src/App.axaml.cs index a1eaee6a..1015ee34 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -15,6 +15,7 @@ using Avalonia.Data.Core.Plugins; using Avalonia.Markup.Xaml; using Avalonia.Media; using Avalonia.Media.Fonts; +using Avalonia.Platform.Storage; using Avalonia.Styling; using Avalonia.Threading; @@ -41,6 +42,13 @@ namespace SourceGit public partial class App : Application { + public static readonly SimpleCommand OpenPreferenceCommand = new SimpleCommand(() => OpenDialog(new Views.Preference())); + public static readonly SimpleCommand OpenHotkeysCommand = new SimpleCommand(() => OpenDialog(new Views.Hotkeys())); + public static readonly SimpleCommand OpenAppDataDirCommand = new SimpleCommand(() => Native.OS.OpenInFileManager(Native.OS.DataDir)); + public static readonly SimpleCommand OpenAboutCommand = new SimpleCommand(() => OpenDialog(new Views.About())); + public static readonly SimpleCommand CheckForUpdateCommand = new SimpleCommand(() => Check4Update(true)); + public static readonly SimpleCommand QuitCommand = new SimpleCommand(() => Quit(0)); + [STAThread] public static void Main(string[] args) { @@ -89,47 +97,36 @@ namespace SourceGit return builder; } - public static readonly SimpleCommand OpenPreferenceCommand = new SimpleCommand(() => + public override void Initialize() { - var toplevel = GetTopLevel() as Window; - if (toplevel == null) - return; + AvaloniaXamlLoader.Load(this); - var dialog = new Views.Preference(); - dialog.ShowDialog(toplevel); - }); + var pref = ViewModels.Preference.Instance; + SetLocale(pref.Locale); + SetTheme(pref.Theme, pref.ThemeOverrides); + } - public static readonly SimpleCommand OpenHotkeysCommand = new SimpleCommand(() => + public override void OnFrameworkInitializationCompleted() { - var toplevel = GetTopLevel() as Window; - if (toplevel == null) - return; + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + BindingPlugins.DataValidators.RemoveAt(0); - var dialog = new Views.Hotkeys(); - dialog.ShowDialog(toplevel); - }); + if (TryLaunchedAsCoreEditor(desktop)) + return; - public static readonly SimpleCommand OpenAppDataDirCommand = new SimpleCommand(() => + if (TryLaunchedAsAskpass(desktop)) + return; + + TryLaunchedAsNormal(desktop); + } + } + + public static void OpenDialog(Window window) { - Native.OS.OpenInFileManager(Native.OS.DataDir); - }); - - public static readonly SimpleCommand OpenAboutCommand = new SimpleCommand(() => - { - var toplevel = GetTopLevel() as Window; - if (toplevel == null) - return; - - var dialog = new Views.About(); - dialog.ShowDialog(toplevel); - }); - - public static readonly SimpleCommand CheckForUpdateCommand = new SimpleCommand(() => - { - Check4Update(true); - }); - - public static readonly SimpleCommand QuitCommand = new SimpleCommand(() => Quit(0)); + if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + window.ShowDialog(desktop.MainWindow); + } public static void RaiseException(string context, string message) { @@ -258,17 +255,74 @@ namespace SourceGit return icon; } - public static TopLevel GetTopLevel() + public static IStorageProvider GetStorageProvider() { if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + return desktop.MainWindow.StorageProvider; + + return null; + } + + public static ViewModels.Launcher GetLauncer() + { + return Current is App app ? app._launcher : null; + } + + public static ViewModels.Repository FindOpenedRepository(string repoPath) + { + if (Current is App app && app._launcher != null) { - return desktop.MainWindow; + foreach (var page in app._launcher.Pages) + { + var id = page.Node.Id.Replace("\\", "/"); + if (id == repoPath && page.Data is ViewModels.Repository repo) + return repo; + } } return null; } - public static void Check4Update(bool manually = false) + public static void Quit(int exitCode) + { + if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + desktop.MainWindow?.Close(); + desktop.Shutdown(exitCode); + } + else + { + Environment.Exit(exitCode); + } + } + + private static void LogException(Exception ex) + { + if (ex == null) + return; + + var builder = new StringBuilder(); + builder.Append($"Crash::: {ex.GetType().FullName}: {ex.Message}\n\n"); + builder.Append("----------------------------\n"); + builder.Append($"Version: {Assembly.GetExecutingAssembly().GetName().Version}\n"); + builder.Append($"OS: {Environment.OSVersion.ToString()}\n"); + builder.Append($"Framework: {AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName}\n"); + builder.Append($"Source: {ex.Source}\n"); + builder.Append($"---------------------------\n\n"); + builder.Append(ex.StackTrace); + while (ex.InnerException != null) + { + ex = ex.InnerException; + builder.Append($"\n\nInnerException::: {ex.GetType().FullName}: {ex.Message}\n"); + builder.Append(ex.StackTrace); + } + + var time = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"); + var file = Path.Combine(Native.OS.DataDir, $"crash_{time}.log"); + File.WriteAllText(file, builder.ToString()); + } + + private static void Check4Update(bool manually = false) { Task.Run(async () => { @@ -309,104 +363,11 @@ namespace SourceGit }); } - public static ViewModels.Launcher GetLauncer() - { - return Current is App app ? app._launcher : null; - } - - public static ViewModels.Repository FindOpenedRepository(string repoPath) - { - if (Current is App app && app._launcher != null) - { - foreach (var page in app._launcher.Pages) - { - var id = page.Node.Id.Replace("\\", "/"); - if (id == repoPath && page.Data is ViewModels.Repository repo) - return repo; - } - } - - return null; - } - - public static void Quit(int exitCode) - { - if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - desktop.MainWindow?.Close(); - desktop.Shutdown(exitCode); - } - else - { - Environment.Exit(exitCode); - } - } - - public override void Initialize() - { - AvaloniaXamlLoader.Load(this); - - var pref = ViewModels.Preference.Instance; - - SetLocale(pref.Locale); - SetTheme(pref.Theme, pref.ThemeOverrides); - } - - public override void OnFrameworkInitializationCompleted() - { - if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - BindingPlugins.DataValidators.RemoveAt(0); - - if (TryLaunchedAsCoreEditor(desktop)) - return; - - if (TryLaunchedAsAskpass(desktop)) - return; - - TryLaunchedAsNormal(desktop); - } - } - - private static void LogException(Exception ex) - { - if (ex == null) - return; - - var builder = new StringBuilder(); - builder.Append($"Crash::: {ex.GetType().FullName}: {ex.Message}\n\n"); - builder.Append("----------------------------\n"); - builder.Append($"Version: {Assembly.GetExecutingAssembly().GetName().Version}\n"); - builder.Append($"OS: {Environment.OSVersion.ToString()}\n"); - builder.Append($"Framework: {AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName}\n"); - builder.Append($"Source: {ex.Source}\n"); - builder.Append($"---------------------------\n\n"); - builder.Append(ex.StackTrace); - while (ex.InnerException != null) - { - ex = ex.InnerException; - builder.Append($"\n\nInnerException::: {ex.GetType().FullName}: {ex.Message}\n"); - builder.Append(ex.StackTrace); - } - - var time = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"); - var file = Path.Combine(Native.OS.DataDir, $"crash_{time}.log"); - File.WriteAllText(file, builder.ToString()); - } - private static void ShowSelfUpdateResult(object data) { Dispatcher.UIThread.Post(() => { - if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: not null } desktop) - { - var dialog = new Views.SelfUpdate() - { - DataContext = new ViewModels.SelfUpdate() { Data = data } - }; - - dialog.Show(desktop.MainWindow); - } + OpenDialog(new Views.SelfUpdate() { DataContext = new ViewModels.SelfUpdate() { Data = data } }); }); } diff --git a/src/ViewModels/CommitDetail.cs b/src/ViewModels/CommitDetail.cs index f6c01259..1462d701 100644 --- a/src/ViewModels/CommitDetail.cs +++ b/src/ViewModels/CommitDetail.cs @@ -380,12 +380,12 @@ namespace SourceGit.ViewModels saveAs.IsEnabled = file.Type == Models.ObjectType.Blob; saveAs.Click += async (_, ev) => { - var topLevel = App.GetTopLevel(); - if (topLevel == null) + var storageProvider = App.GetStorageProvider(); + if (storageProvider == null) return; var options = new FolderPickerOpenOptions() { AllowMultiple = false }; - var selected = await topLevel.StorageProvider.OpenFolderPickerAsync(options); + var selected = await storageProvider.OpenFolderPickerAsync(options); if (selected.Count == 1) { var saveTo = Path.Combine(selected[0].Path.LocalPath, Path.GetFileName(file.Path)); diff --git a/src/ViewModels/Histories.cs b/src/ViewModels/Histories.cs index 86a211fa..ad597985 100644 --- a/src/ViewModels/Histories.cs +++ b/src/ViewModels/Histories.cs @@ -428,12 +428,12 @@ namespace SourceGit.ViewModels saveToPatch.Header = App.Text("CommitCM.SaveAsPatch"); saveToPatch.Click += async (_, e) => { - var topLevel = App.GetTopLevel(); - if (topLevel == null) + var storageProvider = App.GetStorageProvider(); + if (storageProvider == null) return; var options = new FolderPickerOpenOptions() { AllowMultiple = false }; - var selected = await topLevel.StorageProvider.OpenFolderPickerAsync(options); + var selected = await storageProvider.OpenFolderPickerAsync(options); if (selected.Count == 1) { var succ = new Commands.FormatPatch(_repo.FullPath, commit.SHA, selected[0].Path.LocalPath).Exec(); diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index e304be0b..82a03f64 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -940,9 +940,7 @@ namespace SourceGit.ViewModels }; } - var launcher = App.GetTopLevel().DataContext as Launcher; - if (launcher != null) - launcher.OpenRepositoryInTab(node, null); + App.GetLauncer()?.OpenRepositoryInTab(node, null); } public void AddWorktree() @@ -971,9 +969,7 @@ namespace SourceGit.ViewModels }; } - var launcher = App.GetTopLevel().DataContext as Launcher; - if (launcher != null) - launcher.OpenRepositoryInTab(node, null); + App.GetLauncer()?.OpenRepositoryInTab(node, null); } public ContextMenu CreateContextMenuForGitFlow() @@ -1130,12 +1126,8 @@ namespace SourceGit.ViewModels { locks.Click += (_, e) => { - var topLevel = App.GetTopLevel() as Window; - if (topLevel == null) - return; - var dialog = new Views.LFSLocks() { DataContext = new LFSLocks(_fullpath, _remotes[0].Name) }; - dialog.Show(topLevel); + App.OpenDialog(dialog); e.Handled = true; }; } @@ -1148,12 +1140,8 @@ namespace SourceGit.ViewModels lockRemote.Header = remoteName; lockRemote.Click += (_, e) => { - var topLevel = App.GetTopLevel() as Window; - if (topLevel == null) - return; - var dialog = new Views.LFSLocks() { DataContext = new LFSLocks(_fullpath, remoteName) }; - dialog.Show(topLevel); + App.OpenDialog(dialog); e.Handled = true; }; locks.Items.Add(lockRemote); @@ -1878,16 +1866,9 @@ namespace SourceGit.ViewModels target.Icon = App.CreateMenuIcon(b.IsCurrent ? "Icons.Check" : "Icons.Branch"); target.Click += (_, e) => { - var topLevel = App.GetTopLevel() as Window; - if (topLevel == null) - return; - - var wnd = new Views.BranchCompare() - { - DataContext = new BranchCompare(_fullpath, branch, dup) - }; - - wnd.Show(topLevel); + App.OpenDialog(new Views.BranchCompare() { + DataContext = new BranchCompare(_fullpath, branch, dup) + }); e.Handled = true; }; diff --git a/src/ViewModels/WorkingCopy.cs b/src/ViewModels/WorkingCopy.cs index e34f06c5..90e6b6c9 100644 --- a/src/ViewModels/WorkingCopy.cs +++ b/src/ViewModels/WorkingCopy.cs @@ -303,16 +303,10 @@ namespace SourceGit.ViewModels public void OpenAssumeUnchanged() { - var toplevel = App.GetTopLevel() as Window; - if (toplevel == null) - return; - - var dialog = new Views.AssumeUnchangedManager() + App.OpenDialog(new Views.AssumeUnchangedManager() { DataContext = new AssumeUnchangedManager(_repo.FullPath) - }; - - dialog.ShowDialog(toplevel); + }); } public void StashAll(bool autoStart) @@ -530,8 +524,8 @@ namespace SourceGit.ViewModels patch.Icon = App.CreateMenuIcon("Icons.Diff"); patch.Click += async (_, e) => { - var topLevel = App.GetTopLevel(); - if (topLevel == null) + var storageProvider = App.GetStorageProvider(); + if (storageProvider == null) return; var options = new FilePickerSaveOptions(); @@ -539,7 +533,7 @@ namespace SourceGit.ViewModels options.DefaultExtension = ".patch"; options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }]; - var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options); + var storageFile = await storageProvider.SaveFilePickerAsync(options); if (storageFile != null) { var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, _selectedUnstaged, true, storageFile.Path.LocalPath)); @@ -853,8 +847,8 @@ namespace SourceGit.ViewModels patch.Icon = App.CreateMenuIcon("Icons.Diff"); patch.Click += async (_, e) => { - var topLevel = App.GetTopLevel(); - if (topLevel == null) + var storageProvider = App.GetStorageProvider(); + if (storageProvider == null) return; var options = new FilePickerSaveOptions(); @@ -862,7 +856,7 @@ namespace SourceGit.ViewModels options.DefaultExtension = ".patch"; options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }]; - var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options); + var storageFile = await storageProvider.SaveFilePickerAsync(options); if (storageFile != null) { var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, _selectedUnstaged, true, storageFile.Path.LocalPath)); @@ -938,8 +932,8 @@ namespace SourceGit.ViewModels patch.Icon = App.CreateMenuIcon("Icons.Diff"); patch.Click += async (_, e) => { - var topLevel = App.GetTopLevel(); - if (topLevel == null) + var storageProvider = App.GetStorageProvider(); + if (storageProvider == null) return; var options = new FilePickerSaveOptions(); @@ -947,7 +941,7 @@ namespace SourceGit.ViewModels options.DefaultExtension = ".patch"; options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }]; - var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options); + var storageFile = await storageProvider.SaveFilePickerAsync(options); if (storageFile != null) { var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, _selectedStaged, false, storageFile.Path.LocalPath)); @@ -1085,9 +1079,8 @@ namespace SourceGit.ViewModels stash.Click += (_, e) => { if (PopupHost.CanCreatePopup()) - { PopupHost.ShowPopup(new StashChanges(_repo, _selectedStaged, false)); - } + e.Handled = true; }; @@ -1096,8 +1089,8 @@ namespace SourceGit.ViewModels patch.Icon = App.CreateMenuIcon("Icons.Diff"); patch.Click += async (_, e) => { - var topLevel = App.GetTopLevel(); - if (topLevel == null) + var storageProvider = App.GetStorageProvider(); + if (storageProvider == null) return; var options = new FilePickerSaveOptions(); @@ -1105,7 +1098,7 @@ namespace SourceGit.ViewModels options.DefaultExtension = ".patch"; options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }]; - var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options); + var storageFile = await storageProvider.SaveFilePickerAsync(options); if (storageFile != null) { var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, _selectedStaged, false, storageFile.Path.LocalPath)); diff --git a/src/Views/Launcher.axaml.cs b/src/Views/Launcher.axaml.cs index e73ad1a9..d2672c5e 100644 --- a/src/Views/Launcher.axaml.cs +++ b/src/Views/Launcher.axaml.cs @@ -59,7 +59,7 @@ namespace SourceGit.Views // Ctrl+Shift+P opens preference dialog (macOS use hotkeys in system menu bar) if (!OperatingSystem.IsMacOS() && e.KeyModifiers == (KeyModifiers.Control | KeyModifiers.Shift) && e.Key == Key.P) { - App.OpenPreferenceCommand.Execute(null); + App.OpenDialog(new Preference()); e.Handled = true; return; } From 211c2630433638a30ac0e0b614e82419267c66c2 Mon Sep 17 00:00:00 2001 From: Aikawa Yataro Date: Mon, 19 Aug 2024 07:36:51 +0000 Subject: [PATCH 02/93] fix: failed to resolve dependencies with aarch64 RPM package dependencies on aarch64 will not be resolved if library names are specified without architecture we could use `%{?_isa}` macro, however it looks like specifying library names as dependencies is generally avoided --- build/resources/rpm/SPECS/build.spec | 2 -- 1 file changed, 2 deletions(-) diff --git a/build/resources/rpm/SPECS/build.spec b/build/resources/rpm/SPECS/build.spec index 86a7cfdd..0babdb75 100644 --- a/build/resources/rpm/SPECS/build.spec +++ b/build/resources/rpm/SPECS/build.spec @@ -5,8 +5,6 @@ Summary: Open-source & Free Git Gui Client License: MIT URL: https://sourcegit-scm.github.io/ Source: https://github.com/sourcegit-scm/sourcegit/archive/refs/tags/v%_version.tar.gz -Requires: libX11.so.6 -Requires: libSM.so.6 %define _build_id_links none From 90edabeb32a6ec608f2863c73fd1079ebcca3ded Mon Sep 17 00:00:00 2001 From: Aikawa Yataro Date: Mon, 19 Aug 2024 08:02:35 +0000 Subject: [PATCH 03/93] fix: update RPM dependencies --- build/resources/rpm/SPECS/build.spec | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/resources/rpm/SPECS/build.spec b/build/resources/rpm/SPECS/build.spec index 0babdb75..ba47aed4 100644 --- a/build/resources/rpm/SPECS/build.spec +++ b/build/resources/rpm/SPECS/build.spec @@ -5,6 +5,8 @@ Summary: Open-source & Free Git Gui Client License: MIT URL: https://sourcegit-scm.github.io/ Source: https://github.com/sourcegit-scm/sourcegit/archive/refs/tags/v%_version.tar.gz +Requires: libX11 +Requires: libSM %define _build_id_links none From 6267101eaf653405545976a6a7ce565174bd65a7 Mon Sep 17 00:00:00 2001 From: Aikawa Yataro Date: Mon, 19 Aug 2024 08:04:28 +0000 Subject: [PATCH 04/93] refactor: instead of a hardcoded path, use the `-r` when creating a relative link --- build/resources/rpm/SPECS/build.spec | 2 +- build/scripts/package.linux.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/resources/rpm/SPECS/build.spec b/build/resources/rpm/SPECS/build.spec index ba47aed4..289cbe39 100644 --- a/build/resources/rpm/SPECS/build.spec +++ b/build/resources/rpm/SPECS/build.spec @@ -19,7 +19,7 @@ mkdir -p %{buildroot}/%{_bindir} mkdir -p %{buildroot}/usr/share/applications mkdir -p %{buildroot}/usr/share/icons cp -f ../../../SourceGit/* %{buildroot}/opt/sourcegit/ -ln -sf ../../opt/sourcegit/sourcegit %{buildroot}/%{_bindir} +ln -rsf %{buildroot}/opt/sourcegit/sourcegit %{buildroot}/%{_bindir} cp -r ../../_common/applications %{buildroot}/%{_datadir} cp -r ../../_common/icons %{buildroot}/%{_datadir} chmod 755 -R %{buildroot}/opt/sourcegit diff --git a/build/scripts/package.linux.sh b/build/scripts/package.linux.sh index 04309018..5eceffd6 100755 --- a/build/scripts/package.linux.sh +++ b/build/scripts/package.linux.sh @@ -60,7 +60,7 @@ mkdir -p resources/deb/usr/bin mkdir -p resources/deb/usr/share/applications mkdir -p resources/deb/usr/share/icons cp -f SourceGit/* resources/deb/opt/sourcegit -ln -sf ../../opt/sourcegit/sourcegit resources/deb/usr/bin +ln -rsf resources/deb/opt/sourcegit/sourcegit resources/deb/usr/bin cp -r resources/_common/applications resources/deb/usr/share cp -r resources/_common/icons resources/deb/usr/share sed -i -e "s/^Version:.*/Version: $VERSION/" -e "s/^Architecture:.*/Architecture: $arch/" resources/deb/DEBIAN/control From 9057b71f2d018e0e33eaeca9a300f15ca4bcc80f Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 19 Aug 2024 17:14:41 +0800 Subject: [PATCH 05/93] refactor: rewrite the font configuration (#366) * input font name directly instead of a font picker because localized font family name is not supported by Avalonia * fallback monospace font to default font * remove unused code --- src/App.JsonCodeGen.cs | 15 --------- src/App.axaml.cs | 47 +++++++++++++++++++++++++- src/Native/Linux.cs | 6 ---- src/Native/MacOS.cs | 6 ---- src/Native/Windows.cs | 7 ---- src/Resources/Styles.axaml | 6 ++-- src/Resources/Themes.axaml | 4 +++ src/ViewModels/Preference.cs | 49 +++++++++++---------------- src/Views/Avatar.cs | 3 +- src/Views/Blame.axaml | 2 +- src/Views/BranchTree.axaml | 2 +- src/Views/ChangeStatusIcon.cs | 3 +- src/Views/CommitBaseInfo.axaml | 2 +- src/Views/Histories.axaml | 2 +- src/Views/Preference.axaml | 36 +++++--------------- src/Views/Preference.axaml.cs | 60 ---------------------------------- src/Views/Repository.axaml | 4 +-- src/Views/RevisionFiles.axaml | 2 +- src/Views/Statistics.axaml.cs | 3 +- src/Views/TextDiffView.axaml | 6 ++-- 20 files changed, 96 insertions(+), 169 deletions(-) diff --git a/src/App.JsonCodeGen.cs b/src/App.JsonCodeGen.cs index 5d7b114e..8daea4f9 100644 --- a/src/App.JsonCodeGen.cs +++ b/src/App.JsonCodeGen.cs @@ -20,20 +20,6 @@ namespace SourceGit } } - public class FontFamilyConverter : JsonConverter - { - public override FontFamily Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - var name = reader.GetString(); - return new FontFamily(name); - } - - public override void Write(Utf8JsonWriter writer, FontFamily value, JsonSerializerOptions options) - { - writer.WriteStringValue(value.ToString()); - } - } - public class GridLengthConverter : JsonConverter { public override GridLength Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) @@ -54,7 +40,6 @@ namespace SourceGit IgnoreReadOnlyProperties = true, Converters = [ typeof(ColorConverter), - typeof(FontFamilyConverter), typeof(GridLengthConverter), ] )] diff --git a/src/App.axaml.cs b/src/App.axaml.cs index 1015ee34..ce6b5ced 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -104,6 +104,7 @@ namespace SourceGit var pref = ViewModels.Preference.Instance; SetLocale(pref.Locale); SetTheme(pref.Theme, pref.ThemeOverrides); + SetFonts(pref.DefaultFontFamily, pref.MonospaceFontFamily, pref.OnlyUseMonoFontInEditor); } public override void OnFrameworkInitializationCompleted() @@ -143,7 +144,10 @@ namespace SourceGit public static void SetLocale(string localeKey) { var app = Current as App; - var targetLocale = app?.Resources[localeKey] as ResourceDictionary; + if (app == null) + return; + + var targetLocale = app.Resources[localeKey] as ResourceDictionary; if (targetLocale == null || targetLocale == app._activeLocale) return; @@ -208,6 +212,46 @@ namespace SourceGit } } + public static void SetFonts(string defaultFont, string monospaceFont, bool onlyUseMonospaceFontInEditor) + { + var app = Current as App; + if (app == null) + return; + + if (app._fontsOverrides != null) + { + app.Resources.MergedDictionaries.Remove(app._fontsOverrides); + app._fontsOverrides = null; + } + + var resDic = new ResourceDictionary(); + if (!string.IsNullOrEmpty(defaultFont)) + resDic.Add("Fonts.Default", new FontFamily(defaultFont)); + + if (string.IsNullOrEmpty(monospaceFont)) + { + if (!string.IsNullOrEmpty(defaultFont)) + monospaceFont = $"fonts:SourceGit#JetBrains Mono,{defaultFont}"; + } + else + { + if (!string.IsNullOrEmpty(defaultFont) && !monospaceFont.Contains(defaultFont, StringComparison.Ordinal)) + monospaceFont = $"{monospaceFont},{defaultFont}"; + + resDic.Add("Fonts.Monospace", new FontFamily(monospaceFont)); + } + + var primary = onlyUseMonospaceFontInEditor ? defaultFont : monospaceFont; + if (!string.IsNullOrEmpty(primary)) + resDic.Add("Fonts.Primary", new FontFamily(primary)); + + if (resDic.Count > 0) + { + app.Resources.MergedDictionaries.Add(resDic); + app._fontsOverrides = resDic; + } + } + public static async void CopyText(string data) { if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) @@ -510,5 +554,6 @@ namespace SourceGit private ViewModels.Launcher _launcher = null; private ResourceDictionary _activeLocale = null; private ResourceDictionary _themeOverrides = null; + private ResourceDictionary _fontsOverrides = null; } } diff --git a/src/Native/Linux.cs b/src/Native/Linux.cs index 9d444dae..bd973f5f 100644 --- a/src/Native/Linux.cs +++ b/src/Native/Linux.cs @@ -5,7 +5,6 @@ using System.IO; using System.Runtime.Versioning; using Avalonia; -using Avalonia.Media; namespace SourceGit.Native { @@ -37,11 +36,6 @@ namespace SourceGit.Native public void SetupApp(AppBuilder builder) { - builder.With(new FontManagerOptions() - { - DefaultFamilyName = "fonts:SourceGit#JetBrains Mono", - }); - builder.With(new X11PlatformOptions() { EnableIme = true, diff --git a/src/Native/MacOS.cs b/src/Native/MacOS.cs index e034c674..8a401c38 100644 --- a/src/Native/MacOS.cs +++ b/src/Native/MacOS.cs @@ -6,7 +6,6 @@ using System.Runtime.Versioning; using System.Text; using Avalonia; -using Avalonia.Media; namespace SourceGit.Native { @@ -15,11 +14,6 @@ namespace SourceGit.Native { public void SetupApp(AppBuilder builder) { - builder.With(new FontManagerOptions() - { - DefaultFamilyName = "PingFang SC", - }); - builder.With(new MacOSPlatformOptions() { DisableDefaultApplicationMenuItems = true, diff --git a/src/Native/Windows.cs b/src/Native/Windows.cs index d8203d13..7609fe7b 100644 --- a/src/Native/Windows.cs +++ b/src/Native/Windows.cs @@ -8,7 +8,6 @@ using System.Text; using Avalonia; using Avalonia.Controls; -using Avalonia.Media; namespace SourceGit.Native { @@ -62,12 +61,6 @@ namespace SourceGit.Native public void SetupApp(AppBuilder builder) { - builder.With(new FontManagerOptions() - { - DefaultFamilyName = "Microsoft YaHei UI", - FontFallbacks = [new FontFallback { FontFamily = "Microsoft YaHei" }], - }); - // Fix drop shadow issue on Windows 10 RTL_OSVERSIONINFOEX v = new RTL_OSVERSIONINFOEX(); v.dwOSVersionInfoSize = (uint)Marshal.SizeOf(); diff --git a/src/Resources/Styles.axaml b/src/Resources/Styles.axaml index e9477212..3874060a 100644 --- a/src/Resources/Styles.axaml +++ b/src/Resources/Styles.axaml @@ -156,7 +156,7 @@ @@ -251,7 +251,7 @@ - + + + + + + + + + + - + + + - - - - - + + - - - - - + + - - + + - - - - - + + - - + + - - - - - - - - - - - - + + + + + + + diff --git a/src/Views/Repository.axaml b/src/Views/Repository.axaml index 82396bc9..22ec2d07 100644 --- a/src/Views/Repository.axaml +++ b/src/Views/Repository.axaml @@ -240,70 +240,31 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + @@ -329,74 +290,30 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/src/Views/Repository.axaml.cs b/src/Views/Repository.axaml.cs index a83d24bd..6c3c80ea 100644 --- a/src/Views/Repository.axaml.cs +++ b/src/Views/Repository.axaml.cs @@ -200,7 +200,7 @@ namespace SourceGit.Views private void OnSubmoduleContextRequested(object sender, ContextRequestedEventArgs e) { - if (sender is DataGrid { SelectedItem: Models.Submodule submodule } grid && DataContext is ViewModels.Repository repo) + if (sender is ListBox { SelectedItem: Models.Submodule submodule } grid && DataContext is ViewModels.Repository repo) { var menu = repo.CreateContextMenuForSubmodule(submodule.Path); grid.OpenContextMenu(menu); @@ -211,7 +211,7 @@ namespace SourceGit.Views private void OnDoubleTappedSubmodule(object sender, TappedEventArgs e) { - if (sender is DataGrid { SelectedItem: Models.Submodule submodule } && DataContext is ViewModels.Repository repo) + if (sender is ListBox { SelectedItem: Models.Submodule submodule } && DataContext is ViewModels.Repository repo) { repo.OpenSubmodule(submodule.Path); } @@ -221,7 +221,7 @@ namespace SourceGit.Views private void OnWorktreeContextRequested(object sender, ContextRequestedEventArgs e) { - if (sender is DataGrid { SelectedItem: Models.Worktree worktree } grid && DataContext is ViewModels.Repository repo) + if (sender is ListBox { SelectedItem: Models.Worktree worktree } grid && DataContext is ViewModels.Repository repo) { var menu = repo.CreateContextMenuForWorktree(worktree); grid.OpenContextMenu(menu); @@ -232,7 +232,7 @@ namespace SourceGit.Views private void OnDoubleTappedWorktree(object sender, TappedEventArgs e) { - if (sender is DataGrid { SelectedItem: Models.Worktree worktree } && DataContext is ViewModels.Repository repo) + if (sender is ListBox { SelectedItem: Models.Worktree worktree } && DataContext is ViewModels.Repository repo) { repo.OpenWorktree(worktree); } @@ -242,7 +242,7 @@ namespace SourceGit.Views private void OnLeftSidebarDataGridPropertyChanged(object _, AvaloniaPropertyChangedEventArgs e) { - if (e.Property == DataGrid.ItemsSourceProperty || e.Property == DataGrid.IsVisibleProperty) + if (e.Property == ListBox.ItemsSourceProperty || e.Property == ListBox.IsVisibleProperty) UpdateLeftSidebarLayout(); } @@ -266,8 +266,8 @@ namespace SourceGit.Views var remoteBranchRows = vm.IsRemoteGroupExpanded ? RemoteBranchTree.Rows.Count : 0; var desiredBranches = (localBranchRows + remoteBranchRows) * 24.0; var desiredTag = vm.IsTagGroupExpanded ? 24.0 * TagsList.Rows : 0; - var desiredSubmodule = vm.IsSubmoduleGroupExpanded ? SubmoduleList.RowHeight * vm.Submodules.Count : 0; - var desiredWorktree = vm.IsWorktreeGroupExpanded ? WorktreeList.RowHeight * vm.Worktrees.Count : 0; + var desiredSubmodule = vm.IsSubmoduleGroupExpanded ? 24.0 * vm.Submodules.Count : 0; + var desiredWorktree = vm.IsWorktreeGroupExpanded ? 24.0 * vm.Worktrees.Count : 0; var desiredOthers = desiredTag + desiredSubmodule + desiredWorktree; var hasOverflow = (desiredBranches + desiredOthers > leftHeight); diff --git a/src/Views/TagsView.axaml b/src/Views/TagsView.axaml index bcbbe358..4bafa55b 100644 --- a/src/Views/TagsView.axaml +++ b/src/Views/TagsView.axaml @@ -7,31 +7,12 @@ xmlns:vm="using:SourceGit.ViewModels" xmlns:c="using:SourceGit.Converters" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" - x:Class="SourceGit.Views.TagsView"> - - - - - - + x:Class="SourceGit.Views.TagsView"> - @@ -70,7 +51,8 @@ - diff --git a/src/Views/TagsView.axaml.cs b/src/Views/TagsView.axaml.cs index 23d31ab4..9bb20aca 100644 --- a/src/Views/TagsView.axaml.cs +++ b/src/Views/TagsView.axaml.cs @@ -282,6 +282,7 @@ namespace SourceGit.Views var tags = Tags; if (tags == null || tags.Count == 0) { + Rows = 0; Content = null; return; } From 5370526fcc57841cba4013315698b084c91c8fd2 Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 20 Aug 2024 17:05:15 +0800 Subject: [PATCH 15/93] ux: selected style for repo dashboard contents --- src/Resources/Styles.axaml | 38 +++++++++++++++++--------------------- src/Views/Repository.axaml | 12 +++++++++++- src/Views/TagsView.axaml | 8 +++++++- 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/Resources/Styles.axaml b/src/Resources/Styles.axaml index d3204fae..79479272 100644 --- a/src/Resources/Styles.axaml +++ b/src/Resources/Styles.axaml @@ -191,9 +191,6 @@ - - - - - - - + + + + + + @@ -301,6 +306,11 @@ DoubleTapped="OnDoubleTappedWorktree" PropertyChanged="OnLeftSidebarDataGridPropertyChanged" IsVisible="{Binding IsWorktreeGroupExpanded, Mode=OneWay}"> + + + diff --git a/src/Views/TagsView.axaml b/src/Views/TagsView.axaml index 4bafa55b..a66f4728 100644 --- a/src/Views/TagsView.axaml +++ b/src/Views/TagsView.axaml @@ -7,7 +7,13 @@ xmlns:vm="using:SourceGit.ViewModels" xmlns:c="using:SourceGit.Converters" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" - x:Class="SourceGit.Views.TagsView"> + x:Class="SourceGit.Views.TagsView"> + + + + Date: Tue, 20 Aug 2024 14:32:52 +0300 Subject: [PATCH 16/93] Support iTerm2 in MacOS --- src/Native/MacOS.cs | 105 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 89 insertions(+), 16 deletions(-) diff --git a/src/Native/MacOS.cs b/src/Native/MacOS.cs index 8a401c38..bdc521b4 100644 --- a/src/Native/MacOS.cs +++ b/src/Native/MacOS.cs @@ -12,6 +12,87 @@ namespace SourceGit.Native [SupportedOSPlatform("macOS")] internal class MacOS : OS.IBackend { + enum SupportedTerminals + { + NativeTerminal, + iTerm2, + } + + class Terminal + { + public SupportedTerminals Application { get; set; } + + public Terminal(SupportedTerminals application) + { + Application = application; + } + + public void Open(string dir) + { + string command = GetTerminalCommand(dir); + if (command == null) + return; + + var tmp = Path.GetTempFileName(); + File.WriteAllText(tmp, command); + + var proc = Process.Start("osascript", $"\"{tmp}\""); + if (proc != null) + proc.Exited += (_, _) => File.Delete(tmp); + else + File.Delete(tmp); + } + + private string GetTerminalCommand(string dir) + { + switch(Application) + { + case SupportedTerminals.NativeTerminal: + return GetNativeTerminalCommand(dir); + case SupportedTerminals.iTerm2: + return GetITerm2Command(dir); + default: + App.RaiseException(dir, $"Only supports native Terminal and iTerm2!"); + return null; + } + } + + private string GetNativeTerminalCommand(string dir) + { + var builder = new StringBuilder(); + builder.AppendLine("on run argv"); + builder.AppendLine(" tell application \"Terminal\""); + builder.AppendLine($" do script \"cd {dir}\""); + builder.AppendLine(" activate"); + builder.AppendLine(" end tell"); + builder.AppendLine("end run"); + return builder.ToString(); + } + + private string GetITerm2Command(string dir) + { + var builder = new StringBuilder(); + builder.AppendLine("on run argv"); + builder.AppendLine(" tell application \"iTerm2\""); + builder.AppendLine(" create window with default profile"); + builder.AppendLine(" tell the current session of the current window"); + builder.AppendLine($" write text \"cd {dir}\""); + builder.AppendLine(" end tell"); + builder.AppendLine(" end tell"); + builder.AppendLine("end run"); + return builder.ToString(); + } + } + + public MacOS() + { + // TODO: use config + if (IsITermInstalled()) + _terminal = new Terminal(SupportedTerminals.iTerm2); + else + _terminal = new Terminal(SupportedTerminals.NativeTerminal); + } + public void SetupApp(AppBuilder builder) { builder.With(new MacOSPlatformOptions() @@ -60,27 +141,19 @@ namespace SourceGit.Native var dir = string.IsNullOrEmpty(workdir) ? "~" : workdir; dir = dir.Replace(" ", "\\ "); - var builder = new StringBuilder(); - builder.AppendLine("on run argv"); - builder.AppendLine(" tell application \"Terminal\""); - builder.AppendLine($" do script \"cd {dir}\""); - builder.AppendLine(" activate"); - builder.AppendLine(" end tell"); - builder.AppendLine("end run"); - - var tmp = Path.GetTempFileName(); - File.WriteAllText(tmp, builder.ToString()); - - var proc = Process.Start("osascript", $"\"{tmp}\""); - if (proc != null) - proc.Exited += (_, _) => File.Delete(tmp); - else - File.Delete(tmp); + _terminal.Open(dir); } public void OpenWithDefaultEditor(string file) { Process.Start("open", file); } + + private bool IsITermInstalled() + { + return Directory. Exists("/Applications/iTerm.app"); + } + + private readonly Terminal _terminal = null; } } From 05db9e429d6bf23c6b7d3e3c79ab0487d57ae8b8 Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 20 Aug 2024 20:44:54 +0800 Subject: [PATCH 17/93] code_review: PR #377 * simplify the implementation --- src/Native/MacOS.cs | 117 ++++++++++++++------------------------------ 1 file changed, 36 insertions(+), 81 deletions(-) diff --git a/src/Native/MacOS.cs b/src/Native/MacOS.cs index bdc521b4..c3b259ee 100644 --- a/src/Native/MacOS.cs +++ b/src/Native/MacOS.cs @@ -12,85 +12,15 @@ namespace SourceGit.Native [SupportedOSPlatform("macOS")] internal class MacOS : OS.IBackend { - enum SupportedTerminals + enum TerminalType { - NativeTerminal, + Terminal, iTerm2, } - class Terminal - { - public SupportedTerminals Application { get; set; } - - public Terminal(SupportedTerminals application) - { - Application = application; - } - - public void Open(string dir) - { - string command = GetTerminalCommand(dir); - if (command == null) - return; - - var tmp = Path.GetTempFileName(); - File.WriteAllText(tmp, command); - - var proc = Process.Start("osascript", $"\"{tmp}\""); - if (proc != null) - proc.Exited += (_, _) => File.Delete(tmp); - else - File.Delete(tmp); - } - - private string GetTerminalCommand(string dir) - { - switch(Application) - { - case SupportedTerminals.NativeTerminal: - return GetNativeTerminalCommand(dir); - case SupportedTerminals.iTerm2: - return GetITerm2Command(dir); - default: - App.RaiseException(dir, $"Only supports native Terminal and iTerm2!"); - return null; - } - } - - private string GetNativeTerminalCommand(string dir) - { - var builder = new StringBuilder(); - builder.AppendLine("on run argv"); - builder.AppendLine(" tell application \"Terminal\""); - builder.AppendLine($" do script \"cd {dir}\""); - builder.AppendLine(" activate"); - builder.AppendLine(" end tell"); - builder.AppendLine("end run"); - return builder.ToString(); - } - - private string GetITerm2Command(string dir) - { - var builder = new StringBuilder(); - builder.AppendLine("on run argv"); - builder.AppendLine(" tell application \"iTerm2\""); - builder.AppendLine(" create window with default profile"); - builder.AppendLine(" tell the current session of the current window"); - builder.AppendLine($" write text \"cd {dir}\""); - builder.AppendLine(" end tell"); - builder.AppendLine(" end tell"); - builder.AppendLine("end run"); - return builder.ToString(); - } - } - public MacOS() { - // TODO: use config - if (IsITermInstalled()) - _terminal = new Terminal(SupportedTerminals.iTerm2); - else - _terminal = new Terminal(SupportedTerminals.NativeTerminal); + _terminal = Directory.Exists("/Applications/iTerm2.app") ? TerminalType.iTerm2 : TerminalType.Terminal; } public void SetupApp(AppBuilder builder) @@ -141,19 +71,44 @@ namespace SourceGit.Native var dir = string.IsNullOrEmpty(workdir) ? "~" : workdir; dir = dir.Replace(" ", "\\ "); - _terminal.Open(dir); + var cmdBuilder = new StringBuilder(); + switch (_terminal) + { + case TerminalType.iTerm2: + cmdBuilder.AppendLine("on run argv"); + cmdBuilder.AppendLine(" tell application \"iTerm2\""); + cmdBuilder.AppendLine(" create window with default profile"); + cmdBuilder.AppendLine(" tell the current session of the current window"); + cmdBuilder.AppendLine($" write text \"cd {dir}\""); + cmdBuilder.AppendLine(" end tell"); + cmdBuilder.AppendLine(" end tell"); + cmdBuilder.AppendLine("end run"); + break; + default: + cmdBuilder.AppendLine("on run argv"); + cmdBuilder.AppendLine(" tell application \"Terminal\""); + cmdBuilder.AppendLine($" do script \"cd {dir}\""); + cmdBuilder.AppendLine(" activate"); + cmdBuilder.AppendLine(" end tell"); + cmdBuilder.AppendLine("end run"); + break; + } + + var tmp = Path.GetTempFileName(); + File.WriteAllText(tmp, cmdBuilder.ToString()); + + var proc = Process.Start("osascript", $"\"{tmp}\""); + if (proc != null) + proc.Exited += (_, _) => File.Delete(tmp); + else + File.Delete(tmp); } public void OpenWithDefaultEditor(string file) { - Process.Start("open", file); + Process.Start("open", $"\"{file}\""); } - private bool IsITermInstalled() - { - return Directory. Exists("/Applications/iTerm.app"); - } - - private readonly Terminal _terminal = null; + private readonly TerminalType _terminal; } } From c1c1e1f0c896b46f4112a5460b50a345ee903210 Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 20 Aug 2024 20:48:44 +0800 Subject: [PATCH 18/93] ux: remove rounded corner when window is maximized (#378) --- src/Resources/Styles.axaml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Resources/Styles.axaml b/src/Resources/Styles.axaml index 79479272..0af9d9e8 100644 --- a/src/Resources/Styles.axaml +++ b/src/Resources/Styles.axaml @@ -143,6 +143,7 @@ - From 7b26589485f6649d154185895b81a59e1e255839 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 21 Aug 2024 10:17:42 +0800 Subject: [PATCH 24/93] fix: button tooltip foreground (#381) * TextBlock may not be the direct child of Button, for example, it may wrapped by a StackPanel with icons. * Introduce another way to solve this problem --- src/Resources/Styles.axaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Resources/Styles.axaml b/src/Resources/Styles.axaml index 8cbe4388..535d1c44 100644 --- a/src/Resources/Styles.axaml +++ b/src/Resources/Styles.axaml @@ -514,9 +514,12 @@ - + + diff --git a/src/Views/Welcome.axaml.cs b/src/Views/Welcome.axaml.cs index 91c156e6..2cb08021 100644 --- a/src/Views/Welcome.axaml.cs +++ b/src/Views/Welcome.axaml.cs @@ -35,6 +35,40 @@ namespace SourceGit.Views } } + private void OnSearchBoxKeyDown(object sender, KeyEventArgs e) + { + if (e.Key == Key.Down || e.Key == Key.FnDownArrow) + { + var containers = ReposTree.GetRealizedContainers(); + if (containers == null) + return; + + foreach (var c in containers) + { + if (c is TreeViewItem { IsVisible: true } item) + { + ReposTree.SelectedItem = item.DataContext; + item.Focus(); + break; + } + } + + e.Handled = true; + } + } + + private void OnTreeViewKeyDown(object sender, KeyEventArgs e) + { + if (e.Key == Key.Space && ReposTree.SelectedItem is ViewModels.RepositoryNode { IsRepository: true } node) + { + var parent = this.FindAncestorOfType(); + if (parent?.DataContext is ViewModels.Launcher launcher) + launcher.OpenRepositoryInTab(node, null); + + e.Handled = true; + } + } + private void OnTreeNodeContextRequested(object sender, ContextRequestedEventArgs e) { if (sender is Grid grid) From af6d2cc725f82f78930ad2a10cf896c523cf50e5 Mon Sep 17 00:00:00 2001 From: leo Date: Thu, 22 Aug 2024 18:11:25 +0800 Subject: [PATCH 45/93] fix: `TreeView` do NOT support NavigationMethod.Direction with invisible nodes (#391) --- src/ViewModels/Welcome.cs | 39 +++++++++++++++++++++++++ src/Views/Welcome.axaml | 1 + src/Views/Welcome.axaml.cs | 59 ++++++++++++++++++++++++++++++++++---- 3 files changed, 93 insertions(+), 6 deletions(-) diff --git a/src/ViewModels/Welcome.cs b/src/ViewModels/Welcome.cs index 8d6c423e..97eb4448 100644 --- a/src/ViewModels/Welcome.cs +++ b/src/ViewModels/Welcome.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Avalonia.Collections; using Avalonia.Controls; @@ -76,6 +77,30 @@ namespace SourceGit.ViewModels Preference.Instance.MoveNode(from, to); } + public RepositoryNode GetPrevVisible(RepositoryNode node) + { + var visibleRows = new List(); + CollectVisibleRows(visibleRows, RepositoryNodes); + + var idx = visibleRows.IndexOf(node); + if (idx <= 1) + return null; + + return visibleRows[idx - 1]; + } + + public RepositoryNode GetNextVisible(RepositoryNode node) + { + var visibleRows = new List(); + CollectVisibleRows(visibleRows, RepositoryNodes); + + var idx = visibleRows.IndexOf(node); + if (idx < 0 || idx >= visibleRows.Count - 1) + return null; + + return visibleRows[idx + 1]; + } + public ContextMenu CreateContextMenu(RepositoryNode node) { var menu = new ContextMenu(); @@ -212,6 +237,20 @@ namespace SourceGit.ViewModels } } + private void CollectVisibleRows(List visible, AvaloniaList collection) + { + foreach (var node in collection) + { + if (node.IsVisible) + { + visible.Add(node); + + if (!node.IsRepository) + CollectVisibleRows(visible, node.SubNodes); + } + } + } + private static Welcome _instance = new Welcome(); private string _searchFilter = string.Empty; } diff --git a/src/Views/Welcome.axaml b/src/Views/Welcome.axaml index 6e0e1d94..ecb8c855 100644 --- a/src/Views/Welcome.axaml +++ b/src/Views/Welcome.axaml @@ -42,6 +42,7 @@ ScrollViewer.VerticalScrollBarVisibility="Auto" Loaded="SetupTreeViewDragAndDrop" LostFocus="OnTreeViewLostFocus" + SelectionChanged="OnTreeViewSelectionChanged" KeyDown="OnTreeViewKeyDown"> diff --git a/src/Views/Welcome.axaml.cs b/src/Views/Welcome.axaml.cs index 2cb08021..7f0630f3 100644 --- a/src/Views/Welcome.axaml.cs +++ b/src/Views/Welcome.axaml.cs @@ -48,7 +48,6 @@ namespace SourceGit.Views if (c is TreeViewItem { IsVisible: true } item) { ReposTree.SelectedItem = item.DataContext; - item.Focus(); break; } } @@ -59,13 +58,41 @@ namespace SourceGit.Views private void OnTreeViewKeyDown(object sender, KeyEventArgs e) { - if (e.Key == Key.Space && ReposTree.SelectedItem is ViewModels.RepositoryNode { IsRepository: true } node) + if (ReposTree.SelectedItem is ViewModels.RepositoryNode node) { - var parent = this.FindAncestorOfType(); - if (parent?.DataContext is ViewModels.Launcher launcher) - launcher.OpenRepositoryInTab(node, null); + if (e.Key == Key.Space && node.IsRepository) + { + var parent = this.FindAncestorOfType(); + if (parent?.DataContext is ViewModels.Launcher launcher) + launcher.OpenRepositoryInTab(node, null); - e.Handled = true; + e.Handled = true; + } + else if (e.Key == Key.Down) + { + var next = ViewModels.Welcome.Instance.GetNextVisible(node); + if (next != null) + ReposTree.SelectedItem = next; + + e.Handled = true; + } + else if (e.Key == Key.Up) + { + var prev = ViewModels.Welcome.Instance.GetPrevVisible(node); + if (prev != null) + ReposTree.SelectedItem = prev; + + e.Handled = true; + } + } + } + + private void OnTreeViewSelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (ReposTree.SelectedItem is ViewModels.RepositoryNode node) + { + var item = FindTreeViewItemByNode(node, ReposTree); + item?.Focus(NavigationMethod.Directional); } } @@ -264,6 +291,26 @@ namespace SourceGit.Views launcher?.OpenRepositoryInTab(node, launcher.ActivePage); } + private TreeViewItem FindTreeViewItemByNode(ViewModels.RepositoryNode node, ItemsControl container) + { + var items = container.GetRealizedContainers(); + + foreach (var item in items) + { + if (item is TreeViewItem { DataContext: ViewModels.RepositoryNode test } treeViewItem) + { + if (test == node) + return treeViewItem; + + var child = FindTreeViewItemByNode(node, treeViewItem); + if (child != null) + return child; + } + } + + return null; + } + private bool _pressedTreeNode = false; private Point _pressedTreeNodePosition = new Point(); private bool _startDragTreeNode = false; From 38e2e0f3f406c8af7a4a85d75a796eefb9d7937c Mon Sep 17 00:00:00 2001 From: leo Date: Thu, 22 Aug 2024 21:10:23 +0800 Subject: [PATCH 46/93] refactor: rewrite the welcome page since the original `TreeView` has many limitations (#391) --- src/Resources/Styles.axaml | 66 ----------- src/ViewModels/Clone.cs | 1 + src/ViewModels/CreateGroup.cs | 1 + src/ViewModels/DeleteRepositoryNode.cs | 1 + src/ViewModels/EditRepositoryNode.cs | 3 + src/ViewModels/Init.cs | 1 + src/ViewModels/Launcher.cs | 1 + src/ViewModels/RepositoryNode.cs | 7 ++ src/ViewModels/Welcome.cs | 133 ++++++++++++---------- src/Views/Welcome.axaml | 106 +++++++++++------- src/Views/Welcome.axaml.cs | 148 +++++++++++-------------- src/Views/WelcomeToolbar.axaml.cs | 2 + 12 files changed, 227 insertions(+), 243 deletions(-) diff --git a/src/Resources/Styles.axaml b/src/Resources/Styles.axaml index 65a3b493..816dbe15 100644 --- a/src/Resources/Styles.axaml +++ b/src/Resources/Styles.axaml @@ -1397,72 +1397,6 @@ - - - - - - - - + + - + - - - + + + + + + + + + + + + + + + + + + - + + - - - + - - - + + + + typeof(ToggleButton); + + protected override void OnPointerPressed(PointerPressedEventArgs e) + { + if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed && + DataContext is ViewModels.RepositoryNode { IsRepository: false } node) + { + ViewModels.Welcome.Instance.ToggleNodeIsExpanded(node); + } + + e.Handled = true; + } + } + public partial class Welcome : UserControl { public Welcome() @@ -15,9 +33,30 @@ namespace SourceGit.Views InitializeComponent(); } + protected override void OnKeyDown(KeyEventArgs e) + { + base.OnKeyDown(e); + + if (!e.Handled) + { + if (e.Key == Key.Down && ViewModels.Welcome.Instance.Rows.Count > 0) + { + TreeContainer.SelectedIndex = 0; + TreeContainer.Focus(NavigationMethod.Directional); + e.Handled = true; + } + else if (e.Key == Key.F && + ((OperatingSystem.IsMacOS() && e.KeyModifiers.HasFlag(KeyModifiers.Meta)) || + (!OperatingSystem.IsMacOS() && e.KeyModifiers.HasFlag(KeyModifiers.Control)))) + { + SearchBox.Focus(); + } + } + } + private void SetupTreeViewDragAndDrop(object sender, RoutedEventArgs _) { - if (sender is TreeView view) + if (sender is ListBox view) { DragDrop.SetAllowDrop(view, true); view.AddHandler(DragDrop.DragOverEvent, DragOverTreeView); @@ -35,67 +74,25 @@ namespace SourceGit.Views } } - private void OnSearchBoxKeyDown(object sender, KeyEventArgs e) + private void OnTreeViewKeyDown(object sender, KeyEventArgs e) { - if (e.Key == Key.Down || e.Key == Key.FnDownArrow) + if (TreeContainer.SelectedItem is ViewModels.RepositoryNode node && e.Key == Key.Enter) { - var containers = ReposTree.GetRealizedContainers(); - if (containers == null) - return; - - foreach (var c in containers) + if (node.IsRepository) { - if (c is TreeViewItem { IsVisible: true } item) - { - ReposTree.SelectedItem = item.DataContext; - break; - } + var parent = this.FindAncestorOfType(); + if (parent is { DataContext: ViewModels.Launcher launcher }) + launcher.OpenRepositoryInTab(node, null); + } + else + { + ViewModels.Welcome.Instance.ToggleNodeIsExpanded(node); } e.Handled = true; } } - private void OnTreeViewKeyDown(object sender, KeyEventArgs e) - { - if (ReposTree.SelectedItem is ViewModels.RepositoryNode node) - { - if (e.Key == Key.Space && node.IsRepository) - { - var parent = this.FindAncestorOfType(); - if (parent?.DataContext is ViewModels.Launcher launcher) - launcher.OpenRepositoryInTab(node, null); - - e.Handled = true; - } - else if (e.Key == Key.Down) - { - var next = ViewModels.Welcome.Instance.GetNextVisible(node); - if (next != null) - ReposTree.SelectedItem = next; - - e.Handled = true; - } - else if (e.Key == Key.Up) - { - var prev = ViewModels.Welcome.Instance.GetPrevVisible(node); - if (prev != null) - ReposTree.SelectedItem = prev; - - e.Handled = true; - } - } - } - - private void OnTreeViewSelectionChanged(object sender, SelectionChangedEventArgs e) - { - if (ReposTree.SelectedItem is ViewModels.RepositoryNode node) - { - var item = FindTreeViewItemByNode(node, ReposTree); - item?.Focus(NavigationMethod.Directional); - } - } - private void OnTreeNodeContextRequested(object sender, ContextRequestedEventArgs e) { if (sender is Grid grid) @@ -256,16 +253,21 @@ namespace SourceGit.Views private void OnDoubleTappedTreeNode(object sender, TappedEventArgs e) { - var grid = sender as Grid; - var to = grid?.DataContext as ViewModels.RepositoryNode; - if (to is not { IsRepository: true }) - return; + if (sender is Grid { DataContext: ViewModels.RepositoryNode 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); + } - var parent = this.FindAncestorOfType(); - if (parent?.DataContext is ViewModels.Launcher launcher) - launcher.OpenRepositoryInTab(to, null); - - e.Handled = true; + e.Handled = true; + } } private void OpenOrInitRepository(string path, ViewModels.RepositoryNode parent = null) @@ -287,30 +289,12 @@ namespace SourceGit.Views var normalizedPath = root.Replace("\\", "/"); var node = ViewModels.Preference.Instance.FindOrAddNodeByRepositoryPath(normalizedPath, parent, true); + ViewModels.Welcome.Instance.Refresh(); + var launcher = this.FindAncestorOfType()?.DataContext as ViewModels.Launcher; launcher?.OpenRepositoryInTab(node, launcher.ActivePage); } - private TreeViewItem FindTreeViewItemByNode(ViewModels.RepositoryNode node, ItemsControl container) - { - var items = container.GetRealizedContainers(); - - foreach (var item in items) - { - if (item is TreeViewItem { DataContext: ViewModels.RepositoryNode test } treeViewItem) - { - if (test == node) - return treeViewItem; - - var child = FindTreeViewItemByNode(node, treeViewItem); - if (child != null) - return child; - } - } - - return null; - } - private bool _pressedTreeNode = false; private Point _pressedTreeNodePosition = new Point(); private bool _startDragTreeNode = false; diff --git a/src/Views/WelcomeToolbar.axaml.cs b/src/Views/WelcomeToolbar.axaml.cs index 2f8de643..6c5e21d0 100644 --- a/src/Views/WelcomeToolbar.axaml.cs +++ b/src/Views/WelcomeToolbar.axaml.cs @@ -56,6 +56,8 @@ namespace SourceGit.Views var normalizedPath = root.Replace("\\", "/"); var node = ViewModels.Preference.Instance.FindOrAddNodeByRepositoryPath(normalizedPath, parent, false); + ViewModels.Welcome.Instance.Refresh(); + var launcher = this.FindAncestorOfType()?.DataContext as ViewModels.Launcher; launcher?.OpenRepositoryInTab(node, launcher.ActivePage); } From 38770af13de6ba33ffee4fd8d471c84b3000fbbf Mon Sep 17 00:00:00 2001 From: leo Date: Thu, 22 Aug 2024 21:23:05 +0800 Subject: [PATCH 47/93] ux: vertical align center --- src/Views/Welcome.axaml | 1 + src/Views/Welcome.axaml.cs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Views/Welcome.axaml b/src/Views/Welcome.axaml index 89e7f286..fbf2b6b1 100644 --- a/src/Views/Welcome.axaml +++ b/src/Views/Welcome.axaml @@ -89,6 +89,7 @@ Date: Thu, 22 Aug 2024 21:46:06 +0800 Subject: [PATCH 48/93] code_style: clean up unused styles --- src/Resources/Styles.axaml | 127 +++++-------------------------------- 1 file changed, 15 insertions(+), 112 deletions(-) diff --git a/src/Resources/Styles.axaml b/src/Resources/Styles.axaml index 816dbe15..bdb7ef93 100644 --- a/src/Resources/Styles.axaml +++ b/src/Resources/Styles.axaml @@ -534,28 +534,10 @@ HorizontalAlignment="Right" VerticalAlignment="Top" Padding="10"> - - - - - - - + + - + - + - + @@ -605,84 +572,20 @@ Width="28" Background="Transparent" VerticalAlignment="Stretch" - Command="{x:Static aes:SearchCommands.FindPrevious}"> - - - - + Command="{x:Static aes:SearchCommands.FindPrevious}" + ToolTip.Tip="Find Previous (Shift+F3)"> + - - - - - - + From 7389f5d521600fbc8b9d22f20ab2c2366665ba3f Mon Sep 17 00:00:00 2001 From: leo Date: Thu, 22 Aug 2024 21:57:39 +0800 Subject: [PATCH 49/93] revert: item should fill the height of tree node --- src/Views/Welcome.axaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Views/Welcome.axaml b/src/Views/Welcome.axaml index fbf2b6b1..e5626dbd 100644 --- a/src/Views/Welcome.axaml +++ b/src/Views/Welcome.axaml @@ -89,7 +89,7 @@ Date: Fri, 23 Aug 2024 10:22:53 +0800 Subject: [PATCH 50/93] ux: different node in graph (#395) --- src/Models/CommitGraph.cs | 29 ++++++++++++++----- src/Resources/Icons.axaml | 2 +- src/Views/Histories.axaml.cs | 56 +++++++++++++++++++++++++----------- 3 files changed, 62 insertions(+), 25 deletions(-) diff --git a/src/Models/CommitGraph.cs b/src/Models/CommitGraph.cs index 718d19c1..55746a43 100644 --- a/src/Models/CommitGraph.cs +++ b/src/Models/CommitGraph.cs @@ -97,8 +97,16 @@ namespace SourceGit.Models public int Color; } + public enum DotType + { + Default, + Head, + Merge, + } + public class Dot { + public DotType Type; public Point Center; public int Color; } @@ -134,6 +142,7 @@ namespace SourceGit.Models double HALF_WIDTH = 6; double UNIT_HEIGHT = 28; double HALF_HEIGHT = 14; + double H_MARGIN = 2; var temp = new CommitGraph(); var unsolved = new List(); @@ -152,7 +161,7 @@ namespace SourceGit.Models offsetY += UNIT_HEIGHT; // Find first curves that links to this commit and marks others that links to this commit ended. - double offsetX = -HALF_WIDTH; + double offsetX = H_MARGIN - HALF_WIDTH; foreach (var l in unsolved) { if (l.Next == commit.SHA) @@ -204,16 +213,22 @@ namespace SourceGit.Models // Calculate link position of this commit. Point position = new Point(offsetX, offsetY); + int dotColor = 0; if (major != null) { major.IsMerged = isMerged; position = new Point(major.LastX, offsetY); - temp.Dots.Add(new Dot() { Center = position, Color = major.Path.Color }); + dotColor = major.Path.Color; } + + Dot anchor = new Dot() { Center = position, Color = dotColor }; + if (commit.IsCurrentHead) + anchor.Type = DotType.Head; + else if (commit.Parents.Count > 1) + anchor.Type = DotType.Merge; else - { - temp.Dots.Add(new Dot() { Center = position, Color = 0 }); - } + anchor.Type = DotType.Default; + temp.Dots.Add(anchor); // Deal with other parents (the first parent has been processed) if (!firstParentOnlyEnabled) @@ -258,7 +273,7 @@ namespace SourceGit.Models // Margins & merge state (used by datagrid). commit.IsMerged = isMerged; - commit.Margin = new Thickness(Math.Max(offsetX + HALF_WIDTH, oldCount * UNIT_WIDTH), 0, 0, 0); + commit.Margin = new Thickness(Math.Max(offsetX + HALF_WIDTH, oldCount * UNIT_WIDTH + H_MARGIN) + H_MARGIN, 0, 0, 0); // Clean up ended.Clear(); @@ -274,7 +289,7 @@ namespace SourceGit.Models if (path.Path.Points.Count == 1 && Math.Abs(path.Path.Points[0].Y - endY) < 0.0001) continue; - path.Add((i + 0.5) * UNIT_WIDTH, endY + HALF_HEIGHT, HALF_HEIGHT, true); + path.Add((i + 0.5) * UNIT_WIDTH + H_MARGIN, endY + HALF_HEIGHT, HALF_HEIGHT, true); } unsolved.Clear(); diff --git a/src/Resources/Icons.axaml b/src/Resources/Icons.axaml index 7345d2d2..61989186 100644 --- a/src/Resources/Icons.axaml +++ b/src/Resources/Icons.axaml @@ -56,7 +56,6 @@ M412 66C326 132 271 233 271 347c0 17 1 34 4 50-41-48-98-79-162-83a444 444 0 00-46 196c0 207 142 382 337 439h2c19 0 34 15 34 33 0 11-6 21-14 26l1 14C183 973 0 763 0 511 0 272 166 70 393 7A35 35 0 01414 0c19 0 34 15 34 33a33 33 0 01-36 33zm200 893c86-66 141-168 141-282 0-17-1-34-4-50 41 48 98 79 162 83a444 444 0 0046-196c0-207-142-382-337-439h-2a33 33 0 01-34-33c0-11 6-21 14-26L596 0C841 51 1024 261 1024 513c0 239-166 441-393 504A35 35 0 01610 1024a33 33 0 01-34-33 33 33 0 0136-33zM512 704a192 192 0 110-384 192 192 0 010 384z M512 64A447 447 0 0064 512c0 248 200 448 448 448s448-200 448-448S760 64 512 64zM218 295h31c54 0 105 19 145 55 13 12 13 31 3 43a35 35 0 01-22 10 36 36 0 01-21-7 155 155 0 00-103-39h-31a32 32 0 01-31-31c0-18 13-31 30-31zm31 433h-31a32 32 0 01-31-31c0-16 13-31 31-31h31A154 154 0 00403 512 217 217 0 01620 295h75l-93-67a33 33 0 01-7-43 33 33 0 0143-7l205 148-205 148a29 29 0 01-18 6 32 32 0 01-31-31c0-10 4-19 13-25l93-67H620a154 154 0 00-154 154c0 122-97 220-217 220zm390 118a29 29 0 01-18 6 32 32 0 01-31-31c0-10 4-19 13-25l93-67h-75c-52 0-103-19-143-54-12-12-13-31-1-43a30 30 0 0142-3 151 151 0 00102 39h75L602 599a33 33 0 01-7-43 33 33 0 0143-7l205 148-203 151z M922 39H102A65 65 0 0039 106v609a65 65 0 0063 68h94v168a34 34 0 0019 31 30 30 0 0012 3 30 30 0 0022-10l182-192H922a65 65 0 0063-68V106A65 65 0 00922 39zM288 378h479a34 34 0 010 68H288a34 34 0 010-68zm0-135h479a34 34 0 010 68H288a34 34 0 010-68zm0 270h310a34 34 0 010 68H288a34 34 0 010-68z - M640 96c-158 0-288 130-288 288 0 17 3 31 5 46L105 681 96 691V928h224v-96h96v-96h96v-95c38 18 82 31 128 31 158 0 288-130 288-288s-130-288-288-288zm0 64c123 0 224 101 224 224s-101 224-224 224a235 235 0 01-109-28l-8-4H448v96h-96v96H256v96H160v-146l253-254 12-11-3-17C419 417 416 400 416 384c0-123 101-224 224-224zm64 96a64 64 0 100 128 64 64 0 100-128z M875 117H149C109 117 75 151 75 192v640c0 41 34 75 75 75h725c41 0 75-34 75-75V192c0-41-34-75-75-75zM139 832V192c0-6 4-11 11-11h331v661H149c-6 0-11-4-11-11zm747 0c0 6-4 11-11 11H544v-661H875c6 0 11 4 11 11v640z M875 117H149C109 117 75 151 75 192v640c0 41 34 75 75 75h725c41 0 75-34 75-75V192c0-41-34-75-75-75zm-725 64h725c6 0 11 4 11 11v288h-747V192c0-6 4-11 11-11zm725 661H149c-6 0-11-4-11-11V544h747V832c0 6-4 11-11 11z M40 9 15 23 15 31 9 28 9 20 34 5 24 0 0 14 0 34 25 48 25 28 49 14zM26 29 26 48 49 34 49 15z @@ -75,6 +74,7 @@ M896 64H128C96 64 64 96 64 128v768c0 32 32 64 64 64h768c32 0 64-32 64-64V128c0-32-32-64-64-64z m-64 736c0 16-17 32-32 32H224c-18 0-32-12-32-32V224c0-16 16-32 32-32h576c15 0 32 16 32 32v576zM512 384c-71 0-128 57-128 128s57 128 128 128 128-57 128-128-57-128-128-128z M299 811 299 725 384 725 384 811 299 811M469 811 469 725 555 725 555 811 469 811M640 811 640 725 725 725 725 811 640 811M299 640 299 555 384 555 384 640 299 640M469 640 469 555 555 555 555 640 469 640M640 640 640 555 725 555 725 640 640 640M299 469 299 384 384 384 384 469 299 469M469 469 469 384 555 384 555 469 469 469M640 469 640 384 725 384 725 469 640 469M299 299 299 213 384 213 384 299 299 299M469 299 469 213 555 213 555 299 469 299M640 299 640 213 725 213 725 299 640 299Z M683 409v204L1024 308 683 0v191c-413 0-427 526-427 526c117-229 203-307 427-307zm85 492H102V327h153s38-63 114-122H51c-28 0-51 27-51 61v697c0 34 23 61 51 61h768c28 0 51-27 51-61V614l-102 100v187z + M640 96c-158 0-288 130-288 288 0 17 3 31 5 46L105 681 96 691V928h224v-96h96v-96h96v-95c38 18 82 31 128 31 158 0 288-130 288-288s-130-288-288-288zm0 64c123 0 224 101 224 224s-101 224-224 224a235 235 0 01-109-28l-8-4H448v96h-96v96H256v96H160v-146l253-254 12-11-3-17C419 417 416 400 416 384c0-123 101-224 224-224zm64 96a64 64 0 100 128 64 64 0 100-128z M544 85c49 0 90 37 95 85h75a96 96 0 0196 89L811 267a32 32 0 01-28 32L779 299a32 32 0 01-32-28L747 267a32 32 0 00-28-32L715 235h-91a96 96 0 01-80 42H395c-33 0-62-17-80-42L224 235a32 32 0 00-32 28L192 267v576c0 16 12 30 28 32l4 0h128a32 32 0 0132 28l0 4a32 32 0 01-32 32h-128a96 96 0 01-96-89L128 843V267a96 96 0 0189-96L224 171h75a96 96 0 0195-85h150zm256 256a96 96 0 0196 89l0 7v405a96 96 0 01-89 96L800 939h-277a96 96 0 01-96-89L427 843v-405a96 96 0 0189-96L523 341h277zm-256-192H395a32 32 0 000 64h150a32 32 0 100-64z m186 532 287 0 0 287c0 11 9 20 20 20s20-9 20-20l0-287 287 0c11 0 20-9 20-20s-9-20-20-20l-287 0 0-287c0-11-9-20-20-20s-20 9-20 20l0 287-287 0c-11 0-20 9-20 20s9 20 20 20z M432 0h160c27 0 48 21 48 48v336h175c36 0 53 43 28 68L539 757c-15 15-40 15-55 0L180 452c-25-25-7-68 28-68H384V48c0-27 21-48 48-48zm592 752v224c0 27-21 48-48 48H48c-27 0-48-21-48-48V752c0-27 21-48 48-48h293l98 98c40 40 105 40 145 0l98-98H976c27 0 48 21 48 48zm-248 176c0-22-18-40-40-40s-40 18-40 40s18 40 40 40s40-18 40-40zm128 0c0-22-18-40-40-40s-40 18-40 40s18 40 40 40s40-18 40-40z diff --git a/src/Views/Histories.axaml.cs b/src/Views/Histories.axaml.cs index 86fdd6eb..19394e33 100644 --- a/src/Views/Histories.axaml.cs +++ b/src/Views/Histories.axaml.cs @@ -475,25 +475,14 @@ namespace SourceGit.Views var top = startY; var bottom = startY + grid.Bounds.Height + rowHeight * 2; - // Draw all curves - DrawCurves(context, top, bottom); - - // Draw connect dots - IBrush dotFill = DotBrush; - foreach (var dot in graph.Dots) - { - if (dot.Center.Y < top) - continue; - if (dot.Center.Y > bottom) - break; - - context.DrawEllipse(dotFill, Models.CommitGraph.Pens[dot.Color], dot.Center, 3, 3); - } + // Draw contents + DrawCurves(context, graph, top, bottom); + DrawAnchors(context, graph, top, bottom); } - private void DrawCurves(DrawingContext context, double top, double bottom) + private void DrawCurves(DrawingContext context, Models.CommitGraph graph, double top, double bottom) { - foreach (var line in Graph.Paths) + foreach (var line in graph.Paths) { var last = line.Points[0]; var size = line.Points.Count; @@ -561,7 +550,7 @@ namespace SourceGit.Views context.DrawGeometry(null, pen, geo); } - foreach (var link in Graph.Links) + foreach (var link in graph.Links) { if (link.End.Y < top) continue; @@ -578,6 +567,39 @@ namespace SourceGit.Views context.DrawGeometry(null, Models.CommitGraph.Pens[link.Color], geo); } } + + private void DrawAnchors(DrawingContext context, Models.CommitGraph graph, double top, double bottom) + { + IBrush dotFill = DotBrush; + Pen dotFillPen = new Pen(dotFill, 2); + + foreach (var dot in graph.Dots) + { + if (dot.Center.Y < top) + continue; + if (dot.Center.Y > bottom) + break; + + var pen = Models.CommitGraph.Pens[dot.Color]; + switch (dot.Type) + { + case Models.CommitGraph.DotType.Head: + context.DrawEllipse(dotFill, pen, dot.Center, 6, 6); + context.DrawEllipse(pen.Brush, null, dot.Center, 2, 2); + 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)); + break; + default: + context.DrawEllipse(dotFill, pen, dot.Center, 3, 3); + break; + } + } + } + + private Geometry _mergeIcon = null; } public partial class Histories : UserControl From c1ee47a79db926c6856f36392c897124b7c3b014 Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 23 Aug 2024 10:28:16 +0800 Subject: [PATCH 51/93] code_style: remove unused code and run `dotnet format` --- src/ViewModels/Welcome.cs | 2 +- src/Views/Histories.axaml.cs | 4 +--- src/Views/Preference.axaml.cs | 2 +- src/Views/Welcome.axaml.cs | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/ViewModels/Welcome.cs b/src/ViewModels/Welcome.cs index 03cbb528..d675a73f 100644 --- a/src/ViewModels/Welcome.cs +++ b/src/ViewModels/Welcome.cs @@ -255,7 +255,7 @@ namespace SourceGit.ViewModels if (node.IsRepository || !node.IsExpanded) continue; - MakeTreeRows(rows, node.SubNodes, depth+1); + MakeTreeRows(rows, node.SubNodes, depth + 1); } } diff --git a/src/Views/Histories.axaml.cs b/src/Views/Histories.axaml.cs index 19394e33..a73fa12b 100644 --- a/src/Views/Histories.axaml.cs +++ b/src/Views/Histories.axaml.cs @@ -477,7 +477,7 @@ namespace SourceGit.Views // Draw contents DrawCurves(context, graph, top, bottom); - DrawAnchors(context, graph, top, bottom); + DrawAnchors(context, graph, top, bottom); } private void DrawCurves(DrawingContext context, Models.CommitGraph graph, double top, double bottom) @@ -598,8 +598,6 @@ namespace SourceGit.Views } } } - - private Geometry _mergeIcon = null; } public partial class Histories : UserControl diff --git a/src/Views/Preference.axaml.cs b/src/Views/Preference.axaml.cs index 1c5d203a..28bdeeaa 100644 --- a/src/Views/Preference.axaml.cs +++ b/src/Views/Preference.axaml.cs @@ -259,7 +259,7 @@ namespace SourceGit.Views var dialog = new ConfirmRestart(); App.OpenDialog(dialog); } - + e.Handled = true; } } diff --git a/src/Views/Welcome.axaml.cs b/src/Views/Welcome.axaml.cs index 4d34eec1..91ba005c 100644 --- a/src/Views/Welcome.axaml.cs +++ b/src/Views/Welcome.axaml.cs @@ -45,7 +45,7 @@ namespace SourceGit.Views TreeContainer.Focus(NavigationMethod.Directional); e.Handled = true; } - else if (e.Key == Key.F && + else if (e.Key == Key.F && ((OperatingSystem.IsMacOS() && e.KeyModifiers.HasFlag(KeyModifiers.Meta)) || (!OperatingSystem.IsMacOS() && e.KeyModifiers.HasFlag(KeyModifiers.Control)))) { From 729e0d6cc85f0b52a75217c7c70c75e8d8bdd393 Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 23 Aug 2024 10:29:43 +0800 Subject: [PATCH 52/93] ux: new style for current HEAD --- src/Views/Histories.axaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Views/Histories.axaml.cs b/src/Views/Histories.axaml.cs index a73fa12b..80192c1e 100644 --- a/src/Views/Histories.axaml.cs +++ b/src/Views/Histories.axaml.cs @@ -585,7 +585,7 @@ namespace SourceGit.Views { case Models.CommitGraph.DotType.Head: context.DrawEllipse(dotFill, pen, dot.Center, 6, 6); - context.DrawEllipse(pen.Brush, null, dot.Center, 2, 2); + context.DrawEllipse(pen.Brush, null, dot.Center, 3, 3); break; case Models.CommitGraph.DotType.Merge: context.DrawEllipse(pen.Brush, null, dot.Center, 6, 6); From c3f2c5836a454033db105ec2d53c2115aa3f32c2 Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 23 Aug 2024 10:39:09 +0800 Subject: [PATCH 53/93] feature: handle `ESC` key to clear search in Welcome page --- src/Views/Welcome.axaml.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Views/Welcome.axaml.cs b/src/Views/Welcome.axaml.cs index 91ba005c..e4a329e8 100644 --- a/src/Views/Welcome.axaml.cs +++ b/src/Views/Welcome.axaml.cs @@ -50,6 +50,12 @@ namespace SourceGit.Views (!OperatingSystem.IsMacOS() && e.KeyModifiers.HasFlag(KeyModifiers.Control)))) { SearchBox.Focus(); + e.Handled = true; + } + else if (e.Key == Key.Escape) + { + ViewModels.Welcome.Instance.ClearSearchFilter(); + e.Handled = true; } } } From 9bcadf3523f8a64737b15ad3612204d9b7482022 Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 23 Aug 2024 10:57:33 +0800 Subject: [PATCH 54/93] ux: using WrapPanel instead of StackPanel to prevent content out of bounds (#396) --- src/Views/Checkout.axaml | 19 ++++++++++++------- src/Views/CreateBranch.axaml | 21 ++++++++++++++------- src/Views/Pull.axaml | 24 +++++++++++++++++------- 3 files changed, 43 insertions(+), 21 deletions(-) diff --git a/src/Views/Checkout.axaml b/src/Views/Checkout.axaml index 02ed64b7..eaf2e79e 100644 --- a/src/Views/Checkout.axaml +++ b/src/Views/Checkout.axaml @@ -13,7 +13,12 @@ Classes="bold" Text="{DynamicResource Text.Checkout}"/> - + + + + + + - - + + - + - + diff --git a/src/Views/CreateBranch.axaml b/src/Views/CreateBranch.axaml index 156e9766..3516c317 100644 --- a/src/Views/CreateBranch.axaml +++ b/src/Views/CreateBranch.axaml @@ -14,7 +14,14 @@ - + + + + + + + + - - + + - + - + - + + + + + + + + + + + - - + + - + - + Date: Fri, 23 Aug 2024 11:24:31 +0800 Subject: [PATCH 55/93] refactor: there's no need to use AvaloniaList since we have replaced the TreeView with custom control --- src/ViewModels/Preference.cs | 47 ++++++++++---------------------- src/ViewModels/RepositoryNode.cs | 14 ++++------ src/ViewModels/Welcome.cs | 2 +- 3 files changed, 21 insertions(+), 42 deletions(-) diff --git a/src/ViewModels/Preference.cs b/src/ViewModels/Preference.cs index 858cc2d8..63d6fcb1 100644 --- a/src/ViewModels/Preference.cs +++ b/src/ViewModels/Preference.cs @@ -4,8 +4,6 @@ using System.IO; using System.Text.Json; using System.Text.Json.Serialization; -using Avalonia.Collections; - using CommunityToolkit.Mvvm.ComponentModel; namespace SourceGit.ViewModels @@ -306,11 +304,11 @@ namespace SourceGit.ViewModels set => SetProperty(ref _externalMergeToolPath, value); } - public AvaloniaList RepositoryNodes + public List RepositoryNodes { - get => _repositoryNodes; - set => SetProperty(ref _repositoryNodes, value); - } + get; + set; + } = []; public List OpenedTabs { @@ -353,21 +351,15 @@ namespace SourceGit.ViewModels public void AddNode(RepositoryNode node, RepositoryNode to = null) { - var collection = to == null ? _repositoryNodes : to.SubNodes; - var list = new List(); - list.AddRange(collection); - list.Add(node); - list.Sort((l, r) => + var collection = to == null ? RepositoryNodes : to.SubNodes; + collection.Add(node); + collection.Sort((l, r) => { if (l.IsRepository != r.IsRepository) return l.IsRepository ? 1 : -1; return string.Compare(l.Name, r.Name, StringComparison.Ordinal); }); - - collection.Clear(); - foreach (var one in list) - collection.Add(one); } public RepositoryNode FindNode(string id) @@ -400,7 +392,7 @@ namespace SourceGit.ViewModels public void MoveNode(RepositoryNode node, RepositoryNode to = null) { - if (to == null && _repositoryNodes.Contains(node)) + if (to == null && RepositoryNodes.Contains(node)) return; if (to != null && to.SubNodes.Contains(node)) return; @@ -411,28 +403,19 @@ namespace SourceGit.ViewModels public void RemoveNode(RepositoryNode node) { - RemoveNodeRecursive(node, _repositoryNodes); + RemoveNodeRecursive(node, RepositoryNodes); } public void SortByRenamedNode(RepositoryNode node) { - var container = FindNodeContainer(node, _repositoryNodes); - if (container == null) - return; - - var list = new List(); - list.AddRange(container); - list.Sort((l, r) => + var container = FindNodeContainer(node, RepositoryNodes); + container?.Sort((l, r) => { if (l.IsRepository != r.IsRepository) return l.IsRepository ? 1 : -1; return string.Compare(l.Name, r.Name, StringComparison.Ordinal); }); - - container.Clear(); - foreach (var one in list) - container.Add(one); } public void Save() @@ -441,7 +424,7 @@ namespace SourceGit.ViewModels File.WriteAllText(_savePath, data); } - private RepositoryNode FindNodeRecursive(string id, AvaloniaList collection) + private RepositoryNode FindNodeRecursive(string id, List collection) { foreach (var node in collection) { @@ -456,7 +439,7 @@ namespace SourceGit.ViewModels return null; } - private AvaloniaList FindNodeContainer(RepositoryNode node, AvaloniaList collection) + private List FindNodeContainer(RepositoryNode node, List collection) { foreach (var sub in collection) { @@ -471,7 +454,7 @@ namespace SourceGit.ViewModels return null; } - private bool RemoveNodeRecursive(RepositoryNode node, AvaloniaList collection) + private bool RemoveNodeRecursive(RepositoryNode node, List collection) { if (collection.Contains(node)) { @@ -525,7 +508,5 @@ namespace SourceGit.ViewModels private int _externalMergeToolType = 0; private string _externalMergeToolPath = string.Empty; - - private AvaloniaList _repositoryNodes = new AvaloniaList(); } } diff --git a/src/ViewModels/RepositoryNode.cs b/src/ViewModels/RepositoryNode.cs index fd33153b..3cc98c16 100644 --- a/src/ViewModels/RepositoryNode.cs +++ b/src/ViewModels/RepositoryNode.cs @@ -1,6 +1,5 @@ -using System.Text.Json.Serialization; - -using Avalonia.Collections; +using System.Collections.Generic; +using System.Text.Json.Serialization; using CommunityToolkit.Mvvm.ComponentModel; @@ -56,11 +55,11 @@ namespace SourceGit.ViewModels set; } = 0; - public AvaloniaList SubNodes + public List SubNodes { - get => _subNodes; - set => SetProperty(ref _subNodes, value); - } + get; + set; + } = []; public void Edit() { @@ -100,6 +99,5 @@ namespace SourceGit.ViewModels private int _bookmark = 0; private bool _isExpanded = false; private bool _isVisible = true; - private AvaloniaList _subNodes = new AvaloniaList(); } } diff --git a/src/ViewModels/Welcome.cs b/src/ViewModels/Welcome.cs index d675a73f..f1e1c921 100644 --- a/src/ViewModels/Welcome.cs +++ b/src/ViewModels/Welcome.cs @@ -242,7 +242,7 @@ namespace SourceGit.ViewModels } } - private void MakeTreeRows(List rows, AvaloniaList nodes, int depth = 0) + private void MakeTreeRows(List rows, List nodes, int depth = 0) { foreach (var node in nodes) { From c6b26517c003b9d6163653c5494f9f1e48d470c9 Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 23 Aug 2024 11:35:57 +0800 Subject: [PATCH 56/93] ux: style for branch/tag filter toggle button --- src/Resources/Styles.axaml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Resources/Styles.axaml b/src/Resources/Styles.axaml index bdb7ef93..3323cee8 100644 --- a/src/Resources/Styles.axaml +++ b/src/Resources/Styles.axaml @@ -1046,13 +1046,15 @@ - + + + @@ -1063,7 +1065,7 @@ + + + From e9a269f4c1793dfc7747562ff91e095d95adc413 Mon Sep 17 00:00:00 2001 From: NilsPvR Date: Fri, 23 Aug 2024 12:19:44 +0200 Subject: [PATCH 66/93] localization: Add DE keys for commit tracking were added in 6ab0900 and c76d521 --- src/Resources/Locales/de_DE.axaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Resources/Locales/de_DE.axaml b/src/Resources/Locales/de_DE.axaml index a393a8be..bcd3e271 100644 --- a/src/Resources/Locales/de_DE.axaml +++ b/src/Resources/Locales/de_DE.axaml @@ -119,6 +119,8 @@ AUTOR GEÄNDERT COMMITTER + Prüfe Refs, die diesen Commit enthalten + COMMIT ENTHALTEN IN Zeigt nur die ersten 100 Änderungen. Alle Änderungen im ÄNDERUNGEN Tab. COMMIT-NACHRICHT VORGÄNGER From 3569e1696fd91439aadabddfdb1815bb57a3e46e Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 23 Aug 2024 18:47:08 +0800 Subject: [PATCH 67/93] ux: larger font size for HEAD decorator (#395) --- src/Views/CommitRefsPresenter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Views/CommitRefsPresenter.cs b/src/Views/CommitRefsPresenter.cs index 3dff6253..2e7e9e61 100644 --- a/src/Views/CommitRefsPresenter.cs +++ b/src/Views/CommitRefsPresenter.cs @@ -154,7 +154,7 @@ namespace SourceGit.Views CultureInfo.CurrentCulture, FlowDirection.LeftToRight, isHead ? typefaceBold : typeface, - labelSize, + isHead ? labelSize + 0.5 : labelSize, labelFG); var item = new RenderItem() From a717dc18762c61088dc3a8ed931da408627ae01b Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 23 Aug 2024 19:22:04 +0800 Subject: [PATCH 68/93] ux: add `Color.DecoratorHead` for current branch head (#395) --- src/Resources/Themes.axaml | 7 +++++-- src/Views/CommitBaseInfo.axaml | 1 + src/Views/CommitRefsPresenter.cs | 34 +++++++++++++++++++++----------- src/Views/Histories.axaml | 1 + 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/Resources/Themes.axaml b/src/Resources/Themes.axaml index daad5eb6..f517b53e 100644 --- a/src/Resources/Themes.axaml +++ b/src/Resources/Themes.axaml @@ -16,7 +16,8 @@ #FF6F6F6F #FFF8F8F8 #FFFFB835 - #FF02C302 + #fb5607 + #669900 Black #FF836C2E #FFFFFFFF @@ -50,7 +51,8 @@ #FF505050 #FFF8F8F8 #FFFFB835 - #FF02C302 + #f4f1de + #669900 Black #FFFAFAD2 #FF252525 @@ -84,6 +86,7 @@ + diff --git a/src/Views/CommitBaseInfo.axaml b/src/Views/CommitBaseInfo.axaml index d6ec6eae..8c2bf3cf 100644 --- a/src/Views/CommitBaseInfo.axaml +++ b/src/Views/CommitBaseInfo.axaml @@ -98,6 +98,7 @@ > RefsProperty = @@ -80,6 +80,15 @@ namespace SourceGit.Views set => SetValue(BranchNameBackgroundProperty, value); } + public static readonly StyledProperty HeadBranchNameBackgroundProperty = + AvaloniaProperty.Register(nameof(HeadBranchNameBackground), Brushes.White); + + public IBrush HeadBranchNameBackground + { + get => GetValue(HeadBranchNameBackgroundProperty); + set => SetValue(HeadBranchNameBackgroundProperty, value); + } + public static readonly StyledProperty TagNameBackgroundProperty = AvaloniaProperty.Register(nameof(TagNameBackground), Brushes.White); @@ -111,8 +120,6 @@ namespace SourceGit.Views var iconFG = IconForeground; var iconBG = IconBackground; - var branchBG = BranchNameBackground; - var tagBG = TagNameBackground; var x = 0.0; foreach (var item in _items) @@ -121,7 +128,7 @@ namespace SourceGit.Views var labelRect = new RoundedRect(new Rect(x + 16, 0, item.Label.Width + 8, 16), new CornerRadius(0, 2, 2, 0)); context.DrawRectangle(iconBG, null, iconRect); - context.DrawRectangle(item.IsTag ? tagBG : branchBG, null, labelRect); + context.DrawRectangle(item.LabelBG, null, labelRect); context.DrawText(item.Label, new Point(x + 20, 8.0 - item.Label.Height * 0.5)); using (context.PushTransform(Matrix.CreateTranslation(x + 4, 4))) @@ -141,6 +148,9 @@ namespace SourceGit.Views var typeface = new Typeface(FontFamily); var typefaceBold = new Typeface(FontFamily, FontStyle.Normal, FontWeight.Bold); var labelFG = LabelForeground; + var branchBG = BranchNameBackground; + var headBG = HeadBranchNameBackground; + var tagBG = TagNameBackground; var labelSize = FontSize; var requiredWidth = 0.0; @@ -154,29 +164,31 @@ namespace SourceGit.Views CultureInfo.CurrentCulture, FlowDirection.LeftToRight, isHead ? typefaceBold : typeface, - isHead ? labelSize + 0.5 : labelSize, + labelSize, labelFG); - var item = new RenderItem() - { - Label = label, - IsTag = decorator.Type == Models.DecoratorType.Tag, - }; - + var item = new RenderItem() { Label = label }; StreamGeometry geo; switch (decorator.Type) { case Models.DecoratorType.CurrentBranchHead: + item.LabelBG = headBG; + geo = this.FindResource("Icons.Check") as StreamGeometry; + break; case Models.DecoratorType.CurrentCommitHead: + item.LabelBG = branchBG; geo = this.FindResource("Icons.Check") as StreamGeometry; break; case Models.DecoratorType.RemoteBranchHead: + item.LabelBG = branchBG; geo = this.FindResource("Icons.Remote") as StreamGeometry; break; case Models.DecoratorType.Tag: + item.LabelBG = tagBG; geo = this.FindResource("Icons.Tag") as StreamGeometry; break; default: + item.LabelBG = branchBG; geo = this.FindResource("Icons.Branch") as StreamGeometry; break; } diff --git a/src/Views/Histories.axaml b/src/Views/Histories.axaml index b89ab8c4..931e4951 100644 --- a/src/Views/Histories.axaml +++ b/src/Views/Histories.axaml @@ -70,6 +70,7 @@ Date: Fri, 23 Aug 2024 21:00:31 +0800 Subject: [PATCH 69/93] fix: duplicated key --- src/Resources/Locales/de_DE.axaml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Resources/Locales/de_DE.axaml b/src/Resources/Locales/de_DE.axaml index bcd3e271..52684679 100644 --- a/src/Resources/Locales/de_DE.axaml +++ b/src/Resources/Locales/de_DE.axaml @@ -485,7 +485,6 @@ LOKALE BRANCHES Zum HEAD wechseln Aktiviere '--first-parent' Option - Erster-Eltern-Filter Erstelle Branch Öffne in {0} Öffne in externen Tools From 43e8c5e8a05844c856ad3babcc236a40c1442656 Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 23 Aug 2024 21:19:30 +0800 Subject: [PATCH 70/93] ux: new decorator colors for light theme (#395) --- src/Resources/Themes.axaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Resources/Themes.axaml b/src/Resources/Themes.axaml index f517b53e..32985e9c 100644 --- a/src/Resources/Themes.axaml +++ b/src/Resources/Themes.axaml @@ -13,12 +13,12 @@ #FFFAFAFA #FFB0CEE8 #FF1F1F1F - #FF6F6F6F - #FFF8F8F8 - #FFFFB835 - #fb5607 - #669900 - Black + DarkGray + #0C0E21 + #008585 + #0C0E21 + #79855f + White #FF836C2E #FFFFFFFF #FFCFCFCF @@ -52,7 +52,7 @@ #FFF8F8F8 #FFFFB835 #f4f1de - #669900 + #FF02C302 Black #FFFAFAD2 #FF252525 From 1caf02ff063362902b03ea21f3ba1b8fada2fed7 Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 23 Aug 2024 22:26:17 +0800 Subject: [PATCH 71/93] code_style: remove unused namespace using and run `dotnet format` --- src/Commands/QueryRefsContainsCommit.cs | 2 +- src/Views/CommitBaseInfo.axaml.cs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Commands/QueryRefsContainsCommit.cs b/src/Commands/QueryRefsContainsCommit.cs index df45cfc6..e3b73ccc 100644 --- a/src/Commands/QueryRefsContainsCommit.cs +++ b/src/Commands/QueryRefsContainsCommit.cs @@ -5,7 +5,7 @@ namespace SourceGit.Commands { public class QueryRefsContainsCommit : Command { - public QueryRefsContainsCommit(string repo, string commit) + public QueryRefsContainsCommit(string repo, string commit) { WorkingDirectory = repo; RaiseError = false; diff --git a/src/Views/CommitBaseInfo.axaml.cs b/src/Views/CommitBaseInfo.axaml.cs index a55e4687..9806860d 100644 --- a/src/Views/CommitBaseInfo.axaml.cs +++ b/src/Views/CommitBaseInfo.axaml.cs @@ -1,5 +1,3 @@ -using System.Threading.Tasks; - using Avalonia; using Avalonia.Collections; using Avalonia.Controls; From db8de81120ee2be80686c32760f570381b743c9e Mon Sep 17 00:00:00 2001 From: leo Date: Sat, 24 Aug 2024 11:36:02 +0800 Subject: [PATCH 72/93] refactor: rewrite file histories page to only focus on selected file (#403) --- src/Resources/Locales/en_US.axaml | 2 + src/Resources/Locales/zh_CN.axaml | 2 + src/Resources/Locales/zh_TW.axaml | 2 + src/ViewModels/FileHistories.cs | 157 ++++++++++++++++--- src/Views/FileHistories.axaml | 133 ++++++++++++---- src/Views/FileHistories.axaml.cs | 11 ++ src/Views/RevisionFileContentViewer.axaml | 58 +++++++ src/Views/RevisionFileContentViewer.axaml.cs | 13 ++ src/Views/RevisionFiles.axaml | 52 +----- 9 files changed, 327 insertions(+), 103 deletions(-) create mode 100644 src/Views/RevisionFileContentViewer.axaml create mode 100644 src/Views/RevisionFileContentViewer.axaml.cs diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index a6e1a4cf..0f275deb 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -243,6 +243,8 @@ Use Theirs (checkout --theirs) Use Mine (checkout --ours) File History + CONTENT + CHANGE FILTER Git-Flow Development Branch: diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 94c92d53..16f0df3d 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -246,6 +246,8 @@ 使用 THEIRS (checkout --theirs) 使用 MINE (checkout --ours) 文件历史 + 文件内容 + 文件变更 过滤 GIT工作流 开发分支 : diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 8e35c100..193c25c4 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -246,6 +246,8 @@ 使用 THEIRS (checkout --theirs) 使用 MINE (checkout --ours) 檔案歷史 + 檔案内容 + 檔案更改 過濾 GIT工作流 開發分支 : diff --git a/src/ViewModels/FileHistories.cs b/src/ViewModels/FileHistories.cs index e1284b2f..68c5a7a1 100644 --- a/src/ViewModels/FileHistories.cs +++ b/src/ViewModels/FileHistories.cs @@ -1,11 +1,22 @@ using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; using System.Threading.Tasks; + +using Avalonia.Media.Imaging; using Avalonia.Threading; + using CommunityToolkit.Mvvm.ComponentModel; namespace SourceGit.ViewModels { - public class FileHistories : ObservableObject + public class FileHistoriesRevisionFile(string path, object content) + { + public string Path { get; set; } = path; + public object Content { get; set; } = content; + } + + public partial class FileHistories : ObservableObject { public bool IsLoading { @@ -25,38 +36,30 @@ namespace SourceGit.ViewModels set { if (SetProperty(ref _selectedCommit, value)) - { - if (value == null) - { - DiffContext = null; - DetailContext.Commit = null; - } - else - { - DiffContext = new DiffContext(_repo.FullPath, new Models.DiffOption(value, _file), _diffContext); - DetailContext.Commit = value; - } - } + RefreshViewContent(); } } - public DiffContext DiffContext + public int ViewMode { - get => _diffContext; - set => SetProperty(ref _diffContext, value); + get => _viewMode; + set + { + if (SetProperty(ref _viewMode, value)) + RefreshViewContent(); + } } - public CommitDetail DetailContext + public object ViewContent { - get => _detailContext; - set => SetProperty(ref _detailContext, value); + get => _viewContent; + private set => SetProperty(ref _viewContent, value); } public FileHistories(Repository repo, string file) { _repo = repo; _file = file; - _detailContext = new CommitDetail(repo); Task.Run(() => { @@ -71,12 +74,122 @@ namespace SourceGit.ViewModels }); } + public void NavigateToCommit(Models.Commit commit) + { + _repo.NavigateToCommit(commit.SHA); + } + + private void RefreshViewContent() + { + if (_selectedCommit == null) + { + ViewContent = null; + return; + } + + if (_viewMode == 0) + SetViewContentAsRevisionFile(); + else + SetViewContentAsDiff(); + } + + private void SetViewContentAsRevisionFile() + { + var objs = new Commands.QueryRevisionObjects(_repo.FullPath, _selectedCommit.SHA, _file).Result(); + if (objs.Count == 0) + { + ViewContent = new FileHistoriesRevisionFile(_file, null); + return; + } + + var obj = objs[0]; + switch (obj.Type) + { + case Models.ObjectType.Blob: + Task.Run(() => + { + var isBinary = new Commands.IsBinary(_repo.FullPath, _selectedCommit.SHA, _file).Result(); + if (isBinary) + { + var ext = Path.GetExtension(_file); + if (IMG_EXTS.Contains(ext)) + { + var stream = Commands.QueryFileContent.Run(_repo.FullPath, _selectedCommit.SHA, _file); + var bitmap = stream.Length > 0 ? new Bitmap(stream) : null; + var image = new Models.RevisionImageFile() { Image = bitmap }; + Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(_file, image)); + } + else + { + var size = new Commands.QueryFileSize(_repo.FullPath, _file, _selectedCommit.SHA).Result(); + var binaryFile = new Models.RevisionBinaryFile() { Size = size }; + Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(_file, binaryFile)); + } + + return; + } + + var contentStream = Commands.QueryFileContent.Run(_repo.FullPath, _selectedCommit.SHA, _file); + var content = new StreamReader(contentStream).ReadToEnd(); + var matchLFS = REG_LFS_FORMAT().Match(content); + if (matchLFS.Success) + { + var lfs = new Models.RevisionLFSObject() { Object = new Models.LFSObject() }; + lfs.Object.Oid = matchLFS.Groups[1].Value; + lfs.Object.Size = long.Parse(matchLFS.Groups[2].Value); + Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(_file, lfs)); + } + else + { + var txt = new Models.RevisionTextFile() { FileName = obj.Path, Content = content }; + Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(_file, txt)); + } + }); + break; + case Models.ObjectType.Commit: + Task.Run(() => + { + var submoduleRoot = Path.Combine(_repo.FullPath, _file); + var commit = new Commands.QuerySingleCommit(submoduleRoot, obj.SHA).Result(); + if (commit != null) + { + var message = new Commands.QueryCommitFullMessage(submoduleRoot, obj.SHA).Result(); + var module = new Models.RevisionSubmodule() { Commit = commit, FullMessage = message }; + Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(_file, module)); + } + else + { + var module = new Models.RevisionSubmodule() { Commit = new Models.Commit() { SHA = obj.SHA }, FullMessage = "" }; + Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(_file, module)); + } + }); + break; + default: + ViewContent = new FileHistoriesRevisionFile(_file, null); + break; + } + } + + private void SetViewContentAsDiff() + { + var option = new Models.DiffOption(_selectedCommit, _file); + ViewContent = new DiffContext(_repo.FullPath, option, _viewContent as DiffContext); + } + + [GeneratedRegex(@"^version https://git-lfs.github.com/spec/v\d+\r?\noid sha256:([0-9a-f]+)\r?\nsize (\d+)[\r\n]*$")] + private static partial Regex REG_LFS_FORMAT(); + + private static readonly HashSet IMG_EXTS = new HashSet() + { + ".ico", ".bmp", ".jpg", ".png", ".jpeg" + }; + private readonly Repository _repo = null; private readonly string _file = null; private bool _isLoading = true; private List _commits = null; private Models.Commit _selectedCommit = null; - private DiffContext _diffContext = null; - private CommitDetail _detailContext = null; + private int _viewMode = 0; + private object _viewContent = null; } } diff --git a/src/Views/FileHistories.axaml b/src/Views/FileHistories.axaml index 79e716c4..6c26fbd3 100644 --- a/src/Views/FileHistories.axaml +++ b/src/Views/FileHistories.axaml @@ -22,7 +22,7 @@ - - + @@ -104,40 +111,106 @@ HorizontalAlignment="Center" VerticalAlignment="Center" IsVisible="{Binding IsLoading}"/> - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - diff --git a/src/Views/FileHistories.axaml.cs b/src/Views/FileHistories.axaml.cs index fc525f14..dad2baa9 100644 --- a/src/Views/FileHistories.axaml.cs +++ b/src/Views/FileHistories.axaml.cs @@ -27,5 +27,16 @@ namespace SourceGit.Views e.Handled = true; } + + private void OnPressCommitSHA(object sender, PointerPressedEventArgs e) + { + if (sender is TextBlock { DataContext: Models.Commit commit } && + DataContext is ViewModels.FileHistories vm) + { + vm.NavigateToCommit(commit); + } + + e.Handled = true; + } } } diff --git a/src/Views/RevisionFileContentViewer.axaml b/src/Views/RevisionFileContentViewer.axaml new file mode 100644 index 00000000..eef7605c --- /dev/null +++ b/src/Views/RevisionFileContentViewer.axaml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/RevisionFileContentViewer.axaml.cs b/src/Views/RevisionFileContentViewer.axaml.cs new file mode 100644 index 00000000..bca6a082 --- /dev/null +++ b/src/Views/RevisionFileContentViewer.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia.Controls; + +namespace SourceGit.Views +{ + public partial class RevisionFileContentViewer : UserControl + { + public RevisionFileContentViewer() + { + InitializeComponent(); + } + } +} + diff --git a/src/Views/RevisionFiles.axaml b/src/Views/RevisionFiles.axaml index bbdd5841..0165ccab 100644 --- a/src/Views/RevisionFiles.axaml +++ b/src/Views/RevisionFiles.axaml @@ -2,7 +2,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:m="using:SourceGit.Models" xmlns:vm="using:SourceGit.ViewModels" xmlns:v="using:SourceGit.Views" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" @@ -28,56 +27,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + From 9a68d70b29c76647f0935082eef2574b96f701fd Mon Sep 17 00:00:00 2001 From: leo Date: Sat, 24 Aug 2024 12:06:38 +0800 Subject: [PATCH 73/93] feature: add a button in file histories view to reset selected file to selected commit --- src/ViewModels/FileHistories.cs | 5 +++++ src/Views/FileHistories.axaml | 17 ++++++++++++++++- src/Views/FileHistories.axaml.cs | 18 ++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/ViewModels/FileHistories.cs b/src/ViewModels/FileHistories.cs index 68c5a7a1..5afd4302 100644 --- a/src/ViewModels/FileHistories.cs +++ b/src/ViewModels/FileHistories.cs @@ -79,6 +79,11 @@ namespace SourceGit.ViewModels _repo.NavigateToCommit(commit.SHA); } + public void ResetToSelectedRevision() + { + new Commands.Checkout(_repo.FullPath).FileWithRevision(_file, $"{_selectedCommit.SHA}"); + } + private void RefreshViewContent() { if (_selectedCommit == null) diff --git a/src/Views/FileHistories.axaml b/src/Views/FileHistories.axaml index 6c26fbd3..a00737fb 100644 --- a/src/Views/FileHistories.axaml +++ b/src/Views/FileHistories.axaml @@ -111,7 +111,7 @@ HorizontalAlignment="Center" VerticalAlignment="Center" IsVisible="{Binding IsLoading}"/> - + + + diff --git a/src/Views/Histories.axaml.cs b/src/Views/Histories.axaml.cs index 8ad7256a..a0828459 100644 --- a/src/Views/Histories.axaml.cs +++ b/src/Views/Histories.axaml.cs @@ -184,7 +184,7 @@ namespace SourceGit.Views if (change.Property == SubjectProperty || change.Property == IssueTrackerRulesProperty) { - Inlines.Clear(); + Inlines!.Clear(); _matches = null; ClearHoveredIssueLink(); diff --git a/src/Views/Preference.axaml b/src/Views/Preference.axaml index 7576ab4e..e13fd7cd 100644 --- a/src/Views/Preference.axaml +++ b/src/Views/Preference.axaml @@ -2,7 +2,6 @@ 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:sys="clr-namespace:System;assembly=mscorlib" xmlns:m="using:SourceGit.Models" xmlns:c="using:SourceGit.Converters" xmlns:vm="using:SourceGit.ViewModels" @@ -57,7 +56,7 @@ - + - - - - - - + - - From 5dc4ed6f543f6f14c16c6f0b80d500bc88bc15be Mon Sep 17 00:00:00 2001 From: leo Date: Sun, 25 Aug 2024 11:47:39 +0800 Subject: [PATCH 84/93] localization: en_US translation for `Text.CommitDetail.Info.ContainsIn.Title` --- src/Resources/Locales/en_US.axaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index afdc191a..c729451f 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -117,7 +117,7 @@ CHANGED COMMITTER Check refs that contains this commit - COMMIT CONTAINS IN + COMMIT IS CONTAINED BY Shows only the first 100 changes. See all changes on the CHANGES tab. MESSAGE PARENTS From 7a2722e928d35059b60b8f9da4416a7a02edf6e6 Mon Sep 17 00:00:00 2001 From: leo Date: Sun, 25 Aug 2024 15:11:31 +0800 Subject: [PATCH 85/93] feature: add a context menu to copy entire content of SelectableTextBlock (#394) --- src/App.Commands.cs | 65 +++++++++++++++++++++++++++++++ src/App.axaml.cs | 35 ++--------------- 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 | 9 +++++ 6 files changed, 81 insertions(+), 31 deletions(-) create mode 100644 src/App.Commands.cs diff --git a/src/App.Commands.cs b/src/App.Commands.cs new file mode 100644 index 00000000..55241afc --- /dev/null +++ b/src/App.Commands.cs @@ -0,0 +1,65 @@ +using System; +using System.Windows.Input; +using Avalonia.Controls; + +namespace SourceGit +{ + public partial class App + { + public class SimpleCommand : ICommand + { + public event EventHandler CanExecuteChanged + { + add { } + remove { } + } + + public SimpleCommand(Action action) + { + _action = action; + } + + public bool CanExecute(object parameter) => _action != null; + public void Execute(object parameter) => _action?.Invoke(); + + private Action _action = null; + } + + public class ParameterCommand : ICommand + { + public event EventHandler CanExecuteChanged + { + add { } + remove { } + } + + public ParameterCommand(Action action) + { + _action = action; + } + + public bool CanExecute(object parameter) => _action != null; + public void Execute(object parameter) => _action?.Invoke(parameter); + + private Action _action = null; + } + + public static readonly SimpleCommand OpenPreferenceCommand = new SimpleCommand(() => OpenDialog(new Views.Preference())); + public static readonly SimpleCommand OpenHotkeysCommand = new SimpleCommand(() => OpenDialog(new Views.Hotkeys())); + public static readonly SimpleCommand OpenAppDataDirCommand = new SimpleCommand(() => Native.OS.OpenInFileManager(Native.OS.DataDir)); + public static readonly SimpleCommand OpenAboutCommand = new SimpleCommand(() => OpenDialog(new Views.About())); + public static readonly SimpleCommand CheckForUpdateCommand = new SimpleCommand(() => Check4Update(true)); + public static readonly SimpleCommand QuitCommand = new SimpleCommand(() => Quit(0)); + + public static readonly ParameterCommand CopyTextCommand = new ParameterCommand(param => + { + if (param is TextBlock textBlock) + { + if (textBlock.Inlines is { Count: > 0 } inlines) + CopyText(inlines.Text); + else + CopyText(textBlock.Text); + } + }); + } +} diff --git a/src/App.axaml.cs b/src/App.axaml.cs index 0ae1a4c4..5dab4836 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -6,7 +6,6 @@ using System.Reflection; using System.Text; using System.Text.Json; using System.Threading.Tasks; -using System.Windows.Input; using Avalonia; using Avalonia.Controls; @@ -21,34 +20,8 @@ using Avalonia.Threading; namespace SourceGit { - public class SimpleCommand : ICommand - { - public event EventHandler CanExecuteChanged - { - add { } - remove { } - } - - public SimpleCommand(Action action) - { - _action = action; - } - - public bool CanExecute(object parameter) => _action != null; - public void Execute(object parameter) => _action?.Invoke(); - - private Action _action = null; - } - public partial class App : Application { - public static readonly SimpleCommand OpenPreferenceCommand = new SimpleCommand(() => OpenDialog(new Views.Preference())); - public static readonly SimpleCommand OpenHotkeysCommand = new SimpleCommand(() => OpenDialog(new Views.Hotkeys())); - public static readonly SimpleCommand OpenAppDataDirCommand = new SimpleCommand(() => Native.OS.OpenInFileManager(Native.OS.DataDir)); - public static readonly SimpleCommand OpenAboutCommand = new SimpleCommand(() => OpenDialog(new Views.About())); - public static readonly SimpleCommand CheckForUpdateCommand = new SimpleCommand(() => Check4Update(true)); - public static readonly SimpleCommand QuitCommand = new SimpleCommand(() => Quit(0)); - [STAThread] public static void Main(string[] args) { @@ -125,8 +98,8 @@ namespace SourceGit public static void OpenDialog(Window window) { - if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - window.ShowDialog(desktop.MainWindow); + if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner}) + window.ShowDialog(owner); } public static void RaiseException(string context, string message) @@ -260,7 +233,7 @@ namespace SourceGit if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { if (desktop.MainWindow?.Clipboard is { } clipboard) - await clipboard.SetTextAsync(data); + await clipboard.SetTextAsync(data ?? ""); } } @@ -305,7 +278,7 @@ namespace SourceGit public static IStorageProvider GetStorageProvider() { if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - return desktop.MainWindow.StorageProvider; + return desktop.MainWindow?.StorageProvider; return null; } diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index c729451f..223d8955 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -145,6 +145,7 @@ User Name User name for this repository Copy + Copy All Text COPY MESSAGE Copy Path Copy File Name diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index e86b8e52..a521ca2b 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -148,6 +148,7 @@ 用户名 应用于本仓库的用户名 复制 + 复制全部文本 复制内容 复制路径 复制文件名 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 7fe6610e..8f73e7ca 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -148,6 +148,7 @@ 使用者名稱 應用於本倉庫的使用者名稱 複製 + 複製全部内容 複製內容 複製路徑 複製檔名 diff --git a/src/Resources/Styles.axaml b/src/Resources/Styles.axaml index 3323cee8..a267e144 100644 --- a/src/Resources/Styles.axaml +++ b/src/Resources/Styles.axaml @@ -1,5 +1,6 @@ + + + + + + From 2b7b1e81e0fefc5ce10b634d6b8ca4d801872f83 Mon Sep 17 00:00:00 2001 From: leo Date: Sun, 25 Aug 2024 18:17:45 +0800 Subject: [PATCH 86/93] ux: force using arrow cursor when hover a menu item --- src/Resources/Styles.axaml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Resources/Styles.axaml b/src/Resources/Styles.axaml index a267e144..497e9f49 100644 --- a/src/Resources/Styles.axaml +++ b/src/Resources/Styles.axaml @@ -739,6 +739,7 @@ + From 585cf1416205d21458f2cf01eeb05512c42ff33d Mon Sep 17 00:00:00 2001 From: leo Date: Sun, 25 Aug 2024 18:31:05 +0800 Subject: [PATCH 87/93] enhance: new welcome page will clear the search filter before --- src/ViewModels/LauncherPage.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ViewModels/LauncherPage.cs b/src/ViewModels/LauncherPage.cs index 65d5f7a5..4e831dbc 100644 --- a/src/ViewModels/LauncherPage.cs +++ b/src/ViewModels/LauncherPage.cs @@ -28,6 +28,9 @@ namespace SourceGit.ViewModels { _node = new RepositoryNode() { Id = Guid.NewGuid().ToString() }; _data = Welcome.Instance; + + // New welcome page will clear the search filter before. + Welcome.Instance.ClearSearchFilter(); } public LauncherPage(RepositoryNode node, Repository repo) From e8b0aa9a7d901c7d47d6d65bca20de725f9b9fa0 Mon Sep 17 00:00:00 2001 From: Chiahong Hong Date: Sun, 25 Aug 2024 20:01:07 +0800 Subject: [PATCH 88/93] localization: update zh_TW.axaml --- src/Resources/Locales/zh_TW.axaml | 877 +++++++++++++++--------------- 1 file changed, 439 insertions(+), 438 deletions(-) diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 8f73e7ca..15512286 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -2,209 +2,209 @@ - 關於軟體 - 關於本軟體 + 關於 + 關於 SourceGit • 專案依賴於 © 2024 sourcegit-scm • 文字編輯器使用 • 等寬字型來自於 - • 專案原始碼地址 - 開源免費的Git客戶端 - 新增工作樹 - 檢出分支方式 : + • 專案原始碼網址 + 開源免費的 Git 客戶端 + 新增工作區 + 簽出分支方式: 已有分支 - 創建新分支 - 工作樹路徑 : - 填寫該工作樹的路徑。支援相對路徑。 - 分支名 : - 選填。 預設使用目標資料夾名稱。 - 跟蹤分支 - 設置上游跟蹤分支 - 應用補丁(apply) + 建立新分支 + 工作區路徑: + 填寫該工作區的路徑。支援相對路徑。 + 分支名稱: + 選填。預設使用目標資料夾名稱。 + 追蹤分支 + 設定遠端追蹤分支 + 套用修補檔 (apply patch) 錯誤 - 輸出錯誤,並終止應用補丁 + 輸出錯誤,並中止套用修補檔 更多錯誤 - 與【錯誤】級別相似,但輸出內容更多 - 補丁檔案 : - 選擇補丁檔案 + 與 [錯誤] 級別相似,但輸出更多內容 + 修補檔: + 選擇修補檔 忽略空白符號 忽略 關閉所有警告 - 應用補丁 + 套用修補檔 警告 - 應用補丁,輸出關於空白符的警告 - 空白符號處理 : - 存檔(archive) ... - 存檔檔案路徑: - 選擇存檔檔案的存放路徑 - 指定的提交: - 存檔 + 套用修補檔,輸出關於空白字元的警告 + 空白字元處理: + 封存 (archive)... + 封存檔案路徑: + 選擇封存檔案的儲存路徑 + 指定的提交: + 封存 SourceGit Askpass - 不跟蹤更改的檔案 - 沒有不跟蹤更改的檔案 + 不追蹤變更的檔案 + 沒有不追蹤變更的檔案 移除 - 二進位制檔案不支援該操作!!! - 逐行追溯(blame) - 選中檔案不支援該操作!!! - 檢出(checkout) ${0}$... + 二進位檔案不支援該操作! + 逐行溯源 (blame) + 所選擇的檔案不支援該操作! + 簽出 (checkout) ${0}$... 與其他分支比較 - 與當前HEAD比較 - 與本地工作樹比較 - 複製分支名 + 與目前 HEAD 比較 + 與本機工作區比較 + 複製分支名稱 刪除 ${0}$... - 刪除選中的 {0} 個分支 - 放棄所有更改 - 快進(fast-forward)到 ${0}$ - GIT工作流 - 完成 ${0}$ + 刪除所選的 {0} 個分支 + 捨棄所有變更 + 快轉 (fast-forward) 到 ${0}$ + Git 工作流 - 完成 ${0}$ 合併 ${0}$ 到 ${1}$... - 拉回(pull) ${0}$ - 拉回(pull) ${0}$ 內容至 ${1}$... - 推送(push)${0}$ - 變基(rebase) ${0}$ 分支至 ${1}$... + 拉取 (pull) ${0}$ + 拉取 (pull) ${0}$ 內容至 ${1}$... + 推送 (push) ${0}$ + 重定基底 (rebase) ${0}$ 分支至 ${1}$... 重新命名 ${0}$... 切換上游分支 - 取消追蹤 + 取消設定上游分支 分支比較 位元組 - 取 消 - 重置檔案到該版本 - 重置檔案到上一版本 + 取 消 + 重設檔案為此版本 + 重設檔案到上一版本 切換變更顯示模式 - 檔名+路徑列表模式 + 檔案名稱 + 路徑列表模式 全路徑列表模式 - 檔案目錄樹形結構模式 - 檢出(checkout)分支 - 檢出(checkout)提交 - 注意:執行該操作後,當前HEAD會變為遊離(detached)狀態! - 提交 : - 目標分支 : - 未提交更改 : - 丟棄更改 + 檔案目錄樹狀結構模式 + 簽出 (checkout) 分支 + 簽出 (checkout) 提交 + 注意: 執行該操作後,目前 HEAD 會變為分離 (detached) 狀態! + 提交: + 目標分支: + 未提交變更: + 捨棄變更 不做處理 - 儲藏並自動恢復 - 挑選(cherry-pick)此提交 - 提交ID : - 提交變化 - 挑選提交 - 丟棄儲藏確認 - 您正在丟棄所有的儲藏,一經操作,無法回退,是否繼續? - 克隆遠端倉庫 - 額外引數 : - 其他克隆引數,選填。 - 本地倉庫名 : - 本地倉庫目錄的名字,選填。 - 父級目錄 : - 遠端倉庫 : + 擱置變更並自動復原 + 揀選 (cherry-pick) 此提交 + 提交編號: + 提交變更 + 揀選提交 + 捨棄擱置變更確認 + 您正在捨棄所有的擱置變更,一經操作便無法復原,是否繼續? + 複製 (clone) 遠端存放庫 + 額外參數: + 其他複製參數,選填。 + 本機存放庫名稱: + 本機存放庫目錄的名稱,選填。 + 父級目錄: + 遠端存放庫: 關閉 - 提交資訊編輯器 - 挑選(cherry-pick)此提交 - 檢出此提交 - 與當前HEAD比較 - 與本地工作樹比較 - 複製簡要資訊 - 複製提交指紋 - 互動式變基(rebase -i) ${0}$ 到此處 - 變基(rebase) ${0}$ 到此處 - 重置(reset) ${0}$ 到此處 - 回滾此提交 - 編輯提交資訊 - 另存為補丁 ... + 提交訊息編輯器 + 揀選 (cherry-pick) 此提交 + 簽出 (checkout) 此提交 + 與目前 HEAD 比較 + 與本機工作區比較 + 複製摘要資訊 + 複製提交編號 + 互動式重定基底 (rebase -i) ${0}$ 到此處 + 重定基底 (rebase) ${0}$ 到此處 + 重設 (reset) ${0}$ 到此處 + 復原此提交 + 編輯提交訊息 + 另存為修補檔 (patch)... 合併此提交到上一個提交 變更對比 - 查詢變更... + 搜尋變更... 檔案列表 - LFS檔案 + LFS 檔案 子模組 基本資訊 - 修改者 + 作者 變更列表 提交者 - 查看包含此提交的分支/標籤 - 本提交已被以下分支/標籤包含 - 僅顯示前100項變更。 請前往『變更對比』頁面查看全部。 - 提交資訊 - 父提交 - 相關引用 - 提交指紋 - 填寫提交信息主題 + 檢視包含此提交的分支或標籤 + 本提交包含於以下分支或標籤 + 僅顯示前 100 項變更。請前往 [變更對比] 頁面以瀏覽所有變更。 + 提交訊息 + 前次提交 + 相關參照 + 提交編號 + 填寫提交訊息標題 詳細描述 - 倉庫配置 - 提交資訊範本 - 範本名稱 : - 範本內容 : - 電子郵箱 - 郵箱地址 - GIT配置 - ISSUE追蹤 - 新增匹配Github Issue規則 - 新增匹配Jira規則 - 新增自定義規則 - 匹配ISSUE的正則表達式 : - 規則名 : - 為ISSUE生成的URL連結 : - 可在URL中使用$1,$2等變數填入正則表示式匹配的內容 - HTTP代理 - HTTP網路代理 + 存放庫設定 + 提交訊息範本 + 範本名稱: + 範本內容: + 電子郵件 + 電子郵件地址 + Git 設定 + Issue 追蹤 + 新增符合 Github Issue 規則 + 新增符合 Jira 規則 + 新增自訂規則 + 符合 Issue 的正則表達式: + 規則名稱: + 為 Issue 產生的網址連結: + 可在網址中使用 $1、$2 等變數填入正則表示式相符的內容 + HTTP 代理 + HTTP 網路代理 使用者名稱 - 應用於本倉庫的使用者名稱 + 用於本存放庫的使用者名稱 複製 - 複製全部内容 + 複製全部內容 複製內容 複製路徑 - 複製檔名 - 新建分支 ... - 新分支基於 : + 複製檔案名稱 + 新增分支... + 新分支基於: 完成後切換到新分支 - 未提交更改 : - 丟棄更改 + 未提交變更: + 捨棄變更 不做處理 - 儲藏並自動恢復 - 新分支名 : - 填寫分支名稱。 - 建立本地分支 - 新建標籤 ... - 標籤位於 : - 使用GPG簽名 - 標籤描述 : + 擱置變更並自動復原 + 新分支名稱: + 輸入分支名稱。 + 建立本機分支 + 新增標籤... + 標籤位於: + 使用 GPG 簽名 + 標籤描述: 選填。 - 標籤名 : - 推薦格式 :v1.0.0-alpha - 推送到所有遠端倉庫 - 新建標籤 - 型別 : + 標籤名稱: + 建議格式: v1.0.0-alpha + 推送到所有遠端存放庫 + 新增標籤 + 類型: 附註標籤 輕量標籤 - 按住Ctrl鍵點擊將以預設參數運行 + 按住 Ctrl 鍵將直接以預設參數執行 剪下 刪除分支確認 - 分支名 : - 您正在刪除遠端上的分支,請務必小心!!! + 分支名稱: + 您正在刪除遠端上的分支,請務必小心! 同時刪除遠端分支 ${0}$ 刪除多個分支 - 您正在嘗試一次性刪除多個分支,請務必仔細檢查後再執行操作! + 您正在嘗試一次性刪除多個分支,請務必仔細檢查後再刪除! 刪除遠端確認 - 遠端名 : - 目標 : - 刪除分組確認 - 刪除倉庫確認 + 遠端名稱: + 目標: + 刪除群組確認 + 刪除存放庫確認 刪除子模組確認 - 子模組路徑 : + 子模組路徑: 刪除標籤確認 - 標籤名 : - 同時刪除遠端倉庫中的此標籤 - 二進位制檔案 - 當前大小 + 標籤名稱: + 同時刪除遠端存放庫中的此標籤 + 二進位檔案 + 目前大小 原始大小 複製 - 檔案許可權已變化 - LFS物件變更 + 檔案權限已變更 + LFS 物件變更 下一個差異 - 沒有變更或僅有換行符差異 + 沒有變更或僅有換行字元差異 上一個差異 - 分列對比 + 並排對比 子模組 新增 - 語法高亮 + 語法上色 自動換行 使用外部合併工具檢視 減少可見的行數 @@ -213,413 +213,414 @@ 顯示隱藏符號 交換比對雙方 使用外部比對工具檢視 - 放棄更改確認 - 所有本地址未提交的修改。 - 變更 : - 總計{0}項選中更改 - 本操作不支援回退,請確認後繼續!!! - 書籤 : - 名稱 : - 目標 : - 編輯分組 - 編輯倉庫 - 快進(fast-forward,無需checkout) - 拉取(fetch) - 拉取所有的遠端倉庫 - 不拉取遠端標籤 + 捨棄變更 + 所有本機未提交的變更。 + 變更: + 將捨棄總計 {0} 項已選取的變更 + 您無法復原此操作,請確認後再繼續! + 書籤: + 名稱: + 目標: + 編輯群組 + 編輯存放庫 + 快進 (fast-forward,無需 checkout) + 提取 (fetch) + 提取所有的遠端存放庫 + 不提取遠端標籤 自動清理遠端已刪除分支 - 遠端倉庫 : - 拉取遠端倉庫內容 - 不跟蹤此檔案的更改 - 放棄更改... - 放棄 {0} 個檔案的更改... - 放棄選中的更改 + 遠端存放庫: + 提取遠端存放庫內容 + 不追蹤此檔案的變更 + 捨棄變更... + 捨棄已選的 {0} 個檔案變更... + 捨棄選取的變更 使用外部合併工具開啟 - 另存為補丁... - 暫存(add) - 暫存(add){0} 個檔案 - 暫存選中的更改 - 儲藏(stash)... - 儲藏(stash)選中的 {0} 個檔案... - 從暫存中移除 + 另存為修補檔 (patch)... + 暫存 (add) + 暫存 (add) 已選的 {0} 個檔案 + 暫存選取的變更 + 擱置變更 (stash)... + 擱置變更 (stash) 所選的 {0} 個檔案... + 取消暫存 從暫存中移除 {0} 個檔案 - 從暫存中移除選中的更改 - 使用 THEIRS (checkout --theirs) - 使用 MINE (checkout --ours) + 取消暫存選取的變更 + 使用對方版本 (checkout --theirs) + 使用我方版本 (checkout --ours) 檔案歷史 檔案内容 - 檔案更改 - 過濾 - GIT工作流 - 開發分支 : - 特性分支 : - 特性分支名字首 : - 結束特性分支 - 結束脩復分支 - 結束版本分支 - 目標分支 : - 修復分支 : - 修復分支名字首 : - 初始化GIT工作流 + 檔案變更 + 篩選 + Git 工作流 + 開發分支: + 功能分支: + 功能分支前置詞: + 完成功能分支 + 完成修復分支 + 完成發行分支 + 目標分支: + 修復分支: + 修復分支前置詞: + 初始化 Git 工作流 保留分支 - 釋出分支 : - 版本分支 : - 版本分支名字首 : - 開始特性分支... - 開始特性分支 + 發行分支: + 版本分支: + 發行分支前置詞: + 開始功能分支... + 開始功能分支 開始修復分支... 開始修復分支 - 輸入分支名 - 開始版本分支... - 開始版本分支 - 版本標籤字首 : + 輸入分支名稱 + 開始發行分支... + 開始發行分支 + 版本標籤前置詞: Git LFS - 添加追蹤檔案規則... - 匹配完整檔案名 - 規則 : - 添加LFS追蹤檔案規則 - 拉取LFS物件 (fetch) - 拉取LFS物件 - 執行`git lfs fetch`命令,下載遠端LFS物件,但不會更新工作副本。 - 啟用Git LFS支援 - 顯示LFS物件鎖 - 沒有鎖定的LFS物件 + 加入追蹤檔案規則... + 符合完整檔案名稱 + 規則: + 加入 LFS 追蹤檔案規則 + 提取 (fetch) + 提取 LFS 物件 + 執行 `git lfs fetch` 以下載遠端 LFS 物件,但不會更新工作副本。 + 啟用 Git LFS 支援 + 顯示 LFS 物件鎖 + 沒有鎖定的 LFS 物件 鎖定 - LFS物件鎖 + LFS 物件鎖 解鎖 強制解鎖 - 精簡本地LFS物件存儲 - 執行`git lfs prune`命令,從本地存儲中精簡當前版本不需要的LFS物件 - 拉回LFS物件 (pull) - 拉回LFS物件 - 執行`git lfs pull`命令,下載遠端LFS物件并更新工作副本。 - 推送 - 推送LFS物件 - 將排隊的大檔推送到Git LFS遠端服務 - 遠端倉庫 : - 跟蹤名為'{0}'的檔案 - 跟蹤所有 *{0} 檔案 + 清理 (prune) + 執行 `git lfs prune` 以從本機中清理目前版本不需要的 LFS 物件 + 拉取 (pull) + 拉取 LFS 物件 + 執行 `git lfs pull` 以下載遠端 LFS 物件並更新工作副本。 + 推送 (push) + 推送 LFS 物件 + 將大型檔案推送到 Git LFS 遠端服務 + 遠端存放庫: + 追蹤名稱為「{0}」的檔案 + 追蹤所有 *{0} 檔案 歷史記錄 切換橫向/縱向顯示 切換曲線/折線顯示 作者 - 路線圖與主題 - 提交指紋 + 路線圖與訊息標題 + 提交編號 提交時間 - 查詢提交指紋、資訊、作者。回車鍵開始,ESC鍵取消 + 搜尋提交編號、訊息、作者。按下 Enter 鍵以搜尋,ESC 鍵取消 清空 - 已選中 {0} 項提交 - 快捷鍵參考 - 全域性快捷鍵 + 已選取 {0} 項提交 + 快速鍵參考 + 全域快速鍵 取消彈出面板 - 關閉當前頁面 + 關閉目前頁面 切換到上一個頁面 切換到下一個頁面 - 新建頁面 + 新增頁面 開啟偏好設定面板 - 倉庫頁面快捷鍵 + 存放庫頁面快速鍵 提交暫存區變更 - 提交暫存區變更併推送 - 切換左邊欄為分支/標籤等顯示模式(預設) - 重新載入倉庫狀態 - 將選中的變更暫存或從暫存列表中移除 + 提交暫存區變更並推送 + 切換左邊欄為分支/標籤等顯示模式 (預設) + 強制重新載入存放庫 + 暫存選取的變更或從暫存列表中移除 切換左邊欄為歷史搜尋模式 - 顯示本地更改 + 顯示本機變更 顯示歷史記錄 - 顯示儲藏列表 - 文字編輯器 - 關閉搜尋 - 定位到下一個匹配搜尋的位置 - 定位到上一個匹配搜尋的位置 - 開啟搜尋 + 顯示擱置變更列表 + 文字編輯器快速鍵 + 關閉搜尋面板 + 前往下一個搜尋相符的位置 + 前往上一個搜尋相符的位置 + 開啟搜尋面板 暫存 - 移出暫存區 - 丟棄 - 初始化新倉庫 - 路徑 : - 選擇目錄不是有效的Git倉庫。是否需要在此目錄執行`git init`操作? - 挑選(Cherry-Pick)操作進行中。點選【終止】回滾到操作前的狀態。 - 合併操作進行中。點選【終止】回滾到操作前的狀態。 - 變基(Rebase)操作進行中。點選【終止】回滾到操作前的狀態。 - 回滾提交操作進行中。點選【終止】回滾到操作前的狀態。 - 互動式變基 - 目標分支 : - 起始提交 : + 取消暫存 + 捨棄 + 初始化存放庫 + 路徑: + 選擇目錄並非有效的 Git 存放庫。是否要在此目錄執行 `git init` 以進行初始化? + 揀選 (cherry-pick) 操作進行中。點選 [中止] 復原到操作前的狀態。 + 合併操作進行中。點選 [中止] 復原到操作前的狀態。 + 重定基底 (rebase) 操作進行中。點選 [中止] 復原到操作前的狀態。 + 復原提交操作進行中。點選 [中止] 復原到操作前的狀態。 + 互動式重定基底 + 目標分支: + 起始提交: 向上移動 向下移動 Source Git - 出錯了 + 發生錯誤 系統提示 主選單 合併分支 - 目標分支 : - 合併方式 : - 合併分支 : - 名稱 : - GIT尚未配置。請開啟【偏好設定】配置GIT路徑。 + 目標分支: + 合併方式: + 合併分支: + 名稱: + 尚未設定 Git。請開啟 [偏好設定] 以設定 Git 路徑。 系統提示 瀏覽程式資料目錄 選擇資料夾 開啟檔案... 選填。 - 新建空白頁 + 新增分頁 設定書籤 - 關閉標籤頁 - 關閉其他標籤頁 - 關閉右側標籤頁 - 複製倉庫路徑 - 新標籤頁 + 關閉分頁 + 關閉其他分頁 + 關閉右側分頁 + 複製存放庫路徑 + 新分頁 貼上 剛剛 - {0}分鐘前 - {0}小時前 + {0} 分鐘前 + {0} 小時前 昨天 - {0}天前 + {0} 天前 上個月 - {0}個月前 + {0} 個月前 一年前 - {0}年前 + {0} 年前 偏好設定 - 外觀配置 + 外觀設定 預設字型 預設字型大小 等寬字型 - 僅在文字編輯器中使用等寬字體 - 主題 - 主題自訂 - 使用固定寬度的標題欄標籤 - 使用系統預設窗口樣式 - 通用配置 - 啟動時檢測軟體更新 + 僅在文字編輯器中使用等寬字型 + 佈景主題 + 自訂主題 + 使用固定寬度的分頁標籤 + 使用系統原生預設視窗樣式 + 一般設定 + 啟動時檢查軟體更新 顯示語言 最大歷史提交數 - 啟動時恢復上次開啟的倉庫 - SUBJECT字數檢測 - GIT配置 - 啟用定時自動拉取遠端更新 - 自動拉取間隔 + 啟動時還原上次開啟的存放庫 + 提交標題字數偵測 + Git 設定 + 啟用定時自動提取 (fetch) 遠端更新 + 自動提取間隔 分鐘 自動換行轉換 - 預設克隆路徑 - 郵箱 - 預設GIT使用者郵箱 + 預設複製 (clone) 路徑 + 電子郵件 + 預設 Git 使用者電子郵件 安裝路徑 - 終端Shell + 終端 Shell 使用者名稱 - 預設GIT使用者名稱 + 預設 Git 使用者名稱 Git 版本 - 本軟體要求GIT最低版本為2.23.0 - GPG簽名 + 本軟體要求 Git 最低版本為 2.23.0 + GPG 簽名 啟用提交簽名 啟用標籤簽名 - GPG簽名格式 - 可執行檔案位置 - gpg.exe所在路徑 - 使用者簽名KEY - 輸入簽名提交所使用的KEY + GPG 簽名格式 + 可執行檔案路徑 + 填寫 gpg.exe 所在路徑 + 使用者簽名金鑰 + 填寫簽名提交所使用的金鑰 對比/合併工具 安裝路徑 - 填寫工具可執行檔案所在位置 + 填寫可執行檔案所在路徑 工具 清理遠端已刪除分支 - 目標 : - 清理工作樹 - 清理在`$GIT_DIR/worktrees`中的無效工作樹資訊 - 拉回(pull) - 拉取分支 : - 拉取遠端中的所有分支變更 - 本地分支 : - 未提交更改 : - 丟棄更改 + 目標: + 清理工作區 + 清理在 `$GIT_DIR/worktrees` 中的無效工作區資訊 + 拉取 (pull) + 拉取分支: + 拉取遠端中的所有分支 + 本機分支: + 未提交變更: + 捨棄變更 不做處理 - 儲藏並自動恢復 + 擱置變更並自動復原 不拉取遠端標籤 - 遠端 : - 拉回(拉取併合並) - 使用變基方式合併分支 - 推送(push) - 確保子模組變更已推送 + 遠端: + 拉取 (提取並合併) + 使用重定基底 (rebase) 合併分支 + 推送 (push) + 確保已推送子模組 啟用強制推送 - 本地分支 : - 遠端倉庫 : - 推送到遠端倉庫 - 遠端分支 : - 跟蹤遠端分支 + 本機分支: + 遠端存放庫: + 推送到遠端存放庫 + 遠端分支: + 追蹤遠端分支 同時推送標籤 - 推送標籤到遠端倉庫 - 推送到所有遠端倉庫 - 遠端倉庫 : - 標籤 : - 退出 - 變基(rebase)操作 - 自動儲藏並恢復本地變更 - 目標提交 : - 分支 : + 推送標籤到遠端存放庫 + 推送到所有遠端存放庫 + 遠端存放庫: + 標籤: + 結束 + 重定基底 (rebase) 操作 + 自動擱置變更並復原本機變更 + 目標提交: + 分支: 重新載入 - 新增遠端倉庫 - 編輯遠端倉庫 - 遠端名 : - 唯一遠端名 - 倉庫地址 : - 遠端倉庫的地址 - 複製遠端地址 - 刪除 ... - 編輯 ... - 拉取(fetch)更新 - 在瀏覽器中訪問網址 + 新增遠端存放庫 + 編輯遠端存放庫 + 遠端名稱: + 唯一遠端名稱 + 存放庫網址: + 遠端存放庫的網址 + 複製遠端網址 + 刪除... + 編輯... + 提取 (fetch) 更新 + 在瀏覽器中存取網址 清理遠端已刪除分支 - 刪除工作樹操作確認 - 啟用`--force`選項 - 目標工作樹 : + 目標: + 刪除工作區操作確認 + 啟用 [--force] 選項 + 目標工作區: 分支重新命名 - 新的名稱 : - 新的分支名不能與現有分支名相同 - 分支 : - 終止合併 - 清理本倉庫(GC) - 本操作將執行`git gc`命令。 - 清空過濾規則 - 配置本倉庫 + 新名稱: + 新的分支名稱不能與現有分支名稱相同 + 分支: + 中止 + 清理本存放庫 (GC) + 本操作將執行 `git gc` 命令。 + 清空篩選規則 + 設定本存放庫 下一步 在檔案瀏覽器中開啟 - 過濾規則 : - 本地分支 - 定位HEAD - 启用 -first-parent 过滤选项 - 新建分支 + 篩選規則: + 本機分支 + 回到 HEAD + 啟用 [--first-parent] 選項 + 新增分支 在 {0} 中開啟 使用外部工具開啟 重新載入 遠端列表 新增遠端 解決衝突 - 查詢提交 - 查詢方式 + 搜尋提交 + 搜尋方式 檔案 - 提交資訊 - 提交指紋 + 提交訊息 + 提交編號 作者及提交者 - 快速查找分支、標籤 + 快速搜尋分支、標籤 以樹型結構展示 提交統計 子模組列表 新增子模組 更新子模組 標籤列表 - 新建標籤 - 在終端中開啟 - 工作樹清單 - 新建工作樹 + 新增標籤 + 在終端機中開啟 + 工作區列表 + 新增工作區 清理 - 遠端倉庫地址 - 重置(reset)當前分支到指定版本 - 重置模式 : - 提交 : - 當前分支 : + 遠端存放庫網址 + 重設目前分支到指定版本 + 重設模式: + 移至提交: + 目前分支: 在檔案瀏覽器中檢視 - 回滾操作確認 - 目標提交 : - 回滾後提交更改 - 編輯提交資訊 - 請使用Shift+Enter換行。Enter鍵已被【確 定】按鈕佔用。 + 復原操作確認 + 目標提交: + 復原後提交變更 + 編輯提交訊息 + 請使用 Shift + Enter 換行。Enter 鍵已被 [確定] 按鈕佔用。 執行操作中,請耐心等待... - 保 存 - 另存為... - 補丁已成功儲存! - 檢測更新... - 檢測到軟體有版本更新: - 獲取最新版本資訊失敗! - 下 載 + 儲存 + 另存新檔... + 修補檔已成功儲存! + 檢查更新... + 軟體有版本更新: + 獲取最新版本資訊失敗! + 下載 忽略此版本 軟體更新 - 當前已是最新版本。 - 合併HEAD到上一個提交 - SSH金鑰 : - SSH金鑰檔案 - 開 始 - 儲藏(stash) - 包含未跟蹤的檔案 - 資訊 : - 選填,用於命名此儲藏 - 儲藏本地變更 - 應用(apply) - 刪除(drop) - 應用並刪除(pop) - 丟棄儲藏確認 - 丟棄儲藏 : - 儲藏列表 + 目前已是最新版本。 + 合併 HEAD 至前次提交 + SSH 金鑰: + SSH 金鑰檔案 + 開 始 + 擱置變更 (stash) + 包含未追蹤的檔案 + 擱置變更訊息: + 選填,用於命名此擱置變更 + 擱置變更本機變更 + 套用 (apply) + 刪除 (drop) + 套用並刪除 (pop) + 捨棄擱置變更確認 + 捨棄擱置變更: + 擱置變更 檢視變更 - 儲藏列表 + 擱置變更列表 提交統計 提交次數 提交者 本月 本週 本年 - 提交次數: - 提交者: + 提交次數: + 提交者: 子模組 新增子模組 複製路徑 - 拉取子孫模組 - 開啟倉庫 - 相對倉庫路徑 : - 本地存放的相對路徑。 + 提取子模組 + 開啟存放庫 + 相對存放庫路徑: + 本機存放的相對路徑。 刪除子模組 - 確 定 - 複製標籤名 + 確 定 + 複製標籤名稱 刪除 ${0}$... 推送 ${0}$... - 倉庫地址 : + 存放庫網址: 更新子模組 更新所有子模組 - 啟用『--init』選項 - 啟用『--recursive』選項 - 子模組 : - 啟用『--remote』選項 + 啟用 [--init] 選項 + 啟用 [--recursive] 選項 + 子模組: + 啟用 [--remote] 選項 警告 起始頁 - 新建分組 - 新建子分組 - 克隆遠端倉庫 + 新增群組 + 新增子群組 + 複製 (clone) 遠端存放庫 刪除 - 支援拖放目錄新增。支援自定義分組。 + 支援拖放以新增目錄與自訂群組。 編輯 - 打開所有包含倉庫 - 開啟本地倉庫 - 開啟終端 - 快速查詢倉庫... + 開啟所有包含存放庫 + 開啟本機存放庫 + 開啟終端機 + 快速搜尋存放庫... 排序 - 本地更改 - 添加至 .gitignore 忽略清單 + 本機變更 + 加入至 .gitignore 忽略清單 忽略所有 *{0} 檔案 忽略同路徑下所有 *{0} 檔案 忽略同路徑下所有檔案 忽略本檔案 - 修補(--amend) + 修補 (--amend) 自動暫存 現在您已可將其加入暫存區中 - 提交 - 提交併推送 + 提 交 + 提交並推送 歷史輸入/範本 CTRL + Enter 檢測到衝突 檔案衝突已解決 - 顯示未跟蹤檔案 - 沒有提交資訊記錄 - 沒有可應用的提交資訊範本 + 顯示未追蹤檔案 + 沒有提交訊息記錄 + 沒有可套用的提交訊息範本 已暫存 - 從暫存區移除選中 - 從暫存區移除所有 + 取消暫存選取的檔案 + 取消暫存所有檔案 未暫存 - 暫存選中 - 暫存所有 - 檢視忽略變更檔案 - 範本:${0}$ - 請選中衝突檔案,開啟右鍵選單,選擇合適的解決方式 - 本地工作樹 - 拷贝工作樹路徑 - 鎖定工作樹 - 移除工作樹 - 解除鎖定工作樹 + 暫存選取的檔案 + 暫存所有檔案 + 檢視不追蹤變更的檔案 + 範本: ${0}$ + 請選擇發生衝突的檔案,開啟右鍵選單,選擇合適的解決方式 + 本機工作區 + 複製工作區路徑 + 鎖定工作區 + 移除工作區 + 解除鎖定工作區 From 4f8ccc4563c530b2aa385b1ac30b76c2e6b8549a Mon Sep 17 00:00:00 2001 From: leo Date: Sun, 25 Aug 2024 20:51:53 +0800 Subject: [PATCH 89/93] enhance: clean search filter when open Welcome page by closing the last opened repository tab --- src/ViewModels/Launcher.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ViewModels/Launcher.cs b/src/ViewModels/Launcher.cs index bd59f8a2..7e5fdad0 100644 --- a/src/ViewModels/Launcher.cs +++ b/src/ViewModels/Launcher.cs @@ -145,6 +145,7 @@ namespace SourceGit.ViewModels Models.AutoFetchManager.Instance.RemoveRepository(repo.FullPath); repo.Close(); + Welcome.Instance.ClearSearchFilter(); last.Node = new RepositoryNode() { Id = Guid.NewGuid().ToString() }; last.Data = Welcome.Instance; last.Popup = null; From 184c89ea1dbc1742897599ffbceeb9a552508efb Mon Sep 17 00:00:00 2001 From: leo Date: Sun, 25 Aug 2024 21:39:59 +0800 Subject: [PATCH 90/93] feature: supports squash multiple commits into selected commit (#408) --- src/Resources/Locales/de_DE.axaml | 2 +- src/Resources/Locales/en_US.axaml | 3 ++- src/Resources/Locales/fr_FR.axaml | 2 +- src/Resources/Locales/pt_BR.axaml | 2 +- src/Resources/Locales/zh_CN.axaml | 3 ++- src/Resources/Locales/zh_TW.axaml | 3 ++- src/ViewModels/Histories.cs | 23 ++++++++++++++++++++++- src/ViewModels/Squash.cs | 23 ++++++++--------------- src/Views/Squash.axaml | 25 ++++++------------------- 9 files changed, 45 insertions(+), 41 deletions(-) diff --git a/src/Resources/Locales/de_DE.axaml b/src/Resources/Locales/de_DE.axaml index 03355e01..3752841a 100644 --- a/src/Resources/Locales/de_DE.axaml +++ b/src/Resources/Locales/de_DE.axaml @@ -109,7 +109,7 @@ Commit rückgängig machen Umformulieren Als Patch speichern... - Squash in den Vorgänger + Squash Commits ÄNDERUNGEN Änderungen durchsuchen... DATEIEN diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 223d8955..a271cf8e 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -107,6 +107,7 @@ Reword Save as Patch... Squash Into Parent + Squash Commits since Here CHANGES Search Changes... FILES @@ -531,7 +532,7 @@ Skip This Version Software Update There are currently no updates available. - Squash HEAD Into Parent + Squash Commits SSH Private Key: Private SSH key store path START diff --git a/src/Resources/Locales/fr_FR.axaml b/src/Resources/Locales/fr_FR.axaml index 6cb21693..ac7265c5 100644 --- a/src/Resources/Locales/fr_FR.axaml +++ b/src/Resources/Locales/fr_FR.axaml @@ -534,7 +534,7 @@ Passer cette version Mise à jour du logiciel Il n'y a pas de mise à jour pour le moment. - Squash HEAD Into Parent + Squash Commits SSH Private Key: Private SSH key store path START diff --git a/src/Resources/Locales/pt_BR.axaml b/src/Resources/Locales/pt_BR.axaml index 0b42e4e9..7d6f8cee 100644 --- a/src/Resources/Locales/pt_BR.axaml +++ b/src/Resources/Locales/pt_BR.axaml @@ -527,7 +527,7 @@ Ignorar esta versão Atualização de Software Não há atualizações disponíveis no momento. - Unir HEAD ao Parent + Squash Commits Chave SSH Privada: Caminho para a chave SSH privada INICIAR diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index a521ca2b..41301673 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -110,6 +110,7 @@ 编辑提交信息 另存为补丁 ... 合并此提交到上一个提交 + 合并之后的提交到此处 变更对比 查找变更... 文件列表 @@ -533,7 +534,7 @@ 忽略此版本 软件更新 当前已是最新版本。 - 合并HEAD到上一个提交 + 压缩为单个提交 SSH密钥 : SSH密钥文件 开 始 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 15512286..f3c2f129 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -110,6 +110,7 @@ 編輯提交訊息 另存為修補檔 (patch)... 合併此提交到上一個提交 + 合併之後的提交到此處 變更對比 搜尋變更... 檔案列表 @@ -534,7 +535,7 @@ 忽略此版本 軟體更新 目前已是最新版本。 - 合併 HEAD 至前次提交 + 壓縮為單個提交 SSH 金鑰: SSH 金鑰檔案 開 始 diff --git a/src/ViewModels/Histories.cs b/src/ViewModels/Histories.cs index ad597985..ef3ef953 100644 --- a/src/ViewModels/Histories.cs +++ b/src/ViewModels/Histories.cs @@ -1,9 +1,11 @@ using System; using System.Collections; using System.Collections.Generic; + using Avalonia.Controls; using Avalonia.Platform.Storage; using Avalonia.VisualTree; + using CommunityToolkit.Mvvm.ComponentModel; namespace SourceGit.ViewModels @@ -240,6 +242,25 @@ namespace SourceGit.ViewModels e.Handled = true; }; menu.Items.Add(reset); + + var squash = new MenuItem(); + squash.Header = App.Text("CommitCM.SquashCommitsSinceThis"); + squash.Icon = App.CreateMenuIcon("Icons.SquashIntoParent"); + squash.IsVisible = commit.IsMerged; + squash.Click += (_, e) => + { + if (_repo.LocalChangesCount > 0) + { + App.RaiseException(_repo.FullPath, "You have local changes. Please run stash or discard first."); + return; + } + + if (PopupHost.CanCreatePopup()) + PopupHost.ShowPopup(new Squash(_repo, commit, commit.SHA)); + + e.Handled = true; + }; + menu.Items.Add(squash); } else { @@ -276,7 +297,7 @@ namespace SourceGit.ViewModels { var parent = _commits.Find(x => x.SHA == commit.Parents[0]); if (parent != null && PopupHost.CanCreatePopup()) - PopupHost.ShowPopup(new Squash(_repo, commit, parent)); + PopupHost.ShowPopup(new Squash(_repo, parent, commit.SHA)); } e.Handled = true; diff --git a/src/ViewModels/Squash.cs b/src/ViewModels/Squash.cs index f0f4b8bb..d7426135 100644 --- a/src/ViewModels/Squash.cs +++ b/src/ViewModels/Squash.cs @@ -5,16 +5,9 @@ namespace SourceGit.ViewModels { public class Squash : Popup { - public Models.Commit Head + public Models.Commit Target { - get; - private set; - } - - public Models.Commit Parent - { - get; - private set; + get => _target; } [Required(ErrorMessage = "Commit message is required!!!")] @@ -24,13 +17,12 @@ namespace SourceGit.ViewModels set => SetProperty(ref _message, value, true); } - public Squash(Repository repo, Models.Commit head, Models.Commit parent) + public Squash(Repository repo, Models.Commit target, string shaToGetPreferMessage) { _repo = repo; - _message = new Commands.QueryCommitFullMessage(_repo.FullPath, head.SHA).Result(); - - Head = head; - Parent = parent; + _target = target; + _message = new Commands.QueryCommitFullMessage(_repo.FullPath, shaToGetPreferMessage).Result(); + View = new Views.Squash() { DataContext = this }; } @@ -41,7 +33,7 @@ namespace SourceGit.ViewModels return Task.Run(() => { - var succ = new Commands.Reset(_repo.FullPath, Parent.SHA, "--soft").Exec(); + var succ = new Commands.Reset(_repo.FullPath, Target.SHA, "--soft").Exec(); if (succ) succ = new Commands.Commit(_repo.FullPath, _message, true).Exec(); CallUIThread(() => _repo.SetWatcherEnabled(true)); @@ -50,6 +42,7 @@ namespace SourceGit.ViewModels } private readonly Repository _repo; + private Models.Commit _target; private string _message; } } diff --git a/src/Views/Squash.axaml b/src/Views/Squash.axaml index ac8a115c..a517e6c0 100644 --- a/src/Views/Squash.axaml +++ b/src/Views/Squash.axaml @@ -13,30 +13,17 @@ Classes="bold" Text="{DynamicResource Text.Squash}"/> - - - - - - - - + - - + From a42412c7324a9e374ce8f3b0602289c10ad6a1e3 Mon Sep 17 00:00:00 2001 From: leo Date: Sun, 25 Aug 2024 21:45:37 +0800 Subject: [PATCH 91/93] ux: style for squash popup --- src/Resources/Locales/en_US.axaml | 1 + src/Resources/Locales/zh_CN.axaml | 1 + src/Resources/Locales/zh_TW.axaml | 1 + src/Views/Squash.axaml | 12 +++++++----- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index a271cf8e..822e7914 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -533,6 +533,7 @@ Software Update There are currently no updates available. Squash Commits + Into: SSH Private Key: Private SSH key store path START diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 41301673..2fcb2bde 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -535,6 +535,7 @@ 软件更新 当前已是最新版本。 压缩为单个提交 + 合并入: SSH密钥 : SSH密钥文件 开 始 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index f3c2f129..aad55faf 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -536,6 +536,7 @@ 軟體更新 目前已是最新版本。 壓縮為單個提交 + 合併入: SSH 金鑰: SSH 金鑰檔案 開 始 diff --git a/src/Views/Squash.axaml b/src/Views/Squash.axaml index a517e6c0..3af7e793 100644 --- a/src/Views/Squash.axaml +++ b/src/Views/Squash.axaml @@ -13,17 +13,19 @@ Classes="bold" Text="{DynamicResource Text.Squash}"/> - - + + - - + From 6b90a116a5efcc71ed51ffe86d885ecaa750d982 Mon Sep 17 00:00:00 2001 From: leo Date: Sun, 25 Aug 2024 22:34:42 +0800 Subject: [PATCH 92/93] localization: change translation for `Text.CommitCM.SquashCommitsSinceThis` (#408) * Thanks to @thomaschampagne --- src/Resources/Locales/en_US.axaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 822e7914..67f42eb2 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -107,7 +107,7 @@ Reword Save as Patch... Squash Into Parent - Squash Commits since Here + Squash Child Commits to Here CHANGES Search Changes... FILES From e9d79f03a7574284ac98e05350b5d9081eb224d7 Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 26 Aug 2024 10:18:57 +0800 Subject: [PATCH 93/93] version: Release 8.27 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 21783978..eeb14e9b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.26 \ No newline at end of file +8.27 \ No newline at end of file