context_strip.rs

  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}