Back to Blog

egui Custom Widgets: impl Widget for Reusable Components | Rust GUI Ep 25

Celest KimCelest Kim

Video: egui Custom Widgets: impl Widget for Reusable Components | Rust GUI Ep 25 by Taught by Celeste AI - AI Coding Coach

Watch full page →

egui Custom Widgets: impl Widget for Reusable Components

In this tutorial, you will learn how to create a reusable toggle switch widget by implementing the egui::Widget trait in Rust. The example covers reserving widget space, handling click interactions, and custom painting using egui's Painter API to build a polished toggle component that can be easily reused in your GUI applications.

Code

use egui::{Response, Sense, Ui, Widget, Vec2, Painter, Pos2, Color32, Stroke, Rounding, Rect};

/// A reusable toggle switch widget borrowing a boolean state.
pub struct Toggle<'a> {
  /// Mutable reference to the toggle state.
  pub state: &'a mut bool,
}

impl<'a> Toggle<'a> {
  /// Create a new Toggle widget from a mutable boolean reference.
  pub fn new(state: &'a mut bool) -> Self {
    Self { state }
  }
}

impl<'a> Widget for Toggle<'a> {
  fn ui(self, ui: &mut Ui) -> Response {
    // Define desired size for the toggle switch
    let desired_size = Vec2::new(40.0, 20.0);
    // Reserve exact space in the UI
    let (rect, response) = ui.allocate_exact_size(desired_size, Sense::click());

    // Toggle state on click
    if response.clicked() {
      *self.state = !*self.state;
    }

    // Painter for custom drawing
    let painter: &Painter = ui.painter();

    // Background color depends on toggle state
    let bg_color = if *self.state {
      Color32::from_rgb(0, 150, 136) // teal when ON
    } else {
      Color32::from_gray(180) // gray when OFF
    };

    // Draw rounded rectangle background
    painter.rect_filled(
      rect,
      Rounding::same(10.0),
      bg_color,
    );

    // Calculate position for the sliding circle knob
    let circle_radius = 8.0;
    let circle_x = if *self.state {
      rect.right() - circle_radius - 4.0
    } else {
      rect.left() + circle_radius + 4.0
    };
    let circle_center = Pos2::new(circle_x, rect.center().y);

    // Draw the circle knob with white fill and subtle stroke
    painter.circle_filled(circle_center, circle_radius, Color32::WHITE);
    painter.circle_stroke(circle_center, circle_radius, Stroke::new(1.0, Color32::BLACK));

    response
  }
}

// Usage example inside an egui app's ui code:
// let mut toggle_state = false;
// ui.add(Toggle::new(&mut toggle_state));

Key Points

  • Implement the egui::Widget trait by defining the ui(self, &mut Ui) -> Response method for custom widgets.
  • Use ui.allocate_exact_size() with Sense::click() to reserve widget space and handle click interactions.
  • Toggle boolean state by checking response.clicked() inside the widget's ui method.
  • Use ui.painter() to draw custom shapes like rounded rectangles and circles for the widget's visual appearance.
  • Pass mutable references to widget state with lifetimes to enable reusable, stateful components used via ui.add().