use egui::{Response, Sense, StrokeKind, Ui, Widget, WidgetInfo, WidgetText, WidgetType};
pub struct Toggle<'a> {
on: &'a mut bool,
text: WidgetText,
}
impl<'a> Toggle<'a> {
pub fn new(on: &'a mut bool, text: impl Into<WidgetText>) -> Self {
Self {
on,
text: text.into(),
}
}
}
impl<'a> Widget for Toggle<'a> {
fn ui(self, ui: &mut Ui) -> Response {
let Toggle { on, text } = self;
ui.horizontal(|ui| {
let height = ui.spacing().interact_size.y;
let desired_size = egui::vec2(2.0 * height, height);
let (rect, mut response) = ui.allocate_exact_size(desired_size, Sense::click());
if response.clicked() {
*on = !*on;
response.mark_changed();
}
response
.widget_info(|| WidgetInfo::selected(WidgetType::Checkbox, true, *on, text.text()));
if ui.is_rect_visible(rect) {
let how_on = ui.ctx().animate_bool(response.id, *on);
let visuals = ui.style().interact_selectable(&response, *on);
let rect = rect.expand(visuals.expansion);
let radius = 0.5 * rect.height();
ui.painter().rect(
rect,
radius,
visuals.bg_fill,
visuals.bg_stroke,
StrokeKind::Outside,
);
let circle_x = egui::lerp((rect.left() + radius)..=(rect.right() - radius), how_on);
let center = egui::pos2(circle_x, rect.center().y);
ui.painter()
.circle(center, 0.75 * radius, visuals.bg_fill, visuals.fg_stroke);
}
let label_response = ui.add(egui::Label::new(text).sense(Sense::click()));
if label_response.clicked() {
*on = !*on;
response.mark_changed();
}
response | label_response
})
.inner
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_toggle_creation() {
let mut on = false;
let _toggle = Toggle::new(&mut on, "Test");
}
}