1use assistant::assistant_settings::AssistantSettings;
2use assistant::{AssistantPanel, InlineAssist};
3use editor::{Editor, EditorSettings};
4
5use gpui::{
6 Action, ClickEvent, ElementId, EventEmitter, InteractiveElement, ParentElement, Render, Styled,
7 Subscription, View, ViewContext, WeakView,
8};
9use search::{buffer_search, BufferSearchBar};
10use settings::{Settings, SettingsStore};
11use ui::{prelude::*, ButtonSize, ButtonStyle, IconButton, IconName, IconSize, Tooltip};
12use workspace::{
13 item::ItemHandle, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace,
14};
15
16pub struct QuickActionBar {
17 buffer_search_bar: View<BufferSearchBar>,
18 active_item: Option<Box<dyn ItemHandle>>,
19 _inlay_hints_enabled_subscription: Option<Subscription>,
20 workspace: WeakView<Workspace>,
21 show: bool,
22}
23
24impl QuickActionBar {
25 pub fn new(
26 buffer_search_bar: View<BufferSearchBar>,
27 workspace: &Workspace,
28 cx: &mut ViewContext<Self>,
29 ) -> Self {
30 let mut this = Self {
31 buffer_search_bar,
32 active_item: None,
33 _inlay_hints_enabled_subscription: None,
34 workspace: workspace.weak_handle(),
35 show: true,
36 };
37 this.apply_settings(cx);
38 cx.observe_global::<SettingsStore>(|this, cx| this.apply_settings(cx))
39 .detach();
40 this
41 }
42
43 fn active_editor(&self) -> Option<View<Editor>> {
44 self.active_item
45 .as_ref()
46 .and_then(|item| item.downcast::<Editor>())
47 }
48
49 fn apply_settings(&mut self, cx: &mut ViewContext<Self>) {
50 let new_show = EditorSettings::get_global(cx).toolbar.quick_actions;
51 if new_show != self.show {
52 self.show = new_show;
53 cx.emit(ToolbarItemEvent::ChangeLocation(
54 self.get_toolbar_item_location(),
55 ));
56 }
57 }
58
59 fn get_toolbar_item_location(&self) -> ToolbarItemLocation {
60 if self.show && self.active_editor().is_some() {
61 ToolbarItemLocation::PrimaryRight
62 } else {
63 ToolbarItemLocation::Hidden
64 }
65 }
66}
67
68impl Render for QuickActionBar {
69 fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
70 let Some(editor) = self.active_editor() else {
71 return div().id("empty quick action bar");
72 };
73 let inlay_hints_button = Some(QuickActionBarButton::new(
74 "toggle inlay hints",
75 IconName::InlayHint,
76 editor.read(cx).inlay_hints_enabled(),
77 Box::new(editor::actions::ToggleInlayHints),
78 "Toggle Inlay Hints",
79 {
80 let editor = editor.clone();
81 move |_, cx| {
82 editor.update(cx, |editor, cx| {
83 editor.toggle_inlay_hints(&editor::actions::ToggleInlayHints, cx);
84 });
85 }
86 },
87 ))
88 .filter(|_| editor.read(cx).supports_inlay_hints(cx));
89
90 let search_button = Some(QuickActionBarButton::new(
91 "toggle buffer search",
92 IconName::MagnifyingGlass,
93 !self.buffer_search_bar.read(cx).is_dismissed(),
94 Box::new(buffer_search::Deploy { focus: false }),
95 "Buffer Search",
96 {
97 let buffer_search_bar = self.buffer_search_bar.clone();
98 move |_, cx| {
99 buffer_search_bar.update(cx, |search_bar, cx| {
100 search_bar.toggle(&buffer_search::Deploy { focus: true }, cx)
101 });
102 }
103 },
104 ))
105 .filter(|_| editor.is_singleton(cx));
106
107 let assistant_button = QuickActionBarButton::new(
108 "toggle inline assistant",
109 IconName::MagicWand,
110 false,
111 Box::new(InlineAssist),
112 "Inline Assist",
113 {
114 let workspace = self.workspace.clone();
115 move |_, cx| {
116 if let Some(workspace) = workspace.upgrade() {
117 workspace.update(cx, |workspace, cx| {
118 AssistantPanel::inline_assist(workspace, &InlineAssist, cx);
119 });
120 }
121 }
122 },
123 );
124
125 h_flex()
126 .id("quick action bar")
127 .gap_2()
128 .children(inlay_hints_button)
129 .children(search_button)
130 .when(AssistantSettings::get_global(cx).button, |bar| {
131 bar.child(assistant_button)
132 })
133 }
134}
135
136impl EventEmitter<ToolbarItemEvent> for QuickActionBar {}
137
138#[derive(IntoElement)]
139struct QuickActionBarButton {
140 id: ElementId,
141 icon: IconName,
142 toggled: bool,
143 action: Box<dyn Action>,
144 tooltip: SharedString,
145 on_click: Box<dyn Fn(&ClickEvent, &mut WindowContext)>,
146}
147
148impl QuickActionBarButton {
149 fn new(
150 id: impl Into<ElementId>,
151 icon: IconName,
152 toggled: bool,
153 action: Box<dyn Action>,
154 tooltip: impl Into<SharedString>,
155 on_click: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
156 ) -> Self {
157 Self {
158 id: id.into(),
159 icon,
160 toggled,
161 action,
162 tooltip: tooltip.into(),
163 on_click: Box::new(on_click),
164 }
165 }
166}
167
168impl RenderOnce for QuickActionBarButton {
169 fn render(self, _: &mut WindowContext) -> impl IntoElement {
170 let tooltip = self.tooltip.clone();
171 let action = self.action.boxed_clone();
172
173 IconButton::new(self.id.clone(), self.icon)
174 .size(ButtonSize::Compact)
175 .icon_size(IconSize::Small)
176 .style(ButtonStyle::Subtle)
177 .selected(self.toggled)
178 .tooltip(move |cx| Tooltip::for_action(tooltip.clone(), &*action, cx))
179 .on_click(move |event, cx| (self.on_click)(event, cx))
180 }
181}
182
183impl ToolbarItemView for QuickActionBar {
184 fn set_active_pane_item(
185 &mut self,
186 active_pane_item: Option<&dyn ItemHandle>,
187 cx: &mut ViewContext<Self>,
188 ) -> ToolbarItemLocation {
189 self.active_item = active_pane_item.map(ItemHandle::boxed_clone);
190 if let Some(active_item) = active_pane_item {
191 self._inlay_hints_enabled_subscription.take();
192
193 if let Some(editor) = active_item.downcast::<Editor>() {
194 let mut inlay_hints_enabled = editor.read(cx).inlay_hints_enabled();
195 let mut supports_inlay_hints = editor.read(cx).supports_inlay_hints(cx);
196 self._inlay_hints_enabled_subscription =
197 Some(cx.observe(&editor, move |_, editor, cx| {
198 let editor = editor.read(cx);
199 let new_inlay_hints_enabled = editor.inlay_hints_enabled();
200 let new_supports_inlay_hints = editor.supports_inlay_hints(cx);
201 let should_notify = inlay_hints_enabled != new_inlay_hints_enabled
202 || supports_inlay_hints != new_supports_inlay_hints;
203 inlay_hints_enabled = new_inlay_hints_enabled;
204 supports_inlay_hints = new_supports_inlay_hints;
205 if should_notify {
206 cx.notify()
207 }
208 }));
209 }
210 }
211 self.get_toolbar_item_location()
212 }
213}