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