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}