context_pill.rs

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