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}