ui: migrate client back to WinForms

-> let's use wine for comatibility again
-> easier to maintain
-> easier to build dynamic options with
This commit is contained in:
2025-06-29 20:03:02 +02:00
parent 6e8b4f0a9d
commit 433613e43d
26 changed files with 2592 additions and 704 deletions

View File

@@ -1,17 +0,0 @@
using Pilz.UI.Gtk.Symbols;
using System.Reflection;
namespace ModpackUpdater.Apps.Client;
internal class AppSymbolFactory : GtkSymbolFactory<AppSymbols>
{
public override Assembly GetImageResourceAssembly()
{
return typeof(AppSymbols).Assembly;
}
public override string GetImageRessourcePath(AppSymbols svgImage)
{
return $"{typeof(AppSymbols).Namespace}.Symbols.{svgImage}.svg";
}
}

View File

@@ -0,0 +1,286 @@
using System;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
namespace ModpackUpdater.Apps.Client
{
[Microsoft.VisualBasic.CompilerServices.DesignerGenerated()]
public partial class MainForm : Telerik.WinControls.UI.RadForm
{
// Das Formular überschreibt den Löschvorgang, um die Komponentenliste zu bereinigen.
[DebuggerNonUserCode()]
protected override void Dispose(bool disposing)
{
try
{
if (disposing && components is not null)
components.Dispose();
}
finally
{
base.Dispose(disposing);
}
}
// Wird vom Windows Form-Designer benötigt.
private System.ComponentModel.IContainer components = new System.ComponentModel.Container();
// Hinweis: Die folgende Prozedur ist für den Windows Form-Designer erforderlich.
// Das Bearbeiten ist mit dem Windows Form-Designer möglich.
// Das Bearbeiten mit dem Code-Editor ist nicht möglich.
[DebuggerStepThrough()]
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
RadLabel1 = new Telerik.WinControls.UI.RadLabel();
RadLabel2 = new Telerik.WinControls.UI.RadLabel();
RadTextBox_MinecraftProfileFolder = new Telerik.WinControls.UI.RadTextBoxControl();
RadTextBox_ModpackConfig = new Telerik.WinControls.UI.RadTextBoxControl();
RadButton_Install = new Telerik.WinControls.UI.RadSplitButton();
radMenuItem_Install = new Telerik.WinControls.UI.RadMenuItem();
radMenuItem_Repair = new Telerik.WinControls.UI.RadMenuItem();
RadButton_CheckForUpdates = new Telerik.WinControls.UI.RadButton();
RadButton_SearchMinecraftProfileFolder = new Telerik.WinControls.UI.RadButton();
tableLayoutPanel1 = new TableLayoutPanel();
RadLabel_Status = new Telerik.WinControls.UI.RadLabel();
RadLabel3 = new Telerik.WinControls.UI.RadLabel();
radLabel4 = new Telerik.WinControls.UI.RadLabel();
radTextBox_InstallKey = new Telerik.WinControls.UI.RadTextBoxControl();
((System.ComponentModel.ISupportInitialize)RadLabel1).BeginInit();
((System.ComponentModel.ISupportInitialize)RadLabel2).BeginInit();
((System.ComponentModel.ISupportInitialize)RadTextBox_MinecraftProfileFolder).BeginInit();
((System.ComponentModel.ISupportInitialize)RadTextBox_ModpackConfig).BeginInit();
((System.ComponentModel.ISupportInitialize)RadButton_Install).BeginInit();
((System.ComponentModel.ISupportInitialize)RadButton_CheckForUpdates).BeginInit();
((System.ComponentModel.ISupportInitialize)RadButton_SearchMinecraftProfileFolder).BeginInit();
tableLayoutPanel1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)RadLabel_Status).BeginInit();
((System.ComponentModel.ISupportInitialize)RadLabel3).BeginInit();
((System.ComponentModel.ISupportInitialize)radLabel4).BeginInit();
((System.ComponentModel.ISupportInitialize)radTextBox_InstallKey).BeginInit();
((System.ComponentModel.ISupportInitialize)this).BeginInit();
SuspendLayout();
//
// RadLabel1
//
RadLabel1.Anchor = AnchorStyles.Left;
RadLabel1.Location = new Point(3, 4);
RadLabel1.Name = "RadLabel1";
RadLabel1.Size = new Size(113, 22);
RadLabel1.TabIndex = 0;
RadLabel1.Text = "Minecraft profile";
//
// RadLabel2
//
RadLabel2.Anchor = AnchorStyles.Left;
RadLabel2.Location = new Point(3, 34);
RadLabel2.Name = "RadLabel2";
RadLabel2.Size = new Size(96, 22);
RadLabel2.TabIndex = 1;
RadLabel2.Text = "Modpack URL";
//
// RadTextBox_MinecraftProfileFolder
//
RadTextBox_MinecraftProfileFolder.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
tableLayoutPanel1.SetColumnSpan(RadTextBox_MinecraftProfileFolder, 3);
RadTextBox_MinecraftProfileFolder.Location = new Point(122, 3);
RadTextBox_MinecraftProfileFolder.Name = "RadTextBox_MinecraftProfileFolder";
RadTextBox_MinecraftProfileFolder.NullText = "C:\\Users\\...\\AppData\\...";
RadTextBox_MinecraftProfileFolder.Size = new Size(197, 24);
RadTextBox_MinecraftProfileFolder.TabIndex = 1;
RadTextBox_MinecraftProfileFolder.TextChanged += RadTextBox_MinecraftFolder_TextInserted;
//
// RadTextBox_ModpackConfig
//
RadTextBox_ModpackConfig.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
tableLayoutPanel1.SetColumnSpan(RadTextBox_ModpackConfig, 5);
RadTextBox_ModpackConfig.Location = new Point(122, 33);
RadTextBox_ModpackConfig.Name = "RadTextBox_ModpackConfig";
RadTextBox_ModpackConfig.NullText = "https://...";
RadTextBox_ModpackConfig.Size = new Size(297, 24);
RadTextBox_ModpackConfig.TabIndex = 3;
RadTextBox_ModpackConfig.TextChanged += RadTextBox_ModpackUrl_TextInserted;
//
// RadButton_Install
//
RadButton_Install.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
tableLayoutPanel1.SetColumnSpan(RadButton_Install, 2);
RadButton_Install.ImageAlignment = ContentAlignment.MiddleRight;
RadButton_Install.Items.AddRange(new Telerik.WinControls.RadItem[] { radMenuItem_Install, radMenuItem_Repair });
RadButton_Install.Location = new Point(325, 121);
RadButton_Install.Name = "RadButton_Install";
RadButton_Install.Size = new Size(94, 24);
RadButton_Install.TabIndex = 0;
RadButton_Install.Text = "Install";
RadButton_Install.TextAlignment = ContentAlignment.MiddleLeft;
RadButton_Install.TextImageRelation = TextImageRelation.ImageBeforeText;
//
// radMenuItem_Install
//
radMenuItem_Install.Name = "radMenuItem_Install";
radMenuItem_Install.Text = "Install";
radMenuItem_Install.Click += RadMenuItem_Install_Click;
//
// radMenuItem_Repair
//
radMenuItem_Repair.Name = "radMenuItem_Repair";
radMenuItem_Repair.Text = "Repair";
radMenuItem_Repair.Click += RadMenuItem_Repair_Click;
//
// RadButton_CheckForUpdates
//
RadButton_CheckForUpdates.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
tableLayoutPanel1.SetColumnSpan(RadButton_CheckForUpdates, 3);
RadButton_CheckForUpdates.ImageAlignment = ContentAlignment.MiddleRight;
RadButton_CheckForUpdates.Location = new Point(122, 121);
RadButton_CheckForUpdates.Name = "RadButton_CheckForUpdates";
RadButton_CheckForUpdates.Size = new Size(197, 24);
RadButton_CheckForUpdates.TabIndex = 100;
RadButton_CheckForUpdates.Text = "Check for Updates";
RadButton_CheckForUpdates.TextAlignment = ContentAlignment.MiddleLeft;
RadButton_CheckForUpdates.TextImageRelation = TextImageRelation.ImageBeforeText;
RadButton_CheckForUpdates.Click += ButtonX_CheckForUpdates_Click;
//
// RadButton_SearchMinecraftProfileFolder
//
RadButton_SearchMinecraftProfileFolder.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
tableLayoutPanel1.SetColumnSpan(RadButton_SearchMinecraftProfileFolder, 2);
RadButton_SearchMinecraftProfileFolder.ImageAlignment = ContentAlignment.MiddleRight;
RadButton_SearchMinecraftProfileFolder.Location = new Point(325, 3);
RadButton_SearchMinecraftProfileFolder.Name = "RadButton_SearchMinecraftProfileFolder";
RadButton_SearchMinecraftProfileFolder.Size = new Size(94, 24);
RadButton_SearchMinecraftProfileFolder.TabIndex = 2;
RadButton_SearchMinecraftProfileFolder.Text = "Search";
RadButton_SearchMinecraftProfileFolder.TextAlignment = ContentAlignment.MiddleLeft;
RadButton_SearchMinecraftProfileFolder.TextImageRelation = TextImageRelation.ImageBeforeText;
RadButton_SearchMinecraftProfileFolder.Click += ButtonX_SearchMinecraftProfile_Click;
//
// tableLayoutPanel1
//
tableLayoutPanel1.AutoSize = true;
tableLayoutPanel1.AutoSizeMode = AutoSizeMode.GrowAndShrink;
tableLayoutPanel1.ColumnCount = 6;
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle());
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 50F));
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 50F));
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 50F));
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 50F));
tableLayoutPanel1.Controls.Add(RadLabel1, 0, 0);
tableLayoutPanel1.Controls.Add(RadLabel2, 0, 1);
tableLayoutPanel1.Controls.Add(RadTextBox_MinecraftProfileFolder, 1, 0);
tableLayoutPanel1.Controls.Add(RadTextBox_ModpackConfig, 1, 1);
tableLayoutPanel1.Controls.Add(RadLabel_Status, 1, 3);
tableLayoutPanel1.Controls.Add(RadLabel3, 0, 3);
tableLayoutPanel1.Controls.Add(RadButton_Install, 4, 4);
tableLayoutPanel1.Controls.Add(radLabel4, 0, 2);
tableLayoutPanel1.Controls.Add(radTextBox_InstallKey, 1, 2);
tableLayoutPanel1.Controls.Add(RadButton_CheckForUpdates, 1, 4);
tableLayoutPanel1.Controls.Add(RadButton_SearchMinecraftProfileFolder, 4, 0);
tableLayoutPanel1.Dock = DockStyle.Fill;
tableLayoutPanel1.Location = new Point(0, 0);
tableLayoutPanel1.Name = "tableLayoutPanel1";
tableLayoutPanel1.RowCount = 5;
tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.Size = new Size(422, 173);
tableLayoutPanel1.TabIndex = 7;
//
// RadLabel_Status
//
RadLabel_Status.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
tableLayoutPanel1.SetColumnSpan(RadLabel_Status, 5);
RadLabel_Status.Location = new Point(122, 93);
RadLabel_Status.Name = "RadLabel_Status";
RadLabel_Status.Size = new Size(13, 22);
RadLabel_Status.TabIndex = 3;
RadLabel_Status.Text = "-";
RadLabel_Status.TextImageRelation = TextImageRelation.ImageBeforeText;
//
// RadLabel3
//
RadLabel3.Anchor = AnchorStyles.Left;
RadLabel3.Location = new Point(3, 93);
RadLabel3.Name = "RadLabel3";
RadLabel3.Size = new Size(46, 22);
RadLabel3.TabIndex = 2;
RadLabel3.Text = "Status";
//
// radLabel4
//
radLabel4.Anchor = AnchorStyles.Left;
radLabel4.Location = new Point(3, 64);
radLabel4.Name = "radLabel4";
radLabel4.Size = new Size(102, 22);
radLabel4.TabIndex = 12;
radLabel4.Text = "Installation key";
//
// radTextBox_InstallKey
//
radTextBox_InstallKey.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
tableLayoutPanel1.SetColumnSpan(radTextBox_InstallKey, 5);
radTextBox_InstallKey.Location = new Point(122, 63);
radTextBox_InstallKey.Name = "radTextBox_InstallKey";
radTextBox_InstallKey.NullText = "AAAAA-BBBBB-CCCCC-DDDDD-EEEEE";
radTextBox_InstallKey.Size = new Size(297, 24);
radTextBox_InstallKey.TabIndex = 4;
radTextBox_InstallKey.TextChanged += RadTextBox_InstallationKey_TextInserted;
//
// MainForm
//
AutoScaleBaseSize = new Size(7, 15);
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
AutoSize = true;
AutoSizeMode = AutoSizeMode.GrowAndShrink;
ClientSize = new Size(422, 173);
Controls.Add(tableLayoutPanel1);
Icon = (Icon)resources.GetObject("$this.Icon");
MaximizeBox = false;
MinimumSize = new Size(430, 0);
Name = "MainForm";
StartPosition = FormStartPosition.CenterScreen;
Text = "Minecraft Modpack Updater";
FormClosing += Form1_FormClosing;
Load += Form1_Load;
Shown += Form1_Shown;
((System.ComponentModel.ISupportInitialize)RadLabel1).EndInit();
((System.ComponentModel.ISupportInitialize)RadLabel2).EndInit();
((System.ComponentModel.ISupportInitialize)RadTextBox_MinecraftProfileFolder).EndInit();
((System.ComponentModel.ISupportInitialize)RadTextBox_ModpackConfig).EndInit();
((System.ComponentModel.ISupportInitialize)RadButton_Install).EndInit();
((System.ComponentModel.ISupportInitialize)RadButton_CheckForUpdates).EndInit();
((System.ComponentModel.ISupportInitialize)RadButton_SearchMinecraftProfileFolder).EndInit();
tableLayoutPanel1.ResumeLayout(false);
tableLayoutPanel1.PerformLayout();
((System.ComponentModel.ISupportInitialize)RadLabel_Status).EndInit();
((System.ComponentModel.ISupportInitialize)RadLabel3).EndInit();
((System.ComponentModel.ISupportInitialize)radLabel4).EndInit();
((System.ComponentModel.ISupportInitialize)radTextBox_InstallKey).EndInit();
((System.ComponentModel.ISupportInitialize)this).EndInit();
ResumeLayout(false);
PerformLayout();
}
internal Telerik.WinControls.UI.RadLabel RadLabel1;
internal Telerik.WinControls.UI.RadLabel RadLabel2;
internal Telerik.WinControls.UI.RadTextBoxControl RadTextBox_MinecraftProfileFolder;
internal Telerik.WinControls.UI.RadTextBoxControl RadTextBox_ModpackConfig;
internal Telerik.WinControls.UI.RadSplitButton RadButton_Install;
internal Telerik.WinControls.UI.RadButton RadButton_CheckForUpdates;
internal Telerik.WinControls.UI.RadButton RadButton_SearchMinecraftProfileFolder;
private TableLayoutPanel tableLayoutPanel1;
internal Telerik.WinControls.UI.RadLabel radLabel4;
internal Telerik.WinControls.UI.RadTextBoxControl radTextBox_InstallKey;
private Telerik.WinControls.UI.RadMenuItem radMenuItem_Repair;
private Telerik.WinControls.UI.RadMenuItem radMenuItem_Install;
internal Telerik.WinControls.UI.RadLabel RadLabel_Status;
internal Telerik.WinControls.UI.RadLabel RadLabel3;
}
}

