History

Project search bar layout improvements (#7963)

The PR matches project search layout with the recent changes in the buffer project layout. https://github.com/zed-industries/zed/assets/2101250/91b905ea-aed8-4740-9e60-67f3052885e2 Release Notes: - Improve project search bar layout, match it with the buffer search bar ([7722](https://github.com/zed-industries/zed/issues/7722))

Andrew Lygin committed 2 years ago

Commit f7d2cb11 parent 78dcd722 files · +173 −176Browse files

Unified · Split

2 files
  1. crates/search/src/buffer_search.rs +1 −2
  2. crates/search/src/project_search.rs +172 −174
crates/search/src/buffer_search.rs+1−2
@@ -38,7 +38,7 @@ pub use registrar::DivRegistrar; · 37 unmodified lines
38use registrar::{ForDeployed, ForDismissed, SearchActionsRegistrar, WithResults};38use registrar::{ForDeployed, ForDismissed, SearchActionsRegistrar, WithResults};
39 39
40const MIN_INPUT_WIDTH_REMS: f32 = 15.;40const MIN_INPUT_WIDTH_REMS: f32 = 15.;
41const MAX_INPUT_WIDTH_REMS: f32 = 25.;41const MAX_INPUT_WIDTH_REMS: f32 = 30.;
42 42
43#[derive(PartialEq, Clone, Deserialize)]43#[derive(PartialEq, Clone, Deserialize)]
44pub struct Deploy {44pub struct Deploy {
@@ -217,7 +217,6 @@ impl Render for BufferSearchBar { · 172 unmodified lines
217 .flex_1()217 .flex_1()
218 .px_2()218 .px_2()
219 .py_1()219 .py_1()
220 .gap_2()
221 .border_1()220 .border_1()
222 .border_color(editor_border)221 .border_color(editor_border)
223 .min_w(rems(MIN_INPUT_WIDTH_REMS))222 .min_w(rems(MIN_INPUT_WIDTH_REMS))
crates/search/src/project_search.rs+172−174
@@ -50,6 +50,9 @@ use workspace::{ · 49 unmodified lines
50 WorkspaceId,50 WorkspaceId,
51};51};
52 52
53const MIN_INPUT_WIDTH_REMS: f32 = 15.;
54const MAX_INPUT_WIDTH_REMS: f32 = 30.;
55
53actions!(56actions!(
54 project_search,57 project_search,
55 [SearchInNew, ToggleFocus, NextField, ToggleFilters]58 [SearchInNew, ToggleFocus, NextField, ToggleFilters]
@@ -1409,25 +1412,22 @@ impl ProjectSearchBar { · 1353 unmodified lines
1409 1412
1410 active_project_search.update(cx, |project_view, cx| {1413 active_project_search.update(cx, |project_view, cx| {
1411 let mut views = vec![&project_view.query_editor];1414 let mut views = vec![&project_view.query_editor];
1415 if project_view.replace_enabled {
1416 views.push(&project_view.replacement_editor);
1417 }
1412 if project_view.filters_enabled {1418 if project_view.filters_enabled {
1413 views.extend([1419 views.extend([
1414 &project_view.included_files_editor,1420 &project_view.included_files_editor,
1415 &project_view.excluded_files_editor,1421 &project_view.excluded_files_editor,
1416 ]);1422 ]);
1417 }1423 }
1418 if project_view.replace_enabled {
1419 views.push(&project_view.replacement_editor);
1420 }
1421 let current_index = match views1424 let current_index = match views
1422 .iter()1425 .iter()
1423 .enumerate()1426 .enumerate()
1424 .find(|(_, view)| view.focus_handle(cx).is_focused(cx))1427 .find(|(_, view)| view.focus_handle(cx).is_focused(cx))
1425 {1428 {
1426 Some((index, _)) => index,1429 Some((index, _)) => index,
1427 1430 None => return,
1428 None => {
1429 return;
1430 }
1431 };1431 };
1432 1432
1433 let new_index = match direction {1433 let new_index = match direction {
@@ -1665,78 +1665,64 @@ impl Render for ProjectSearchBar { · 231 unmodified lines
1665 let search = search.read(cx);1665 let search = search.read(cx);
1666 let semantic_is_available = SemanticIndex::enabled(cx);1666 let semantic_is_available = SemanticIndex::enabled(cx);
1667 1667
1668 let query_column = v_flex().child(1668 let query_column = h_flex()
1669 h_flex()1669 .flex_1()
1670 .min_w(rems(512. / 16.))1670 .px_2()
1671 .px_2()1671 .py_1()
1672 .py_1()1672 .border_1()
1673 .gap_2()1673 .border_color(search.border_color_for(InputPanel::Query, cx))
1674 .bg(cx.theme().colors().editor_background)1674 .rounded_lg()
1675 .border_1()1675 .min_w(rems(MIN_INPUT_WIDTH_REMS))
1676 .border_color(search.border_color_for(InputPanel::Query, cx))1676 .max_w(rems(MAX_INPUT_WIDTH_REMS))
1677 .rounded_lg()1677 .on_action(cx.listener(|this, action, cx| this.confirm(action, cx)))
1678 .on_action(cx.listener(|this, action, cx| this.confirm(action, cx)))1678 .on_action(cx.listener(|this, action, cx| this.previous_history_query(action, cx)))
1679 .on_action(cx.listener(|this, action, cx| this.previous_history_query(action, cx)))1679 .on_action(cx.listener(|this, action, cx| this.next_history_query(action, cx)))
1680 .on_action(cx.listener(|this, action, cx| this.next_history_query(action, cx)))1680 .child(self.render_text_input(&search.query_editor, cx))
1681 .child(Icon::new(IconName::MagnifyingGlass))1681 .child(
1682 .child(self.render_text_input(&search.query_editor, cx))1682 h_flex()
1683 .child(1683 .child(
1684 h_flex()1684 IconButton::new("project-search-filter-button", IconName::Filter)
1685 .tooltip(|cx| Tooltip::for_action("Toggle filters", &ToggleFilters, cx))
1686 .on_click(cx.listener(|this, _, cx| {
1687 this.toggle_filters(cx);
1688 }))
1689 .selected(
1690 self.active_project_search
1691 .as_ref()
1692 .map(|search| search.read(cx).filters_enabled)
1693 .unwrap_or_default(),
1694 ),
1695 )
1696 .when(search.current_mode != SearchMode::Semantic, |this| {
1697 this.child(
1698 IconButton::new(
1699 "project-search-case-sensitive",
1700 IconName::CaseSensitive,
1701 )
1702 .tooltip(|cx| {
1703 Tooltip::for_action(
1704 "Toggle case sensitive",
1705 &ToggleCaseSensitive,
1706 cx,
1707 )
1708 })
1709 .selected(self.is_option_enabled(SearchOptions::CASE_SENSITIVE, cx))
1710 .on_click(cx.listener(|this, _, cx| {
1711 this.toggle_search_option(SearchOptions::CASE_SENSITIVE, cx);
1712 })),
1713 )
1685 .child(1714 .child(
1686 IconButton::new("project-search-filter-button", IconName::Filter)1715 IconButton::new("project-search-whole-word", IconName::WholeWord)
1687 .tooltip(|cx| {1716 .tooltip(|cx| {
1688 Tooltip::for_action("Toggle filters", &ToggleFilters, cx)1717 Tooltip::for_action("Toggle whole word", &ToggleWholeWord, cx)
1689 })1718 })
1719 .selected(self.is_option_enabled(SearchOptions::WHOLE_WORD, cx))
1690 .on_click(cx.listener(|this, _, cx| {1720 .on_click(cx.listener(|this, _, cx| {
1691 this.toggle_filters(cx);1721 this.toggle_search_option(SearchOptions::WHOLE_WORD, cx);
1692 }))1722 })),
1693 .selected(
1694 self.active_project_search
1695 .as_ref()
1696 .map(|search| search.read(cx).filters_enabled)
1697 .unwrap_or_default(),
1698 ),
1699 )1723 )
1700 .when(search.current_mode != SearchMode::Semantic, |this| {1724 }),
1701 this.child(1725 );
1702 IconButton::new(
1703 "project-search-case-sensitive",
1704 IconName::CaseSensitive,
1705 )
1706 .tooltip(|cx| {
1707 Tooltip::for_action(
1708 "Toggle case sensitive",
1709 &ToggleCaseSensitive,
1710 cx,
1711 )
1712 })
1713 .selected(self.is_option_enabled(SearchOptions::CASE_SENSITIVE, cx))
1714 .on_click(cx.listener(
1715 |this, _, cx| {
1716 this.toggle_search_option(
1717 SearchOptions::CASE_SENSITIVE,
1718 cx,
1719 );
1720 },
1721 )),
1722 )
1723 .child(
1724 IconButton::new("project-search-whole-word", IconName::WholeWord)
1725 .tooltip(|cx| {
1726 Tooltip::for_action(
1727 "Toggle whole word",
1728 &ToggleWholeWord,
1729 cx,
1730 )
1731 })
1732 .selected(self.is_option_enabled(SearchOptions::WHOLE_WORD, cx))
1733 .on_click(cx.listener(|this, _, cx| {
1734 this.toggle_search_option(SearchOptions::WHOLE_WORD, cx);
1735 })),
1736 )
1737 }),
1738 ),
1739 );
1740 1726
1741 let mode_column = v_flex().items_start().justify_start().child(1727 let mode_column = v_flex().items_start().justify_start().child(
1742 h_flex()1728 h_flex()
@@ -1807,56 +1793,23 @@ impl Render for ProjectSearchBar { · 64 unmodified lines
1807 .tooltip(|cx| Tooltip::for_action("Toggle replace", &ToggleReplace, cx)),1793 .tooltip(|cx| Tooltip::for_action("Toggle replace", &ToggleReplace, cx)),
1808 ),1794 ),
1809 );1795 );
1810 let replace_column = if search.replace_enabled {1796
1811 h_flex()1797 let match_text = search
1812 .flex_1()1798 .active_match_index
1813 .h_full()1799 .and_then(|index| {
1814 .gap_2()
1815 .px_2()
1816 .py_1()
1817 .border_1()
1818 .border_color(cx.theme().colors().border)
1819 .rounded_lg()
1820 .child(Icon::new(IconName::Replace).size(ui::IconSize::Small))
1821 .child(self.render_text_input(&search.replacement_editor, cx))
1822 } else {
1823 // Fill out the space if we don't have a replacement editor.
1824 h_flex().flex_1()
1825 };
1826 let actions_column = h_flex()
1827 .when(search.replace_enabled, |this| {
1828 this.child(
1829 IconButton::new("project-search-replace-next", IconName::ReplaceNext)
1830 .on_click(cx.listener(|this, _, cx| {
1831 if let Some(search) = this.active_project_search.as_ref() {
1832 search.update(cx, |this, cx| {
1833 this.replace_next(&ReplaceNext, cx);
1834 })
1835 }
1836 }))
1837 .tooltip(|cx| Tooltip::for_action("Replace next match", &ReplaceNext, cx)),
1838 )
1839 .child(
1840 IconButton::new("project-search-replace-all", IconName::ReplaceAll)
1841 .on_click(cx.listener(|this, _, cx| {
1842 if let Some(search) = this.active_project_search.as_ref() {
1843 search.update(cx, |this, cx| {
1844 this.replace_all(&ReplaceAll, cx);
1845 })
1846 }
1847 }))
1848 .tooltip(|cx| Tooltip::for_action("Replace all matches", &ReplaceAll, cx)),
1849 )
1850 })
1851 .when_some(search.active_match_index, |mut this, index| {
1852 let index = index + 1;1800 let index = index + 1;
1853 let match_quantity = search.model.read(cx).match_ranges.len();1801 let match_quantity = search.model.read(cx).match_ranges.len();
1854 if match_quantity > 0 {1802 if match_quantity > 0 {
1855 debug_assert!(match_quantity >= index);1803 debug_assert!(match_quantity >= index);
1856 this = this.child(Label::new(format!("{index}/{match_quantity}")))1804 Some(format!("{index}/{match_quantity}").to_string())
1805 } else {
1806 None
1857 }1807 }
1858 this
1859 })1808 })
1809 .unwrap_or_else(|| "No matches".to_string());
1810
1811 let matches_column = h_flex()
1812 .child(div().min_w(rems(6.)).child(Label::new(match_text)))
1860 .child(1813 .child(
1861 IconButton::new("project-search-prev-match", IconName::ChevronLeft)1814 IconButton::new("project-search-prev-match", IconName::ChevronLeft)
1862 .disabled(search.active_match_index.is_none())1815 .disabled(search.active_match_index.is_none())
@@ -1884,10 +1837,104 @@ impl Render for ProjectSearchBar { · 21 unmodified lines
1884 .tooltip(|cx| Tooltip::for_action("Go to next match", &SelectNextMatch, cx)),1837 .tooltip(|cx| Tooltip::for_action("Go to next match", &SelectNextMatch, cx)),
1885 );1838 );
1886 1839
1840 let search_line = h_flex()
1841 .gap_2()
1842 .flex_1()
1843 .child(query_column)
1844 .child(mode_column)
1845 .child(matches_column);
1846
1847 let replace_line = search.replace_enabled.then(|| {
1848 let replace_column = h_flex()
1849 .flex_1()
1850 .min_w(rems(MIN_INPUT_WIDTH_REMS))
1851 .max_w(rems(MAX_INPUT_WIDTH_REMS))
1852 .h_8()
1853 .px_2()
1854 .py_1()
1855 .border_1()
1856 .border_color(cx.theme().colors().border)
1857 .rounded_lg()
1858 .child(self.render_text_input(&search.replacement_editor, cx));
1859 let replace_actions = h_flex().when(search.replace_enabled, |this| {
1860 this.child(
1861 IconButton::new("project-search-replace-next", IconName::ReplaceNext)
1862 .on_click(cx.listener(|this, _, cx| {
1863 if let Some(search) = this.active_project_search.as_ref() {
1864 search.update(cx, |this, cx| {
1865 this.replace_next(&ReplaceNext, cx);
1866 })
1867 }
1868 }))
1869 .tooltip(|cx| Tooltip::for_action("Replace next match", &ReplaceNext, cx)),
1870 )
1871 .child(
1872 IconButton::new("project-search-replace-all", IconName::ReplaceAll)
1873 .on_click(cx.listener(|this, _, cx| {
1874 if let Some(search) = this.active_project_search.as_ref() {
1875 search.update(cx, |this, cx| {
1876 this.replace_all(&ReplaceAll, cx);
1877 })
1878 }
1879 }))
1880 .tooltip(|cx| Tooltip::for_action("Replace all matches", &ReplaceAll, cx)),
1881 )
1882 });
1883 h_flex()
1884 .gap_2()
1885 .child(replace_column)
1886 .child(replace_actions)
1887 });
1888
1889 let filter_line = search.filters_enabled.then(|| {
1890 h_flex()
1891 .w_full()
1892 .gap_2()
1893 .child(
1894 h_flex()
1895 .flex_1()
1896 .min_w(rems(MIN_INPUT_WIDTH_REMS))
1897 .max_w(rems(MAX_INPUT_WIDTH_REMS))
1898 .h_8()
1899 .px_2()
1900 .py_1()
1901 .border_1()
1902 .border_color(search.border_color_for(InputPanel::Include, cx))
1903 .rounded_lg()
1904 .child(self.render_text_input(&search.included_files_editor, cx))
1905 .when(search.current_mode != SearchMode::Semantic, |this| {
1906 this.child(
1907 SearchOptions::INCLUDE_IGNORED.as_button(
1908 search
1909 .search_options
1910 .contains(SearchOptions::INCLUDE_IGNORED),
1911 cx.listener(|this, _, cx| {
1912 this.toggle_search_option(
1913 SearchOptions::INCLUDE_IGNORED,
1914 cx,
1915 );
1916 }),
1917 ),
1918 )
1919 }),
1920 )
1921 .child(
1922 h_flex()
1923 .flex_1()
1924 .min_w(rems(MIN_INPUT_WIDTH_REMS))
1925 .max_w(rems(MAX_INPUT_WIDTH_REMS))
1926 .h_8()
1927 .px_2()
1928 .py_1()
1929 .border_1()
1930 .border_color(search.border_color_for(InputPanel::Exclude, cx))
1931 .rounded_lg()
1932 .child(self.render_text_input(&search.excluded_files_editor, cx)),
1933 )
1934 });
1935
1887 v_flex()1936 v_flex()
1888 .key_context(key_context)1937 .key_context(key_context)
1889 .flex_grow()
1890 .gap_2()
1891 .on_action(cx.listener(|this, _: &ToggleFocus, cx| this.move_focus_to_results(cx)))1938 .on_action(cx.listener(|this, _: &ToggleFocus, cx| this.move_focus_to_results(cx)))
1892 .on_action(cx.listener(|this, _: &ToggleFilters, cx| {1939 .on_action(cx.listener(|this, _: &ToggleFilters, cx| {
1893 this.toggle_filters(cx);1940 this.toggle_filters(cx);
@@ -1945,60 +1992,11 @@ impl Render for ProjectSearchBar { · 51 unmodified lines
1945 })1992 })
1946 .on_action(cx.listener(Self::select_next_match))1993 .on_action(cx.listener(Self::select_next_match))
1947 .on_action(cx.listener(Self::select_prev_match))1994 .on_action(cx.listener(Self::select_prev_match))
1948 .child(1995 .gap_2()
1949 h_flex()1996 .w_full()
1950 .justify_between()1997 .child(search_line)
1951 .gap_2()1998 .children(replace_line)
1952 .child(query_column)1999 .children(filter_line)
1953 .child(mode_column)
1954 .child(replace_column)
1955 .child(actions_column),
1956 )
1957 .when(search.filters_enabled, |this| {
1958 this.child(
1959 h_flex()
1960 .flex_1()
1961 .gap_2()
1962 .justify_between()
1963 .child(
1964 h_flex()
1965 .flex_1()
1966 .h_full()
1967 .px_2()
1968 .py_1()
1969 .border_1()
1970 .border_color(search.border_color_for(InputPanel::Include, cx))
1971 .rounded_lg()
1972 .child(self.render_text_input(&search.included_files_editor, cx))
1973 .when(search.current_mode != SearchMode::Semantic, |this| {
1974 this.child(
1975 SearchOptions::INCLUDE_IGNORED.as_button(
1976 search
1977 .search_options
1978 .contains(SearchOptions::INCLUDE_IGNORED),
1979 cx.listener(|this, _, cx| {
1980 this.toggle_search_option(
1981 SearchOptions::INCLUDE_IGNORED,
1982 cx,
1983 );
1984 }),
1985 ),
1986 )
1987 }),
1988 )
1989 .child(
1990 h_flex()
1991 .flex_1()
1992 .h_full()
1993 .px_2()
1994 .py_1()
1995 .border_1()
1996 .border_color(search.border_color_for(InputPanel::Exclude, cx))
1997 .rounded_lg()
1998 .child(self.render_text_input(&search.excluded_files_editor, cx)),
1999 ),
2000 )
2001 })
2002 }2000 }
2003}2001}
2004 2002