From 462d48fafd62ae9220f6aea3cc85175bceaa68c6 Mon Sep 17 00:00:00 2001 From: pizzaboxer <41478239+pizzaboxer@users.noreply.github.com> Date: Tue, 16 Aug 2022 23:06:08 +0100 Subject: [PATCH] Finalize update for v1.2.0 - Features - Added three new bootstrapper styles (Vista, Legacy 2009 and Progress Dark Theme) - Added ability to disable rich presence activity buttons - Added ability to restore old mouse cursor - Quality of Life - Refactored code for bootstrapper styles --- Bloxstrap/Bloxstrap.csproj | 10 +- .../Bootstrapper/Bootstrapper.AppInstall.cs | 3 - .../Bootstrapper.RobloxInstall.cs | 39 +++- .../Bootstrapper.RobloxModifications.cs | 52 ++--- Bloxstrap/Bootstrapper/Bootstrapper.cs | 15 +- ...gacyDialog.cs => BootstrapperStyleForm.cs} | 109 +++++----- .../BootstrapperStyles/IBootstrapperStyle.cs | 27 +++ .../LegacyDialog2009.Designer.cs | 93 ++++++++ .../BootstrapperStyles/LegacyDialog2009.cs | 41 ++++ ...egacyDialog.resx => LegacyDialog2009.resx} | 0 ...signer.cs => LegacyDialog2011.Designer.cs} | 54 ++--- .../BootstrapperStyles/LegacyDialog2011.cs | 45 ++++ .../BootstrapperStyles/LegacyDialog2011.resx | 60 ++++++ .../ProgressDialog.Designer.cs | 64 +++--- .../BootstrapperStyles/ProgressDialog.cs | 162 ++------------ .../ProgressDialogDark.Designer.cs | 127 +++++++++++ .../BootstrapperStyles/ProgressDialogDark.cs | 54 +++++ .../ProgressDialogDark.resx | 60 ++++++ .../VistaDialog.Designer.cs | 49 +++++ .../Dialogs/BootstrapperStyles/VistaDialog.cs | 200 +++++++++++------- .../BootstrapperStyles/VistaDialog.resx | 60 ++++++ Bloxstrap/Dialogs/Preferences.Designer.cs | 52 +++-- Bloxstrap/Dialogs/Preferences.cs | 106 ++++++++-- Bloxstrap/Enums/BootstrapperStyle.cs | 6 +- Bloxstrap/Helpers/DiscordRichPresence.cs | 35 +-- Bloxstrap/Helpers/UpdateChecker.cs | 4 +- Bloxstrap/Properties/Resources.Designer.cs | 20 ++ Bloxstrap/Properties/Resources.resx | 6 + Bloxstrap/Resources/DarkCancelButton.png | Bin 0 -> 1134 bytes Bloxstrap/Resources/DarkCancelButtonHover.png | Bin 0 -> 1175 bytes Bloxstrap/Resources/Icon2009-png.png | Bin 17074 -> 16441 bytes Bloxstrap/Settings.cs | 16 ++ README.md | 8 +- 33 files changed, 1157 insertions(+), 420 deletions(-) rename Bloxstrap/Dialogs/BootstrapperStyles/{LegacyDialog.cs => BootstrapperStyleForm.cs} (57%) create mode 100644 Bloxstrap/Dialogs/BootstrapperStyles/IBootstrapperStyle.cs create mode 100644 Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2009.Designer.cs create mode 100644 Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2009.cs rename Bloxstrap/Dialogs/BootstrapperStyles/{LegacyDialog.resx => LegacyDialog2009.resx} (100%) rename Bloxstrap/Dialogs/BootstrapperStyles/{LegacyDialog.Designer.cs => LegacyDialog2011.Designer.cs} (71%) create mode 100644 Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2011.cs create mode 100644 Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2011.resx create mode 100644 Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogDark.Designer.cs create mode 100644 Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogDark.cs create mode 100644 Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogDark.resx create mode 100644 Bloxstrap/Dialogs/BootstrapperStyles/VistaDialog.Designer.cs create mode 100644 Bloxstrap/Dialogs/BootstrapperStyles/VistaDialog.resx create mode 100644 Bloxstrap/Resources/DarkCancelButton.png create mode 100644 Bloxstrap/Resources/DarkCancelButtonHover.png diff --git a/Bloxstrap/Bloxstrap.csproj b/Bloxstrap/Bloxstrap.csproj index 34e8cfb3..cda5ea0a 100644 --- a/Bloxstrap/Bloxstrap.csproj +++ b/Bloxstrap/Bloxstrap.csproj @@ -9,8 +9,8 @@ AnyCPU AnyCPU;x86 Bloxstrap.ico - 1.1.0 - 1.1.0.0 + 1.2.0 + 1.2.0.0 @@ -27,6 +27,12 @@ + + Form + + + Form + True True diff --git a/Bloxstrap/Bootstrapper/Bootstrapper.AppInstall.cs b/Bloxstrap/Bootstrapper/Bootstrapper.AppInstall.cs index f45aece2..54741edd 100644 --- a/Bloxstrap/Bootstrapper/Bootstrapper.AppInstall.cs +++ b/Bloxstrap/Bootstrapper/Bootstrapper.AppInstall.cs @@ -81,8 +81,6 @@ private void Uninstall() CheckIfRunning(); - // lots of try/catches here... lol - Message = $"Uninstalling {Program.ProjectName}..."; Program.SettingsManager.ShouldSave = false; @@ -122,7 +120,6 @@ private void Uninstall() catch (Exception) { } ShowSuccess($"{Program.ProjectName} has been uninstalled"); - Program.Exit(); } } } diff --git a/Bloxstrap/Bootstrapper/Bootstrapper.RobloxInstall.cs b/Bloxstrap/Bootstrapper/Bootstrapper.RobloxInstall.cs index 915272cb..151a40d4 100644 --- a/Bloxstrap/Bootstrapper/Bootstrapper.RobloxInstall.cs +++ b/Bloxstrap/Bootstrapper/Bootstrapper.RobloxInstall.cs @@ -91,8 +91,11 @@ private async Task InstallLatestVersion() File.Delete(filename); } - // and also to delete our old version folder - Directory.Delete(Path.Combine(Program.BaseDirectory, "Versions", Program.Settings.VersionGuid), true); + if (VersionGuid != Program.Settings.VersionGuid) + { + // and also to delete our old version folder + Directory.Delete(Path.Combine(Program.BaseDirectory, "Versions", Program.Settings.VersionGuid), true); + } } CancelEnabled = false; @@ -115,6 +118,7 @@ private async void ApplyModifications() // but for now, let's just keep it at this await ModifyDeathSound(); + await ModifyMouseCursor(); } private async void DownloadPackage(Package package) @@ -204,5 +208,36 @@ private void ExtractPackage(Package package) } } } + + private void ExtractFilesFromPackage(string packageName, string[] files) + { + Package? package = VersionPackageManifest.Find(x => x.Name == packageName); + + if (package is null) + return; + + DownloadPackage(package); + + string packageLocation = Path.Combine(DownloadsFolder, package.Signature); + string packageFolder = Path.Combine(VersionFolder, PackageDirectories[package.Name]); + + using (ZipArchive archive = ZipFile.OpenRead(packageLocation)) + { + foreach (string fileName in files) + { + ZipArchiveEntry? entry = archive.Entries.Where(x => x.FullName == fileName).FirstOrDefault(); + + if (entry is null) + return; + + string fileLocation = Path.Combine(packageFolder, entry.FullName); + + if (File.Exists(fileLocation)) + File.Delete(fileLocation); + + entry.ExtractToFile(fileLocation); + } + } + } } } diff --git a/Bloxstrap/Bootstrapper/Bootstrapper.RobloxModifications.cs b/Bloxstrap/Bootstrapper/Bootstrapper.RobloxModifications.cs index 955f82e7..b5545d52 100644 --- a/Bloxstrap/Bootstrapper/Bootstrapper.RobloxModifications.cs +++ b/Bloxstrap/Bootstrapper/Bootstrapper.RobloxModifications.cs @@ -1,6 +1,4 @@ -using System.IO.Compression; - -using Bloxstrap.Helpers; +using Bloxstrap.Helpers; namespace Bloxstrap { @@ -12,10 +10,10 @@ private async Task ModifyDeathSound() string fileContentLocation = "content\\sounds\\ouch.ogg"; string fileLocation = Path.Combine(VersionFolder, fileContentLocation); - string officialDeathSoundHash = VersionFileManifest[fileContentLocation]; - string currentDeathSoundHash = Utilities.CalculateMD5(fileLocation); + string officialHash = VersionFileManifest[fileContentLocation]; + string currentHash = Utilities.CalculateMD5(fileLocation); - if (Program.Settings.UseOldDeathSound && currentDeathSoundHash == officialDeathSoundHash) + if (Program.Settings.UseOldDeathSound && currentHash == officialHash) { // let's get the old one! @@ -29,33 +27,37 @@ private async Task ModifyDeathSound() await response.Content.CopyToAsync(fileStream); } } - else if (!Program.Settings.UseOldDeathSound && currentDeathSoundHash != officialDeathSoundHash) + else if (!Program.Settings.UseOldDeathSound && currentHash != officialHash) { // who's lame enough to ever do this? // well, we need to re-extract the one that's in the content-sounds.zip package - var package = VersionPackageManifest.Find(x => x.Name == "content-sounds.zip"); - - if (package is null) - return; - - DownloadPackage(package); - - string packageLocation = Path.Combine(DownloadsFolder, package.Signature); - string packageFolder = Path.Combine(VersionFolder, PackageDirectories[package.Name]); + string[] files = { fileContentName }; + ExtractFilesFromPackage("content-sounds.zip", files); + } + } - using (ZipArchive archive = ZipFile.OpenRead(packageLocation)) - { - ZipArchiveEntry? entry = archive.Entries.Where(x => x.FullName == fileContentName).FirstOrDefault(); + private async Task ModifyMouseCursor() + { + string baseFolder = Path.Combine(VersionFolder, "content\\textures\\"); + + string arrowCursor = "Cursors\\KeyboardMouse\\ArrowCursor.png"; + string arrowFarCursor = "Cursors\\KeyboardMouse\\ArrowFarCursor.png"; - if (entry is null) - return; + string officialHash = VersionFileManifest["content\\textures\\Cursors\\KeyboardMouse\\ArrowCursor.png"]; + string currentHash = Utilities.CalculateMD5(Path.Combine(baseFolder, arrowCursor)); - if (File.Exists(fileLocation)) - File.Delete(fileLocation); + if (Program.Settings.UseOldMouseCursor && currentHash == officialHash) + { + // the old cursors are actually still in the content\textures\ folder, so we can just get them from there - entry.ExtractToFile(fileLocation); - } + File.Copy(Path.Combine(baseFolder, "ArrowCursor.png"), Path.Combine(baseFolder, arrowCursor), true); + File.Copy(Path.Combine(baseFolder, "ArrowFarCursor.png"), Path.Combine(baseFolder, arrowFarCursor), true); + } + else if (!Program.Settings.UseOldMouseCursor && currentHash != officialHash) + { + string[] files = { arrowCursor, arrowFarCursor }; + ExtractFilesFromPackage("content-textures2.zip", files); } } } diff --git a/Bloxstrap/Bootstrapper/Bootstrapper.cs b/Bloxstrap/Bootstrapper/Bootstrapper.cs index 30727dcd..3fa20e41 100644 --- a/Bloxstrap/Bootstrapper/Bootstrapper.cs +++ b/Bloxstrap/Bootstrapper/Bootstrapper.cs @@ -26,16 +26,24 @@ public void Initialize(BootstrapperStyle bootstrapperStyle, string? launchComman switch (bootstrapperStyle) { case BootstrapperStyle.VistaDialog: - new VistaDialog(this); + Application.Run(new VistaDialog(this)); break; - case BootstrapperStyle.LegacyDialog: - Application.Run(new LegacyDialog(this)); + case BootstrapperStyle.LegacyDialog2009: + Application.Run(new LegacyDialog2009(this)); + break; + + case BootstrapperStyle.LegacyDialog2011: + Application.Run(new LegacyDialog2011(this)); break; case BootstrapperStyle.ProgressDialog: Application.Run(new ProgressDialog(this)); break; + + case BootstrapperStyle.ProgressDialogDark: + Application.Run(new ProgressDialogDark(this)); + break; } } @@ -136,6 +144,7 @@ private async Task StartRoblox() return; CloseDialog(); + await gameClient.WaitForExitAsync(); } } diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog.cs b/Bloxstrap/Dialogs/BootstrapperStyles/BootstrapperStyleForm.cs similarity index 57% rename from Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog.cs rename to Bloxstrap/Dialogs/BootstrapperStyles/BootstrapperStyleForm.cs index da6c3b65..9f9f2a88 100644 --- a/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog.cs +++ b/Bloxstrap/Dialogs/BootstrapperStyles/BootstrapperStyleForm.cs @@ -1,36 +1,29 @@ +using System.Diagnostics; + using Bloxstrap.Helpers; using Bloxstrap.Helpers.RSMM; namespace Bloxstrap.Dialogs.BootstrapperStyles { - // TODO - universal implementation for winforms-based styles? (to reduce duplicate code) - - // example: https://youtu.be/3K9oCEMHj2s?t=35 + public class BootstrapperStyleForm : Form, IBootstrapperStyle + { + public Bootstrapper? Bootstrapper { get; set; } - // so this specifically emulates the 2011 version of the legacy dialog, - // but once winforms code is cleaned up we could also do the 2009 version too - // example: https://youtu.be/VpduiruysuM?t=18 + public virtual string Message { get; set; } + public virtual ProgressBarStyle ProgressStyle { get; set; } + public virtual int ProgressValue { get; set; } + public virtual bool CancelEnabled { get; set; } - public partial class LegacyDialog : Form - { - private readonly Bootstrapper? Bootstrapper; - public LegacyDialog(Bootstrapper? bootstrapper = null) + public void SetupDialog() { - InitializeComponent(); - - Bootstrapper = bootstrapper; - - Icon icon = IconManager.GetIconResource(); this.Text = Program.ProjectName; - this.Icon = icon; - this.IconBox.Image = icon.ToBitmap(); + this.Icon = IconManager.GetIconResource(); if (Bootstrapper is null) { - this.Message.Text = "Click the Cancel button to return to preferences"; - this.ButtonCancel.Enabled = true; - this.ButtonCancel.Visible = true; + Message = "Select Cancel to return to preferences"; + CancelEnabled = true; } else { @@ -46,6 +39,7 @@ public LegacyDialog(Bootstrapper? bootstrapper = null) } } + public async void RunBootstrapper() { if (Bootstrapper is null) @@ -60,37 +54,47 @@ public async void RunBootstrapper() // string message = String.Format("{0}: {1}", ex.GetType(), ex.Message); string message = ex.ToString(); ShowError(message); - - Program.Exit(); } } - private void ShowError(string message) + public virtual void ShowSuccess(object sender, ChangeEventArgs e) { MessageBox.Show( - $"An error occurred while starting Roblox\n\nDetails: {message}", - Program.ProjectName, - MessageBoxButtons.OK, - MessageBoxIcon.Error + e.Value, + Program.ProjectName, + MessageBoxButtons.OK, + MessageBoxIcon.Information ); + + Program.Exit(); } - private void ShowSuccess(object sender, ChangeEventArgs e) + public virtual void ShowError(string message) { MessageBox.Show( - e.Value, + $"An error occurred while starting Roblox\n\nDetails: {message}", Program.ProjectName, MessageBoxButtons.OK, - MessageBoxIcon.Information + MessageBoxIcon.Error ); + + Program.Exit(); } - private void CloseDialog(object? sender, EventArgs e) + public virtual void CloseDialog(object? sender, EventArgs e) { - this.Close(); + if (this.InvokeRequired) + { + EventHandler handler = new(CloseDialog); + this.Invoke(handler, sender, e); + } + else + { + this.Hide(); + } } - private void PromptShutdown(object? sender, EventArgs e) + public void PromptShutdown(object? sender, EventArgs e) { DialogResult result = MessageBox.Show( "Roblox is currently running, but needs to close. Would you like close Roblox now?", @@ -103,60 +107,61 @@ private void PromptShutdown(object? sender, EventArgs e) Environment.Exit(0); } - private void MessageChanged(object sender, ChangeEventArgs e) + + public void MessageChanged(object sender, ChangeEventArgs e) { if (this.InvokeRequired) { ChangeEventHandler handler = new(MessageChanged); - this.Message.Invoke(handler, sender, e); + this.Invoke(handler, sender, e); } else { - this.Message.Text = e.Value; + Message = e.Value; } } - private void ProgressBarValueChanged(object sender, ChangeEventArgs e) + public void ProgressBarStyleChanged(object sender, ChangeEventArgs e) { - if (this.ProgressBar.InvokeRequired) + if (this.InvokeRequired) { - ChangeEventHandler handler = new(ProgressBarValueChanged); - this.ProgressBar.Invoke(handler, sender, e); + ChangeEventHandler handler = new(this.ProgressBarStyleChanged); + this.Invoke(handler, sender, e); } else { - this.ProgressBar.Value = e.Value; + ProgressStyle = e.Value; } } - private void ProgressBarStyleChanged(object sender, ChangeEventArgs e) + public void ProgressBarValueChanged(object sender, ChangeEventArgs e) { - if (this.ProgressBar.InvokeRequired) + if (this.InvokeRequired) { - ChangeEventHandler handler = new(this.ProgressBarStyleChanged); - this.ProgressBar.Invoke(handler, sender, e); + ChangeEventHandler handler = new(ProgressBarValueChanged); + this.Invoke(handler, sender, e); } else { - this.ProgressBar.Style = e.Value; + ProgressValue = e.Value; } } - private void CancelEnabledChanged(object sender, ChangeEventArgs e) + public void CancelEnabledChanged(object sender, ChangeEventArgs e) { - if (this.ButtonCancel.InvokeRequired) + if (this.InvokeRequired) { ChangeEventHandler handler = new(CancelEnabledChanged); - this.ButtonCancel.Invoke(handler, sender, e); + this.Invoke(handler, sender, e); } else { - this.ButtonCancel.Enabled = e.Value; - this.ButtonCancel.Visible = e.Value; + this.CancelEnabled = e.Value; } } - private void ButtonCancel_Click(object sender, EventArgs e) + + public void ButtonCancel_Click(object? sender, EventArgs e) { if (Bootstrapper is null) this.Close(); diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/IBootstrapperStyle.cs b/Bloxstrap/Dialogs/BootstrapperStyles/IBootstrapperStyle.cs new file mode 100644 index 00000000..25cbe630 --- /dev/null +++ b/Bloxstrap/Dialogs/BootstrapperStyles/IBootstrapperStyle.cs @@ -0,0 +1,27 @@ +using Bloxstrap.Helpers.RSMM; + +namespace Bloxstrap.Dialogs.BootstrapperStyles +{ + interface IBootstrapperStyle + { + Bootstrapper? Bootstrapper { get; set; } + + string Message { get; set; } + ProgressBarStyle ProgressStyle { get; set; } + int ProgressValue { get; set; } + bool CancelEnabled { get; set; } + + void RunBootstrapper(); + void ShowError(string message); + void ShowSuccess(object sender, ChangeEventArgs e); + void CloseDialog(object? sender, EventArgs e); + void PromptShutdown(object? sender, EventArgs e); + + void MessageChanged(object sender, ChangeEventArgs e); + void ProgressBarValueChanged(object sender, ChangeEventArgs e); + void ProgressBarStyleChanged(object sender, ChangeEventArgs e); + void CancelEnabledChanged(object sender, ChangeEventArgs e); + + void ButtonCancel_Click(object sender, EventArgs e); + } +} diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2009.Designer.cs b/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2009.Designer.cs new file mode 100644 index 00000000..7e4e43cc --- /dev/null +++ b/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2009.Designer.cs @@ -0,0 +1,93 @@ +namespace Bloxstrap.Dialogs.BootstrapperStyles +{ + partial class LegacyDialog2009 + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.labelMessage = new System.Windows.Forms.Label(); + this.ProgressBar = new System.Windows.Forms.ProgressBar(); + this.buttonCancel = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // labelMessage + // + this.labelMessage.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + this.labelMessage.Location = new System.Drawing.Point(12, 16); + this.labelMessage.Name = "labelMessage"; + this.labelMessage.Size = new System.Drawing.Size(287, 17); + this.labelMessage.TabIndex = 0; + this.labelMessage.Text = "Please wait..."; + // + // ProgressBar + // + this.ProgressBar.Location = new System.Drawing.Point(15, 47); + this.ProgressBar.MarqueeAnimationSpeed = 33; + this.ProgressBar.Name = "ProgressBar"; + this.ProgressBar.Size = new System.Drawing.Size(281, 20); + this.ProgressBar.Style = System.Windows.Forms.ProgressBarStyle.Marquee; + this.ProgressBar.TabIndex = 1; + // + // buttonCancel + // + this.buttonCancel.Enabled = false; + this.buttonCancel.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + this.buttonCancel.Location = new System.Drawing.Point(221, 83); + this.buttonCancel.Name = "buttonCancel"; + this.buttonCancel.Size = new System.Drawing.Size(75, 23); + this.buttonCancel.TabIndex = 3; + this.buttonCancel.Text = "Cancel"; + this.buttonCancel.UseVisualStyleBackColor = true; + this.buttonCancel.Click += new System.EventHandler(this.ButtonCancel_Click); + // + // LegacyDialog2009 + // + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(311, 122); + this.Controls.Add(this.buttonCancel); + this.Controls.Add(this.ProgressBar); + this.Controls.Add(this.labelMessage); + this.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.MaximizeBox = false; + this.MaximumSize = new System.Drawing.Size(327, 161); + this.MinimizeBox = false; + this.MinimumSize = new System.Drawing.Size(327, 161); + this.Name = "LegacyDialog2009"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "LegacyDialog2009"; + this.ResumeLayout(false); + + } + + #endregion + + private Label labelMessage; + private ProgressBar ProgressBar; + private Button buttonCancel; + } +} \ No newline at end of file diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2009.cs b/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2009.cs new file mode 100644 index 00000000..23d7ca4e --- /dev/null +++ b/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2009.cs @@ -0,0 +1,41 @@ +namespace Bloxstrap.Dialogs.BootstrapperStyles +{ + // windows: https://youtu.be/VpduiruysuM?t=18 + // mac: https://youtu.be/ncHhbcVDRgQ?t=63 + + public partial class LegacyDialog2009 : BootstrapperStyleForm + { + public override string Message + { + get => labelMessage.Text; + set => labelMessage.Text = value; + } + + public override ProgressBarStyle ProgressStyle + { + get => ProgressBar.Style; + set => ProgressBar.Style = value; + } + + public override int ProgressValue + { + get => ProgressBar.Value; + set => ProgressBar.Value = value; + } + + public override bool CancelEnabled + { + get => this.buttonCancel.Enabled; + set => this.buttonCancel.Enabled = value; + } + + public LegacyDialog2009(Bootstrapper? bootstrapper = null) + { + InitializeComponent(); + + Bootstrapper = bootstrapper; + + SetupDialog(); + } + } +} diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog.resx b/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2009.resx similarity index 100% rename from Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog.resx rename to Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2009.resx diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog.Designer.cs b/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2011.Designer.cs similarity index 71% rename from Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog.Designer.cs rename to Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2011.Designer.cs index 270c4998..d1312906 100644 --- a/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog.Designer.cs +++ b/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2011.Designer.cs @@ -1,6 +1,6 @@ namespace Bloxstrap.Dialogs.BootstrapperStyles { - partial class LegacyDialog + partial class LegacyDialog2011 { /// /// Required designer variable. @@ -28,20 +28,20 @@ protected override void Dispose(bool disposing) /// private void InitializeComponent() { - this.Message = new System.Windows.Forms.Label(); + this.labelMessage = new System.Windows.Forms.Label(); this.ProgressBar = new System.Windows.Forms.ProgressBar(); this.IconBox = new System.Windows.Forms.PictureBox(); - this.ButtonCancel = new System.Windows.Forms.Button(); + this.buttonCancel = new System.Windows.Forms.Button(); ((System.ComponentModel.ISupportInitialize)(this.IconBox)).BeginInit(); this.SuspendLayout(); // - // Message + // labelMessage // - this.Message.Location = new System.Drawing.Point(55, 23); - this.Message.Name = "Message"; - this.Message.Size = new System.Drawing.Size(287, 17); - this.Message.TabIndex = 0; - this.Message.Text = "Please wait..."; + this.labelMessage.Location = new System.Drawing.Point(55, 23); + this.labelMessage.Name = "labelMessage"; + this.labelMessage.Size = new System.Drawing.Size(287, 17); + this.labelMessage.TabIndex = 0; + this.labelMessage.Text = "Please wait..."; // // ProgressBar // @@ -62,37 +62,37 @@ private void InitializeComponent() this.IconBox.TabIndex = 2; this.IconBox.TabStop = false; // - // ButtonCancel + // buttonCancel // - this.ButtonCancel.Enabled = false; - this.ButtonCancel.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); - this.ButtonCancel.Location = new System.Drawing.Point(271, 83); - this.ButtonCancel.Name = "ButtonCancel"; - this.ButtonCancel.Size = new System.Drawing.Size(75, 23); - this.ButtonCancel.TabIndex = 3; - this.ButtonCancel.Text = "Cancel"; - this.ButtonCancel.UseVisualStyleBackColor = true; - this.ButtonCancel.Visible = false; - this.ButtonCancel.Click += new System.EventHandler(this.ButtonCancel_Click); + this.buttonCancel.Enabled = false; + this.buttonCancel.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + this.buttonCancel.Location = new System.Drawing.Point(271, 83); + this.buttonCancel.Name = "buttonCancel"; + this.buttonCancel.Size = new System.Drawing.Size(75, 23); + this.buttonCancel.TabIndex = 3; + this.buttonCancel.Text = "Cancel"; + this.buttonCancel.UseVisualStyleBackColor = true; + this.buttonCancel.Visible = false; + this.buttonCancel.Click += new System.EventHandler(this.ButtonCancel_Click); // - // LegacyDialogStyle + // LegacyDialog // this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(362, 131); - this.Controls.Add(this.ButtonCancel); + this.Controls.Add(this.buttonCancel); this.Controls.Add(this.IconBox); this.Controls.Add(this.ProgressBar); - this.Controls.Add(this.Message); + this.Controls.Add(this.labelMessage); this.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; this.MaximizeBox = false; this.MaximumSize = new System.Drawing.Size(378, 170); this.MinimizeBox = false; this.MinimumSize = new System.Drawing.Size(378, 170); - this.Name = "LegacyDialogStyle"; + this.Name = "LegacyDialog2011"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "LegacyDialogStyle"; + this.Text = "LegacyDialog2011"; ((System.ComponentModel.ISupportInitialize)(this.IconBox)).EndInit(); this.ResumeLayout(false); @@ -100,9 +100,9 @@ private void InitializeComponent() #endregion - private Label Message; + private Label labelMessage; private ProgressBar ProgressBar; private PictureBox IconBox; - private Button ButtonCancel; + private Button buttonCancel; } } \ No newline at end of file diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2011.cs b/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2011.cs new file mode 100644 index 00000000..f0144b60 --- /dev/null +++ b/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2011.cs @@ -0,0 +1,45 @@ +using Bloxstrap.Helpers; + +namespace Bloxstrap.Dialogs.BootstrapperStyles +{ + // https://youtu.be/3K9oCEMHj2s?t=35 + + public partial class LegacyDialog2011 : BootstrapperStyleForm + { + public override string Message + { + get => labelMessage.Text; + set => labelMessage.Text = value; + } + + public override ProgressBarStyle ProgressStyle + { + get => ProgressBar.Style; + set => ProgressBar.Style = value; + } + + public override int ProgressValue + { + get => ProgressBar.Value; + set => ProgressBar.Value = value; + } + + public override bool CancelEnabled + { + get => this.buttonCancel.Enabled; + set => this.buttonCancel.Enabled = this.buttonCancel.Visible = value; + } + + public LegacyDialog2011(Bootstrapper? bootstrapper = null) + { + InitializeComponent(); + + Bootstrapper = bootstrapper; + + // have to convert icon -> bitmap since winforms scaling is poop + this.IconBox.Image = IconManager.GetIconResource().ToBitmap(); + + SetupDialog(); + } + } +} diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2011.resx b/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2011.resx new file mode 100644 index 00000000..f298a7be --- /dev/null +++ b/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2011.resx @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialog.Designer.cs b/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialog.Designer.cs index 91bd4533..e53820a0 100644 --- a/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialog.Designer.cs +++ b/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialog.Designer.cs @@ -29,12 +29,12 @@ protected override void Dispose(bool disposing) private void InitializeComponent() { this.ProgressBar = new System.Windows.Forms.ProgressBar(); - this.Message = new System.Windows.Forms.Label(); + this.labelMessage = new System.Windows.Forms.Label(); this.IconBox = new System.Windows.Forms.PictureBox(); - this.ButtonCancel = new System.Windows.Forms.PictureBox(); + this.buttonCancel = new System.Windows.Forms.PictureBox(); this.panel1 = new System.Windows.Forms.Panel(); ((System.ComponentModel.ISupportInitialize)(this.IconBox)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.ButtonCancel)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.buttonCancel)).BeginInit(); this.panel1.SuspendLayout(); this.SuspendLayout(); // @@ -48,16 +48,16 @@ private void InitializeComponent() this.ProgressBar.Style = System.Windows.Forms.ProgressBarStyle.Marquee; this.ProgressBar.TabIndex = 0; // - // Message + // labelMessage // - this.Message.Font = new System.Drawing.Font("Tahoma", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); - this.Message.Location = new System.Drawing.Point(29, 199); - this.Message.Name = "Message"; - this.Message.Size = new System.Drawing.Size(460, 18); - this.Message.TabIndex = 1; - this.Message.Text = "Please wait..."; - this.Message.TextAlign = System.Drawing.ContentAlignment.TopCenter; - this.Message.UseMnemonic = false; + this.labelMessage.Font = new System.Drawing.Font("Tahoma", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + this.labelMessage.Location = new System.Drawing.Point(29, 199); + this.labelMessage.Name = "labelMessage"; + this.labelMessage.Size = new System.Drawing.Size(460, 18); + this.labelMessage.TabIndex = 1; + this.labelMessage.Text = "Please wait..."; + this.labelMessage.TextAlign = System.Drawing.ContentAlignment.TopCenter; + this.labelMessage.UseMnemonic = false; // // IconBox // @@ -69,33 +69,33 @@ private void InitializeComponent() this.IconBox.TabIndex = 2; this.IconBox.TabStop = false; // - // ButtonCancel + // buttonCancel // - this.ButtonCancel.Enabled = false; - this.ButtonCancel.Image = global::Bloxstrap.Properties.Resources.CancelButton; - this.ButtonCancel.Location = new System.Drawing.Point(194, 264); - this.ButtonCancel.Name = "ButtonCancel"; - this.ButtonCancel.Size = new System.Drawing.Size(130, 44); - this.ButtonCancel.TabIndex = 3; - this.ButtonCancel.TabStop = false; - this.ButtonCancel.Visible = false; - this.ButtonCancel.Click += new System.EventHandler(this.ButtonCancel_Click); - this.ButtonCancel.MouseEnter += new System.EventHandler(this.ButtonCancel_MouseEnter); - this.ButtonCancel.MouseLeave += new System.EventHandler(this.ButtonCancel_MouseLeave); + this.buttonCancel.Enabled = false; + this.buttonCancel.Image = global::Bloxstrap.Properties.Resources.CancelButton; + this.buttonCancel.Location = new System.Drawing.Point(194, 264); + this.buttonCancel.Name = "buttonCancel"; + this.buttonCancel.Size = new System.Drawing.Size(130, 44); + this.buttonCancel.TabIndex = 3; + this.buttonCancel.TabStop = false; + this.buttonCancel.Visible = false; + this.buttonCancel.Click += new System.EventHandler(this.ButtonCancel_Click); + this.buttonCancel.MouseEnter += new System.EventHandler(this.ButtonCancel_MouseEnter); + this.buttonCancel.MouseLeave += new System.EventHandler(this.ButtonCancel_MouseLeave); // // panel1 // this.panel1.BackColor = System.Drawing.SystemColors.Window; - this.panel1.Controls.Add(this.Message); + this.panel1.Controls.Add(this.labelMessage); this.panel1.Controls.Add(this.IconBox); - this.panel1.Controls.Add(this.ButtonCancel); + this.panel1.Controls.Add(this.buttonCancel); this.panel1.Controls.Add(this.ProgressBar); this.panel1.Location = new System.Drawing.Point(1, 1); this.panel1.Name = "panel1"; this.panel1.Size = new System.Drawing.Size(518, 318); this.panel1.TabIndex = 4; // - // ProgressDialogStyle + // ProgressDialog // this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; @@ -105,11 +105,11 @@ private void InitializeComponent() this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; this.MaximumSize = new System.Drawing.Size(520, 320); this.MinimumSize = new System.Drawing.Size(520, 320); - this.Name = "ProgressDialogStyle"; + this.Name = "ProgressDialog"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "ProgressDialogStyle"; + this.Text = "ProgressDialog"; ((System.ComponentModel.ISupportInitialize)(this.IconBox)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.ButtonCancel)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.buttonCancel)).EndInit(); this.panel1.ResumeLayout(false); this.ResumeLayout(false); @@ -118,9 +118,9 @@ private void InitializeComponent() #endregion private ProgressBar ProgressBar; - private Label Message; + private Label labelMessage; private PictureBox IconBox; - private PictureBox ButtonCancel; + private PictureBox buttonCancel; private Panel panel1; } } \ No newline at end of file diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialog.cs b/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialog.cs index 70f21a7e..2dccbfca 100644 --- a/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialog.cs +++ b/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialog.cs @@ -1,172 +1,54 @@ -using System.Diagnostics; - -using Bloxstrap.Helpers; -using Bloxstrap.Helpers.RSMM; +using Bloxstrap.Helpers; namespace Bloxstrap.Dialogs.BootstrapperStyles { - // TODO - universal implementation for winforms-based styles? (to reduce duplicate code) + // basically just the modern dialog - public partial class ProgressDialog : Form + public partial class ProgressDialog : BootstrapperStyleForm { - private readonly Bootstrapper? Bootstrapper; - - public ProgressDialog(Bootstrapper? bootstrapper = null) - { - InitializeComponent(); - - Bootstrapper = bootstrapper; - - this.Text = Program.ProjectName; - this.Icon = IconManager.GetIconResource(); - this.IconBox.BackgroundImage = IconManager.GetBitmapResource(); - - if (Bootstrapper is null) - { - this.Message.Text = "Click the Cancel button to return to preferences"; - this.ButtonCancel.Enabled = true; - this.ButtonCancel.Visible = true; - } - else - { - Bootstrapper.CloseDialogEvent += new EventHandler(CloseDialog); - Bootstrapper.PromptShutdownEvent += new EventHandler(PromptShutdown); - Bootstrapper.ShowSuccessEvent += new ChangeEventHandler(ShowSuccess); - Bootstrapper.MessageChanged += new ChangeEventHandler(MessageChanged); - Bootstrapper.ProgressBarValueChanged += new ChangeEventHandler(ProgressBarValueChanged); - Bootstrapper.ProgressBarStyleChanged += new ChangeEventHandler(ProgressBarStyleChanged); - Bootstrapper.CancelEnabledChanged += new ChangeEventHandler(CancelEnabledChanged); - - Task.Run(() => RunBootstrapper()); - } - } - - public async void RunBootstrapper() - { - if (Bootstrapper is null) - return; - - try - { - await Bootstrapper.Run(); - } - catch (Exception ex) - { - // string message = String.Format("{0}: {1}", ex.GetType(), ex.Message); - string message = ex.ToString(); - ShowError(message); - - Program.Exit(); - } - } - - private void ShowError(string message) - { - MessageBox.Show( - $"An error occurred while starting Roblox\n\nDetails: {message}", - Program.ProjectName, - MessageBoxButtons.OK, - MessageBoxIcon.Error - ); - } - - private void ShowSuccess(object sender, ChangeEventArgs e) + public override string Message { - MessageBox.Show( - e.Value, - Program.ProjectName, - MessageBoxButtons.OK, - MessageBoxIcon.Information - ); + get => labelMessage.Text; + set => labelMessage.Text = value; } - private void CloseDialog(object? sender, EventArgs e) + public override ProgressBarStyle ProgressStyle { - this.Hide(); + get => ProgressBar.Style; + set => ProgressBar.Style = value; } - private void PromptShutdown(object? sender, EventArgs e) + public override int ProgressValue { - DialogResult result = MessageBox.Show( - "Roblox is currently running, but needs to close. Would you like close Roblox now?", - Program.ProjectName, - MessageBoxButtons.OKCancel, - MessageBoxIcon.Information - ); - - if (result != DialogResult.OK) - Environment.Exit(0); + get => ProgressBar.Value; + set => ProgressBar.Value = value; } - private void MessageChanged(object sender, ChangeEventArgs e) + public override bool CancelEnabled { - if (this.InvokeRequired) - { - ChangeEventHandler handler = new(MessageChanged); - this.Message.Invoke(handler, sender, e); - } - else - { - this.Message.Text = e.Value; - } + get => this.buttonCancel.Enabled; + set => this.buttonCancel.Enabled = this.buttonCancel.Visible = value; } - private void ProgressBarValueChanged(object sender, ChangeEventArgs e) + public ProgressDialog(Bootstrapper? bootstrapper = null) { - if (this.ProgressBar.InvokeRequired) - { - ChangeEventHandler handler = new(ProgressBarValueChanged); - this.ProgressBar.Invoke(handler, sender, e); - } - else - { - this.ProgressBar.Value = e.Value; - } - } + InitializeComponent(); - private void ProgressBarStyleChanged(object sender, ChangeEventArgs e) - { - if (this.ProgressBar.InvokeRequired) - { - ChangeEventHandler handler = new(ProgressBarStyleChanged); - this.ProgressBar.Invoke(handler, sender, e); - } - else - { - this.ProgressBar.Style = e.Value; - } - } + Bootstrapper = bootstrapper; - private void CancelEnabledChanged(object sender, ChangeEventArgs e) - { - if (this.ButtonCancel.InvokeRequired) - { - ChangeEventHandler handler = new(CancelEnabledChanged); - this.ButtonCancel.Invoke(handler, sender, e); - } - else - { - this.ButtonCancel.Enabled = e.Value; - this.ButtonCancel.Visible = e.Value; - } - } + this.IconBox.BackgroundImage = IconManager.GetBitmapResource(); - private void ButtonCancel_Click(object sender, EventArgs e) - { - if (Bootstrapper is null) - this.Close(); - else - Task.Run(() => Bootstrapper.CancelButtonClicked()); + SetupDialog(); } private void ButtonCancel_MouseEnter(object sender, EventArgs e) { - this.ButtonCancel.Image = Properties.Resources.CancelButtonHover; + this.buttonCancel.Image = Properties.Resources.CancelButtonHover; } private void ButtonCancel_MouseLeave(object sender, EventArgs e) { - this.ButtonCancel.Image = Properties.Resources.CancelButton; + this.buttonCancel.Image = Properties.Resources.CancelButton; } } } diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogDark.Designer.cs b/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogDark.Designer.cs new file mode 100644 index 00000000..59b989b3 --- /dev/null +++ b/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogDark.Designer.cs @@ -0,0 +1,127 @@ +namespace Bloxstrap.Dialogs.BootstrapperStyles +{ + partial class ProgressDialogDark + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.ProgressBar = new System.Windows.Forms.ProgressBar(); + this.labelMessage = new System.Windows.Forms.Label(); + this.IconBox = new System.Windows.Forms.PictureBox(); + this.buttonCancel = new System.Windows.Forms.PictureBox(); + this.panel1 = new System.Windows.Forms.Panel(); + ((System.ComponentModel.ISupportInitialize)(this.IconBox)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.buttonCancel)).BeginInit(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // ProgressBar + // + this.ProgressBar.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.ProgressBar.Location = new System.Drawing.Point(29, 241); + this.ProgressBar.MarqueeAnimationSpeed = 20; + this.ProgressBar.Name = "ProgressBar"; + this.ProgressBar.Size = new System.Drawing.Size(460, 20); + this.ProgressBar.Style = System.Windows.Forms.ProgressBarStyle.Marquee; + this.ProgressBar.TabIndex = 0; + // + // labelMessage + // + this.labelMessage.Font = new System.Drawing.Font("Tahoma", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + this.labelMessage.ForeColor = System.Drawing.SystemColors.Window; + this.labelMessage.Location = new System.Drawing.Point(29, 199); + this.labelMessage.Name = "labelMessage"; + this.labelMessage.Size = new System.Drawing.Size(460, 18); + this.labelMessage.TabIndex = 1; + this.labelMessage.Text = "Please wait..."; + this.labelMessage.TextAlign = System.Drawing.ContentAlignment.TopCenter; + this.labelMessage.UseMnemonic = false; + // + // IconBox + // + this.IconBox.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; + this.IconBox.ImageLocation = ""; + this.IconBox.Location = new System.Drawing.Point(212, 66); + this.IconBox.Name = "IconBox"; + this.IconBox.Size = new System.Drawing.Size(92, 92); + this.IconBox.TabIndex = 2; + this.IconBox.TabStop = false; + // + // buttonCancel + // + this.buttonCancel.Enabled = false; + this.buttonCancel.Image = global::Bloxstrap.Properties.Resources.DarkCancelButton; + this.buttonCancel.Location = new System.Drawing.Point(194, 264); + this.buttonCancel.Name = "buttonCancel"; + this.buttonCancel.Size = new System.Drawing.Size(130, 44); + this.buttonCancel.TabIndex = 3; + this.buttonCancel.TabStop = false; + this.buttonCancel.Visible = false; + this.buttonCancel.Click += new System.EventHandler(this.ButtonCancel_Click); + this.buttonCancel.MouseEnter += new System.EventHandler(this.ButtonCancel_MouseEnter); + this.buttonCancel.MouseLeave += new System.EventHandler(this.ButtonCancel_MouseLeave); + // + // panel1 + // + this.panel1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(35)))), ((int)(((byte)(37)))), ((int)(((byte)(39))))); + this.panel1.Controls.Add(this.labelMessage); + this.panel1.Controls.Add(this.IconBox); + this.panel1.Controls.Add(this.buttonCancel); + this.panel1.Controls.Add(this.ProgressBar); + this.panel1.Location = new System.Drawing.Point(1, 1); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(518, 318); + this.panel1.TabIndex = 4; + // + // ProgressDialogDark + // + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(27)))), ((int)(((byte)(29))))); + this.ClientSize = new System.Drawing.Size(520, 320); + this.Controls.Add(this.panel1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; + this.MaximumSize = new System.Drawing.Size(520, 320); + this.MinimumSize = new System.Drawing.Size(520, 320); + this.Name = "ProgressDialogDark"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "ProgressDialog"; + ((System.ComponentModel.ISupportInitialize)(this.IconBox)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.buttonCancel)).EndInit(); + this.panel1.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private ProgressBar ProgressBar; + private Label labelMessage; + private PictureBox IconBox; + private PictureBox buttonCancel; + private Panel panel1; + } +} \ No newline at end of file diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogDark.cs b/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogDark.cs new file mode 100644 index 00000000..76e3399c --- /dev/null +++ b/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogDark.cs @@ -0,0 +1,54 @@ +using Bloxstrap.Helpers; + +namespace Bloxstrap.Dialogs.BootstrapperStyles +{ + // basically just the modern dialog + + public partial class ProgressDialogDark : BootstrapperStyleForm + { + public override string Message + { + get => labelMessage.Text; + set => labelMessage.Text = value; + } + + public override ProgressBarStyle ProgressStyle + { + get => ProgressBar.Style; + set => ProgressBar.Style = value; + } + + public override int ProgressValue + { + get => ProgressBar.Value; + set => ProgressBar.Value = value; + } + + public override bool CancelEnabled + { + get => this.buttonCancel.Enabled; + set => this.buttonCancel.Enabled = this.buttonCancel.Visible = value; + } + + public ProgressDialogDark(Bootstrapper? bootstrapper = null) + { + InitializeComponent(); + + Bootstrapper = bootstrapper; + + this.IconBox.BackgroundImage = IconManager.GetBitmapResource(); + + SetupDialog(); + } + + private void ButtonCancel_MouseEnter(object sender, EventArgs e) + { + this.buttonCancel.Image = Properties.Resources.DarkCancelButtonHover; + } + + private void ButtonCancel_MouseLeave(object sender, EventArgs e) + { + this.buttonCancel.Image = Properties.Resources.DarkCancelButton; + } + } +} diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogDark.resx b/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogDark.resx new file mode 100644 index 00000000..f298a7be --- /dev/null +++ b/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogDark.resx @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/VistaDialog.Designer.cs b/Bloxstrap/Dialogs/BootstrapperStyles/VistaDialog.Designer.cs new file mode 100644 index 00000000..b11f2c8b --- /dev/null +++ b/Bloxstrap/Dialogs/BootstrapperStyles/VistaDialog.Designer.cs @@ -0,0 +1,49 @@ +namespace Bloxstrap.Dialogs.BootstrapperStyles +{ + partial class VistaDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.SuspendLayout(); + // + // TestDialog + // + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(0, 0); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; + this.Name = "VistaDialog"; + this.ShowInTaskbar = false; + this.Text = "VistaDialog"; + this.Load += new System.EventHandler(this.TestDialog_Load); + this.ResumeLayout(false); + + } + + #endregion + } +} \ No newline at end of file diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/VistaDialog.cs b/Bloxstrap/Dialogs/BootstrapperStyles/VistaDialog.cs index e65a3fe6..e36b065d 100644 --- a/Bloxstrap/Dialogs/BootstrapperStyles/VistaDialog.cs +++ b/Bloxstrap/Dialogs/BootstrapperStyles/VistaDialog.cs @@ -3,34 +3,71 @@ namespace Bloxstrap.Dialogs.BootstrapperStyles { - // example: https://youtu.be/h0_AL95Sc3o?t=48 + // https://youtu.be/h0_AL95Sc3o?t=48 - // this currently doesn't work because c# is stupid - // technically, task dialogs are treated as winforms controls, but they don't classify as winforms controls at all - // all winforms controls have the ability to be invoked from another thread, but task dialogs don't - // so we're just kind of stuck with this not working in multithreaded use - // (unless we want the bootstrapper to freeze during package extraction) + // a bit hacky, but this is actually a hidden form + // since taskdialog is part of winforms, it can't really be properly used without a form + // for example, cross-threaded calls to ui controls can't really be done outside of a form - // for now, just stick to legacydialog and progressdialog - - public class VistaDialog + public partial class VistaDialog : BootstrapperStyleForm { - private readonly Bootstrapper Bootstrapper; private TaskDialogPage Dialog; - public VistaDialog(Bootstrapper bootstrapper) + public override string Message + { + get => Dialog.Heading ?? ""; + set => Dialog.Heading = value; + } + + public override ProgressBarStyle ProgressStyle + { + set + { + if (Dialog.ProgressBar is null) + return; + + switch (value) + { + case ProgressBarStyle.Continuous: + case ProgressBarStyle.Blocks: + Dialog.ProgressBar.State = TaskDialogProgressBarState.Normal; + break; + + case ProgressBarStyle.Marquee: + Dialog.ProgressBar.State = TaskDialogProgressBarState.Marquee; + break; + } + } + } + + public override int ProgressValue + { + get => Dialog.ProgressBar is null ? 0 : Dialog.ProgressBar.Value; + set + { + if (Dialog.ProgressBar is null) + return; + + Dialog.ProgressBar.Value = value; + } + } + + public override bool CancelEnabled { + get => Dialog.Buttons[0].Enabled; + set => Dialog.Buttons[0].Enabled = value; + } + + public VistaDialog(Bootstrapper? bootstrapper = null) + { + InitializeComponent(); + Bootstrapper = bootstrapper; - Bootstrapper.ShowSuccessEvent += new ChangeEventHandler(ShowSuccess); - Bootstrapper.MessageChanged += new ChangeEventHandler(MessageChanged); - Bootstrapper.ProgressBarValueChanged += new ChangeEventHandler(ProgressBarValueChanged); - Bootstrapper.ProgressBarStyleChanged += new ChangeEventHandler(ProgressBarStyleChanged); Dialog = new TaskDialogPage() { Icon = new TaskDialogIcon(IconManager.GetIconResource()), Caption = Program.ProjectName, - Heading = "Please wait...", Buttons = { TaskDialogButton.Cancel }, ProgressBar = new TaskDialogProgressBar() @@ -39,92 +76,95 @@ public VistaDialog(Bootstrapper bootstrapper) } }; - Task.Run(() => RunBootstrapper()); - TaskDialog.ShowDialog(Dialog); + Message = "Please wait..."; + CancelEnabled = false; + + Dialog.Buttons[0].Click += (sender, e) => ButtonCancel_Click(sender, e); + + SetupDialog(); } - public async void RunBootstrapper() + public override void ShowSuccess(object sender, ChangeEventArgs e) { - try + if (this.InvokeRequired) { - await Bootstrapper.Run(); + ChangeEventHandler handler = new(ShowSuccess); + this.Invoke(handler, sender, e); } - catch (Exception ex) + else { - // string message = String.Format("{0}: {1}", ex.GetType(), ex.Message); - string message = ex.ToString(); - ShowError(message); + TaskDialogPage successDialog = new() + { + Icon = TaskDialogIcon.ShieldSuccessGreenBar, + Caption = Program.ProjectName, + Heading = e.Value, + Buttons = { TaskDialogButton.OK } + }; - Program.Exit(); + successDialog.Buttons[0].Click += (sender, e) => Program.Exit(); + + Dialog.Navigate(successDialog); + Dialog = successDialog; } } - public void ShowError(string message) + private void InvokeShowError(object sender, ChangeEventArgs e) { - TaskDialogPage errorDialog = new() - { - Icon = TaskDialogIcon.Error, - Caption = Program.ProjectName, - Heading = "An error occurred while starting Roblox", - Buttons = { TaskDialogButton.Close }, - Expander = new TaskDialogExpander() - { - Text = message, - CollapsedButtonText = "See details", - ExpandedButtonText = "Hide details", - Position = TaskDialogExpanderPosition.AfterText - } - }; - - Dialog.Navigate(errorDialog); - Dialog = errorDialog; + ShowError(e.Value); } - public void ShowSuccess(object sender, ChangeEventArgs e) + public override void ShowError(string message) { - TaskDialogPage successDialog = new() + if (this.InvokeRequired) { - Icon = TaskDialogIcon.ShieldSuccessGreenBar, - Caption = Program.ProjectName, - Heading = e.Value - }; - - Dialog.Navigate(successDialog); - Dialog = successDialog; + ChangeEventHandler handler = new(InvokeShowError); + this.Invoke(handler, this, new ChangeEventArgs(message)); + } + else + { + TaskDialogPage errorDialog = new() + { + Icon = TaskDialogIcon.Error, + Caption = Program.ProjectName, + Heading = "An error occurred while starting Roblox", + Buttons = { TaskDialogButton.Close }, + Expander = new TaskDialogExpander() + { + Text = message, + CollapsedButtonText = "See details", + ExpandedButtonText = "Hide details", + Position = TaskDialogExpanderPosition.AfterText + } + }; + + errorDialog.Buttons[0].Click += (sender, e) => Program.Exit(); + + Dialog.Navigate(errorDialog); + Dialog = errorDialog; + } } - private void MessageChanged(object sender, ChangeEventArgs e) + public override void CloseDialog(object? sender, EventArgs e) { - if (Dialog is null) - return; - - Dialog.Heading = e.Value; + if (this.InvokeRequired) + { + EventHandler handler = new(CloseDialog); + this.Invoke(handler, sender, e); + } + else + { + if (Dialog.BoundDialog is null) + return; + + Dialog.BoundDialog.Close(); + } } - private void ProgressBarValueChanged(object sender, ChangeEventArgs e) - { - if (Dialog is null || Dialog.ProgressBar is null) - return; - - Dialog.ProgressBar.Value = e.Value; - } - private void ProgressBarStyleChanged(object sender, ChangeEventArgs e) + private void TestDialog_Load(object sender, EventArgs e) { - if (Dialog is null || Dialog.ProgressBar is null) - return; - - switch (e.Value) - { - case ProgressBarStyle.Continuous: - case ProgressBarStyle.Blocks: - Dialog.ProgressBar.State = TaskDialogProgressBarState.Normal; - break; - - case ProgressBarStyle.Marquee: - Dialog.ProgressBar.State = TaskDialogProgressBarState.Marquee; - break; - } + this.Hide(); + TaskDialog.ShowDialog(Dialog); } } } diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/VistaDialog.resx b/Bloxstrap/Dialogs/BootstrapperStyles/VistaDialog.resx new file mode 100644 index 00000000..f298a7be --- /dev/null +++ b/Bloxstrap/Dialogs/BootstrapperStyles/VistaDialog.resx @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Bloxstrap/Dialogs/Preferences.Designer.cs b/Bloxstrap/Dialogs/Preferences.Designer.cs index fe111a66..b505db76 100644 --- a/Bloxstrap/Dialogs/Preferences.Designer.cs +++ b/Bloxstrap/Dialogs/Preferences.Designer.cs @@ -33,6 +33,7 @@ private void InitializeComponent() this.Tabs = new System.Windows.Forms.TabControl(); this.DialogTab = new System.Windows.Forms.TabPage(); this.groupBox5 = new System.Windows.Forms.GroupBox(); + this.ToggleRPCButtons = new System.Windows.Forms.CheckBox(); this.ToggleDiscordRichPresence = new System.Windows.Forms.CheckBox(); this.groupBox3 = new System.Windows.Forms.GroupBox(); this.IconPreview = new System.Windows.Forms.PictureBox(); @@ -41,6 +42,7 @@ private void InitializeComponent() this.StyleSelection = new System.Windows.Forms.ListBox(); this.InstallationTab = new System.Windows.Forms.TabPage(); this.groupBox4 = new System.Windows.Forms.GroupBox(); + this.ToggleMouseCursor = new System.Windows.Forms.CheckBox(); this.ToggleDeathSound = new System.Windows.Forms.CheckBox(); this.GroupBoxInstallLocation = new System.Windows.Forms.GroupBox(); this.InstallLocationBrowseButton = new System.Windows.Forms.Button(); @@ -79,7 +81,7 @@ private void InitializeComponent() this.Tabs.Location = new System.Drawing.Point(12, 40); this.Tabs.Name = "Tabs"; this.Tabs.SelectedIndex = 0; - this.Tabs.Size = new System.Drawing.Size(442, 226); + this.Tabs.Size = new System.Drawing.Size(442, 247); this.Tabs.TabIndex = 2; // // DialogTab @@ -90,20 +92,32 @@ private void InitializeComponent() this.DialogTab.Location = new System.Drawing.Point(4, 24); this.DialogTab.Name = "DialogTab"; this.DialogTab.Padding = new System.Windows.Forms.Padding(3); - this.DialogTab.Size = new System.Drawing.Size(434, 198); + this.DialogTab.Size = new System.Drawing.Size(434, 219); this.DialogTab.TabIndex = 0; this.DialogTab.Text = "Bootstrapper"; this.DialogTab.UseVisualStyleBackColor = true; // // groupBox5 // + this.groupBox5.Controls.Add(this.ToggleRPCButtons); this.groupBox5.Controls.Add(this.ToggleDiscordRichPresence); this.groupBox5.Location = new System.Drawing.Point(5, 146); this.groupBox5.Name = "groupBox5"; - this.groupBox5.Size = new System.Drawing.Size(422, 46); + this.groupBox5.Size = new System.Drawing.Size(422, 67); this.groupBox5.TabIndex = 7; this.groupBox5.TabStop = false; - this.groupBox5.Text = "Launch"; + this.groupBox5.Text = "Discord Rich Presence"; + // + // ToggleRPCButtons + // + this.ToggleRPCButtons.AutoSize = true; + this.ToggleRPCButtons.Location = new System.Drawing.Point(9, 40); + this.ToggleRPCButtons.Name = "ToggleRPCButtons"; + this.ToggleRPCButtons.Size = new System.Drawing.Size(196, 19); + this.ToggleRPCButtons.TabIndex = 1; + this.ToggleRPCButtons.Text = "Hide activity interaction buttons"; + this.ToggleRPCButtons.UseVisualStyleBackColor = true; + this.ToggleRPCButtons.CheckedChanged += new System.EventHandler(this.ToggleRPCButtons_CheckedChanged); // // ToggleDiscordRichPresence // @@ -112,9 +126,9 @@ private void InitializeComponent() this.ToggleDiscordRichPresence.CheckState = System.Windows.Forms.CheckState.Checked; this.ToggleDiscordRichPresence.Location = new System.Drawing.Point(9, 19); this.ToggleDiscordRichPresence.Name = "ToggleDiscordRichPresence"; - this.ToggleDiscordRichPresence.Size = new System.Drawing.Size(274, 19); + this.ToggleDiscordRichPresence.Size = new System.Drawing.Size(129, 19); this.ToggleDiscordRichPresence.TabIndex = 0; - this.ToggleDiscordRichPresence.Text = "Show game activity with Discord Rich Presence"; + this.ToggleDiscordRichPresence.Text = "Show game activity"; this.ToggleDiscordRichPresence.UseVisualStyleBackColor = true; this.ToggleDiscordRichPresence.CheckedChanged += new System.EventHandler(this.ToggleDiscordRichPresence_CheckedChanged); // @@ -176,21 +190,34 @@ private void InitializeComponent() this.InstallationTab.Location = new System.Drawing.Point(4, 24); this.InstallationTab.Name = "InstallationTab"; this.InstallationTab.Padding = new System.Windows.Forms.Padding(3); - this.InstallationTab.Size = new System.Drawing.Size(434, 198); + this.InstallationTab.Size = new System.Drawing.Size(434, 219); this.InstallationTab.TabIndex = 2; this.InstallationTab.Text = "Installation"; this.InstallationTab.UseVisualStyleBackColor = true; // // groupBox4 // + this.groupBox4.Controls.Add(this.ToggleMouseCursor); this.groupBox4.Controls.Add(this.ToggleDeathSound); this.groupBox4.Location = new System.Drawing.Point(5, 60); this.groupBox4.Name = "groupBox4"; - this.groupBox4.Size = new System.Drawing.Size(422, 46); + this.groupBox4.Size = new System.Drawing.Size(422, 65); this.groupBox4.TabIndex = 2; this.groupBox4.TabStop = false; this.groupBox4.Text = "Modifications"; // + // ToggleMouseCursor + // + this.ToggleMouseCursor.AutoSize = true; + this.ToggleMouseCursor.Location = new System.Drawing.Point(9, 40); + this.ToggleMouseCursor.Margin = new System.Windows.Forms.Padding(2); + this.ToggleMouseCursor.Name = "ToggleMouseCursor"; + this.ToggleMouseCursor.Size = new System.Drawing.Size(140, 19); + this.ToggleMouseCursor.TabIndex = 2; + this.ToggleMouseCursor.Text = "Use old mouse cursor"; + this.ToggleMouseCursor.UseVisualStyleBackColor = true; + this.ToggleMouseCursor.CheckedChanged += new System.EventHandler(this.ToggleMouseCursor_CheckedChanged); + // // ToggleDeathSound // this.ToggleDeathSound.AutoSize = true; @@ -214,7 +241,7 @@ private void InitializeComponent() this.GroupBoxInstallLocation.Size = new System.Drawing.Size(422, 54); this.GroupBoxInstallLocation.TabIndex = 0; this.GroupBoxInstallLocation.TabStop = false; - this.GroupBoxInstallLocation.Text = "Install Location"; + this.GroupBoxInstallLocation.Text = "Location"; // // InstallLocationBrowseButton // @@ -252,10 +279,9 @@ private void InitializeComponent() this.panel1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.panel1.BackColor = System.Drawing.SystemColors.Control; - this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.panel1.Controls.Add(this.PreviewButton); this.panel1.Controls.Add(this.SaveButton); - this.panel1.Location = new System.Drawing.Point(-1, 277); + this.panel1.Location = new System.Drawing.Point(-1, 298); this.panel1.Name = "panel1"; this.panel1.Size = new System.Drawing.Size(466, 42); this.panel1.TabIndex = 6; @@ -282,7 +308,7 @@ private void InitializeComponent() this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BackColor = System.Drawing.SystemColors.Window; - this.ClientSize = new System.Drawing.Size(464, 318); + this.ClientSize = new System.Drawing.Size(464, 339); this.Controls.Add(this.panel1); this.Controls.Add(this.Tabs); this.Controls.Add(this.label1); @@ -332,5 +358,7 @@ private void InitializeComponent() private GroupBox groupBox5; private CheckBox ToggleDiscordRichPresence; private ToolTip InfoTooltip; + private CheckBox ToggleMouseCursor; + private CheckBox ToggleRPCButtons; } } \ No newline at end of file diff --git a/Bloxstrap/Dialogs/Preferences.cs b/Bloxstrap/Dialogs/Preferences.cs index 5c35e2cd..e4577f5e 100644 --- a/Bloxstrap/Dialogs/Preferences.cs +++ b/Bloxstrap/Dialogs/Preferences.cs @@ -10,8 +10,11 @@ public partial class Preferences : Form { private static readonly IReadOnlyDictionary SelectableStyles = new Dictionary() { - { "Legacy (2011 - 2014)", BootstrapperStyle.LegacyDialog }, + { "Vista (2009 - 2011)", BootstrapperStyle.VistaDialog }, + { "Legacy (2009 - 2011)", BootstrapperStyle.LegacyDialog2009 }, + { "Legacy (2011 - 2014)", BootstrapperStyle.LegacyDialog2011 }, { "Progress (~2014)", BootstrapperStyle.ProgressDialog }, + { "Progress (Dark)", BootstrapperStyle.ProgressDialogDark }, }; private static readonly IReadOnlyDictionary SelectableIcons = new Dictionary() @@ -28,7 +31,9 @@ public partial class Preferences : Form private BootstrapperStyle? _selectedStyle; private BootstrapperIcon? _selectedIcon; private bool _useDiscordRichPresence = true; + private bool _hideRPCButtons = false; private bool _useOldDeathSound = true; + private bool _useOldMouseCursor = false; private BootstrapperStyle SelectedStyle { @@ -75,6 +80,22 @@ private bool UseDiscordRichPresence _useDiscordRichPresence = value; this.ToggleDiscordRichPresence.Checked = value; + this.ToggleRPCButtons.Enabled = value; + } + } + + private bool HideRPCButtons + { + get => _hideRPCButtons; + + set + { + if (_hideRPCButtons == value) + return; + + _hideRPCButtons = value; + + this.ToggleRPCButtons.Checked = value; } } @@ -93,6 +114,21 @@ private bool UseOldDeathSound } } + private bool UseOldMouseCursor + { + get => _useOldMouseCursor; + + set + { + if (_useOldMouseCursor == value) + return; + + _useOldMouseCursor = value; + + this.ToggleMouseCursor.Checked = value; + } + } + public Preferences() { InitializeComponent(); @@ -122,15 +158,18 @@ public Preferences() this.IconSelection.Items.Add(icon.Key); } - this.InfoTooltip.SetToolTip(this.StyleSelection, "Choose how the bootstrapper dialog should look."); + this.InfoTooltip.SetToolTip(this.StyleSelection, "Choose how the bootstrapper dialog should look.\nYou can use the 'Preview' button to preview the bootstrapper look."); this.InfoTooltip.SetToolTip(this.IconSelection, "Choose what icon the bootstrapper should use."); this.InfoTooltip.SetToolTip(this.GroupBoxInstallLocation, "Choose where Bloxstrap should install to.\nThis is useful if you typically install all your games to a separate storage drive."); - this.InfoTooltip.SetToolTip(this.ToggleDiscordRichPresence, "Choose whether to show what game you're playing on your Discord profile.\nThis will ONLY work when you launch a game from the website, and is not supported in the Beta App."); + this.InfoTooltip.SetToolTip(this.ToggleDiscordRichPresence, "Choose whether to show what game you're playing on your Discord activity status.\nThis will only work when you launch a game from the website, and is not supported in the Beta App."); + this.InfoTooltip.SetToolTip(this.ToggleRPCButtons, "Choose whether the buttons to play/view game info should be hidden from activity status."); SelectedStyle = Program.Settings.BootstrapperStyle; SelectedIcon = Program.Settings.BootstrapperIcon; UseDiscordRichPresence = Program.Settings.UseDiscordRichPresence; + HideRPCButtons = Program.Settings.HideRPCButtons; UseOldDeathSound = Program.Settings.UseOldDeathSound; + UseOldMouseCursor = Program.Settings.UseOldMouseCursor; } private void InstallLocationBrowseButton_Click(object sender, EventArgs e) @@ -196,32 +235,37 @@ private void SaveButton_Click(object sender, EventArgs e) // this will be set in the registry after first install Program.BaseDirectory = installLocation; } - else if (Program.BaseDirectory != installLocation) + else { - Program.ShowMessageBox(MessageBoxIcon.Information, $"{Program.ProjectName} will install to the new location you've set the next time it runs."); - - Program.Settings.VersionGuid = ""; + Program.SettingsManager.ShouldSave = true; - using (RegistryKey registryKey = Registry.CurrentUser.CreateSubKey($@"Software\{Program.ProjectName}")) + if (Program.BaseDirectory != installLocation) { - registryKey.SetValue("InstallLocation", installLocation); - registryKey.SetValue("OldInstallLocation", Program.BaseDirectory); - } + Program.ShowMessageBox(MessageBoxIcon.Information, $"{Program.ProjectName} will install to the new location you've set the next time it runs."); - // preserve settings - // we don't need to copy the bootstrapper over since the install process will do that automatically + Program.Settings.VersionGuid = ""; - Program.SettingsManager.Save(); - File.Copy(Path.Combine(Program.BaseDirectory, "Settings.json"), Path.Combine(installLocation, "Settings.json")); - } + using (RegistryKey registryKey = Registry.CurrentUser.CreateSubKey($@"Software\{Program.ProjectName}")) + { + registryKey.SetValue("InstallLocation", installLocation); + registryKey.SetValue("OldInstallLocation", Program.BaseDirectory); + } - if (!Program.IsFirstRun) - Program.SettingsManager.ShouldSave = true; + // preserve settings + // we don't need to copy the bootstrapper over since the install process will do that automatically + + Program.SettingsManager.Save(); + + File.Copy(Path.Combine(Program.BaseDirectory, "Settings.json"), Path.Combine(installLocation, "Settings.json")); + } + } Program.Settings.BootstrapperStyle = SelectedStyle; Program.Settings.BootstrapperIcon = SelectedIcon; Program.Settings.UseDiscordRichPresence = UseDiscordRichPresence; + Program.Settings.HideRPCButtons = HideRPCButtons; Program.Settings.UseOldDeathSound = UseOldDeathSound; + Program.Settings.UseOldMouseCursor = UseOldMouseCursor; this.Close(); } @@ -236,13 +280,25 @@ private void PreviewButton_Click(object sender, EventArgs e) switch (SelectedStyle) { - case BootstrapperStyle.LegacyDialog: - new LegacyDialog().ShowDialog(); + case BootstrapperStyle.VistaDialog: + new VistaDialog().ShowDialog(); + break; + + case BootstrapperStyle.LegacyDialog2009: + new LegacyDialog2009().ShowDialog(); + break; + + case BootstrapperStyle.LegacyDialog2011: + new LegacyDialog2011().ShowDialog(); break; case BootstrapperStyle.ProgressDialog: new ProgressDialog().ShowDialog(); break; + + case BootstrapperStyle.ProgressDialogDark: + new ProgressDialogDark().ShowDialog(); + break; } Program.Settings.BootstrapperIcon = savedIcon; @@ -255,9 +311,19 @@ private void ToggleDiscordRichPresence_CheckedChanged(object sender, EventArgs e UseDiscordRichPresence = this.ToggleDiscordRichPresence.Checked; } + private void ToggleRPCButtons_CheckedChanged(object sender, EventArgs e) + { + HideRPCButtons = this.ToggleRPCButtons.Checked; + } + private void ToggleDeathSound_CheckedChanged(object sender, EventArgs e) { UseOldDeathSound = this.ToggleDeathSound.Checked; } + + private void ToggleMouseCursor_CheckedChanged(object sender, EventArgs e) + { + UseOldMouseCursor = this.ToggleMouseCursor.Checked; + } } } diff --git a/Bloxstrap/Enums/BootstrapperStyle.cs b/Bloxstrap/Enums/BootstrapperStyle.cs index fd0a9fd8..5f30c4b4 100644 --- a/Bloxstrap/Enums/BootstrapperStyle.cs +++ b/Bloxstrap/Enums/BootstrapperStyle.cs @@ -3,7 +3,9 @@ public enum BootstrapperStyle { VistaDialog, - LegacyDialog, - ProgressDialog + LegacyDialog2009, + LegacyDialog2011, + ProgressDialog, + ProgressDialogDark, } } diff --git a/Bloxstrap/Helpers/DiscordRichPresence.cs b/Bloxstrap/Helpers/DiscordRichPresence.cs index 8df615dc..e57fe531 100644 --- a/Bloxstrap/Helpers/DiscordRichPresence.cs +++ b/Bloxstrap/Helpers/DiscordRichPresence.cs @@ -30,6 +30,26 @@ public async Task SetPresence(string placeId) placeThumbnail = thumbnailInfo["data"][0]["imageUrl"].Value(); } + DiscordRPC.Button[]? buttons = null; + + if (!Program.Settings.HideRPCButtons) + { + buttons = new DiscordRPC.Button[] + { + new DiscordRPC.Button() + { + Label = "Play", + Url = $"https://www.roblox.com/games/start?placeId={placeId}&launchData=%7B%7D" + }, + + new DiscordRPC.Button() + { + Label = "View Details", + Url = $"https://www.roblox.com/games/{placeId}" + } + }; + } + RichPresence.Initialize(); RichPresence.SetPresence(new RichPresence() @@ -46,20 +66,7 @@ public async Task SetPresence(string placeId) SmallImageText = "Rich Presence provided by Bloxstrap" }, - Buttons = new DiscordRPC.Button[] - { - new DiscordRPC.Button() - { - Label = "Play", - Url = $"https://www.roblox.com/games/start?placeId={placeId}&launchData=%7B%7D" - }, - - new DiscordRPC.Button() - { - Label = "View Details", - Url = $"https://www.roblox.com/games/{placeId}" - } - } + Buttons = buttons }); return true; diff --git a/Bloxstrap/Helpers/UpdateChecker.cs b/Bloxstrap/Helpers/UpdateChecker.cs index 291ac89d..e02be98a 100644 --- a/Bloxstrap/Helpers/UpdateChecker.cs +++ b/Bloxstrap/Helpers/UpdateChecker.cs @@ -17,7 +17,7 @@ public static void CheckInstalledVersion() if (installedVersionInfo.ProductVersion != currentVersionInfo.ProductVersion) { DialogResult result = MessageBox.Show( - $"The version of {Program.ProjectName} you've launched is newer than the version you currently have installed.\nWould you like to update your installed version of {Program.ProjectName}?", + $"The version of {Program.ProjectName} you've launched is newer than the version you currently have installed.\nWould you like to update your currently installed version?", Program.ProjectName, MessageBoxButtons.YesNo, MessageBoxIcon.Question @@ -59,7 +59,7 @@ public static async Task Check() if (currentVersion != latestVersion) { DialogResult result = MessageBox.Show( - $"A new version of {Program.ProjectName} is available\n\nRelease notes:\n{releaseNotes}\n\nDo you want to download {latestVersion}?", + $"A new version of {Program.ProjectName} is available\n\n[{latestVersion}]\n{releaseNotes}\n\nWould you like to download it?", Program.ProjectName, MessageBoxButtons.YesNo, MessageBoxIcon.Question diff --git a/Bloxstrap/Properties/Resources.Designer.cs b/Bloxstrap/Properties/Resources.Designer.cs index 9c57c694..a95d9ea6 100644 --- a/Bloxstrap/Properties/Resources.Designer.cs +++ b/Bloxstrap/Properties/Resources.Designer.cs @@ -80,6 +80,26 @@ internal static System.Drawing.Bitmap CancelButtonHover { } } + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap DarkCancelButton { + get { + object obj = ResourceManager.GetObject("DarkCancelButton", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap DarkCancelButtonHover { + get { + object obj = ResourceManager.GetObject("DarkCancelButtonHover", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). /// diff --git a/Bloxstrap/Properties/Resources.resx b/Bloxstrap/Properties/Resources.resx index 924339dc..059cfaca 100644 --- a/Bloxstrap/Properties/Resources.resx +++ b/Bloxstrap/Properties/Resources.resx @@ -124,6 +124,12 @@ ..\Resources\CancelButtonHover.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Resources\DarkCancelButton.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\DarkCancelButtonHover.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Resources\Icon2009-ico.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a diff --git a/Bloxstrap/Resources/DarkCancelButton.png b/Bloxstrap/Resources/DarkCancelButton.png new file mode 100644 index 0000000000000000000000000000000000000000..4a75277616bda582438a1b4e58d1a23dfc124252 GIT binary patch literal 1134 zcmV-!1d;oRP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1OrJ#K~#8N?VD>) z6G0S){V&G&C%k>pVB-BkRK%cY1o3{2qDD}O7f=y3ic-8r5TytL+KT?PvCo0iOxI~? zS7W>Ne0b9C%-Pvy-gnQO-R>?~x@@^K3^Rfum=O%Yj9>_61Vbpx27bY8yV=H~bl zONt4erj}UNH!hTkTX^E@p(7e4d<=iDOv^_3v*z zSYU!(d-l4g&j$0$2Qc}x`}+;0QqspAHVl@@W?eS>-HnWny6rm~3u57B&n;P&_$|bS z#^kqr{4_4(0N-izW6TIKIDO`u6E z8lLaVWgp_$N4HxJv`P%OZDsk&fteUXV|>xg2r;;I`;JT+lNahQM)SUwf^D7r{)Yp< zwIP_AOvFUI98~tS`Nr}y0Ke_V&7So=3e*%s;#(Rc6ue2UKP zH#_?^zcfHOMu-ZfXL>2&^vo|s)L3SO3fQ=LYhG09g;0!NJCM$aji&MACr(Lw93ya+ zmpVRZEki87A%tawKSY-Y#EsrOLhKJ?AKl>d7qWfg}Lb$tG^ zZ2<&+(EqUDOy%qskrrYQ4^j9Y#|Xg9E5ujh0riJw1cl@HH3_gE`)iCD;V+?o+VC0b zS9a}gS~R9oFkHFE_UnRLB=JTt{tjjYL#W6I_V8+bQt`v9_BeZeQt{*L_JT`&Qt=Be zfOG7XxB4O0D{qRu1X~}VVeFS+6!rFa$G#kz=|01<5StnoXi|!vFvP07*qoM6N<$f}3v~ AYybcN literal 0 HcmV?d00001 diff --git a/Bloxstrap/Resources/DarkCancelButtonHover.png b/Bloxstrap/Resources/DarkCancelButtonHover.png new file mode 100644 index 0000000000000000000000000000000000000000..4686f0d795ea1e40d1d0f709ba6801f6ff58c704 GIT binary patch literal 1175 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 delta 15536 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 Ka%D26f&T+0fZ+`Q diff --git a/Bloxstrap/Settings.cs b/Bloxstrap/Settings.cs index 83064fca..4d4f573c 100644 --- a/Bloxstrap/Settings.cs +++ b/Bloxstrap/Settings.cs @@ -11,13 +11,16 @@ public class SettingsFormat public BootstrapperStyle BootstrapperStyle { get; set; } = BootstrapperStyle.ProgressDialog; public BootstrapperIcon BootstrapperIcon { get; set; } = BootstrapperIcon.IconBloxstrap; public bool UseDiscordRichPresence { get; set; } = true; + public bool HideRPCButtons { get; set; } = false; public bool UseOldDeathSound { get; set; } = true; + public bool UseOldMouseCursor { get; set; } = false; } public class SettingsManager { public SettingsFormat Settings = new(); public bool ShouldSave = false; + private bool IsSaving = false; private string _saveLocation; public string SaveLocation @@ -52,6 +55,17 @@ public string SaveLocation public void Save() { + if (IsSaving) + { + // sometimes Save() is called at the same time from both Main() and Exit(), + // so this is here to avoid the program exiting before saving + + Thread.Sleep(1000); + return; + } + + IsSaving = true; + Debug.WriteLine("Attempting to save..."); string SettingsJson = JsonSerializer.Serialize(Settings, new JsonSerializerOptions { WriteIndented = true }); @@ -65,6 +79,8 @@ public void Save() // save settings File.WriteAllText(SaveLocation, SettingsJson); + + IsSaving = false; } } } diff --git a/README.md b/README.md index 6ad8a2c0..d556d849 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,10 @@ Please keep in mind that **Bloxstrap is in very early development**, and you'll ## Features Here's some of the features that Bloxstrap provides over the stock Roblox bootstrapper: -* Bootstrapper style can be customized (including being able to emulate older version looks) -* Ability to choose where to install Roblox to -* Old death sound can be re-enabled permanently -* Support for Discord Rich Presence +* Old death sound and mouse cursor can be permanently re-enabled +* Lets you choose where to install Roblox to +* Integration with Discord Rich Presence +* Custom bootstrapper styles (includes old versions and dark theme) ## Installing Bloxstrap requires the [x86 .NET 6 Desktop Runtime](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-6.0.7-windows-x86-installer). If you don't already have it installed, you'll be prompted to install it when running Bloxstrap.