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