This project creates a manually crafted C wrapper around the wxWidgets C++ GUI library. The primary goal is to expose a stable C API that can be consumed by bindgen to generate unsafe Rust bindings (-sys crate), which are then used to build safe, idiomatic Rust wrappers.
Add wxdragon to your Cargo.toml.
use wxdragon::prelude::*;
fn main() {
wxdragon::main(|_| {
let frame = Frame::builder()
.with_title("Hello, World!")
.with_size(Size::new(300, 200))
.build();
let sizer = BoxSizer::builder(Orientation::Vertical).build();
let button = Button::builder(&frame).with_label("Click me").build();
button.on_click( |_| {
println!("Button clicked");
});
sizer.add(
&button,
1,
SizerFlag::AlignCenterHorizontal | SizerFlag::AlignCenterVertical,
0,
);
frame.set_sizer(sizer, true);
frame.show(true);
frame.centre();
});
}wxDragon also supports XRC (XML Resource) files for declarative UI development with compile-time type safety.
You could use wxFormBuilder as UI designer to generate XRC.
1. Define your UI in XRC format (ui/main.xrc):
<?xml version="1.0" encoding="UTF-8"?>
<resource>
<object class="wxFrame" name="main_frame">
<title>wxDragon XRC Demo</title>
<size>400,300</size>
<object class="wxPanel" name="main_panel">
<object class="wxButton" name="hello_button">
<label>Click Me!</label>
<pos>50,50</pos>
</object>
<object class="wxTextCtrl" name="input_field">
<value>Enter text here...</value>
<pos>50,100</pos>
<size>200,25</size>
</object>
<object class="wxStaticText" name="status_label">
<label>Ready</label>
<pos>50,150</pos>
</object>
</object>
</object>
</resource>2. Use the include_xrc! macro to generate a typed UI struct:
use wxdragon::prelude::*;
// Generate MyUI struct with typed fields for all named widgets
wxdragon::include_xrc!("ui/main.xrc", MyUI);
fn main() {
wxdragon::main(|_| {
// Create UI instance - automatically loads XRC and finds all widgets
let ui = MyUI::new(None);
// Access widgets with full type safety
let button = &ui.hello_button; // Button
let input = &ui.input_field; // TextCtrl
let label = &ui.status_label; // StaticText
let frame = &ui.main_frame; // Frame (root object)
// Bind events with closures
let label_clone = label.clone();
let input_clone = input.clone();
button.on_click(move |_| {
let text = input_clone.get_value();
label_clone.set_label(&format!("You entered: {}", text));
println!("Button clicked! Input: {}", text);
});
// Show the window
frame.show(true);
frame.centre();
});
}Key benefits of the XRC approach:
- Declarative UI: Define layouts in XML, separate from logic
- Compile-time safety: Auto-generated structs with typed widget fields
- No verbose syntax: Just
MyUI::new()instead ofMyUI::new::<Frame>() - Designer support: XRC files can be created with visual designers
- Automatic widget discovery: Macro finds all named widgets and generates appropriate Rust types
| Platform | Support |
|---|---|
| macOS | Static Build + Cross build to Windows via gnu |
| Windows | Yes |
| Linux | Yes |
- C API: Define a C API (
rust/wxdragon-sys/cpp/include/wxdragon.h) using opaque pointers for wxWidgets objects and C functions to interact with them. Uses stable C types for flags/constants andconst char*for strings. - C++ Implementation: Implement the C API functions (
rust/wxdragon-sys/cpp/src/*.cpp), translating C calls into wxWidgets C++ calls. Manages wxWidgets object creation (new) and destruction (Destroy()). - Event Handling: Implement a robust event handling system using wxWidgets' native mechanism:
- C++ Side: Uses
wxEvtHandler::Bindwith a C++ functor (CxxClosureVoid) that wraps the Rust trampoline function pointer and the Rust closure data pointer. The lifetime of this functor (and thus the Rust closureBox) is managed by wxWidgets. - C API: Exposes
wxd_EvtHandler_Bind, which takes a stable C enum value (WXDEventTypeCEnum) representing the event type, a C function pointer (to the Rust trampoline), and avoid*(the Rust closure box pointer). The C++ implementation maps the stable C enum back to the appropriatewxEVT_XXXconstant for the call toBind. - Rust Side: Provides a
WxEvtHandlertrait with a safebindmethod. Uses a type-safeEventTypeenum wrapping the stableWXDEventTypeCEnumfrom the FFI layer. Closures (FnMut(Event) + 'static) are used for callbacks. TheDropimplementation in the C++ functor calls a Rust function (drop_rust_closure_box) to correctly free the closureBoxwhen the binding is destroyed.
- C++ Side: Uses
- Build: Use CMake to build the C wrapper library (invoked via
wxdragon-sys/build.rsfromrust/wxdragon-sys/cpp/CMakeLists.txt). wxWidgets source is downloaded and built automatically as part of this process. - Rust Bindings: Use
bindgenon the C header (rust/wxdragon-sys/cpp/include/wxdragon.h) to generate awxdragon-syscrate. - Safe Rust Wrapper: Develop a
wxdragonRust crate providing safe abstractions over thewxdragon-syscrate. - Incremental: Started with core widgets (
wxApp,wxFrame) and expand coverage gradually. - Constant Handling:
- Event Types: Event types (
wxEVT_*) are handled via a manually defined stable C enum (WXDEventTypeCEnuminrust/wxdragon-sys/cpp/include/wxdragon.h) and a corresponding Rust enum (EventTypeinrust/wxdragon/src/event.rs). This ensures stability across wxWidgets versions and platforms for event type identifiers. - Other Constants (Styles, IDs, Flags): For other constants like style flags, standard IDs, etc., wxDragon now uses pre-generated Rust files. These files (e.g.,
wx_msw_constants.rs,wx_gtk_constants.rs,wx_osx_constants.rs) are located inrust/wxdragon-sys/src/generated_constants/and are checked into the repository. Theconst_extractortool (intools/const_extractor/) is used by maintainers to generate these files for each major platform/wxWidgets port. During a user's build (cargo build), thewxdragon-sys/build.rsscript detects the target OS and copies the appropriate pre-generated file to$OUT_DIR/wx_other_constants.rs. This approach enhances cross-compilation capabilities and simplifies the build process for users by removing the need to runconst_extractorat build time.
- Event Types: Event types (
wxdragon/
├── rust/
│ ├── wxdragon-sys/ # Raw FFI bindings, C++ source, and build script
│ │ ├── cpp/ # C++ wrapper source, headers, and CMake file
│ │ │ ├── include/ # Public C header (wxdragon.h)
│ │ │ ├── src/ # C++ wrapper implementation
│ │ │ ├── tools/ # Helper tools (e.g., const_extractor for maintainers)
│ │ │ │ └── const_extractor/
│ │ │ └── CMakeLists.txt # CMake for libwxdragon & wxWidgets
│ │ ├── src/
│ │ │ ├── lib.rs
│ │ │ └── generated_constants/ # Pre-generated OS-specific constants
│ │ ├── build.rs # Rust build script
│ │ └── Cargo.toml
│ ├── wxdragon/ # Safe Rust wrapper
│ │ ├── src/
│ │ └── Cargo.toml
│ └── Cargo.toml # Workspace Cargo.toml (might be at root)
├── examples/
│ └── rust/
│ └── gallery/
├── Cargo.toml # Top-level Workspace Cargo.toml (if not in rust/)
└── README.md # This file
(Note: The const_extractor path and the exact location of the workspace Cargo.toml in the diagram might need slight adjustment based on your final layout.)
-
Install a C++ compiler suitable for your platform (CMake is used by the build script but doesn't need separate installation if you have
cmakein your PATH or use thecmakecrate feature that bundles it). -
wxWidgets is downloaded and built automatically as part of the project.
-
Navigate to the root of the repository (or the directory containing the main workspace
Cargo.toml). -
Run
cargo buildorcargo run -p gallery.- The
wxdragon-sysbuild script (build.rs) will automatically:- Download the wxWidgets source tarball (version 3.2.8 currently) if not already present.
- Extract wxWidgets.
- Invoke CMake to configure and build:
- The wxWidgets static libraries.
- The C++ wrapper static library (
libwxdragon.a).
- Copy the appropriate pre-generated platform-specific constants file (e.g.,
wx_msw_constants.rs) fromrust/wxdragon-sys/src/generated_constants/to$OUT_DIR/wx_other_constants.rs. - Generate Rust FFI bindings (
bindings.rs) usingbindgenagainstrust/wxdragon-sys/cpp/include/wxdragon.hand the built wxWidgets headers. - Configure Cargo to link
libwxdragon.aand the necessary wxWidgets libraries.
Apart from the necessary build tools (CMake, Rust and the C++ compiler), the build requires no additional dependencies. wxDragon uses a hardcoded set of linker flags and bindgen include paths derived from
wx-configfor stability.wxDragon requires gtk+-3.0 which the build script finds via pkg-config. It also requires libclang to generate the bindings. This requires the user to install the development packages necessary to build wxWidgets. Which for debian-based distros:
sudo apt-get install libclang-dev pkg-config libgtk-3-dev libpng-dev libjpeg-dev libgl1-mesa-dev libglu1-mesa-dev libxkbcommon-dev libexpat1-dev libtiff-dev
If you're targeting the gnu toolchain, you will still need to install libclang which can be done using
pacman -S $MINGW_PACKAGE_PREFIX-clang. This is necessary for generating the rust-bindgen bindings.An additional build tool is required: Ninja! This can be installed using winget (or any of the current windows package managers):
winget install --id=Ninja-build.Ninja -elibclang is also needed to generate the rust-bindgen bindings. This can be installed as instructed in the rust-bindgen documentation, LLVM and libclang can also be installed through the Visual Studio Installer. Also verify that you have a windows sdk installed through your Visual Studio Installer.
- The
To build the project on macOS targeting Windows (specifically x86_64-pc-windows-gnu):
-
Install Dependencies via Homebrew:
brew install mingw-w64
This installs the MinGW-w64 toolchain, including the necessary C/C++ cross-compilers (e.g.,
x86_64-w64-mingw32-gcc,x86_64-w64-mingw32-g++) and linker. -
Add Rust Target:
rustup target add x86_64-pc-windows-gnu
This downloads the Rust standard library pre-compiled for the target.
-
Build:
cargo build --target=x86_64-pc-windows-gnu
Or for a release build:
cargo build --target=x86_64-pc-windows-gnu --release
The
wxdragon-sys/build.rsscript contains specific logic to detect this target, configure CMake appropriately, and set the correct linker flags (including static linking forlibstdc++andlibgcc) to produce a standalone.exefile.
- Basic project structure set up with CMake and Cargo workspace.
- Core C wrapper implementation for various widgets is ongoing.
- Build System:
- wxWidgets source (currently 3.2.8) is automatically downloaded, extracted, configured, and built statically by
wxdragon-sys/build.rsusing thecmakecrate. - C++ wrapper library (
libwxdragon.a) built by CMake invoked frombuild.rs(usingrust/wxdragon-sys/cpp/CMakeLists.txt). - Hybrid Constant Handling:
- Event Types: Stable C enum (
WXDEventTypeCEnum) and Rust enum (EventType). - Other Constants: Pre-generated platform-specific Rust files (e.g.,
wx_msw_constants.rs,wx_gtk_constants.rs) located inrust/wxdragon-sys/src/generated_constants/, copied to$OUT_DIR/wx_other_constants.rsbybuild.rs.
- Event Types: Stable C enum (
-
build.rssupports incremental C++ builds. - macOS Build: Uses hardcoded linker and bindgen flags for stability.
- Linux Build: Uses gtk+-3.0 which it finds via pkg-config. This requires additional installs of development packages and pkg-config on the system. Check the
Build Instructionssection. - Windows Build: Uses the current msw win32 backend. CMake currently builds wxWidgets for Release.
- wxWidgets source (currently 3.2.8) is automatically downloaded, extracted, configured, and built statically by
wxdragon-sysRust crate:-
bindgengenerates raw FFI bindings fromrust/wxdragon-sys/cpp/include/wxdragon.h. -
build.rscorrectly invokes CMake, downloads wxWidgets, copies pre-generated constants, and links libraries using a manual configuration for macOS.
-
wxdragonSafe Rust Wrapper:- Safe wrappers for core widgets, sizers, and menu components implemented with builder pattern.
- Stable Event Handling:
WxEvtHandlertrait provides genericbind(EventType, FnMut(Event))using the stableEventTypeenum.- Relies on C++
wxEvtHandler::Bindwith functor for lifetime management.
- Safe application entry point (
wxdragon::main). - Basic lifetime management (
Drop,preserve()). - Basic layout management (
set_sizer,set_sizer_and_fit). - Gallery Rust example (
gallery) demonstrates core features and event handling.
- Core:
-
wxApp -
wxFrame
-
- Widgets/Controls:
- Basic:
-
wxButton -
wxStaticText -
wxTextCtrl -
wxCheckBox -
wxRadioButton -
wxToggleButton -
wxGauge -
wxSlider -
wxSpinCtrl -
wxSpinButton -
wxDatePickerCtrl -
wxBitmapButton -
wxRadioBox -
wxScrollBar -
wxSpinCtrlDouble -
wxStaticBitmap -
wxStaticLine -
wxSearchCtrl -
wxStyledTextCtrl -
wxRichTextCtrl -
wxHyperlinkCtrl -
wxActivityIndicator -
wxAnimationCtrl -
wxCommandLinkButton
-
- Choices/Lists:
-
wxChoice -
wxComboBox -
wxListBox -
wxCheckListBox -
wxTreeCtrl -
wxListCtrl -
wxBitmapComboBox -
wxComboCtrl -
wxDataViewCtrl -
wxDataViewListCtrl -
wxDataViewTreeCtrl -
wxEditableListBox -
wxFileCtrl -
wxGenericDirCtrl -
wxHtmlListBox -
wxOwnerDrawnComboBox -
wxPropertyGrid -
wxPropertyGridManager -
wxRearrangeCtrl -
wxSimpleHtmlListBox -
wxTreeListCtrl
-
- Picker Controls:
-
wxColourPickerCtrl -
wxDatePickerCtrl -
wxFilePickerCtrl -
wxDirPickerCtrl -
wxFontPickerCtrl -
wxTimePicker
-
- Containers:
-
wxPanel -
wxScrolledWindow -
wxSplitterWindow -
wxNotebook(Tabs) -
wxStaticBox -
wxAuiMDIChildFrame -
wxAuiMDIParentFrame -
wxAuiMDIClientWindow -
wxAuiNotebook -
wxAuiToolBar -
wxBannerWindow -
wxChoicebook -
wxCollapsiblePane -
wxListbook -
wxSimplebook -
wxTreebook
-
- Other:
-
wxArtProvider(Basic support forGetBitmap) -
wxCalendarCtrl -
wxGLCanvas -
wxHtmlWindow -
wxMediaCtrl -
wxNotificationMessage -
wxRichToolTip -
wxSplashScreen -
wxStatusBar -
wxTaskBarIcon -
wxTimer -
wxWebView -
wxWizard
-
- Basic:
- Sizers:
-
wxBoxSizer - Expand
BoxSizermethods (AddSizer,AddSpacer,AddStretchSpacer) -
wxGridSizer -
wxFlexGridSizer -
wxStaticBoxSizer
-
- Menus & Toolbars:
-
wxMenuBar -
wxMenu -
wxMenuItem -
wxToolBar(UseFrame::create_tool_barand callrealize()after adding tools)
-
- Dialogs:
-
wxDialog(base class) -
wxMessageDialog -
wxFileDialog -
wxDirDialog -
wxColourDialog -
wxFontDialog -
wxTextEntryDialog -
wxProgressDialog -
wxSingleChoiceDialog -
wxMultiChoiceDialog -
wxSplashScreen(Moved here, also related to Other) -
wxWizard(Moved here, also related to Other)
-
- Event Handling Expansion:
- Add specific event data access (e.g.,
event.get_string(),event.get_position(),event.get_checked(),event.get_key_code()).
- Add specific event data access (e.g.,
- XRC Support:
- Implement C++ bindings for
wxXmlResourceinwxdragon-sys. - Create safe Rust wrappers for
wxXmlResourceinwxdragon. - Develop
include_xrc!("file.xrc", UStructName)procedural macro to generate Rust UI structs from XRC files.- Compile-time XRC parsing.
- Mapping XRC widget classes to
wxDragontypes. - Generation of struct with typed fields for named XRC widgets.
- Automatic loading of XRC and widget retrieval in the generated struct's constructor.
- Implement C++ bindings for
- Documentation:
- Improve C API docs (doxygen?).
- Rust API docs (rustdoc).
