4 releases

0.5.0 Sep 21, 2024
0.4.0 Jan 21, 2024
0.4.0-rc.0 Jan 20, 2024

#1221 in Rust patterns

Download history 5/week @ 2024-07-28 193/week @ 2024-09-15 105/week @ 2024-09-22 42/week @ 2024-09-29 3/week @ 2024-10-06

342 downloads per month
Used in 2 crates

MIT license

18KB
301 lines

whiskers-widgets

Part of the vsvg project.

What's this?

This crate implements the UI widget machinery used by the whiskers crate to display the sketch parameter UI.


lib.rs:

Overview

This crate implements the UI widget machinery used by the whiskers crate to display the sketch parameter UI. The main entry point is the [trait@Widget] trait and the associated macros defined in whiskers-derive.

This crate is separate from the main crate to allow other crates (including the vsvg crate) to implement the [trait@Widget] trait for their own types.

Implementing the Widget trait

For each supported sketch parameter type T, there must exist a widget type that implements Widget<T> and is registered with register_widget_ui! macro. This module include the traits and macros needed to support this mechanism, as well as widgets for basic types.

For example, let's consider the [prim@bool] type:

#[derive(Default)]
pub struct BoolWidget;

impl whiskers_widgets::Widget<bool> for BoolWidget {
    fn ui(&self, ui: &mut egui::Ui, label: &str, value: &mut bool) -> egui::Response {
        ui.horizontal(|_| {});
        ui.checkbox(value, label)
    }
}

whiskers_widgets::register_widget_ui!(bool, BoolWidget);

The BoolWidget type implements the Widget<bool> trait, which requires the Widget::ui method, and is registered with the crate::register_widget_ui! macro. Note that the Widget::ui method is called in the context of an 2-column egui::Grid, so it must contain exactly two top level UI calls, where the first one typically is the label, and the second the actual interactive widget. In the case of a checkbox, the label is already embedded in the UI widget, we leave the first column empty.

The BoolWidget type is already provided by the crate crate, but custom widgets can be implemented for custom types using the same pattern.

Configuring widgets

Many widgets support additional configuration options, which can be set using the #[param(...)] attribute of the whiskers_derive::Sketch macro. This is done by using the builder pattern on the widget type. For example, here is an extract of NumericWidget, which supports numerical types such as [f64] and [i32]:

# use egui::emath::Numeric;
# use core::f64;

#[derive(Default)]
pub struct NumericWidget<T: Numeric> {
    step: Option<T>,
    slider: bool,
    /* ... */
}

impl<T: Numeric> NumericWidget<T> {
    pub fn step(mut self, step: T) -> Self {
        self.step = Some(step);
        self
    }

    pub fn slider(mut self, slider: bool) -> Self {
        self.slider = slider;
        self
    }
}

impl<T: Numeric> whiskers_widgets::Widget<T> for NumericWidget<T> {
    /* ... */
#    fn ui(&self, ui: &mut egui::Ui, label: &str, value: &mut T) -> bool { todo!(); }
}

whiskers_widgets::register_widget_ui!(f64, NumericWidget<f64>);
/* ... */

# fn main() {}

Now let's consider a hypothetical sketch:

#[sketch_app]
#[derive(Default)]
struct MySketch {
    #[param(slider, step = 0.1)]
    irregularity: f64,
}

Based on the #[param(...)] attributes, the whiskers_derive::Sketch derive macro will automatically generate the corresponding builder pattern calls:

whiskers_widgets::NumericWidget::<f64>::default().slider(true).step(0.1);

Note that when no value is provided for a key (such as slider here), a boolean value of true is assumed.

Dependencies

~5–10MB
~109K SLoC