Skip to main content

egui_charts/ui_kit/list_item/
label_content.rs

1//! LabelContent - Simple text content with optional icon
2
3use crate::localization as l10n;
4use crate::theme::ThemeExt;
5use egui::{Response, RichText, Role, Ui, Vec2, WidgetInfo};
6
7use crate::icons::Icon;
8use crate::styles::icons;
9
10use super::content::{ContentContext, ListItemContent};
11
12/// Simple text content with optional icon
13///
14/// # Example
15///
16/// ```
17/// # use egui_charts::icons::icons;
18/// # use egui_charts::ui_kit::list_item::{LabelContent, ListItem};
19/// # egui::__run_test_ui(|ui| {
20/// ListItem::new()
21///     .show(ui, LabelContent::new("Hello World").with_icon(&icons::SETTINGS));
22/// # });
23/// ```
24pub struct LabelContent<'a> {
25    text: String,
26    icon: Option<&'a Icon>,
27    subdued: bool,
28    italics: bool,
29    strong: bool,
30    icon_size: f32,
31}
32
33impl<'a> LabelContent<'a> {
34    /// Create new label content with the given text
35    pub fn new(text: impl Into<String>) -> Self {
36        Self {
37            text: text.into(),
38            icon: None,
39            subdued: false,
40            italics: false,
41            strong: false,
42            icon_size: icons::SM,
43        }
44    }
45
46    /// Add an icon to the label
47    #[must_use]
48    pub fn with_icon(mut self, icon: &'a Icon) -> Self {
49        self.icon = Some(icon);
50        self
51    }
52
53    /// Make the text subdued (secondary color)
54    #[must_use]
55    pub fn subdued(mut self, subdued: bool) -> Self {
56        self.subdued = subdued;
57        self
58    }
59
60    /// Make the text italic
61    #[must_use]
62    pub fn italics(mut self, italics: bool) -> Self {
63        self.italics = italics;
64        self
65    }
66
67    /// Make the text bold
68    #[must_use]
69    pub fn strong(mut self, strong: bool) -> Self {
70        self.strong = strong;
71        self
72    }
73
74    /// Set the icon size
75    #[must_use]
76    pub fn icon_size(mut self, size: f32) -> Self {
77        self.icon_size = size;
78        self
79    }
80}
81
82impl<'a> ListItemContent for LabelContent<'a> {
83    fn ui(self, ui: &mut Ui, context: &ContentContext<'_>) -> Response {
84        let egui_visuals = ui.style().visuals.clone();
85
86        let mut list_visuals = context.visuals.clone();
87        list_visuals.strong = self.strong;
88
89        // Determine text color using ListVisuals
90        let text_color = if self.subdued {
91            egui_visuals.weak_text_color()
92        } else {
93            list_visuals.text_color(&egui_visuals)
94        };
95
96        // Determine icon color using ListVisuals
97        let icon_color = list_visuals.icon_tint(&egui_visuals);
98
99        // Calculate layout
100        let icon_width = if self.icon.is_some() {
101            self.icon_size + ui.active_theme().spacing.sm
102        } else {
103            0.0
104        };
105
106        let content_rect = context.rect;
107        let is_menu = ui.stack().contained_in(egui::UiKind::Menu);
108        let translated_label = l10n::text(ui.ctx(), &self.text);
109        context.response.widget_info(|| {
110            WidgetInfo::labeled(
111                if is_menu {
112                    Role::MenuItem
113                } else if context.response.sense.senses_click() {
114                    Role::ListBoxOption
115                } else {
116                    Role::ListItem
117                },
118                context.response.enabled(),
119                &translated_label,
120            )
121        });
122
123        if !is_menu && context.response.sense.senses_click() {
124            ui.ctx()
125                .accesskit_node_builder(context.response.id, |node| {
126                    node.set_selected(context.selected)
127                });
128        }
129
130        if ui.is_rect_visible(content_rect) {
131            // Draw icon if present
132            if let Some(icon) = self.icon {
133                let icon_rect = egui::Rect::from_min_size(
134                    content_rect.min
135                        + Vec2::new(0.0, (content_rect.height() - self.icon_size) / 2.0),
136                    Vec2::splat(self.icon_size),
137                );
138                icon.as_image_tinted(Vec2::splat(self.icon_size), icon_color)
139                    .paint_at(ui, icon_rect);
140            }
141
142            // Draw text
143            let text_pos = egui::pos2(content_rect.min.x + icon_width, content_rect.center().y);
144
145            let mut rich_text = RichText::new(l10n::text(ui.ctx(), &self.text)).color(text_color);
146            if self.italics {
147                rich_text = rich_text.italics();
148            }
149            if self.strong {
150                rich_text = rich_text.strong();
151            }
152
153            ui.painter().text(
154                text_pos,
155                egui::Align2::LEFT_CENTER,
156                rich_text.text(),
157                egui::TextStyle::Body.resolve(ui.style()),
158                text_color,
159            );
160        }
161
162        context.response.clone()
163    }
164}