Reputation: 16706
I'm writing a fairly minimal cross-platform program that uses OpenGL for display and GLFW for the cross-platform window creation.
I would like to pop up a meaningful error message to the user when exiting in some situations - a simple "Error: could not initialise because of n" pop-up message with an "ok" click box prior to exiting.
I really don't want the bloat of adding a full-featured gui system such as wxWidgets just to pop up a single error message. I don't mind writing three different platform-dependent subroutines if using the native platform APIs is really the simplest way, but I wonder if there isn't already a very lightweight / minimal cross-platform library capable of doing this for me?
Upvotes: 4
Views: 6651
Reputation: 1
Use this. It is very simple. One C/C++ file & header provides 8 functions:
https://sourceforge.net/projects/tinyfiledialogs/
Upvotes: 0
Reputation: 532
Since it didn't appear that a simple cross-platform message box library used to exist, I decided to create one.
In order to let the caller not worry about the platform-dependent specifics, I created a single interface which I placed in a header file:
#ifndef BOXER_H
#define BOXER_H
namespace boxer {
enum class Style {
Info,
Warning,
Error,
Question
};
enum class Buttons {
OK,
OKCancel,
YesNo
};
enum class Selection {
OK,
Cancel,
Yes,
No,
None
};
const Style DEFAULT_STYLE = Style::Info;
const Buttons DEFAULT_BUTTONS = Buttons::OK;
Selection show(const char *message, const char *title, Style style, Buttons buttons);
inline Selection show(const char *message, const char *title, Style style) {
return show(message, title, style, DEFAULT_BUTTONS);
}
inline Selection show(const char *message, const char *title, Buttons buttons) {
return show(message, title, DEFAULT_STYLE, buttons);
}
inline Selection show(const char *message, const char *title) {
return show(message, title, DEFAULT_STYLE, DEFAULT_BUTTONS);
}
} // namespace boxer
#endif
Next, I created implementation files for each of the operating systems I wanted to support (Windows, OS X, and Linux (using GTK+)). When building, one of the implementation files is chosen, based on the target operating system.
#include <boxer/boxer.h>
#include <gtk/gtk.h>
namespace boxer {
namespace {
GtkMessageType getMessageType(Style style) {
switch (style) {
case Style::Info:
return GTK_MESSAGE_INFO;
case Style::Warning:
return GTK_MESSAGE_WARNING;
case Style::Error:
return GTK_MESSAGE_ERROR;
case Style::Question:
return GTK_MESSAGE_QUESTION;
default:
return GTK_MESSAGE_INFO;
}
}
GtkButtonsType getButtonsType(Buttons buttons) {
switch (buttons) {
case Buttons::OK:
return GTK_BUTTONS_OK;
case Buttons::OKCancel:
return GTK_BUTTONS_OK_CANCEL;
case Buttons::YesNo:
return GTK_BUTTONS_YES_NO;
default:
return GTK_BUTTONS_OK;
}
}
Selection getSelection(gint response) {
switch (response) {
case GTK_RESPONSE_OK:
return Selection::OK;
case GTK_RESPONSE_CANCEL:
return Selection::Cancel;
case GTK_RESPONSE_YES:
return Selection::Yes;
case GTK_RESPONSE_NO:
return Selection::No;
default:
return Selection::None;
}
}
} // namespace
Selection show(const char *message, const char *title, Style style, Buttons buttons) {
if (!gtk_init_check(0, NULL)) {
return Selection::None;
}
GtkWidget *dialog = gtk_message_dialog_new(NULL,
GTK_DIALOG_MODAL,
getMessageType(style),
getButtonsType(buttons),
"%s",
message);
gtk_window_set_title(GTK_WINDOW(dialog), title);
Selection selection = getSelection(gtk_dialog_run(GTK_DIALOG(dialog)));
gtk_widget_destroy(GTK_WIDGET(dialog));
while (g_main_context_iteration(NULL, false));
return selection;
}
} // namespace boxer
#include <boxer/boxer.h>
#import <Cocoa/Cocoa.h>
namespace boxer {
namespace {
NSString* const OK_STR = @"OK";
NSString* const CANCEL_STR = @"Cancel";
NSString* const YES_STR = @"Yes";
NSString* const NO_STR = @"No";
NSAlertStyle getAlertStyle(Style style) {
switch (style) {
case Style::Info:
return NSInformationalAlertStyle;
case Style::Warning:
return NSWarningAlertStyle;
case Style::Error:
return NSCriticalAlertStyle;
case Style::Question:
return NSWarningAlertStyle;
default:
return NSInformationalAlertStyle;
}
}
void setButtons(NSAlert *alert, Buttons buttons) {
switch (buttons) {
case Buttons::OK:
[alert addButtonWithTitle:OK_STR];
break;
case Buttons::OKCancel:
[alert addButtonWithTitle:OK_STR];
[alert addButtonWithTitle:CANCEL_STR];
break;
case Buttons::YesNo:
[alert addButtonWithTitle:YES_STR];
[alert addButtonWithTitle:NO_STR];
break;
default:
[alert addButtonWithTitle:OK_STR];
}
}
Selection getSelection(int index, Buttons buttons) {
switch (buttons) {
case Buttons::OK:
return index == NSAlertFirstButtonReturn ? Selection::OK : Selection::None;
case Buttons::OKCancel:
if (index == NSAlertFirstButtonReturn) {
return Selection::OK;
} else if (index == NSAlertSecondButtonReturn) {
return Selection::Cancel;
} else {
return Selection::None;
}
case Buttons::YesNo:
if (index == NSAlertFirstButtonReturn) {
return Selection::Yes;
} else if (index == NSAlertSecondButtonReturn) {
return Selection::No;
} else {
return Selection::None;
}
default:
return Selection::None;
}
}
} // namespace
Selection show(const char *message, const char *title, Style style, Buttons buttons) {
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:[NSString stringWithCString:title
encoding:[NSString defaultCStringEncoding]]];
[alert setInformativeText:[NSString stringWithCString:message
encoding:[NSString defaultCStringEncoding]]];
[alert setAlertStyle:getAlertStyle(style)];
setButtons(alert, buttons);
Selection selection = getSelection([alert runModal], buttons);
[alert release];
return selection;
}
} // namespace boxer
#include <boxer/boxer.h>
#include <windows.h>
namespace boxer {
namespace {
UINT getIcon(Style style) {
switch (style) {
case Style::Info:
return MB_ICONINFORMATION;
case Style::Warning:
return MB_ICONWARNING;
case Style::Error:
return MB_ICONERROR;
case Style::Question:
return MB_ICONQUESTION;
default:
return MB_ICONINFORMATION;
}
}
UINT getButtons(Buttons buttons) {
switch (buttons) {
case Buttons::OK:
return MB_OK;
case Buttons::OKCancel:
return MB_OKCANCEL;
case Buttons::YesNo:
return MB_YESNO;
default:
return MB_OK;
}
}
Selection getSelection(int response) {
switch (response) {
case IDOK:
return Selection::OK;
case IDCANCEL:
return Selection::Cancel;
case IDYES:
return Selection::Yes;
case IDNO:
return Selection::No;
default:
return Selection::None;
}
}
} // namespace
Selection show(const char *message, const char *title, Style style, Buttons buttons) {
UINT flags = MB_TASKMODAL;
flags |= getIcon(style);
flags |= getButtons(buttons);
return getSelection(MessageBox(NULL, message, title, flags));
}
} // namespace boxer
A full implementation of the library is available on my GitHub (under the MIT license, so feel free to use it!): https://github.com/aaronmjacobs/Boxer
Upvotes: 9
Reputation: 7773
Not to depend on external libraries or having to maintain more "cross-platform" code, have you considered doing it with OpenGL
? Draw a red quad with text inside?
You will most likely have at some point some sort of user interface so you could probably reuse that code.
Edit: with the specs that error messages could appear before OpenGL is ready, I will have to say that what seems to me the only option would be to write a minimal wrapper for each OS to try to natively display a message box. This thread can be a useful reference.
Upvotes: 1