diff --git a/crates/search2/src/buffer_search.rs b/crates/search2/src/buffer_search.rs index cbbeeb0f12..ccbec98c5d 100644 --- a/crates/search2/src/buffer_search.rs +++ b/crates/search2/src/buffer_search.rs @@ -11,14 +11,14 @@ use editor::{Editor, EditorMode}; use futures::channel::oneshot; use gpui::{ actions, div, red, Action, AppContext, Div, EventEmitter, FocusableView, - InteractiveElement as _, IntoElement, ParentElement as _, Render, Styled, Subscription, Task, - View, ViewContext, VisualContext as _, WeakView, WindowContext, + InteractiveElement as _, IntoElement, KeyContext, ParentElement as _, Render, Styled, + Subscription, Task, View, ViewContext, VisualContext as _, WeakView, WindowContext, }; use project::search::SearchQuery; use serde::Deserialize; use std::{any::Any, sync::Arc}; -use ui::{h_stack, Clickable, Icon, IconButton, IconElement}; +use ui::{h_stack, ButtonCommon, Clickable, Icon, IconButton, IconElement, Tooltip}; use util::ResultExt; use workspace::{ item::ItemHandle, @@ -131,13 +131,7 @@ impl Render for BufferSearchBar { let search_button_for_mode = |mode| { let is_active = self.current_mode == mode; - render_search_mode_button( - mode, - is_active, - cx.listener(move |this, _, cx| { - this.activate_search_mode(mode, cx); - }), - ) + render_search_mode_button(mode, is_active) }; let search_option_button = |option| { let is_active = self.search_options.contains(option); @@ -163,23 +157,35 @@ impl Render for BufferSearchBar { }); let should_show_replace_input = self.replace_enabled && supported_options.replacement; let replace_all = should_show_replace_input - .then(|| super::render_replace_button(ReplaceAll, ui::Icon::ReplaceAll)); - let replace_next = should_show_replace_input - .then(|| super::render_replace_button(ReplaceNext, ui::Icon::Replace)); + .then(|| super::render_replace_button(ReplaceAll, ui::Icon::ReplaceAll, "Replace all")); + let replace_next = should_show_replace_input.then(|| { + super::render_replace_button(ReplaceNext, ui::Icon::ReplaceNext, "Replace next") + }); let in_replace = self.replacement_editor.focus_handle(cx).is_focused(cx); + let mut key_context = KeyContext::default(); + key_context.add("BufferSearchBar"); + if in_replace { + key_context.add("in_replace"); + } + h_stack() - .key_context("BufferSearchBar") + .key_context(key_context) .on_action(cx.listener(Self::previous_history_query)) .on_action(cx.listener(Self::next_history_query)) .on_action(cx.listener(Self::dismiss)) .on_action(cx.listener(Self::select_next_match)) .on_action(cx.listener(Self::select_prev_match)) + .on_action(cx.listener(|this, _: &ActivateRegexMode, cx| { + this.activate_search_mode(SearchMode::Regex, cx); + })) + .on_action(cx.listener(|this, _: &ActivateTextMode, cx| { + this.activate_search_mode(SearchMode::Text, cx); + })) .when(self.supported_options().replacement, |this| { this.on_action(cx.listener(Self::toggle_replace)) .when(in_replace, |this| { - this.key_context("in_replace") - .on_action(cx.listener(Self::replace_next)) + this.on_action(cx.listener(Self::replace_next)) .on_action(cx.listener(Self::replace_all)) }) }) @@ -238,21 +244,19 @@ impl Render for BufferSearchBar { h_stack() .gap_0p5() .flex_none() - .child(self.render_action_button(cx)) + .child(self.render_action_button()) .children(match_count) .child(render_nav_button( ui::Icon::ChevronLeft, self.active_match_index.is_some(), - cx.listener(move |this, _, cx| { - this.select_prev_match(&Default::default(), cx); - }), + "Select previous match", + &SelectPrevMatch, )) .child(render_nav_button( ui::Icon::ChevronRight, self.active_match_index.is_some(), - cx.listener(move |this, _, cx| { - this.select_next_match(&Default::default(), cx); - }), + "Select next match", + &SelectNextMatch, )), ) } @@ -597,14 +601,10 @@ impl BufferSearchBar { self.update_matches(cx) } - fn render_action_button(&self, cx: &mut ViewContext) -> impl IntoElement { - // let tooltip_style = theme.tooltip.clone(); - - // let style = theme.search.action_button.clone(); - - IconButton::new("select-all", ui::Icon::SelectAll).on_click(cx.listener(|this, _, cx| { - this.select_all_matches(&SelectAllMatches, cx); - })) + fn render_action_button(&self) -> impl IntoElement { + IconButton::new("select-all", ui::Icon::SelectAll) + .on_click(|_, cx| cx.dispatch_action(SelectAllMatches.boxed_clone())) + .tooltip(|cx| Tooltip::for_action("Select all matches", &SelectAllMatches, cx)) } pub fn activate_search_mode(&mut self, mode: SearchMode, cx: &mut ViewContext) { diff --git a/crates/search2/src/mode.rs b/crates/search2/src/mode.rs index 817fb454d2..3fd53cee49 100644 --- a/crates/search2/src/mode.rs +++ b/crates/search2/src/mode.rs @@ -1,3 +1,7 @@ +use gpui::{Action, SharedString}; + +use crate::{ActivateRegexMode, ActivateSemanticMode, ActivateTextMode}; + // TODO: Update the default search mode to get from config #[derive(Copy, Clone, Debug, Default, PartialEq)] pub enum SearchMode { @@ -15,6 +19,16 @@ impl SearchMode { SearchMode::Regex => "Regex", } } + pub(crate) fn tooltip(&self) -> SharedString { + format!("Activate {} Mode", self.label()).into() + } + pub(crate) fn action(&self) -> Box { + match self { + SearchMode::Text => ActivateTextMode.boxed_clone(), + SearchMode::Semantic => ActivateSemanticMode.boxed_clone(), + SearchMode::Regex => ActivateRegexMode.boxed_clone(), + } + } } pub(crate) fn next_mode(mode: &SearchMode, semantic_enabled: bool) -> SearchMode { diff --git a/crates/search2/src/search.rs b/crates/search2/src/search.rs index 13def6b4a7..c98a033c72 100644 --- a/crates/search2/src/search.rs +++ b/crates/search2/src/search.rs @@ -3,7 +3,7 @@ pub use buffer_search::BufferSearchBar; use gpui::{actions, Action, AppContext, IntoElement}; pub use mode::SearchMode; use project::search::SearchQuery; -use ui::prelude::*; +use ui::{prelude::*, Tooltip}; use ui::{ButtonStyle, Icon, IconButton}; //pub use project_search::{ProjectSearchBar, ProjectSearchView}; // use theme::components::{ @@ -84,7 +84,7 @@ impl SearchOptions { } pub fn as_button(&self, active: bool) -> impl IntoElement { - IconButton::new(0, self.icon()) + IconButton::new(self.label(), self.icon()) .on_click({ let action = self.to_toggle_action(); move |_, cx| { @@ -93,26 +93,38 @@ impl SearchOptions { }) .style(ButtonStyle::Subtle) .when(active, |button| button.style(ButtonStyle::Filled)) + .tooltip({ + let action = self.to_toggle_action(); + let label: SharedString = format!("Toggle {}", self.label()).into(); + move |cx| Tooltip::for_action(label.clone(), &*action, cx) + }) } } fn toggle_replace_button(active: bool) -> impl IntoElement { // todo: add toggle_replace button - IconButton::new(0, Icon::Replace) + IconButton::new("buffer-search-bar-toggle-replace-button", Icon::Replace) .on_click(|_, cx| { cx.dispatch_action(Box::new(ToggleReplace)); cx.notify(); }) .style(ButtonStyle::Subtle) .when(active, |button| button.style(ButtonStyle::Filled)) + .tooltip(|cx| Tooltip::for_action("Toggle replace", &ToggleReplace, cx)) } fn render_replace_button( action: impl Action + 'static + Send + Sync, icon: Icon, + tooltip: &'static str, ) -> impl IntoElement { - // todo: add tooltip - IconButton::new(0, icon).on_click(move |_, cx| { - cx.dispatch_action(action.boxed_clone()); - }) + let id: SharedString = format!("search-replace-{}", action.name()).into(); + IconButton::new(id, icon) + .tooltip({ + let action = action.boxed_clone(); + move |cx| Tooltip::for_action(tooltip, &*action, cx) + }) + .on_click(move |_, cx| { + cx.dispatch_action(action.boxed_clone()); + }) } diff --git a/crates/search2/src/search_bar.rs b/crates/search2/src/search_bar.rs index 44ba287d78..dcc46ac228 100644 --- a/crates/search2/src/search_bar.rs +++ b/crates/search2/src/search_bar.rs @@ -1,30 +1,36 @@ -use gpui::{ClickEvent, IntoElement, WindowContext}; -use ui::prelude::*; +use gpui::{Action, IntoElement}; +use ui::{prelude::*, Tooltip}; use ui::{Button, IconButton}; use crate::mode::SearchMode; pub(super) fn render_nav_button( icon: ui::Icon, - _active: bool, - on_click: impl Fn(&ClickEvent, &mut WindowContext) + 'static, + active: bool, + tooltip: &'static str, + action: &'static dyn Action, ) -> impl IntoElement { - // let tooltip_style = cx.theme().tooltip.clone(); - // let cursor_style = if active { - // CursorStyle::PointingHand - // } else { - // CursorStyle::default() - // }; - // enum NavButton {} - IconButton::new("search-nav-button", icon).on_click(on_click) + IconButton::new( + SharedString::from(format!("search-nav-button-{}", action.name())), + icon, + ) + .on_click(|_, cx| cx.dispatch_action(action.boxed_clone())) + .tooltip(move |cx| Tooltip::for_action(tooltip, action, cx)) + .disabled(!active) } -pub(crate) fn render_search_mode_button( - mode: SearchMode, - is_active: bool, - on_click: impl Fn(&ClickEvent, &mut WindowContext) + 'static, -) -> Button { +pub(crate) fn render_search_mode_button(mode: SearchMode, is_active: bool) -> Button { Button::new(mode.label(), mode.label()) .selected(is_active) - .on_click(on_click) + .on_click({ + let action = mode.action(); + move |_, cx| { + cx.dispatch_action(action.boxed_clone()); + } + }) + .tooltip({ + let action = mode.action(); + let tooltip_text = mode.tooltip(); + move |cx| Tooltip::for_action(tooltip_text.clone(), &*action, cx) + }) } diff --git a/crates/ui2/src/components/icon.rs b/crates/ui2/src/components/icon.rs index a5b09782f5..f534e04b68 100644 --- a/crates/ui2/src/components/icon.rs +++ b/crates/ui2/src/components/icon.rs @@ -80,6 +80,7 @@ pub enum Icon { Quote, Replace, ReplaceAll, + ReplaceNext, Screen, SelectAll, Split, @@ -157,6 +158,7 @@ impl Icon { Icon::Quote => "icons/quote.svg", Icon::Replace => "icons/replace.svg", Icon::ReplaceAll => "icons/replace_all.svg", + Icon::ReplaceNext => "icons/replace_next.svg", Icon::Screen => "icons/desktop.svg", Icon::SelectAll => "icons/select-all.svg", Icon::Split => "icons/split.svg",