View File

@@ -0,0 +1,321 @@
using ModpackUpdater.Manager;
using ModpackUpdater.My.Resources;
using Pilz.Extensions;
using Pilz.UI.Symbols;
using Pilz.UI.WinForms.Extensions;
using System.Diagnostics;
using System.Reflection;
using Telerik.WinControls;
using Telerik.WinControls.UI;
namespace ModpackUpdater.Apps.Client;
public partial class MainForm
{
private readonly UpdateCheckOptionsAdv updateOptions;
private ModpackInfo modpackInfo = new();
private ModpackConfig updateConfig = new();
private ModpackFeatures features;
private UpdateCheckResult lastUpdateCheckResult;
private bool currentUpdating;
private bool loadingData;
public MainForm() : this(new())
{
}
public MainForm(UpdateCheckOptionsAdv updateOptions)
{
this.updateOptions = updateOptions;
InitializeComponent();
Text = $"{Text} (v{Assembly.GetExecutingAssembly().GetAppVersion()})";
RadButton_Install.DefaultItem = radMenuItem_Install;
RadButton_CheckForUpdates.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.update_done, SymbolSize.Small);
RadButton_SearchMinecraftProfileFolder.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.opened_folder, SymbolSize.Small);
RadButton_Install.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.software_installer, SymbolSize.Small);
radMenuItem_Install.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.software_installer, SymbolSize.Small);
radMenuItem_Repair.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.wrench, SymbolSize.Small);
LoadProfileToUi();
}
#region Features
private void SetStatus(string statusText, RadSvgImage image)
{
RadLabel_Status.Text = statusText;
RadLabel_Status.SvgImage = image;
}
private void ClearStatus()
{
RadLabel_Status.Text = "-";
RadLabel_Status.SvgImage = null;
}
private void LoadProfileToUi()
{
loadingData = true;
RadTextBox_MinecraftProfileFolder.Text = modpackInfo?.LocaLPath ?? updateOptions.ProfileFolder ?? AppConfig.Instance.LastMinecraftProfilePath ?? RadTextBox_MinecraftProfileFolder.Text;
RadTextBox_ModpackConfig.Text = modpackInfo?.ConfigUrl ?? updateOptions.ModpackConfig ?? RadTextBox_ModpackConfig.Text;
radTextBox_InstallKey.Text = modpackInfo?.ExtrasKey ?? updateOptions.ExtrasKey ?? radTextBox_InstallKey.Text;
loadingData = false;
}
private async void CheckStatusAndUpdate(bool loadProfileToUi)
{
if (CheckStatus(loadProfileToUi))
await ExecuteUpdate(false, false);
}
private bool CheckStatus(bool loadProfileToUi)
{
try
{
modpackInfo = ModpackInfo.TryLoad(RadTextBox_MinecraftProfileFolder.Text.Trim());
}
catch
{
}
if (loadProfileToUi)
LoadProfileToUi();
try
{
updateConfig = ModpackConfig.LoadFromUrl(RadTextBox_ModpackConfig.Text);
}
catch (Exception)
{
}
if (modpackInfo != null)
features = new(updateConfig);
if (modpackInfo == null || string.IsNullOrWhiteSpace(RadTextBox_MinecraftProfileFolder.Text) /*|| modpackInfo.Valid*/)
{
SetStatus(LangRes.StatusText_MinecraftProfileWarning, AppGlobals.Symbols.GetSvgImage(AppSymbols.general_warning_sign, SymbolSize.Small));
RadButton_CheckForUpdates.Enabled = false;
RadButton_Install.Enabled = false;
return false;
}
else if (updateConfig == null || string.IsNullOrWhiteSpace(RadTextBox_ModpackConfig.Text))
{
SetStatus(LangRes.StatusText_ConfigIncompleteOrNotLoaded, AppGlobals.Symbols.GetSvgImage(AppSymbols.general_warning_sign, SymbolSize.Small));
RadButton_CheckForUpdates.Enabled = false;
RadButton_Install.Enabled = false;
return false;
}
else if (updateConfig.Maintenance && !updateOptions.IgnoreMaintenance)
{
SetStatus(LangRes.StatusText_Maintenance, AppGlobals.Symbols.GetSvgImage(AppSymbols.services, SymbolSize.Small));
RadButton_CheckForUpdates.Enabled = false;
RadButton_Install.Enabled = false;
return false;
}
RadButton_CheckForUpdates.Enabled = true;
RadButton_Install.Enabled = true;
return true;
}
private async Task ExecuteUpdate(bool doInstall, bool repair)
{
// Ensure set extras key
modpackInfo.ExtrasKey = radTextBox_InstallKey.Text.Trim();
var updater = new ModpackInstaller(updateConfig, modpackInfo)
{
OverwriteRefTag = Program.Options.RefTag,
OverwriteVersion = Program.Options.Version,
};
updater.InstallProgessUpdated += Update_InstallProgessUpdated;
updater.CheckingProgressUpdated += Updated_CheckingProgresssUpdated;
void error()
{
SetStatus(LangRes.StatusText_ErrorWhileUpdateCheckOrUpdate, AppGlobals.Symbols.GetSvgImage(AppSymbols.close, SymbolSize.Small));
currentUpdating = false;
}
void installing()
{
SetStatus(LangRes.StatusText_Installing, AppGlobals.Symbols.GetSvgImage(AppSymbols.software_installer, SymbolSize.Small));
currentUpdating = true;
}
void updatesAvailable()
{
SetStatus(LangRes.StatusText_UpdateAvailable, AppGlobals.Symbols.GetSvgImage(AppSymbols.software_installer, SymbolSize.Small));
}
void everythingOk()
{
SetStatus(LangRes.StatusTest_EverythingOk, AppGlobals.Symbols.GetSvgImage(AppSymbols.done, SymbolSize.Small));
currentUpdating = false;
}
// Check only if not pressed "install", not really needed otherwise.
if (lastUpdateCheckResult is null || !doInstall || repair)
{
SetStatus(LangRes.StatusText_CheckingForUpdates, AppGlobals.Symbols.GetSvgImage(AppSymbols.update_done, SymbolSize.Small));
// Check for extras once again
updateOptions.IncludeExtras = features.IsEnabled(ModpackFeatures.FeatureAllowExtas, new AllowExtrasFeatureContext(modpackInfo));
// Force re-install on repair
updateOptions.IgnoreInstalledVersion = repair;
try
{
lastUpdateCheckResult = await updater.Check(updateOptions);
}
catch (Exception)
{
error();
if (Debugger.IsAttached)
throw;
}
finally
{
}
}
// Error while update check
if (lastUpdateCheckResult is null || lastUpdateCheckResult.HasError)
{
error();
return;
}
// No updates available
if (!lastUpdateCheckResult.HasUpdates)
{
everythingOk();
return;
}
// Updates available (but don't install)
if (!doInstall)
{
updatesAvailable();
return;
}
// Install updates
installing();
currentUpdating = true;
try
{
// Install
if (await updater.Install(lastUpdateCheckResult) == false)
{
error();
return;
}
// Success
lastUpdateCheckResult = null; // Reset last update check, a new one would be needed now.
everythingOk();
}
catch (Exception)
{
// Error
error();
if (Debugger.IsAttached)
throw;
}
}
private void Updated_CheckingProgresssUpdated(int toCheck, int processed)
{
SetStatus(Math.Round(processed / (double)toCheck * 100d, 1) + "%", AppGlobals.Symbols.GetSvgImage(AppSymbols.update_done, SymbolSize.Small));
}
private void Update_InstallProgessUpdated(UpdateCheckResult result, int processedSyncs)
{
int actionCount = result.Actions.Count;
SetStatus(Math.Round(processedSyncs / (double)actionCount * 100d, 1) + "%", AppGlobals.Symbols.GetSvgImage(AppSymbols.software_installer, SymbolSize.Small));
}
#endregion
#region Gui
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
AppConfig.Instance.LastMinecraftProfilePath = RadTextBox_MinecraftProfileFolder.Text.Trim();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private async void Form1_Shown(object sender, EventArgs e)
{
var updater = new AppUpdater();
if (!updateOptions.NoUpdate && await updater.Check() && RadMessageBox.Show(LangRes.MsgBox_UpdateAvailable, LangRes.MsgBox_UpdateAvailable_Title, MessageBoxButtons.YesNo, RadMessageIcon.Info).IsYes())
{
SetStatus(LangRes.StatusText_InstallingAppUpdate, AppGlobals.Symbols.GetSvgImage(AppSymbols.software_installer, SymbolSize.Small));
Enabled = false;
await updater.Install();
Application.Restart();
return;
}
CheckStatusAndUpdate(true);
}
private void RadTextBox_MinecraftFolder_TextInserted(object o, EventArgs args)
{
if (!loadingData)
CheckStatusAndUpdate(false);
}
private void RadTextBox_ModpackUrl_TextInserted(object o, EventArgs args)
{
if (!loadingData)
CheckStatusAndUpdate(false);
}
private void RadTextBox_InstallationKey_TextInserted(object o, EventArgs args)
{
if (!loadingData)
CheckStatusAndUpdate(false);
}
private void ButtonX_SearchMinecraftProfile_Click(object sender, EventArgs e)
{
var ofd = new RadOpenFolderDialog();
if (ofd.ShowDialog(this) == DialogResult.OK)
RadTextBox_MinecraftProfileFolder.Text = ofd.FileName;
}
private async void ButtonX_CheckForUpdates_Click(object sender, EventArgs e)
{
ClearStatus();
await ExecuteUpdate(false, false);
}
private async void RadMenuItem_Install_Click(object sender, EventArgs e)
{
if (!currentUpdating)
{
ClearStatus();
await ExecuteUpdate(true, false);
}
}
private async void RadMenuItem_Repair_Click(object sender, EventArgs e)
{
if (!currentUpdating)
{
ClearStatus();
await ExecuteUpdate(true, true);
}
}
#endregion
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,347 +0,0 @@
using Gtk;
using ModpackUpdater.Manager;
using ModpackUpdater.My.Resources;
using Pilz.Extensions;
using Pilz.UI.Gtk.Dialogs;
using Pilz.UI.Symbols;
using System.Diagnostics;
using System.Reflection;
using UI = Gtk.Builder.ObjectAttribute;
namespace ModpackUpdater.Apps.Client;
internal class MainWindow : Window
{
[UI] private readonly Entry textBox_MinecraftFolder;
[UI] private readonly Entry textBox_ModpackUrl;
[UI] private readonly Entry textBox_InstallationKey;
[UI] private readonly Button button_SearchMinecraftFolder;
[UI] private readonly Button button_Install;
[UI] private readonly Button button_CheckForUpdates;
[UI] private readonly ImageMenuItem menuItem_Repair;
[UI] private readonly Label label_Status;
[UI] private readonly Image image_Status;
[UI] private readonly Image image_Repair;
private readonly UpdateCheckOptionsAdv updateOptions;
private ModpackInfo modpackInfo = new();
private ModpackConfig updateConfig = new();
private ModpackFeatures features;
private UpdateCheckResult lastUpdateCheckResult;
private bool currentUpdating;
private bool loadingData;
public MainWindow(UpdateCheckOptionsAdv updateOptions) : this(new Builder("MainWindow.glade"), updateOptions)
{
}
private MainWindow(Builder builder, UpdateCheckOptionsAdv updateOptions) : base(builder.GetRawOwnedObject("MainWindow"))
{
this.updateOptions = updateOptions;
builder.Autoconnect(this);
Title = $"{Title} (v{Assembly.GetExecutingAssembly().GetAppVersion()})";
button_CheckForUpdates.Image = Program.Symbols.GetImage(AppSymbols.update_done, SymbolSize.Small);
button_SearchMinecraftFolder.Image = Program.Symbols.GetImage(AppSymbols.opened_folder, SymbolSize.Small);
button_Install.Image = Program.Symbols.GetImage(AppSymbols.software_installer, SymbolSize.Small);
image_Repair.Pixbuf = Program.Symbols.GetPixbuf(AppSymbols.wrench, SymbolSize.Small);
LoadProfileToUi();
DeleteEvent += MainWindow_DeleteEvent;
Shown += MainWindow_Shown;
textBox_MinecraftFolder.Changed += TextBox_MinecraftFolder_TextInserted;
textBox_ModpackUrl.Changed += TextBox_ModpackUrl_TextInserted;
textBox_InstallationKey.Changed += TextBox_InstallationKey_TextInserted;
button_SearchMinecraftFolder.Clicked += Button_SearchMinecraftFolder_Clicked;
button_Install.Clicked += Button_Install_Clicked;
button_CheckForUpdates.Clicked += Button_CheckForUpdates_Clicked;
menuItem_Repair.Activated += MenuItem_Repair_Activated;
}
#region Gui
private void MainWindow_DeleteEvent(object o, DeleteEventArgs args)
{
AppConfig.Instance.LastMinecraftProfilePath = textBox_MinecraftFolder.Text.Trim();
Application.Quit();
}
private async void MainWindow_Shown(object sender, EventArgs e)
{
var updater = new AppUpdater();
if (!updateOptions.NoUpdate && await updater.Check() && MessageBox.Show(this, ButtonsType.YesNo, MessageType.Info, LangRes.MsgBox_UpdateAvailable) == ResponseType.Yes)
{
SetStatus(LangRes.StatusText_InstallingAppUpdate, Program.Symbols.GetPixbuf(AppSymbols.software_installer, SymbolSize.Small));
Sensitive = false;
await updater.Install();
Program.Restart();
return;
}
CheckStatusAndUpdate(true);
LoadProfileToUi();
}
private void TextBox_MinecraftFolder_TextInserted(object o, EventArgs args)
{
if (!loadingData)
CheckStatusAndUpdate(false);
}
private void TextBox_ModpackUrl_TextInserted(object o, EventArgs args)
{
if (!loadingData)
CheckStatusAndUpdate(false);
}
private void TextBox_InstallationKey_TextInserted(object o, EventArgs args)
{
if (!loadingData)
CheckStatusAndUpdate(false);
}
private void Button_SearchMinecraftFolder_Clicked(object sender, EventArgs e)
{
var ofd = new FileChooserDialog("Select Minecraft profile folder", this, FileChooserAction.SelectFolder, "Accept", ResponseType.Ok, "Cancel", ResponseType.Cancel);
ofd.Response += SearchMinecraftFolder_Response;
ofd.Present();
}
private void SearchMinecraftFolder_Response(object o, ResponseArgs args)
{
if (o is not FileChooserDialog ofd)
return;
if (args.ResponseId == ResponseType.Ok)
textBox_MinecraftFolder.Text = ofd.Filename;
ofd.Destroy();
}
private async void Button_CheckForUpdates_Clicked(object sender, EventArgs e)
{
ClearStatus();
await ExecuteUpdate(false, false);
}
private async void Button_Install_Clicked(object sender, EventArgs e)
{
if (!currentUpdating)
{
ClearStatus();
await ExecuteUpdate(true, false);
}
}
private async void MenuItem_Repair_Activated(object sender, EventArgs e)
{
if (!currentUpdating)
{
ClearStatus();
await ExecuteUpdate(true, true);
}
}
#endregion
#region Features
private void SetStatus(string statusText, Gdk.Pixbuf image)
{
label_Status.Text = statusText;
image_Status.Pixbuf = image;
}
private void ClearStatus()
{
label_Status.Text = "-";
image_Status.Pixbuf = null;
}
private void LoadProfileToUi()
{
loadingData = true;
textBox_MinecraftFolder.Text = modpackInfo?.LocaLPath ?? updateOptions.ProfileFolder ?? AppConfig.Instance.LastMinecraftProfilePath ?? textBox_MinecraftFolder.Text;
textBox_ModpackUrl.Text = modpackInfo?.ConfigUrl ?? updateOptions.ModpackConfig ?? textBox_ModpackUrl.Text;
textBox_InstallationKey.Text = modpackInfo?.ExtrasKey ?? updateOptions.ExtrasKey ?? textBox_InstallationKey.Text;
loadingData = false;
}
private async void CheckStatusAndUpdate(bool loadProfileToUi)
{
if (CheckStatus(loadProfileToUi))
await ExecuteUpdate(false, false);
}
private bool CheckStatus(bool loadProfileToUi)
{
try
{
modpackInfo = ModpackInfo.TryLoad(textBox_MinecraftFolder.Text.Trim());
}
catch
{
}
if (loadProfileToUi)
LoadProfileToUi();
try
{
updateConfig = ModpackConfig.LoadFromUrl(textBox_ModpackUrl.Text);
}
catch (Exception)
{
}
if (modpackInfo != null)
features = new(updateConfig);
if (modpackInfo == null || string.IsNullOrWhiteSpace(textBox_MinecraftFolder.Text) /*|| modpackInfo.Valid*/)
{
SetStatus(LangRes.StatusText_MinecraftProfileWarning, Program.Symbols.GetPixbuf(AppSymbols.general_warning_sign, SymbolSize.Small));
button_CheckForUpdates.Sensitive = false;
button_Install.Sensitive = false;
return false;
}
else if (updateConfig == null || string.IsNullOrWhiteSpace(textBox_ModpackUrl.Text))
{
SetStatus(LangRes.StatusText_ConfigIncompleteOrNotLoaded, Program.Symbols.GetPixbuf(AppSymbols.general_warning_sign, SymbolSize.Small));
button_CheckForUpdates.Sensitive = false;
button_Install.Sensitive = false;
return false;
}
else if (updateConfig.Maintenance && !updateOptions.IgnoreMaintenance)
{
SetStatus(LangRes.StatusText_Maintenance, Program.Symbols.GetPixbuf(AppSymbols.services, SymbolSize.Small));
button_CheckForUpdates.Sensitive = false;
button_Install.Sensitive = false;
return false;
}
button_CheckForUpdates.Sensitive = true;
button_Install.Sensitive = true;
return true;
}
private async Task ExecuteUpdate(bool doInstall, bool repair)
{
// Ensure set extras key
modpackInfo.ExtrasKey = textBox_InstallationKey.Text.Trim();
var updater = new ModpackInstaller(updateConfig, modpackInfo)
{
OverwriteRefTag = Program.Options.RefTag,
OverwriteVersion = Program.Options.Version,
};
updater.InstallProgessUpdated += Update_InstallProgessUpdated;
updater.CheckingProgressUpdated += Updated_CheckingProgresssUpdated;
void error()
{
SetStatus(LangRes.StatusText_ErrorWhileUpdateCheckOrUpdate, Program.Symbols.GetPixbuf(AppSymbols.close, SymbolSize.Small));
currentUpdating = false;
}
void installing()
{
SetStatus(LangRes.StatusText_Installing, Program.Symbols.GetPixbuf(AppSymbols.software_installer, SymbolSize.Small));
currentUpdating = true;
}
void updatesAvailable()
{
SetStatus(LangRes.StatusText_UpdateAvailable, Program.Symbols.GetPixbuf(AppSymbols.software_installer, SymbolSize.Small));
}
void everythingOk()
{
SetStatus(LangRes.StatusTest_EverythingOk, Program.Symbols.GetPixbuf(AppSymbols.done, SymbolSize.Small));
currentUpdating = false;
}
// Check only if not pressed "install", not really needed otherwise.
if (lastUpdateCheckResult is null || !doInstall || repair)
{
SetStatus(LangRes.StatusText_CheckingForUpdates, Program.Symbols.GetPixbuf(AppSymbols.update_done, SymbolSize.Small));
// Check for extras once again
updateOptions.IncludeExtras = features.IsEnabled(ModpackFeatures.FeatureAllowExtas, new AllowExtrasFeatureContext(modpackInfo));
// Force re-install on repair
updateOptions.IgnoreInstalledVersion = repair;
try
{
lastUpdateCheckResult = await updater.Check(updateOptions);
}
catch (Exception)
{
error();
if (Debugger.IsAttached)
throw;
}
finally
{
}
}
// Error while update check
if (lastUpdateCheckResult is null || lastUpdateCheckResult.HasError)
{
error();
return;
}
// No updates available
if (!lastUpdateCheckResult.HasUpdates)
{
everythingOk();
return;
}
// Updates available (but don't install)
if (!doInstall)
{
updatesAvailable();
return;
}
// Install updates
installing();
currentUpdating = true;
try
{
// Install
if (await updater.Install(lastUpdateCheckResult) == false)
{
error();
return;
}
// Success
lastUpdateCheckResult = null; // Reset last update check, a new one would be needed now.
everythingOk();
}
catch (Exception)
{
// Error
error();
if (Debugger.IsAttached)
throw;
}
}
private void Updated_CheckingProgresssUpdated(int toCheck, int processed)
{
SetStatus(Math.Round(processed / (double)toCheck * 100d, 1) + "%", Program.Symbols.GetPixbuf(AppSymbols.update_done, SymbolSize.Small));
}
private void Update_InstallProgessUpdated(UpdateCheckResult result, int processedSyncs)
{
int actionCount = result.Actions.Count;
SetStatus(Math.Round(processedSyncs / (double)actionCount * 100d, 1) + "%", Program.Symbols.GetPixbuf(AppSymbols.software_installer, SymbolSize.Small));
}
#endregion
}

