quick_action_bar.rs

  1// use assistant::{assistant_panel::InlineAssist, AssistantPanel};
  2use editor::Editor;
  3
  4use gpui::{
  5    Action, Div, ElementId, EventEmitter, InteractiveElement, ParentElement, Render, Stateful,
  6    Styled, Subscription, View, ViewContext, WeakView,
  7};
  8use search::BufferSearchBar;
  9use ui::{prelude::*, ButtonSize, ButtonStyle, Icon, IconButton, IconSize, Tooltip};
 10use workspace::{
 11    item::ItemHandle, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace,
 12};
 13
 14pub struct QuickActionBar {
 15    buffer_search_bar: View<BufferSearchBar>,
 16    active_item: Option<Box<dyn ItemHandle>>,
 17    _inlay_hints_enabled_subscription: Option<Subscription>,
 18    #[allow(unused)]
 19    workspace: WeakView<Workspace>,
 20}
 21
 22impl QuickActionBar {
 23    pub fn new(buffer_search_bar: View<BufferSearchBar>, workspace: &Workspace) -> Self {
 24        Self {
 25            buffer_search_bar,
 26            active_item: None,
 27            _inlay_hints_enabled_subscription: None,
 28            workspace: workspace.weak_handle(),
 29        }
 30    }
 31
 32    #[allow(dead_code)]
 33    fn active_editor(&self) -> Option<View<Editor>> {
 34        self.active_item
 35            .as_ref()
 36            .and_then(|item| item.downcast::<Editor>())
 37    }
 38}
 39
 40impl Render for QuickActionBar {
 41    type Element = Stateful<Div>;
 42
 43    fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
 44        let search_button = QuickActionBarButton::new(
 45            "toggle buffer search",
 46            Icon::MagnifyingGlass,
 47            !self.buffer_search_bar.read(cx).is_dismissed(),
 48            Box::new(search::buffer_search::Deploy { focus: false }),
 49            "Buffer Search",
 50        );
 51        let assistant_button = QuickActionBarButton::new(
 52            "toggle inline assitant",
 53            Icon::MagicWand,
 54            false,
 55            Box::new(gpui::NoAction),
 56            "Inline assistant",
 57        );
 58        h_stack()
 59            .id("quick action bar")
 60            .p_1()
 61            .gap_2()
 62            .child(search_button)
 63            .child(
 64                div()
 65                    .border()
 66                    .border_color(gpui::red())
 67                    .child(assistant_button),
 68            )
 69    }
 70}
 71
 72impl EventEmitter<ToolbarItemEvent> for QuickActionBar {}
 73
 74// impl View for QuickActionBar {
 75//     fn ui_name() -> &'static str {
 76//         "QuickActionsBar"
 77//     }
 78
 79//     fn render(&mut self, cx: &mut gpui::ViewContext<'_, '_, Self>) -> gpui::AnyElement<Self> {
 80//         let Some(editor) = self.active_editor() else {
 81//             return div();
 82//         };
 83
 84//         let mut bar = Flex::row();
 85//         if editor.read(cx).supports_inlay_hints(cx) {
 86//             bar = bar.with_child(render_quick_action_bar_button(
 87//                 0,
 88//                 "icons/inlay_hint.svg",
 89//                 editor.read(cx).inlay_hints_enabled(),
 90//                 (
 91//                     "Toggle Inlay Hints".to_string(),
 92//                     Some(Box::new(editor::ToggleInlayHints)),
 93//                 ),
 94//                 cx,
 95//                 |this, cx| {
 96//                     if let Some(editor) = this.active_editor() {
 97//                         editor.update(cx, |editor, cx| {
 98//                             editor.toggle_inlay_hints(&editor::ToggleInlayHints, cx);
 99//                         });
