From 8bcce5f723aabf36a98d17b4515a984a107d8ee8 Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 11 Jun 2024 11:08:20 +0800 Subject: [PATCH 01/29] fix: missing subject for the last commit (oldest in commit graph) --- src/Commands/QueryCommits.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Commands/QueryCommits.cs b/src/Commands/QueryCommits.cs index 075d6467..a67ee106 100644 --- a/src/Commands/QueryCommits.cs +++ b/src/Commands/QueryCommits.cs @@ -63,6 +63,9 @@ namespace SourceGit.Commands end = rs.StdOut.IndexOf('\n', start); } + if (start < rs.StdOut.Length) + _current.Subject = rs.StdOut.Substring(start); + if (_findFirstMerged && !_isHeadFounded && _commits.Count > 0) MarkFirstMerged(); From 7f2e22def6c0c9bdc8147f1da99fc9862b0698f9 Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 11 Jun 2024 15:30:06 +0800 Subject: [PATCH 02/29] feature: supports branch compare (#174) --- src/Commands/QuerySingleCommit.cs | 1 - src/Resources/Locales/en_US.axaml | 2 + src/Resources/Locales/zh_CN.axaml | 2 + src/Resources/Locales/zh_TW.axaml | 2 + src/ViewModels/BranchCompare.cs | 222 +++++++++++++++++++++++++++ src/ViewModels/Repository.cs | 145 ++++++++++-------- src/Views/BranchCompare.axaml | 240 ++++++++++++++++++++++++++++++ src/Views/BranchCompare.axaml.cs | 58 ++++++++ 8 files changed, 608 insertions(+), 64 deletions(-) create mode 100644 src/ViewModels/BranchCompare.cs create mode 100644 src/Views/BranchCompare.axaml create mode 100644 src/Views/BranchCompare.axaml.cs diff --git a/src/Commands/QuerySingleCommit.cs b/src/Commands/QuerySingleCommit.cs index 4a553817..a3b13929 100644 --- a/src/Commands/QuerySingleCommit.cs +++ b/src/Commands/QuerySingleCommit.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; namespace SourceGit.Commands { diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 856577d7..fd962463 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -33,6 +33,7 @@ Blame BLAME ON THIS FILE IS NOT SUPPORTED!!! Checkout${0}$ + Compare with Branch Compare with HEAD Compare with Worktree Copy Branch Name @@ -49,6 +50,7 @@ Rename${0}$ Tracking ... Unset Upstream + Branch Compare Bytes CANCEL CHANGE DISPLAY MODE diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index e5b46759..60868f16 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -36,6 +36,7 @@ 逐行追溯(blame) 选中文件不支持该操作!!! 检出(checkout)${0}$ + 与其他分支对比 与当前HEAD比较 与本地工作树比较 复制分支名 @@ -52,6 +53,7 @@ 重命名${0}$ 切换上游分支... 取消追踪 + 分支比较 字节 取 消 切换变更显示模式 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index ae7a6d47..4ad5108d 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -36,6 +36,7 @@ 逐行追溯(blame) 選中檔案不支援該操作!!! 檢出(checkout)${0}$ + 與其他分支比較 與當前HEAD比較 與本地工作樹比較 複製分支名 @@ -52,6 +53,7 @@ 重新命名${0}$ 切換上游分支... 取消追蹤 + 分支比較 位元組 取 消 切換變更顯示模式 diff --git a/src/ViewModels/BranchCompare.cs b/src/ViewModels/BranchCompare.cs new file mode 100644 index 00000000..6b19d249 --- /dev/null +++ b/src/ViewModels/BranchCompare.cs @@ -0,0 +1,222 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +using Avalonia.Controls; +using Avalonia.Threading; + +using CommunityToolkit.Mvvm.ComponentModel; + +namespace SourceGit.ViewModels +{ + public class BranchCompare : ObservableObject + { + public Models.Branch Base + { + get; + private set; + } + + public Models.Branch To + { + get; + private set; + } + + public Models.Commit BaseHead + { + get => _baseHead; + private set => SetProperty(ref _baseHead, value); + } + + public Models.Commit ToHead + { + get => _toHead; + private set => SetProperty(ref _toHead, value); + } + + public List VisibleChanges + { + get => _visibleChanges; + private set => SetProperty(ref _visibleChanges, value); + } + + public List SelectedChanges + { + get => _selectedChanges; + set + { + if (SetProperty(ref _selectedChanges, value)) + { + if (value != null && value.Count == 1) + DiffContext = new DiffContext(_repo, new Models.DiffOption(Base.Head, To.Head, value[0]), _diffContext); + else + DiffContext = null; + } + } + } + + public string SearchFilter + { + get => _searchFilter; + set + { + if (SetProperty(ref _searchFilter, value)) + { + RefreshVisible(); + } + } + } + + public DiffContext DiffContext + { + get => _diffContext; + private set => SetProperty(ref _diffContext, value); + } + + public BranchCompare(string repo, Models.Branch baseBranch, Models.Branch toBranch) + { + _repo = repo; + + Base = baseBranch; + To = toBranch; + + Task.Run(() => + { + var baseHead = new Commands.QuerySingleCommit(_repo, Base.Head).Result(); + var toHead = new Commands.QuerySingleCommit(_repo, To.Head).Result(); + _changes = new Commands.CompareRevisions(_repo, Base.Head, To.Head).Result(); + + var visible = _changes; + if (!string.IsNullOrWhiteSpace(_searchFilter)) + { + visible = new List(); + foreach (var c in _changes) + { + if (c.Path.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase)) + visible.Add(c); + } + } + + Dispatcher.UIThread.Invoke(() => + { + BaseHead = baseHead; + ToHead = toHead; + VisibleChanges = visible; + }); + }); + } + + public void NavigateTo(string commitSHA) + { + var repo = Preference.FindRepository(_repo); + if (repo != null) + repo.NavigateToCommit(commitSHA); + } + + public void ClearSearchFilter() + { + SearchFilter = string.Empty; + } + + public ContextMenu CreateChangeContextMenu() + { + if (_selectedChanges == null || _selectedChanges.Count != 1) + return null; + + var change = _selectedChanges[0]; + var menu = new ContextMenu(); + + var diffWithMerger = new MenuItem(); + diffWithMerger.Header = App.Text("DiffWithMerger"); + diffWithMerger.Icon = App.CreateMenuIcon("Icons.Diff"); + diffWithMerger.Click += (_, ev) => + { + var opt = new Models.DiffOption(Base.Head, To.Head, change); + var type = Preference.Instance.ExternalMergeToolType; + var exec = Preference.Instance.ExternalMergeToolPath; + + var tool = Models.ExternalMerger.Supported.Find(x => x.Type == type); + if (tool == null || !File.Exists(exec)) + { + App.RaiseException(_repo, "Invalid merge tool in preference setting!"); + return; + } + + var args = tool.Type != 0 ? tool.DiffCmd : Preference.Instance.ExternalMergeToolDiffCmd; + Task.Run(() => Commands.MergeTool.OpenForDiff(_repo, exec, args, opt)); + ev.Handled = true; + }; + menu.Items.Add(diffWithMerger); + + if (change.Index != Models.ChangeState.Deleted) + { + var full = Path.GetFullPath(Path.Combine(_repo, change.Path)); + var explore = new MenuItem(); + explore.Header = App.Text("RevealFile"); + explore.Icon = App.CreateMenuIcon("Icons.Folder.Open"); + explore.IsEnabled = File.Exists(full); + explore.Click += (_, ev) => + { + Native.OS.OpenInFileManager(full, true); + ev.Handled = true; + }; + menu.Items.Add(explore); + } + + var copyPath = new MenuItem(); + copyPath.Header = App.Text("CopyPath"); + copyPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyPath.Click += (_, ev) => + { + App.CopyText(change.Path); + ev.Handled = true; + }; + menu.Items.Add(copyPath); + + var copyFileName = new MenuItem(); + copyFileName.Header = App.Text("CopyFileName"); + copyFileName.Icon = App.CreateMenuIcon("Icons.Copy"); + copyFileName.Click += (_, e) => + { + App.CopyText(Path.GetFileName(change.Path)); + e.Handled = true; + }; + menu.Items.Add(copyFileName); + + return menu; + } + + private void RefreshVisible() + { + if (_changes == null) + return; + + if (string.IsNullOrEmpty(_searchFilter)) + { + VisibleChanges = _changes; + } + else + { + var visible = new List(); + foreach (var c in _changes) + { + if (c.Path.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase)) + visible.Add(c); + } + + VisibleChanges = visible; + } + } + + private string _repo = string.Empty; + private Models.Commit _baseHead = null; + private Models.Commit _toHead = null; + private List _changes = null; + private List _visibleChanges = null; + private List _selectedChanges = null; + private string _searchFilter = string.Empty; + private DiffContext _diffContext = null; + } +} diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index bbb0d6be..d564c15a 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -909,6 +909,13 @@ namespace SourceGit.ViewModels } menu.Items.Add(push); + + var compareWithBranch = CreateMenuItemToCompareBranches(branch); + if (compareWithBranch != null) + { + menu.Items.Add(new MenuItem() { Header = "-" }); + menu.Items.Add(compareWithBranch); + } } else { @@ -968,24 +975,6 @@ namespace SourceGit.ViewModels menu.Items.Add(merge); menu.Items.Add(rebase); - var compare = new MenuItem(); - compare.Header = App.Text("BranchCM.CompareWithHead"); - compare.Icon = App.CreateMenuIcon("Icons.Compare"); - compare.Click += (o, e) => - { - SearchResultSelectedCommit = null; - - if (_histories != null) - { - var target = new Commands.QuerySingleCommit(FullPath, branch.Head).Result(); - var head = new Commands.QuerySingleCommit(FullPath, current.Head).Result(); - _histories.AutoSelectedCommit = null; - _histories.DetailContext = new RevisionCompare(FullPath, target, head); - } - - e.Handled = true; - }; - if (WorkingCopyChangesCount > 0) { var compareWithWorktree = new MenuItem(); @@ -1002,11 +991,18 @@ namespace SourceGit.ViewModels _histories.DetailContext = new RevisionCompare(FullPath, target, null); } }; + menu.Items.Add(new MenuItem() { Header = "-" }); menu.Items.Add(compareWithWorktree); } - menu.Items.Add(new MenuItem() { Header = "-" }); - menu.Items.Add(compare); + var compareWithBranch = CreateMenuItemToCompareBranches(branch); + if (compareWithBranch != null) + { + if (WorkingCopyChangesCount == 0) + menu.Items.Add(new MenuItem() { Header = "-" }); + + menu.Items.Add(compareWithBranch); + } } var type = GitFlow.GetBranchType(branch.Name); @@ -1263,51 +1259,39 @@ namespace SourceGit.ViewModels menu.Items.Add(merge); menu.Items.Add(rebase); menu.Items.Add(new MenuItem() { Header = "-" }); - - if (current.Head != branch.Head) - { - var compare = new MenuItem(); - compare.Header = App.Text("BranchCM.CompareWithHead"); - compare.Icon = App.CreateMenuIcon("Icons.Compare"); - compare.Click += (o, e) => - { - SearchResultSelectedCommit = null; - - if (_histories != null) - { - var target = new Commands.QuerySingleCommit(FullPath, branch.Head).Result(); - var head = new Commands.QuerySingleCommit(FullPath, current.Head).Result(); - _histories.AutoSelectedCommit = null; - _histories.DetailContext = new RevisionCompare(FullPath, target, head); - } - - e.Handled = true; - }; - menu.Items.Add(compare); - - if (WorkingCopyChangesCount > 0) - { - var compareWithWorktree = new MenuItem(); - compareWithWorktree.Header = App.Text("BranchCM.CompareWithWorktree"); - compareWithWorktree.Icon = App.CreateMenuIcon("Icons.Compare"); - compareWithWorktree.Click += (o, e) => - { - SearchResultSelectedCommit = null; - - if (_histories != null) - { - var target = new Commands.QuerySingleCommit(FullPath, branch.Head).Result(); - _histories.AutoSelectedCommit = null; - _histories.DetailContext = new RevisionCompare(FullPath, target, null); - } - }; - menu.Items.Add(compareWithWorktree); - } - - menu.Items.Add(new MenuItem() { Header = "-" }); - } } + var hasCompare = false; + if (WorkingCopyChangesCount > 0) + { + var compareWithWorktree = new MenuItem(); + compareWithWorktree.Header = App.Text("BranchCM.CompareWithWorktree"); + compareWithWorktree.Icon = App.CreateMenuIcon("Icons.Compare"); + compareWithWorktree.Click += (o, e) => + { + SearchResultSelectedCommit = null; + + if (_histories != null) + { + var target = new Commands.QuerySingleCommit(FullPath, branch.Head).Result(); + _histories.AutoSelectedCommit = null; + _histories.DetailContext = new RevisionCompare(FullPath, target, null); + } + }; + menu.Items.Add(compareWithWorktree); + hasCompare = true; + } + + var compareWithBranch = CreateMenuItemToCompareBranches(branch); + if (compareWithBranch != null) + { + menu.Items.Add(compareWithBranch); + hasCompare = true; + } + + if (hasCompare) + menu.Items.Add(new MenuItem() { Header = "-" }); + var delete = new MenuItem(); delete.Header = new Views.NameHighlightedTextBlock("BranchCM.Delete", $"{branch.Remote}/{branch.Name}"); delete.Icon = App.CreateMenuIcon("Icons.Clear"); @@ -1485,6 +1469,41 @@ namespace SourceGit.ViewModels return menu; } + private MenuItem CreateMenuItemToCompareBranches(Models.Branch branch) + { + if (Branches.Count == 1) + return null; + + var compare = new MenuItem(); + compare.Header = App.Text("BranchCM.CompareWithBranch"); + compare.Icon = App.CreateMenuIcon("Icons.Compare"); + + foreach (var b in Branches) + { + if (b.FullName != branch.FullName) + { + var dup = b; + var target = new MenuItem(); + target.Header = b.IsLocal ? b.Name : $"{b.Remote}/{b.Name}"; + target.Icon = App.CreateMenuIcon(b.IsCurrent ? "Icons.Check" : "Icons.Branch"); + target.Click += (_, e) => + { + var wnd = new Views.BranchCompare() + { + DataContext = new BranchCompare(FullPath, branch, dup) + }; + + wnd.Show(App.GetTopLevel() as Window); + e.Handled = true; + }; + + compare.Items.Add(target); + } + } + + return compare; + } + private BranchTreeNode.Builder BuildBranchTree(List branches, List remotes) { var builder = new BranchTreeNode.Builder(); diff --git a/src/Views/BranchCompare.axaml b/src/Views/BranchCompare.axaml new file mode 100644 index 00000000..faaf26a3 --- /dev/null +++ b/src/Views/BranchCompare.axaml @@ -0,0 +1,240 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/BranchCompare.axaml.cs b/src/Views/BranchCompare.axaml.cs new file mode 100644 index 00000000..7888caf5 --- /dev/null +++ b/src/Views/BranchCompare.axaml.cs @@ -0,0 +1,58 @@ +using Avalonia.Controls; +using Avalonia.Input; + +namespace SourceGit.Views +{ + public partial class BranchCompare : Window + { + public BranchCompare() + { + InitializeComponent(); + } + + private void MaximizeOrRestoreWindow(object sender, TappedEventArgs e) + { + if (WindowState == WindowState.Maximized) + WindowState = WindowState.Normal; + else + WindowState = WindowState.Maximized; + + e.Handled = true; + } + + private void CustomResizeWindow(object sender, PointerPressedEventArgs e) + { + if (sender is Border border) + { + if (border.Tag is WindowEdge edge) + { + BeginResizeDrag(edge, e); + } + } + } + + private void BeginMoveWindow(object sender, PointerPressedEventArgs e) + { + BeginMoveDrag(e); + } + + private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e) + { + if (DataContext is ViewModels.BranchCompare vm && sender is ChangeCollectionView view) + { + var menu = vm.CreateChangeContextMenu(); + view.OpenContextMenu(menu); + } + + e.Handled = true; + } + + private void OnPressedSHA(object sender, PointerPressedEventArgs e) + { + if (DataContext is ViewModels.BranchCompare vm && sender is TextBlock block) + vm.NavigateTo(block.Text); + + e.Handled = true; + } + } +} From f977e76ea706deb08fda64450c5a6889cb3f44bd Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 11 Jun 2024 16:36:52 +0800 Subject: [PATCH 03/29] feature: remember window size and layout information (#165) --- src/ViewModels/LayoutInfo.cs | 80 +++++++++++++++++++++++++++++++++++ src/ViewModels/Preference.cs | 7 +++ src/Views/CommitChanges.axaml | 2 +- src/Views/Launcher.axaml | 2 + src/Views/Repository.axaml | 2 +- src/Views/RevisionFiles.axaml | 2 +- src/Views/StashesPage.axaml | 2 +- src/Views/WorkingCopy.axaml | 2 +- 8 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 src/ViewModels/LayoutInfo.cs diff --git a/src/ViewModels/LayoutInfo.cs b/src/ViewModels/LayoutInfo.cs new file mode 100644 index 00000000..f22258e9 --- /dev/null +++ b/src/ViewModels/LayoutInfo.cs @@ -0,0 +1,80 @@ +using System; +using System.Text.Json.Serialization; +using System.Text.Json; + +using Avalonia.Controls; + +using CommunityToolkit.Mvvm.ComponentModel; + +namespace SourceGit.ViewModels +{ + public class LayoutInfo : ObservableObject + { + public double LauncherWidth + { + get; + set; + } = 1280; + + public double LauncherHeight + { + get; + set; + } = 720; + + [JsonConverter(typeof(GridLengthConverter))] + public GridLength RepositorySidebarWidth + { + get => _repositorySidebarWidth; + set => SetProperty(ref _repositorySidebarWidth, value); + } + + [JsonConverter(typeof(GridLengthConverter))] + public GridLength WorkingCopyLeftWidth + { + get => _workingCopyLeftWidth; + set => SetProperty(ref _workingCopyLeftWidth, value); + } + + [JsonConverter(typeof(GridLengthConverter))] + public GridLength StashesLeftWidth + { + get => _stashesLeftWidth; + set => SetProperty(ref _stashesLeftWidth, value); + } + + [JsonConverter(typeof(GridLengthConverter))] + public GridLength CommitDetailChangesLeftWidth + { + get => _commitDetailChangesLeftWidth; + set => SetProperty(ref _commitDetailChangesLeftWidth, value); + } + + [JsonConverter(typeof(GridLengthConverter))] + public GridLength CommitDetailFilesLeftWidth + { + get => _commitDetailFilesLeftWidth; + set => SetProperty(ref _commitDetailFilesLeftWidth, value); + } + + private GridLength _repositorySidebarWidth = new GridLength(250, GridUnitType.Pixel); + private GridLength _workingCopyLeftWidth = new GridLength(300, GridUnitType.Pixel); + private GridLength _stashesLeftWidth = new GridLength(300, GridUnitType.Pixel); + private GridLength _commitDetailChangesLeftWidth = new GridLength(256, GridUnitType.Pixel); + private GridLength _commitDetailFilesLeftWidth = new GridLength(256, GridUnitType.Pixel); + } + + public class GridLengthConverter : JsonConverter + { + public override GridLength Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var size = reader.GetDouble(); + return new GridLength(size, GridUnitType.Pixel); + } + + public override void Write(Utf8JsonWriter writer, GridLength value, JsonSerializerOptions options) + { + writer.WriteNumberValue(value.Value); + } + } +} diff --git a/src/ViewModels/Preference.cs b/src/ViewModels/Preference.cs index 917349e0..debe672a 100644 --- a/src/ViewModels/Preference.cs +++ b/src/ViewModels/Preference.cs @@ -109,6 +109,12 @@ namespace SourceGit.ViewModels set => SetProperty(ref _defaultFontSize, value); } + public LayoutInfo Layout + { + get => _layout; + set => SetProperty(ref _layout, value); + } + public string AvatarServer { get => Models.AvatarManager.SelectedServer; @@ -531,6 +537,7 @@ namespace SourceGit.ViewModels private FontFamily _defaultFont = null; private FontFamily _monospaceFont = null; private double _defaultFontSize = 13; + private LayoutInfo _layout = new LayoutInfo(); private int _maxHistoryCommits = 20000; private bool _restoreTabs = false; diff --git a/src/Views/CommitChanges.axaml b/src/Views/CommitChanges.axaml index 8dd585bd..fffb3bd3 100644 --- a/src/Views/CommitChanges.axaml +++ b/src/Views/CommitChanges.axaml @@ -10,7 +10,7 @@ x:DataType="vm:CommitDetail"> - + diff --git a/src/Views/Launcher.axaml b/src/Views/Launcher.axaml index 16e242bc..4399b472 100644 --- a/src/Views/Launcher.axaml +++ b/src/Views/Launcher.axaml @@ -15,6 +15,8 @@ Title="SourceGit" Background="Transparent" MinWidth="1280" MinHeight="720" + Width="{Binding Source={x:Static vm:Preference.Instance}, Path=Layout.LauncherWidth, Mode=TwoWay}" + Height="{Binding Source={x:Static vm:Preference.Instance}, Path=Layout.LauncherHeight, Mode=TwoWay}" WindowStartupLocation="CenterScreen" ExtendClientAreaToDecorationsHint="True" ExtendClientAreaChromeHints="NoChrome" diff --git a/src/Views/Repository.axaml b/src/Views/Repository.axaml index 7a631e31..40144289 100644 --- a/src/Views/Repository.axaml +++ b/src/Views/Repository.axaml @@ -103,7 +103,7 @@ - + diff --git a/src/Views/RevisionFiles.axaml b/src/Views/RevisionFiles.axaml index 9c999061..a3fb85e7 100644 --- a/src/Views/RevisionFiles.axaml +++ b/src/Views/RevisionFiles.axaml @@ -12,7 +12,7 @@ x:DataType="vm:CommitDetail"> - + diff --git a/src/Views/StashesPage.axaml b/src/Views/StashesPage.axaml index a3d91d59..3a98c852 100644 --- a/src/Views/StashesPage.axaml +++ b/src/Views/StashesPage.axaml @@ -11,7 +11,7 @@ x:DataType="vm:StashesPage"> - + diff --git a/src/Views/WorkingCopy.axaml b/src/Views/WorkingCopy.axaml index 77013178..88c0dce9 100644 --- a/src/Views/WorkingCopy.axaml +++ b/src/Views/WorkingCopy.axaml @@ -10,7 +10,7 @@ x:DataType="vm:WorkingCopy"> - + From 7c79340cc15e395a315331f1994f0af31499c994 Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 11 Jun 2024 17:04:28 +0800 Subject: [PATCH 04/29] fix: view not update after `Close Other Tabs` clicked --- src/ViewModels/Launcher.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ViewModels/Launcher.cs b/src/ViewModels/Launcher.cs index c8453930..62b8029a 100644 --- a/src/ViewModels/Launcher.cs +++ b/src/ViewModels/Launcher.cs @@ -188,6 +188,7 @@ namespace SourceGit.ViewModels } Pages = new AvaloniaList { ActivePage }; + OnPropertyChanged(nameof(Pages)); GC.Collect(); } From 662c9e158052b8f06374090ee8d5ce0457c920e8 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 12 Jun 2024 10:22:26 +0800 Subject: [PATCH 05/29] enhance: remember last WindowState of Launcher --- src/ViewModels/LayoutInfo.cs | 6 ++++++ src/Views/Launcher.axaml | 1 + 2 files changed, 7 insertions(+) diff --git a/src/ViewModels/LayoutInfo.cs b/src/ViewModels/LayoutInfo.cs index f22258e9..80ce00a8 100644 --- a/src/ViewModels/LayoutInfo.cs +++ b/src/ViewModels/LayoutInfo.cs @@ -22,6 +22,12 @@ namespace SourceGit.ViewModels set; } = 720; + public WindowState LauncherWindowState + { + get; + set; + } = WindowState.Normal; + [JsonConverter(typeof(GridLengthConverter))] public GridLength RepositorySidebarWidth { diff --git a/src/Views/Launcher.axaml b/src/Views/Launcher.axaml index 4399b472..b630ee9f 100644 --- a/src/Views/Launcher.axaml +++ b/src/Views/Launcher.axaml @@ -17,6 +17,7 @@ MinWidth="1280" MinHeight="720" Width="{Binding Source={x:Static vm:Preference.Instance}, Path=Layout.LauncherWidth, Mode=TwoWay}" Height="{Binding Source={x:Static vm:Preference.Instance}, Path=Layout.LauncherHeight, Mode=TwoWay}" + WindowState="{Binding Source={x:Static vm:Preference.Instance}, Path=Layout.LauncherWindowState, Mode=TwoWay}" WindowStartupLocation="CenterScreen" ExtendClientAreaToDecorationsHint="True" ExtendClientAreaChromeHints="NoChrome" From b59c12e1f0b20e663a2e1acd8d5805dab5a485e7 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 12 Jun 2024 11:42:22 +0800 Subject: [PATCH 06/29] ux: re-design UI for checkout commit as deteched action --- src/Resources/Icons.axaml | 1 + src/ViewModels/Histories.cs | 25 ++++++++++++++----------- src/Views/CheckoutCommit.axaml | 16 +++++++++------- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/Resources/Icons.axaml b/src/Resources/Icons.axaml index 8f43742c..69907e3a 100644 --- a/src/Resources/Icons.axaml +++ b/src/Resources/Icons.axaml @@ -98,4 +98,5 @@ M408 232C408 210 426 192 448 192h416a40 40 0 110 80H448a40 40 0 01-40-40zM408 512c0-22 18-40 40-40h416a40 40 0 110 80H448A40 40 0 01408 512zM448 752A40 40 0 00448 832h416a40 40 0 100-80H448zM32 480l328 0 0 64-328 0Z M645 448l64 64 220-221L704 64l-64 64 115 115H128v90h628zM375 576l-64-64-220 224L314 960l64-64-116-115H896v-90H262z M248 221a77 77 0 00-30-21c-18-7-40-10-68-5a224 224 0 00-45 13c-5 2-10 5-15 8l-3 2v68l11-9c10-8 21-14 34-19 13-5 26-7 39-7 12 0 21 3 28 10 6 6 9 16 9 29l-62 9c-14 2-26 6-36 11a80 80 0 00-25 20c-7 8-12 17-15 27-6 21-6 44 1 65a70 70 0 0041 43c10 4 21 6 34 6a80 80 0 0063-28v22h64V298c0-16-2-31-6-44a91 91 0 00-18-33zm-41 121v15c0 8-1 15-4 22a48 48 0 01-24 29 44 44 0 01-33 2 29 29 0 01-10-6 25 25 0 01-6-9 30 30 0 01-2-12c0-5 1-9 2-14a21 21 0 015-9 28 28 0 0110-7 83 83 0 0120-5l42-6zm323-68a144 144 0 00-16-42 87 87 0 00-28-29 75 75 0 00-41-11 73 73 0 00-44 14c-6 5-12 11-17 17V64H326v398h59v-18c8 10 18 17 30 21 6 2 13 3 21 3 16 0 31-4 43-11 12-7 23-18 31-31a147 147 0 0019-46 248 248 0 006-57c0-17-2-33-5-49zm-55 49c0 15-1 28-4 39-2 11-6 20-10 27a41 41 0 01-15 15 37 37 0 01-36 1 44 44 0 01-13-12 59 59 0 01-9-18A76 76 0 01384 352v-33c0-10 1-20 4-29 2-8 6-15 10-22a43 43 0 0115-13 37 37 0 0119-5 35 35 0 0132 18c4 6 7 14 9 23 2 9 3 20 3 31zM154 634a58 58 0 0120-15c14-6 35-7 49-1 7 3 13 6 20 12l21 17V572l-6-4a124 124 0 00-58-14c-20 0-38 4-54 11-16 7-30 17-41 30-12 13-20 29-26 46-6 17-9 36-9 57 0 18 3 36 8 52 6 16 14 30 24 42 10 12 23 21 38 28 15 7 32 10 50 10 15 0 28-2 39-5 11-3 21-8 30-14l5-4v-57l-13 6a26 26 0 01-5 2c-3 1-6 2-8 3-2 1-15 6-15 6-4 2-9 3-14 4a63 63 0 01-38-4 53 53 0 01-20-14 70 70 0 01-13-24 111 111 0 01-5-34c0-13 2-26 5-36 3-10 8-19 14-26zM896 384h-256V320h288c21 1 32 12 32 32v384c0 18-12 32-32 32H504l132 133-45 45-185-185c-16-21-16-25 0-45l185-185L637 576l-128 128H896V384z + M128 183C128 154 154 128 183 128h521c30 0 55 26 55 55v38c0 17-17 34-34 34s-34-17-34-34v-26H196v495h26c17 0 34 17 34 34s-17 34-34 34h-38c-30 0-55-26-55-55V183zM380 896h-34c-26 0-47-21-47-47v-90h68V828h64V896H380c4 0 0 0 0 0zM759 828V896h90c26 0 47-21 47-47v-90h-68V828h-68zM828 435H896V346c0-26-21-47-47-47h-90v68H828v68zM435 299v68H367V439H299V346C299 320 320 299 346 299h90zM367 649H299v-107h68v107zM546 367V299h107v68h-107zM828 546H896v107h-68v-107zM649 828V896h-107v-68h107zM730 508v188c0 17-17 34-34 34h-188c-17 0-34-17-34-34s17-34 34-34h102l-124-124c-13-13-13-34 0-47 13-13 34-13 47 0l124 124V512c0-17 17-34 34-34 21-4 38 9 38 30z diff --git a/src/ViewModels/Histories.cs b/src/ViewModels/Histories.cs index 73007e02..1b740f1d 100644 --- a/src/ViewModels/Histories.cs +++ b/src/ViewModels/Histories.cs @@ -209,17 +209,6 @@ namespace SourceGit.ViewModels e.Handled = true; }; menu.Items.Add(reset); - - var checkoutCommit = new MenuItem(); - checkoutCommit.Header = App.Text("CommitCM.Checkout"); - checkoutCommit.Icon = App.CreateMenuIcon("Icons.Check"); - checkoutCommit.Click += (o, e) => - { - if (PopupHost.CanCreatePopup()) - PopupHost.ShowPopup(new CheckoutCommit(_repo, commit)); - e.Handled = true; - }; - menu.Items.Add(checkoutCommit); } else { @@ -290,6 +279,20 @@ namespace SourceGit.ViewModels menu.Items.Add(revert); } + if (current.Head != commit.SHA) + { + var checkoutCommit = new MenuItem(); + checkoutCommit.Header = App.Text("CommitCM.Checkout"); + checkoutCommit.Icon = App.CreateMenuIcon("Icons.Detached"); + checkoutCommit.Click += (o, e) => + { + if (PopupHost.CanCreatePopup()) + PopupHost.ShowPopup(new CheckoutCommit(_repo, commit)); + e.Handled = true; + }; + menu.Items.Add(checkoutCommit); + } + menu.Items.Add(new MenuItem() { Header = "-" }); if (current.Head != commit.SHA) diff --git a/src/Views/CheckoutCommit.axaml b/src/Views/CheckoutCommit.axaml index f73e2041..1ab41375 100644 --- a/src/Views/CheckoutCommit.axaml +++ b/src/Views/CheckoutCommit.axaml @@ -11,14 +11,16 @@ - - - + + + + + + Date: Wed, 12 Jun 2024 12:04:51 +0800 Subject: [PATCH 07/29] ux: trim subject if it's too long to display --- src/Views/Archive.axaml | 10 +++++----- src/Views/CheckoutCommit.axaml | 10 +++++----- src/Views/CherryPick.axaml | 12 ++++++------ src/Views/CreateBranch.axaml | 10 +++++----- src/Views/CreateTag.axaml | 12 ++++++------ src/Views/Rebase.axaml | 12 ++++++------ src/Views/Reset.axaml | 10 +++++----- src/Views/Revert.axaml | 12 ++++++------ src/Views/Reword.axaml | 15 ++++++++------- src/Views/Squash.axaml | 25 +++++++++++++------------ 10 files changed, 65 insertions(+), 63 deletions(-) diff --git a/src/Views/Archive.axaml b/src/Views/Archive.axaml index 8cb89d49..5dc79018 100644 --- a/src/Views/Archive.axaml +++ b/src/Views/Archive.axaml @@ -28,11 +28,11 @@ - - - - - + + + + + diff --git a/src/Views/CheckoutCommit.axaml b/src/Views/CheckoutCommit.axaml index 1ab41375..679299d3 100644 --- a/src/Views/CheckoutCommit.axaml +++ b/src/Views/CheckoutCommit.axaml @@ -25,11 +25,11 @@ HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,8,0" Text="{DynamicResource Text.Checkout.Commit.Target}" /> - - - - - + + + + + - + - - - - - + + + + + - - - - - + + + + + diff --git a/src/Views/CreateTag.axaml b/src/Views/CreateTag.axaml index a896a344..fa42c9f2 100644 --- a/src/Views/CreateTag.axaml +++ b/src/Views/CreateTag.axaml @@ -13,7 +13,7 @@ - + - - - - - + + + + + diff --git a/src/Views/Rebase.axaml b/src/Views/Rebase.axaml index 2dc670d2..aa52a15b 100644 --- a/src/Views/Rebase.axaml +++ b/src/Views/Rebase.axaml @@ -12,7 +12,7 @@ - + - - - - - + + + + + diff --git a/src/Views/Reset.axaml b/src/Views/Reset.axaml index fc45b330..8afc6e2e 100644 --- a/src/Views/Reset.axaml +++ b/src/Views/Reset.axaml @@ -25,11 +25,11 @@ HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,8,0" Text="{DynamicResource Text.Reset.MoveTo}"/> - - - - - + + + + + - + - - - - - + + + + + - + - - - - - + + + + + - - + - - - - - + + + + + - - - - - + + + + + - Date: Wed, 12 Jun 2024 12:08:00 +0800 Subject: [PATCH 08/29] fix: close popup if current page is the last one --- src/ViewModels/Launcher.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ViewModels/Launcher.cs b/src/ViewModels/Launcher.cs index 62b8029a..60cf93c8 100644 --- a/src/ViewModels/Launcher.cs +++ b/src/ViewModels/Launcher.cs @@ -136,6 +136,7 @@ namespace SourceGit.ViewModels last.Node = new RepositoryNode() { Id = Guid.NewGuid().ToString() }; last.Data = Welcome.Instance; + last.Popup = null; GC.Collect(); } From eab680ae552fc97ffc3e2a854f8c87dec9d05b7c Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 12 Jun 2024 12:13:45 +0800 Subject: [PATCH 09/29] fix: reset current revision file content when selected commit changed --- src/ViewModels/CommitDetail.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ViewModels/CommitDetail.cs b/src/ViewModels/CommitDetail.cs index 27a13836..430f4b7d 100644 --- a/src/ViewModels/CommitDetail.cs +++ b/src/ViewModels/CommitDetail.cs @@ -385,6 +385,7 @@ namespace SourceGit.ViewModels FullMessage = string.Empty; VisibleChanges = null; SelectedChanges = null; + ViewRevisionFileContent = null; if (_commit == null) return; From 68061f82b1f4421b1d31bebd1a440d59af570c57 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 12 Jun 2024 21:12:45 +0800 Subject: [PATCH 10/29] refactor: rewrite TextDiffView --- src/Resources/Themes.axaml | 30 +- src/ViewModels/TwoSideTextDiff.cs | 15 +- src/ViewModels/WorkingCopy.cs | 23 +- src/Views/DiffView.axaml | 4 +- src/Views/TextDiffView.axaml | 62 ++- src/Views/TextDiffView.axaml.cs | 681 ++++++++++++------------------ 6 files changed, 318 insertions(+), 497 deletions(-) diff --git a/src/Resources/Themes.axaml b/src/Resources/Themes.axaml index 36889bca..b01cafe9 100644 --- a/src/Resources/Themes.axaml +++ b/src/Resources/Themes.axaml @@ -25,11 +25,11 @@ #FF1F1F1F #FF6F6F6F #FFFFFFFF - #3C000000 - #3C00FF00 - #3CFF0000 - #5A00FF00 - #50FF0000 + #3C000000 + #3C00FF00 + #3CFF0000 + #5A00FF00 + #50FF0000 @@ -56,11 +56,11 @@ #FFDDDDDD #40F1F1F1 #FF252525 - #3C000000 - #3C00FF00 - #3CFF0000 - #5A00FF00 - #50FF0000 + #3C000000 + #3C00FF00 + #3CFF0000 + #5A00FF00 + #50FF0000 @@ -89,9 +89,9 @@ - - - - - + + + + + diff --git a/src/ViewModels/TwoSideTextDiff.cs b/src/ViewModels/TwoSideTextDiff.cs index ad1b5478..8d2aedcc 100644 --- a/src/ViewModels/TwoSideTextDiff.cs +++ b/src/ViewModels/TwoSideTextDiff.cs @@ -1,5 +1,5 @@ using System.Collections.Generic; - +using Avalonia; using CommunityToolkit.Mvvm.ComponentModel; namespace SourceGit.ViewModels @@ -11,7 +11,13 @@ namespace SourceGit.ViewModels public List New { get; set; } = new List(); public int MaxLineNumber = 0; - public TwoSideTextDiff(Models.TextDiff diff) + public Vector SyncScrollOffset + { + get => _syncScrollOffset; + set => SetProperty(ref _syncScrollOffset, value); + } + + public TwoSideTextDiff(Models.TextDiff diff, TwoSideTextDiff previous = null) { File = diff.File; MaxLineNumber = diff.MaxLineNumber; @@ -35,6 +41,9 @@ namespace SourceGit.ViewModels } FillEmptyLines(); + + if (previous != null && previous.File == File) + _syncScrollOffset = previous._syncScrollOffset; } private void FillEmptyLines() @@ -52,5 +61,7 @@ namespace SourceGit.ViewModels New.Add(new Models.TextDiffLine()); } } + + private Vector _syncScrollOffset = Vector.Zero; } } diff --git a/src/ViewModels/WorkingCopy.cs b/src/ViewModels/WorkingCopy.cs index 16dab612..bb982491 100644 --- a/src/ViewModels/WorkingCopy.cs +++ b/src/ViewModels/WorkingCopy.cs @@ -379,24 +379,16 @@ namespace SourceGit.ViewModels if (isUnstaged) { if (changes.Count == _unstaged.Count && _staged.Count == 0) - { PopupHost.ShowPopup(new Discard(_repo)); - } else - { PopupHost.ShowPopup(new Discard(_repo, changes, true)); - } } else { if (changes.Count == _staged.Count && _unstaged.Count == 0) - { PopupHost.ShowPopup(new Discard(_repo)); - } else - { PopupHost.ShowPopup(new Discard(_repo, changes, false)); - } } } } @@ -921,24 +913,11 @@ namespace SourceGit.ViewModels var isUnstaged = _selectedUnstaged != null && _selectedUnstaged.Count > 0; if (change == null) - { DetailContext = null; - } else if (change.IsConflit && isUnstaged) - { DetailContext = new ConflictContext(_repo.FullPath, change); - } else - { - if (_detailContext is DiffContext previous) - { - DetailContext = new DiffContext(_repo.FullPath, new Models.DiffOption(change, isUnstaged), previous); - } - else - { - DetailContext = new DiffContext(_repo.FullPath, new Models.DiffOption(change, isUnstaged)); - } - } + DetailContext = new DiffContext(_repo.FullPath, new Models.DiffOption(change, isUnstaged), _detailContext as DiffContext); } private async void UseTheirs(List changes) diff --git a/src/Views/DiffView.axaml b/src/Views/DiffView.axaml index 081abdf0..027a2bb3 100644 --- a/src/Views/DiffView.axaml +++ b/src/Views/DiffView.axaml @@ -234,9 +234,7 @@ - + diff --git a/src/Views/TextDiffView.axaml b/src/Views/TextDiffView.axaml index f435b2e2..538a0a6e 100644 --- a/src/Views/TextDiffView.axaml +++ b/src/Views/TextDiffView.axaml @@ -11,18 +11,16 @@ Background="{DynamicResource Brush.Contents}"> - @@ -30,40 +28,36 @@ + UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting}" + WordWrap="{Binding Source={x:Static vm:Preference.Instance}, Path=EnableDiffViewWordWrap}"/> + UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting}" + WordWrap="{Binding Source={x:Static vm:Preference.Instance}, Path=EnableDiffViewWordWrap}"/> diff --git a/src/Views/TextDiffView.axaml.cs b/src/Views/TextDiffView.axaml.cs index 50576cc4..204b5131 100644 --- a/src/Views/TextDiffView.axaml.cs +++ b/src/Views/TextDiffView.axaml.cs @@ -7,6 +7,7 @@ using System.Text; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Primitives; +using Avalonia.Data; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Media; @@ -21,7 +22,161 @@ using AvaloniaEdit.Utils; namespace SourceGit.Views { - public class CombinedTextDiffPresenter : TextEditor + public class IThemedTextDiffPresenter : TextEditor + { + public static readonly StyledProperty FileNameProperty = + AvaloniaProperty.Register(nameof(FileName), string.Empty); + + public string FileName + { + get => GetValue(FileNameProperty); + set => SetValue(FileNameProperty, value); + } + + public static readonly StyledProperty LineBrushProperty = + AvaloniaProperty.Register(nameof(LineBrush), new SolidColorBrush(Colors.DarkGray)); + + public IBrush LineBrush + { + get => GetValue(LineBrushProperty); + set => SetValue(LineBrushProperty, value); + } + + public static readonly StyledProperty EmptyContentBackgroundProperty = + AvaloniaProperty.Register(nameof(EmptyContentBackground), new SolidColorBrush(Color.FromArgb(60, 0, 0, 0))); + + public IBrush EmptyContentBackground + { + get => GetValue(EmptyContentBackgroundProperty); + set => SetValue(EmptyContentBackgroundProperty, value); + } + + public static readonly StyledProperty AddedContentBackgroundProperty = + AvaloniaProperty.Register(nameof(AddedContentBackground), new SolidColorBrush(Color.FromArgb(60, 0, 255, 0))); + + public IBrush AddedContentBackground + { + get => GetValue(AddedContentBackgroundProperty); + set => SetValue(AddedContentBackgroundProperty, value); + } + + public static readonly StyledProperty DeletedContentBackgroundProperty = + AvaloniaProperty.Register(nameof(DeletedContentBackground), new SolidColorBrush(Color.FromArgb(60, 255, 0, 0))); + + public IBrush DeletedContentBackground + { + get => GetValue(DeletedContentBackgroundProperty); + set => SetValue(DeletedContentBackgroundProperty, value); + } + + public static readonly StyledProperty AddedHighlightBrushProperty = + AvaloniaProperty.Register(nameof(AddedHighlightBrush), new SolidColorBrush(Color.FromArgb(90, 0, 255, 0))); + + public IBrush AddedHighlightBrush + { + get => GetValue(AddedHighlightBrushProperty); + set => SetValue(AddedHighlightBrushProperty, value); + } + + public static readonly StyledProperty DeletedHighlightBrushProperty = + AvaloniaProperty.Register(nameof(DeletedHighlightBrush), new SolidColorBrush(Color.FromArgb(80, 255, 0, 0))); + + public IBrush DeletedHighlightBrush + { + get => GetValue(DeletedHighlightBrushProperty); + set => SetValue(DeletedHighlightBrushProperty, value); + } + + public static readonly StyledProperty IndicatorForegroundProperty = + AvaloniaProperty.Register(nameof(IndicatorForeground), Brushes.Gray); + + public IBrush IndicatorForeground + { + get => GetValue(IndicatorForegroundProperty); + set => SetValue(IndicatorForegroundProperty, value); + } + + public static readonly StyledProperty UseSyntaxHighlightingProperty = + AvaloniaProperty.Register(nameof(UseSyntaxHighlighting), false); + + public bool UseSyntaxHighlighting + { + get => GetValue(UseSyntaxHighlightingProperty); + set => SetValue(UseSyntaxHighlightingProperty, value); + } + + protected override Type StyleKeyOverride => typeof(TextEditor); + + public IThemedTextDiffPresenter(TextArea area, TextDocument doc): base(area, doc) + { + IsReadOnly = true; + ShowLineNumbers = false; + BorderThickness = new Thickness(0); + + TextArea.TextView.Margin = new Thickness(4, 0); + TextArea.TextView.Options.EnableHyperlinks = false; + TextArea.TextView.Options.EnableEmailHyperlinks = false; + } + + protected override void OnLoaded(RoutedEventArgs e) + { + base.OnLoaded(e); + UpdateTextMate(); + } + + protected override void OnUnloaded(RoutedEventArgs e) + { + base.OnUnloaded(e); + + if (_textMate != null) + { + _textMate.Dispose(); + _textMate = null; + } + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == UseSyntaxHighlightingProperty) + UpdateTextMate(); + else if (change.Property == FileNameProperty) + Models.TextMateHelper.SetGrammarByFileName(_textMate, FileName); + else if (change.Property.Name == "ActualThemeVariant" && change.NewValue != null) + Models.TextMateHelper.SetThemeByApp(_textMate); + } + + protected void UpdateTextMate() + { + if (UseSyntaxHighlighting) + { + if (_textMate == null) + { + TextArea.TextView.LineTransformers.Remove(_lineStyleTransformer); + _textMate = Models.TextMateHelper.CreateForEditor(this); + TextArea.TextView.LineTransformers.Add(_lineStyleTransformer); + Models.TextMateHelper.SetGrammarByFileName(_textMate, FileName); + } + } + else + { + if (_textMate != null) + { + _textMate.Dispose(); + _textMate = null; + GC.Collect(); + + TextArea.TextView.Redraw(); + } + } + } + + private TextMate.Installation _textMate = null; + protected IVisualLineTransformer _lineStyleTransformer = null; + } + + public class CombinedTextDiffPresenter : IThemedTextDiffPresenter { public class LineNumberMargin : AbstractMargin { @@ -104,7 +259,7 @@ namespace SourceGit.Views public override void Render(DrawingContext context) { - var pen = new Pen(_editor.BorderBrush, 1); + var pen = new Pen(_editor.LineBrush, 1); context.DrawLine(pen, new Point(0, 0), new Point(0, Bounds.Height)); } @@ -155,11 +310,11 @@ namespace SourceGit.Views switch (type) { case Models.TextDiffLineType.None: - return _editor.LineBGEmpty; + return _editor.EmptyContentBackground; case Models.TextDiffLineType.Added: - return _editor.LineBGAdd; + return _editor.AddedContentBackground; case Models.TextDiffLineType.Deleted: - return _editor.LineBGDeleted; + return _editor.DeletedContentBackground; default: return null; } @@ -186,7 +341,7 @@ namespace SourceGit.Views { ChangeLinePart(line.Offset, line.EndOffset, v => { - v.TextRunProperties.SetForegroundBrush(_editor.SecondaryFG); + v.TextRunProperties.SetForegroundBrush(_editor.IndicatorForeground); v.TextRunProperties.SetTypeface(new Typeface(_editor.FontFamily, FontStyle.Italic)); }); @@ -195,7 +350,7 @@ namespace SourceGit.Views if (info.Highlights.Count > 0) { - var bg = info.Type == Models.TextDiffLineType.Added ? _editor.SecondaryLineBGAdd : _editor.SecondaryLineBGDeleted; + var bg = info.Type == Models.TextDiffLineType.Added ? _editor.AddedHighlightBrush : _editor.DeletedHighlightBrush; foreach (var highlight in info.Highlights) { ChangeLinePart(line.Offset + highlight.Start, line.Offset + highlight.Start + highlight.Count, v => @@ -209,129 +364,57 @@ namespace SourceGit.Views private readonly CombinedTextDiffPresenter _editor; } - public static readonly StyledProperty DiffDataProperty = - AvaloniaProperty.Register(nameof(DiffData)); - - public Models.TextDiff DiffData - { - get => GetValue(DiffDataProperty); - set => SetValue(DiffDataProperty, value); - } - - public static readonly StyledProperty LineBGEmptyProperty = - AvaloniaProperty.Register(nameof(LineBGEmpty), new SolidColorBrush(Color.FromArgb(60, 0, 0, 0))); - - public IBrush LineBGEmpty - { - get => GetValue(LineBGEmptyProperty); - set => SetValue(LineBGEmptyProperty, value); - } - - public static readonly StyledProperty LineBGAddProperty = - AvaloniaProperty.Register(nameof(LineBGAdd), new SolidColorBrush(Color.FromArgb(60, 0, 255, 0))); - - public IBrush LineBGAdd - { - get => GetValue(LineBGAddProperty); - set => SetValue(LineBGAddProperty, value); - } - - public static readonly StyledProperty LineBGDeletedProperty = - AvaloniaProperty.Register(nameof(LineBGDeleted), new SolidColorBrush(Color.FromArgb(60, 255, 0, 0))); - - public IBrush LineBGDeleted - { - get => GetValue(LineBGDeletedProperty); - set => SetValue(LineBGDeletedProperty, value); - } - - public static readonly StyledProperty SecondaryLineBGAddProperty = - AvaloniaProperty.Register(nameof(SecondaryLineBGAdd), new SolidColorBrush(Color.FromArgb(90, 0, 255, 0))); - - public IBrush SecondaryLineBGAdd - { - get => GetValue(SecondaryLineBGAddProperty); - set => SetValue(SecondaryLineBGAddProperty, value); - } - - public static readonly StyledProperty SecondaryLineBGDeletedProperty = - AvaloniaProperty.Register(nameof(SecondaryLineBGDeleted), new SolidColorBrush(Color.FromArgb(80, 255, 0, 0))); - - public IBrush SecondaryLineBGDeleted - { - get => GetValue(SecondaryLineBGDeletedProperty); - set => SetValue(SecondaryLineBGDeletedProperty, value); - } - - public static readonly StyledProperty SecondaryFGProperty = - AvaloniaProperty.Register(nameof(SecondaryFG), Brushes.Gray); - - public IBrush SecondaryFG - { - get => GetValue(SecondaryFGProperty); - set => SetValue(SecondaryFGProperty, value); - } - - public static readonly StyledProperty SyncScrollOffsetProperty = - AvaloniaProperty.Register(nameof(SyncScrollOffset)); - - public Vector SyncScrollOffset - { - get => GetValue(SyncScrollOffsetProperty); - set => SetValue(SyncScrollOffsetProperty, value); - } - - public static readonly StyledProperty UseSyntaxHighlightingProperty = - AvaloniaProperty.Register(nameof(UseSyntaxHighlighting), false); - - public bool UseSyntaxHighlighting - { - get => GetValue(UseSyntaxHighlightingProperty); - set => SetValue(UseSyntaxHighlightingProperty, value); - } - - protected override Type StyleKeyOverride => typeof(TextEditor); + public Models.TextDiff DiffData => DataContext as Models.TextDiff; public CombinedTextDiffPresenter() : base(new TextArea(), new TextDocument()) { _lineStyleTransformer = new LineStyleTransformer(this); - IsReadOnly = true; - ShowLineNumbers = false; - TextArea.LeftMargins.Add(new LineNumberMargin(this, true) { Margin = new Thickness(8, 0) }); TextArea.LeftMargins.Add(new VerticalSeperatorMargin(this)); TextArea.LeftMargins.Add(new LineNumberMargin(this, false) { Margin = new Thickness(8, 0) }); TextArea.LeftMargins.Add(new VerticalSeperatorMargin(this)); - TextArea.TextView.Margin = new Thickness(4, 0); TextArea.TextView.BackgroundRenderers.Add(new LineBackgroundRenderer(this)); TextArea.TextView.LineTransformers.Add(_lineStyleTransformer); - TextArea.TextView.Options.EnableHyperlinks = false; - TextArea.TextView.Options.EnableEmailHyperlinks = false; + } + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + + var scroller = (ScrollViewer)e.NameScope.Find("PART_ScrollViewer"); + scroller.Bind(ScrollViewer.OffsetProperty, new Binding("SyncScrollOffset", BindingMode.TwoWay)); } protected override void OnLoaded(RoutedEventArgs e) { base.OnLoaded(e); - - UpdateTextMate(); - TextArea.TextView.ContextRequested += OnTextViewContextRequested; - TextArea.TextView.ScrollOffsetChanged += OnTextViewScrollOffsetChanged; } protected override void OnUnloaded(RoutedEventArgs e) { base.OnUnloaded(e); - TextArea.TextView.ContextRequested -= OnTextViewContextRequested; - TextArea.TextView.ScrollOffsetChanged -= OnTextViewScrollOffsetChanged; + } - if (_textMate != null) + protected override void OnDataContextChanged(EventArgs e) + { + base.OnDataContextChanged(e); + + var textDiff = DataContext as Models.TextDiff; + if (textDiff != null) { - _textMate.Dispose(); - _textMate = null; + var builder = new StringBuilder(); + foreach (var line in textDiff.Lines) + builder.AppendLine(line.Content); + + Text = builder.ToString(); + } + else + { + Text = string.Empty; } GC.Collect(); @@ -346,9 +429,7 @@ namespace SourceGit.Views var menu = new ContextMenu(); var parentView = this.FindAncestorOfType(); if (parentView != null) - { parentView.FillContextMenuForWorkingCopyChange(menu, selection.StartPosition.Line, selection.EndPosition.Line, false); - } var copy = new MenuItem(); copy.Header = App.Text("Copy"); @@ -364,84 +445,9 @@ namespace SourceGit.Views TextArea.TextView.OpenContextMenu(menu); e.Handled = true; } - - private void OnTextViewScrollOffsetChanged(object sender, EventArgs e) - { - SetCurrentValue(SyncScrollOffsetProperty, TextArea.TextView.ScrollOffset); - } - - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) - { - base.OnPropertyChanged(change); - - if (change.Property == DiffDataProperty) - { - if (DiffData != null) - { - var builder = new StringBuilder(); - foreach (var line in DiffData.Lines) - { - builder.AppendLine(line.Content); - } - - Text = builder.ToString(); - Models.TextMateHelper.SetGrammarByFileName(_textMate, DiffData.File); - } - else - { - Text = string.Empty; - } - } - else if (change.Property == SyncScrollOffsetProperty) - { - if (TextArea.TextView.ScrollOffset != SyncScrollOffset) - { - IScrollable scrollable = TextArea.TextView; - scrollable.Offset = SyncScrollOffset; - } - } - else if (change.Property == UseSyntaxHighlightingProperty) - { - UpdateTextMate(); - } - else if (change.Property.Name == "ActualThemeVariant" && change.NewValue != null) - { - Models.TextMateHelper.SetThemeByApp(_textMate); - } - } - - private void UpdateTextMate() - { - if (UseSyntaxHighlighting) - { - if (_textMate == null) - { - TextArea.TextView.LineTransformers.Remove(_lineStyleTransformer); - _textMate = Models.TextMateHelper.CreateForEditor(this); - TextArea.TextView.LineTransformers.Add(_lineStyleTransformer); - - if (DiffData != null) - Models.TextMateHelper.SetGrammarByFileName(_textMate, DiffData.File); - } - } - else - { - if (_textMate != null) - { - _textMate.Dispose(); - _textMate = null; - GC.Collect(); - - TextArea.TextView.Redraw(); - } - } - } - - private TextMate.Installation _textMate; - private readonly LineStyleTransformer _lineStyleTransformer = null; } - public class SingleSideTextDiffPresenter : TextEditor + public class SingleSideTextDiffPresenter : IThemedTextDiffPresenter { public class LineNumberMargin : AbstractMargin { @@ -523,7 +529,7 @@ namespace SourceGit.Views public override void Render(DrawingContext context) { - var pen = new Pen(_editor.BorderBrush, 1); + var pen = new Pen(_editor.LineBrush, 1); context.DrawLine(pen, new Point(0, 0), new Point(0, Bounds.Height)); } @@ -575,11 +581,11 @@ namespace SourceGit.Views switch (type) { case Models.TextDiffLineType.None: - return _editor.LineBGEmpty; + return _editor.EmptyContentBackground; case Models.TextDiffLineType.Added: - return _editor.LineBGAdd; + return _editor.AddedContentBackground; case Models.TextDiffLineType.Deleted: - return _editor.LineBGDeleted; + return _editor.DeletedContentBackground; default: return null; } @@ -607,7 +613,7 @@ namespace SourceGit.Views { ChangeLinePart(line.Offset, line.EndOffset, v => { - v.TextRunProperties.SetForegroundBrush(_editor.SecondaryFG); + v.TextRunProperties.SetForegroundBrush(_editor.IndicatorForeground); v.TextRunProperties.SetTypeface(new Typeface(_editor.FontFamily, FontStyle.Italic)); }); @@ -616,7 +622,7 @@ namespace SourceGit.Views if (info.Highlights.Count > 0) { - var bg = info.Type == Models.TextDiffLineType.Added ? _editor.LineBGAdd : _editor.LineBGDeleted; + var bg = info.Type == Models.TextDiffLineType.Added ? _editor.AddedHighlightBrush : _editor.DeletedHighlightBrush; foreach (var highlight in info.Highlights) { ChangeLinePart(line.Offset + highlight.Start, line.Offset + highlight.Start + highlight.Count, v => @@ -639,103 +645,16 @@ namespace SourceGit.Views set => SetValue(IsOldProperty, value); } - public static readonly StyledProperty DiffDataProperty = - AvaloniaProperty.Register(nameof(DiffData)); - - public ViewModels.TwoSideTextDiff DiffData - { - get => GetValue(DiffDataProperty); - set => SetValue(DiffDataProperty, value); - } - - public static readonly StyledProperty LineBGEmptyProperty = - AvaloniaProperty.Register(nameof(LineBGEmpty), new SolidColorBrush(Color.FromArgb(60, 0, 0, 0))); - - public IBrush LineBGEmpty - { - get => GetValue(LineBGEmptyProperty); - set => SetValue(LineBGEmptyProperty, value); - } - - public static readonly StyledProperty LineBGAddProperty = - AvaloniaProperty.Register(nameof(LineBGAdd), new SolidColorBrush(Color.FromArgb(60, 0, 255, 0))); - - public IBrush LineBGAdd - { - get => GetValue(LineBGAddProperty); - set => SetValue(LineBGAddProperty, value); - } - - public static readonly StyledProperty LineBGDeletedProperty = - AvaloniaProperty.Register(nameof(LineBGDeleted), new SolidColorBrush(Color.FromArgb(60, 255, 0, 0))); - - public IBrush LineBGDeleted - { - get => GetValue(LineBGDeletedProperty); - set => SetValue(LineBGDeletedProperty, value); - } - - public static readonly StyledProperty SecondaryLineBGAddProperty = - AvaloniaProperty.Register(nameof(SecondaryLineBGAdd), new SolidColorBrush(Color.FromArgb(90, 0, 255, 0))); - - public IBrush SecondaryLineBGAdd - { - get => GetValue(SecondaryLineBGAddProperty); - set => SetValue(SecondaryLineBGAddProperty, value); - } - - public static readonly StyledProperty SecondaryLineBGDeletedProperty = - AvaloniaProperty.Register(nameof(SecondaryLineBGDeleted), new SolidColorBrush(Color.FromArgb(80, 255, 0, 0))); - - public IBrush SecondaryLineBGDeleted - { - get => GetValue(SecondaryLineBGDeletedProperty); - set => SetValue(SecondaryLineBGDeletedProperty, value); - } - - public static readonly StyledProperty SecondaryFGProperty = - AvaloniaProperty.Register(nameof(SecondaryFG), Brushes.Gray); - - public IBrush SecondaryFG - { - get => GetValue(SecondaryFGProperty); - set => SetValue(SecondaryFGProperty, value); - } - - public static readonly StyledProperty SyncScrollOffsetProperty = - AvaloniaProperty.Register(nameof(SyncScrollOffset), Vector.Zero); - - public Vector SyncScrollOffset - { - get => GetValue(SyncScrollOffsetProperty); - set => SetValue(SyncScrollOffsetProperty, value); - } - - public static readonly StyledProperty UseSyntaxHighlightingProperty = - AvaloniaProperty.Register(nameof(UseSyntaxHighlighting), false); - - public bool UseSyntaxHighlighting - { - get => GetValue(UseSyntaxHighlightingProperty); - set => SetValue(UseSyntaxHighlightingProperty, value); - } - - protected override Type StyleKeyOverride => typeof(TextEditor); + public ViewModels.TwoSideTextDiff DiffData => DataContext as ViewModels.TwoSideTextDiff; public SingleSideTextDiffPresenter() : base(new TextArea(), new TextDocument()) { _lineStyleTransformer = new LineStyleTransformer(this); - IsReadOnly = true; - ShowLineNumbers = false; - TextArea.LeftMargins.Add(new LineNumberMargin(this) { Margin = new Thickness(8, 0) }); TextArea.LeftMargins.Add(new VerticalSeperatorMargin(this)); - TextArea.TextView.Margin = new Thickness(4, 0); TextArea.TextView.BackgroundRenderers.Add(new LineBackgroundRenderer(this)); TextArea.TextView.LineTransformers.Add(_lineStyleTransformer); - TextArea.TextView.Options.EnableHyperlinks = false; - TextArea.TextView.Options.EnableEmailHyperlinks = false; } protected override void OnLoaded(RoutedEventArgs e) @@ -745,12 +664,10 @@ namespace SourceGit.Views _scrollViewer = this.FindDescendantOfType(); if (_scrollViewer != null) { - _scrollViewer.Offset = SyncScrollOffset; _scrollViewer.ScrollChanged += OnTextViewScrollChanged; + _scrollViewer.Bind(ScrollViewer.OffsetProperty, new Binding("SyncScrollOffset", BindingMode.OneWay)); } - UpdateTextMate(); - TextArea.PointerWheelChanged += OnTextAreaPointerWheelChanged; TextArea.TextView.ContextRequested += OnTextViewContextRequested; } @@ -765,18 +682,31 @@ namespace SourceGit.Views _scrollViewer = null; } - if (_textMate != null) - { - _textMate.Dispose(); - _textMate = null; - } - TextArea.PointerWheelChanged -= OnTextAreaPointerWheelChanged; TextArea.TextView.ContextRequested -= OnTextViewContextRequested; GC.Collect(); } + protected override void OnDataContextChanged(EventArgs e) + { + base.OnDataContextChanged(e); + + if (DataContext is ViewModels.TwoSideTextDiff diff) + { + var builder = new StringBuilder(); + var lines = IsOld ? diff.Old : diff.New; + foreach (var line in lines) + builder.AppendLine(line.Content); + + Text = builder.ToString(); + } + else + { + Text = string.Empty; + } + } + private void OnTextAreaPointerWheelChanged(object sender, PointerWheelEventArgs e) { if (!TextArea.IsFocused) @@ -785,8 +715,8 @@ namespace SourceGit.Views private void OnTextViewScrollChanged(object sender, ScrollChangedEventArgs e) { - if (TextArea.IsFocused) - SetCurrentValue(SyncScrollOffsetProperty, _scrollViewer.Offset); + if (TextArea.IsFocused && DataContext is ViewModels.TwoSideTextDiff diff) + diff.SyncScrollOffset = _scrollViewer.Offset; } private void OnTextViewContextRequested(object sender, ContextRequestedEventArgs e) @@ -798,9 +728,7 @@ namespace SourceGit.Views var menu = new ContextMenu(); var parentView = this.FindAncestorOfType(); if (parentView != null) - { parentView.FillContextMenuForWorkingCopyChange(menu, selection.StartPosition.Line, selection.EndPosition.Line, IsOld); - } var copy = new MenuItem(); copy.Header = App.Text("Copy"); @@ -817,96 +745,11 @@ namespace SourceGit.Views e.Handled = true; } - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) - { - base.OnPropertyChanged(change); - - if (change.Property == DiffDataProperty) - { - if (DiffData != null) - { - var builder = new StringBuilder(); - if (IsOld) - { - foreach (var line in DiffData.Old) - { - builder.AppendLine(line.Content); - } - } - else - { - foreach (var line in DiffData.New) - { - builder.AppendLine(line.Content); - } - } - - Text = builder.ToString(); - Models.TextMateHelper.SetGrammarByFileName(_textMate, DiffData.File); - } - else - { - Text = string.Empty; - } - } - else if (change.Property == SyncScrollOffsetProperty) - { - if (!TextArea.IsFocused && _scrollViewer != null) - _scrollViewer.Offset = SyncScrollOffset; - } - else if (change.Property == UseSyntaxHighlightingProperty) - { - UpdateTextMate(); - } - else if (change.Property.Name == "ActualThemeVariant" && change.NewValue != null) - { - Models.TextMateHelper.SetThemeByApp(_textMate); - } - } - - private void UpdateTextMate() - { - if (UseSyntaxHighlighting) - { - if (_textMate == null) - { - TextArea.TextView.LineTransformers.Remove(_lineStyleTransformer); - _textMate = Models.TextMateHelper.CreateForEditor(this); - TextArea.TextView.LineTransformers.Add(_lineStyleTransformer); - - if (DiffData != null) - Models.TextMateHelper.SetGrammarByFileName(_textMate, DiffData.File); - } - } - else - { - if (_textMate != null) - { - _textMate.Dispose(); - _textMate = null; - GC.Collect(); - - TextArea.TextView.Redraw(); - } - } - } - - private TextMate.Installation _textMate; - private readonly LineStyleTransformer _lineStyleTransformer = null; private ScrollViewer _scrollViewer = null; } public partial class TextDiffView : UserControl { - public static readonly StyledProperty TextDiffProperty = - AvaloniaProperty.Register(nameof(TextDiff), null); - - public Models.TextDiff TextDiff - { - get => GetValue(TextDiffProperty); - set => SetValue(TextDiffProperty, value); - } - public static readonly StyledProperty UseSideBySideDiffProperty = AvaloniaProperty.Register(nameof(UseSideBySideDiff), false); @@ -916,13 +759,20 @@ namespace SourceGit.Views set => SetValue(UseSideBySideDiffProperty, value); } - public static readonly StyledProperty SyncScrollOffsetProperty = - AvaloniaProperty.Register(nameof(SyncScrollOffset)); - - public Vector SyncScrollOffset + static TextDiffView() { - get => GetValue(SyncScrollOffsetProperty); - set => SetValue(SyncScrollOffsetProperty, value); + UseSideBySideDiffProperty.Changed.AddClassHandler((v, e) => + { + if (v.DataContext is Models.TextDiff diff) + { + diff.SyncScrollOffset = Vector.Zero; + + if (v.UseSideBySideDiff) + v.Content = new ViewModels.TwoSideTextDiff(diff); + else + v.Content = diff; + } + }); } public TextDiffView() @@ -932,6 +782,10 @@ namespace SourceGit.Views public void FillContextMenuForWorkingCopyChange(ContextMenu menu, int startLine, int endLine, bool isOldSide) { + var diff = DataContext as Models.TextDiff; + if (diff == null) + return; + var parentView = this.FindAncestorOfType(); if (parentView == null) return; @@ -951,7 +805,7 @@ namespace SourceGit.Views endLine = tmp; } - var selection = GetUnifiedSelection(startLine, endLine, isOldSide); + var selection = GetUnifiedSelection(diff, startLine, endLine, isOldSide); if (!selection.HasChanges) return; @@ -1033,17 +887,17 @@ namespace SourceGit.Views var tmpFile = Path.GetTempFileName(); if (change.WorkTree == Models.ChangeState.Untracked) { - TextDiff.GenerateNewPatchFromSelection(change, null, selection, false, tmpFile); + diff.GenerateNewPatchFromSelection(change, null, selection, false, tmpFile); } else if (!UseSideBySideDiff) { var treeGuid = new Commands.QueryStagedFileBlobGuid(ctx.RepositoryPath, change.Path).Result(); - TextDiff.GeneratePatchFromSelection(change, treeGuid, selection, false, tmpFile); + diff.GeneratePatchFromSelection(change, treeGuid, selection, false, tmpFile); } else { var treeGuid = new Commands.QueryStagedFileBlobGuid(ctx.RepositoryPath, change.Path).Result(); - TextDiff.GeneratePatchFromSelectionSingleSide(change, treeGuid, selection, false, isOldSide, tmpFile); + diff.GeneratePatchFromSelectionSingleSide(change, treeGuid, selection, false, isOldSide, tmpFile); } new Commands.Apply(ctx.RepositoryPath, tmpFile, true, "nowarn", "--cache --index").Exec(); @@ -1065,17 +919,17 @@ namespace SourceGit.Views var tmpFile = Path.GetTempFileName(); if (change.WorkTree == Models.ChangeState.Untracked) { - TextDiff.GenerateNewPatchFromSelection(change, null, selection, true, tmpFile); + diff.GenerateNewPatchFromSelection(change, null, selection, true, tmpFile); } else if (!UseSideBySideDiff) { var treeGuid = new Commands.QueryStagedFileBlobGuid(ctx.RepositoryPath, change.Path).Result(); - TextDiff.GeneratePatchFromSelection(change, treeGuid, selection, true, tmpFile); + diff.GeneratePatchFromSelection(change, treeGuid, selection, true, tmpFile); } else { var treeGuid = new Commands.QueryStagedFileBlobGuid(ctx.RepositoryPath, change.Path).Result(); - TextDiff.GeneratePatchFromSelectionSingleSide(change, treeGuid, selection, true, isOldSide, tmpFile); + diff.GeneratePatchFromSelectionSingleSide(change, treeGuid, selection, true, isOldSide, tmpFile); } new Commands.Apply(ctx.RepositoryPath, tmpFile, true, "nowarn", "--reverse").Exec(); @@ -1103,15 +957,15 @@ namespace SourceGit.Views var tmpFile = Path.GetTempFileName(); if (change.Index == Models.ChangeState.Added) { - TextDiff.GenerateNewPatchFromSelection(change, treeGuid, selection, true, tmpFile); + diff.GenerateNewPatchFromSelection(change, treeGuid, selection, true, tmpFile); } else if (!UseSideBySideDiff) { - TextDiff.GeneratePatchFromSelection(change, treeGuid, selection, true, tmpFile); + diff.GeneratePatchFromSelection(change, treeGuid, selection, true, tmpFile); } else { - TextDiff.GeneratePatchFromSelectionSingleSide(change, treeGuid, selection, true, isOldSide, tmpFile); + diff.GeneratePatchFromSelectionSingleSide(change, treeGuid, selection, true, isOldSide, tmpFile); } new Commands.Apply(ctx.RepositoryPath, tmpFile, true, "nowarn", "--cache --index --reverse").Exec(); @@ -1133,17 +987,17 @@ namespace SourceGit.Views var tmpFile = Path.GetTempFileName(); if (change.WorkTree == Models.ChangeState.Untracked) { - TextDiff.GenerateNewPatchFromSelection(change, null, selection, true, tmpFile); + diff.GenerateNewPatchFromSelection(change, null, selection, true, tmpFile); } else if (!UseSideBySideDiff) { var treeGuid = new Commands.QueryStagedFileBlobGuid(ctx.RepositoryPath, change.Path).Result(); - TextDiff.GeneratePatchFromSelection(change, treeGuid, selection, true, tmpFile); + diff.GeneratePatchFromSelection(change, treeGuid, selection, true, tmpFile); } else { var treeGuid = new Commands.QueryStagedFileBlobGuid(ctx.RepositoryPath, change.Path).Result(); - TextDiff.GeneratePatchFromSelectionSingleSide(change, treeGuid, selection, true, isOldSide, tmpFile); + diff.GeneratePatchFromSelectionSingleSide(change, treeGuid, selection, true, isOldSide, tmpFile); } new Commands.Apply(ctx.RepositoryPath, tmpFile, true, "nowarn", "--index --reverse").Exec(); @@ -1162,44 +1016,29 @@ namespace SourceGit.Views menu.Items.Add(new MenuItem() { Header = "-" }); } - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + protected override void OnDataContextChanged(EventArgs e) { - base.OnPropertyChanged(change); + base.OnDataContextChanged(e); - var data = TextDiff; - if (data == null) + var diff = DataContext as Models.TextDiff; + if (diff == null) { Content = null; - SyncScrollOffset = Vector.Zero; + GC.Collect(); return; - } - - if (change.Property == TextDiffProperty) - { - if (UseSideBySideDiff) - Content = new ViewModels.TwoSideTextDiff(TextDiff); - else - Content = TextDiff; - - SetCurrentValue(SyncScrollOffsetProperty, TextDiff.SyncScrollOffset); } - else if (change.Property == UseSideBySideDiffProperty) - { - if (UseSideBySideDiff) - Content = new ViewModels.TwoSideTextDiff(TextDiff); - else - Content = TextDiff; - SetCurrentValue(SyncScrollOffsetProperty, Vector.Zero); - } + if (UseSideBySideDiff) + Content = new ViewModels.TwoSideTextDiff(diff, Content as ViewModels.TwoSideTextDiff); + else + Content = diff; } - private Models.TextDiffSelection GetUnifiedSelection(int startLine, int endLine, bool isOldSide) + private Models.TextDiffSelection GetUnifiedSelection(Models.TextDiff diff, int startLine, int endLine, bool isOldSide) { var rs = new Models.TextDiffSelection(); - var diff = TextDiff; - endLine = Math.Min(endLine, TextDiff.Lines.Count); + endLine = Math.Min(endLine, diff.Lines.Count); if (Content is ViewModels.TwoSideTextDiff twoSides) { var target = isOldSide ? twoSides.Old : twoSides.New; @@ -1233,8 +1072,8 @@ namespace SourceGit.Views var firstContent = target[firstContentLine]; var endContent = target[endContentLine]; - startLine = TextDiff.Lines.IndexOf(firstContent) + 1; - endLine = TextDiff.Lines.IndexOf(endContent) + 1; + startLine = diff.Lines.IndexOf(firstContent) + 1; + endLine = diff.Lines.IndexOf(endContent) + 1; } rs.StartLine = startLine; From f5b35d3db2dc182f6c8675311143f9d7eb1ac3b4 Mon Sep 17 00:00:00 2001 From: leo Date: Thu, 13 Jun 2024 11:54:10 +0800 Subject: [PATCH 11/29] refactor: replace all window with custom ChromelessWindow --- src/Converters/WindowStateConverters.cs | 34 ------ src/Resources/Styles.axaml | 123 ++++++++++++++++++++++ src/Views/About.axaml | 50 ++++----- src/Views/About.axaml.cs | 3 +- src/Views/AssumeUnchangedManager.axaml | 46 ++++---- src/Views/AssumeUnchangedManager.axaml.cs | 2 +- src/Views/Blame.axaml | 109 ++++--------------- src/Views/Blame.axaml.cs | 18 +--- src/Views/BranchCompare.axaml | 109 ++++--------------- src/Views/BranchCompare.axaml.cs | 13 +-- src/Views/ChromelessWindow.cs | 54 ++++++++++ src/Views/FileHistories.axaml | 105 ++++-------------- src/Views/FileHistories.axaml.cs | 18 +--- src/Views/Hotkeys.axaml | 43 +++----- src/Views/Hotkeys.axaml.cs | 3 +- src/Views/Launcher.axaml | 123 +++++----------------- src/Views/Launcher.axaml.cs | 45 ++++---- src/Views/Preference.axaml | 54 ++++------ src/Views/Preference.axaml.cs | 3 +- src/Views/SelfUpdate.axaml | 47 ++++----- src/Views/SelfUpdate.axaml.cs | 2 +- src/Views/Statistics.axaml | 57 ++++------ src/Views/Statistics.axaml.cs | 2 +- 23 files changed, 414 insertions(+), 649 deletions(-) delete mode 100644 src/Converters/WindowStateConverters.cs create mode 100644 src/Views/ChromelessWindow.cs diff --git a/src/Converters/WindowStateConverters.cs b/src/Converters/WindowStateConverters.cs deleted file mode 100644 index 05f0b1cd..00000000 --- a/src/Converters/WindowStateConverters.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; - -using Avalonia; -using Avalonia.Controls; -using Avalonia.Data.Converters; - -namespace SourceGit.Converters -{ - public static class WindowStateConverters - { - public static readonly FuncValueConverter ToContentMargin = - new FuncValueConverter(state => - { - if (OperatingSystem.IsWindows() && state == WindowState.Maximized) - return new Thickness(6); - else if (OperatingSystem.IsLinux() && state != WindowState.Maximized) - return new Thickness(6); - else - return new Thickness(0); - }); - - public static readonly FuncValueConverter ToTitleBarHeight = - new FuncValueConverter(state => - { - if (state == WindowState.Maximized) - return new GridLength(OperatingSystem.IsMacOS() ? 34 : 30); - else - return new GridLength(38); - }); - - public static readonly FuncValueConverter IsNormal = - new FuncValueConverter(state => state == WindowState.Normal); - } -} diff --git a/src/Resources/Styles.axaml b/src/Resources/Styles.axaml index 114ac72d..219187cf 100644 --- a/src/Resources/Styles.axaml +++ b/src/Resources/Styles.axaml @@ -18,6 +18,129 @@ + + + + + + + + + + + + + + + + diff --git a/src/Views/ChromelessWindow.cs b/src/Views/ChromelessWindow.cs index 14501eba..7c4178c2 100644 --- a/src/Views/ChromelessWindow.cs +++ b/src/Views/ChromelessWindow.cs @@ -14,6 +14,8 @@ namespace SourceGit.Views { if (OperatingSystem.IsLinux()) Classes.Add("custom_window_frame"); + else if (OperatingSystem.IsWindows()) + Classes.Add("fix_maximized_padding"); } protected override void OnApplyTemplate(TemplateAppliedEventArgs e) From d85f7ffe1bcf0a4d71d33727951a01b732eaa3b4 Mon Sep 17 00:00:00 2001 From: leo Date: Thu, 13 Jun 2024 14:20:26 +0800 Subject: [PATCH 13/29] ux: use the same style for popup --- src/Views/Launcher.axaml | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/Views/Launcher.axaml b/src/Views/Launcher.axaml index f66f73ea..496fec4b 100644 --- a/src/Views/Launcher.axaml +++ b/src/Views/Launcher.axaml @@ -258,19 +258,9 @@ - - - - - - - - - - - - + CornerRadius="0,0,4,4" + BorderThickness="0" + Effect="drop-shadow(0 0 8 #8F000000)"> Date: Thu, 13 Jun 2024 15:25:04 +0800 Subject: [PATCH 14/29] fix: dragging the border to resize the window looks weird on Linux (#177) --- src/Resources/Styles.axaml | 130 ++++++++++++++++++---------------- src/Views/ChromelessWindow.cs | 2 +- 2 files changed, 68 insertions(+), 64 deletions(-) diff --git a/src/Resources/Styles.axaml b/src/Resources/Styles.axaml index c08922e1..b0797692 100644 --- a/src/Resources/Styles.axaml +++ b/src/Resources/Styles.axaml @@ -40,21 +40,83 @@ - - diff --git a/src/Views/ChromelessWindow.cs b/src/Views/ChromelessWindow.cs index 7c4178c2..434d22d4 100644 --- a/src/Views/ChromelessWindow.cs +++ b/src/Views/ChromelessWindow.cs @@ -49,7 +49,7 @@ namespace SourceGit.Views private void OnWindowBorderPointerPressed(object sender, PointerPressedEventArgs e) { - if (sender is Border border && border.Tag is WindowEdge edge) + if (sender is Border border && border.Tag is WindowEdge edge && CanResize) BeginResizeDrag(edge, e); } } From b77ed6520cca432559602f73bd3dac042f3a81c2 Mon Sep 17 00:00:00 2001 From: leo Date: Thu, 13 Jun 2024 17:08:38 +0800 Subject: [PATCH 15/29] readme: add tips to customize current active theme --- README.md | 44 ++++++++++++++++++++++++++++++++++++++ src/Resources/Themes.axaml | 9 +++----- src/Views/Repository.axaml | 8 +++---- 3 files changed, 51 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 22e909bf..d597db41 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ Opensource Git GUI client. * Fast * English/简体中文/繁體中文 * Built-in light/dark themes +* Customize theme * Visual commit graph * Supports SSH access with each remote * GIT commands with GUI @@ -24,6 +25,8 @@ Opensource Git GUI client. * File histories * Blame * Revision Diffs + * Branch Diff + * Image Diff * GitFlow support > **Linux** only tested on **Ubuntu 22.04** on **X11**. @@ -87,6 +90,47 @@ This app supports open repository in external tools listed in the table below. ![Theme Light](./screenshots/theme_light.png) +## How to Customize Theme + +1. Create a new json file, and provide your favorite colors with follow keys: + +| Key | Description | +| --- | --- | +| Color.Window | Window background color | +| Color.TitleBar | Title bar background color | +| Color.ToolBar | Tool bar background color | +| Color.Popup | Popup panel background color | +| Color.Contents | Background color used in inputs, data grids, file content viewer, change lists, text diff viewer, etc. | +| Color.Badage | Badage background color | +| Color.Conflict | Conflict panel background color | +| Color.ConflictForeground | Conflict panel foreground color | +| Color.Border0 | Border color used in some controls, like Window, Tab, Toolbar, etc. | +| Color.Border1 | Border color used in inputs, like TextBox, ComboBox, etc. | +| Color.Border2 | Border color used in visual lines, like seperators, Rectange, etc. | +| Color.FlatButton.Background | Flat button background color, like `Cancel`, `Commit & Push` button | +| Color.FlatButton.BackgroundHovered | Flat button background color when hovered, like `Cancel` button | +| Color.FlatButton.PrimaryBackground | Primary flat button background color, like `Ok`, `Commit` button | +| Color.FlatButton.PrimaryBackgroundHovered | Primary flat button background color when hovered, like `Ok`, `Commit` button | +| Color.FG1 | Primary foreground color for all text elements | +| Color.FG2 | Secondary foreground color for all text elements | +| Color.Diff.EmptyBG | Background color used in empty lines in diff viewer | +| Color.Diff.AddedBG | Background color used in added lines in diff viewer | +| Color.Diff.DeletedBG | Background color used in deleted lines in diff viewer | +| Color.Diff.AddedHighlight | Background color used for changed words in added lines in diff viewer | +| Color.Diff.DeletedHighlight | Background color used for changed words in deleted lines in diff viewer | + +For example: + +```json +{ + "Color.Window": "#FFFF6059" +} +``` + +2. Open `Preference` -> `Appearance`, choose the json file you just created in `Custom Color Schema`. + +> **NOTE**: The `Custom Color Schema` will override the colors with same keys in current active theme. + ## Contributing Thanks to all the people who contribute. diff --git a/src/Resources/Themes.axaml b/src/Resources/Themes.axaml index b01cafe9..00cdda4f 100644 --- a/src/Resources/Themes.axaml +++ b/src/Resources/Themes.axaml @@ -14,17 +14,16 @@ #FF6F6F6F #FFF8F8F8 #FF836C2E + #FFFFFFFF #FFCFCFCF #FF898989 #FFCFCFCF - #FFEFEFEF #FFF8F8F8 White #FF4295FF #FF529DFB #FF1F1F1F #FF6F6F6F - #FFFFFFFF #3C000000 #3C00FF00 #3CFF0000 @@ -45,17 +44,16 @@ #FF505050 #FFF8F8F8 #FFFAFAD2 + #FF252525 #FF181818 #FF7C7C7C #FF404040 - #FF252525 #FF303030 #FF333333 #FF3A3A3A #FF404040 #FFDDDDDD #40F1F1F1 - #FF252525 #3C000000 #3C00FF00 #3CFF0000 @@ -76,17 +74,16 @@ + - - diff --git a/src/Views/Repository.axaml b/src/Views/Repository.axaml index 40144289..1dc364aa 100644 --- a/src/Views/Repository.axaml +++ b/src/Views/Repository.axaml @@ -615,19 +615,19 @@ - + - + - + - + From 762ec1f702538dd51dfb474c1197d48910352a2d Mon Sep 17 00:00:00 2001 From: leo Date: Thu, 13 Jun 2024 17:55:22 +0800 Subject: [PATCH 16/29] fix: begin drag was triggered when double click on the title bar --- src/Views/Blame.axaml | 4 +++- src/Views/Blame.axaml.cs | 26 ++++++++++++++++++++++- src/Views/BranchCompare.axaml | 4 +++- src/Views/BranchCompare.axaml.cs | 27 +++++++++++++++++++++++- src/Views/FileHistories.axaml | 4 +++- src/Views/FileHistories.axaml.cs | 27 +++++++++++++++++++++++- src/Views/Launcher.axaml | 7 ++++--- src/Views/Launcher.axaml.cs | 36 +++++++++++++++++++++++++------- 8 files changed, 119 insertions(+), 16 deletions(-) diff --git a/src/Views/Blame.axaml b/src/Views/Blame.axaml index fdd4b7bc..a7e850fb 100644 --- a/src/Views/Blame.axaml +++ b/src/Views/Blame.axaml @@ -28,7 +28,9 @@ Background="{DynamicResource Brush.TitleBar}" BorderThickness="0,0,0,1" BorderBrush="{DynamicResource Brush.Border2}" DoubleTapped="MaximizeOrRestoreWindow" - PointerPressed="BeginMoveWindow"/> + PointerPressed="BeginMoveWindow" + PointerMoved="MoveWindow" + PointerReleased="EndMoveWindow"/> diff --git a/src/Views/Blame.axaml.cs b/src/Views/Blame.axaml.cs index dc876612..8a6ffee9 100644 --- a/src/Views/Blame.axaml.cs +++ b/src/Views/Blame.axaml.cs @@ -343,7 +343,29 @@ namespace SourceGit.Views private void BeginMoveWindow(object sender, PointerPressedEventArgs e) { - BeginMoveDrag(e); + if (e.ClickCount != 2) + _pressedTitleBar = true; + } + + private void MoveWindow(object sender, PointerEventArgs e) + { + if (!_pressedTitleBar) + return; + + var visual = (Visual)e.Source; + BeginMoveDrag(new PointerPressedEventArgs( + e.Source, + e.Pointer, + visual, + e.GetPosition(visual), + e.Timestamp, + new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.LeftButtonPressed), + e.KeyModifiers)); + } + + private void EndMoveWindow(object sender, PointerReleasedEventArgs e) + { + _pressedTitleBar = false; } protected override void OnClosed(EventArgs e) @@ -361,5 +383,7 @@ namespace SourceGit.Views } e.Handled = true; } + + private bool _pressedTitleBar = false; } } diff --git a/src/Views/BranchCompare.axaml b/src/Views/BranchCompare.axaml index 2733b11c..9ad7db03 100644 --- a/src/Views/BranchCompare.axaml +++ b/src/Views/BranchCompare.axaml @@ -28,7 +28,9 @@ Background="{DynamicResource Brush.TitleBar}" BorderThickness="0,0,0,1" BorderBrush="{DynamicResource Brush.Border2}" DoubleTapped="MaximizeOrRestoreWindow" - PointerPressed="BeginMoveWindow"/> + PointerPressed="BeginMoveWindow" + PointerMoved="MoveWindow" + PointerReleased="EndMoveWindow"/> diff --git a/src/Views/BranchCompare.axaml.cs b/src/Views/BranchCompare.axaml.cs index 717b0812..9d506ef3 100644 --- a/src/Views/BranchCompare.axaml.cs +++ b/src/Views/BranchCompare.axaml.cs @@ -1,3 +1,4 @@ +using Avalonia; using Avalonia.Controls; using Avalonia.Input; @@ -22,7 +23,29 @@ namespace SourceGit.Views private void BeginMoveWindow(object sender, PointerPressedEventArgs e) { - BeginMoveDrag(e); + if (e.ClickCount != 2) + _pressedTitleBar = true; + } + + private void MoveWindow(object sender, PointerEventArgs e) + { + if (!_pressedTitleBar) + return; + + var visual = (Visual)e.Source; + BeginMoveDrag(new PointerPressedEventArgs( + e.Source, + e.Pointer, + visual, + e.GetPosition(visual), + e.Timestamp, + new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.LeftButtonPressed), + e.KeyModifiers)); + } + + private void EndMoveWindow(object sender, PointerReleasedEventArgs e) + { + _pressedTitleBar = false; } private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e) @@ -43,5 +66,7 @@ namespace SourceGit.Views e.Handled = true; } + + private bool _pressedTitleBar = false; } } diff --git a/src/Views/FileHistories.axaml b/src/Views/FileHistories.axaml index 9c3b1fd9..bcbc02b0 100644 --- a/src/Views/FileHistories.axaml +++ b/src/Views/FileHistories.axaml @@ -26,7 +26,9 @@ Background="{DynamicResource Brush.TitleBar}" BorderThickness="0,0,0,1" BorderBrush="{DynamicResource Brush.Border0}" DoubleTapped="MaximizeOrRestoreWindow" - PointerPressed="BeginMoveWindow"/> + PointerPressed="BeginMoveWindow" + PointerMoved="MoveWindow" + PointerReleased="EndMoveWindow"/> diff --git a/src/Views/FileHistories.axaml.cs b/src/Views/FileHistories.axaml.cs index c1a534ef..2c45ab9f 100644 --- a/src/Views/FileHistories.axaml.cs +++ b/src/Views/FileHistories.axaml.cs @@ -1,3 +1,4 @@ +using Avalonia; using Avalonia.Controls; using Avalonia.Input; @@ -22,7 +23,31 @@ namespace SourceGit.Views private void BeginMoveWindow(object sender, PointerPressedEventArgs e) { - BeginMoveDrag(e); + if (e.ClickCount != 2) + _pressedTitleBar = true; } + + private void MoveWindow(object sender, PointerEventArgs e) + { + if (!_pressedTitleBar) + return; + + var visual = (Visual)e.Source; + BeginMoveDrag(new PointerPressedEventArgs( + e.Source, + e.Pointer, + visual, + e.GetPosition(visual), + e.Timestamp, + new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.LeftButtonPressed), + e.KeyModifiers)); + } + + private void EndMoveWindow(object sender, PointerReleasedEventArgs e) + { + _pressedTitleBar = false; + } + + private bool _pressedTitleBar = false; } } diff --git a/src/Views/Launcher.axaml b/src/Views/Launcher.axaml index 496fec4b..9da3fe76 100644 --- a/src/Views/Launcher.axaml +++ b/src/Views/Launcher.axaml @@ -31,7 +31,9 @@ Background="{DynamicResource Brush.TitleBar}" BorderThickness="0,0,0,1" BorderBrush="{DynamicResource Brush.Border0}" DoubleTapped="OnTitleBarDoubleTapped" - PointerPressed="BeginMoveWindow"/> + PointerPressed="BeginMoveWindow" + PointerMoved="MoveWindow" + PointerReleased="EndMoveWindow"/> @@ -81,10 +83,9 @@ diff --git a/src/Views/Launcher.axaml.cs b/src/Views/Launcher.axaml.cs index 96f006d4..60ac7d97 100644 --- a/src/Views/Launcher.axaml.cs +++ b/src/Views/Launcher.axaml.cs @@ -172,7 +172,28 @@ namespace SourceGit.Views private void BeginMoveWindow(object sender, PointerPressedEventArgs e) { if (e.ClickCount != 2) - BeginMoveDrag(e); + _pressedTitleBar = true; + } + + private void MoveWindow(object sender, PointerEventArgs e) + { + if (!_pressedTitleBar) + return; + + var visual = (Visual)e.Source; + BeginMoveDrag(new PointerPressedEventArgs( + e.Source, + e.Pointer, + visual, + e.GetPosition(visual), + e.Timestamp, + new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.LeftButtonPressed), + e.KeyModifiers)); + } + + private void EndMoveWindow(object sender, PointerReleasedEventArgs e) + { + _pressedTitleBar = false; } private void ScrollTabs(object sender, PointerWheelEventArgs e) @@ -236,26 +257,26 @@ namespace SourceGit.Views private void OnPointerPressedTab(object sender, PointerPressedEventArgs e) { _pressedTab = true; - _startDrag = false; + _startDragTab = false; _pressedTabPosition = e.GetPosition(sender as Border); } private void OnPointerReleasedTab(object sender, PointerReleasedEventArgs e) { _pressedTab = false; - _startDrag = false; + _startDragTab = false; } private void OnPointerMovedOverTab(object sender, PointerEventArgs e) { - if (_pressedTab && !_startDrag && sender is Border border) + if (_pressedTab && !_startDragTab && sender is Border border) { var delta = e.GetPosition(border) - _pressedTabPosition; var sizeSquired = delta.X * delta.X + delta.Y * delta.Y; if (sizeSquired < 64) return; - _startDrag = true; + _startDragTab = true; var data = new DataObject(); data.Set("MovedTab", border.DataContext); @@ -277,7 +298,7 @@ namespace SourceGit.Views } _pressedTab = false; - _startDrag = false; + _startDragTab = false; e.Handled = true; } @@ -304,8 +325,9 @@ namespace SourceGit.Views OnPopupCancel(sender, e); } + private bool _pressedTitleBar = false; private bool _pressedTab = false; private Point _pressedTabPosition = new Point(); - private bool _startDrag = false; + private bool _startDragTab = false; } } From 13cc494a4882251ff4e97df3b494889309948976 Mon Sep 17 00:00:00 2001 From: leo Date: Thu, 13 Jun 2024 20:26:24 +0800 Subject: [PATCH 17/29] enhance: forbid `MoveWindow` being called while double tap action taking place (#177) --- src/Views/Blame.axaml.cs | 2 ++ src/Views/BranchCompare.axaml.cs | 2 ++ src/Views/FileHistories.axaml.cs | 2 ++ src/Views/Launcher.axaml.cs | 2 ++ 4 files changed, 8 insertions(+) diff --git a/src/Views/Blame.axaml.cs b/src/Views/Blame.axaml.cs index 8a6ffee9..d4c2da28 100644 --- a/src/Views/Blame.axaml.cs +++ b/src/Views/Blame.axaml.cs @@ -333,6 +333,8 @@ namespace SourceGit.Views private void MaximizeOrRestoreWindow(object sender, TappedEventArgs e) { + _pressedTitleBar = false; + if (WindowState == WindowState.Maximized) WindowState = WindowState.Normal; else diff --git a/src/Views/BranchCompare.axaml.cs b/src/Views/BranchCompare.axaml.cs index 9d506ef3..280cfc6a 100644 --- a/src/Views/BranchCompare.axaml.cs +++ b/src/Views/BranchCompare.axaml.cs @@ -13,6 +13,8 @@ namespace SourceGit.Views private void MaximizeOrRestoreWindow(object sender, TappedEventArgs e) { + _pressedTitleBar = false; + if (WindowState == WindowState.Maximized) WindowState = WindowState.Normal; else diff --git a/src/Views/FileHistories.axaml.cs b/src/Views/FileHistories.axaml.cs index 2c45ab9f..51570310 100644 --- a/src/Views/FileHistories.axaml.cs +++ b/src/Views/FileHistories.axaml.cs @@ -13,6 +13,8 @@ namespace SourceGit.Views private void MaximizeOrRestoreWindow(object sender, TappedEventArgs e) { + _pressedTitleBar = false; + if (WindowState == WindowState.Maximized) WindowState = WindowState.Normal; else diff --git a/src/Views/Launcher.axaml.cs b/src/Views/Launcher.axaml.cs index 60ac7d97..7cae36dc 100644 --- a/src/Views/Launcher.axaml.cs +++ b/src/Views/Launcher.axaml.cs @@ -161,6 +161,8 @@ namespace SourceGit.Views private void OnTitleBarDoubleTapped(object sender, TappedEventArgs e) { + _pressedTitleBar = false; + if (WindowState == WindowState.Maximized) WindowState = WindowState.Normal; else From 3257566781731daf81af9d063cf189b9ab74069b Mon Sep 17 00:00:00 2001 From: leo Date: Thu, 13 Jun 2024 20:39:10 +0800 Subject: [PATCH 18/29] enhance: add hotkeys to commit buttons (#176) --- src/Resources/Locales/en_US.axaml | 2 ++ src/Resources/Locales/zh_CN.axaml | 2 ++ src/Resources/Locales/zh_TW.axaml | 2 ++ src/Views/Hotkeys.axaml | 12 +++++++++--- src/Views/WorkingCopy.axaml | 6 +++++- 5 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index fd962463..ccedbe84 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -243,6 +243,8 @@ Create new page Open preference dialog REPOSITORY + Commit staged changes + Commit and push staged changes Force to reload this repository Stage/Unstage selected changes Open commit search diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 60868f16..faaeb2e5 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -246,6 +246,8 @@ 新建页面 打开偏好设置面板 仓库页面快捷键 + 提交暂存区更改 + 提交暂存区更改并推送 重新加载仓库状态 将选中的变更暂存或从暂存列表中移除 打开历史搜索 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 4ad5108d..86c6781b 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -246,6 +246,8 @@ 新建頁面 開啟偏好設定面板 倉庫頁面快捷鍵 + 提交暫存區變更 + 提交暫存區變更併推送 重新載入倉庫狀態 將選中的變更暫存或從暫存列表中移除 開啟歷史搜尋 diff --git a/src/Views/Hotkeys.axaml b/src/Views/Hotkeys.axaml index ae6dc89d..bf633bff 100644 --- a/src/Views/Hotkeys.axaml +++ b/src/Views/Hotkeys.axaml @@ -84,7 +84,7 @@ FontSize="{Binding Source={x:Static vm:Preference.Instance}, Path=DefaultFontSize, Converter={x:Static c:FontSizeModifyConverters.Increase}}" Margin="0,8"/> - + @@ -100,8 +100,14 @@ - - + + + + + + + + + Command="{Binding Commit}" + HotKey="{OnPlatform Ctrl+Enter, macOS=⌘+Enter}" + ToolTip.Tip="{OnPlatform Ctrl+Enter, macOS=⌘+Enter}"/> - - - - + - - - - - - - - - + + + + + + diff --git a/src/Views/Launcher.axaml b/src/Views/Launcher.axaml index 94076e25..255d57bd 100644 --- a/src/Views/Launcher.axaml +++ b/src/Views/Launcher.axaml @@ -250,59 +250,46 @@ - + - - + + - - - - - + + + + - - - - - - - - - - -