egui/widgets/
radio_button.rs1use crate::{
2 epaint, pos2, vec2, NumExt, Response, Sense, TextStyle, Ui, Vec2, Widget, WidgetInfo,
3 WidgetText, WidgetType,
4};
5
6#[must_use = "You should put this widget in a ui with `ui.add(widget);`"]
26pub struct RadioButton {
27 checked: bool,
28 text: WidgetText,
29}
30
31impl RadioButton {
32 pub fn new(checked: bool, text: impl Into<WidgetText>) -> Self {
33 Self {
34 checked,
35 text: text.into(),
36 }
37 }
38}
39
40impl Widget for RadioButton {
41 fn ui(self, ui: &mut Ui) -> Response {
42 let Self { checked, text } = self;
43
44 let spacing = &ui.spacing();
45 let icon_width = spacing.icon_width;
46 let icon_spacing = spacing.icon_spacing;
47
48 let (galley, mut desired_size) = if text.is_empty() {
49 (None, vec2(icon_width, 0.0))
50 } else {
51 let total_extra = vec2(icon_width + icon_spacing, 0.0);
52
53 let wrap_width = ui.available_width() - total_extra.x;
54 let text = text.into_galley(ui, None, wrap_width, TextStyle::Button);
55
56 let mut desired_size = total_extra + text.size();
57 desired_size = desired_size.at_least(spacing.interact_size);
58
59 (Some(text), desired_size)
60 };
61
62 desired_size = desired_size.at_least(Vec2::splat(spacing.interact_size.y));
63 desired_size.y = desired_size.y.max(icon_width);
64 let (rect, response) = ui.allocate_exact_size(desired_size, Sense::click());
65
66 response.widget_info(|| {
67 WidgetInfo::selected(
68 WidgetType::RadioButton,
69 ui.is_enabled(),
70 checked,
71 galley.as_ref().map_or("", |x| x.text()),
72 )
73 });
74
75 if ui.is_rect_visible(rect) {
76 let visuals = ui.style().interact(&response);
78
79 let (small_icon_rect, big_icon_rect) = ui.spacing().icon_rectangles(rect);
80
81 let painter = ui.painter();
82
83 painter.add(epaint::CircleShape {
84 center: big_icon_rect.center(),
85 radius: big_icon_rect.width() / 2.0 + visuals.expansion,
86 fill: visuals.bg_fill,
87 stroke: visuals.bg_stroke,
88 });
89
90 if checked {
91 painter.add(epaint::CircleShape {
92 center: small_icon_rect.center(),
93 radius: small_icon_rect.width() / 3.0,
94 fill: visuals.fg_stroke.color, stroke: Default::default(),
97 });
98 }
99
100 if let Some(galley) = galley {
101 let text_pos = pos2(
102 rect.min.x + icon_width + icon_spacing,
103 rect.center().y - 0.5 * galley.size().y,
104 );
105 ui.painter().galley(text_pos, galley, visuals.text_color());
106 }
107 }
108
109 response
110 }
111}