using DataModel.Database; using Processor; using UI_WinForms.Resources; namespace UI_WinForms.Components; public sealed partial class Setting : UserControl { public Setting() { this.InitializeComponent(); } private Setting(SettingUIData settingMetaData) { this.InitializeComponent(); this.Dock = DockStyle.Top; this.labelIcon.Image = settingMetaData.Icon; this.labelSettingName.Text = settingMetaData.SettingName(); this.labelExplanation.Text = settingMetaData.SettingExplanation(); var dataControl = settingMetaData.SetupDataControl(); this.tableLayout.Controls.Add(dataControl, 2, 0); // Ensure, that this data control is vertical centered by calculating the needed margin, considering the outer size of the table layout: var margin = (this.tableLayout.GetRowHeights().First() - dataControl.Height) / 2f; dataControl.Margin = new Padding(0, (int) margin, 0, (int)margin); // Calculate the needed height of the explanation label & centering of the data control when the parent window is resized: this.tableLayout.Resize += (sender, args) => { // Adjust the height of the parent controls (table & user control): this.tableLayout.Height = Math.Max((int)this.labelExplanation.CreateGraphics().MeasureString(this.labelExplanation.Text, this.labelExplanation.Font, new SizeF(this.labelExplanation.Width, 1000)).Height, 66); this.Height = this.tableLayout.Height + this.tableLayout.Margin.Vertical; // Ensure, that this data control is vertical centered by calculating the needed margin, considering the outer size of the table layout: var margin = (this.tableLayout.GetRowHeights().First() - dataControl.Height) / 2f; dataControl.Margin = new Padding(0, (int) margin, 0, (int)margin); }; } private readonly record struct SettingUIData( Bitmap Icon, Func SettingName, Func SettingExplanation, Func SetupDataControl ); private static async Task ShowDeepLModeSettingAsync() { var currentSetting = await AppSettings.GetDeepLMode(); var settingData = new SettingUIData( Icon: Icons.deepl_logo_icon_170284, SettingName: () => "DeepL Service", 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.", SetupDataControl: () => { var dropdown = new ComboBox(); dropdown.Items.Add("Disabled"); dropdown.Items.Add("Free version"); dropdown.Items.Add("Pro version"); dropdown.SelectedIndex = currentSetting switch { SettingDeepLMode.DISABLED => 0, SettingDeepLMode.USE_FREE_ACCOUNT => 1, SettingDeepLMode.USE_PRO_ACCOUNT => 2, _ => 0, }; // Setup the change event handler: dropdown.SelectedValueChanged += async (sender, args) => await AppSettings.SetDeepLMode(dropdown.SelectedIndex switch { 0 => SettingDeepLMode.DISABLED, 1 => SettingDeepLMode.USE_FREE_ACCOUNT, 2 => SettingDeepLMode.USE_PRO_ACCOUNT, _ => SettingDeepLMode.DISABLED, }); // Apply the desired layout: dropdown.Dock = DockStyle.Fill; dropdown.DropDownStyle = ComboBoxStyle.DropDownList; return dropdown; } ); return new Setting(settingData); } private static async Task ShowDeepLAPIKeySettingAsync() { var currentSetting = await AppSettings.GetDeepLAPIKey(); var settingData = new SettingUIData( Icon: Icons.icons8_key_512, SettingName: () => "DeepL API Key", SettingExplanation: () => "The API key is required to use the DeepL translation service. You can find your API key on the DeepL website.", SetupDataControl: () => { var textbox = new TextBox(); textbox.Text = currentSetting; textbox.TextChanged += async (sender, args) => await AppSettings.SetDeepLAPIKey(textbox.Text); textbox.Dock = DockStyle.Fill; return textbox; } ); return new Setting(settingData); } private static async Task ShowDeepLActionSettingAsync() { var currentSetting = await AppSettings.GetDeepLAction(); var settingData = new SettingUIData( Icon: Icons.icons8_play_512__2_, SettingName: () => "DeepL Operation", SettingExplanation: () => "Should the missing translations be automatically completed by DeepL? This can lead to higher costs. By default, DeepL is only applied manually.", SetupDataControl: () => { // We set up a combo box with the available actions: var dropdown = new ComboBox(); dropdown.Items.Add("Manual"); dropdown.Items.Add("Automatic"); dropdown.SelectedIndex = currentSetting switch { SettingDeepLAction.MANUAL => 0, SettingDeepLAction.AUTOMATIC_ALL => 1, _ => 0, }; // Setup the change event handler: dropdown.SelectedValueChanged += async (sender, args) => await AppSettings.SetDeepLAction(dropdown.SelectedIndex switch { 0 => SettingDeepLAction.MANUAL, 1 => SettingDeepLAction.AUTOMATIC_ALL, _ => SettingDeepLAction.MANUAL, }); // Apply the desired layout: dropdown.Dock = DockStyle.Fill; dropdown.DropDownStyle = ComboBoxStyle.DropDownList; return dropdown; } ); return new Setting(settingData); } private static IEnumerable> ShowCultureSettingsAsync() { var isFirstCulture = true; // We need this flag to distinguish the first task from the others. var cultureIndices = new List(new []{ -1 }); while (cultureIndices.Count > 0) { var innerLoopIndex = cultureIndices.Last(); // needed to avoid closure issues. yield return Task.Run(async () => { var localCultureIndex = innerLoopIndex; // Get a list of culture indices. Thus, we know the number of cultures. We cannot do this in the outer loop, // because we cannot await there. The AppSettings is caching the answer, though. The list of indices is ordered // ascending. var localCultureIndices = await AppSettings.GetCultureIndices(); // Update the number of cultures in the outer loop for the first call: if(isFirstCulture) { localCultureIndex = localCultureIndices.Last(); innerLoopIndex = localCultureIndices.Last(); cultureIndices.Clear(); cultureIndices.AddRange(localCultureIndices); isFirstCulture = false; } // Get the current culture code: var currentCultureCode = await AppSettings.GetCultureCode(localCultureIndex); // Construct the setting: return new Setting(new() { Icon = Icons.icons8_chat_bubble_512, SettingName = () => $"{localCultureIndex}. Culture", 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.", SetupDataControl = () => { var textbox = new TextBox(); textbox.Text = currentCultureCode; textbox.TextChanged += async (sender, args) => { await AppSettings.SetCultureCode(localCultureIndex, textbox.Text); }; textbox.Dock = DockStyle.Fill; return textbox; } }); }); cultureIndices.Remove(innerLoopIndex); } } public static IEnumerable> GetAllSettings() { foreach (var setting in ShowCultureSettingsAsync()) { yield return setting; } yield return ShowDeepLActionSettingAsync(); yield return ShowDeepLAPIKeySettingAsync(); yield return ShowDeepLModeSettingAsync(); } }