1use std::rc::Rc;
2
3use gpui::{FocusHandle, Model, View, WeakModel, WeakView};
4use ui::{prelude::*, PopoverMenu, PopoverMenuHandle, Tooltip};
5use workspace::Workspace;
6
7use crate::context_picker::{ConfirmBehavior, ContextPicker};
8use crate::context_store::ContextStore;
9use crate::thread_store::ThreadStore;
10use crate::ui::ContextPill;
11use crate::ToggleContextPicker;
12use settings::Settings;
13
14pub struct ContextStrip {
15 context_store: Model<ContextStore>,
16 context_picker: View<ContextPicker>,
17 context_picker_menu_handle: PopoverMenuHandle<ContextPicker>,
18 focus_handle: FocusHandle,
19}
20
21impl ContextStrip {
22 pub fn new(
23 context_store: Model<ContextStore>,
24 workspace: WeakView<Workspace>,
25 thread_store: Option<WeakModel<ThreadStore>>,
26 focus_handle: FocusHandle,
27 context_picker_menu_handle: PopoverMenuHandle<ContextPicker>,
28 cx: &mut ViewContext<Self>,
29 ) -> Self {
30 Self {
31 context_store: context_store.clone(),
32 context_picker: cx.new_view(|cx| {
33 ContextPicker::new(
34 workspace.clone(),
35 thread_store.clone(),
36 context_store.downgrade(),
37 ConfirmBehavior::KeepOpen,
38 cx,
39 )
40 }),
41 context_picker_menu_handle,
42 focus_handle,
43 }
44 }
45}
46
47impl Render for ContextStrip {
48 fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
49 let context = self.context_store.read(cx).context().clone();
50 let context_picker = self.context_picker.clone();
51 let focus_handle = self.focus_handle.clone();
52
53 h_flex()
54 .flex_wrap()
55 .gap_1()
56 .child(
57 PopoverMenu::new("context-picker")
58 .menu(move |_cx| Some(context_picker.clone()))
59 .trigger(
60 IconButton::new("add-context", IconName::Plus)
61 .icon_size(IconSize::Small)
62 .style(ui::ButtonStyle::Filled)
63 .tooltip(move |cx| {
64 Tooltip::for_action_in(
65 "Add Context",
66 &ToggleContextPicker,
67 &focus_handle,
68 cx,
69 )
70 }),
71 )
72 .attach(gpui::Corner::TopLeft)
73 .anchor(gpui::Corner::BottomLeft)
74 .offset(gpui::Point {
75 x: px(0.0),
76 y: px(-16.0),
77 })
78 .with_handle(self.context_picker_menu_handle.clone()),
79 )
80 .when(context.is_empty(), {
81 |parent| {
82 parent.child(
83 h_flex()
84 .id("no-content-info")
85 .ml_1p5()
86 .gap_2()
87 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
88 .text_size(TextSize::Small.rems(cx))
89 .text_color(cx.theme().colors().text_muted)
90 .child("Add Context")
91 .children(
92 ui::KeyBinding::for_action_in(
93 &ToggleContextPicker,
94 &self.focus_handle,
95 cx,
96 )
97 .map(|binding| binding.into_any_element()),
98 )
99 .opacity(0.5),
100 )
101 }
102 })
103 .children(context.iter().map(|context| {
104 ContextPill::new(context.clone()).on_remove({
105 let context = context.clone();
106 let context_store = self.context_store.clone();
107 Rc::new(cx.listener(move |_this, _event, cx| {
108 context_store.update(cx, |this, _cx| {
109 this.remove_context(&context.id);
110 });
111 cx.notify();
112 }))
113 })
114 }))
115 .when(!context.is_empty(), {
116 move |parent| {
117 parent.child(
118 IconButton::new("remove-all-context", IconName::Eraser)
119 .icon_size(IconSize::Small)
120 .tooltip(move |cx| Tooltip::text("Remove All Context", cx))
121 .on_click({
122 let context_store = self.context_store.clone();
123 cx.listener(move |_this, _event, cx| {
124 context_store.update(cx, |this, _cx| this.clear());
125 cx.notify();
126 })
127 }),
128 )
129 }
130 })
131 }
132}