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}