egui_charts/ui_kit/list_item/
label_content.rs1use 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
12pub 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 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 #[must_use]
48 pub fn with_icon(mut self, icon: &'a Icon) -> Self {
49 self.icon = Some(icon);
50 self
51 }
52
53 #[must_use]
55 pub fn subdued(mut self, subdued: bool) -> Self {
56 self.subdued = subdued;
57 self
58 }
59
60 #[must_use]
62 pub fn italics(mut self, italics: bool) -> Self {
63 self.italics = italics;
64 self
65 }
66
67 #[must_use]
69 pub fn strong(mut self, strong: bool) -> Self {
70 self.strong = strong;
71 self
72 }
73
74 #[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 let text_color = if self.subdued {
91 egui_visuals.weak_text_color()
92 } else {
93 list_visuals.text_color(&egui_visuals)
94 };
95
96 let icon_color = list_visuals.icon_tint(&egui_visuals);
98
99 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 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 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}