gpui_component/
breadcrumb.rs1use std::rc::Rc;
2
3use gpui::{
4 div, prelude::FluentBuilder as _, App, ClickEvent, ElementId, InteractiveElement as _,
5 IntoElement, ParentElement, RenderOnce, SharedString, StatefulInteractiveElement,
6 StyleRefinement, Styled, Window,
7};
8
9use crate::{h_flex, ActiveTheme, Icon, IconName, StyledExt};
10
11#[derive(IntoElement)]
12pub struct Breadcrumb {
13 style: StyleRefinement,
14 items: Vec<BreadcrumbItem>,
15}
16
17#[derive(IntoElement)]
18pub struct BreadcrumbItem {
19 id: ElementId,
20 style: StyleRefinement,
21 text: SharedString,
22 on_click: Option<Rc<dyn Fn(&ClickEvent, &mut Window, &mut App)>>,
23 disabled: bool,
24 is_last: bool,
25}
26
27impl BreadcrumbItem {
28 pub fn new(id: impl Into<ElementId>, text: impl Into<SharedString>) -> Self {
29 Self {
30 id: id.into(),
31 style: StyleRefinement::default(),
32 text: text.into(),
33 on_click: None,
34 disabled: false,
35 is_last: false,
36 }
37 }
38
39 pub fn disabled(mut self, disabled: bool) -> Self {
40 self.disabled = disabled;
41 self
42 }
43
44 pub fn on_click(
45 mut self,
46 on_click: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
47 ) -> Self {
48 self.on_click = Some(Rc::new(on_click));
49 self
50 }
51
52 fn is_last(mut self, is_last: bool) -> Self {
54 self.is_last = is_last;
55 self
56 }
57}
58
59impl Styled for BreadcrumbItem {
60 fn style(&mut self) -> &mut StyleRefinement {
61 &mut self.style
62 }
63}
64
65impl RenderOnce for BreadcrumbItem {
66 fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
67 div()
68 .id(self.id)
69 .child(self.text)
70 .text_color(cx.theme().muted_foreground)
71 .when(self.is_last, |this| this.text_color(cx.theme().foreground))
72 .when(self.disabled, |this| {
73 this.text_color(cx.theme().muted_foreground)
74 })
75 .refine_style(&self.style)
76 .when(!self.disabled, |this| {
77 this.when_some(self.on_click, |this, on_click| {
78 this.cursor_pointer().on_click(move |event, window, cx| {
79 on_click(event, window, cx);
80 })
81 })
82 })
83 }
84}
85
86impl Breadcrumb {
87 pub fn new() -> Self {
88 Self {
89 items: Vec::new(),
90 style: StyleRefinement::default(),
91 }
92 }
93
94 pub fn item(mut self, item: BreadcrumbItem) -> Self {
96 self.items.push(item);
97 self
98 }
99}
100
101#[derive(IntoElement)]
102struct BreadcrumbSeparator;
103impl RenderOnce for BreadcrumbSeparator {
104 fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
105 Icon::new(IconName::ChevronRight)
106 .text_color(cx.theme().muted_foreground)
107 .size_3p5()
108 .into_any_element()
109 }
110}
111
112impl Styled for Breadcrumb {
113 fn style(&mut self) -> &mut StyleRefinement {
114 &mut self.style
115 }
116}
117
118impl RenderOnce for Breadcrumb {
119 fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
120 let items_count = self.items.len();
121
122 let mut children = vec![];
123 for (ix, item) in self.items.into_iter().enumerate() {
124 let is_last = ix == items_count - 1;
125
126 children.push(item.is_last(is_last).into_any_element());
127 if !is_last {
128 children.push(BreadcrumbSeparator.into_any_element());
129 }
130 }
131
132 h_flex()
133 .gap_1p5()
134 .text_sm()
135 .text_color(cx.theme().muted_foreground)
136 .refine_style(&self.style)
137 .children(children)
138 }
139}