input: Add mask_toggle button to toggle masked state to Input. (#758)

This commit is contained in:
Jason Lee 2025-03-31 22:57:07 +08:00 committed by GitHub
parent 3357197f60
commit 88b5e5c023
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 57 additions and 9 deletions

View file

@ -33,7 +33,7 @@ pub struct InputStory {
number_input1: Entity<NumberInput>,
number_input2: Entity<NumberInput>,
number_input2_value: u64,
mash_input: Entity<TextInput>,
mask_input: Entity<TextInput>,
disabled_input: Entity<TextInput>,
prefix_input1: Entity<TextInput>,
suffix_input1: Entity<TextInput>,
@ -138,8 +138,10 @@ impl InputStory {
.detach();
let mask_input = cx.new(|cx| {
let mut input = TextInput::new(window, cx).cleanable();
input.set_masked(true, window, cx);
let mut input = TextInput::new(window, cx)
.masked(true)
.mask_toggle()
.cleanable();
input.set_text("this-is-password", window, cx);
input
});
@ -183,7 +185,7 @@ impl InputStory {
number_input1_value,
number_input2,
number_input2_value: 0,
mash_input: mask_input,
mask_input,
disabled_input: cx.new(|cx| {
let mut input = TextInput::new(window, cx);
input.set_text("This is disabled input", window, cx);
@ -375,7 +377,7 @@ impl FocusableCycle for InputStory {
self.input1.focus_handle(cx),
self.input2.focus_handle(cx),
self.disabled_input.focus_handle(cx),
self.mash_input.focus_handle(cx),
self.mask_input.focus_handle(cx),
self.prefix_input1.focus_handle(cx),
self.both_input1.focus_handle(cx),
self.suffix_input1.focus_handle(cx),
@ -450,7 +452,7 @@ impl Render for InputStory {
.child(
section("Input State", cx)
.child(self.disabled_input.clone())
.child(self.mash_input.clone()),
.child(self.mask_input.clone()),
),
)
.child(

View file

@ -5,6 +5,7 @@ use crate::{
ActiveTheme as _, Icon, IconName, Sizable as _,
};
#[inline]
pub(crate) fn clear_button(cx: &App) -> Button {
Button::new("clean")
.icon(Icon::new(IconName::CircleX))

View file

@ -27,13 +27,14 @@ use super::change::Change;
use super::element::TextElement;
use super::number_input;
use crate::button::{Button, ButtonVariants as _};
use crate::history::History;
use crate::indicator::Indicator;
use crate::input::clear_button;
use crate::scroll::{Scrollbar, ScrollbarAxis, ScrollbarState};
use crate::Size;
use crate::StyledExt;
use crate::{ActiveTheme, Root};
use crate::{IconName, Size};
use crate::{Sizable, StyleSized};
actions!(
@ -220,6 +221,7 @@ pub struct TextInput {
pub(super) selecting: bool,
pub(super) disabled: bool,
pub(super) masked: bool,
pub(super) mask_toggle: bool,
pub(super) appearance: bool,
pub(super) cleanable: bool,
pub(super) size: Size,
@ -279,6 +281,7 @@ impl TextInput {
selecting: false,
disabled: false,
masked: false,
mask_toggle: false,
appearance: true,
cleanable: false,
loading: false,
@ -520,12 +523,24 @@ impl TextInput {
cx.notify();
}
/// Set with masked state.
pub fn masked(mut self, masked: bool) -> Self {
self.masked = masked;
self
}
/// Set the masked state of the input field.
pub fn set_masked(&mut self, masked: bool, _: &mut Window, cx: &mut Context<Self>) {
self.masked = masked;
cx.notify();
}
/// Set to enable toggle button for mask state.
pub fn mask_toggle(mut self) -> Self {
self.mask_toggle = true;
self
}
/// Set the prefix element of the input field.
pub fn set_prefix<F, E>(&mut self, builder: F, _: &mut Window, cx: &mut Context<Self>)
where
@ -1447,6 +1462,35 @@ impl TextInput {
.map(|p| p.is_match(new_text))
.unwrap_or(true)
}
fn render_toggle_mask_button(
&self,
_: &mut Window,
cx: &mut Context<Self>,
) -> Option<impl IntoElement> {
if !self.mask_toggle {
return None;
}
Some(
Button::new("toggle-mask")
.icon(IconName::Eye)
.xsmall()
.ghost()
.on_mouse_down(
MouseButton::Left,
cx.listener(|this, _, window, cx| {
this.set_masked(false, window, cx);
}),
)
.on_mouse_up(
MouseButton::Left,
cx.listener(|this, _, window, cx| {
this.set_masked(true, window, cx);
}),
),
)
}
}
impl Sizable for TextInput {
@ -1642,8 +1686,8 @@ impl Render for TextInput {
let focused = self.focus_handle.is_focused(window);
let mut gap_x = match self.size {
Size::Small => px(4.),
Size::Large => px(12.),
_ => px(8.),
Size::Large => px(8.),
_ => px(4.),
};
if self.no_gap {
gap_x = px(0.);
@ -1738,6 +1782,7 @@ impl Render for TextInput {
.when(self.loading, |this| {
this.child(Indicator::new().color(cx.theme().muted_foreground))
})
.children(self.render_toggle_mask_button(window, cx))
.when(
self.cleanable && !self.loading && !self.text.is_empty() && self.is_single_line(),
|this| this.child(clear_button(cx).on_click(cx.listener(Self::clean))),