3

私は、表示に OpenGL を使用し、クロスプラットフォーム ウィンドウの作成に GLFW を使用する、かなり最小限のクロスプラットフォーム プログラムを作成しています。

いくつかの状況で終了するときに、意味のあるエラー メッセージをユーザーにポップアップ表示したいと思います。単純な「エラー: n のために初期化できませんでした」というポップアップ メッセージで、終了する前に「OK」をクリックします。

wxWidgets などのフル機能の GUI システムを追加して、1 つのエラー メッセージをポップアップ表示するだけで肥大化することは本当に望んでいません。ネイティブ プラットフォーム API を使用することが本当に最も簡単な方法である場合は、プラットフォームに依存する 3 つの異なるサブルーチンを作成してもかまいませんが、これを実行できる非常に軽量で最小限のクロスプラットフォーム ライブラリがまだないのでしょうか?

4

2 に答える 2

7

シンプルなクロスプラットフォームのメッセージ ボックス ライブラリが存在していないようだったので、作成することにしました。

呼び出し元がプラットフォームに依存する詳細を気にしないようにするために、ヘッダー ファイルに配置する単一のインターフェイスを作成しました。

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+ を使用)) ごとに実装ファイルを作成しました。ビルド時に、ターゲット オペレーティング システムに基づいて、実装ファイルの 1 つが選択されます。

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

于 2015-01-21T03:37:46.143 に答える
1

外部ライブラリに依存したり、より多くの「クロスプラットフォーム」コードを維持する必要がないようにするには、OpenGL. テキストが入った赤いクワッドを描画しますか?

おそらく、ある時点である種のユーザー インターフェイスを使用する可能性が高いため、そのコードを再利用することができます。

編集:OpenGLの準備が整う前にエラーメッセージが表示される可能性があるという仕様では、メッセージボックスをネイティブに表示しようとするために、OSごとに最小限のラッパーを作成することが唯一のオプションと思われる. このスレッドは参考になります。

于 2012-11-21T18:53:14.370 に答える