View File

@@ -1,224 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.40.0 -->
<interface>
<requires lib="gtk+" version="3.24"/>
<object class="GtkImage" id="image_Repair">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="stock">gtk-missing-image</property>
</object>
<object class="GtkMenu" id="menu_Advanced">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkImageMenuItem" id="menuItem_Repair">
<property name="label" translatable="yes">Repair</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="image">image_Repair</property>
<property name="use-stock">False</property>
<property name="always-show-image">True</property>
</object>
</child>
</object>
<object class="GtkWindow" id="MainWindow">
<property name="can-focus">False</property>
<property name="title" translatable="yes">Minecraft Modpack Updater</property>
<property name="window-position">center</property>
<property name="default-width">400</property>
<child>
<!-- n-columns=3 n-rows=5 -->
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-start">3</property>
<property name="margin-end">3</property>
<property name="margin-top">3</property>
<property name="margin-bottom">3</property>
<property name="row-spacing">3</property>
<property name="column-spacing">3</property>
<property name="row-homogeneous">True</property>
<child>
<object class="GtkEntry" id="textBox_MinecraftFolder">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="hexpand">True</property>
<property name="placeholder-text" translatable="yes">C:\Users\...\AppData\...</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Minecraft profile</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button_SearchMinecraftFolder">
<property name="label" translatable="yes">Search</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="always-show-image">True</property>
</object>
<packing>
<property name="left-attach">2</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Modpack URL</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="textBox_ModpackUrl">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="hexpand">True</property>
<property name="placeholder-text" translatable="yes">https://...</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">1</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Installation key</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="textBox_InstallationKey">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="hexpand">True</property>
<property name="placeholder-text" translatable="yes">AAAAA-BBBBB-CCCCC-DDDDD-EEEEE</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">2</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Status</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">3</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button_Install">
<property name="label" translatable="yes">Install</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="has-focus">True</property>
<property name="receives-default">True</property>
<property name="always-show-image">True</property>
</object>
<packing>
<property name="left-attach">2</property>
<property name="top-attach">4</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button_CheckForUpdates">
<property name="label" translatable="yes">Check for updates</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="halign">end</property>
<property name="always-show-image">True</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">4</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkImage" id="image_Status">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="stock">gtk-missing-image</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_Status">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Everything is right and up-to-date.</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">3</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkMenuButton">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="halign">start</property>
<property name="popup">menu_Advanced</property>
<property name="use-popover">False</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">4</property>
</packing>
</child>
</object>
</child>
</object>
</interface>

