Reworked Wireless ADB options: Manual IP address field now prefills the first 3 octets by getting the local IPv4 of the system. Automatic no longer requires USB connection to establish a connection, instead now performs a network scan to find the device and connects to it. Included multi-device selection support (untested - I only have a single device.) Nonetheless Wireless ADB still requires a one-time USB setup for the ADB 'tcpip 5555' command (I wasn't able to ever establish a connection without that in Rookie, neither here nor in 2.34). Updated ADB button label to match new logic. Updated ADB messages to provide more guidance for the user. Synced sideloading button text to correctly match the sideloading status. Added rookie application status updates, device ID, mirror and sideloading status labels in bottom left corner. Fixed ProcessNewApps regression (no longer blocks UI thread). Replaced Program.form.changeTitle calls with local changeTitle for consistency. Fixed Game Gallery application install status not updating after installation.
856 lines
40 KiB
C#
856 lines
40 KiB
C#
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
|
|
{
|
|
public class FlexibleMessageBox
|
|
{
|
|
#region Public statics
|
|
|
|
public static double MAX_WIDTH_FACTOR = 0.7;
|
|
public static double MAX_HEIGHT_FACTOR = 0.9;
|
|
public static Font FONT = SystemFonts.MessageBoxFont;
|
|
|
|
#endregion
|
|
|
|
#region Public show functions
|
|
|
|
public static DialogResult Show(string text)
|
|
{
|
|
return FlexibleMessageBoxForm.Show(null, text, string.Empty, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
|
}
|
|
|
|
public static DialogResult Show(IWin32Window owner, string text)
|
|
{
|
|
return FlexibleMessageBoxForm.Show(owner, text, string.Empty, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
|
}
|
|
|
|
public static DialogResult Show(string text, string caption)
|
|
{
|
|
return FlexibleMessageBoxForm.Show(null, text, caption, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
|
}
|
|
|
|
public static DialogResult Show(IWin32Window owner, string text, string caption)
|
|
{
|
|
return FlexibleMessageBoxForm.Show(owner, text, caption, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
|
}
|
|
|
|
public static DialogResult Show(string text, string caption, MessageBoxButtons buttons)
|
|
{
|
|
return FlexibleMessageBoxForm.Show(null, text, caption, buttons, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
|
}
|
|
|
|
public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons)
|
|
{
|
|
return FlexibleMessageBoxForm.Show(owner, text, caption, buttons, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
|
}
|
|
|
|
public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon)
|
|
{
|
|
return FlexibleMessageBoxForm.Show(null, text, caption, buttons, icon, MessageBoxDefaultButton.Button1);
|
|
}
|
|
|
|
public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon)
|
|
{
|
|
return FlexibleMessageBoxForm.Show(owner, text, caption, buttons, icon, MessageBoxDefaultButton.Button1);
|
|
}
|
|
|
|
public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton)
|
|
{
|
|
return FlexibleMessageBoxForm.Show(null, text, caption, buttons, icon, defaultButton);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Internal form class
|
|
|
|
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
|
|
|
|
private System.ComponentModel.IContainer components = null;
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
if (disposing && (components != null))
|
|
{
|
|
components.Dispose();
|
|
}
|
|
base.Dispose(disposing);
|
|
}
|
|
|
|
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();
|
|
button1 = new System.Windows.Forms.Button();
|
|
richTextBoxMessage = new System.Windows.Forms.RichTextBox();
|
|
FlexibleMessageBoxFormBindingSource = new System.Windows.Forms.BindingSource(components);
|
|
panel1 = new System.Windows.Forms.Panel();
|
|
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 = "<Caption>";
|
|
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.DialogResult = System.Windows.Forms.DialogResult.OK;
|
|
button1.Location = new System.Drawing.Point(16, 80);
|
|
button1.Name = "button1";
|
|
button1.Size = new System.Drawing.Size(75, 28);
|
|
button1.TabIndex = 2;
|
|
button1.Text = "OK";
|
|
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
|
|
//
|
|
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.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(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(190, 20);
|
|
richTextBoxMessage.TabIndex = 0;
|
|
richTextBoxMessage.TabStop = false;
|
|
richTextBoxMessage.Text = "<Message>";
|
|
richTextBoxMessage.LinkClicked += new System.Windows.Forms.LinkClickedEventHandler(richTextBoxMessage_LinkClicked);
|
|
//
|
|
// panel1
|
|
//
|
|
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.FromArgb(20, 24, 29);
|
|
panel1.Controls.Add(pictureBoxForIcon);
|
|
panel1.Controls.Add(richTextBoxMessage);
|
|
panel1.Location = new System.Drawing.Point(6, 34);
|
|
panel1.Name = "panel1";
|
|
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, 15);
|
|
pictureBoxForIcon.Name = "pictureBoxForIcon";
|
|
pictureBoxForIcon.Size = new System.Drawing.Size(32, 32);
|
|
pictureBoxForIcon.TabIndex = 8;
|
|
pictureBoxForIcon.TabStop = false;
|
|
//
|
|
// button2
|
|
//
|
|
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(97, 80);
|
|
button2.Name = "button2";
|
|
button2.Size = new System.Drawing.Size(75, 28);
|
|
button2.TabIndex = 3;
|
|
button2.Text = "OK";
|
|
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.DialogResult = System.Windows.Forms.DialogResult.OK;
|
|
button3.Location = new System.Drawing.Point(178, 80);
|
|
button3.Name = "button3";
|
|
button3.Size = new System.Drawing.Size(75, 28);
|
|
button3.TabIndex = 0;
|
|
button3.Text = "OK";
|
|
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;
|
|
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(button1);
|
|
Controls.Add(panel1);
|
|
MaximizeBox = false;
|
|
MinimizeBox = false;
|
|
MinimumSize = new System.Drawing.Size(276, 120);
|
|
Name = "FlexibleMessageBoxForm";
|
|
ShowIcon = false;
|
|
SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
|
|
StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
|
Text = "<Caption>";
|
|
Shown += new System.EventHandler(FlexibleMessageBoxForm_Shown);
|
|
((System.ComponentModel.ISupportInitialize)FlexibleMessageBoxFormBindingSource).EndInit();
|
|
panel1.ResumeLayout(false);
|
|
((System.ComponentModel.ISupportInitialize)pictureBoxForIcon).EndInit();
|
|
titlePanel.ResumeLayout(false);
|
|
ResumeLayout(false);
|
|
|
|
// 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 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<Button, bool> _buttonHoverState = new Dictionary<Button, bool>();
|
|
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
|
|
|
|
private static readonly string STANDARD_MESSAGEBOX_SEPARATOR_LINES = "---------------------------\n";
|
|
private static readonly string STANDARD_MESSAGEBOX_SEPARATOR_SPACES = " ";
|
|
|
|
private enum ButtonID { OK = 0, CANCEL, YES, NO, ABORT, RETRY, IGNORE };
|
|
|
|
private enum TwoLetterISOLanguageID { en, de, es, it };
|
|
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" };
|
|
|
|
#endregion
|
|
|
|
#region Private members
|
|
|
|
private MessageBoxDefaultButton defaultButton;
|
|
private int visibleButtonsCount;
|
|
private readonly TwoLetterISOLanguageID languageID = TwoLetterISOLanguageID.en;
|
|
|
|
#endregion
|
|
|
|
#region Private constructor
|
|
|
|
private FlexibleMessageBoxForm()
|
|
{
|
|
InitializeComponent();
|
|
_ = Enum.TryParse<TwoLetterISOLanguageID>(CultureInfo.CurrentUICulture.TwoLetterISOLanguageName, out languageID);
|
|
KeyPreview = true;
|
|
KeyUp += FlexibleMessageBoxForm_KeyUp;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private helper functions
|
|
|
|
private static string[] GetStringRows(string message)
|
|
{
|
|
if (string.IsNullOrEmpty(message))
|
|
{
|
|
return null;
|
|
}
|
|
return message.Split(new char[] { '\n' }, StringSplitOptions.None);
|
|
}
|
|
|
|
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];
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
private static void SetDialogStartPosition(FlexibleMessageBoxForm flexibleMessageBoxForm, IWin32Window owner)
|
|
{
|
|
flexibleMessageBoxForm.StartPosition = FormStartPosition.Manual;
|
|
|
|
// Try to get owner form, fallback to active form if owner is null
|
|
Form ownerForm = null;
|
|
if (owner != null)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
flexibleMessageBoxForm.MaximumSize = new Size(Convert.ToInt32(SystemInformation.WorkingArea.Width * FlexibleMessageBoxForm.GetCorrectedWorkingAreaFactor(MAX_WIDTH_FACTOR)),
|
|
Convert.ToInt32(SystemInformation.WorkingArea.Height * FlexibleMessageBoxForm.GetCorrectedWorkingAreaFactor(MAX_HEIGHT_FACTOR)));
|
|
|
|
string[] stringRows = GetStringRows(text);
|
|
if (stringRows == null) return;
|
|
|
|
int textHeight = TextRenderer.MeasureText(text, FONT).Height;
|
|
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);
|
|
|
|
int marginWidth = flexibleMessageBoxForm.Width - flexibleMessageBoxForm.richTextBoxMessage.Width;
|
|
int marginHeight = flexibleMessageBoxForm.Height - flexibleMessageBoxForm.richTextBoxMessage.Height;
|
|
|
|
flexibleMessageBoxForm.Size = new Size(textWidth + marginWidth, textHeight + marginHeight);
|
|
}
|
|
|
|
private static void SetDialogIcon(FlexibleMessageBoxForm flexibleMessageBoxForm, MessageBoxIcon icon)
|
|
{
|
|
switch (icon)
|
|
{
|
|
case MessageBoxIcon.Information:
|
|
flexibleMessageBoxForm.pictureBoxForIcon.Image = SystemIcons.Information.ToBitmap();
|
|
break;
|
|
case MessageBoxIcon.Warning:
|
|
flexibleMessageBoxForm.pictureBoxForIcon.Image = SystemIcons.Warning.ToBitmap();
|
|
break;
|
|
case MessageBoxIcon.Error:
|
|
flexibleMessageBoxForm.pictureBoxForIcon.Image = SystemIcons.Error.ToBitmap();
|
|
break;
|
|
case MessageBoxIcon.Question:
|
|
flexibleMessageBoxForm.pictureBoxForIcon.Image = SystemIcons.Question.ToBitmap();
|
|
break;
|
|
default:
|
|
flexibleMessageBoxForm.pictureBoxForIcon.Visible = false;
|
|
flexibleMessageBoxForm.richTextBoxMessage.Left -= flexibleMessageBoxForm.pictureBoxForIcon.Width;
|
|
flexibleMessageBoxForm.richTextBoxMessage.Width += flexibleMessageBoxForm.pictureBoxForIcon.Width;
|
|
break;
|
|
}
|
|
}
|
|
|
|
private static void SetDialogButtons(FlexibleMessageBoxForm flexibleMessageBoxForm, MessageBoxButtons buttons, MessageBoxDefaultButton defaultButton)
|
|
{
|
|
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;
|
|
|
|
case MessageBoxButtons.OK:
|
|
default:
|
|
flexibleMessageBoxForm.visibleButtonsCount = 1;
|
|
flexibleMessageBoxForm.button3.Visible = true;
|
|
flexibleMessageBoxForm.button3.Text = flexibleMessageBoxForm.GetButtonText(ButtonID.OK);
|
|
flexibleMessageBoxForm.button3.DialogResult = DialogResult.OK;
|
|
flexibleMessageBoxForm.CancelButton = flexibleMessageBoxForm.button3;
|
|
break;
|
|
}
|
|
flexibleMessageBoxForm.defaultButton = defaultButton;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private event handlers
|
|
|
|
private void FlexibleMessageBoxForm_Shown(object sender, EventArgs e)
|
|
{
|
|
int buttonIndexToFocus;
|
|
switch (defaultButton)
|
|
{
|
|
case MessageBoxDefaultButton.Button1:
|
|
default:
|
|
buttonIndexToFocus = 1;
|
|
break;
|
|
case MessageBoxDefaultButton.Button2:
|
|
buttonIndexToFocus = 2;
|
|
break;
|
|
case MessageBoxDefaultButton.Button3:
|
|
buttonIndexToFocus = 3;
|
|
break;
|
|
}
|
|
|
|
if (buttonIndexToFocus > visibleButtonsCount)
|
|
buttonIndexToFocus = visibleButtonsCount;
|
|
|
|
Button buttonToFocus = buttonIndexToFocus == 3 ? button3 : buttonIndexToFocus == 2 ? button2 : button1;
|
|
_ = buttonToFocus.Focus();
|
|
}
|
|
|
|
private void richTextBoxMessage_LinkClicked(object sender, LinkClickedEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
Cursor.Current = Cursors.WaitCursor;
|
|
_ = Process.Start(e.LinkText);
|
|
}
|
|
finally
|
|
{
|
|
Cursor.Current = Cursors.Default;
|
|
}
|
|
}
|
|
|
|
private void FlexibleMessageBoxForm_KeyUp(object sender, KeyEventArgs e)
|
|
{
|
|
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);
|
|
|
|
string textForClipboard = STANDARD_MESSAGEBOX_SEPARATOR_LINES
|
|
+ Text + Environment.NewLine
|
|
+ STANDARD_MESSAGEBOX_SEPARATOR_LINES
|
|
+ richTextBoxMessage.Text + Environment.NewLine
|
|
+ STANDARD_MESSAGEBOX_SEPARATOR_LINES
|
|
+ buttonsTextLine.Replace("&", string.Empty) + Environment.NewLine
|
|
+ STANDARD_MESSAGEBOX_SEPARATOR_LINES;
|
|
|
|
Clipboard.SetText(textForClipboard);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Properties
|
|
|
|
public string CaptionText { get; set; }
|
|
public string MessageText { get; set; }
|
|
|
|
#endregion
|
|
|
|
#region Public show function
|
|
|
|
public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton)
|
|
{
|
|
FlexibleMessageBoxForm flexibleMessageBoxForm = new FlexibleMessageBoxForm
|
|
{
|
|
ShowInTaskbar = false,
|
|
CaptionText = caption,
|
|
MessageText = text
|
|
};
|
|
flexibleMessageBoxForm.FlexibleMessageBoxFormBindingSource.DataSource = flexibleMessageBoxForm;
|
|
flexibleMessageBoxForm.titleLabel.Text = caption;
|
|
|
|
SetDialogButtons(flexibleMessageBoxForm, buttons, defaultButton);
|
|
SetDialogIcon(flexibleMessageBoxForm, icon);
|
|
|
|
flexibleMessageBoxForm.Font = FONT;
|
|
flexibleMessageBoxForm.richTextBoxMessage.Font = FONT;
|
|
|
|
SetDialogSizes(flexibleMessageBoxForm, text, caption);
|
|
SetDialogStartPosition(flexibleMessageBoxForm, owner);
|
|
|
|
return flexibleMessageBoxForm.ShowDialog(owner);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
} |