use egui::{
Color32, CornerRadius, Response, Sense, Stroke, Ui, Vec2, Widget, WidgetInfo, WidgetText,
WidgetType,
};
use crate::button::ButtonSize;
use crate::theme::{with_alpha, Accent, Theme};
#[must_use = "Add with `ui.add(...)`."]
pub struct SegmentedButton<'a> {
on: &'a mut bool,
label: WidgetText,
accent: Accent,
size: ButtonSize,
dim_when_on: bool,
rounded: bool,
corner_radius: Option<CornerRadius>,
min_width: Option<f32>,
}
impl<'a> std::fmt::Debug for SegmentedButton<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SegmentedButton")
.field("on", &*self.on)
.field("label", &self.label.text())
.field("accent", &self.accent)
.field("size", &self.size)
.field("dim_when_on", &self.dim_when_on)
.field("rounded", &self.rounded)
.field("corner_radius", &self.corner_radius)
.field("min_width", &self.min_width)
.finish()
}
}
impl<'a> SegmentedButton<'a> {
pub fn new(on: &'a mut bool, label: impl Into<WidgetText>) -> Self {
Self {
on,
label: label.into(),
accent: Accent::Green,
size: ButtonSize::Medium,
dim_when_on: false,
rounded: true,
corner_radius: None,
min_width: None,
}
}
pub fn accent(mut self, accent: Accent) -> Self {
self.accent = accent;
self
}
#[inline]
pub fn size(mut self, size: ButtonSize) -> Self {
self.size = size;
self
}
pub fn dim_when_on(mut self, dim: bool) -> Self {
self.dim_when_on = dim;
self
}
pub fn rounded(mut self, rounded: bool) -> Self {
self.rounded = rounded;
self
}
pub fn corner_radius(mut self, radius: impl Into<CornerRadius>) -> Self {
self.corner_radius = Some(radius.into());
self
}
pub fn min_width(mut self, width: f32) -> Self {
self.min_width = Some(width);
self
}
fn on_fill(&self, theme: &Theme) -> Color32 {
theme.palette.accent_fill(self.accent)
}
fn on_fill_hover(&self, theme: &Theme) -> Color32 {
theme.palette.accent_hover(self.accent)
}
}
impl<'a> Widget for SegmentedButton<'a> {
fn ui(self, ui: &mut Ui) -> Response {
let theme = Theme::current(ui.ctx());
let p = &theme.palette;
let padding = self.size.padding(&theme);
let font_size = self.size.font_size(&theme);
let led_size = 8.0;
let led_gap = 7.0;
let galley =
crate::theme::placeholder_galley(ui, self.label.text(), font_size, true, f32::INFINITY);
let content_w = led_size + led_gap + galley.size().x;
let mut desired = Vec2::new(
padding.x * 2.0 + content_w,
(padding.y * 2.0 + galley.size().y).max(font_size + 2.0 * padding.y),
);
if let Some(min_w) = self.min_width {
desired.x = desired.x.max(min_w);
}
let (rect, mut response) = ui.allocate_exact_size(desired, Sense::click());
if response.clicked() {
*self.on = !*self.on;
response.mark_changed();
}
if ui.is_rect_visible(rect) {
let on = *self.on;
let hovered = response.hovered();
let is_down = response.is_pointer_button_down_on();
let (fill, text_color, led_color, led_glow) = if on {
let mut fill = if is_down {
crate::theme::mix(self.on_fill_hover(&theme), Color32::BLACK, 0.1)
} else if hovered {
self.on_fill_hover(&theme)
} else {
self.on_fill(&theme)
};
let mut text = Color32::WHITE;
if self.dim_when_on {
fill = crate::theme::mix(fill, p.card, 0.55);
text = p.text_muted;
}
let led = Color32::WHITE;
let glow = !self.dim_when_on;
(fill, text, led, glow)
} else {
let fill = if hovered {
p.depth_tint(p.input_bg, 0.05)
} else {
p.input_bg
};
let text = if hovered { p.text_muted } else { p.text_faint };
let led = p.text_faint;
(fill, text, led, false)
};
let radius = self.corner_radius.unwrap_or_else(|| {
if self.rounded {
CornerRadius::same(theme.control_radius as u8 + 2)
} else {
CornerRadius::ZERO
}
});
ui.painter()
.rect(rect, radius, fill, Stroke::NONE, egui::StrokeKind::Inside);
let content_start = rect.center().x - content_w * 0.5;
let led_center = egui::pos2(content_start + led_size * 0.5, rect.center().y);
if led_glow {
ui.painter().circle_filled(
led_center,
led_size * 0.5 + 2.0,
with_alpha(Color32::WHITE, 70),
);
}
ui.painter()
.circle_filled(led_center, led_size * 0.5, led_color);
let text_pos = egui::pos2(
led_center.x + led_size * 0.5 + led_gap,
rect.center().y - galley.size().y * 0.5,
);
ui.painter().galley(text_pos, galley, text_color);
}
response.widget_info(|| {
WidgetInfo::selected(WidgetType::Checkbox, true, *self.on, self.label.text())
});
response
}
}