diff --git a/I18N Commander/UI WinForms/Components/Setting.Designer.cs b/I18N Commander/UI WinForms/Components/Setting.Designer.cs index c78b1e7..1d0fbd3 100644 --- a/I18N Commander/UI WinForms/Components/Setting.Designer.cs +++ b/I18N Commander/UI WinForms/Components/Setting.Designer.cs @@ -29,9 +29,9 @@ private void InitializeComponent() { this.tableLayout = new System.Windows.Forms.TableLayoutPanel(); - this.labelExplanation = new System.Windows.Forms.Label(); this.labelSettingName = new System.Windows.Forms.Label(); this.labelIcon = new System.Windows.Forms.Label(); + this.labelExplanation = new System.Windows.Forms.LinkLabel(); this.tableLayout.SuspendLayout(); this.SuspendLayout(); // @@ -42,9 +42,9 @@ this.tableLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 250F)); this.tableLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 300F)); this.tableLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.tableLayout.Controls.Add(this.labelExplanation, 3, 0); this.tableLayout.Controls.Add(this.labelSettingName, 1, 0); this.tableLayout.Controls.Add(this.labelIcon, 0, 0); + this.tableLayout.Controls.Add(this.labelExplanation, 3, 0); this.tableLayout.Dock = System.Windows.Forms.DockStyle.Fill; this.tableLayout.Location = new System.Drawing.Point(0, 0); this.tableLayout.Margin = new System.Windows.Forms.Padding(0); @@ -54,16 +54,6 @@ this.tableLayout.Size = new System.Drawing.Size(1000, 72); this.tableLayout.TabIndex = 0; // - // labelExplanation - // - this.labelExplanation.AutoSize = true; - this.labelExplanation.Dock = System.Windows.Forms.DockStyle.Fill; - this.labelExplanation.Location = new System.Drawing.Point(619, 0); - this.labelExplanation.Name = "labelExplanation"; - this.labelExplanation.Size = new System.Drawing.Size(378, 72); - this.labelExplanation.TabIndex = 1; - this.labelExplanation.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; - // // labelSettingName // this.labelSettingName.AutoSize = true; @@ -83,6 +73,15 @@ this.labelIcon.Size = new System.Drawing.Size(60, 72); this.labelIcon.TabIndex = 3; // + // labelExplanation + // + this.labelExplanation.Dock = System.Windows.Forms.DockStyle.Fill; + this.labelExplanation.Location = new System.Drawing.Point(619, 0); + this.labelExplanation.Name = "labelExplanation"; + this.labelExplanation.Size = new System.Drawing.Size(378, 72); + this.labelExplanation.TabIndex = 4; + this.labelExplanation.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // // Setting // this.AutoScaleDimensions = new System.Drawing.SizeF(120F, 120F); @@ -101,8 +100,8 @@ #endregion private TableLayoutPanel tableLayout; - private Label labelExplanation; private Label labelSettingName; private Label labelIcon; + private LinkLabel labelExplanation; } } diff --git a/I18N Commander/UI WinForms/Components/Setting.cs b/I18N Commander/UI WinForms/Components/Setting.cs index 2fd9010..2bee739 100644 --- a/I18N Commander/UI WinForms/Components/Setting.cs +++ b/I18N Commander/UI WinForms/Components/Setting.cs @@ -1,4 +1,5 @@ -using DataModel.Database; +using System.Diagnostics; +using DataModel.Database; using Processor; using UI_WinForms.Resources; @@ -6,6 +7,15 @@ namespace UI_WinForms.Components; public sealed partial class Setting : UserControl { + private readonly record struct SettingUIData( + Bitmap Icon, + Func SettingName, + Func SettingExplanation, + Func<(string URL, string TextPattern)> SettingExplanationLink, + Func SetupDataControl, + bool ChangeNeedsRestart + ); + private readonly SettingUIData data; private bool needRestart = false; @@ -28,6 +38,21 @@ public sealed partial class Setting : UserControl this.labelSettingName.Text = settingMetaData.SettingName(); this.labelExplanation.Text = settingMetaData.SettingExplanation(); + // + // Handle up to one link in the explanation: + // + var link = settingMetaData.SettingExplanationLink(); + var startIdx = this.labelExplanation.Text.IndexOf(link.TextPattern, StringComparison.InvariantCulture); + this.labelExplanation.Links.Clear(); + if (!string.IsNullOrWhiteSpace(link.TextPattern) && !string.IsNullOrWhiteSpace(link.URL) && startIdx > -1) + this.labelExplanation.Links.Add(startIdx, link.TextPattern.Length, link.URL); + + this.labelExplanation.LinkClicked += (_, args) => + { + if(args.Link.LinkData is string url && !string.IsNullOrWhiteSpace(url) && url.ToLowerInvariant().StartsWith("https://", StringComparison.InvariantCulture)) + Process.Start(new ProcessStartInfo(url) { UseShellExecute = true }); + }; + var dataControl = settingMetaData.SetupDataControl(this.ChangeTrigger); this.tableLayout.Controls.Add(dataControl, 2, 0); @@ -56,14 +81,6 @@ public sealed partial class Setting : UserControl private void UpdateExplanation() => this.labelExplanation.Text = this.data.SettingExplanation(); - private readonly record struct SettingUIData( - Bitmap Icon, - Func SettingName, - Func SettingExplanation, - Func SetupDataControl, - bool ChangeNeedsRestart - ); - private static async Task ShowDeepLModeSettingAsync() { var currentSetting = await AppSettings.GetDeepLMode(); @@ -72,6 +89,7 @@ public sealed partial class Setting : UserControl SettingName: () => "DeepL Service", ChangeNeedsRestart: true, SettingExplanation: () => "DeepL is a translation service that offers a wide range of translation services. This setting allows you to choose between the free and pro version of DeepL.", + SettingExplanationLink: () => ("https://www.deepl.com/", "DeepL"), SetupDataControl: (changeTrigger) => { var dropdown = new ComboBox(); @@ -119,6 +137,7 @@ public sealed partial class Setting : UserControl SettingName: () => "DeepL API Key", ChangeNeedsRestart: true, SettingExplanation: () => "The API key is required to use the DeepL translation service. You can find your API key on the DeepL website.", + SettingExplanationLink: () => ("https://www.deepl.com/", "DeepL"), SetupDataControl: (changeTrigger) => { var textbox = new TextBox(); @@ -149,6 +168,7 @@ public sealed partial class Setting : UserControl SettingName: () => "DeepL Usage", ChangeNeedsRestart: false, SettingExplanation: GetUsageText, + SettingExplanationLink: () => (string.Empty, string.Empty), SetupDataControl: (changeTrigger) => { var progressbar = new ProgressBar(); @@ -221,6 +241,7 @@ public sealed partial class Setting : UserControl SettingName: () => "DeepL Operation", ChangeNeedsRestart: true, SettingExplanation: () => "Should the missing translations be automatically completed by DeepL? This can lead to higher costs. By default, DeepL is only applied manually.", + SettingExplanationLink: () => ("https://www.deepl.com/", "DeepL"), SetupDataControl: (changeTrigger) => { // We set up a combo box with the available actions: @@ -276,6 +297,7 @@ public sealed partial class Setting : UserControl SettingName: () => "DeepL Source Culture", ChangeNeedsRestart: true, SettingExplanation: () => "The source culture is used to translate the missing translations.", + SettingExplanationLink: () => (string.Empty, string.Empty), SetupDataControl: (changeTrigger) => { var dropdown = new ComboBox(); @@ -347,6 +369,7 @@ public sealed partial class Setting : UserControl SettingName = () => $"{localCultureIndex}. Culture", ChangeNeedsRestart = true, SettingExplanation = () => "The culture according to RFC 4646: First comes the ISO 639-1 language code in lower case, followed by a hyphen, followed by the ISO 3166-1 alpha-2 country code in upper case. Example: en-US for English in the USA, de-DE for German in Germany.", + SettingExplanationLink = () => ("https://lonewolfonline.net/list-net-culture-country-codes/", "according to RFC 4646"), SetupDataControl = (changeTrigger) => { var textbox = new TextBox();