1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
//! # 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:
//!
//! ```ignore
//! #[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`]:
//!
//! ```ignore
//! # 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:
//! ```ignore
//! #[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:
//! ```rust
//! # #[allow(unused_must_use)]
//! # fn main() {
//! 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.
mod bool;
mod numeric;
mod string;
mod ui;
pub use bool::*;
pub use numeric::*;
pub use string::*;
pub use ui::*;
// reexport whiskers-derive macros
pub use whiskers_derive::{sketch_app, sketch_widget, Sketch, Widget};
/// Exported dependencies, for use by whiskers_derive
pub mod exports {
pub use egui;
pub use serde;
}
pub trait WidgetApp {
/// The name of the sketch, used the window title, the default output file name, and persistent
/// settings.
fn name(&self) -> String;
/// Draw the UI for the sketch, return whether the sketch should be updated.
///
/// This function is generated by the [`Sketch`] derive macro.
fn ui(&mut self, ui: &mut egui::Ui) -> bool;
}
/// This is the base trait for widgets used to display sketch parameters in the UI.
///
/// For each supported sketch parameter type `T`, there must exist an implementation of
/// [`Widget<T>`] that is furthermore registered using the [`crate::register_widget_ui!`] macro.
pub trait Widget<T> {
/// This function implements the actual UI for the widget.
///
/// Returns [`true`] if the value was changed.
fn ui(&self, ui: &mut egui::Ui, label: &str, value: &mut T) -> bool;
/// Indicates whether the widget should be displayed in a two-column grid.
///
/// Complex and/or compound widgets which cannot fit the two-column grid layout should return
/// [`false`].
fn use_grid() -> bool {
true
}
}
/// This utility trait serves to associate a [`Widget`] type with a given sketch parameter type `T`.
///
/// Do not implement this trait manually, instead use the [`crate::register_widget_ui!`] macro.
pub trait WidgetMapper<T> {
/// [`Widget`] type associated with the sketch parameter type `T`.
type Type: Widget<T>;
}
/// Registers a given [`Widget`] type for given sketch parameter type.
///
/// This is a convenience macro that implements the [`WidgetMapper`] trait for the given types.
///
/// # Example
///
/// ```ignore
/// register_widget_ui!(bool, BoolWidget);
/// ```
#[macro_export]
macro_rules! register_widget_ui {
($t: ty, $ui: ty) => {
impl $crate::WidgetMapper<$t> for $t {
type Type = $ui;
}
};
}