diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 69844221..6ab89410 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -181,17 +181,17 @@ Open Explore in File Manager - Push '{0}' + Push ${0}$ Discard all changes - Fast-Forward to '{0}' - Pull '{0}' - Pull '{0}' into '{1}' - Checkout '{0}' - Merge '{0}' into '{1}' - Rebase '{0}' on '{1}' - Git Flow - Finish '{0}' - Rename '{0}' - Delete '{0}' + Fast-Forward to ${0}$ + Pull ${0}$ + Pull ${0}$ into ${1}$ + Checkout ${0}$ + Merge ${0}$ into ${1}$ + Rebase ${0}$ on ${1}$ + Git Flow - Finish ${0}$ + Rename ${0}$ + Delete ${0}$ Tracking ... Copy Branch Name Unset Upstream @@ -203,8 +203,8 @@ Delete ... Copy URL - Reset '{0}' to Here - Rebase '{0}' to Here + Reset ${0}$ to Here + Rebase ${0}$ to Here Cherry-Pick This Commit Reword Squash Into Parent @@ -212,8 +212,8 @@ Save as Patch ... Copy SHA - Push '{0}' - Delete '{0}' + Push ${0}$ + Delete ${0}$ Copy Tag Name Apply @@ -458,66 +458,12 @@ To : Reword : - Statistics - WEEK - MONTH - YEAR - Total Committers: {0} - Total Commits:{0} - COMMITTER - COMMITS - FILES ASSUME UNCHANGED REMOVE NO FILES ASSUMED AS UNCHANGED - SUN - MON - TUE - WED - THU - FRI - SAT - - Jan - Feb - Mar - Apr - May - Jun - Jul - Aug - Sep - Oct - Nov - Dec - - By Name - By Recently Opened - By Bookmark Color - Git has NOT been configured. Please to go [Preference] and configure it first. - Path[{0}] not exists! - Can NOT locate bash.exe. Make sure bash.exe exists under the same folder with git.exe BINARY FILE NOT SUPPORTED!!! BLAME ON THIS FILE IS NOT SUPPORTED!!! - GIT_DIR for this repository NOT FOUND! - Initialize Git-flow failed! - Bad git-flow branch type! - EXISTS and FULL ACCESS CONTROL needed - Remote git URL not supported - Bad local repository name - Remote name can NOT be null - Bad name for remote. Regex: ^[\\w\\-\\.]+$ - Duplicated remote name! - Branch name can NOT be null - Bad name for branch. Regex: ^[\\w\\-/\\.]+$ - Duplicated branch name! - Commit message can NOT be empty - Invalid path for patch file - Invalid relative path - Invalid path for archive file - This field is required - You are removing repository '{0}'. Are you sure to continue? Patch has been saved successfully! diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index b733a23d..7357fa7b 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -180,17 +180,17 @@ 打开 在浏览器中查看 - 推送 '{0}' + 推送 ${0}$ 放弃所有更改 - 快进到 '{0}' - 拉回 '{0}' - 拉回 '{0}' 内容至 '{1}' - 检出 '{0}' - 合并 '{0}' 到 '{1}' - 变基 '{0}' 分支至 '{1}' - GIT工作流 - 完成 '{0}' - 重命名 '{0}' - 删除 '{0}' + 快进到 ${0}$ + 拉回 ${0}$ + 拉回 ${0}$ 内容至 ${1}$ + 检出 ${0}$ + 合并 ${0}$ 到 ${1}$ + 变基 ${0}$ 分支至 ${1}$ + GIT工作流 - 完成 ${0}$ + 重命名 ${0}$ + 删除 ${0}$ 切换上游分支... 复制分支名 取消追踪 @@ -202,8 +202,8 @@ 删除 ... 复制远程地址 - 重置 '{0}' 到此处 - 变基 '{0}' 到此处 + 重置 ${0}$ 到此处 + 变基 ${0}$ 到此处 挑选此提交 编辑提交信息 合并此提交到上一个提交 @@ -211,8 +211,8 @@ 另存为补丁 ... 复制提交指纹 - 推送 '{0}' - 删除 '{0}' + 推送 ${0}$ + 删除 ${0}$ 复制标签名 应用 @@ -457,66 +457,12 @@ 合并到 : 修改提交信息: - 提交统计 - 本周 - 本月 - 本年 - 提交者人数:{0} - 总计提交次数:{0} - 提交者 - 提交次数 - 不跟踪更改的文件 移除 没有不跟踪更改的文件 - 星期日 - 星期一 - 星期二 - 星期三 - 星期四 - 星期五 - 星期六 - - 1月 - 2月 - 3月 - 4月 - 5月 - 6月 - 7月 - 8月 - 9月 - 10月 - 11月 - 12月 - - 按名称升序 - 按最近访问 - 按书签颜色 - GIT尚未配置。请打开【偏好设置】配置GIT路径。 - 路径({0})不存在或不可读取! - 无法找到bash.exe,请确保其在git.exe同目录中! 二进制文件不支持该操作!!! 选中文件不支持该操作!!! - 获取仓库GIT_DIR失败! - 初始化GIT FLOW失败! - 不支持的GIT FLOW分支! - 目录不存在或不可写!!! - 非法的远程仓库地址! - 非法的本地仓库地址! - 远程仓库地址不可为空 - 远程仓库地址包含非法字符!仅支持字母、数字、下划线、横线或英文点号! - 远程仓库名已存在! - 分支名不可为空 - 分支名包含非法字符!仅支持字母、数字、下划线、横线或英文点号! - 分支名已存在! - 提交信息未填写! - 补丁文件不存在或不可访问! - 非法的子路径! - 非法的存档文件路径! - 内容未填写! - 正在将 '{0}' 从列表中移除,是否要继续? 补丁已成功保存! diff --git a/src/ViewModels/Histories.cs b/src/ViewModels/Histories.cs index 3a9222e9..942e3144 100644 --- a/src/ViewModels/Histories.cs +++ b/src/ViewModels/Histories.cs @@ -131,7 +131,7 @@ namespace SourceGit.ViewModels { if (current.Head != commit.SHA) { var reset = new MenuItem(); - reset.Header = App.Text("CommitCM.Reset", current.Name); + reset.Header = CreateHighlightLabel("CommitCM.Reset", current.Name); reset.Icon = CreateMenuIcon("Icons.Reset"); reset.Click += (o, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Reset(_repo, current, commit)); @@ -165,7 +165,7 @@ namespace SourceGit.ViewModels { if (!commit.IsMerged) { var rebase = new MenuItem(); - rebase.Header = App.Text("CommitCM.Rebase", current.Name); + rebase.Header = CreateHighlightLabel("CommitCM.Rebase", current.Name); rebase.Icon = CreateMenuIcon("Icons.Rebase"); rebase.Click += (o, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Rebase(_repo, current, commit)); @@ -262,7 +262,7 @@ namespace SourceGit.ViewModels { var upstream = current.Upstream.Substring(13); var fastForward = new MenuItem(); - fastForward.Header = App.Text("BranchCM.FastForward", upstream); + fastForward.Header = CreateHighlightLabel("BranchCM.FastForward", upstream); fastForward.Icon = CreateMenuIcon("Icons.FastForward"); fastForward.IsEnabled = dirty; fastForward.Click += (o, e) => { @@ -272,7 +272,7 @@ namespace SourceGit.ViewModels { submenu.Items.Add(fastForward); var pull = new MenuItem(); - pull.Header = App.Text("BranchCM.Pull", upstream); + pull.Header = CreateHighlightLabel("BranchCM.Pull", upstream); pull.Icon = CreateMenuIcon("Icons.Pull"); pull.IsEnabled = dirty; pull.Click += (o, e) => { @@ -283,7 +283,7 @@ namespace SourceGit.ViewModels { } var push = new MenuItem(); - push.Header = App.Text("BranchCM.Push", current.Name); + push.Header = CreateHighlightLabel("BranchCM.Push", current.Name); push.Icon = CreateMenuIcon("Icons.Push"); push.IsEnabled = _repo.Remotes.Count > 0 && dirty; push.Click += (o, e) => { @@ -296,7 +296,7 @@ namespace SourceGit.ViewModels { var type = _repo.GitFlow.GetBranchType(current.Name); if (type != Models.GitFlowBranchType.None) { var finish = new MenuItem(); - finish.Header = App.Text("BranchCM.Finish", current.Name); + finish.Header = CreateHighlightLabel("BranchCM.Finish", current.Name); finish.Icon = CreateMenuIcon("Icons.Flow"); finish.Click += (o, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new GitFlowFinish(_repo, current, type)); @@ -307,7 +307,7 @@ namespace SourceGit.ViewModels { } var rename = new MenuItem(); - rename.Header = App.Text("BranchCM.Rename", current.Name); + rename.Header = CreateHighlightLabel("BranchCM.Rename", current.Name); rename.Icon = CreateMenuIcon("Icons.Rename"); rename.Click += (o, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new RenameBranch(_repo, current)); @@ -324,7 +324,7 @@ namespace SourceGit.ViewModels { submenu.Header = branch.Name; var checkout = new MenuItem(); - checkout.Header = App.Text("BranchCM.Checkout", branch.Name); + checkout.Header = CreateHighlightLabel("BranchCM.Checkout", branch.Name); checkout.Icon = CreateMenuIcon("Icons.Check"); checkout.Click += (o, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowAndStartPopup(new Checkout(_repo, branch.Name)); @@ -333,7 +333,7 @@ namespace SourceGit.ViewModels { submenu.Items.Add(checkout); var merge = new MenuItem(); - merge.Header = App.Text("BranchCM.Merge", branch.Name, current.Name); + merge.Header = CreateHighlightLabel("BranchCM.Merge", branch.Name, current.Name); merge.Icon = CreateMenuIcon("Icons.Merge"); merge.IsEnabled = !merged; merge.Click += (o, e) => { @@ -346,7 +346,7 @@ namespace SourceGit.ViewModels { var type = _repo.GitFlow.GetBranchType(branch.Name); if (type != Models.GitFlowBranchType.None) { var finish = new MenuItem(); - finish.Header = App.Text("BranchCM.Finish", branch.Name); + finish.Header = CreateHighlightLabel("BranchCM.Finish", branch.Name); finish.Icon = CreateMenuIcon("Icons.Flow"); finish.Click += (o, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new GitFlowFinish(_repo, branch, type)); @@ -357,7 +357,7 @@ namespace SourceGit.ViewModels { } var rename = new MenuItem(); - rename.Header = App.Text("BranchCM.Rename", branch.Name); + rename.Header = CreateHighlightLabel("BranchCM.Rename", branch.Name); rename.Icon = CreateMenuIcon("Icons.Rename"); rename.Click += (o, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new RenameBranch(_repo, branch)); @@ -366,7 +366,7 @@ namespace SourceGit.ViewModels { submenu.Items.Add(rename); var delete = new MenuItem(); - delete.Header = App.Text("BranchCM.Delete", branch.Name); + delete.Header = CreateHighlightLabel("BranchCM.Delete", branch.Name); delete.Icon = CreateMenuIcon("Icons.Clear"); delete.Click += (o, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteBranch(_repo, branch)); @@ -385,7 +385,7 @@ namespace SourceGit.ViewModels { submenu.Header = name; var checkout = new MenuItem(); - checkout.Header = App.Text("BranchCM.Checkout", name); + checkout.Header = CreateHighlightLabel("BranchCM.Checkout", name); checkout.Icon = CreateMenuIcon("Icons.Check"); checkout.Click += (o, e) => { foreach (var b in _repo.Branches) { @@ -402,7 +402,7 @@ namespace SourceGit.ViewModels { submenu.Items.Add(checkout); var merge = new MenuItem(); - merge.Header = App.Text("BranchCM.Merge", name, current.Name); + merge.Header = CreateHighlightLabel("BranchCM.Merge", name, current.Name); merge.Icon = CreateMenuIcon("Icons.Merge"); merge.IsEnabled = !merged; merge.Click += (o, e) => { @@ -414,7 +414,7 @@ namespace SourceGit.ViewModels { submenu.Items.Add(new MenuItem() { Header = "-" }); var delete = new MenuItem(); - delete.Header = App.Text("BranchCM.Delete", name); + delete.Header = CreateHighlightLabel("BranchCM.Delete", name); delete.Icon = CreateMenuIcon("Icons.Clear"); delete.Click += (o, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteBranch(_repo, branch)); @@ -432,7 +432,7 @@ namespace SourceGit.ViewModels { submenu.MinWidth = 200; var push = new MenuItem(); - push.Header = App.Text("TagCM.Push", tag.Name); + push.Header = CreateHighlightLabel("TagCM.Push", tag.Name); push.Icon = CreateMenuIcon("Icons.Push"); push.IsEnabled = _repo.Remotes.Count > 0; push.Click += (o, e) => { @@ -442,7 +442,7 @@ namespace SourceGit.ViewModels { submenu.Items.Add(push); var delete = new MenuItem(); - delete.Header = App.Text("TagCM.Delete", tag.Name); + delete.Header = CreateHighlightLabel("TagCM.Delete", tag.Name); delete.Icon = CreateMenuIcon("Icons.Clear"); delete.Click += (o, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteTag(_repo, tag)); @@ -453,6 +453,13 @@ namespace SourceGit.ViewModels { menu.Items.Add(submenu); } + private object CreateHighlightLabel(string key, params object[] args) { + var label = new Views.NameHighlightedTextBlock(); + label.Text = App.Text(key, args); + label.VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center; + return label; + } + private Avalonia.Controls.Shapes.Path CreateMenuIcon(string key) { var icon = new Avalonia.Controls.Shapes.Path(); icon.Width = 12; diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index db70e47f..b42bb31b 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -578,7 +578,7 @@ namespace SourceGit.ViewModels { var menu = new ContextMenu(); var push = new MenuItem(); - push.Header = App.Text("BranchCM.Push", branch.Name); + push.Header = CreateHighlightLabel("BranchCM.Push", branch.Name); push.Icon = CreateMenuIcon("Icons.Push"); push.IsEnabled = Remotes.Count > 0; push.Click += (_, e) => { @@ -601,7 +601,7 @@ namespace SourceGit.ViewModels { if (!string.IsNullOrEmpty(branch.Upstream)) { var upstream = branch.Upstream.Substring(13); var fastForward = new MenuItem(); - fastForward.Header = App.Text("BranchCM.FastForward", upstream); + fastForward.Header = CreateHighlightLabel("BranchCM.FastForward", upstream); fastForward.Icon = CreateMenuIcon("Icons.FastForward"); fastForward.IsEnabled = !string.IsNullOrEmpty(branch.UpstreamTrackStatus) && branch.UpstreamTrackStatus.IndexOf('↑') < 0; fastForward.Click += (o, e) => { @@ -610,7 +610,7 @@ namespace SourceGit.ViewModels { }; var pull = new MenuItem(); - pull.Header = App.Text("BranchCM.Pull", upstream); + pull.Header = CreateHighlightLabel("BranchCM.Pull", upstream); pull.Icon = CreateMenuIcon("Icons.Pull"); pull.IsEnabled = !string.IsNullOrEmpty(branch.UpstreamTrackStatus); pull.Click += (o, e) => { @@ -627,7 +627,7 @@ namespace SourceGit.ViewModels { var current = Branches.Find(x => x.IsCurrent); var checkout = new MenuItem(); - checkout.Header = App.Text("BranchCM.Checkout", branch.Name); + checkout.Header = CreateHighlightLabel("BranchCM.Checkout", branch.Name); checkout.Icon = CreateMenuIcon("Icons.Check"); checkout.Click += (o, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowAndStartPopup(new Checkout(this, branch.Name)); @@ -638,7 +638,7 @@ namespace SourceGit.ViewModels { var upstream = Branches.Find(x => x.FullName == branch.Upstream); if (upstream != null) { var fastForward = new MenuItem(); - fastForward.Header = App.Text("BranchCM.FastForward", $"{upstream.Remote}/{upstream.Name}"); + fastForward.Header = CreateHighlightLabel("BranchCM.FastForward", $"{upstream.Remote}/{upstream.Name}"); fastForward.Icon = CreateMenuIcon("Icons.FastForward"); fastForward.IsEnabled = !string.IsNullOrEmpty(branch.UpstreamTrackStatus) && branch.UpstreamTrackStatus.IndexOf('↑') < 0; fastForward.Click += (o, e) => { @@ -654,7 +654,7 @@ namespace SourceGit.ViewModels { menu.Items.Add(push); var merge = new MenuItem(); - merge.Header = App.Text("BranchCM.Merge", branch.Name, current.Name); + merge.Header = CreateHighlightLabel("BranchCM.Merge", branch.Name, current.Name); merge.Icon = CreateMenuIcon("Icons.Merge"); merge.Click += (o, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Merge(this, branch.Name, current.Name)); @@ -662,7 +662,7 @@ namespace SourceGit.ViewModels { }; var rebase = new MenuItem(); - rebase.Header = App.Text("BranchCM.Rebase", current.Name, branch.Name); + rebase.Header = CreateHighlightLabel("BranchCM.Rebase", current.Name, branch.Name); rebase.Icon = CreateMenuIcon("Icons.Rebase"); rebase.Click += (o, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Rebase(this, current, branch)); @@ -676,7 +676,7 @@ namespace SourceGit.ViewModels { var type = GitFlow.GetBranchType(branch.Name); if (type != Models.GitFlowBranchType.None) { var finish = new MenuItem(); - finish.Header = App.Text("BranchCM.Finish", branch.Name); + finish.Header = CreateHighlightLabel("BranchCM.Finish", branch.Name); finish.Icon = CreateMenuIcon("Icons.Flow"); finish.Click += (o, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new GitFlowFinish(this, branch, type)); @@ -687,7 +687,7 @@ namespace SourceGit.ViewModels { } var rename = new MenuItem(); - rename.Header = App.Text("BranchCM.Rename", branch.Name); + rename.Header = CreateHighlightLabel("BranchCM.Rename", branch.Name); rename.Icon = CreateMenuIcon("Icons.Rename"); rename.Click += (o, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new RenameBranch(this, branch)); @@ -695,7 +695,7 @@ namespace SourceGit.ViewModels { }; var delete = new MenuItem(); - delete.Header = App.Text("BranchCM.Delete", branch.Name); + delete.Header = CreateHighlightLabel("BranchCM.Delete", branch.Name); delete.Icon = CreateMenuIcon("Icons.Clear"); delete.IsEnabled = !branch.IsCurrent; delete.Click += (o, e) => { @@ -847,7 +847,7 @@ namespace SourceGit.ViewModels { var current = Branches.Find(x => x.IsCurrent); var checkout = new MenuItem(); - checkout.Header = App.Text("BranchCM.Checkout", $"{branch.Remote}/{branch.Name}"); + checkout.Header = CreateHighlightLabel("BranchCM.Checkout", $"{branch.Remote}/{branch.Name}"); checkout.Icon = CreateMenuIcon("Icons.Check"); checkout.Click += (o, e) => { foreach (var b in Branches) { @@ -866,7 +866,7 @@ namespace SourceGit.ViewModels { if (current != null) { var pull = new MenuItem(); - pull.Header = App.Text("BranchCM.PullInto", $"{branch.Remote}/{branch.Name}", current.Name); + pull.Header = CreateHighlightLabel("BranchCM.PullInto", $"{branch.Remote}/{branch.Name}", current.Name); pull.Icon = CreateMenuIcon("Icons.Pull"); pull.Click += (o, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Pull(this, branch)); @@ -874,7 +874,7 @@ namespace SourceGit.ViewModels { }; var merge = new MenuItem(); - merge.Header = App.Text("BranchCM.Merge", $"{branch.Remote}/{branch.Name}", current.Name); + merge.Header = CreateHighlightLabel("BranchCM.Merge", $"{branch.Remote}/{branch.Name}", current.Name); merge.Icon = CreateMenuIcon("Icons.Merge"); merge.Click += (o, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Merge(this, $"{branch.Remote}/{branch.Name}", current.Name)); @@ -882,7 +882,7 @@ namespace SourceGit.ViewModels { }; var rebase = new MenuItem(); - rebase.Header = App.Text("BranchCM.Rebase", current.Name, $"{branch.Remote}/{branch.Name}"); + rebase.Header = CreateHighlightLabel("BranchCM.Rebase", current.Name, $"{branch.Remote}/{branch.Name}"); rebase.Icon = CreateMenuIcon("Icons.Rebase"); rebase.Click += (o, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Rebase(this, current, branch)); @@ -896,7 +896,7 @@ namespace SourceGit.ViewModels { } var delete = new MenuItem(); - delete.Header = App.Text("BranchCM.Delete", $"{branch.Remote}/{branch.Name}"); + delete.Header = CreateHighlightLabel("BranchCM.Delete", $"{branch.Remote}/{branch.Name}"); delete.Icon = CreateMenuIcon("Icons.Clear"); delete.Click += (o, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteBranch(this, branch)); @@ -956,7 +956,7 @@ namespace SourceGit.ViewModels { }; var pushTag = new MenuItem(); - pushTag.Header = App.Text("TagCM.Push", tag.Name); + pushTag.Header = CreateHighlightLabel("TagCM.Push", tag.Name); pushTag.Icon = CreateMenuIcon("Icons.Push"); pushTag.IsEnabled = Remotes.Count > 0; pushTag.Click += (o, ev) => { @@ -965,7 +965,7 @@ namespace SourceGit.ViewModels { }; var deleteTag = new MenuItem(); - deleteTag.Header = App.Text("TagCM.Delete", tag.Name); + deleteTag.Header = CreateHighlightLabel("TagCM.Delete", tag.Name); deleteTag.Icon = CreateMenuIcon("Icons.Clear"); deleteTag.Click += (o, ev) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteTag(this, tag)); @@ -1046,6 +1046,13 @@ namespace SourceGit.ViewModels { return menu; } + private object CreateHighlightLabel(string key, params object[] args) { + var label = new Views.NameHighlightedTextBlock(); + label.Text = App.Text(key, args); + label.VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center; + return label; + } + private Avalonia.Controls.Shapes.Path CreateMenuIcon(string key) { var icon = new Avalonia.Controls.Shapes.Path(); icon.Width = 12; diff --git a/src/Views/NameHighlightedTextBlock.cs b/src/Views/NameHighlightedTextBlock.cs new file mode 100644 index 00000000..a4ea98b6 --- /dev/null +++ b/src/Views/NameHighlightedTextBlock.cs @@ -0,0 +1,100 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Media; +using System; +using System.Globalization; + +namespace SourceGit.Views { + public class NameHighlightedTextBlock : Control { + + public static readonly StyledProperty TextProperty = + AvaloniaProperty.Register(nameof(Text)); + + public string Text { + get => GetValue(TextProperty); + set => SetValue(TextProperty, value); + } + + public static readonly StyledProperty FontFamilyProperty = + TextBlock.FontFamilyProperty.AddOwner(); + + public FontFamily FontFamily { + get => GetValue(FontFamilyProperty); + set => SetValue(FontFamilyProperty, value); + } + + public static readonly StyledProperty FontSizeProperty = + TextBlock.FontSizeProperty.AddOwner(); + + public double FontSize { + get => GetValue(FontSizeProperty); + set => SetValue(FontSizeProperty, value); + } + + public static readonly StyledProperty ForegroundProperty = + TextBlock.ForegroundProperty.AddOwner(); + + public IBrush Foreground { + get => GetValue(ForegroundProperty); + set => SetValue(ForegroundProperty, value); + } + + static NameHighlightedTextBlock() { + AffectsMeasure(TextProperty); + } + + protected override Size MeasureOverride(Size availableSize) { + var text = Text; + if (string.IsNullOrEmpty(text)) return base.MeasureOverride(availableSize); + + var typeface = new Typeface(FontFamily, FontStyle.Normal, FontWeight.Normal, FontStretch.Normal); + var formatted = new FormattedText( + Text, + CultureInfo.CurrentCulture, + FlowDirection.LeftToRight, + typeface, + FontSize, + Foreground); + return new Size(formatted.Width, formatted.Height); + } + + public override void Render(DrawingContext context) { + var text = Text; + if (string.IsNullOrEmpty(text)) return; + + var normalTypeface = new Typeface(FontFamily, FontStyle.Normal, FontWeight.Normal, FontStretch.Normal); + var highlightTypeface = new Typeface(FontFamily, FontStyle.Normal, FontWeight.Bold, FontStretch.Normal); + var underlinePen = new Pen(Foreground, 1); + var offsetX = 0.0; + + var parts = text.Split('$', StringSplitOptions.None); + var isName = false; + foreach (var part in parts) { + if (string.IsNullOrEmpty(part)) { + isName = !isName; + continue; + } + + var formatted = new FormattedText( + part, + CultureInfo.CurrentCulture, + FlowDirection.LeftToRight, + isName ? highlightTypeface : normalTypeface, + FontSize, + Foreground); + + context.DrawText(formatted, new Point(offsetX, 0)); + + if (isName) { + var lineY = formatted.Baseline + 2; + context.DrawLine(underlinePen, new Point(offsetX, lineY), new Point(offsetX + formatted.Width, lineY)); + offsetX += formatted.Width; + } else { + offsetX += formatted.Width + 4; + } + + isName = !isName; + } + } + } +}