View File

@@ -2,19 +2,13 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net8.0-windows</TargetFramework>
<ApplicationIcon>icons8_download_from_ftp.ico</ApplicationIcon>
<AssemblyName>Minecraft Modpack Updater</AssemblyName>
<ImplicitUsings>true</ImplicitUsings>
<UseWindowsForms>true</UseWindowsForms>
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
</PropertyGroup>
<ItemGroup>
<None Remove="**\*.glade" />
<EmbeddedResource Include="**\*.glade">
<LogicalName>%(Filename)%(Extension)</LogicalName>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Compile Update="FiledialogFilters.Designer.cs">
@@ -48,7 +42,11 @@
<PackageReference Include="Pilz.Configuration" Version="3.2.2" />
<PackageReference Include="Pilz.Cryptography" Version="2.1.2" />
<PackageReference Include="Pilz.IO" Version="2.1.0" />
<PackageReference Include="Pilz.UI.Gtk" Version="1.0.2" />
<PackageReference Include="Pilz.UI" Version="3.0.0" />
<PackageReference Include="Pilz.UI.WinForms.Telerik" Version="2.12.3" />
<PackageReference Include="Pilz.UI.WinForms.Telerik.Symbols" Version="1.2.1" />
<PackageReference Include="UI.for.WinForms.Common" Version="2025.2.612-preview" />
<PackageReference Include="UI.for.WinForms.Themes" Version="2025.2.612-preview" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,14 +1,9 @@
using Castle.Core.Logging;
using Gtk;
using Microsoft.Win32;
using ModpackUpdater.Manager;
using Newtonsoft.Json;
using Pilz;
using Pilz.Configuration;
using Pilz.UI.Gtk.Symbols;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
[assembly: AssemblyAppVersion("1.8.0.0")]
@@ -18,9 +13,7 @@ public static class Program
{
private static readonly SettingsManager settingsManager;
private static readonly ILogger log = new ConsoleLogger();
private static Application uiApp;
public static IGtkSymbolFactory<AppSymbols> Symbols { get; } = new AppSymbolFactory();
public static ISettings Settings => settingsManager.Instance;
public static ILogger Log => log;
internal static Options Options { get; private set; }
@@ -55,49 +48,9 @@ public static class Program
private static void RunApp(UpdateCheckOptionsAdv updateOptions)
{
if (UsesWindowsDarkTheme())
Environment.SetEnvironmentVariable("GTK_THEME", "Adwaita:dark");
Application.Init();
uiApp = new Application("ModpackUpdater.Apps.Client", GLib.ApplicationFlags.None);
uiApp.Register(GLib.Cancellable.Current);
var win = new MainWindow(updateOptions);
uiApp.AddWindow(win);
win.Show();
Application.Run();
}
public static void Restart()
{
var process = Process.GetCurrentProcess();
var arguments = Environment.GetCommandLineArgs();
ProcessStartInfo currentStartInfo = new()
{
FileName = Environment.ProcessPath,
};
if (arguments.Length >= 2)
{
var sb = new StringBuilder((arguments.Length - 1) * 16);
for (int argumentIndex = 1; argumentIndex < arguments.Length; argumentIndex++)
sb.Append($"\"{arguments[argumentIndex]}\" ");
currentStartInfo.Arguments = sb.ToString(0, sb.Length - 1);
}
// Close the current application
Application.Quit();
// Start a new process
Process.Start(currentStartInfo);
// Close this proccess
Environment.Exit(0);
ApplicationConfiguration.Initialize();
AppGlobals.Initialize();
Application.Run(new MainForm(updateOptions));
}
private static string GetSettingsPath(int? settingsVersion = 3)
@@ -174,19 +127,4 @@ public static class Program
return info.ConfigUrl;
return configUrl;
}
private static bool UsesWindowsDarkTheme()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
try
{
return Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize")?.GetValue("AppsUseLightTheme") as bool? ?? default;
}
catch (Exception)
{
}
}
return default;
}
}