context_strip.rs

  1use std::rc::Rc;
  2
  3use gpui::{View, WeakModel, WeakView};
  4use ui::{prelude::*, IconButtonShape, PopoverMenu, PopoverMenuHandle, Tooltip};
  5use workspace::Workspace;
  6
  7use crate::context::{Context, ContextId, ContextKind};
  8use crate::context_picker::ContextPicker;
  9use crate::thread_store::ThreadStore;
 10use crate::ui::ContextPill;
 11
 12pub struct ContextStrip {
 13    context: Vec<Context>,
 14    next_context_id: ContextId,
 15    context_picker: View<ContextPicker>,
 16    pub(crate) context_picker_handle: PopoverMenuHandle<ContextPicker>,
 17}
 18
 19impl ContextStrip {
 20    pub fn new(
 21        workspace: WeakView<Workspace>,
 22        thread_store: WeakModel<ThreadStore>,
 23        cx: &mut ViewContext<Self>,
 24    ) -> Self {
 25        let weak_self = cx.view().downgrade();
 26
 27        Self {
 28            context: Vec::new(),
 29            next_context_id: ContextId(0),
 30            context_picker: cx.new_view(|cx| {
 31                ContextPicker::new(workspace.clone(), thread_store.clone(), weak_self, cx)
 32            }),
 33            context_picker_handle: PopoverMenuHandle::default(),
 34        }
 35    }
 36
 37    pub fn drain(&mut self) -> Vec<Context> {
 38        self.context.drain(..).collect()
 39    }
 40
 41    pub fn insert_context(
 42        &mut self,
 43        kind: ContextKind,
 44        name: impl Into<SharedString>,
 45        text: impl Into<SharedString>,
 46    ) {
 47        self.context.push(Context {
 48            id: self.next_context_id.post_inc(),
 49            name: name.into(),
 50            kind,
 51            text: text.into(),
 52        });
 53    }
 54}
 55
 56impl Render for ContextStrip {
 57    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
 58        let context_picker = self.context_picker.clone();
 59
 60        h_flex()
 61            .flex_wrap()
 62            .gap_2()
 63            .child(
 64                PopoverMenu::new("context-picker")
 65                    .menu(move |_cx| Some(context_picker.clone()))
 66                    .trigger(
 67                        IconButton::new("add-context", IconName::Plus)
 68                            .shape(IconButtonShape::Square)
 69                            .icon_size(IconSize::Small),
 70                    )
 71                    .attach(gpui::AnchorCorner::TopLeft)
 72                    .anchor(gpui::AnchorCorner::BottomLeft)
 73                    .offset(gpui::Point {
 74                        x: px(0.0),
 75                        y: px(-16.0),
 76                    })
 77                    .with_handle(self.context_picker_handle.clone()),
 78            )
 79            .children(self.context.iter().map(|context| {
 80                ContextPill::new(context.clone()).on_remove({
 81                    let context = context.clone();
 82                    Rc::new(cx.listener(move |this, _event, cx| {
 83                        this.context.retain(|other| other.id != context.id);
 84                        cx.notify();
 85                    }))
 86                })
 87            }))
 88            .when(!self.context.is_empty(), |parent| {
 89                parent.child(
 90                    IconButton::new("remove-all-context", IconName::Eraser)
 91                        .shape(IconButtonShape::Square)
 92                        .icon_size(IconSize::Small)
 93                        .tooltip(move |cx| Tooltip::text("Remove All Context", cx))
 94                        .on_click(cx.listener(|this, _event, cx| {
 95                            this.context.clear();
 96                            cx.notify();
 97                        })),
 98                )
 99            })
100    }
101}