Самый простой способ открыть окно с сообщением об ошибке в Windows, NIX и MacOS?

Я пишу довольно минимальную кроссплатформенную программу, которая использует OpenGL для отображения и GLFW для создания кроссплатформенного окна.

Я хотел бы выдать пользователю значимое сообщение об ошибке при выходе в некоторых ситуациях — простую ошибку «Ошибка: не удалось инициализировать из-за n» всплывающего сообщения с окном «ОК» перед выходом.

Я действительно не хочу, чтобы добавление полнофункциональной графической системы, такой как wxWidgets, просто вызывало одно сообщение об ошибке. Я не возражаю написать три разные подпрограммно-зависимые подпрограммы, если использование API-интерфейсов нативной платформы действительно является самым простым способом, но мне интересно, нет ли уже очень легкой / минимальной кроссплатформенной библиотеки, способной сделать это для меня?

3

Решение

Поскольку не казалось, что когда-то существовала простая межплатформенная библиотека сообщений, я решил создать ее.

Чтобы вызывающий не беспокоился о специфике, зависящей от платформы, я создал один интерфейс, который я поместил в заголовочный файл:

Boxer.h

#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

Затем я создал файлы реализации для каждой из операционных систем, которые я хотел поддерживать (Windows, OS X и Linux (используя GTK +)). При сборке выбирается один из файлов реализации в зависимости от целевой операционной системы.

boxer_linux.cpp

#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

boxer_osx.mm

#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

boxer_win.cpp

#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

Полная версия библиотеки доступна на моем GitHub (под лицензией MIT, так что не стесняйтесь ее использовать!): https://github.com/aaronmjacobs/Boxer

3

Другие решения

Чтобы не зависеть от внешних библиотек или необходимости поддерживать больше «кроссплатформенного» кода, вы рассматривали возможность сделать это с OpenGL? Рисовать красный квад с текстом внутри?

Скорее всего, в какой-то момент у вас будет какой-то пользовательский интерфейс, чтобы вы могли использовать этот код повторно.

Изменить: со спецификациями, что сообщения об ошибках могут появляться до того, как OpenGL будет готов, я должен сказать, что единственное, что мне кажется, это написать минимальную оболочку для каждой ОС, чтобы попытаться естественным образом отобразить окно сообщения. Эта тема может быть полезной ссылкой.

1

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector