2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

2
Posted at

はじめに

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

screenshot

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++コンパイラがあれば使えます。

必要な環境

# 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を追加します。

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

基本的な使い方

Hello Worldアプリケーション

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

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の新機能

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

クリップボードサポート

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

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);
}

タイマーウィジェット

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

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

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

高DPI対応(BitmapBundle)

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

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

高度な機能

ノートブック(タブ)コントロール

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

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);
    });
}

ダイアログの表示

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

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へのクロスコンパイルをサポートしています。

# 必要なツールのインストール(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で公開されており、コントリビューションも歓迎されているそうです。

参考リンク

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?