ui(client): recent profile selector

This commit is contained in:
Pascal
2025-11-20 06:49:21 +01:00
parent c0f013a6c5
commit e7aa79db64
7 changed files with 174 additions and 68 deletions

View File

@@ -7,19 +7,22 @@ public class AppConfig : ISettingsNode, ISettingsIdentifier
{ {
public static string Identifier => "pilz.appconfig"; public static string Identifier => "pilz.appconfig";
public string? LastMinecraftProfilePath { get; set; } public List<string> RecentMinecraftProfilePaths { get; } = [];
[JsonIgnore, Obsolete] [JsonProperty, Obsolete]
public string? ConfigFilePath { get; private set; } private string? LastMinecraftProfilePath
[JsonProperty("ConfigFilePath"), Obsolete]
private string ConfigFilePathLegacy
{ {
set => ConfigFilePath = value; get => RecentMinecraftProfilePaths.FirstOrDefault();
set
{
if (!string.IsNullOrWhiteSpace(value))
RecentMinecraftProfilePaths.Insert(0, value);
}
} }
public void Reset() public void Reset()
{ {
LastMinecraftProfilePath = null; RecentMinecraftProfilePaths.Clear();
} }
public static AppConfig Instance => Program.Settings.Get<AppConfig>(); public static AppConfig Instance => Program.Settings.Get<AppConfig>();

View File

@@ -11,32 +11,46 @@ namespace ModpackUpdater.Apps.Client.Gui.LangRes {
using System; using System;
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] /// <summary>
[System.Diagnostics.DebuggerNonUserCodeAttribute()] /// A strongly-typed resource class, for looking up localized strings, etc.
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()] /// </summary>
// 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 { 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() { internal GeneralLangRes() {
} }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] /// <summary>
public static System.Resources.ResourceManager ResourceManager { /// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Resources.ResourceManager ResourceManager {
get { get {
if (object.Equals(null, resourceMan)) { if (object.ReferenceEquals(resourceMan, null)) {
System.Resources.ResourceManager temp = new System.Resources.ResourceManager("ModpackUpdater.Apps.Client.Gui.LangRes.GeneralLangRes", typeof(GeneralLangRes).Assembly); global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ModpackUpdater.Apps.Client.Gui.LangRes.GeneralLangRes", typeof(GeneralLangRes).Assembly);
resourceMan = temp; resourceMan = temp;
} }
return resourceMan; return resourceMan;
} }
} }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] /// <summary>
public static System.Globalization.CultureInfo Culture { /// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Globalization.CultureInfo Culture {
get { get {
return resourceCulture; return resourceCulture;
} }
@@ -45,118 +59,184 @@ namespace ModpackUpdater.Apps.Client.Gui.LangRes {
} }
} }
/// <summary>
/// Looks up a localized string similar to An update is available!.
/// </summary>
public static string AnUpdateIsAvailable { public static string AnUpdateIsAvailable {
get { get {
return ResourceManager.GetString("AnUpdateIsAvailable", resourceCulture); return ResourceManager.GetString("AnUpdateIsAvailable", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Check for updates.
/// </summary>
public static string CheckForUpdates { public static string CheckForUpdates {
get { get {
return ResourceManager.GetString("CheckForUpdates", resourceCulture); return ResourceManager.GetString("CheckForUpdates", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Checking for Updates....
/// </summary>
public static string CheckingForUpdates { public static string CheckingForUpdates {
get { get {
return ResourceManager.GetString("CheckingForUpdates", resourceCulture); return ResourceManager.GetString("CheckingForUpdates", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Config incomplete or not loaded!.
/// </summary>
public static string ConfigIncompleteOrNotLoaded { public static string ConfigIncompleteOrNotLoaded {
get { get {
return ResourceManager.GetString("ConfigIncompleteOrNotLoaded", resourceCulture); return ResourceManager.GetString("ConfigIncompleteOrNotLoaded", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Downloading program update....
/// </summary>
public static string DownloadProgramUpdate { public static string DownloadProgramUpdate {
get { get {
return ResourceManager.GetString("DownloadProgramUpdate", resourceCulture); return ResourceManager.GetString("DownloadProgramUpdate", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Error on update check or while updating!.
/// </summary>
public static string ErrorOnUpdateCheckOrUpdating { public static string ErrorOnUpdateCheckOrUpdating {
get { get {
return ResourceManager.GetString("ErrorOnUpdateCheckOrUpdating", resourceCulture); return ResourceManager.GetString("ErrorOnUpdateCheckOrUpdating", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Everything is right and up-to-date..
/// </summary>
public static string EverythingIsRightAndUpToDate { public static string EverythingIsRightAndUpToDate {
get { get {
return ResourceManager.GetString("EverythingIsRightAndUpToDate", resourceCulture); return ResourceManager.GetString("EverythingIsRightAndUpToDate", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Install.
/// </summary>
public static string Install { public static string Install {
get { get {
return ResourceManager.GetString("Install", resourceCulture); return ResourceManager.GetString("Install", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Installation key.
/// </summary>
public static string InstallationKey { public static string InstallationKey {
get { get {
return ResourceManager.GetString("InstallationKey", resourceCulture); return ResourceManager.GetString("InstallationKey", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Installation key seems to be invalid.
/// </summary>
public static string InstallationKeyNotValid {
get {
return ResourceManager.GetString("InstallationKeyNotValid", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Installing....
/// </summary>
public static string Installing { public static string Installing {
get { get {
return ResourceManager.GetString("Installing", resourceCulture); return ResourceManager.GetString("Installing", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Minecraft profile.
/// </summary>
public static string MinecraftProfile { public static string MinecraftProfile {
get { get {
return ResourceManager.GetString("MinecraftProfile", resourceCulture); return ResourceManager.GetString("MinecraftProfile", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Minecraft profile folder seems to be not valid..
/// </summary>
public static string MinecraftProfileFolderSeemsInvalid { public static string MinecraftProfileFolderSeemsInvalid {
get { get {
return ResourceManager.GetString("MinecraftProfileFolderSeemsInvalid", resourceCulture); return ResourceManager.GetString("MinecraftProfileFolderSeemsInvalid", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Modpack config url.
/// </summary>
public static string ModpackConfigUrl { public static string ModpackConfigUrl {
get { get {
return ResourceManager.GetString("ModpackConfigUrl", resourceCulture); return ResourceManager.GetString("ModpackConfigUrl", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to No recent profiles available..
/// </summary>
public static string NoRecentProfilesAvailable {
get {
return ResourceManager.GetString("NoRecentProfilesAvailable", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Repair.
/// </summary>
public static string Repair { public static string Repair {
get { get {
return ResourceManager.GetString("Repair", resourceCulture); return ResourceManager.GetString("Repair", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Select.
/// </summary>
public static string Select { public static string Select {
get { get {
return ResourceManager.GetString("Select", resourceCulture); return ResourceManager.GetString("Select", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Select the minecraft profile folder (usually named .minecraft).
/// </summary>
public static string SelectMinecraftProfileFolder { public static string SelectMinecraftProfileFolder {
get { get {
return ResourceManager.GetString("SelectMinecraftProfileFolder", resourceCulture); return ResourceManager.GetString("SelectMinecraftProfileFolder", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Status.
/// </summary>
public static string Status { public static string Status {
get { get {
return ResourceManager.GetString("Status", resourceCulture); return ResourceManager.GetString("Status", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to The update servers are in maintenance..
/// </summary>
public static string UpdateServerInMaintenance { public static string UpdateServerInMaintenance {
get { get {
return ResourceManager.GetString("UpdateServerInMaintenance", resourceCulture); return ResourceManager.GetString("UpdateServerInMaintenance", resourceCulture);
} }
} }
public static string InstallationKeyNotValid {
get {
return ResourceManager.GetString("InstallationKeyNotValid", resourceCulture);
}
}
} }
} }

View File

@@ -174,4 +174,7 @@
<data name="InstallationKeyNotValid" xml:space="preserve"> <data name="InstallationKeyNotValid" xml:space="preserve">
<value>Installation key seems to be invalid</value> <value>Installation key seems to be invalid</value>
</data> </data>
<data name="NoRecentProfilesAvailable" xml:space="preserve">
<value>No recent profiles available.</value>
</data>
</root> </root>

View File

@@ -12,7 +12,8 @@
WindowStartupLocation="CenterScreen" WindowStartupLocation="CenterScreen"
CanMaximize="false" CanMaximize="false"
Title="Minecraft Modpack Updater" Title="Minecraft Modpack Updater"
Icon="/Assets/app.ico"> Icon="/Assets/app.ico"
Loaded="MainForm_Loaded">
<Grid <Grid
RowDefinitions="Auto,Auto,Auto,Auto,Auto" RowDefinitions="Auto,Auto,Auto,Auto,Auto"
@@ -71,7 +72,7 @@
IsVisible="false"/> IsVisible="false"/>
<!-- Button: SearchProfileFolder --> <!-- Button: SearchProfileFolder -->
<pilz:ImageButton <pilz:ImageSplitButton
x:Name="ButtonSearchProfileFolder" x:Name="ButtonSearchProfileFolder"
Grid.Row="0" Grid.Row="0"
Grid.Column="2" Grid.Column="2"

View File

@@ -8,12 +8,14 @@ using Avalonia.Threading;
using ModpackUpdater.Apps.Client.Gui.LangRes; using ModpackUpdater.Apps.Client.Gui.LangRes;
using ModpackUpdater.Manager; using ModpackUpdater.Manager;
using Pilz.Extensions; using Pilz.Extensions;
using Pilz.Extensions.Collections;
using Pilz.UI.Symbols; using Pilz.UI.Symbols;
namespace ModpackUpdater.Apps.Client.Gui; namespace ModpackUpdater.Apps.Client.Gui;
public partial class MainForm : Window public partial class MainForm : Window
{ {
private readonly MenuFlyout menuFlyoutSearchProfileFolder = new();
private readonly UpdateCheckOptions updateOptions = new(); private readonly UpdateCheckOptions updateOptions = new();
private ModpackInfo modpackInfo = new(); private ModpackInfo modpackInfo = new();
private ModpackConfig updateConfig = new(); private ModpackConfig updateConfig = new();
@@ -21,16 +23,13 @@ public partial class MainForm : Window
private UpdateCheckResult? lastUpdateCheckResult; private UpdateCheckResult? lastUpdateCheckResult;
private bool currentUpdating; private bool currentUpdating;
private bool loadingData; private bool loadingData;
private int curOptionsRow = 3;
public MainForm() public MainForm()
{ {
InitializeComponent(); InitializeComponent();
Title = $"{Title} (v{Assembly.GetExecutingAssembly().GetAppVersion().ToShortString()})"; Title = $"{Title} (v{Assembly.GetExecutingAssembly().GetAppVersion().ToShortString()})";
ButtonSearchProfileFolder.Flyout = menuFlyoutSearchProfileFolder;
Closing += MainForm_Closing;
Loaded += MainForm_Loaded;
ButtonSearchProfileFolder.ImageSource = AppGlobals.Symbols.GetImageSource(AppSymbols.opened_folder); ButtonSearchProfileFolder.ImageSource = AppGlobals.Symbols.GetImageSource(AppSymbols.opened_folder);
ButtonCheckForUpdates.ImageSource = AppGlobals.Symbols.GetImageSource(AppSymbols.update_done); ButtonCheckForUpdates.ImageSource = AppGlobals.Symbols.GetImageSource(AppSymbols.update_done);
@@ -54,12 +53,50 @@ public partial class MainForm : Window
TextStatus.Text = "-"; TextStatus.Text = "-";
ImageStatus.Source = null; ImageStatus.Source = null;
} }
private void LoadRecentFilesToUi()
{
menuFlyoutSearchProfileFolder.Items.Clear();
if (AppConfig.Instance.RecentMinecraftProfilePaths.Count == 0)
{
menuFlyoutSearchProfileFolder.Items.Add(new TextBlock
{
Text = GeneralLangRes.NoRecentProfilesAvailable,
FontStyle = FontStyle.Italic,
});
return;
}
AppConfig.Instance.RecentMinecraftProfilePaths.ForEach(path =>
{
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() private void LoadProfileToUi()
{ {
loadingData = true; 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; TextBoxModpackConfig.Text = modpackInfo.ConfigUrl ?? TextBoxModpackConfig.Text;
TextBoxInstallKey.Text = modpackInfo.ExtrasKey ?? TextBoxInstallKey.Text; TextBoxInstallKey.Text = modpackInfo.ExtrasKey ?? TextBoxInstallKey.Text;
@@ -76,8 +113,13 @@ public partial class MainForm : Window
private async void CheckStatusAndUpdate(bool loadProfileToUi) private async void CheckStatusAndUpdate(bool loadProfileToUi)
{ {
if (CheckStatus(loadProfileToUi)) if (!CheckStatus(loadProfileToUi))
await ExecuteUpdate(false, false); return;
await ExecuteUpdate(false, false);
StoreRecentMinecraftProfilePath(modpackInfo.LocalPath);
LoadRecentFilesToUi();
} }
private bool CheckStatus(bool loadProfileToUi) private bool CheckStatus(bool loadProfileToUi)
@@ -262,18 +304,13 @@ public partial class MainForm : Window
#region Gui #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) private async void MainForm_Loaded(object? sender, RoutedEventArgs e)
{ {
var updates = new AppUpdates(Program.UpdateUrl, this); 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(); await updates.UpdateApp();
ClearStatus(); ClearStatus();
LoadRecentFilesToUi();
CheckStatusAndUpdate(true); CheckStatusAndUpdate(true);
} }
@@ -332,5 +369,11 @@ public partial class MainForm : Window
await ExecuteUpdate(true, true); 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 #endregion
} }

View File

@@ -18,7 +18,6 @@ public static class Program
static Program() static Program()
{ {
settingsManager = new(GetSettingsPath(2), true); settingsManager = new(GetSettingsPath(2), true);
MigrateLegacySettings(GetSettingsPath(null));
} }
[STAThread] [STAThread]
@@ -47,25 +46,4 @@ public static class Program
return settingsPath; return settingsPath;
} }
private static void MigrateLegacySettings(string settingsPath)
{
// Try load legacy config file
if (!File.Exists(settingsPath) || JsonConvert.DeserializeObject<AppConfig>(File.ReadAllText(settingsPath)) is not AppConfig legacyConfig)
return;
// Migrate
var newConfig = Settings.Get<AppConfig>();
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);
}
} }

View File

@@ -11,6 +11,7 @@ using ModpackUpdater.Apps.Manager.Api.Plugins.Params;
using ModpackUpdater.Apps.Manager.Settings; using ModpackUpdater.Apps.Manager.Settings;
using ModpackUpdater.Apps.Manager.Ui.Models.MainWindow; using ModpackUpdater.Apps.Manager.Ui.Models.MainWindow;
using Pilz.Extensions; using Pilz.Extensions;
using Pilz.Extensions.Collections;
using Pilz.Features; using Pilz.Features;
using Pilz.UI.AvaloniaUI.Features; using Pilz.UI.AvaloniaUI.Features;
using Pilz.UI.Symbols; using Pilz.UI.Symbols;
@@ -119,12 +120,9 @@ public partial class MainWindow : Window, IMainApi
private static void AddToRecentFiles(IWorkspace workspace) private static void AddToRecentFiles(IWorkspace workspace)
{ {
var settings = Program.Settings.Get<WorkspaceSettings>(); var settings = Program.Settings.Get<WorkspaceSettings>();
settings.Workspaces.Remove(workspace.Config); settings.Workspaces.Remove(workspace.Config);
settings.Workspaces.Insert(0, workspace.Config); settings.Workspaces.Insert(0, workspace.Config);
settings.Workspaces.Skip(20).ForEach(n => settings.Workspaces.Remove(n));
while (settings.Workspaces.Count > 20)
settings.Workspaces.RemoveAt(20);
} }
private async void Window_OnLoaded(object? sender, RoutedEventArgs e) private async void Window_OnLoaded(object? sender, RoutedEventArgs e)
@@ -133,7 +131,7 @@ public partial class MainWindow : Window, IMainApi
{ {
UsePopups = true, UsePopups = true,
}; };
updater.OnDownloadProgramUpdate += (o, _) => Model.Progress.Start(); updater.OnDownloadProgramUpdate += (_, _) => Model.Progress.Start();
await updater.UpdateApp(); await updater.UpdateApp();
Model.Progress.Stop(); Model.Progress.Stop();