Skip to content

Commit

Permalink
Add consistency between buffer and project search design (#20754)
Browse files Browse the repository at this point in the history
Follow up to #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
  • Loading branch information
danilo-leal authored Nov 28, 2024
1 parent f309445 commit 301a890
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 78 deletions.
125 changes: 63 additions & 62 deletions crates/search/src/buffer_search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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)]
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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(|| {
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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(
Expand All @@ -353,30 +366,15 @@ impl Render for BufferSearchBar {
let replace_line = should_show_replace_input.then(|| {
h_flex()
.gap_2()
.flex_1()
.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(input_base_styles().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)
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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)
})),
),
)
}),
)
Expand Down
38 changes: 22 additions & 16 deletions crates/search/src/project_search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ use std::{
};
use theme::ThemeSettings;
use ui::{
h_flex, prelude::*, v_flex, Icon, IconButton, IconButtonShape, IconName, KeyBinding, Label,
LabelCommon, LabelSize, Selectable, Tooltip,
h_flex, prelude::*, utils::SearchInputWidth, v_flex, Icon, IconButton, IconButtonShape,
IconName, KeyBinding, Label, LabelCommon, LabelSize, Selectable, Tooltip,
};
use util::paths::PathMatcher;
use workspace::{
Expand Down Expand Up @@ -669,7 +669,7 @@ impl ProjectSearchView {

let query_editor = cx.new_view(|cx| {
let mut editor = Editor::single_line(cx);
editor.set_placeholder_text("Search all files...", cx);
editor.set_placeholder_text("Search all files", cx);
editor.set_text(query_text, cx);
editor
});
Expand All @@ -692,7 +692,7 @@ impl ProjectSearchView {
);
let replacement_editor = cx.new_view(|cx| {
let mut editor = Editor::single_line(cx);
editor.set_placeholder_text("Replace in project...", cx);
editor.set_placeholder_text("Replace in project", cx);
if let Some(text) = replacement_text {
editor.set_text(text, cx);
}
Expand Down Expand Up @@ -1586,9 +1586,12 @@ impl Render for ProjectSearchBar {
let search = search.read(cx);
let focus_handle = search.focus_handle(cx);

let container_width = cx.viewport_size().width;
let input_width = SearchInputWidth::calc_width(container_width);

let input_base_styles = || {
h_flex()
.w_full()
.w(input_width)
.h_8()
.px_2()
.py_1()
Expand Down Expand Up @@ -1701,6 +1704,10 @@ impl Render for ProjectSearchBar {
.unwrap_or_else(|| "0/0".to_string());

let matches_column = h_flex()
.pl_2()
.ml_2()
.border_l_1()
.border_color(cx.theme().colors().border_variant)
.child(
IconButton::new("project-search-prev-match", IconName::ChevronLeft)
.shape(IconButtonShape::Square)
Expand Down Expand Up @@ -1751,13 +1758,13 @@ impl Render for ProjectSearchBar {
div()
.id("matches")
.ml_1()
.child(
Label::new(match_text).color(if search.active_match_index.is_some() {
.child(Label::new(match_text).size(LabelSize::Small).color(
if search.active_match_index.is_some() {
Color::Default
} else {
Color::Disabled
}),
)
},
))
.when(limit_reached, |el| {
el.tooltip(|cx| {
Tooltip::text("Search limits reached.\nTry narrowing your search.", cx)
Expand All @@ -1767,9 +1774,9 @@ impl Render for ProjectSearchBar {

let search_line = h_flex()
.w_full()
.gap_1p5()
.gap_2()
.child(query_column)
.child(h_flex().min_w_40().child(mode_column).child(matches_column));
.child(h_flex().min_w_64().child(mode_column).child(matches_column));

let replace_line = search.replace_enabled.then(|| {
let replace_column =
Expand All @@ -1779,7 +1786,7 @@ impl Render for ProjectSearchBar {

let replace_actions =
h_flex()
.min_w_40()
.min_w_64()
.gap_1()
.when(search.replace_enabled, |this| {
this.child(
Expand Down Expand Up @@ -1830,15 +1837,15 @@ impl Render for ProjectSearchBar {

h_flex()
.w_full()
.gap_1p5()
.gap_2()
.child(replace_column)
.child(replace_actions)
});

let filter_line = search.filters_enabled.then(|| {
h_flex()
.w_full()
.gap_1p5()
.gap_2()
.child(
input_base_styles()
.on_action(
Expand All @@ -1861,12 +1868,11 @@ impl Render for ProjectSearchBar {
)
.child(
h_flex()
.min_w_40()
.min_w_64()
.gap_1()
.child(
IconButton::new("project-search-opened-only", IconName::FileSearch)
.shape(IconButtonShape::Square)
.icon_size(IconSize::XSmall)
.selected(self.is_opened_only_enabled(cx))
.tooltip(|cx| Tooltip::text("Only Search Open Files", cx))
.on_click(cx.listener(|this, _, cx| {
Expand Down
2 changes: 2 additions & 0 deletions crates/ui/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

mod color_contrast;
mod format_distance;
mod search_input;
mod with_rem_size;

pub use color_contrast::*;
pub use format_distance::*;
pub use search_input::*;
pub use with_rem_size::*;
22 changes: 22 additions & 0 deletions crates/ui/src/utils/search_input.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#![allow(missing_docs)]

use gpui::Pixels;

pub struct SearchInputWidth;

impl SearchInputWidth {
/// The containzer size in which the input stops filling the whole width.
pub const THRESHOLD_WIDTH: f32 = 1200.0;

/// The maximum width for the search input when the container is larger than the threshold.
pub const MAX_WIDTH: f32 = 1200.0;

/// Calculates the actual width in pixels based on the container width.
pub fn calc_width(container_width: Pixels) -> Pixels {
if container_width.0 < Self::THRESHOLD_WIDTH {
container_width
} else {
Pixels(container_width.0.min(Self::MAX_WIDTH))
}
}
}

0 comments on commit 301a890

Please sign in to comment.