diff --git a/ADB.cs b/ADB.cs index 3c080b3..f00c6c3 100644 --- a/ADB.cs +++ b/ADB.cs @@ -305,7 +305,7 @@ namespace AndroidSideloader _ = ADB.RunAdbCommandToString($"pull \"/sdcard/Android/data/{MainForm.CurrPCKG}\" \"{Environment.CurrentDirectory}\""); Program.form.changeTitle("Uninstalling game..."); _ = Sideloader.UninstallGame(MainForm.CurrPCKG); - Program.form.changeTitle("Reinstalling Game"); + Program.form.changeTitle("Reinstalling game..."); ret += ADB.RunAdbCommandToString($"install -g \"{path}\""); _ = ADB.RunAdbCommandToString($"push \"{Environment.CurrentDirectory}\\{MainForm.CurrPCKG}\" /sdcard/Android/data/"); string directoryToDelete = Path.Combine(Environment.CurrentDirectory, MainForm.CurrPCKG); @@ -317,7 +317,7 @@ namespace AndroidSideloader } } - Program.form.changeTitle(" \n\n"); + Program.form.changeTitle(""); return ret; } } diff --git a/AndroidSideloader.csproj b/AndroidSideloader.csproj index 406308c..f18d336 100644 --- a/AndroidSideloader.csproj +++ b/AndroidSideloader.csproj @@ -155,7 +155,7 @@ .\SergeUtils.dll - C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.2\System.dll + ..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.2\System.dll @@ -172,7 +172,7 @@ - C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.2\System.Drawing.dll + ..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.2\System.Drawing.dll @@ -211,6 +211,9 @@ NewApps.cs + + Component + Form diff --git a/App.config b/App.config index 519115b..32efb8b 100644 --- a/App.config +++ b/App.config @@ -33,7 +33,7 @@ True - Microsoft Sans Serif, 9pt + Microsoft Sans Serif, 10pt @@ -135,13 +135,13 @@ False - DarkGray + White 25, 25, 25 - 25, 25, 25 + 42, 45, 58 25, 25, 25 diff --git a/FlexibleMessageBox.cs b/FlexibleMessageBox.cs index 587e65a..299d746 100644 --- a/FlexibleMessageBox.cs +++ b/FlexibleMessageBox.cs @@ -1,246 +1,72 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.Drawing; +using System.Drawing.Drawing2D; using System.Globalization; using System.Linq; +using System.Runtime.InteropServices; using System.Windows.Forms; namespace JR.Utils.GUI.Forms { - /* FlexibleMessageBox – A flexible replacement for the .NET MessageBox - * - * Author: Jörg Reichert (public@jreichert.de) - * Contributors: Thanks to: David Hall, Roink - * Version: 1.3 - * Published at: http://www.codeproject.com/Articles/601900/FlexibleMessageBox - * - ************************************************************************************************************ - * Features: - * - It can be simply used instead of MessageBox since all important static "Show"-Functions are supported - * - It is small, only one source file, which could be added easily to each solution - * - It can be resized and the content is correctly word-wrapped - * - It tries to auto-size the width to show the longest text row - * - It never exceeds the current desktop working area - * - It displays a vertical scrollbar when needed - * - It does support hyperlinks in text - * - * Because the interface is identical to MessageBox, you can add this single source file to your project - * and use the FlexibleMessageBox almost everywhere you use a standard MessageBox. - * The goal was NOT to produce as many features as possible but to provide a simple replacement to fit my - * own needs. Feel free to add additional features on your own, but please left my credits in this class. - * - ************************************************************************************************************ - * Usage examples: - * - * FlexibleMessageBox.Show("Just a text"); - * - * FlexibleMessageBox.Show("A text", - * "A caption"); - * - * FlexibleMessageBox.Show("Some text with a link: www.google.com", - * "Some caption", - * MessageBoxButton - * - * - * - * - * - * s.AbortRetryIgnore, - * MessageBoxIcon.Information, - * MessageBoxDefaultButton.Button2); - * - * var dialogResult = FlexibleMessageBox.Show("Do you know the answer to life the universe and everything?", - * "One short question", - * MessageBoxButtons.YesNo); - * - ************************************************************************************************************ - * THE SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS", WITHOUT WARRANTY - * OF ANY KIND, EXPRESS OR IMPLIED. IN NO EVENT SHALL THE AUTHOR BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OF THIS - * SOFTWARE. - * - ************************************************************************************************************ - * History: - * Version 1.3 - 19.Dezember 2014 - * - Added refactoring function GetButtonText() - * - Used CurrentUICulture instead of InstalledUICulture - * - Added more button localizations. Supported languages are now: ENGLISH, GERMAN, SPANISH, ITALIAN - * - Added standard MessageBox handling for "copy to clipboard" with + and + - * - Tab handling is now corrected (only tabbing over the visible buttons) - * - Added standard MessageBox handling for ALT-Keyboard shortcuts - * - SetDialogSizes: Refactored completely: Corrected sizing and added caption driven sizing - * - * Version 1.2 - 10.August 2013 - * - Do not ShowInTaskbar anymore (original MessageBox is also hidden in taskbar) - * - Added handling for Escape-Button - * - Adapted top right close button (red X) to behave like MessageBox (but hidden instead of deactivated) - * - * Version 1.1 - 14.June 2013 - * - Some Refactoring - * - Added internal form class - * - Added missing code comments, etc. - * - * Version 1.0 - 15.April 2013 - * - Initial Version - */ public class FlexibleMessageBox { #region Public statics - /// - /// Defines the maximum width for all FlexibleMessageBox instances in percent of the working area. - /// - /// Allowed values are 0.2 - 1.0 where: - /// 0.2 means: The FlexibleMessageBox can be at most half as wide as the working area. - /// 1.0 means: The FlexibleMessageBox can be as wide as the working area. - /// - /// Default is: 70% of the working area width. - /// public static double MAX_WIDTH_FACTOR = 0.7; - - /// - /// Defines the maximum height for all FlexibleMessageBox instances in percent of the working area. - /// - /// Allowed values are 0.2 - 1.0 where: - /// 0.2 means: The FlexibleMessageBox can be at most half as high as the working area. - /// 1.0 means: The FlexibleMessageBox can be as high as the working area. - /// - /// Default is: 90% of the working area height. - /// public static double MAX_HEIGHT_FACTOR = 0.9; - - /// - /// Defines the font for all FlexibleMessageBox instances. - /// - /// Default is: SystemFonts.MessageBoxFont - /// public static Font FONT = SystemFonts.MessageBoxFont; #endregion #region Public show functions - /// - /// Shows the specified message box. - /// - /// The text. - /// The dialog result. public static DialogResult Show(string text) { return FlexibleMessageBoxForm.Show(null, text, string.Empty, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1); } - /// - /// Shows the specified message box. - /// - /// The owner. - /// The text. - /// The dialog result. public static DialogResult Show(IWin32Window owner, string text) { return FlexibleMessageBoxForm.Show(owner, text, string.Empty, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1); } - /// - /// Shows the specified message box. - /// - /// The text. - /// The caption. - /// The dialog result. public static DialogResult Show(string text, string caption) { return FlexibleMessageBoxForm.Show(null, text, caption, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1); } - /// - /// Shows the specified message box. - /// - /// The owner. - /// The text. - /// The caption. - /// The dialog result. public static DialogResult Show(IWin32Window owner, string text, string caption) { return FlexibleMessageBoxForm.Show(owner, text, caption, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1); } - /// - /// Shows the specified message box. - /// - /// The text. - /// The caption. - /// The buttons. - /// The dialog result. public static DialogResult Show(string text, string caption, MessageBoxButtons buttons) { return FlexibleMessageBoxForm.Show(null, text, caption, buttons, MessageBoxIcon.None, MessageBoxDefaultButton.Button1); } - /// - /// Shows the specified message box. - /// - /// The owner. - /// The text. - /// The caption. - /// The buttons. - /// The dialog result. public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons) { return FlexibleMessageBoxForm.Show(owner, text, caption, buttons, MessageBoxIcon.None, MessageBoxDefaultButton.Button1); } - /// - /// Shows the specified message box. - /// - /// The text. - /// The caption. - /// The buttons. - /// The icon. - /// public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon) { return FlexibleMessageBoxForm.Show(null, text, caption, buttons, icon, MessageBoxDefaultButton.Button1); } - /// - /// Shows the specified message box. - /// - /// The owner. - /// The text. - /// The caption. - /// The buttons. - /// The icon. - /// The dialog result. public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon) { return FlexibleMessageBoxForm.Show(owner, text, caption, buttons, icon, MessageBoxDefaultButton.Button1); } - /// - /// Shows the specified message box. - /// - /// The text. - /// The caption. - /// The buttons. - /// The icon. - /// The default button. - /// The dialog result. public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton) { return FlexibleMessageBoxForm.Show(null, text, caption, buttons, icon, defaultButton); } - /// - /// Shows the specified message box. - /// - /// The owner. - /// The text. - /// The caption. - /// The buttons. - /// The icon. - /// The default button. - /// The dialog result. public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton) { return FlexibleMessageBoxForm.Show(owner, text, caption, buttons, icon, defaultButton); @@ -250,23 +76,37 @@ namespace JR.Utils.GUI.Forms #region Internal form class - /// - /// The form to show the customized message box. - /// It is defined as an internal class to keep the public interface of the FlexibleMessageBox clean. - /// private class FlexibleMessageBoxForm : Form { + #region Constants and P/Invoke + + private const int CS_DROPSHADOW = 0x00020000; + private const int WM_NCLBUTTONDOWN = 0xA1; + private const int HT_CAPTION = 0x2; + private const int BORDER_RADIUS = 12; + + [DllImport("user32.dll")] + private static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam); + + [DllImport("user32.dll")] + private static extern bool ReleaseCapture(); + + protected override CreateParams CreateParams + { + get + { + CreateParams cp = base.CreateParams; + cp.ClassStyle |= CS_DROPSHADOW; + return cp; + } + } + + #endregion + #region Form-Designer generated code - /// - /// Erforderliche Designervariable. - /// private System.ComponentModel.IContainer components = null; - /// - /// Verwendete Ressourcen bereinigen. - /// - /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. protected override void Dispose(bool disposing) { if (disposing && (components != null)) @@ -276,10 +116,21 @@ namespace JR.Utils.GUI.Forms base.Dispose(disposing); } - /// - /// Erforderliche Methode für die Designerunterstützung. - /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. - /// + private void CloseButton_Click(object sender, EventArgs e) + { + this.DialogResult = DialogResult.Cancel; + this.Close(); + } + + private void TitlePanel_MouseDown(object sender, MouseEventArgs e) + { + if (e.Button == MouseButtons.Left) + { + ReleaseCapture(); + SendMessage(this.Handle, WM_NCLBUTTONDOWN, (IntPtr)HT_CAPTION, IntPtr.Zero); + } + } + private void InitializeComponent() { components = new System.ComponentModel.Container(); @@ -290,23 +141,75 @@ namespace JR.Utils.GUI.Forms pictureBoxForIcon = new System.Windows.Forms.PictureBox(); button2 = new System.Windows.Forms.Button(); button3 = new System.Windows.Forms.Button(); + titlePanel = new System.Windows.Forms.Panel(); + titleLabel = new System.Windows.Forms.Label(); + closeButton = new System.Windows.Forms.Button(); ((System.ComponentModel.ISupportInitialize)FlexibleMessageBoxFormBindingSource).BeginInit(); panel1.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)pictureBoxForIcon).BeginInit(); + titlePanel.SuspendLayout(); SuspendLayout(); // + // titlePanel + // + titlePanel.BackColor = System.Drawing.Color.FromArgb(20, 24, 29); + titlePanel.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; + titlePanel.Location = new System.Drawing.Point(6, 6); + titlePanel.Name = "titlePanel"; + titlePanel.Size = new System.Drawing.Size(248, 28); + titlePanel.TabIndex = 10; + titlePanel.Controls.Add(closeButton); + titlePanel.Controls.Add(titleLabel); + titlePanel.MouseDown += TitlePanel_MouseDown; + // + // titleLabel + // + titleLabel.AutoSize = false; + titleLabel.Dock = System.Windows.Forms.DockStyle.Fill; + titleLabel.ForeColor = System.Drawing.Color.White; + titleLabel.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold); + titleLabel.Location = new System.Drawing.Point(0, 0); + titleLabel.Name = "titleLabel"; + titleLabel.Padding = new System.Windows.Forms.Padding(18, 0, 0, 0); + titleLabel.Size = new System.Drawing.Size(218, 28); + titleLabel.TabIndex = 0; + titleLabel.Text = ""; + titleLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + titleLabel.MouseDown += TitlePanel_MouseDown; + // + // closeButton + // + closeButton.Dock = System.Windows.Forms.DockStyle.Right; + closeButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + closeButton.FlatAppearance.BorderSize = 0; + closeButton.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(200, 60, 60); + closeButton.BackColor = System.Drawing.Color.FromArgb(20, 24, 29); + closeButton.ForeColor = System.Drawing.Color.White; + closeButton.Font = new System.Drawing.Font("Segoe UI", 9F); + closeButton.Location = new System.Drawing.Point(218, 0); + closeButton.Name = "closeButton"; + closeButton.Size = new System.Drawing.Size(30, 28); + closeButton.TabIndex = 1; + closeButton.TabStop = false; + closeButton.Text = "✕"; + closeButton.Click += CloseButton_Click; + // // button1 // button1.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; - button1.AutoSize = true; button1.DialogResult = System.Windows.Forms.DialogResult.OK; - button1.Location = new System.Drawing.Point(11, 67); - button1.MinimumSize = new System.Drawing.Size(0, 24); + button1.Location = new System.Drawing.Point(16, 80); button1.Name = "button1"; - button1.Size = new System.Drawing.Size(75, 24); + button1.Size = new System.Drawing.Size(75, 28); button1.TabIndex = 2; button1.Text = "OK"; - button1.UseVisualStyleBackColor = true; + button1.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + button1.FlatAppearance.BorderSize = 0; + button1.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(60, 65, 80); + button1.BackColor = System.Drawing.Color.FromArgb(42, 45, 58); + button1.ForeColor = System.Drawing.Color.White; + button1.Font = new System.Drawing.Font("Segoe UI", 9F); + button1.Cursor = System.Windows.Forms.Cursors.Hand; button1.Visible = false; // // richTextBoxMessage @@ -314,16 +217,17 @@ namespace JR.Utils.GUI.Forms richTextBoxMessage.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; - richTextBoxMessage.BackColor = System.Drawing.Color.White; + richTextBoxMessage.BackColor = System.Drawing.Color.FromArgb(20, 24, 29); + richTextBoxMessage.ForeColor = System.Drawing.Color.White; richTextBoxMessage.BorderStyle = System.Windows.Forms.BorderStyle.None; richTextBoxMessage.DataBindings.Add(new System.Windows.Forms.Binding("Text", FlexibleMessageBoxFormBindingSource, "MessageText", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); richTextBoxMessage.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, 0); - richTextBoxMessage.Location = new System.Drawing.Point(50, 26); + richTextBoxMessage.Location = new System.Drawing.Point(52, 6); richTextBoxMessage.Margin = new System.Windows.Forms.Padding(0); richTextBoxMessage.Name = "richTextBoxMessage"; richTextBoxMessage.ReadOnly = true; richTextBoxMessage.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.Vertical; - richTextBoxMessage.Size = new System.Drawing.Size(200, 20); + richTextBoxMessage.Size = new System.Drawing.Size(190, 20); richTextBoxMessage.TabIndex = 0; richTextBoxMessage.TabStop = false; richTextBoxMessage.Text = ""; @@ -334,18 +238,18 @@ namespace JR.Utils.GUI.Forms panel1.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; - panel1.BackColor = System.Drawing.Color.White; + panel1.BackColor = System.Drawing.Color.FromArgb(20, 24, 29); panel1.Controls.Add(pictureBoxForIcon); panel1.Controls.Add(richTextBoxMessage); - panel1.Location = new System.Drawing.Point(-3, -4); + panel1.Location = new System.Drawing.Point(6, 34); panel1.Name = "panel1"; - panel1.Size = new System.Drawing.Size(268, 59); + panel1.Size = new System.Drawing.Size(248, 59); panel1.TabIndex = 1; // // pictureBoxForIcon // pictureBoxForIcon.BackColor = System.Drawing.Color.Transparent; - pictureBoxForIcon.Location = new System.Drawing.Point(15, 19); + pictureBoxForIcon.Location = new System.Drawing.Point(15, 15); pictureBoxForIcon.Name = "pictureBoxForIcon"; pictureBoxForIcon.Size = new System.Drawing.Size(32, 32); pictureBoxForIcon.TabIndex = 8; @@ -355,80 +259,273 @@ namespace JR.Utils.GUI.Forms // button2.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; button2.DialogResult = System.Windows.Forms.DialogResult.OK; - button2.Location = new System.Drawing.Point(92, 67); - button2.MinimumSize = new System.Drawing.Size(0, 24); + button2.Location = new System.Drawing.Point(97, 80); button2.Name = "button2"; - button2.Size = new System.Drawing.Size(75, 24); + button2.Size = new System.Drawing.Size(75, 28); button2.TabIndex = 3; button2.Text = "OK"; - button2.UseVisualStyleBackColor = true; + button2.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + button2.FlatAppearance.BorderSize = 0; + button2.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(60, 65, 80); + button2.BackColor = System.Drawing.Color.FromArgb(42, 45, 58); + button2.ForeColor = System.Drawing.Color.White; + button2.Font = new System.Drawing.Font("Segoe UI", 9F); + button2.Cursor = System.Windows.Forms.Cursors.Hand; button2.Visible = false; // // button3 // button3.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; - button3.AutoSize = true; button3.DialogResult = System.Windows.Forms.DialogResult.OK; - button3.Location = new System.Drawing.Point(173, 67); - button3.MinimumSize = new System.Drawing.Size(0, 24); + button3.Location = new System.Drawing.Point(178, 80); button3.Name = "button3"; - button3.Size = new System.Drawing.Size(75, 24); + button3.Size = new System.Drawing.Size(75, 28); button3.TabIndex = 0; button3.Text = "OK"; - button3.UseVisualStyleBackColor = true; + button3.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + button3.FlatAppearance.BorderSize = 0; + button3.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(60, 65, 80); + button3.BackColor = System.Drawing.Color.FromArgb(42, 45, 58); + button3.ForeColor = System.Drawing.Color.White; + button3.Font = new System.Drawing.Font("Segoe UI", 9F); + button3.Cursor = System.Windows.Forms.Cursors.Hand; button3.Visible = false; // // FlexibleMessageBoxForm // AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - ClientSize = new System.Drawing.Size(260, 102); + BackColor = System.Drawing.Color.FromArgb(25, 25, 30); + ForeColor = System.Drawing.Color.White; + FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; + ClientSize = new System.Drawing.Size(260, 115); + Padding = new System.Windows.Forms.Padding(5); + Controls.Add(titlePanel); Controls.Add(button3); Controls.Add(button2); Controls.Add(panel1); Controls.Add(button1); - DataBindings.Add(new System.Windows.Forms.Binding("Text", FlexibleMessageBoxFormBindingSource, "CaptionText", true)); MaximizeBox = false; MinimizeBox = false; - MinimumSize = new System.Drawing.Size(276, 140); + MinimumSize = new System.Drawing.Size(276, 120); Name = "FlexibleMessageBoxForm"; ShowIcon = false; - SizeGripStyle = System.Windows.Forms.SizeGripStyle.Show; + SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; Text = ""; Shown += new System.EventHandler(FlexibleMessageBoxForm_Shown); ((System.ComponentModel.ISupportInitialize)FlexibleMessageBoxFormBindingSource).EndInit(); panel1.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)pictureBoxForIcon).EndInit(); + titlePanel.ResumeLayout(false); ResumeLayout(false); - PerformLayout(); + + // Apply rounded corners and custom painting + this.Paint += FlexibleMessageBoxForm_Paint; + button1.Paint += RoundedButton_Paint; + button2.Paint += RoundedButton_Paint; + button3.Paint += RoundedButton_Paint; + + // Setup hover effects for buttons + SetupButtonHover(button1); + SetupButtonHover(button2); + SetupButtonHover(button3); Activate(); } - private System.Windows.Forms.Button button1; - private System.Windows.Forms.BindingSource FlexibleMessageBoxFormBindingSource; - private System.Windows.Forms.RichTextBox richTextBoxMessage; - private System.Windows.Forms.Panel panel1; - private System.Windows.Forms.PictureBox pictureBoxForIcon; - private System.Windows.Forms.Button button2; - private System.Windows.Forms.Button button3; + private Button button1; + private BindingSource FlexibleMessageBoxFormBindingSource; + private RichTextBox richTextBoxMessage; + private Panel panel1; + private PictureBox pictureBoxForIcon; + private Button button2; + private Button button3; + private Panel titlePanel; + private Label titleLabel; + private Button closeButton; + + #endregion + + #region Custom Painting + + private Dictionary _buttonHoverState = new Dictionary(); + private const int SHADOW_SIZE = 2; + private const int CONTENT_RADIUS = 10; + + private void FlexibleMessageBoxForm_Paint(object sender, PaintEventArgs e) + { + e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; + e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + + int w = this.Width; + int h = this.Height; + + // Draw shadow gradient layers around the content area + for (int i = SHADOW_SIZE; i >= 1; i--) + { + int alpha = (SHADOW_SIZE - i + 1) * 12; // 12, 24, 36, 48, 60 + Rectangle shadowRect = new Rectangle( + SHADOW_SIZE - i, + SHADOW_SIZE - i, + w - (SHADOW_SIZE - i) * 2 - 1, + h - (SHADOW_SIZE - i) * 2 - 1); + + using (Pen shadowPen = new Pen(Color.FromArgb(alpha, 0, 0, 0), 1)) + using (GraphicsPath shadowPath = GetRoundedRectPath(shadowRect, CONTENT_RADIUS + i)) + { + e.Graphics.DrawPath(shadowPen, shadowPath); + } + } + + // Draw content background + Rectangle contentRect = new Rectangle(SHADOW_SIZE, SHADOW_SIZE, w - SHADOW_SIZE * 2, h - SHADOW_SIZE * 2); + using (GraphicsPath contentPath = GetRoundedRectPath(contentRect, CONTENT_RADIUS)) + { + using (SolidBrush bgBrush = new SolidBrush(Color.FromArgb(20, 24, 29))) + { + e.Graphics.FillPath(bgBrush, contentPath); + } + + // Draw thin border + using (Pen borderPen = new Pen(Color.FromArgb(70, 80, 100), 1f)) + { + e.Graphics.DrawPath(borderPen, contentPath); + } + } + + // Apply rounded region to form (with shadow area) + using (GraphicsPath regionPath = GetRoundedRectPath(new Rectangle(0, 0, w, h), CONTENT_RADIUS + SHADOW_SIZE)) + { + this.Region = new Region(regionPath); + } + } + + private void SetupButtonHover(Button btn) + { + _buttonHoverState[btn] = false; + + btn.MouseEnter += (s, e) => + { + _buttonHoverState[btn] = true; + btn.Invalidate(); + }; + + btn.MouseLeave += (s, e) => + { + _buttonHoverState[btn] = false; + btn.Invalidate(); + }; + } + + private void RoundedButton_Paint(object sender, PaintEventArgs e) + { + Button btn = sender as Button; + if (btn == null) return; + + e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; + e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + + bool isHovered = _buttonHoverState.ContainsKey(btn) && _buttonHoverState[btn]; + + int radius = 4; + + // Use a rect that's 1 pixel smaller to avoid edge clipping + Rectangle drawRect = new Rectangle(1, 1, btn.Width - 2, btn.Height - 2); + + // Fill entire button area with parent background first to clear previous state + using (SolidBrush clearBrush = new SolidBrush(Color.FromArgb(20, 24, 29))) + { + e.Graphics.FillRectangle(clearBrush, 0, 0, btn.Width, btn.Height); + } + + using (GraphicsPath path = GetRoundedRectPath(drawRect, radius)) + { + // Determine colors based on hover state + Color bgColor = isHovered + ? Color.FromArgb(93, 203, 173) // Accent color on hover + : btn.BackColor; + + Color textColor = isHovered + ? Color.FromArgb(20, 20, 20) // Dark text on accent + : btn.ForeColor; + + // Draw background + using (SolidBrush brush = new SolidBrush(bgColor)) + { + e.Graphics.FillPath(brush, path); + } + + // Draw subtle border on normal state + if (!isHovered) + { + using (Pen borderPen = new Pen(Color.FromArgb(70, 75, 90), 1)) + { + e.Graphics.DrawPath(borderPen, path); + } + } + + // Draw text centered in original button bounds + TextRenderer.DrawText(e.Graphics, btn.Text, btn.Font, + new Rectangle(0, 0, btn.Width, btn.Height), textColor, + TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter); + } + + // Set region to full button size (not the draw rect) + using (GraphicsPath regionPath = GetRoundedRectPath(new Rectangle(0, 0, btn.Width, btn.Height), radius)) + { + btn.Region = new Region(regionPath); + } + } + + private GraphicsPath GetRoundedRectPath(Rectangle rect, int radius) + { + GraphicsPath path = new GraphicsPath(); + + if (radius <= 0) + { + path.AddRectangle(rect); + return path; + } + + int diameter = radius * 2; + + // Ensure diameter doesn't exceed rect dimensions + diameter = Math.Min(diameter, Math.Min(rect.Width, rect.Height)); + radius = diameter / 2; + + Rectangle arcRect = new Rectangle(rect.Location, new Size(diameter, diameter)); + + // Top left arc + path.AddArc(arcRect, 180, 90); + + // Top right arc + arcRect.X = rect.Right - diameter; + path.AddArc(arcRect, 270, 90); + + // Bottom right arc + arcRect.Y = rect.Bottom - diameter; + path.AddArc(arcRect, 0, 90); + + // Bottom left arc + arcRect.X = rect.Left; + path.AddArc(arcRect, 90, 90); + + path.CloseFigure(); + return path; + } #endregion #region Private constants - //These separators are used for the "copy to clipboard" standard operation, triggered by Ctrl + C (behavior and clipboard format is like in a standard MessageBox) private static readonly string STANDARD_MESSAGEBOX_SEPARATOR_LINES = "---------------------------\n"; private static readonly string STANDARD_MESSAGEBOX_SEPARATOR_SPACES = " "; - //These are the possible buttons (in a standard MessageBox) private enum ButtonID { OK = 0, CANCEL, YES, NO, ABORT, RETRY, IGNORE }; - //These are the buttons texts for different languages. - //If you want to add a new language, add it here and in the GetButtonText-Function private enum TwoLetterISOLanguageID { en, de, es, it }; - private static readonly string[] BUTTON_TEXTS_ENGLISH_EN = { "OK", "Cancel", "&Yes", "&No", "&Abort", "&Retry", "&Ignore" }; //Note: This is also the fallback language + private static readonly string[] BUTTON_TEXTS_ENGLISH_EN = { "OK", "Cancel", "&Yes", "&No", "&Abort", "&Retry", "&Ignore" }; private static readonly string[] BUTTON_TEXTS_GERMAN_DE = { "OK", "Abbrechen", "&Ja", "&Nein", "&Abbrechen", "&Wiederholen", "&Ignorieren" }; private static readonly string[] BUTTON_TEXTS_SPANISH_ES = { "Aceptar", "Cancelar", "&Sí", "&No", "&Abortar", "&Reintentar", "&Ignorar" }; private static readonly string[] BUTTON_TEXTS_ITALIAN_IT = { "OK", "Annulla", "&Sì", "&No", "&Interrompi", "&Riprova", "&Ignora" }; @@ -445,16 +542,10 @@ namespace JR.Utils.GUI.Forms #region Private constructor - /// - /// Initializes a new instance of the class. - /// private FlexibleMessageBoxForm() { InitializeComponent(); - - //Try to evaluate the language. If this fails, the fallback language English will be used _ = Enum.TryParse(CultureInfo.CurrentUICulture.TwoLetterISOLanguageName, out languageID); - KeyPreview = true; KeyUp += FlexibleMessageBoxForm_KeyUp; } @@ -463,120 +554,104 @@ namespace JR.Utils.GUI.Forms #region Private helper functions - /// - /// Gets the string rows. - /// - /// The message. - /// The string rows as 1-dimensional array private static string[] GetStringRows(string message) { if (string.IsNullOrEmpty(message)) { return null; } - - string[] messageRows = message.Split(new char[] { '\n' }, StringSplitOptions.None); - return messageRows; + return message.Split(new char[] { '\n' }, StringSplitOptions.None); } - /// - /// Gets the button text for the CurrentUICulture language. - /// Note: The fallback language is English - /// - /// The ID of the button. - /// The button text private string GetButtonText(ButtonID buttonID) { int buttonTextArrayIndex = Convert.ToInt32(buttonID); - switch (languageID) { case TwoLetterISOLanguageID.de: return BUTTON_TEXTS_GERMAN_DE[buttonTextArrayIndex]; case TwoLetterISOLanguageID.es: return BUTTON_TEXTS_SPANISH_ES[buttonTextArrayIndex]; case TwoLetterISOLanguageID.it: return BUTTON_TEXTS_ITALIAN_IT[buttonTextArrayIndex]; - default: return BUTTON_TEXTS_ENGLISH_EN[buttonTextArrayIndex]; } } - /// - /// Ensure the given working area factor in the range of 0.2 - 1.0 where: - /// - /// 0.2 means: 20 percent of the working area height or width. - /// 1.0 means: 100 percent of the working area height or width. - /// - /// The given working area factor. - /// The corrected given working area factor. private static double GetCorrectedWorkingAreaFactor(double workingAreaFactor) { const double MIN_FACTOR = 0.2; const double MAX_FACTOR = 1.0; - return workingAreaFactor < MIN_FACTOR ? MIN_FACTOR : workingAreaFactor > MAX_FACTOR ? MAX_FACTOR : workingAreaFactor; } - /// - /// Set the dialogs start position when given. - /// Otherwise center the dialog on the current screen. - /// - /// The FlexibleMessageBox dialog. - /// The owner. private static void SetDialogStartPosition(FlexibleMessageBoxForm flexibleMessageBoxForm, IWin32Window owner) { - //If no owner given: Center on current screen - if (owner == null) + flexibleMessageBoxForm.StartPosition = FormStartPosition.Manual; + + // Try to get owner form, fallback to active form if owner is null + Form ownerForm = null; + if (owner != null) { - Screen screen = Screen.FromPoint(Cursor.Position); - flexibleMessageBoxForm.StartPosition = FormStartPosition.Manual; - flexibleMessageBoxForm.Left = screen.Bounds.Left + (screen.Bounds.Width / 2) - (flexibleMessageBoxForm.Width / 2); - flexibleMessageBoxForm.Top = screen.Bounds.Top + (screen.Bounds.Height / 2) - (flexibleMessageBoxForm.Height / 2); + ownerForm = owner as Form; + if (ownerForm == null) + { + Control ownerControl = Control.FromHandle(owner.Handle); + ownerForm = ownerControl?.FindForm(); + } + } + + // Fallback to active form if no owner specified + if (ownerForm == null) + { + ownerForm = Form.ActiveForm; + } + + if (ownerForm != null && ownerForm.Visible) + { + // Center relative to owner window + int x = ownerForm.Left + (ownerForm.Width - flexibleMessageBoxForm.Width) / 2; + int y = ownerForm.Top + (ownerForm.Height - flexibleMessageBoxForm.Height) / 2; + + // Ensure the dialog stays within screen bounds + Screen screen = Screen.FromControl(ownerForm); + x = Math.Max(screen.WorkingArea.Left, Math.Min(x, screen.WorkingArea.Right - flexibleMessageBoxForm.Width)); + y = Math.Max(screen.WorkingArea.Top, Math.Min(y, screen.WorkingArea.Bottom - flexibleMessageBoxForm.Height)); + + flexibleMessageBoxForm.Left = x; + flexibleMessageBoxForm.Top = y; + } + else + { + // No owner found: center on current screen + CenterOnScreen(flexibleMessageBoxForm); } } - /// - /// Calculate the dialogs start size (Try to auto-size width to show longest text row). - /// Also set the maximum dialog size. - /// - /// The FlexibleMessageBox dialog. - /// The text (the longest text row is used to calculate the dialog width). - /// The caption (this can also affect the dialog width). + private static void CenterOnScreen(FlexibleMessageBoxForm form) + { + Screen screen = Screen.FromPoint(Cursor.Position); + form.Left = screen.WorkingArea.Left + (screen.WorkingArea.Width - form.Width) / 2; + form.Top = screen.WorkingArea.Top + (screen.WorkingArea.Height - form.Height) / 2; + } + private static void SetDialogSizes(FlexibleMessageBoxForm flexibleMessageBoxForm, string text, string caption) { - //First set the bounds for the maximum dialog size flexibleMessageBoxForm.MaximumSize = new Size(Convert.ToInt32(SystemInformation.WorkingArea.Width * FlexibleMessageBoxForm.GetCorrectedWorkingAreaFactor(MAX_WIDTH_FACTOR)), Convert.ToInt32(SystemInformation.WorkingArea.Height * FlexibleMessageBoxForm.GetCorrectedWorkingAreaFactor(MAX_HEIGHT_FACTOR))); - //Get rows. Exit if there are no rows to render... string[] stringRows = GetStringRows(text); - if (stringRows == null) - { - return; - } + if (stringRows == null) return; - //Calculate whole text height int textHeight = TextRenderer.MeasureText(text, FONT).Height; - - //Calculate width for longest text line const int SCROLLBAR_WIDTH_OFFSET = 15; int longestTextRowWidth = stringRows.Max(textForRow => TextRenderer.MeasureText(textForRow, FONT).Width); int captionWidth = TextRenderer.MeasureText(caption, SystemFonts.CaptionFont).Width; int textWidth = Math.Max(longestTextRowWidth + SCROLLBAR_WIDTH_OFFSET, captionWidth); - //Calculate margins int marginWidth = flexibleMessageBoxForm.Width - flexibleMessageBoxForm.richTextBoxMessage.Width; int marginHeight = flexibleMessageBoxForm.Height - flexibleMessageBoxForm.richTextBoxMessage.Height; - //Set calculated dialog size (if the calculated values exceed the maximums, they were cut by windows forms automatically) - flexibleMessageBoxForm.Size = new Size(textWidth + marginWidth, - textHeight + marginHeight); + flexibleMessageBoxForm.Size = new Size(textWidth + marginWidth, textHeight + marginHeight); } - /// - /// Set the dialogs icon. - /// When no icon is used: Correct placement and width of rich text box. - /// - /// The FlexibleMessageBox dialog. - /// The MessageBoxIcon. private static void SetDialogIcon(FlexibleMessageBoxForm flexibleMessageBoxForm, MessageBoxIcon icon) { switch (icon) @@ -594,7 +669,6 @@ namespace JR.Utils.GUI.Forms flexibleMessageBoxForm.pictureBoxForIcon.Image = SystemIcons.Question.ToBitmap(); break; default: - //When no icon is used: Correct placement and width of rich text box. flexibleMessageBoxForm.pictureBoxForIcon.Visible = false; flexibleMessageBoxForm.richTextBoxMessage.Left -= flexibleMessageBoxForm.pictureBoxForIcon.Width; flexibleMessageBoxForm.richTextBoxMessage.Width += flexibleMessageBoxForm.pictureBoxForIcon.Width; @@ -602,93 +676,68 @@ namespace JR.Utils.GUI.Forms } } - /// - /// Set dialog buttons visibilities and texts. - /// Also set a default button. - /// - /// The FlexibleMessageBox dialog. - /// The buttons. - /// The default button. private static void SetDialogButtons(FlexibleMessageBoxForm flexibleMessageBoxForm, MessageBoxButtons buttons, MessageBoxDefaultButton defaultButton) { - //Set the buttons visibilities and texts switch (buttons) { case MessageBoxButtons.AbortRetryIgnore: flexibleMessageBoxForm.visibleButtonsCount = 3; - flexibleMessageBoxForm.button1.Visible = true; flexibleMessageBoxForm.button1.Text = flexibleMessageBoxForm.GetButtonText(ButtonID.ABORT); flexibleMessageBoxForm.button1.DialogResult = DialogResult.Abort; - flexibleMessageBoxForm.button2.Visible = true; flexibleMessageBoxForm.button2.Text = flexibleMessageBoxForm.GetButtonText(ButtonID.RETRY); flexibleMessageBoxForm.button2.DialogResult = DialogResult.Retry; - flexibleMessageBoxForm.button3.Visible = true; flexibleMessageBoxForm.button3.Text = flexibleMessageBoxForm.GetButtonText(ButtonID.IGNORE); flexibleMessageBoxForm.button3.DialogResult = DialogResult.Ignore; - flexibleMessageBoxForm.ControlBox = false; break; case MessageBoxButtons.OKCancel: flexibleMessageBoxForm.visibleButtonsCount = 2; - flexibleMessageBoxForm.button2.Visible = true; flexibleMessageBoxForm.button2.Text = flexibleMessageBoxForm.GetButtonText(ButtonID.OK); flexibleMessageBoxForm.button2.DialogResult = DialogResult.OK; - flexibleMessageBoxForm.button3.Visible = true; flexibleMessageBoxForm.button3.Text = flexibleMessageBoxForm.GetButtonText(ButtonID.CANCEL); flexibleMessageBoxForm.button3.DialogResult = DialogResult.Cancel; - flexibleMessageBoxForm.CancelButton = flexibleMessageBoxForm.button3; break; case MessageBoxButtons.RetryCancel: flexibleMessageBoxForm.visibleButtonsCount = 2; - flexibleMessageBoxForm.button2.Visible = true; flexibleMessageBoxForm.button2.Text = flexibleMessageBoxForm.GetButtonText(ButtonID.RETRY); flexibleMessageBoxForm.button2.DialogResult = DialogResult.Retry; - flexibleMessageBoxForm.button3.Visible = true; flexibleMessageBoxForm.button3.Text = flexibleMessageBoxForm.GetButtonText(ButtonID.CANCEL); flexibleMessageBoxForm.button3.DialogResult = DialogResult.Cancel; - flexibleMessageBoxForm.CancelButton = flexibleMessageBoxForm.button3; break; case MessageBoxButtons.YesNo: flexibleMessageBoxForm.visibleButtonsCount = 2; - flexibleMessageBoxForm.button2.Visible = true; flexibleMessageBoxForm.button2.Text = flexibleMessageBoxForm.GetButtonText(ButtonID.YES); flexibleMessageBoxForm.button2.DialogResult = DialogResult.Yes; - flexibleMessageBoxForm.button3.Visible = true; flexibleMessageBoxForm.button3.Text = flexibleMessageBoxForm.GetButtonText(ButtonID.NO); flexibleMessageBoxForm.button3.DialogResult = DialogResult.No; - flexibleMessageBoxForm.ControlBox = false; break; case MessageBoxButtons.YesNoCancel: flexibleMessageBoxForm.visibleButtonsCount = 3; - flexibleMessageBoxForm.button1.Visible = true; flexibleMessageBoxForm.button1.Text = flexibleMessageBoxForm.GetButtonText(ButtonID.YES); flexibleMessageBoxForm.button1.DialogResult = DialogResult.Yes; - flexibleMessageBoxForm.button2.Visible = true; flexibleMessageBoxForm.button2.Text = flexibleMessageBoxForm.GetButtonText(ButtonID.NO); flexibleMessageBoxForm.button2.DialogResult = DialogResult.No; - flexibleMessageBoxForm.button3.Visible = true; flexibleMessageBoxForm.button3.Text = flexibleMessageBoxForm.GetButtonText(ButtonID.CANCEL); flexibleMessageBoxForm.button3.DialogResult = DialogResult.Cancel; - flexibleMessageBoxForm.CancelButton = flexibleMessageBoxForm.button3; break; @@ -698,12 +747,9 @@ namespace JR.Utils.GUI.Forms flexibleMessageBoxForm.button3.Visible = true; flexibleMessageBoxForm.button3.Text = flexibleMessageBoxForm.GetButtonText(ButtonID.OK); flexibleMessageBoxForm.button3.DialogResult = DialogResult.OK; - flexibleMessageBoxForm.CancelButton = flexibleMessageBoxForm.button3; break; } - - //Set default button (used in FlexibleMessageBoxForm_Shown) flexibleMessageBoxForm.defaultButton = defaultButton; } @@ -711,17 +757,9 @@ namespace JR.Utils.GUI.Forms #region Private event handlers - /// - /// Handles the Shown event of the FlexibleMessageBoxForm control. - /// - /// The source of the event. - /// The instance containing the event data. private void FlexibleMessageBoxForm_Shown(object sender, EventArgs e) { - Button buttonToFocus; - int buttonIndexToFocus; - //Set the default button... switch (defaultButton) { case MessageBoxDefaultButton.Button1: @@ -737,20 +775,12 @@ namespace JR.Utils.GUI.Forms } if (buttonIndexToFocus > visibleButtonsCount) - { buttonIndexToFocus = visibleButtonsCount; - } - - buttonToFocus = buttonIndexToFocus == 3 ? button3 : buttonIndexToFocus == 2 ? button2 : button1; + Button buttonToFocus = buttonIndexToFocus == 3 ? button3 : buttonIndexToFocus == 2 ? button2 : button1; _ = buttonToFocus.Focus(); } - /// - /// Handles the LinkClicked event of the richTextBoxMessage control. - /// - /// The source of the event. - /// The instance containing the event data. private void richTextBoxMessage_LinkClicked(object sender, LinkClickedEventArgs e) { try @@ -758,33 +788,20 @@ namespace JR.Utils.GUI.Forms Cursor.Current = Cursors.WaitCursor; _ = Process.Start(e.LinkText); } - catch (Exception) - { - //Let the caller of FlexibleMessageBoxForm decide what to do with this exception... - throw; - } finally { Cursor.Current = Cursors.Default; } - } - /// - /// Handles the KeyUp event of the richTextBoxMessage control. - /// - /// The source of the event. - /// The instance containing the event data. private void FlexibleMessageBoxForm_KeyUp(object sender, KeyEventArgs e) { - //Handle standard key strikes for clipboard copy: "Ctrl + C" and "Ctrl + Insert" if (e.Control && (e.KeyCode == Keys.C || e.KeyCode == Keys.Insert)) { string buttonsTextLine = (button1.Visible ? button1.Text + STANDARD_MESSAGEBOX_SEPARATOR_SPACES : string.Empty) + (button2.Visible ? button2.Text + STANDARD_MESSAGEBOX_SEPARATOR_SPACES : string.Empty) + (button3.Visible ? button3.Text + STANDARD_MESSAGEBOX_SEPARATOR_SPACES : string.Empty); - //Build same clipboard text like the standard .Net MessageBox string textForClipboard = STANDARD_MESSAGEBOX_SEPARATOR_LINES + Text + Environment.NewLine + STANDARD_MESSAGEBOX_SEPARATOR_LINES @@ -793,75 +810,47 @@ namespace JR.Utils.GUI.Forms + buttonsTextLine.Replace("&", string.Empty) + Environment.NewLine + STANDARD_MESSAGEBOX_SEPARATOR_LINES; - //Set text in clipboard Clipboard.SetText(textForClipboard); } } #endregion - #region Properties (only used for binding) + #region Properties - /// - /// The text that is been used for the heading. - /// public string CaptionText { get; set; } - - /// - /// The text that is been used in the FlexibleMessageBoxForm. - /// public string MessageText { get; set; } #endregion #region Public show function - /// - /// Shows the specified message box. - /// - /// The owner. - /// The text. - /// The caption. - /// The buttons. - /// The icon. - /// The default button. - /// The dialog result. public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton) { - //Create a new instance of the FlexibleMessageBox form FlexibleMessageBoxForm flexibleMessageBoxForm = new FlexibleMessageBoxForm { ShowInTaskbar = false, - - //Bind the caption and the message text CaptionText = caption, MessageText = text }; flexibleMessageBoxForm.FlexibleMessageBoxFormBindingSource.DataSource = flexibleMessageBoxForm; + flexibleMessageBoxForm.titleLabel.Text = caption; - //Set the buttons visibilities and texts. Also set a default button. SetDialogButtons(flexibleMessageBoxForm, buttons, defaultButton); - - //Set the dialogs icon. When no icon is used: Correct placement and width of rich text box. SetDialogIcon(flexibleMessageBoxForm, icon); - //Set the font for all controls flexibleMessageBoxForm.Font = FONT; flexibleMessageBoxForm.richTextBoxMessage.Font = FONT; - //Calculate the dialogs start size (Try to auto-size width to show longest text row). Also set the maximum dialog size. SetDialogSizes(flexibleMessageBoxForm, text, caption); - - //Set the dialogs start position when given. Otherwise center the dialog on the current screen. SetDialogStartPosition(flexibleMessageBoxForm, owner); - //Show the dialog return flexibleMessageBoxForm.ShowDialog(owner); } #endregion - } //class FlexibleMessageBoxForm + } #endregion } -} +} \ No newline at end of file diff --git a/GalleryView.cs b/GalleryView.cs index 58c838e..85853f2 100644 --- a/GalleryView.cs +++ b/GalleryView.cs @@ -1,4 +1,5 @@ using AndroidSideloader; +using AndroidSideloader.Utilities; using System; using System.Collections.Generic; using System.Drawing; @@ -53,6 +54,11 @@ public class FastGalleryPanel : Control private int _selectedIndex = -1; private bool _isHoveringDeleteButton = false; + // Context Menu & Favorites + private ContextMenuStrip _contextMenu; + private int _rightClickedIndex = -1; + private HashSet _favoritesCache; + // Rendering private Bitmap _backBuffer; @@ -68,8 +74,9 @@ public class FastGalleryPanel : Control // Theme colors private static readonly Color TileBorderHover = Color.FromArgb(93, 203, 173); private static readonly Color TileBorderSelected = Color.FromArgb(200, 200, 200); + private static readonly Color TileBorderFavorite = Color.FromArgb(255, 215, 0); + private static readonly Color BadgeFavoriteBg = Color.FromArgb(200, 255, 180, 0); private static readonly Color TextColor = Color.FromArgb(245, 255, 255, 255); - private static readonly Color BadgeUpdateBg = Color.FromArgb(180, 76, 175, 80); private static readonly Color BadgeInstalledBg = Color.FromArgb(180, 60, 145, 230); private static readonly Color DeleteButtonBg = Color.FromArgb(200, 180, 50, 50); private static readonly Color DeleteButtonHoverBg = Color.FromArgb(255, 220, 70, 70); @@ -80,6 +87,7 @@ public class FastGalleryPanel : Control public event EventHandler TileClicked; public event EventHandler TileDoubleClicked; public event EventHandler TileDeleteClicked; + public event EventHandler TileRightClicked; public event EventHandler SortChanged; private class TileAnimationState @@ -96,6 +104,8 @@ public class FastGalleryPanel : Control public float TargetTooltipOpacity = 0f; public float DeleteButtonOpacity = 0f; public float TargetDeleteButtonOpacity = 0f; + public float FavoriteOpacity = 0f; + public float TargetFavoriteOpacity = 0f; } public FastGalleryPanel(List items, int tileWidth, int tileHeight, int spacing, int initialWidth, int initialHeight) @@ -109,6 +119,11 @@ public class FastGalleryPanel : Control _cacheOrder = new Queue(); _tileStates = new Dictionary(); _sortButtons = new List