wxWidgets の最小サンプル

wxWidgets 3.0 以降を使った最小の GUI プログラムを例示し、その解説をする。

# コード

本格的なアプリケーションを書くならクラスごとにヘッダーファイルとソースファイルを用意するところだが、今回は簡便さのため、1つのソースファイル (`app.cpp`) に全て記述してしまう。

```c++:app.cpp
#include <wx/wx.h>

// アプリケーションについての挙動を管理するシングルトンクラス。
// 具体的には、初期化処理、メインループの管理など。
class MyApp : public wxApp
{
    virtual bool OnInit() override;
};

// wxGetApp 関数を宣言する（後述）
wxDECLARE_APP(MyApp);

// メインウィンドウを表すクラス。
class MyFrame : public wxFrame
{
public:
    MyFrame();
    virtual ~MyFrame();

    void OnClose(wxCloseEvent& event);
};

// アプリケーションの初期化処理。
// 実質的な main 関数の代わりと思って良い。
// true を返した場合はメインループに突入し、 false を返した場合はそのまま終了する。
bool MyApp::OnInit()
{
    // コマンドライン引数を処理する。
    if(!wxApp::OnInit())
        return false;

    MyFrame* frame = new MyFrame;
    frame->Show();

    return true;
}

MyFrame::MyFrame()
    : wxFrame(nullptr, wxID_ANY, "Minimal App")
{
    // メインメニューの作成
    auto menuBar = new wxMenuBar;
    auto menuFile = new wxMenu;
    menuFile->Append(wxID_EXIT, "Quit"); // wxID_EXIT のような定義済みIDはプラットフォームによって扱いが変わる。例えば、 Mac ではアプリケーションメニューの Quit と統合される。
    menuBar->Append(menuFile, "File");
    SetMenuBar(menuBar);

    // イベントハンドラーの登録
    // 自身のメンバー関数を登録する場合：
    Bind(wxEVT_CLOSE_WINDOW, &MyFrame::OnClose, this);
    // 無名関数で直接記述する場合：
    Bind(wxEVT_MENU, [this](wxCommandEvent&) { Close(true); }, wxID_EXIT);
}

MyFrame::~MyFrame()
{
}

// wxEVT_CLOSE_WINDOW はウィンドウが閉じられようとしていた場合に呼ばれる。
// 実装しなかった場合は単に Destroy() が呼ばれる。
void MyFrame::OnClose(wxCloseEvent& event)
{
    Destroy();
}

// アプリケーションの main 関数がここで定義される（後述）
wxIMPLEMENT_APP(MyApp);
```

ポイント：

- `wxApp` を継承したクラスを作って `OnInit` 関数をオーバーライドする。
- `wxDECLARE_APP` / `wxIMPLEMENT_APP` マクロを使って、 `wxGetApp` 関数の宣言・定義とエントリーポイント（`main` 関数）の定義を行う。（後述）
- イベントハンドラーの登録には `Bind` 関数を使う。`Bind` 関数へはメンバー関数のポインタを渡したり、無名関数を直接渡すことができる。
    - 昔は「イベントテーブル」とか言ってマクロを多用していたが、新規に書くコードでイベントテーブルを使う必要はないだろう。
- プログラム中で `MyFrame`, `wxMenuBar`, `wxMenu` 等のオブジェクトを `new` したまま解放していない。
    - `MyFrame` はウィンドウが `Destroy()` によって破棄された時に自動で `delete` される。
    - `wxMenuBar`, `wxMenu` のインスタンスは所属先である `MyFrame` が破棄されたタイミングで自動で `delete` される。

## wxDECLARE_APP マクロ

このマクロでは `wxGetApp()` 関数の宣言が行われる。`wxGetApp()` 関数はマクロの引数に渡されたクラス（コード例では `MyApp` クラス）の唯一のインスタンスを返す。

つまり、 `wxDECLARE_APP(MyApp);` は

```c++
MyApp& wxGetApp();
```

と等価である。

## wxIMPLEMENT_APP マクロ

このマクロには、 `wxGetApp()` 関数の実装や、アプリケーションの `main` 関数が含まれる。

`wxIMPLEMENT_APP` 中で定義される `main` 関数の中身は、大雑把には

```c++
int main(int argc, char *argv[]) {
    return wxEntry(argc, argv);
}
```

である。

`wxEntry` 関数は諸々の初期化処理をした後、 `wxIMPLEMENT_APP` に指定されたクラスの `OnInit` 関数を呼び、メインループに入る。

