context_pill.rs

  1use std::rc::Rc;
  2
  3use gpui::ClickEvent;
  4use ui::{prelude::*, IconButtonShape, Tooltip};
  5
  6use crate::context::{Context, ContextKind};
  7
  8#[derive(IntoElement)]
  9pub enum ContextPill {
 10    Added {
 11        context: Context,
 12        dupe_name: bool,
 13        on_remove: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext)>>,
 14    },
 15    Suggested {
 16        name: SharedString,
 17        kind: ContextKind,
 18        on_add: Rc<dyn Fn(&ClickEvent, &mut WindowContext)>,
 19    },
 20}
 21
 22impl ContextPill {
 23    pub fn new_added(
 24        context: Context,
 25        dupe_name: bool,
 26        on_remove: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext)>>,
 27    ) -> Self {
 28        Self::Added {
 29            context,
 30            dupe_name,
 31            on_remove,
 32        }
 33    }
 34
 35    pub fn new_suggested(
 36        name: SharedString,
 37        kind: ContextKind,
 38        on_add: Rc<dyn Fn(&ClickEvent, &mut WindowContext)>,
 39    ) -> Self {
 40        Self::Suggested { name, kind, on_add }
 41    }
 42
 43    pub fn id(&self) -> ElementId {
 44        match self {
 45            Self::Added { context, .. } => {
 46                ElementId::NamedInteger("context-pill".into(), context.id.0)
 47            }
 48            Self::Suggested { .. } => "suggested-context-pill".into(),
 49        }
 50    }
 51
 52    pub fn kind(&self) -> &ContextKind {
 53        match self {
 54            Self::Added { context, .. } => &context.kind,
 55            Self::Suggested { kind, .. } => kind,
 56        }
 57    }
 58}
 59
 60impl RenderOnce for ContextPill {
 61    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
 62        let icon = match &self.kind() {
 63            ContextKind::File => IconName::File,
 64            ContextKind::Directory => IconName::Folder,
 65            ContextKind::FetchedUrl => IconName::Globe,
 66            ContextKind::Thread => IconName::MessageCircle,
 67        };
 68
 69        let color = cx.theme().colors();
 70
 71        let base_pill = h_flex()
 72            .id(self.id())
 73            .pl_1()
 74            .pb(px(1.))
 75            .border_1()
 76            .rounded_md()
 77            .gap_1()
 78            .child(Icon::new(icon).size(IconSize::XSmall).color(Color::Muted));
 79
 80        match &self {
 81            ContextPill::Added {
 82                context,
 83                dupe_name,
 84                on_remove,
 85            } => base_pill
 86                .bg(color.element_background)
 87                .border_color(color.border.opacity(0.5))
 88                .pr(if on_remove.is_some() { px(2.) } else { px(4.) })
 89                .child(
 90                    h_flex()
 91                        .id("context-data")
 92                        .gap_1()
 93                        .child(Label::new(context.name.clone()).size(LabelSize::Small))
 94                        .when_some(context.parent.as_ref(), |element, parent_name| {
 95                            if *dupe_name {
 96                                element.child(
 97                                    Label::new(parent_name.clone())
 98                                        .size(LabelSize::XSmall)
 99                                        .color(Color::Muted),
100                                )
101                            } else {
102                                element
103                            }
104                        })
105                        .when_some(context.tooltip.clone(), |element, tooltip| {
106                            element.tooltip(move |cx| Tooltip::text(tooltip.clone(), cx))
107                        }),
108                )
109                .when_some(on_remove.as_ref(), |element, on_remove| {
110                    element.child(
111                        IconButton::new(("remove", context.id.0), IconName::Close)
112                            .shape(IconButtonShape::Square)
113                            .icon_size(IconSize::XSmall)
114                            .tooltip(|cx| Tooltip::text("Remove Context", cx))
115                            .on_click({
116                                let on_remove = on_remove.clone();
117                                move |event, cx| on_remove(event, cx)
118                            }),
119                    )
120                }),
121            ContextPill::Suggested { name, kind, on_add } => base_pill
122                .cursor_pointer()
123                .pr_1()
124                .border_color(color.border_variant.opacity(0.5))
125                .hover(|style| style.bg(color.element_hover.opacity(0.5)))
126                .child(
127                    Label::new(name.clone())
128                        .size(LabelSize::Small)
129                        .color(Color::Muted),
130                )
131                .child(
132                    Label::new(match kind {
133                        ContextKind::File => "Open File",
134                        ContextKind::Thread | ContextKind::Directory | ContextKind::FetchedUrl => {
135                            "Active"
136                        }
137                    })
138                    .size(LabelSize::XSmall)
139                    .color(Color::Muted),
140                )
141                .child(
142                    Icon::new(IconName::Plus)
143                        .size(IconSize::XSmall)
144                        .into_any_element(),
145                )
146                .tooltip(|cx| Tooltip::with_meta("Suggested Context", None, "Click to add it", cx))
147                .on_click({
148                    let on_add = on_add.clone();
149                    move |event, cx| on_add(event, cx)
150                }),
151        }
152    }
153}