quick_action_bar.rs

  1use editor::Editor;
  2use gpui::{
  3    elements::{Empty, Flex, MouseEventHandler, ParentElement, Svg},
  4    platform::{CursorStyle, MouseButton},
  5    Action, AnyElement, Element, Entity, EventContext, Subscription, View, ViewContext, ViewHandle,
  6};
  7
  8use search::{buffer_search, BufferSearchBar};
  9use workspace::{item::ItemHandle, ToolbarItemLocation, ToolbarItemView};
 10
 11pub struct QuickActionBar {
 12    buffer_search_bar: ViewHandle<BufferSearchBar>,
 13    active_item: Option<Box<dyn ItemHandle>>,
 14    _inlay_hints_enabled_subscription: Option<Subscription>,
 15}
 16
 17impl QuickActionBar {
 18    pub fn new(buffer_search_bar: ViewHandle<BufferSearchBar>) -> Self {
 19        Self {
 20            buffer_search_bar,
 21            active_item: None,
 22            _inlay_hints_enabled_subscription: None,
 23        }
 24    }
 25
 26    fn active_editor(&self) -> Option<ViewHandle<Editor>> {
 27        self.active_item
 28            .as_ref()
 29            .and_then(|item| item.downcast::<Editor>())
 30    }
 31}
 32
 33impl Entity for QuickActionBar {
 34    type Event = ();
 35}
 36
 37impl View for QuickActionBar {
 38    fn ui_name() -> &'static str {
 39        "QuickActionsBar"
 40    }
 41
 42    fn render(&mut self, cx: &mut gpui::ViewContext<'_, '_, Self>) -> gpui::AnyElement<Self> {
 43        let Some(editor) = self.active_editor() else { return Empty::new().into_any(); };
 44
 45        let inlay_hints_enabled = editor.read(cx).inlay_hints_enabled();
 46        let mut bar = Flex::row().with_child(render_quick_action_bar_button(
 47            0,
 48            "icons/inlay_hint.svg",
 49            inlay_hints_enabled,
 50            (
 51                "Toggle Inlay Hints".to_string(),
 52                Some(Box::new(editor::ToggleInlayHints)),
 53            ),
 54            cx,
 55            |this, cx| {
 56                if let Some(editor) = this.active_editor() {
 57                    editor.update(cx, |editor, cx| {
 58                        editor.toggle_inlay_hints(&editor::ToggleInlayHints, cx);
 59                    });
 60                }
 61            },
 62        ));
 63
 64        if editor.read(cx).buffer().read(cx).is_singleton() {
 65            let search_bar_shown = !self.buffer_search_bar.read(cx).is_dismissed();
 66            let search_action = buffer_search::Deploy { focus: true };
 67
 68            bar = bar.with_child(render_quick_action_bar_button(
 69                1,
 70                "icons/magnifying_glass.svg",
 71                search_bar_shown,
 72                (
 73                    "Buffer Search".to_string(),
 74                    Some(Box::new(search_action.clone())),
 75                ),
 76                cx,
 77                move |this, cx| {
 78                    this.buffer_search_bar.update(cx, |buffer_search_bar, cx| {
 79                        if search_bar_shown {
 80                            buffer_search_bar.dismiss(&buffer_search::Dismiss, cx);
 81                        } else {
 82                            buffer_search_bar.deploy(&search_action, cx);
 83                        }
 84                    });
 85                },
 86            ));
 87        }
 88
 89        bar.into_any()
 90    }
 91}
 92
 93fn render_quick_action_bar_button<
 94    F: 'static + Fn(&mut QuickActionBar, &mut EventContext<QuickActionBar>),
 95>(
 96    index: usize,
 97    icon: &'static str,
 98    toggled: bool,
 99    tooltip: (String, Option<Box<dyn Action>>),
100    cx: &mut ViewContext<QuickActionBar>,
101    on_click: F,
102) -> AnyElement<QuickActionBar> {
103    enum QuickActionBarButton {}
104
105    let theme = theme::current(cx);
106    let (tooltip_text, action) = tooltip;
107
108    MouseEventHandler::new::<QuickActionBarButton, _>(index, cx, |mouse_state, _| {
109        let style = theme
110            .workspace
111            .toolbar
112            .toggleable_tool
113            .in_state(toggled)
114            .style_for(mouse_state);
115        Svg::new(icon)
116            .with_color(style.color)
117            .constrained()
118            .with_width(style.icon_width)
119            .aligned()
120            .constrained()
121            .with_width(style.button_width)
122            .with_height(style.button_width)
123            .contained()
124            .with_style(style.container)
125    })
126    .with_cursor_style(CursorStyle::PointingHand)
127    .on_click(MouseButton::Left, move |_, pane, cx| on_click(pane, cx))
128    .with_tooltip::<QuickActionBarButton>(index, tooltip_text, action, theme.tooltip.clone(), cx)
129    .into_any_named("quick action bar button")
130}
131
132impl ToolbarItemView for QuickActionBar {
133    fn set_active_pane_item(
134        &mut self,
135        active_pane_item: Option<&dyn ItemHandle>,
136        cx: &mut ViewContext<Self>,
137    ) -> ToolbarItemLocation {
138        match active_pane_item {
139            Some(active_item) => {
140                self.active_item = Some(active_item.boxed_clone());
141                self._inlay_hints_enabled_subscription.take();
142
143                if let Some(editor) = active_item.downcast::<Editor>() {
144                    let mut inlay_hints_enabled = editor.read(cx).inlay_hints_enabled();
145                    self._inlay_hints_enabled_subscription =
146                        Some(cx.observe(&editor, move |_, editor, cx| {
147                            let new_inlay_hints_enabled = editor.read(cx).inlay_hints_enabled();
148                            if inlay_hints_enabled != new_inlay_hints_enabled {
149                                inlay_hints_enabled = new_inlay_hints_enabled;
150                                cx.notify();
151                            }
152                        }));
153                    ToolbarItemLocation::PrimaryRight { flex: None }
154                } else {
155                    ToolbarItemLocation::Hidden
156                }
157            }
158            None => {
159                self.active_item = None;
160                ToolbarItemLocation::Hidden
161            }
162        }
163    }
164}