1use crate::{IconName, Input, icon, input};
3use gpui_kit::base::input::InputState;
4use gpui_kit::{App, ElementId, Entity, Styled, Window, rems};
5
6pub fn search_field(
8 id: impl Into<ElementId>,
9 state: &Entity<InputState>,
10 window: &Window,
11 cx: &mut App,
12) -> Input {
13 input(id, state, window, cx)
14 .rounded_full()
15 .prefix(icon(IconName::Search).size(rems(0.875)))
16}
17
18pub fn secure_field(
21 id: impl Into<ElementId>,
22 state: &Entity<InputState>,
23 window: &mut Window,
24 cx: &mut App,
25) -> Input {
26 if !state.read(cx).presentation().is_masked() {
27 state.update(cx, |state, cx| state.set_masked(true, window, cx));
28 }
29 input(id, state, window, cx)
30}
31
32#[cfg(test)]
33mod tests {
34 use super::*;
35 use gpui_kit::{AppContext, Context, IntoElement, Render, TestAppContext};
36 use std::{cell::Cell, rc::Rc};
37
38 struct SecureField(Entity<InputState>);
39
40 impl Render for SecureField {
41 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
42 secure_field("password", &self.0, window, cx)
43 }
44 }
45
46 #[gpui_kit::test]
47 fn secure_field_does_not_notify_its_editor_on_unchanged_redraws(cx: &mut TestAppContext) {
48 cx.update(crate::init);
49 let notifications = Rc::new(Cell::new(0));
50 let observed = notifications.clone();
51 let (view, cx) = cx.add_window_view(move |window, cx| {
52 let state = cx.new(|cx| InputState::new(window, cx).default_value("secret"));
53 cx.observe(&state, move |_, _, _| observed.set(observed.get() + 1))
54 .detach();
55 SecureField(state)
56 });
57 cx.update(|window, cx| window.draw(cx).clear(cx));
58 cx.run_until_parked();
59 notifications.set(0);
60 for _ in 0..3 {
61 cx.update(|window, cx| {
62 window.refresh();
63 window.draw(cx).clear(cx);
64 assert!(view.read(cx).0.read(cx).presentation().is_masked());
65 });
66 cx.run_until_parked();
67 }
68 assert_eq!(
69 notifications.get(),
70 0,
71 "Rendering a secure field must not schedule another redraw"
72 );
73 }
74}