1use assistant::{AssistantPanel, InlineAssist};
2use editor::Editor;
3
4use gpui::{
5 Action, ClickEvent, ElementId, EventEmitter, InteractiveElement, ParentElement, Render, Styled,
6 Subscription, View, ViewContext, WeakView,
7};
8use search::{buffer_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 workspace: WeakView<Workspace>,
19}
20
21impl QuickActionBar {
22 pub fn new(buffer_search_bar: View<BufferSearchBar>, workspace: &Workspace) -> Self {
23 Self {
24 buffer_search_bar,
25 active_item: None,
26 _inlay_hints_enabled_subscription: None,
27 workspace: workspace.weak_handle(),
28 }
29 }
30
31 fn active_editor(&self) -> Option<View<Editor>> {
32 self.active_item
33 .as_ref()
34 .and_then(|item| item.downcast::<Editor>())
35 }
36}
37
38impl Render for QuickActionBar {
39 fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
40 let Some(editor) = self.active_editor() else {
41 return div().id("empty quick action bar");
42 };
43
44 let inlay_hints_button = Some(QuickActionBarButton::new(
45 "toggle inlay hints",
46 Icon::InlayHint,
47 editor.read(cx).inlay_hints_enabled(),
48 Box::new(editor::ToggleInlayHints),
49 "Toggle Inlay Hints",
50 {
51 let editor = editor.clone();
52 move |_, cx| {
53 editor.update(cx, |editor, cx| {
54 editor.toggle_inlay_hints(&editor::ToggleInlayHints, cx);
55 });
56 }
57 },
58 ))
59 .filter(|_| editor.read(cx).supports_inlay_hints(cx));
60
61 let search_button = Some(QuickActionBarButton::new(
62 "toggle buffer search",
63 Icon::MagnifyingGlass,
64 !self.buffer_search_bar.read(cx).is_dismissed(),
65 Box::new(buffer_search::Deploy { focus: false }),
66 "Buffer Search",
67 {
68 let buffer_search_bar = self.buffer_search_bar.clone();
69 move |_, cx| {
70 buffer_search_bar.update(cx, |search_bar, cx| {
71 search_bar.toggle(&buffer_search::Deploy { focus: true }, cx)
72 });
73 }
74 },
75 ))
76 .filter(|_| editor.is_singleton(cx));
77
78 let assistant_button = QuickActionBarButton::new(
79 "toggle inline assistant",
80 Icon::MagicWand,
81 false,
82 Box::new(InlineAssist),
83 "Inline Assist",
84 {
85 let workspace = self.workspace.clone();
86 move |_, cx| {
87 if let Some(workspace) = workspace.upgrade() {
88 workspace.update(cx, |workspace, cx| {
89 AssistantPanel::inline_assist(workspace, &InlineAssist, cx);
90 });
91 }
92 }
93 },
94 );
95
96 h_stack()
97 .id("quick action bar")
98 .p_1()
99 .gap_2()
100 .children(inlay_hints_button)
101 .children(search_button)
102 .child(assistant_button)
103 }
104}
105
106impl EventEmitter<ToolbarItemEvent> for QuickActionBar {}
107
108#[derive(IntoElement)]
109struct QuickActionBarButton {
110 id: ElementId,
111 icon: Icon,
112 toggled: bool,
113 action: Box<dyn Action>,
114 tooltip: SharedString,
115 on_click: Box<dyn Fn(&ClickEvent, &mut WindowContext)>,
116}
117
118impl QuickActionBarButton {
119 fn new(
120 id: impl Into<ElementId>,
121 icon: Icon,
122 toggled: bool,
123 action: Box<dyn Action>,
124 tooltip: impl Into<SharedString>,
125 on_click: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
126 ) -> Self {
127 Self {
128 id: id.into(),
129 icon,
130 toggled,
131 action,
132 tooltip: tooltip.into(),
133 on_click: Box::new(on_click),
134 }
135 }
136}
137
138impl RenderOnce for QuickActionBarButton {
139 fn render(self, _: &mut WindowContext) -> impl IntoElement {
140 let tooltip = self.tooltip.clone();
141 let action = self.action.boxed_clone();
142
143 IconButton::new(self.id.clone(), self.icon)
144 .size(ButtonSize::Compact)
145 .icon_size(IconSize::Small)
146 .style(ButtonStyle::Subtle)
147 .selected(self.toggled)
148 .tooltip(move |cx| Tooltip::for_action(tooltip.clone(), &*action, cx))
149 .on_click(move |event, cx| (self.on_click)(event, cx))
150 }
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 let mut supports_inlay_hints = editor.read(cx).supports_inlay_hints(cx);
167 self._inlay_hints_enabled_subscription =
168 Some(cx.observe(&editor, move |_, editor, cx| {
169 let editor = editor.read(cx);
170 let new_inlay_hints_enabled = editor.inlay_hints_enabled();
171 let new_supports_inlay_hints = editor.supports_inlay_hints(cx);
172 let should_notify = inlay_hints_enabled != new_inlay_hints_enabled
173 || supports_inlay_hints != new_supports_inlay_hints;
174 inlay_hints_enabled = new_inlay_hints_enabled;
175 supports_inlay_hints = new_supports_inlay_hints;
176 if should_notify {
177 cx.notify()
178 }
179 }));
180 ToolbarItemLocation::PrimaryRight
181 } else {
182 ToolbarItemLocation::Hidden
183 }
184 }
185 None => {
186 self.active_item = None;
187 ToolbarItemLocation::Hidden
188 }
189 }
190 }
191}