egui/widgets/
radio_button.rs1use crate::{
2 Atom, AtomLayout, Atoms, Id, IntoAtoms, NumExt as _, Response, Sense, Ui, Vec2, Widget,
3 WidgetInfo, WidgetType, epaint,
4};
5
6#[must_use = "You should put this widget in a ui with `ui.add(widget);`"]
26pub struct RadioButton<'a> {
27 checked: bool,
28 atoms: Atoms<'a>,
29}
30
31impl<'a> RadioButton<'a> {
32 pub fn new(checked: bool, atoms: impl IntoAtoms<'a>) -> Self {
33 Self {
34 checked,
35 atoms: atoms.into_atoms(),
36 }
37 }
38
39 pub fn atoms(&self) -> &Atoms<'a> {
43 &self.atoms
44 }
45}
46
47impl Widget for RadioButton<'_> {
48 fn ui(self, ui: &mut Ui) -> Response {
49 let Self { checked, mut atoms } = self;
50
51 let spacing = &ui.spacing();
52 let icon_width = spacing.icon_width;
53
54 let mut min_size = Vec2::splat(spacing.interact_size.y);
55 min_size.y = min_size.y.at_least(icon_width);
56
57 let mut icon_size = Vec2::splat(icon_width);
59 icon_size.y = icon_size.y.at_least(min_size.y);
60 let rect_id = Id::new("egui::radio_button");
61 atoms.push_left(Atom::custom(rect_id, icon_size));
62
63 let text = atoms.text().map(String::from);
64
65 let mut prepared = AtomLayout::new(atoms)
66 .sense(Sense::click())
67 .min_size(min_size)
68 .allocate(ui);
69
70 prepared.response.widget_info(|| {
71 WidgetInfo::selected(
72 WidgetType::RadioButton,
73 ui.is_enabled(),
74 checked,
75 text.as_deref().unwrap_or(""),
76 )
77 });
78
79 if ui.is_rect_visible(prepared.response.rect) {
80 let visuals = *ui.style().interact(&prepared.response);
82
83 prepared.fallback_text_color = visuals.text_color();
84 let response = prepared.paint(ui);
85
86 if let Some(rect) = response.rect(rect_id) {
87 let (small_icon_rect, big_icon_rect) = ui.spacing().icon_rectangles(rect);
88
89 let painter = ui.painter();
90
91 painter.add(epaint::CircleShape {
92 center: big_icon_rect.center(),
93 radius: big_icon_rect.width() / 2.0 + visuals.expansion,
94 fill: visuals.bg_fill,
95 stroke: visuals.bg_stroke,
96 });
97
98 if checked {
99 painter.add(epaint::CircleShape {
100 center: small_icon_rect.center(),
101 radius: small_icon_rect.width() / 3.0,
102 fill: visuals.fg_stroke.color, stroke: Default::default(),
105 });
106 }
107 }
108 response.response
109 } else {
110 prepared.response
111 }
112 }
113}