394 lines
14 KiB
C++
394 lines
14 KiB
C++
#pragma once
|
|
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
|
// Windows Header Files
|
|
#include <windows.h>
|
|
|
|
using namespace System;
|
|
using namespace System::Windows::Forms;
|
|
using namespace System::Drawing;
|
|
|
|
// ported from https://stackoverflow.com/a/7785745
|
|
// original code by user LarsTech https://stackoverflow.com/users/719186/larstech
|
|
|
|
static ref class SkinnedMessageBox
|
|
{
|
|
private: ref class SkinnedMessageBoxForm : Form
|
|
{
|
|
private:
|
|
void SkinnedMessageBoxForm_Shown(Object^ sender, EventArgs^ e)
|
|
{
|
|
Activate();
|
|
}
|
|
|
|
void SkinnedMessageBoxForm_FormClosed(Object^ sender, FormClosedEventArgs^ e)
|
|
{
|
|
if (CancelButton && (DialogResult == System::Windows::Forms::DialogResult::None || DialogResult == System::Windows::Forms::DialogResult::Cancel))
|
|
DialogResult = CancelButton->DialogResult;
|
|
}
|
|
|
|
void InitComponentNew()
|
|
{
|
|
this->SuspendLayout();
|
|
|
|
this->AutoSize = true;
|
|
this->FormBorderStyle = System::Windows::Forms::FormBorderStyle::FixedDialog;
|
|
this->Width = 350;
|
|
this->Height = 140;
|
|
this->StartPosition = FormStartPosition::CenterScreen;
|
|
this->MaximizeBox = false;
|
|
this->MinimizeBox = false;
|
|
this->ShowInTaskbar = false;
|
|
this->ShowIcon = false;
|
|
__try
|
|
{
|
|
this->Icon = Application::OpenForms[0]->Icon;
|
|
}
|
|
__except (1) { }
|
|
this->Shown += gcnew EventHandler(this, &SkinnedMessageBoxForm::SkinnedMessageBoxForm_Shown);
|
|
this->FormClosed += gcnew FormClosedEventHandler(this, &SkinnedMessageBoxForm::SkinnedMessageBoxForm_FormClosed);
|
|
|
|
//this->Font = SystemFonts::MessageBoxFont;
|
|
//this->Font = gcnew Font(SystemFonts::MessageBoxFont->FontFamily, SystemFonts::MessageBoxFont->SizeInPoints);
|
|
this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::None;
|
|
|
|
this->ResumeLayout(false);
|
|
this->PerformLayout();
|
|
}
|
|
|
|
float ScaleWidth = 1.0;
|
|
float ScaleHeight = 1.0;
|
|
int btnWidth = 85;
|
|
int btnHeight = 23;
|
|
int btnPadding = 12;
|
|
|
|
static cli::array<Button^>^ buttonrefs = gcnew cli::array<Button^>{ gcnew Button, gcnew Button, gcnew Button };
|
|
int numbuttons = 1;
|
|
|
|
Button^ AddBtn(String^ text, System::Windows::Forms::DialogResult result, int btnNumber)
|
|
{
|
|
Button^ btn = buttonrefs[btnNumber];
|
|
btn->Text = text;
|
|
btn->DialogResult = result;
|
|
btn->Anchor = AnchorStyles::Bottom | AnchorStyles::Right;
|
|
btn->Width = btnWidth;
|
|
btn->Height = btnHeight;
|
|
|
|
btn->Location = Point(ClientSize.Width - btnWidth * (numbuttons - btnNumber) - btnPadding * (numbuttons - btnNumber), ClientSize.Height - btnHeight - btnPadding);
|
|
Controls->Add(btn);
|
|
|
|
//buttonrefs[btnNumber] = btn;
|
|
return btn;
|
|
}
|
|
|
|
void SetBackCol(Control^ elem, Color color)
|
|
{
|
|
Type^ elemType = elem->GetType();
|
|
|
|
if ((elem->HasChildren || elemType == Button::typeid || elemType == ComboBox::typeid) && elemType != NumericUpDown::typeid)
|
|
{
|
|
elem->BackColor = color;
|
|
|
|
for (int i = 0; i < elem->Controls->Count; i++)
|
|
{
|
|
SetBackCol(elem->Controls[i], color);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SetForeCol(Control^ elem, Color color)
|
|
{
|
|
Type^ elemType = elem->GetType();
|
|
|
|
if (elemType != NumericUpDown::typeid && elemType != TextBox::typeid)
|
|
{
|
|
elem->ForeColor = color;
|
|
}
|
|
if (elem->HasChildren && elemType != NumericUpDown::typeid)
|
|
{
|
|
for (int i = 0; i < elem->Controls->Count; i++)
|
|
{
|
|
SetForeCol(elem->Controls[i], color);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SetUIStyle(Control^ elem, FlatStyle style)
|
|
{
|
|
Type^ elemType = elem->GetType();
|
|
|
|
if (elemType == Button::typeid)
|
|
{
|
|
((Button^)elem)->FlatStyle = style;
|
|
}
|
|
else if (elemType == ComboBox::typeid)
|
|
{
|
|
((ComboBox^)elem)->FlatStyle = style;
|
|
}
|
|
else if (elemType == CheckBox::typeid)
|
|
{
|
|
((CheckBox^)elem)->FlatStyle = style;
|
|
}
|
|
|
|
if (elem->HasChildren && elemType != NumericUpDown::typeid)
|
|
{
|
|
for (int i = 0; i < elem->Controls->Count; i++)
|
|
{
|
|
SetUIStyle(elem->Controls[i], style);
|
|
}
|
|
}
|
|
}
|
|
|
|
public:
|
|
SkinnedMessageBoxForm(IWin32Window^ owner, String^ message, String^ caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultbutton, MessageBoxOptions options)
|
|
{
|
|
InitComponentNew();
|
|
ScaleWidth = Font->SizeInPoints / 8.0f;
|
|
ScaleHeight = ScaleWidth;
|
|
btnWidth *= ScaleWidth;
|
|
btnHeight *= ScaleHeight;
|
|
btnPadding *= ScaleWidth;
|
|
|
|
Width *= ScaleWidth;
|
|
Height *= ScaleHeight;
|
|
|
|
if (owner->Handle != IntPtr::Zero)
|
|
{
|
|
Owner = Control::FromHandle(owner->Handle)->FindForm();
|
|
}
|
|
else
|
|
{
|
|
Owner = ActiveForm;
|
|
}
|
|
|
|
Text = caption;
|
|
|
|
System::Windows::Forms::Padding lblMargin = System::Windows::Forms::Padding(14 * ScaleWidth, 0 * ScaleHeight, 14 * ScaleWidth, 45 * ScaleHeight);
|
|
Point lblLocation = Point(14 * ScaleWidth, 14 * ScaleHeight);
|
|
Drawing::Size lblMinSize = Drawing::Size(ClientSize.Width - lblLocation.X - lblMargin.Right, 0);
|
|
Drawing::Size lblMaxSize = Drawing::Size(600 * ScaleWidth, 0 * ScaleHeight);
|
|
|
|
Label^ MessageLbl = gcnew Label();
|
|
MessageLbl->Text = message;
|
|
MessageLbl->AutoSize = true;
|
|
MessageLbl->Margin = lblMargin;
|
|
MessageLbl->Location = lblLocation;
|
|
MessageLbl->MinimumSize = lblMinSize;
|
|
MessageLbl->MaximumSize = lblMaxSize;
|
|
|
|
Controls->Add(MessageLbl);
|
|
|
|
if (buttons == MessageBoxButtons::OK)
|
|
{
|
|
numbuttons = 1;
|
|
Button^ OkBtn = AddBtn("&OK", System::Windows::Forms::DialogResult::OK, 0);
|
|
CancelButton = OkBtn;
|
|
//(not sure why, but this matches official .NET behaviour)
|
|
}
|
|
else if (buttons == MessageBoxButtons::OKCancel)
|
|
{
|
|
numbuttons = 2;
|
|
AddBtn("&OK", System::Windows::Forms::DialogResult::OK, 0);
|
|
Button^ CancelBtn = AddBtn("&Cancel", System::Windows::Forms::DialogResult::Cancel, 1);
|
|
CancelButton = CancelBtn;
|
|
}
|
|
else if (buttons == MessageBoxButtons::YesNo)
|
|
{
|
|
numbuttons = 2;
|
|
AddBtn("&Yes", System::Windows::Forms::DialogResult::Yes, 0);
|
|
Button^ NoBtn = AddBtn("&No", System::Windows::Forms::DialogResult::No, 1);
|
|
CancelButton = NoBtn;
|
|
//(official .NET doesn't set cancel button, but this seems pretty clear to me...)
|
|
}
|
|
else if (buttons == MessageBoxButtons::RetryCancel)
|
|
{
|
|
numbuttons = 2;
|
|
AddBtn("&Retry", System::Windows::Forms::DialogResult::Retry, 0);
|
|
Button^ CancelBtn = AddBtn("&Cancel", System::Windows::Forms::DialogResult::Cancel, 1);
|
|
CancelButton = CancelBtn;
|
|
}
|
|
else if (buttons == MessageBoxButtons::YesNoCancel)
|
|
{
|
|
numbuttons = 3;
|
|
AddBtn("&Yes", System::Windows::Forms::DialogResult::Yes, 0);
|
|
AddBtn("&No", System::Windows::Forms::DialogResult::No, 1);
|
|
Button^ CancelBtn = AddBtn("&Cancel", System::Windows::Forms::DialogResult::Cancel, 2);
|
|
CancelButton = CancelBtn;
|
|
}
|
|
else if (buttons == MessageBoxButtons::AbortRetryIgnore)
|
|
{
|
|
numbuttons = 3;
|
|
AddBtn("&Abort", System::Windows::Forms::DialogResult::Abort, 0);
|
|
AddBtn("&Retry", System::Windows::Forms::DialogResult::Retry, 1);
|
|
AddBtn("&Ignore", System::Windows::Forms::DialogResult::Ignore, 2);
|
|
//this.CancelButton = IgnoreBtn;
|
|
//(official .NET doesn't set cancel button)
|
|
}
|
|
else
|
|
{
|
|
throw gcnew System::ComponentModel::InvalidEnumArgumentException("buttons", (int)buttons, MessageBoxButtons::typeid);
|
|
}
|
|
|
|
if (!CancelButton)
|
|
ControlBox = false;
|
|
|
|
|
|
if (icon != MessageBoxIcon::None)
|
|
{
|
|
int iconsize = 32 * ScaleWidth;
|
|
int iconpadding = 12 * ScaleWidth;
|
|
int textpadding = -8 * ScaleWidth;
|
|
|
|
PictureBox^ iconPB = gcnew PictureBox();
|
|
iconPB->Width = iconsize;
|
|
iconPB->Height = iconsize;
|
|
iconPB->Top = 10 * ScaleHeight;
|
|
iconPB->Left = 10 * ScaleWidth;
|
|
iconPB->AccessibleName = "Icon";
|
|
|
|
// there are many duplicated values:
|
|
// https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.messageboxicon?view=netframework-4.7.2
|
|
if (icon == MessageBoxIcon::Error) // also Hand and Stop
|
|
{
|
|
Drawing::Icon^ erricon = gcnew Drawing::Icon(SystemIcons::Error, iconsize, iconsize);
|
|
iconPB->Image = erricon->ToBitmap();
|
|
iconPB->AccessibleDescription = "Error";
|
|
//this.Icon = SystemIcons.Error;
|
|
System::Media::SystemSounds::Hand->Play();
|
|
}
|
|
else if (icon == MessageBoxIcon::Question)
|
|
{
|
|
Drawing::Icon^ qicon = gcnew Drawing::Icon(SystemIcons::Question, iconsize, iconsize);
|
|
iconPB->Image = qicon->ToBitmap();
|
|
iconPB->AccessibleDescription = "Question";
|
|
//this.Icon = SystemIcons.Question;
|
|
System::Media::SystemSounds::Question->Play();
|
|
}
|
|
else if (icon == MessageBoxIcon::Warning) // also Exclamation
|
|
{
|
|
Drawing::Icon^ warnicon = gcnew Drawing::Icon(SystemIcons::Warning, iconsize, iconsize);
|
|
iconPB->Image = warnicon->ToBitmap();
|
|
//System::ComponentModel::ComponentResourceManager^ resources = (gcnew System::ComponentModel::ComponentResourceManager(SkinnedMessageBox::typeid));
|
|
//iconPB->Image = cli::safe_cast<System::Drawing::Image^>(resources->GetObject(L"PokeSnarf2x"));
|
|
iconPB->AccessibleDescription = "Warning";
|
|
//this.Icon = SystemIcons.Warning;
|
|
textpadding -= 2 * ScaleWidth; // triangle shape looks worse without this
|
|
System::Media::SystemSounds::Exclamation->Play();
|
|
}
|
|
else if (icon == MessageBoxIcon::Information) // also Asterisk
|
|
{
|
|
Drawing::Icon^ infoicon = gcnew Drawing::Icon(SystemIcons::Information, iconsize, iconsize);
|
|
iconPB->Image = infoicon->ToBitmap();
|
|
iconPB->AccessibleDescription = "Information";
|
|
//this.Icon = SystemIcons.Question;
|
|
System::Media::SystemSounds::Asterisk->Play();
|
|
}
|
|
else
|
|
{
|
|
throw gcnew System::ComponentModel::InvalidEnumArgumentException("icon", (int)icon, MessageBoxIcon::typeid);
|
|
}
|
|
|
|
MessageLbl->MinimumSize = Drawing::Size(MessageLbl->MinimumSize.Width - (iconsize + iconpadding + textpadding), MessageLbl->MinimumSize.Height);
|
|
MessageLbl->Left += (iconsize + iconpadding + textpadding);
|
|
Controls->Add(iconPB);
|
|
}
|
|
|
|
if (defaultbutton == MessageBoxDefaultButton::Button1)
|
|
{
|
|
AcceptButton = buttonrefs[0];
|
|
ActiveControl = buttonrefs[0];
|
|
}
|
|
else if (defaultbutton == MessageBoxDefaultButton::Button2)
|
|
{
|
|
if (!Controls->Contains(buttonrefs[1])) throw gcnew ArgumentOutOfRangeException("defaultbutton", "defaultbutton cannot be set to a button number that does not exist");
|
|
AcceptButton = buttonrefs[1];
|
|
ActiveControl = buttonrefs[1];
|
|
}
|
|
else if (defaultbutton == MessageBoxDefaultButton::Button3)
|
|
{
|
|
if (!Controls->Contains(buttonrefs[2])) throw gcnew ArgumentOutOfRangeException("defaultbutton", "defaultbutton cannot be set to a button number that does not exist");
|
|
AcceptButton = buttonrefs[2];
|
|
ActiveControl = buttonrefs[2];
|
|
}
|
|
else
|
|
{
|
|
throw gcnew System::ComponentModel::InvalidEnumArgumentException("defaultbutton", (int)defaultbutton, MessageBoxDefaultButton::typeid);
|
|
}
|
|
|
|
|
|
// TODO: TEXT ALIGNMENT AND RTL MODE
|
|
/*
|
|
// MessageBoxOptions are more like flags than concrete settings.
|
|
// For ease of use, we assume default value is whatever winforms gives us in current locale -- but we override if any value is set
|
|
if ((options & MessageBoxOptions.RightAlign) == MessageBoxOptions.RightAlign) MessageLbl.TextAlign = ContentAlignment.TopRight;
|
|
else if (options != 0) MessageLbl.TextAlign = ContentAlignment.TopLeft;
|
|
|
|
// RtlReading should be what actually affects layout, and RightAlign only affects the text label
|
|
if ((options & MessageBoxOptions.RtlReading) == MessageBoxOptions.RtlReading)
|
|
{
|
|
foreach (Control control in this.Controls)
|
|
{
|
|
control.Left = this.ClientSize.Width - control.Right;
|
|
control.RightToLeft = RightToLeft.Yes;
|
|
}
|
|
this.RightToLeft = RightToLeft.Yes;
|
|
this.RightToLeftLayout = true;
|
|
}
|
|
else if (options != 0)
|
|
{
|
|
foreach (Control control in this.Controls)
|
|
{
|
|
control.RightToLeft = RightToLeft.No;
|
|
}
|
|
this.RightToLeft = RightToLeft.No;
|
|
this.RightToLeftLayout = false;
|
|
}
|
|
*/
|
|
|
|
SetBackCol(this, Color::FromArgb(64, 64, 64));
|
|
SetForeCol(this, Color::White);
|
|
SetUIStyle(this, FlatStyle::Flat);
|
|
}
|
|
|
|
SkinnedMessageBoxForm(String^ message, String^ caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultbutton, MessageBoxOptions options)
|
|
{
|
|
SkinnedMessageBoxForm(NativeWindow::FromHandle((IntPtr::Zero)), message, caption, buttons, icon, defaultbutton, options);
|
|
}
|
|
};
|
|
|
|
public:
|
|
static DialogResult Show(IWin32Window^ owner, String^ message, String^ caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultbutton, MessageBoxOptions options)
|
|
{
|
|
// I probably shouldn't do this, but from what I can tell this should be safe enough -- out of thread owner is allowed
|
|
// and it only seems to appear when debugging (???)
|
|
Control::CheckForIllegalCrossThreadCalls = false;
|
|
SkinnedMessageBoxForm^ mb = gcnew SkinnedMessageBoxForm(owner, message, caption, buttons, icon, defaultbutton, options);
|
|
DialogResult res = mb->ShowDialog();
|
|
Control::CheckForIllegalCrossThreadCalls = true;
|
|
return res;
|
|
}
|
|
|
|
static DialogResult Show(IWin32Window^ owner, String^ message, String^ caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultbutton)
|
|
{
|
|
return Show(owner, message, caption, buttons, icon, defaultbutton, (MessageBoxOptions)0);
|
|
}
|
|
|
|
static DialogResult Show(IWin32Window^ owner, String^ message, String^ caption, MessageBoxButtons buttons, MessageBoxIcon icon)
|
|
{
|
|
return Show(owner, message, caption, buttons, icon, MessageBoxDefaultButton::Button1, (MessageBoxOptions)0);
|
|
}
|
|
|
|
static DialogResult Show(IWin32Window^ owner, String^ message, String^ caption, MessageBoxButtons buttons)
|
|
{
|
|
return Show(owner, message, caption, buttons, MessageBoxIcon::None, MessageBoxDefaultButton::Button1, (MessageBoxOptions)0);
|
|
}
|
|
|
|
static DialogResult Show(IWin32Window^ owner, String^ message, String^ caption)
|
|
{
|
|
return Show(owner, message, caption, MessageBoxButtons::OK, MessageBoxIcon::None, MessageBoxDefaultButton::Button1, (MessageBoxOptions)0);
|
|
}
|
|
|
|
static DialogResult Show(IWin32Window^ owner, String^ message)
|
|
{
|
|
return Show(owner, message, "", MessageBoxButtons::OK, MessageBoxIcon::None, MessageBoxDefaultButton::Button1, (MessageBoxOptions)0);
|
|
}
|
|
}; |