ui/components/
search_input.rs1use gpui::{AnyElement, Context, Entity, Render};
2
3use crate::prelude::*;
4use crate::{InputGroup, TextInput};
5
6pub struct SearchInput {
10 input: Entity<TextInput>,
11}
12
13impl SearchInput {
14 pub fn new(cx: &mut Context<Self>, placeholder: impl Into<SharedString>) -> Self {
15 let input = cx.new(|cx| TextInput::new(cx).placeholder(placeholder));
16 cx.observe(&input, |_, _, cx| cx.notify()).detach();
17 Self { input }
18 }
19
20 pub fn query(&self, cx: &App) -> String {
22 self.input.read(cx).text().to_string()
23 }
24}
25
26impl Render for SearchInput {
27 fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
28 let is_empty = self.input.read(cx).text().is_empty();
29
30 let mut group = InputGroup::new(self.input.clone()).leading(
31 Icon::new(IconName::MagnifyingGlass)
32 .size(IconSize::Small)
33 .color(Color::Muted),
34 );
35
36 if !is_empty {
37 group = group.trailing(
38 div()
39 .id("search-input-clear")
40 .cursor_pointer()
41 .child(
42 Icon::new(IconName::XMark)
43 .size(IconSize::Small)
44 .color(Color::Muted),
45 )
46 .on_click(cx.listener(|this, _, _, cx| {
47 this.input.update(cx, |input, cx| input.clear(cx));
48 cx.notify();
49 })),
50 );
51 }
52
53 group
54 }
55}
56
57pub fn search_input_preview(_window: &mut Window, cx: &mut App) -> AnyElement {
61 v_flex()
62 .gap_4()
63 .child(cx.new(|cx| SearchInput::new(cx, "Search…")))
64 .into_any_element()
65}