`main` 関数をマクロで隠蔽するのは正直言ってキモいが、隠蔽する理由としては、プラットフォームによって適切なエントリーポイントが異なるということが挙げられる。

具体的には、 Windows では GUI アプリは `main` 関数ではなく (`w`)`WinMain` 関数から始まる。コンソールアプリであっても、 Unicode なコマンドライン引数を適切に扱うには `main(int, char **)` ではダメで `wmain(int, wchar_t **)` を使う必要がある。

# ビルド

ビルドには CMake を使うのが楽[^1]だろう。

[^1]: 「解説する側として」楽、という意味。

```cmake:CMakeLists.txt
cmake_minimum_required(VERSION 3.1)

project(firstapp)

find_package(wxWidgets COMPONENTS core base)
include(${wxWidgets_USE_FILE})

add_executable(FirstApp WIN32
  app.cpp
)

target_compile_features(FirstApp PUBLIC cxx_std_14)

target_link_libraries(FirstApp ${wxWidgets_LIBRARIES})
```

wxWidgets を使う上では、 `find_package`, `include`, `target_link_libraries` の3行がポイントである。

wxWidgets は、いくつかのコンポーネントに分割されている。例えば、 GUI に関係ないもの（文字列クラスなど）は wxBase に、基本的な GUI 機能は wxCore というコンポーネントに、それぞれ含まれている（詳しくは [公式ドキュメント](http://docs.wxwidgets.org/stable/page_libs.html) を参照）。

`find_package` に指定している `core` と `base` は、アプリケーションで実際に使う（細かい）コンポーネントを表している。

`add_executable` に指定している `WIN32` は、 Windows 環境向けに「Win32 の GUI アプリである」ということを指示している。他のプラットフォームでは単に無視されるだけである。

Mac でアプリケーションバンドルを作りたいなら、 `add_executable` に `MACOSX_BUNDLE` を指定すると良いだろう。

## インストール済みの wxWidgets の指定方法

運が良ければ自動でシステムにインストールされた wxWidgets を見つけてくれるかもしれないが、そうでない場合は自分で wxWidgets のインストール先を指定してやる必要がある。

Unix系の場合は、 `wx-config` というスクリプト[^2]の位置を指定する。

[^2]: wxWidgets 用の `pkg-config` 的なやつ。Makefile を手書きする際にも使える。

CMake の実行例：

```sh
$ cmake -G "Unix Makefiles" -DwxWidgets_CONFIG_EXECUTABLE=/path/to/wx-config .
```

あとは make を叩けば実行ファイルができる。

Windows の場合は、公式のバイナリ配布を使うという人もいるだろう。ここでは仮に、[Release wxWidgets 3.1.1](https://github.com/wxWidgets/wxWidgets/releases/tag/v3.1.1) から Visual Studio 2017 用のバイナリー (`wxMSW-3.1.1_vc141_x64_Dev.7z`) とヘッダーファイル (`wxWidgets-3.1.1-headers.7z`) を取ってきて、 `D:\wxWidgets-3.1.1` 以下に

```
D:\wxWidgets-3.1.1\include\wx\（たくさんのヘッダファイル）
D:\wxWidgets-3.1.1\include\msvc\wx\setup.h
D:\wxWidgets-3.1.1\lib\vc141_x64_dll\（たくさんの .lib と .dll）
```

と展開したとする。

この時、 wxWidgets のインストール先である `D:\wxWidgets-3.1.1` を指定するには、 CMake の変数 `wxWidgets_ROOT_DIR` を使う：

```
> cmake -G "Visual Studio 15 2017 Win64" -DwxWidgets_ROOT_DIR=D:\wxWidgets-3.1.1 .
```

あとは Visual Studio 向けのソリューションが出来上がるので、それを使って適当にビルドする。

MSYS2 を使う場合は、 Unix 系と同様に `wx-config` を使うのだが、generatorの名前が `"MSYS Makefiles"` になる。

```sh
$ cmake -G "MSYS Makefiles" -DwxWidgets_CONFIG_EXECUTABLE=/path/to/wx-config .
```

MSYS2 公式パッケージではなくて独自ビルドの `wx-config` を指定した場合、ライブラリーが見つからないとか言って失敗するかもしれない。（TODO: 対処法を書く）

# その他

この記事では wxWidgets 自体のビルド・インストールは詳しく扱わなかった。もし wxWidgets を自分でビルドするという場合は、[筆者のブログ](https://blog.miz-ar.info/tag/wxwidgets/)に記事を書いているので、それを参考にしてほしい。