Add consistency between buffer and project search design (#20754)
Follow up to https://github.com/zed-industries/zed/pull/20242 This PR adds the `SearchInputWidth` util, which sets a threshold container size in which an input's width stops filling the available space. In practice, this is in place to make the buffer and project search input fill the whole container width up to a certain point (where this point is really an arbitrary number that can be fine-tuned per taste). For folks using huge monitors, the UX isn't excellent if you have a gigantic input. In the future, upon further review, maybe it makes more sense to reorganize this code better, baking it in as a default behavior of the input component. Or even exposing this is a function many other components could use, given we may want to have dynamic width in different scenarios. For now, I just wanted to make the design of these search UIs better and more consistent. | Buffer Search | Project Search | |--------|--------| | <img width="1042" alt="Screenshot 2024-11-15 at 20 39 21" src="https://github.com/user-attachments/assets/f9cbf0b3-8c58-46d1-8380-e89cd9c89699"> | <img width="1042" alt="Screenshot 2024-11-15 at 20 39 24" src="https://github.com/user-attachments/assets/ed244a51-ea55-4fe3-a719-a3d9cd119aa9"> | Release Notes: - N/A
This commit is contained in:
@@ -27,7 +27,10 @@ use settings::Settings;
|
||||
use std::sync::Arc;
|
||||
use theme::ThemeSettings;
|
||||
|
||||
use ui::{h_flex, prelude::*, IconButton, IconButtonShape, IconName, Tooltip, BASE_REM_SIZE_IN_PX};
|
||||
use ui::{
|
||||
h_flex, prelude::*, utils::SearchInputWidth, IconButton, IconButtonShape, IconName, Tooltip,
|
||||
BASE_REM_SIZE_IN_PX,
|
||||
};
|
||||
use util::ResultExt;
|
||||
use workspace::{
|
||||
item::ItemHandle,
|
||||
@@ -38,8 +41,6 @@ use workspace::{
|
||||
pub use registrar::DivRegistrar;
|
||||
use registrar::{ForDeployed, ForDismissed, SearchActionsRegistrar, WithResults};
|
||||
|
||||
const MIN_INPUT_WIDTH_REMS: f32 = 10.;
|
||||
const MAX_INPUT_WIDTH_REMS: f32 = 30.;
|
||||
const MAX_BUFFER_SEARCH_HISTORY_SIZE: usize = 50;
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize)]
|
||||
@@ -160,12 +161,12 @@ impl Render for BufferSearchBar {
|
||||
query_editor.placeholder_text(cx).is_none()
|
||||
}) {
|
||||
self.query_editor.update(cx, |editor, cx| {
|
||||
editor.set_placeholder_text("Search", cx);
|
||||
editor.set_placeholder_text("Search…", cx);
|
||||
});
|
||||
}
|
||||
|
||||
self.replacement_editor.update(cx, |editor, cx| {
|
||||
editor.set_placeholder_text("Replace with...", cx);
|
||||
editor.set_placeholder_text("Replace with…", cx);
|
||||
});
|
||||
|
||||
let mut text_color = Color::Default;
|
||||
@@ -203,21 +204,26 @@ impl Render for BufferSearchBar {
|
||||
cx.theme().colors().border
|
||||
};
|
||||
|
||||
let container_width = cx.viewport_size().width;
|
||||
let input_width = SearchInputWidth::calc_width(container_width);
|
||||
|
||||
let input_base_styles = || {
|
||||
h_flex()
|
||||
.w(input_width)
|
||||
.h_8()
|
||||
.px_2()
|
||||
.py_1()
|
||||
.border_1()
|
||||
.border_color(editor_border)
|
||||
.rounded_lg()
|
||||
};
|
||||
|
||||
let search_line = h_flex()
|
||||
.gap_2()
|
||||
.child(
|
||||
h_flex()
|
||||
input_base_styles()
|
||||
.id("editor-scroll")
|
||||
.track_scroll(&self.editor_scroll_handle)
|
||||
.flex_1()
|
||||
.h_8()
|
||||
.px_2()
|
||||
.py_1()
|
||||
.border_1()
|
||||
.border_color(editor_border)
|
||||
.min_w(rems(MIN_INPUT_WIDTH_REMS))
|
||||
.max_w(rems(MAX_INPUT_WIDTH_REMS))
|
||||
.rounded_lg()
|
||||
.child(self.render_text_input(&self.query_editor, text_color.color(cx), cx))
|
||||
.when(!hide_inline_icons, |div| {
|
||||
div.children(supported_options.case.then(|| {
|
||||
@@ -249,8 +255,8 @@ impl Render for BufferSearchBar {
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.flex_none()
|
||||
.gap_0p5()
|
||||
.gap_1()
|
||||
.min_w_64()
|
||||
.when(supported_options.replacement, |this| {
|
||||
this.child(
|
||||
IconButton::new(
|
||||
@@ -323,20 +329,27 @@ impl Render for BufferSearchBar {
|
||||
}
|
||||
}),
|
||||
)
|
||||
.child(render_nav_button(
|
||||
ui::IconName::ChevronLeft,
|
||||
self.active_match_index.is_some(),
|
||||
"Select Previous Match",
|
||||
&SelectPrevMatch,
|
||||
focus_handle.clone(),
|
||||
))
|
||||
.child(render_nav_button(
|
||||
ui::IconName::ChevronRight,
|
||||
self.active_match_index.is_some(),
|
||||
"Select Next Match",
|
||||
&SelectNextMatch,
|
||||
focus_handle.clone(),
|
||||
))
|
||||
.child(
|
||||
h_flex()
|
||||
.pl_2()
|
||||
.ml_2()
|
||||
.border_l_1()
|
||||
.border_color(cx.theme().colors().border_variant)
|
||||
.child(render_nav_button(
|
||||
ui::IconName::ChevronLeft,
|
||||
self.active_match_index.is_some(),
|
||||
"Select Previous Match",
|
||||
&SelectPrevMatch,
|
||||
focus_handle.clone(),
|
||||
))
|
||||
.child(render_nav_button(
|
||||
ui::IconName::ChevronRight,
|
||||
self.active_match_index.is_some(),
|
||||
"Select Next Match",
|
||||
&SelectNextMatch,
|
||||
focus_handle.clone(),
|
||||
)),
|
||||
)
|
||||
.when(!narrow_mode, |this| {
|
||||
this.child(h_flex().ml_2().min_w(rems_from_px(40.)).child(
|
||||
Label::new(match_text).size(LabelSize::Small).color(
|
||||
@@ -353,30 +366,15 @@ impl Render for BufferSearchBar {
|
||||
let replace_line = should_show_replace_input.then(|| {
|
||||
h_flex()
|
||||
.gap_2()
|
||||
.flex_1()
|
||||
.child(input_base_styles().child(self.render_text_input(
|
||||
&self.replacement_editor,
|
||||
cx.theme().colors().text,
|
||||
cx,
|
||||
)))
|
||||
.child(
|
||||
h_flex()
|
||||
.flex_1()
|
||||
// We're giving this a fixed height to match the height of the search input,
|
||||
// which has an icon inside that is increasing its height.
|
||||
.h_8()
|
||||
.px_2()
|
||||
.py_1()
|
||||
.border_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.rounded_lg()
|
||||
.min_w(rems(MIN_INPUT_WIDTH_REMS))
|
||||
.max_w(rems(MAX_INPUT_WIDTH_REMS))
|
||||
.child(self.render_text_input(
|
||||
&self.replacement_editor,
|
||||
cx.theme().colors().text,
|
||||
cx,
|
||||
)),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.flex_none()
|
||||
.gap_0p5()
|
||||
.min_w_64()
|
||||
.gap_1()
|
||||
.child(
|
||||
IconButton::new("search-replace-next", ui::IconName::ReplaceNext)
|
||||
.shape(IconButtonShape::Square)
|
||||
@@ -418,6 +416,7 @@ impl Render for BufferSearchBar {
|
||||
|
||||
v_flex()
|
||||
.id("buffer_search")
|
||||
.gap_2()
|
||||
.track_scroll(&self.scroll_handle)
|
||||
.key_context(key_context)
|
||||
.capture_action(cx.listener(Self::tab))
|
||||
@@ -446,20 +445,22 @@ impl Render for BufferSearchBar {
|
||||
.when(self.supported_options().selection, |this| {
|
||||
this.on_action(cx.listener(Self::toggle_selection))
|
||||
})
|
||||
.gap_2()
|
||||
.child(
|
||||
h_flex()
|
||||
.relative()
|
||||
.child(search_line.w_full())
|
||||
.when(!narrow_mode, |div| {
|
||||
div.child(
|
||||
IconButton::new(SharedString::from("Close"), IconName::Close)
|
||||
.shape(IconButtonShape::Square)
|
||||
.tooltip(move |cx| {
|
||||
Tooltip::for_action("Close Search Bar", &Dismiss, cx)
|
||||
})
|
||||
.on_click(cx.listener(|this, _: &ClickEvent, cx| {
|
||||
this.dismiss(&Dismiss, cx)
|
||||
})),
|
||||
h_flex().absolute().right_0().child(
|
||||
IconButton::new(SharedString::from("Close"), IconName::Close)
|
||||
.shape(IconButtonShape::Square)
|
||||
.tooltip(move |cx| {
|
||||
Tooltip::for_action("Close Search Bar", &Dismiss, cx)
|
||||
})
|
||||
.on_click(cx.listener(|this, _: &ClickEvent, cx| {
|
||||
this.dismiss(&Dismiss, cx)
|
||||
})),
|
||||
),
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user