100//                     }
101//                 },
102//             ));
103//         }
104
105//         if editor.read(cx).buffer().read(cx).is_singleton() {
106//             let search_bar_shown = !self.buffer_search_bar.read(cx).is_dismissed();
107//             let search_action = buffer_search::Deploy { focus: true };
108
109//             bar = bar.with_child(render_quick_action_bar_button(
110//                 1,
111//                 "icons/magnifying_glass.svg",
112//                 search_bar_shown,
113//                 (
114//                     "Buffer Search".to_string(),
115//                     Some(Box::new(search_action.clone())),
116//                 ),
117//                 cx,
118//                 move |this, cx| {
119//                     this.buffer_search_bar.update(cx, |buffer_search_bar, cx| {
120//                         if search_bar_shown {
121//                             buffer_search_bar.dismiss(&buffer_search::Dismiss, cx);
122//                         } else {
123//                             buffer_search_bar.deploy(&search_action, cx);
124//                         }
125//                     });
126//                 },
127//             ));
128//         }
129
130//         bar.add_child(render_quick_action_bar_button(
131//             2,
132//             "icons/magic-wand.svg",
133//             false,
134//             ("Inline Assist".into(), Some(Box::new(InlineAssist))),
135//             cx,
136//             move |this, cx| {
137//                 if let Some(workspace) = this.workspace.upgrade(cx) {
138//                     workspace.update(cx, |workspace, cx| {
139//                         AssistantPanel::inline_assist(workspace, &Default::default(), cx);
140//                     });
141//                 }
142//             },
143//         ));
144
145//         bar.into_any()
146//     }
147// }
148
149#[derive(IntoElement)]
150struct QuickActionBarButton {
151    id: ElementId,
152    icon: Icon,
153    toggled: bool,
154    action: Box<dyn Action>,
155    tooltip: SharedString,
156    tooltip_meta: Option<SharedString>,
157}
158
159impl QuickActionBarButton {
160    fn new(
161        id: impl Into<ElementId>,
162        icon: Icon,
163        toggled: bool,
164        action: Box<dyn Action>,
165        tooltip: impl Into<SharedString>,
166    ) -> Self {
167        Self {
168            id: id.into(),
169            icon,
170            toggled,
171            action,
172            tooltip: tooltip.into(),
173            tooltip_meta: None,
174        }
175    }
176
177    #[allow(dead_code)]
178    pub fn meta(mut self, meta: Option<impl Into<SharedString>>) -> Self {
179        self.tooltip_meta = meta.map(|meta| meta.into());
180        self
181    }
182}
183
184impl RenderOnce for QuickActionBarButton {
185    type Rendered = IconButton;
186
187    fn render(self, _: &mut WindowContext) -> Self::Rendered {
188        let tooltip = self.tooltip.clone();
189        let action = self.action.boxed_clone();
190        let tooltip_meta = self.tooltip_meta.clone();
191
192        IconButton::new(self.id.clone(), self.icon)
193            .size(ButtonSize::Compact)
194            .icon_size(IconSize::Small)
195            .style(ButtonStyle::Subtle)
196            .selected(self.toggled)
197            .tooltip(move |cx| {
198                if let Some(meta) = &tooltip_meta {
199                    Tooltip::with_meta(tooltip.clone(), Some(&*action), meta.clone(), cx)
200                } else {
201                    Tooltip::for_action(tooltip.clone(), &*action, cx)
202                }
203            })
204            .on_click({
205                let action = self.action.boxed_clone();
206                move |_, cx| cx.dispatch_action(action.boxed_clone())
207            })
208    }
209}
210
211// fn render_quick_action_bar_button<
212//     F: 'static + Fn(&mut QuickActionBar, &mut ViewContext<QuickActionBar>),
213// >(
214//     index: usize,
215//     icon: &'static str,
216//     toggled: bool,
217//     tooltip: (String, Option<Box<dyn Action>>),
218//     cx: &mut ViewContext<QuickActionBar>,
219//     on_click: F,
220// ) -> AnyElement<QuickActionBar> {
221//     enum QuickActionBarButton {}
222
223//     let theme = theme::current(cx);
224//     let (tooltip_text, action) = tooltip;
225
226//     MouseEventHandler::new::<QuickActionBarButton, _>(index, cx, |mouse_state, _| {
227//         let style = theme
228//             .workspace
229//             .toolbar
230//             .toggleable_tool
231//             .in_state(toggled)
232//             .style_for(mouse_state);
233//         Svg::new(icon)
234//             .with_color(style.color)
235//             .constrained()
236//             .with_width(style.icon_width)
237//             .aligned()
238//             .constrained()
239//             .with_width(style.button_width)
240//             .with_height(style.button_width)
241//             .contained()
242//             .with_style(style.container)
243//     })
244//     .with_cursor_style(CursorStyle::PointingHand)
245//     .on_click(MouseButton::Left, move |_, pane, cx| on_click(pane, cx))
246//     .with_tooltip::<QuickActionBarButton>(index, tooltip_text, action, theme.tooltip.clone(), cx)
247//     .into_any_named("quick action bar button")
248// }
249
250impl ToolbarItemView for QuickActionBar {
251    fn set_active_pane_item(
252        &mut self,
253        active_pane_item: Option<&dyn ItemHandle>,
254        cx: &mut ViewContext<Self>,
255    ) -> ToolbarItemLocation {
256        match active_pane_item {
257            Some(active_item) => {
258                self.active_item = Some(active_item.boxed_clone());
259                self._inlay_hints_enabled_subscription.take();
260
261                if let Some(editor) = active_item.downcast::<Editor>() {
262                    let mut inlay_hints_enabled = editor.read(cx).inlay_hints_enabled();
263                    let mut supports_inlay_hints = editor.read(cx).supports_inlay_hints(cx);
264                    self._inlay_hints_enabled_subscription =
265                        Some(cx.observe(&editor, move |_, editor, cx| {
266                            let editor = editor.read(cx);
267                            let new_inlay_hints_enabled = editor.inlay_hints_enabled();
268                            let new_supports_inlay_hints = editor.supports_inlay_hints(cx);
269                            let should_notify = inlay_hints_enabled != new_inlay_hints_enabled
270                                || supports_inlay_hints != new_supports_inlay_hints;
271                            inlay_hints_enabled = new_inlay_hints_enabled;
272                            supports_inlay_hints = new_supports_inlay_hints;
273                            if should_notify {
274                                cx.notify()
275                            }
276                        }));
277                    ToolbarItemLocation::PrimaryRight
278                } else {
279                    ToolbarItemLocation::Hidden
280                }
281            }
282            None => {
283                self.active_item = None;
284                ToolbarItemLocation::Hidden
285            }
286        }
287    }
288}