【Rust】wxDragonでクロスプラットフォームなデスクトップアプリを作る - wxWidgetsのRustラッパー

## はじめに

Rustでデスクトップアプリケーションを開発する際、クロスプラットフォーム対応のGUIライブラリの選択は重要です。既存の選択肢として、egui、Tauri、iframeworkなどがありますが、今回は成熟したC++ライブラリであるwxWidgetsをRustから使える「wxDragon」を紹介します。

![screenshot](https://raw.githubusercontent.com/AllenDang/wxDragon/refs/heads/main/asset/screenshot.png)

wxDragonは、30年以上の歴史を持つwxWidgetsをRustから安全に利用できるようにするラッパーライブラリです。ネイティブなルック＆フィールを持つアプリケーションを、Rustの安全性と表現力を活かして開発できます。

## wxDragonとは

wxDragonは、wxWidgetsのC++ APIをCラッパー経由でRustから利用可能にするプロジェクトです。2024年1月にv0.4.0がリリースされ、XRCサポートやクリップボード機能、高DPI対応など、多くの新機能が追加されました。

### プロジェクトの特徴

wxDragonの設計は、Rustの安全性を最大限に活かしながら、wxWidgetsの豊富な機能にアクセスできるよう工夫されています。C++のwxWidgetsをC APIでラップし、それをbindgenで自動的にRustバインディングを生成。さらに、その上に安全なRust APIを構築するという多層構造になっています。

ビルドシステムも特徴的で、wxWidgetsのソースコードを自動的にダウンロード・ビルドするため、開発者は事前にwxWidgetsをインストールする必要がありません。CMakeとCargoが連携して、必要なライブラリを静的リンクで組み込みます。

イベント処理においても、wxWidgetsのネイティブなイベントシステムとRustのクロージャを自然に統合。メモリ安全性を保ちながら、柔軟なイベントハンドリングが可能です。

### なぜwxDragonを選ぶのか

他のRust GUIフレームワーク（egui、tauri、iced）と比較して、wxDragonは以下の点で異なります。

- **ネイティブなルック＆フィール**: 各プラットフォームのネイティブウィジェットを使用（macOSではCocoa、WindowsではWin32、LinuxではGTK）
- **50以上の豊富なウィジェット**: DataView、AUI、メディアプレーヤーなどの高度なコントロールを含む
- **伝統的なデスクトップアプリケーションパターン**: 長年の実績があるwxWidgetsの設計思想を継承

## 環境構築

wxDragonを使い始めるのは簡単です。Rustの開発環境とC++コンパイラがあれば使えます。

### 必要な環境

```bash
# Rustのインストール（未インストールの場合）
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# C++コンパイラ（プラットフォームに応じて）
# macOS: Xcodeコマンドラインツール
xcode-select --install

# Windows: Visual Studio Build Tools または MinGW
# Linux: GCCまたはClang
```

wxWidgetsは自動的にダウンロード・ビルドされるため、別途インストールする必要はありません。

### プロジェクトの作成

新しいRustプロジェクトを作成し、`Cargo.toml`にwxDragonを追加します。

```toml
[dependencies]
wxdragon = { git = "https://github.com/AllenDang/wxDragon" }
```

## 基本的な使い方

### Hello Worldアプリケーション

最初に、シンプルなウィンドウとボタンを持つアプリケーションを作成してみましょう。

```rust
use wxdragon::prelude::*;

fn main() {
    wxdragon::main(|_| {
        // メインウィンドウの作成
        let frame = Frame::builder()
            .with_title("Hello, wxDragon!")
            .with_size(Size::new(300, 200))
            .build();

        // 垂直方向のレイアウトマネージャー
        let sizer = BoxSizer::builder(Orientation::Vertical).build();

        // ボタンの作成
        let button = Button::builder(&frame)
            .with_label("クリックしてください")
            .build();

        // ボタンクリックイベントの処理
        button.on_click(|_| {
            println!("ボタンがクリックされました！");
        });

        // レイアウトにボタンを追加
        sizer.add(
            &button,
            1,  // 伸縮比率
            SizerFlag::AlignCenterHorizontal | SizerFlag::AlignCenterVertical,
            0,  // ボーダー幅
        );

        // フレームにレイアウトを設定
        frame.set_sizer(sizer, true);

        // ウィンドウを表示
        frame.show(true);
        frame.centre();
    });
}
```

このコードの特徴的な点は、wxDragonがビルダーパターンを採用していることです。各ウィジェットの作成時に、必要なプロパティをメソッドチェーンで設定できます。また、イベントハンドラーはRustのクロージャとして自然に記述できます。

## v0.4.0の新機能

最新バージョンでは、開発効率を大幅に向上させる新機能が追加されました。

### クリップボードサポート

テキスト、ファイル、ビットマップのクリップボード操作が可能になりました。

```rust
use wxdragon::prelude::*;

// テキストをクリップボードにコピー
clipboard::set_text("Hello from Rust!");

// ファイルをクリップボードにコピー
clipboard::set_files(&["path/to/file1.txt", "path/to/file2.png"]);

// クリップボードの内容を取得
if let Some(text) = clipboard::get_text() {
    println!("クリップボード: {}", text);
}
```

### タイマーウィジェット

定期的なイベント処理やコールバックのスケジューリングが可能です。

```rust
let timer = Timer::new();
timer.start(1000); // 1秒間隔

timer.on_timer(|_| {
    println!("タイマーイベント発火！");
});
```

### 高DPI対応（BitmapBundle）

高解像度ディスプレイに対応した画像管理機能です。

```rust
let bundle = BitmapBundle::from_files(&[
    "icon_16.png",
    "icon_32.png", 
    "icon_64.png"
]);
button.set_bitmap_bundle(bundle);
```

## 高度な機能

### ノートブック（タブ）コントロール

複数のページを持つタブインターフェースの実装例です。

```rust
use wxdragon::prelude::*;

fn create_notebook_app() {
    wxdragon::main(|_| {
        let frame = Frame::builder()
            .with_title("タブ付きアプリケーション")
            .with_size(Size::new(600, 400))
            .build();

        let notebook = Notebook::builder(&frame).build();

        // 基本設定タブ
        let basic_panel = Panel::builder(&notebook).build();
        let basic_sizer = BoxSizer::builder(Orientation::Vertical).build();
        
        let checkbox = CheckBox::builder(&basic_panel)
            .with_label("有効にする")
            .build();
        basic_sizer.add(&checkbox, 0, SizerFlag::All, 5);
        
        basic_panel.set_sizer(basic_sizer, true);

        // 詳細設定タブ
        let advanced_panel = Panel::builder(&notebook).build();
        let advanced_sizer = BoxSizer::builder(Orientation::Vertical).build();
        
        let slider = Slider::builder(&advanced_panel)
            .with_range(0, 100)
            .with_value(50)
            .build();
        advanced_sizer.add(&slider, 0, SizerFlag::Expand | SizerFlag::All, 5);
        
        advanced_panel.set_sizer(advanced_sizer, true);

        // タブの追加
        notebook.add_page(&basic_panel, "基本設定", true, None);
        notebook.add_page(&advanced_panel, "詳細設定", false, None);

        // ページ変更イベント
        notebook.on_page_changed(|event_data| {
            let new_index = event_data.get_selection().unwrap_or(0);
            println!("タブが変更されました: {}", new_index);
        });

        let main_sizer = BoxSizer::builder(Orientation::Vertical).build();
        main_sizer.add(&notebook, 1, SizerFlag::Expand, 0);
        frame.set_sizer(main_sizer, true);

        frame.show(true);
    });
}
```

### ダイアログの表示

ファイル選択やメッセージ表示など、各種ダイアログの使用例です。

```rust
fn show_dialogs(parent: &Frame) {
    // メッセージダイアログ
    let msg_dialog = MessageDialog::builder(parent)
        .with_message("ファイルを保存しますか？")
        .with_caption("確認")
        .with_style(MessageDialogStyle::YesNo | MessageDialogStyle::Question)
        .build();
    
    if msg_dialog.show_modal() == DialogResult::Yes {
        // ファイル保存ダイアログ
        let file_dialog = FileDialog::builder(parent)
            .with_message("保存先を選択")
            .with_wildcard("テキストファイル (*.txt)|*.txt|すべてのファイル (*.*)|*.*")
            .with_style(FileDialogStyle::Save | FileDialogStyle::OverwritePrompt)
            .build();
        
        if file_dialog.show_modal() == DialogResult::Ok {
            let path = file_dialog.get_path();
            println!("選択されたパス: {}", path);
        }
    }
}
```

## クロスコンパイル

wxDragonは、macOSからWindowsへのクロスコンパイルをサポートしています。

```bash
# 必要なツールのインストール（macOS）
brew install mingw-w64

# Rustターゲットの追加
rustup target add x86_64-pc-windows-gnu

# ビルド実行
cargo build --target=x86_64-pc-windows-gnu --release
```

ビルドシステムが自動的に適切なコンパイラとリンカーオプションを設定し、スタンドアロンの実行ファイルを生成します。

## 今後の展望と課題

wxDragonはまだ開発中のプロジェクトですが、既に多くのウィジェットがサポートされています。現在サポートされているウィジェットには、基本的なコントロール（Button、TextCtrl、CheckBox等）、コンテナ（Panel、Notebook、SplitterWindow等）、ダイアログ（MessageDialog、FileDialog等）、高度なコントロール（TreeCtrl、ListCtrl、DataViewCtrl等）などがあります。

### 現在の制限事項

開発中のプロジェクトであるため、以下の制限があることに注意が必要です。

- **プラットフォームサポート**: 現在はmacOSでの開発が最も安定しており、Linux/Windowsの自動ビルドサポートは開発中
- **ドキュメンテーション**: docs.rsでのビルドがネットワークアクセス制限により失敗する問題がある
- **wxWidgetsの制限**: wxWidgets自体が持つ制限（モダンなUIパターンへの対応が限定的など）も継承している

### 今後のロードマップ

今後の開発では、残りのウィジェットのサポート、Linux/Windowsビルドの自動化改善、ドキュメントの充実、パフォーマンスの最適化などが予定されています。コミュニティからのフィードバックやコントリビューションも歓迎されています。

## まとめ

wxDragonは、Rustでネイティブなデスクトップアプリケーションを開発するための強力な選択肢です。wxWidgetsの成熟した機能とRustの安全性を組み合わせることで、堅牢でクロスプラットフォームなアプリケーションを効率的に開発できます。

特に、既存のwxWidgetsの知識がある開発者や、ネイティブなルック＆フィールが重要なアプリケーションを開発する場合に、wxDragonは優れた選択となるでしょう。プロジェクトはGitHubで公開されており、コントリビューションも歓迎されているそうです。

## 参考リンク

- [wxDragon GitHub リポジトリ](https://github.com/AllenDang/wxDragon)
- [wxWidgets 公式サイト](https://www.wxwidgets.org/)
- [wxFormBuilder（XRCデザイナー）](https://github.com/wxFormBuilder/wxFormBuilder)