project: Add spinner while search is underway (#47620)

Andres Suarez and Danilo Leal created

When there are no search results, it's clear that a search is still
ongoing because the landing page says "Searching...". However, once
there's at least one result found, it becomes completely unclear when
search is actually finished. This adds a spinner immediately to the
right of the result counter that stops once the search is finished.


https://github.com/user-attachments/assets/a0ca4e2a-c506-42a4-bc4b-c1eb32d69e79

Release Notes:

- Added spinner to project search, indicating that a search is still
ongoing.

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>

Change summary

crates/search/src/project_search.rs | 39 ++++++++++++++++++++++--------
1 file changed, 28 insertions(+), 11 deletions(-)

Detailed changes

crates/search/src/project_search.rs 🔗

@@ -40,7 +40,10 @@ use std::{
     pin::pin,
     sync::Arc,
 };
-use ui::{IconButtonShape, KeyBinding, Toggleable, Tooltip, prelude::*, utils::SearchInputWidth};
+use ui::{
+    CommonAnimationExt, IconButtonShape, KeyBinding, Toggleable, Tooltip, prelude::*,
+    utils::SearchInputWidth,
+};
 use util::{ResultExt as _, paths::PathMatcher, rel_path::RelPath};
 use workspace::{
     DeploySearch, ItemNavHistory, NewSearch, ToolbarItemEvent, ToolbarItemLocation,
@@ -2013,6 +2016,7 @@ impl Render for ProjectSearchBar {
         let theme_colors = cx.theme().colors();
         let project_search = search.entity.read(cx);
         let limit_reached = project_search.limit_reached;
+        let is_search_underway = project_search.pending_search.is_some();
 
         let color_override = match (
             &project_search.pending_search,
@@ -2105,16 +2109,29 @@ impl Render for ProjectSearchBar {
                     .id("matches")
                     .ml_2()
                     .min_w(rems_from_px(40.))
-                    .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(Tooltip::text(
-                            "Search limits reached.\nTry narrowing your search.",
+                    .child(
+                        h_flex()
+                            .gap_1p5()
+                            .child(
+                                Label::new(match_text)
+                                    .size(LabelSize::Small)
+                                    .when(search.active_match_index.is_some(), |this| {
+                                        this.color(Color::Disabled)
+                                    }),
+                            )
+                            .when(is_search_underway, |this| {
+                                this.child(
+                                    Icon::new(IconName::ArrowCircle)
+                                        .color(Color::Accent)
+                                        .size(IconSize::Small)
+                                        .with_rotate_animation(2)
+                                        .into_any_element(),
+                                )
+                            }),
+                    )
+                    .when(limit_reached, |this| {
+                        this.tooltip(Tooltip::text(
+                            "Search Limits Reached\nTry narrowing your search",
                         ))
                     }),
             );