egui/widgets/
selected_label.rs

1use crate::{NumExt, Response, Sense, TextStyle, Ui, Widget, WidgetInfo, WidgetText, WidgetType};
2
3/// One out of several alternatives, either selected or not.
4/// Will mark selected items with a different background color.
5/// An alternative to [`crate::RadioButton`] and [`crate::Checkbox`].
6///
7/// Usually you'd use [`Ui::selectable_value`] or [`Ui::selectable_label`] instead.
8///
9/// ```
10/// # egui::__run_test_ui(|ui| {
11/// #[derive(PartialEq)]
12/// enum Enum { First, Second, Third }
13/// let mut my_enum = Enum::First;
14///
15/// ui.selectable_value(&mut my_enum, Enum::First, "First");
16///
17/// // is equivalent to:
18///
19/// if ui.add(egui::SelectableLabel::new(my_enum == Enum::First, "First")).clicked() {
20///     my_enum = Enum::First
21/// }
22/// # });
23/// ```
24#[must_use = "You should put this widget in a ui with `ui.add(widget);`"]
25pub struct SelectableLabel {
26    selected: bool,
27    text: WidgetText,
28}
29
30impl SelectableLabel {
31    pub fn new(selected: bool, text: impl Into<WidgetText>) -> Self {
32        Self {
33            selected,
34            text: text.into(),
35        }
36    }
37}
38
39impl Widget for SelectableLabel {
40    fn ui(self, ui: &mut Ui) -> Response {
41        let Self { selected, text } = self;
42
43        let button_padding = ui.spacing().button_padding;
44        let total_extra = button_padding + button_padding;
45
46        let wrap_width = ui.available_width() - total_extra.x;
47        let galley = text.into_galley(ui, None, wrap_width, TextStyle::Button);
48
49        let mut desired_size = total_extra + galley.size();
50        desired_size.y = desired_size.y.at_least(ui.spacing().interact_size.y);
51        let (rect, response) = ui.allocate_at_least(desired_size, Sense::click());
52        response.widget_info(|| {
53            WidgetInfo::selected(
54                WidgetType::SelectableLabel,
55                ui.is_enabled(),
56                selected,
57                galley.text(),
58            )
59        });
60
61        if ui.is_rect_visible(response.rect) {
62            let text_pos = ui
63                .layout()
64                .align_size_within_rect(galley.size(), rect.shrink2(button_padding))
65                .min;
66
67            let visuals = ui.style().interact_selectable(&response, selected);
68
69            if selected || response.hovered() || response.highlighted() || response.has_focus() {
70                let rect = rect.expand(visuals.expansion);
71
72                ui.painter().rect(
73                    rect,
74                    visuals.corner_radius,
75                    visuals.weak_bg_fill,
76                    visuals.bg_stroke,
77                    epaint::StrokeKind::Inside,
78                );
79            }
80
81            ui.painter().galley(text_pos, galley, visuals.text_color());
82        }
83
84        response
85    }
86}