diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 256ef2284cd6c3ffbb6f7e9746ddfb8e75a4bdfd..3c6103cd908b2fd0d5ebc6bd8cafe3c1a54a5ea9 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -302,10 +302,11 @@ actions!( Hover, Format, ToggleSoftWrap, + ToggleInlays, RevealInFinder, CopyPath, CopyRelativePath, - CopyHighlightJson + CopyHighlightJson, ] ); @@ -446,6 +447,7 @@ pub fn init(cx: &mut AppContext) { cx.add_action(Editor::toggle_code_actions); cx.add_action(Editor::open_excerpts); cx.add_action(Editor::toggle_soft_wrap); + cx.add_action(Editor::toggle_inlays); cx.add_action(Editor::reveal_in_finder); cx.add_action(Editor::copy_path); cx.add_action(Editor::copy_relative_path); @@ -1238,6 +1240,7 @@ enum GotoDefinitionKind { #[derive(Debug, Clone)] enum InlayRefreshReason { + Toggled(bool), SettingsChange(InlayHintSettings), NewLinesShown, BufferEdited(HashSet>), @@ -2669,12 +2672,40 @@ impl Editor { } } + pub fn toggle_inlays(&mut self, _: &ToggleInlays, cx: &mut ViewContext) { + self.inlay_hint_cache.enabled = !self.inlay_hint_cache.enabled; + self.refresh_inlays( + InlayRefreshReason::Toggled(self.inlay_hint_cache.enabled), + cx, + ) + } + + pub fn inlays_enabled(&self) -> bool { + self.inlay_hint_cache.enabled + } + fn refresh_inlays(&mut self, reason: InlayRefreshReason, cx: &mut ViewContext) { if self.project.is_none() || self.mode != EditorMode::Full { return; } let (invalidate_cache, required_languages) = match reason { + InlayRefreshReason::Toggled(enabled) => { + if enabled { + (InvalidationStrategy::RefreshRequested, None) + } else { + self.inlay_hint_cache.clear(); + self.splice_inlay_hints( + self.visible_inlay_hints(cx) + .iter() + .map(|inlay| inlay.id) + .collect(), + Vec::new(), + cx, + ); + return; + } + } InlayRefreshReason::SettingsChange(new_settings) => { match self.inlay_hint_cache.update_settings( &self.buffer, diff --git a/crates/editor/src/inlay_hint_cache.rs b/crates/editor/src/inlay_hint_cache.rs index 8be72aec46d7c2192e10b235a9add2038c01684b..4327ff0d7330c96a3ddc662a2bcd985bf1373325 100644 --- a/crates/editor/src/inlay_hint_cache.rs +++ b/crates/editor/src/inlay_hint_cache.rs @@ -380,7 +380,7 @@ impl InlayHintCache { } } - fn clear(&mut self) { + pub fn clear(&mut self) { self.version += 1; self.update_tasks.clear(); self.hints.clear(); diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 36c9d3becd346314269a694f5e58a34f52bc1981..fc3c78afe233c1516fbfed4ef98df381a62f34f8 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -553,7 +553,7 @@ impl BufferSearchBar { .into_any() } - fn deploy(pane: &mut Pane, action: &Deploy, cx: &mut ViewContext) { + pub fn deploy(pane: &mut Pane, action: &Deploy, cx: &mut ViewContext) { let mut propagate_action = true; if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::() { search_bar.update(cx, |search_bar, cx| { diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 69fa7a09b3efa908b2da9a8a89e9fa533aba9505..30a2e8caec70ac6e922cf11d5e45060024122684 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -399,6 +399,7 @@ pub struct Toolbar { pub height: f32, pub item_spacing: f32, pub nav_button: Interactive, + pub toggleable_tool: Toggleable>, } #[derive(Clone, Deserialize, Default, JsonSchema)] diff --git a/crates/zed/src/quick_action_bar.rs b/crates/zed/src/quick_action_bar.rs new file mode 100644 index 0000000000000000000000000000000000000000..28dba9399f9073a2e8aa798c358bcaf8dd80350b --- /dev/null +++ b/crates/zed/src/quick_action_bar.rs @@ -0,0 +1,143 @@ +use editor::Editor; +use gpui::{ + elements::{Empty, Flex, MouseEventHandler, ParentElement, Svg}, + platform::{CursorStyle, MouseButton}, + Action, AnyElement, Element, Entity, EventContext, View, ViewContext, ViewHandle, +}; + +use search::{buffer_search, BufferSearchBar}; +use workspace::{item::ItemHandle, ToolbarItemLocation, ToolbarItemView, Workspace}; + +pub struct QuickActionBar { + workspace: ViewHandle, + active_item: Option>, +} + +impl QuickActionBar { + pub fn new(workspace: ViewHandle) -> Self { + Self { + workspace, + active_item: None, + } + } + + fn active_editor(&self) -> Option> { + self.active_item + .as_ref() + .and_then(|item| item.downcast::()) + } +} + +impl Entity for QuickActionBar { + type Event = (); +} + +impl View for QuickActionBar { + fn ui_name() -> &'static str { + "QuickActionsBar" + } + + fn render(&mut self, cx: &mut gpui::ViewContext<'_, '_, Self>) -> gpui::AnyElement { + let Some(editor) = self.active_editor() else { return Empty::new().into_any(); }; + + let inlays_enabled = editor.read(cx).inlays_enabled(); + let mut bar = Flex::row().with_child(render_quick_action_bar_button( + 0, + "icons/hamburger_15.svg", + inlays_enabled, + ( + "Toggle inlays".to_string(), + Some(Box::new(editor::ToggleInlays)), + ), + cx, + |this, cx| { + if let Some(editor) = this.active_editor() { + editor.update(cx, |editor, cx| { + editor.toggle_inlays(&editor::ToggleInlays, cx); + }); + } + }, + )); + + if editor.read(cx).buffer().read(cx).is_singleton() { + let search_action = buffer_search::Deploy { focus: true }; + + // TODO kb: this opens the search bar in a differently focused pane (should be the same) + should be toggleable + let pane = self.workspace.read(cx).active_pane().clone(); + bar = bar.with_child(render_quick_action_bar_button( + 1, + "icons/magnifying_glass_12.svg", + false, + ( + "Search in buffer".to_string(), + Some(Box::new(search_action.clone())), + ), + cx, + move |_, cx| { + pane.update(cx, |pane, cx| { + BufferSearchBar::deploy(pane, &search_action, cx); + }); + }, + )); + } + + bar.into_any() + } +} + +fn render_quick_action_bar_button< + F: 'static + Fn(&mut QuickActionBar, &mut EventContext), +>( + index: usize, + icon: &'static str, + toggled: bool, + tooltip: (String, Option>), + cx: &mut ViewContext, + on_click: F, +) -> AnyElement { + enum QuickActionBarButton {} + + let theme = theme::current(cx); + let (tooltip_text, action) = tooltip; + + MouseEventHandler::::new(index, cx, |mouse_state, _| { + let style = theme + .workspace + .toolbar + .toggleable_tool + .in_state(toggled) + .style_for(mouse_state); + Svg::new(icon) + .with_color(style.color) + .constrained() + .with_width(style.icon_width) + .aligned() + .constrained() + .with_width(style.button_width) + .with_height(style.button_width) + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, move |_, pane, cx| on_click(pane, cx)) + .with_tooltip::(index, tooltip_text, action, theme.tooltip.clone(), cx) + .into_any_named("quick action bar button") +} + +impl ToolbarItemView for QuickActionBar { + fn set_active_pane_item( + &mut self, + active_pane_item: Option<&dyn ItemHandle>, + _: &mut ViewContext, + ) -> ToolbarItemLocation { + match active_pane_item { + Some(active_item) => { + dbg!("@@@@@@@@@@ TODO kb", active_item.id()); + self.active_item = Some(active_item.boxed_clone()); + ToolbarItemLocation::PrimaryRight { flex: None } + } + None => { + self.active_item = None; + ToolbarItemLocation::Hidden + } + } + } +} diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 1c57174fe289bffe4f0b7e4d245bb017d38f68fc..5ff453484cda20fcb1f4be2a35b69feb0f9e0fd6 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -5,6 +5,8 @@ pub mod only_instance; #[cfg(any(test, feature = "test-support"))] pub mod test; +mod quick_action_bar; + use ai::AssistantPanel; use anyhow::Context; use assets::Assets; @@ -30,6 +32,7 @@ use gpui::{ pub use lsp; pub use project; use project_panel::ProjectPanel; +use quick_action_bar::QuickActionBar; use search::{BufferSearchBar, ProjectSearchBar}; use serde::Deserialize; use serde_json::to_string_pretty; @@ -255,12 +258,16 @@ pub fn initialize_workspace( workspace_handle.update(&mut cx, |workspace, cx| { let workspace_handle = cx.handle(); cx.subscribe(&workspace_handle, { + let workspace_handle = workspace_handle.clone(); move |workspace, _, event, cx| { if let workspace::Event::PaneAdded(pane) = event { pane.update(cx, |pane, cx| { pane.toolbar().update(cx, |toolbar, cx| { let breadcrumbs = cx.add_view(|_| Breadcrumbs::new(workspace)); toolbar.add_item(breadcrumbs, cx); + let quick_action_bar = + cx.add_view(|_| QuickActionBar::new(workspace_handle.clone())); + toolbar.add_item(quick_action_bar, cx); let buffer_search_bar = cx.add_view(BufferSearchBar::new); toolbar.add_item(buffer_search_bar, cx); let project_search_bar = cx.add_view(|_| ProjectSearchBar::new()); diff --git a/styles/src/style_tree/workspace.ts b/styles/src/style_tree/workspace.ts index 5aee3c987d34b75d25db5bc9ad69f4d2a0938f7b..4d44166eb8075ab4aae39fdc033b19b76f8fa7dd 100644 --- a/styles/src/style_tree/workspace.ts +++ b/styles/src/style_tree/workspace.ts @@ -12,6 +12,7 @@ import tabBar from "./tab_bar" import { interactive } from "../element" import { titlebar } from "./titlebar" import { useTheme } from "../theme" +import { toggleable_icon_button } from "../component/icon_button" export default function workspace(): any { const theme = useTheme() @@ -149,6 +150,9 @@ export default function workspace(): any { }, }, }), + toggleable_tool: toggleable_icon_button(theme, { + active_color: "accent", + }), padding: { left: 8, right: 8, top: 4, bottom: 4 }, }, breadcrumb_height: 24,