egui_charts/ui_kit/empty_state/
mod.rs1use crate::localization as l10n;
17use egui::{Frame, Margin, RichText, Ui, Vec2};
18
19use crate::icons::Icon;
20use crate::theme::ThemeExt;
21use crate::ui_kit::buttons::Button;
22
23const ICON_SIZE: f32 = 32.0;
25
26pub struct EmptyState<'a> {
29 message: &'a str,
30 description: Option<&'a str>,
31 icon: Option<&'static Icon>,
32 action: Option<&'a str>,
33 fill_height: bool,
34}
35
36impl<'a> EmptyState<'a> {
37 pub fn new(message: &'a str) -> Self {
39 Self {
40 message,
41 description: None,
42 icon: None,
43 action: None,
44 fill_height: false,
45 }
46 }
47
48 #[must_use]
50 pub fn description(mut self, desc: &'a str) -> Self {
51 self.description = Some(desc);
52 self
53 }
54
55 #[must_use]
57 pub fn icon(mut self, icon: &'static Icon) -> Self {
58 self.icon = Some(icon);
59 self
60 }
61
62 #[must_use]
64 pub fn action(mut self, label: &'a str) -> Self {
65 self.action = Some(label);
66 self
67 }
68
69 #[must_use]
72 pub fn fill_height(mut self) -> Self {
73 self.fill_height = true;
74 self
75 }
76
77 pub fn show(self, ui: &mut Ui) -> bool {
79 let theme = ui.active_theme();
80 let height_id = ui.next_auto_id().with("empty_state_height");
82 let last_height = if self.fill_height {
83 ui.data(|d| d.get_temp::<f32>(height_id))
84 } else {
85 None
86 };
87 if let Some(height) = last_height {
88 ui.add_space(((ui.available_height() - height) / 2.0).max(0.0));
89 }
90
91 let shown = Frame::new()
92 .inner_margin(Margin::symmetric(0, theme.spacing.xl as i8))
93 .show(ui, |ui| {
94 ui.vertical_centered(|ui| {
95 ui.spacing_mut().item_spacing.y = theme.spacing.lg;
96 let muted = ui.visuals().weak_text_color();
97 if let Some(icon) = self.icon {
98 ui.add(icon.as_image_tinted(Vec2::splat(ICON_SIZE), muted));
99 }
100 ui.label(
101 RichText::new(l10n::text(ui.ctx(), self.message)).size(theme.text.body),
102 );
103 if let Some(desc) = self.description {
104 ui.label(
105 RichText::new(l10n::text(ui.ctx(), desc))
106 .size(theme.text.small)
107 .color(muted),
108 );
109 }
110 self.action
111 .is_some_and(|label| Button::new(label).primary().show(ui).clicked())
112 })
113 .inner
114 });
115
116 let height = shown.response.rect.height();
117 if self.fill_height && last_height != Some(height) {
118 ui.data_mut(|d| d.insert_temp(height_id, height));
119 ui.ctx().request_discard("EmptyState measured its height");
120 }
121 shown.inner
122 }
123}