wxDragon

repository·main·Indexed 41 days ago

https://github.com/allendang/wxdragon

A cross-platform native GUI framework for Rust providing safe bindings to the wxWidgets library via the wxDragon C wrapper. It features a `custom_widget!` macro for creating interactive components with builder patterns, support for flicker-free drawing with `AutoBufferedPaintDC`, and compile-time XRC (XML Resource) embedding via `include_xrc!`, `include_xrc_dialog!`, and `include_xrc_panel!` macros for type-safe UI design.

Tokens
22.5K
Snippets
74
Records
94
Agent score
52%

What's inside wxDragon

  1. Overview of Custom Widgets in wxDragon

    main

    wxDragon allows for the creation of highly interactive, custom-drawn widgets. The custom_widget example demonstrates two primary patterns:

    1. AniFillButton: A button widget using timer-based animations (60fps) and AutoBufferedPaintDC for flicker-free drawing. It supports smooth hover fill effects, configurable colors (border, background, fill), and rounded corners.
    2. PieChart: A data visualization widget that supports dynamic data points, automatic proportional slice calculation, and interactive hover effects where slices move outward when highlighted.
  2. How event handling works in wxDragon

    main

    wxDragon maps native platform messages to a Rust-side event system. The process follows this flow:

    1. Native Message: The C++ layer receives a native platform message.
    2. wxEvent: The C++ layer converts the message into a wxEvent.
    3. Rust Wrapper: The event is dispatched to Rust as a thin wrapper around a raw pointer (event::Event(*mut wxd_Event_t)).
    4. Typed Data: For ease of use, events are often wrapped into typed data structures (e.g., TextEventData, MouseEventData) based on their event family.

    Key Concepts:

    • Event Types: Represented by EventType bitflags derived from a stable C enum (ffi::WXDEventTypeCEnum_*).
    • Binding: You bind to events using on_* helper methods provided by traits like WindowEvents, TextEvents, ButtonEvents, etc.
    • Propagation: By default, handlers allow events to continue (equivalent to Skip(true)). Calling event.skip(false) consumes the event, preventing further propagation or default processing.
  3. Safety and Lifetime constraints for Events

    main

    When working with events in wxDragon, adhere to these critical safety rules to avoid undefined behavior:

    1. Transient Event Lifetimes: The event::Event type is a wrapper around a transient C++ pointer. It is only valid during the callback.
      • DO NOT store Event or its raw pointer (*mut wxd_Event_t) beyond the callback.
      • DO NOT move Event across threads.
      • DO extract necessary data (IDs, strings, coordinates, key codes) into owned Rust types inside the callback if you need them later.
    2. Thread Safety: Callbacks run on the GUI thread. Long-running work (I/O, heavy CPU) will freeze the UI. Use worker threads and wxdragon::call_after to return to the GUI thread.
    3. Panic Safety: The FFI layer uses catch_unwind to prevent panics from crossing into C++. However, you should still avoid panicking in callbacks; prefer logging and early returns.
    4. Reference Cycles: When capturing state in a closure, be careful of reference cycles. If a closure captures an Arc to a widget that also owns the closure, use Weak to break the cycle.
  4. Understand the purpose of wxdragon-sys

    main

    The wxdragon-sys crate provides raw, unsafe FFI (Foreign Function Interface) bindings to libwxdragon, a C++ library that wraps the wxWidgets GUI toolkit.

    Note for end-users: This crate is intended as a low-level foundation. It is not typically used directly by application developers. Instead, you should use the wxdragon crate, which provides safe, idiomatic Rust abstractions built on top of this -sys crate.

  5. Handle events and ensure propagation

    main

    When implementing custom event handlers (mouse, keyboard, etc.) inside a widget, it is critical to call event.skip(true). This ensures that the event continues to propagate through the event loop, allowing other handlers (including those attached externally to the widget instance) to receive the event.

    Example of chaining handlers:

    1. The widget's internal on_mouse_left_down handles logic like changing a 'pressed' state.
    2. The application's external my_widget.on_mouse_left_down handles logic like triggering a business action. Both will work only if event.skip(true) is called in the internal handler.
    // Internal widget logic
    panel.on_mouse_left_down(move |event| {
        handle_internal_click();
        event.skip(true); // Allows external handlers to run
    });
    
    // External usage
    my_widget.on_mouse_left_down(|event| {
        println!("Application received click!");
        event.skip(true);
    });
  6. Optimize rust-analyzer for wxDragon workspace

    main

    To prevent rust-analyzer from building the entire workspace every time you make a change, add the following configuration to your VS Code settings.json to set a custom environment variable.

    {
      "rust-analyzer.cargo.extraEnv": {
        "RUST_ANALYZER": "true"
      }
    }
  7. Install wxDragon on Linux (Ubuntu/Debian and Fedora/RHEL)

    main

    Before building, ensure you have the necessary system dependencies installed via your package manager.

    # Ubuntu/Debian
    sudo apt install libclang-dev pkg-config libgtk-3-dev libpng-dev libjpeg-dev libgl1-mesa-dev libglu1-mesa-dev libxkbcommon-dev libexpat1-dev libtiff-dev cmake libssl-dev libwebkit2gtk-4.1-dev
    
    # Fedora/RHEL
    sudo dnf install clang-devel pkg-config gtk3-devel libpng-devel libjpeg-devel mesa-libGL-devel mesa-libGLU-devel libxkbcommon-devel expat-devel libtiff-devel
  8. Build with MSVC on Windows

    main

    To build using the MSVC toolchain on Windows, you need Visual Studio (2019 or later) with C++ support and the Windows SDK.

    1. Open a "x64 Native Tools Command Prompt for VS 2019/2022" or the Developer PowerShell.
    2. Ensure cl.exe and cmake.exe are in your PATH.
    3. Run cargo build.

    MSVC builds do not require the WinLibs toolchain; wxDragon automatically uses the correct prebuilt wxWidgets libraries for MSVC.

    # Ensure you are in a Native Tools Command Prompt
    cargo build
  9. Configure Idle mode and processing

    main

    To perform periodic background tasks without blocking the UI, use the Idle event system.

    1. Set the global idle mode using IdleEvent::set_mode(IdleMode::ProcessSpecified).
    2. Enable idle processing on your specific window using window.set_extra_style(ExtraWindowStyle::ProcessIdle).
    3. Use event.request_more(true) inside the handler if you need to continue idling.

    Example: Idle loop

    use wxdragon::event::{IdleEvent, IdleMode, WindowEvents};
    
    IdleEvent::set_mode(IdleMode::ProcessSpecified);
    window.set_extra_style(ExtraWindowStyle::ProcessIdle);
    
    window.on_idle(|idle| {
        // Do a bit of work
        if more_work_pending() {
            idle.event.request_more(true); // keep idling
        }
    });
    use wxdragon::event::{IdleEvent, IdleMode, WindowEvents};
    IdleEvent::set_mode(IdleMode::ProcessSpecified);
    window.set_extra_style(ExtraWindowStyle::ProcessIdle);
    
    window.on_idle(|idle| {
        // Do a bit of work
        if more_work_pending() {
            idle.event.request_more(true); // keep idling
        }
    });