impl<F> Widget for F where F: FnOnce(&mut Ui) -> Response

This enables functions that return `impl Widget`, so that you can
create a widget by just returning a lambda from a function.

For instance: `ui.add(toggle(bool))` (instead of `toggle(ui, bool)`)
This commit is contained in:
Emil Ernerfeldt 2021-02-20 11:58:08 +01:00
parent 6fe70e685b
commit 040553da78
4 changed files with 62 additions and 48 deletions

View file

@ -506,10 +506,10 @@ impl Spacing {
tooltip_width,
} = self;
ui_slider_vec2(ui, item_spacing, 0.0..=10.0, "item_spacing");
ui_slider_vec2(ui, window_padding, 0.0..=10.0, "window_padding");
ui_slider_vec2(ui, button_padding, 0.0..=10.0, "button_padding");
ui_slider_vec2(ui, interact_size, 0.0..=60.0, "interact_size")
ui.add(slider_vec2(item_spacing, 0.0..=10.0, "item_spacing"));
ui.add(slider_vec2(window_padding, 0.0..=10.0, "window_padding"));
ui.add(slider_vec2(button_padding, 0.0..=10.0, "button_padding"));
ui.add(slider_vec2(interact_size, 0.0..=60.0, "interact_size"))
.on_hover_text("Minimum size of an interactive widget");
ui.add(Slider::f32(indent, 0.0..=100.0).text("indent"));
ui.add(Slider::f32(slider_width, 0.0..=1000.0).text("slider_width"));
@ -694,38 +694,20 @@ impl Visuals {
}
}
// TODO: improve and standardize ui_slider_vec2
fn ui_slider_vec2(
ui: &mut Ui,
value: &mut Vec2,
// TODO: improve and standardize `slider_vec2`
fn slider_vec2<'a>(
value: &'a mut Vec2,
range: std::ops::RangeInclusive<f32>,
text: &str,
) -> Response {
text: &'a str,
) -> impl Widget + 'a {
move |ui: &mut crate::Ui| {
ui.horizontal(|ui| {
/*
let fsw = full slider_width
let ssw = small slider_width
let space = item_spacing.x
let value = interact_size.x;
fsw + space + value = ssw + space + value + space + ssw + space + value
fsw + space + value = 2 * ssw + 3 * space + 2 * value
fsw + space - value = 2 * ssw + 3 * space
fsw - 2 * space - value = 2 * ssw
ssw = fsw / 2 - space - value / 2
*/
// let spacing = &ui.spacing();
// let space = spacing.item_spacing.x;
// let value_w = spacing.interact_size.x;
// let full_slider_width = spacing.slider_width;
// let small_slider_width = full_slider_width / 2.0 - space - value_w / 2.0;
// ui.spacing_mut().slider_width = small_slider_width;
ui.add(Slider::f32(&mut value.x, range.clone()).text("w"));
ui.add(Slider::f32(&mut value.y, range.clone()).text("h"));
ui.label(text);
})
.response
}
}
fn ui_color(ui: &mut Ui, srgba: &mut Color32, text: &str) {

View file

@ -35,6 +35,31 @@ pub trait Widget {
fn ui(self, ui: &mut Ui) -> Response;
}
/// This enables functions that return `impl Widget`, so that you can
/// create a widget by just returning a lambda from a function.
///
/// For instance: `ui.add(slider_vec2(&mut vec2));` with:
///
/// ```
/// pub fn slider_vec2(value: &mut egui::Vec2) -> impl egui::Widget + '_ {
/// move |ui: &mut egui::Ui| {
/// ui.horizontal(|ui| {
/// ui.add(egui::Slider::f32(&mut value.x, 0.0..=1.0).text("x"));
/// ui.add(egui::Slider::f32(&mut value.y, 0.0..=1.0).text("y"));
/// })
/// .response
/// }
/// }
/// ```
impl<F> Widget for F
where
F: FnOnce(&mut Ui) -> Response,
{
fn ui(self, ui: &mut Ui) -> Response {
self(ui)
}
}
// ----------------------------------------------------------------------------
/// Show a button to reset a value to its default.

View file

@ -9,7 +9,12 @@
/// | |.......|
/// \_______\_____/
/// ```
pub fn toggle(ui: &mut egui::Ui, on: &mut bool) -> egui::Response {
///
/// ## Example:
/// ``` ignore
/// toggle_ui(ui, &mut my_bool);
/// ```
pub fn toggle_ui(ui: &mut egui::Ui, on: &mut bool) -> egui::Response {
// Widget code can be broken up in four steps:
// 1. Decide a size for the widget
// 2. Allocate space for it
@ -58,7 +63,7 @@ pub fn toggle(ui: &mut egui::Ui, on: &mut bool) -> egui::Response {
/// Here is the same code again, but a bit more compact:
#[allow(dead_code)]
fn toggle_compact(ui: &mut egui::Ui, on: &mut bool) -> egui::Response {
fn toggle_ui_compact(ui: &mut egui::Ui, on: &mut bool) -> egui::Response {
let desired_size = ui.spacing().interact_size.y * egui::vec2(2.0, 1.0);
let (rect, response) = ui.allocate_exact_size(desired_size, egui::Sense::click());
*on ^= response.clicked(); // toggle if clicked
@ -77,14 +82,13 @@ fn toggle_compact(ui: &mut egui::Ui, on: &mut bool) -> egui::Response {
response
}
pub fn demo(ui: &mut egui::Ui, on: &mut bool) {
ui.horizontal_wrapped_for_text(egui::TextStyle::Button, |ui| {
toggle(ui, on).on_hover_text("Click to toggle");
ui.add(crate::__egui_github_link_file!());
})
.response
.on_hover_text(
"It's easy to create your own widgets!\n\
This toggle switch is just one function and 15 lines of code.",
);
// A wrapper that allows the more idiomatic usage pattern: `ui.add(toggle(&mut my_bool))`
/// iOS-style toggle switch.
///
/// ## Example:
/// ``` ignore
/// ui.add(toggle(&mut my_bool));
/// ```
pub fn toggle(on: &mut bool) -> impl egui::Widget + '_ {
move |ui: &mut egui::Ui| toggle_ui(ui, on)
}

View file

@ -156,7 +156,10 @@ impl super::View for WidgetGallery {
ui.end_row();
ui.label("Custom widget:");
super::toggle_switch::demo(ui, boolean);
ui.add(super::toggle_switch::toggle(boolean)).on_hover_text(
"It's easy to create your own widgets!\n\
This toggle switch is just 15 lines of code.",
);
ui.end_row();
ui.label("Plot:");