GPUI Box GitHub

Components / controls

PasswordInput view

One sensitive password editor with a visual reveal action.

A view survives a frame. Hold it in an Entity with cx.new(..) and reach it with .update(..).

use gpui_kit::controls::auth::PasswordInput;

Reviewed in touch-inputs auth-sign-in.

Construct
new(ident: impl Into<Ident>, window: &mut Window, cx: &mut Context<Self>) -> Self
bind(field: &Entity<Self>, signal: &Signal<String>, cx: &mut App) -> Vec<Subscription>
Options

Chain onto the value.

placeholder(placeholder: impl Into<SharedString>) -> Self
input_options(options: gpui::TextInputOptions) -> Self
name(name: impl Into<SharedString>) -> Self
text(text: impl Into<SharedString>) -> Self
invalid(invalid: bool) -> Self
required(required: bool) -> Self
read_only(read_only: bool) -> Self
Commands

Need a Context, so they need a view.

set_input_options(options: gpui::TextInputOptions, cx: &mut Context<Self>)
set_name(name: Option<SharedString>, cx: &mut Context<Self>)
set_placeholder(placeholder: Option<SharedString>, cx: &mut Context<Self>)
set_required(required: bool, cx: &mut Context<Self>)
set_control_size(size: ControlSize, cx: &mut Context<Self>)
set_value(value: impl Into<SharedString>, cx: &mut Context<Self>)
set_disabled(disabled: bool, cx: &mut Context<Self>)
set_read_only(read_only: bool, cx: &mut Context<Self>)
set_invalid(invalid: bool, cx: &mut Context<Self>)
Queries

Only answer; they change nothing.

value(cx: &App) -> SharedString
is_revealed() -> bool
selected_range(cx: &App) -> Range<usize>
is_disabled() -> bool
Reports

The variants of the event it emits. It never applies the change itself.

  • Change
  • Submit
  • Cancel
  • BackspaceAtStart
  • Focus
  • Blur
Example · touch-inputs
fn touch_inputs(window: &mut Window, cx: &mut App) -> AnyElement {
    if !cx.has_global::<TouchInputs>() {
        let email = cx.new(|cx| {
            TextInput::new("scene.touch.email", window, cx)
                .name("Email")
                .placeholder("[email protected]")
                .control_size(ControlSize::Touch)
                .input_options(gpui::TextInputOptions {
                    purpose: gpui::KeyboardPurpose::Email,
                    action: gpui::TextInputAction::Next,
                    autofill: Some(gpui::AutofillPurpose::Email),
                    ..Default::default()
                })
        });
        let password = cx.new(|cx| {
            PasswordInput::new("scene.touch.password", window, cx)
                .name("Password")
                .placeholder("Password")
                .control_size(ControlSize::Touch)
                .input_options(gpui::TextInputOptions {
                    action: gpui::TextInputAction::Done,
                    autofill: Some(gpui::AutofillPurpose::CurrentPassword),
                    ..Default::default()
                })
        });
        let password_focus = password.clone();
        let host_window = window.window_handle();
        let next = cx.subscribe(&email, move |_, action: &gpui::TextInputAction, cx| {
            if *action == gpui::TextInputAction::Next {
                let focus = password_focus.read(cx).focus_handle(cx);
                cx.update_window(host_window, |_, window, cx| focus.focus(window, cx))
                    .ok();
            }
        });
        let code = cx.new(|cx| {
            OneTimeCodeInput::new("scene.touch.code", window, cx)
                .name("Code")
                .slots(6)
                .control_size(ControlSize::Touch)
                .input_options(gpui::TextInputOptions {
                    purpose: gpui::KeyboardPurpose::Number,
                    autofill: Some(gpui::AutofillPurpose::OneTimeCode),
                    ..Default::default()
                })
        });
        let notes = cx.new(|cx| {
            TextArea::new("scene.touch.notes", window, cx)
                .placeholder("Notes · wraps at this width")
                .rows(2)
                .control_size(ControlSize::Touch)
        });
        let disabled = cx.new(|cx| {
            TextInput::new("scene.touch.disabled", window, cx)
                .name("Disabled fixture")
                .text("Managed by the host")
                .disabled(true)
                .control_size(ControlSize::Touch)
        });
        let invalid = cx.new(|cx| {
            TextInput::new("scene.touch.invalid", window, cx)
                .name("Invalid email fixture")
                .text("not an address")
                .invalid(true)
                .control_size(ControlSize::Touch)
        });
        cx.set_global(TouchInputs {
            fields: vec![
                ("scene.touch.email", "Email", email.into()),
                ("scene.touch.password", "Password", password.into()),
                (
                    "scene.touch.code",
                    "Verification code · 6 characters",
                    code.into(),
                ),
                ("scene.touch.notes", "Notes", notes.into()),
                ("scene.touch.disabled", "Managed field", disabled.into()),
                ("scene.touch.invalid", "Invalid email", invalid.into()),
            ],
            _next: next,
        });
    }
    let theme = cx.theme().clone();
    stack(&theme)
        .w(px(390.0))
        .max_w_full()
        .child(caption(
            &theme,
            "Touch editing fixtures · keyboard hints are platform-dependent",
        ))
        .children(
            cx.global::<TouchInputs>()
                .fields
                .iter()
                .map(|(id, label, view)| {
                    FormField::new(format!("{id}.field"), *label)
                        .control(*id)
                        .child(view.clone())
                }),
        )
        .child(caption(
            &theme,
            "Code paste is not SMS autofill. Error and disabled values remain visible.",
        ))
        .into_any_element()
}

Source: crates/gpui-kit/src/controls/auth.rs