From e7aa79db64ae9cd97d998ab741fb6acfbc844c25 Mon Sep 17 00:00:00 2001 From: Pascal Date: Thu, 20 Nov 2025 06:49:21 +0100 Subject: [PATCH] ui(client): recent profile selector --- ModpackUpdater.Apps.Client.Gui/AppConfig.cs | 17 +-- .../LangRes/GeneralLangRes.Designer.cs | 116 +++++++++++++++--- .../LangRes/GeneralLangRes.resx | 3 + ModpackUpdater.Apps.Client.Gui/MainForm.axaml | 5 +- .../MainForm.axaml.cs | 71 ++++++++--- ModpackUpdater.Apps.Client.Gui/Program.cs | 22 ---- .../Ui/MainWindow.axaml.cs | 8 +- 7 files changed, 174 insertions(+), 68 deletions(-) diff --git a/ModpackUpdater.Apps.Client.Gui/AppConfig.cs b/ModpackUpdater.Apps.Client.Gui/AppConfig.cs index 17e7220..a13b32d 100644 --- a/ModpackUpdater.Apps.Client.Gui/AppConfig.cs +++ b/ModpackUpdater.Apps.Client.Gui/AppConfig.cs @@ -7,19 +7,22 @@ public class AppConfig : ISettingsNode, ISettingsIdentifier { public static string Identifier => "pilz.appconfig"; - public string? LastMinecraftProfilePath { get; set; } + public List RecentMinecraftProfilePaths { get; } = []; - [JsonIgnore, Obsolete] - public string? ConfigFilePath { get; private set; } - [JsonProperty("ConfigFilePath"), Obsolete] - private string ConfigFilePathLegacy + [JsonProperty, Obsolete] + private string? LastMinecraftProfilePath { - set => ConfigFilePath = value; + get => RecentMinecraftProfilePaths.FirstOrDefault(); + set + { + if (!string.IsNullOrWhiteSpace(value)) + RecentMinecraftProfilePaths.Insert(0, value); + } } public void Reset() { - LastMinecraftProfilePath = null; + RecentMinecraftProfilePaths.Clear(); } public static AppConfig Instance => Program.Settings.Get(); diff --git a/ModpackUpdater.Apps.Client.Gui/LangRes/GeneralLangRes.Designer.cs b/ModpackUpdater.Apps.Client.Gui/LangRes/GeneralLangRes.Designer.cs index 74e9206..e905f4f 100644 --- a/ModpackUpdater.Apps.Client.Gui/LangRes/GeneralLangRes.Designer.cs +++ b/ModpackUpdater.Apps.Client.Gui/LangRes/GeneralLangRes.Designer.cs @@ -11,32 +11,46 @@ namespace ModpackUpdater.Apps.Client.Gui.LangRes { using System; - [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] - [System.Diagnostics.DebuggerNonUserCodeAttribute()] - [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class GeneralLangRes { - private static System.Resources.ResourceManager resourceMan; + private static global::System.Resources.ResourceManager resourceMan; - private static System.Globalization.CultureInfo resourceCulture; + private static global::System.Globalization.CultureInfo resourceCulture; - [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal GeneralLangRes() { } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] - public static System.Resources.ResourceManager ResourceManager { + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { get { - if (object.Equals(null, resourceMan)) { - System.Resources.ResourceManager temp = new System.Resources.ResourceManager("ModpackUpdater.Apps.Client.Gui.LangRes.GeneralLangRes", typeof(GeneralLangRes).Assembly); + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ModpackUpdater.Apps.Client.Gui.LangRes.GeneralLangRes", typeof(GeneralLangRes).Assembly); resourceMan = temp; } return resourceMan; } } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] - public static System.Globalization.CultureInfo Culture { + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } @@ -45,118 +59,184 @@ namespace ModpackUpdater.Apps.Client.Gui.LangRes { } } + /// + /// Looks up a localized string similar to An update is available!. + /// public static string AnUpdateIsAvailable { get { return ResourceManager.GetString("AnUpdateIsAvailable", resourceCulture); } } + /// + /// Looks up a localized string similar to Check for updates. + /// public static string CheckForUpdates { get { return ResourceManager.GetString("CheckForUpdates", resourceCulture); } } + /// + /// Looks up a localized string similar to Checking for Updates.... + /// public static string CheckingForUpdates { get { return ResourceManager.GetString("CheckingForUpdates", resourceCulture); } } + /// + /// Looks up a localized string similar to Config incomplete or not loaded!. + /// public static string ConfigIncompleteOrNotLoaded { get { return ResourceManager.GetString("ConfigIncompleteOrNotLoaded", resourceCulture); } } + /// + /// Looks up a localized string similar to Downloading program update.... + /// public static string DownloadProgramUpdate { get { return ResourceManager.GetString("DownloadProgramUpdate", resourceCulture); } } + /// + /// Looks up a localized string similar to Error on update check or while updating!. + /// public static string ErrorOnUpdateCheckOrUpdating { get { return ResourceManager.GetString("ErrorOnUpdateCheckOrUpdating", resourceCulture); } } + /// + /// Looks up a localized string similar to Everything is right and up-to-date.. + /// public static string EverythingIsRightAndUpToDate { get { return ResourceManager.GetString("EverythingIsRightAndUpToDate", resourceCulture); } } + /// + /// Looks up a localized string similar to Install. + /// public static string Install { get { return ResourceManager.GetString("Install", resourceCulture); } } + /// + /// Looks up a localized string similar to Installation key. + /// public static string InstallationKey { get { return ResourceManager.GetString("InstallationKey", resourceCulture); } } + /// + /// Looks up a localized string similar to Installation key seems to be invalid. + /// + public static string InstallationKeyNotValid { + get { + return ResourceManager.GetString("InstallationKeyNotValid", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Installing.... + /// public static string Installing { get { return ResourceManager.GetString("Installing", resourceCulture); } } + /// + /// Looks up a localized string similar to Minecraft profile. + /// public static string MinecraftProfile { get { return ResourceManager.GetString("MinecraftProfile", resourceCulture); } } + /// + /// Looks up a localized string similar to Minecraft profile folder seems to be not valid.. + /// public static string MinecraftProfileFolderSeemsInvalid { get { return ResourceManager.GetString("MinecraftProfileFolderSeemsInvalid", resourceCulture); } } + /// + /// Looks up a localized string similar to Modpack config url. + /// public static string ModpackConfigUrl { get { return ResourceManager.GetString("ModpackConfigUrl", resourceCulture); } } + /// + /// Looks up a localized string similar to No recent profiles available.. + /// + public static string NoRecentProfilesAvailable { + get { + return ResourceManager.GetString("NoRecentProfilesAvailable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Repair. + /// public static string Repair { get { return ResourceManager.GetString("Repair", resourceCulture); } } + /// + /// Looks up a localized string similar to Select. + /// public static string Select { get { return ResourceManager.GetString("Select", resourceCulture); } } + /// + /// Looks up a localized string similar to Select the minecraft profile folder (usually named .minecraft). + /// public static string SelectMinecraftProfileFolder { get { return ResourceManager.GetString("SelectMinecraftProfileFolder", resourceCulture); } } + /// + /// Looks up a localized string similar to Status. + /// public static string Status { get { return ResourceManager.GetString("Status", resourceCulture); } } + /// + /// Looks up a localized string similar to The update servers are in maintenance.. + /// public static string UpdateServerInMaintenance { get { return ResourceManager.GetString("UpdateServerInMaintenance", resourceCulture); } } - - public static string InstallationKeyNotValid { - get { - return ResourceManager.GetString("InstallationKeyNotValid", resourceCulture); - } - } } } diff --git a/ModpackUpdater.Apps.Client.Gui/LangRes/GeneralLangRes.resx b/ModpackUpdater.Apps.Client.Gui/LangRes/GeneralLangRes.resx index 90f2ef1..ddd6ca3 100644 --- a/ModpackUpdater.Apps.Client.Gui/LangRes/GeneralLangRes.resx +++ b/ModpackUpdater.Apps.Client.Gui/LangRes/GeneralLangRes.resx @@ -174,4 +174,7 @@ Installation key seems to be invalid + + No recent profiles available. + \ No newline at end of file diff --git a/ModpackUpdater.Apps.Client.Gui/MainForm.axaml b/ModpackUpdater.Apps.Client.Gui/MainForm.axaml index 115433b..8891503 100644 --- a/ModpackUpdater.Apps.Client.Gui/MainForm.axaml +++ b/ModpackUpdater.Apps.Client.Gui/MainForm.axaml @@ -12,7 +12,8 @@ WindowStartupLocation="CenterScreen" CanMaximize="false" Title="Minecraft Modpack Updater" - Icon="/Assets/app.ico"> + Icon="/Assets/app.ico" + Loaded="MainForm_Loaded"> - + { + if (File.Exists(path)) + return; + + var item = new MenuItem + { + Header = path.Length > 50 ? $"...{path[^50..]}" : path, + DataContext = path, + }; + item.Click += MenuItemRecentMinecraftProfilePathItem_Click; + menuFlyoutSearchProfileFolder.Items.Add(item); + }); + } + + private void StoreRecentMinecraftProfilePath(string? path) + { + if (string.IsNullOrWhiteSpace(path)) + return; + + AppConfig.Instance.RecentMinecraftProfilePaths.Remove(path); + AppConfig.Instance.RecentMinecraftProfilePaths.Insert(0, path); + AppConfig.Instance.RecentMinecraftProfilePaths.Skip(10).ForEach(n => AppConfig.Instance.RecentMinecraftProfilePaths.Remove(n)); + } private void LoadProfileToUi() { loadingData = true; - TextBoxMinecraftProfileFolder.Text = modpackInfo.LocalPath ?? AppConfig.Instance.LastMinecraftProfilePath ?? TextBoxMinecraftProfileFolder.Text; + TextBoxMinecraftProfileFolder.Text = modpackInfo.LocalPath ?? AppConfig.Instance.RecentMinecraftProfilePaths.FirstOrDefault() ?? TextBoxMinecraftProfileFolder.Text; TextBoxModpackConfig.Text = modpackInfo.ConfigUrl ?? TextBoxModpackConfig.Text; TextBoxInstallKey.Text = modpackInfo.ExtrasKey ?? TextBoxInstallKey.Text; @@ -76,8 +113,13 @@ public partial class MainForm : Window private async void CheckStatusAndUpdate(bool loadProfileToUi) { - if (CheckStatus(loadProfileToUi)) - await ExecuteUpdate(false, false); + if (!CheckStatus(loadProfileToUi)) + return; + + await ExecuteUpdate(false, false); + + StoreRecentMinecraftProfilePath(modpackInfo.LocalPath); + LoadRecentFilesToUi(); } private bool CheckStatus(bool loadProfileToUi) @@ -262,18 +304,13 @@ public partial class MainForm : Window #region Gui - private void MainForm_Closing(object? sender, WindowClosingEventArgs e) - { - AppConfig.Instance.LastMinecraftProfilePath = TextBoxMinecraftProfileFolder.Text?.Trim(); - } - private async void MainForm_Loaded(object? sender, RoutedEventArgs e) { var updates = new AppUpdates(Program.UpdateUrl, this); - updates.OnDownloadProgramUpdate += (o, _) => SetStatus(GeneralLangRes.DownloadProgramUpdate, AppGlobals.Symbols.GetImageSource(AppSymbols.software_installer)); + updates.OnDownloadProgramUpdate += (_, _) => SetStatus(GeneralLangRes.DownloadProgramUpdate, AppGlobals.Symbols.GetImageSource(AppSymbols.software_installer)); await updates.UpdateApp(); ClearStatus(); - + LoadRecentFilesToUi(); CheckStatusAndUpdate(true); } @@ -332,5 +369,11 @@ public partial class MainForm : Window await ExecuteUpdate(true, true); } + private void MenuItemRecentMinecraftProfilePathItem_Click(object? sender, RoutedEventArgs e) + { + if (sender is MenuItem item && item.DataContext is string path) + TextBoxMinecraftProfileFolder.Text = path; + } + #endregion } \ No newline at end of file diff --git a/ModpackUpdater.Apps.Client.Gui/Program.cs b/ModpackUpdater.Apps.Client.Gui/Program.cs index b013a09..aeb49ca 100644 --- a/ModpackUpdater.Apps.Client.Gui/Program.cs +++ b/ModpackUpdater.Apps.Client.Gui/Program.cs @@ -18,7 +18,6 @@ public static class Program static Program() { settingsManager = new(GetSettingsPath(2), true); - MigrateLegacySettings(GetSettingsPath(null)); } [STAThread] @@ -47,25 +46,4 @@ public static class Program return settingsPath; } - - private static void MigrateLegacySettings(string settingsPath) - { - // Try load legacy config file - if (!File.Exists(settingsPath) || JsonConvert.DeserializeObject(File.ReadAllText(settingsPath)) is not AppConfig legacyConfig) - return; - - // Migrate - var newConfig = Settings.Get(); - newConfig.LastMinecraftProfilePath = legacyConfig.LastMinecraftProfilePath; - - if (ModpackInfo.TryLoad(legacyConfig.LastMinecraftProfilePath) is ModpackInfo info) -#pragma warning disable CS0612 // Typ oder Element ist veraltet - info.ConfigUrl = legacyConfig.ConfigFilePath; - - // Ensure save settings - settingsManager.Save(); - - // Delete legacy config file - File.Delete(settingsPath); - } } diff --git a/ModpackUpdater.Apps.Manager/Ui/MainWindow.axaml.cs b/ModpackUpdater.Apps.Manager/Ui/MainWindow.axaml.cs index 5f75000..2733108 100644 --- a/ModpackUpdater.Apps.Manager/Ui/MainWindow.axaml.cs +++ b/ModpackUpdater.Apps.Manager/Ui/MainWindow.axaml.cs @@ -11,6 +11,7 @@ using ModpackUpdater.Apps.Manager.Api.Plugins.Params; using ModpackUpdater.Apps.Manager.Settings; using ModpackUpdater.Apps.Manager.Ui.Models.MainWindow; using Pilz.Extensions; +using Pilz.Extensions.Collections; using Pilz.Features; using Pilz.UI.AvaloniaUI.Features; using Pilz.UI.Symbols; @@ -119,12 +120,9 @@ public partial class MainWindow : Window, IMainApi private static void AddToRecentFiles(IWorkspace workspace) { var settings = Program.Settings.Get(); - settings.Workspaces.Remove(workspace.Config); settings.Workspaces.Insert(0, workspace.Config); - - while (settings.Workspaces.Count > 20) - settings.Workspaces.RemoveAt(20); + settings.Workspaces.Skip(20).ForEach(n => settings.Workspaces.Remove(n)); } private async void Window_OnLoaded(object? sender, RoutedEventArgs e) @@ -133,7 +131,7 @@ public partial class MainWindow : Window, IMainApi { UsePopups = true, }; - updater.OnDownloadProgramUpdate += (o, _) => Model.Progress.Start(); + updater.OnDownloadProgramUpdate += (_, _) => Model.Progress.Start(); await updater.UpdateApp(); Model.Progress.Stop();