Emscripten: Support Custom Message Boxes (#12583)
* Allow custom message boxes with colors and multiple buttons to work if Asyncify is enabled * Keep old functionality of using alert when Asyncify is not available * Update testmessage to allow for setting random colors as the color scheme of the message box
This commit is contained in:
@@ -5724,23 +5724,7 @@ bool SDL_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID)
|
|||||||
|
|
||||||
bool SDL_ShowSimpleMessageBox(SDL_MessageBoxFlags flags, const char *title, const char *message, SDL_Window *window)
|
bool SDL_ShowSimpleMessageBox(SDL_MessageBoxFlags flags, const char *title, const char *message, SDL_Window *window)
|
||||||
{
|
{
|
||||||
#ifdef SDL_PLATFORM_EMSCRIPTEN
|
#if defined(SDL_PLATFORM_3DS)
|
||||||
// !!! FIXME: propose a browser API for this, get this #ifdef out of here?
|
|
||||||
/* Web browsers don't (currently) have an API for a custom message box
|
|
||||||
that can block, but for the most common case (SDL_ShowSimpleMessageBox),
|
|
||||||
we can use the standard Javascript alert() function. */
|
|
||||||
if (!title) {
|
|
||||||
title = "";
|
|
||||||
}
|
|
||||||
if (!message) {
|
|
||||||
message = "";
|
|
||||||
}
|
|
||||||
EM_ASM({
|
|
||||||
alert(UTF8ToString($0) + "\n\n" + UTF8ToString($1));
|
|
||||||
},
|
|
||||||
title, message);
|
|
||||||
return true;
|
|
||||||
#elif defined(SDL_PLATFORM_3DS)
|
|
||||||
errorConf errCnf;
|
errorConf errCnf;
|
||||||
bool hasGpuRight;
|
bool hasGpuRight;
|
||||||
|
|
||||||
|
|||||||
@@ -192,10 +192,181 @@ static SDL_VideoDevice *Emscripten_CreateDevice(void)
|
|||||||
return device;
|
return device;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool Emscripten_ShowMessagebox(const SDL_MessageBoxData *messageboxdata, int *buttonID) {
|
||||||
|
if (emscripten_has_asyncify() && SDL_GetHintBoolean(SDL_HINT_EMSCRIPTEN_ASYNCIFY, true)) {
|
||||||
|
char dialog_background[32];
|
||||||
|
char dialog_color[32];
|
||||||
|
char button_border[32];
|
||||||
|
char button_background[32];
|
||||||
|
char button_hovered[32];
|
||||||
|
|
||||||
|
if (messageboxdata->colorScheme) {
|
||||||
|
SDL_MessageBoxColor color = messageboxdata->colorScheme->colors[SDL_MESSAGEBOX_COLOR_BACKGROUND];
|
||||||
|
SDL_snprintf(dialog_background, sizeof(dialog_background), "rgb(%u, %u, %u)", color.r, color.g, color.b);
|
||||||
|
|
||||||
|
color = messageboxdata->colorScheme->colors[SDL_MESSAGEBOX_COLOR_TEXT];
|
||||||
|
SDL_snprintf(dialog_color, sizeof(dialog_color), "rgb(%u, %u, %u)", color.r, color.g, color.b);
|
||||||
|
|
||||||
|
color = messageboxdata->colorScheme->colors[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER];
|
||||||
|
SDL_snprintf(button_border, sizeof(button_border), "rgb(%u, %u, %u)", color.r, color.g, color.b);
|
||||||
|
|
||||||
|
color = messageboxdata->colorScheme->colors[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND];
|
||||||
|
SDL_snprintf(button_background, sizeof(button_background), "rgb(%u, %u, %u)", color.r, color.g, color.b);
|
||||||
|
|
||||||
|
color = messageboxdata->colorScheme->colors[SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED];
|
||||||
|
SDL_snprintf(button_hovered, sizeof(button_hovered), "rgb(%u, %u, %u)", color.r, color.g, color.b);
|
||||||
|
} else {
|
||||||
|
SDL_zero(dialog_background);
|
||||||
|
SDL_zero(dialog_color);
|
||||||
|
SDL_zero(button_border);
|
||||||
|
SDL_zero(button_background);
|
||||||
|
SDL_zero(button_hovered);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Handle parent window when multiple windows can be added in Emscripten builds
|
||||||
|
char dialog_id[64];
|
||||||
|
SDL_snprintf(dialog_id, sizeof(dialog_id), "SDL3_messagebox_%u", SDL_rand_bits());
|
||||||
|
EM_ASM({
|
||||||
|
var title = UTF8ToString($0);
|
||||||
|
var message = UTF8ToString($1);
|
||||||
|
var background = UTF8ToString($2);
|
||||||
|
var color = UTF8ToString($3);
|
||||||
|
var id = UTF8ToString($4);
|
||||||
|
|
||||||
|
// Dialogs are always put in the front of the DOM
|
||||||
|
var dialog = document.createElement("dialog");
|
||||||
|
// Set class to allow for CSS selectors
|
||||||
|
dialog.classList.add("SDL3_messagebox");
|
||||||
|
dialog.id = id;
|
||||||
|
dialog.style.color = color;
|
||||||
|
dialog.style.backgroundColor = background;
|
||||||
|
document.body.append(dialog);
|
||||||
|
|
||||||
|
var h1 = document.createElement("h1");
|
||||||
|
h1.innerText = title;
|
||||||
|
dialog.append(h1);
|
||||||
|
|
||||||
|
var p = document.createElement("p");
|
||||||
|
p.innerText = message;
|
||||||
|
dialog.append(p);
|
||||||
|
|
||||||
|
dialog.showModal();
|
||||||
|
}, messageboxdata->title, messageboxdata->message, dialog_background, dialog_color, dialog_id);
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < messageboxdata->numbuttons; ++i) {
|
||||||
|
SDL_MessageBoxButtonData button = messageboxdata->buttons[i];
|
||||||
|
|
||||||
|
const int created = EM_ASM_INT({
|
||||||
|
var dialog_id = UTF8ToString($0);
|
||||||
|
var text = UTF8ToString($1);
|
||||||
|
var responseId = $2;
|
||||||
|
var clickOnReturn = $3;
|
||||||
|
var clickOnEscape = $4;
|
||||||
|
var border = UTF8ToString($5);
|
||||||
|
var background = UTF8ToString($6);
|
||||||
|
var hovered = UTF8ToString($7);
|
||||||
|
|
||||||
|
var dialog = document.getElementById(dialog_id);
|
||||||
|
if (!dialog) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var button = document.createElement("button");
|
||||||
|
button.innerText = text;
|
||||||
|
button.style.borderColor = border;
|
||||||
|
button.style.backgroundColor = background;
|
||||||
|
|
||||||
|
dialog.addEventListener('keydown', function(e) {
|
||||||
|
if (clickOnReturn && e.key === "Enter") {
|
||||||
|
e.preventDefault();
|
||||||
|
button.click();
|
||||||
|
} else if (clickOnEscape && e.key === "Escape") {
|
||||||
|
e.preventDefault();
|
||||||
|
button.click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dialog.addEventListener('cancel', function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
button.onmouseenter = function(e){
|
||||||
|
button.style.backgroundColor = hovered;
|
||||||
|
};
|
||||||
|
button.onmouseleave = function(e){
|
||||||
|
button.style.backgroundColor = background;
|
||||||
|
};
|
||||||
|
button.onclick = function(e) {
|
||||||
|
dialog.close(responseId);
|
||||||
|
};
|
||||||
|
|
||||||
|
dialog.append(button);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
dialog_id,
|
||||||
|
button.text,
|
||||||
|
button.buttonID,
|
||||||
|
button.flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT,
|
||||||
|
button.flags & SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT,
|
||||||
|
button_border,
|
||||||
|
button_background,
|
||||||
|
button_hovered
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!created) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
// give back control to browser for screen refresh
|
||||||
|
emscripten_sleep(0);
|
||||||
|
|
||||||
|
const int dialog_open = EM_ASM_INT({
|
||||||
|
var dialog_id = UTF8ToString($0);
|
||||||
|
|
||||||
|
var dialog = document.getElementById(dialog_id);
|
||||||
|
if (!dialog) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return dialog.open;
|
||||||
|
}, dialog_id);
|
||||||
|
|
||||||
|
if (dialog_open) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
*buttonID = EM_ASM_INT({
|
||||||
|
var dialog_id = UTF8ToString($0);
|
||||||
|
var dialog = document.getElementById(dialog_id);
|
||||||
|
if (!dialog) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return parseInt(dialog.returnValue);
|
||||||
|
}
|
||||||
|
catch(e)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}, dialog_id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Cannot add elements to DOM and block without Asyncify. So, fall back to the alert function.
|
||||||
|
EM_ASM({
|
||||||
|
alert(UTF8ToString($0) + "\n\n" + UTF8ToString($1));
|
||||||
|
}, messageboxdata->title, messageboxdata->message);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
VideoBootStrap Emscripten_bootstrap = {
|
VideoBootStrap Emscripten_bootstrap = {
|
||||||
EMSCRIPTENVID_DRIVER_NAME, "SDL emscripten video driver",
|
EMSCRIPTENVID_DRIVER_NAME, "SDL emscripten video driver",
|
||||||
Emscripten_CreateDevice,
|
Emscripten_CreateDevice,
|
||||||
NULL, // no ShowMessageBox implementation
|
Emscripten_ShowMessagebox,
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ quit(int rc)
|
|||||||
static int SDLCALL
|
static int SDLCALL
|
||||||
button_messagebox(void *eventNumber)
|
button_messagebox(void *eventNumber)
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
const SDL_MessageBoxButtonData buttons[] = {
|
const SDL_MessageBoxButtonData buttons[] = {
|
||||||
{ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT,
|
{ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT,
|
||||||
0,
|
0,
|
||||||
@@ -43,52 +44,78 @@ button_messagebox(void *eventNumber)
|
|||||||
{ SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT,
|
{ SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT,
|
||||||
1,
|
1,
|
||||||
"Cancel" },
|
"Cancel" },
|
||||||
|
{ 0,
|
||||||
|
2,
|
||||||
|
"Retry" }
|
||||||
};
|
};
|
||||||
|
|
||||||
SDL_MessageBoxData data = {
|
SDL_MessageBoxData data = {
|
||||||
SDL_MESSAGEBOX_INFORMATION,
|
SDL_MESSAGEBOX_INFORMATION,
|
||||||
NULL, /* no parent window */
|
NULL, /* no parent window */
|
||||||
"Custom MessageBox",
|
"Custom MessageBox",
|
||||||
"This is a custom messagebox",
|
"This is a custom messagebox",
|
||||||
2,
|
sizeof(buttons) / sizeof(SDL_MessageBoxButtonData),
|
||||||
NULL, /* buttons */
|
NULL, /* buttons */
|
||||||
NULL /* Default color scheme */
|
NULL /* Default color scheme */
|
||||||
};
|
};
|
||||||
|
|
||||||
int button = -1;
|
for (i = 0; ; ++i) {
|
||||||
int success = 0;
|
SDL_MessageBoxColorScheme colorScheme;
|
||||||
data.buttons = buttons;
|
if (i != 0) {
|
||||||
if (eventNumber) {
|
int j;
|
||||||
data.message = "This is a custom messagebox from a background thread.";
|
for (j = 0; j < SDL_MESSAGEBOX_COLOR_COUNT; ++j) {
|
||||||
}
|
colorScheme.colors[j].r = SDL_rand(256);
|
||||||
|
colorScheme.colors[j].g = SDL_rand(256);
|
||||||
|
colorScheme.colors[j].b = SDL_rand(256);
|
||||||
|
}
|
||||||
|
data.colorScheme = &colorScheme;
|
||||||
|
} else {
|
||||||
|
data.colorScheme = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int button = -1;
|
||||||
|
data.buttons = buttons;
|
||||||
|
if (eventNumber) {
|
||||||
|
data.message = "This is a custom messagebox from a background thread.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SDL_ShowMessageBox(&data, &button)) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error Presenting MessageBox: %s", SDL_GetError());
|
||||||
|
if (eventNumber) {
|
||||||
|
SDL_Event event;
|
||||||
|
event.type = (Uint32)(intptr_t)eventNumber;
|
||||||
|
SDL_PushEvent(&event);
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
quit(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* text;
|
||||||
|
if (button == 1) {
|
||||||
|
text = "Cancel";
|
||||||
|
} else if (button == 2) {
|
||||||
|
text = "Retry";
|
||||||
|
} else {
|
||||||
|
text = "OK";
|
||||||
|
}
|
||||||
|
SDL_Log("Pressed button: %d, %s", button, button == -1 ? "[closed]" : text);
|
||||||
|
|
||||||
success = SDL_ShowMessageBox(&data, &button);
|
|
||||||
if (success == -1) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error Presenting MessageBox: %s", SDL_GetError());
|
|
||||||
if (eventNumber) {
|
if (eventNumber) {
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
event.type = (Uint32)(intptr_t)eventNumber;
|
event.type = (Uint32)(intptr_t)eventNumber;
|
||||||
SDL_PushEvent(&event);
|
SDL_PushEvent(&event);
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
quit(2);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
SDL_Log("Pressed button: %d, %s", button, button == -1 ? "[closed]" : button == 1 ? "Cancel"
|
|
||||||
: "OK");
|
|
||||||
|
|
||||||
if (eventNumber) {
|
if (button == 2) {
|
||||||
SDL_Event event;
|
continue;
|
||||||
event.type = (Uint32)(intptr_t)eventNumber;
|
}
|
||||||
SDL_PushEvent(&event);
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int success;
|
bool success;
|
||||||
SDLTest_CommonState *state;
|
SDLTest_CommonState *state;
|
||||||
|
|
||||||
/* Initialize test framework */
|
/* Initialize test framework */
|
||||||
|
|||||||
Reference in New Issue
Block a user