egui_components/
breadcrumb.rs1use egui::{vec2, FontId, Rect, Sense, Ui};
19use egui_components_theme::Theme;
20
21use crate::icon::{paint_icon, IconKind};
22
23pub struct Breadcrumb {
24 items: Vec<String>,
25 has_current: bool,
27}
28
29impl Default for Breadcrumb {
30 fn default() -> Self {
31 Self::new()
32 }
33}
34
35impl Breadcrumb {
36 pub fn new() -> Self {
37 Self {
38 items: Vec::new(),
39 has_current: false,
40 }
41 }
42
43 pub fn item(mut self, label: impl Into<String>) -> Self {
45 self.items.push(label.into());
46 self.has_current = false;
47 self
48 }
49
50 pub fn current(mut self, label: impl Into<String>) -> Self {
52 self.items.push(label.into());
53 self.has_current = true;
54 self
55 }
56
57 pub fn show(self, ui: &mut Ui) -> Option<usize> {
58 let theme = Theme::get(ui.ctx());
59 let c = theme.colors;
60 let m = theme.metrics;
61 let font = FontId::proportional(m.font_size_sm);
62 let last = self.items.len().saturating_sub(1);
63
64 let mut clicked = None;
65 ui.horizontal(|ui| {
66 for (i, label) in self.items.iter().enumerate() {
67 let is_current = self.has_current && i == last;
68 let galley = ui.ctx().fonts_mut(|f| {
69 f.layout_no_wrap(label.clone(), font.clone(), c.muted_foreground)
70 });
71 let sense = if is_current {
72 Sense::hover()
73 } else {
74 Sense::click()
75 };
76 let (rect, resp) = ui.allocate_exact_size(galley.size(), sense);
77
78 let color = if is_current {
79 c.foreground
80 } else if resp.hovered() {
81 c.link_hover_foreground
82 } else {
83 c.muted_foreground
84 };
85 ui.painter()
86 .galley_with_override_text_color(rect.min, galley, color);
87 if resp.hovered() && !is_current {
88 let y = rect.bottom() - 1.0;
89 ui.painter().line_segment(
90 [egui::pos2(rect.left(), y), egui::pos2(rect.right(), y)],
91 egui::Stroke::new(1.0, color),
92 );
93 ui.ctx().set_cursor_icon(egui::CursorIcon::PointingHand);
94 }
95 if resp.clicked() {
96 clicked = Some(i);
97 }
98
99 if i != last {
101 let (sep_rect, _) =
102 ui.allocate_exact_size(vec2(14.0, galley_h(&font)), Sense::hover());
103 let ir = Rect::from_center_size(sep_rect.center(), vec2(12.0, 12.0));
104 paint_icon(ui.painter(), IconKind::ChevronRight, ir, c.muted_foreground, 1.4);
105 }
106 }
107 });
108
109 clicked
110 }
111}
112
113fn galley_h(font: &FontId) -> f32 {
114 font.size + 4.0
115}