input: Add Esc to clean. (#803)

This commit is contained in:
Jens Krause 2025-04-20 04:24:47 +02:00 committed by GitHub
parent 7719d8b6fc
commit f7be6a245a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 48 additions and 17 deletions

View file

@ -1,9 +1,5 @@
use gpui::*; use gpui::*;
use gpui_component::{
alert::Alert,
button::{Button, ButtonGroup},
v_flex, IconName, Selectable as _, Sizable as _, Size,
};
use story::{AlertStory, Assets}; use story::{AlertStory, Assets};
pub struct Example { pub struct Example {

View file

@ -9,7 +9,7 @@ use gpui_component::{
button::{Button, ButtonVariant, ButtonVariants as _}, button::{Button, ButtonVariant, ButtonVariants as _},
h_flex, h_flex,
input::{InputEvent, TextInput}, input::{InputEvent, TextInput},
v_flex, FocusableCycle, Icon, IconName, Sizable, StyledExt, v_flex, FocusableCycle, Icon, IconName, Sizable,
}; };
actions!(input_story, [Tab, TabPrev]); actions!(input_story, [Tab, TabPrev]);
@ -26,6 +26,7 @@ pub fn init(cx: &mut App) {
pub struct InputStory { pub struct InputStory {
input1: Entity<TextInput>, input1: Entity<TextInput>,
input2: Entity<TextInput>, input2: Entity<TextInput>,
input_esc: Entity<TextInput>,
mask_input: Entity<TextInput>, mask_input: Entity<TextInput>,
disabled_input: Entity<TextInput>, disabled_input: Entity<TextInput>,
prefix_input1: Entity<TextInput>, prefix_input1: Entity<TextInput>,
@ -68,6 +69,12 @@ impl InputStory {
}); });
let input2 = cx.new(|cx| TextInput::new(window, cx).placeholder("Enter text here...")); let input2 = cx.new(|cx| TextInput::new(window, cx).placeholder("Enter text here..."));
let input_esc = cx.new(|cx| {
TextInput::new(window, cx)
.placeholder("Enter text and clear it by pressing ESC")
.cleanable()
.clean_on_escape()
});
let mask_input = cx.new(|cx| { let mask_input = cx.new(|cx| {
let mut input = TextInput::new(window, cx) let mut input = TextInput::new(window, cx)
@ -119,6 +126,7 @@ impl InputStory {
Self { Self {
input1, input1,
input2, input2,
input_esc,
mask_input, mask_input,
disabled_input: cx.new(|cx| { disabled_input: cx.new(|cx| {
let mut input = TextInput::new(window, cx); let mut input = TextInput::new(window, cx);
@ -173,6 +181,7 @@ impl FocusableCycle for InputStory {
[ [
self.input1.focus_handle(cx), self.input1.focus_handle(cx),
self.input2.focus_handle(cx), self.input2.focus_handle(cx),
self.input_esc.focus_handle(cx),
self.disabled_input.focus_handle(cx), self.disabled_input.focus_handle(cx),
self.mask_input.focus_handle(cx), self.mask_input.focus_handle(cx),
self.prefix_input1.focus_handle(cx), self.prefix_input1.focus_handle(cx),
@ -202,21 +211,18 @@ impl Render for InputStory {
.gap_3() .gap_3()
.child( .child(
section("Normal Input") section("Normal Input")
.v_flex()
.max_w_md() .max_w_md()
.child(self.input1.clone()) .child(self.input1.clone())
.child(self.input2.clone()), .child(self.input2.clone()),
) )
.child( .child(
section("Input State") section("Input State")
.v_flex()
.max_w_md() .max_w_md()
.child(self.disabled_input.clone()) .child(self.disabled_input.clone())
.child(self.mask_input.clone()), .child(self.mask_input.clone()),
) )
.child( .child(
section("Prefix and Suffix") section("Prefix and Suffix")
.v_flex()
.max_w_md() .max_w_md()
.child(self.prefix_input1.clone()) .child(self.prefix_input1.clone())
.child(self.both_input1.clone()) .child(self.both_input1.clone())
@ -224,11 +230,15 @@ impl Render for InputStory {
) )
.child( .child(
section("Input Size") section("Input Size")
.v_flex()
.max_w_md() .max_w_md()
.child(self.large_input.clone()) .child(self.large_input.clone())
.child(self.small_input.clone()), .child(self.small_input.clone()),
) )
.child(
section("Cleanable and ESC to clean")
.max_w_md()
.child(self.input_esc.clone()),
)
.child( .child(
h_flex() h_flex()
.items_center() .items_center()

View file

@ -13,11 +13,11 @@ use unicode_segmentation::*;
use gpui::prelude::FluentBuilder as _; use gpui::prelude::FluentBuilder as _;
use gpui::{ use gpui::{
actions, div, impl_internal_actions, point, px, relative, AnyElement, App, AppContext, Bounds, actions, div, impl_internal_actions, point, px, relative, AnyElement, App, AppContext, Bounds,
ClickEvent, ClipboardItem, Context, DefiniteLength, Entity, EntityInputHandler, EventEmitter, ClipboardItem, Context, DefiniteLength, Entity, EntityInputHandler, EventEmitter, FocusHandle,
FocusHandle, Focusable, InteractiveElement as _, IntoElement, KeyBinding, KeyDownEvent, Focusable, InteractiveElement as _, IntoElement, KeyBinding, KeyDownEvent, MouseButton,
MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement as _, Pixels, Point, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement as _, Pixels, Point, Rems, Render,
Rems, Render, ScrollHandle, ScrollWheelEvent, SharedString, Styled as _, Subscription, ScrollHandle, ScrollWheelEvent, SharedString, Styled as _, Subscription, UTF16Selection,
UTF16Selection, Window, WrappedLine, Window, WrappedLine,
}; };
// TODO: // TODO:
@ -85,6 +85,7 @@ actions!(
MoveToPreviousWord, MoveToPreviousWord,
MoveToNextWord, MoveToNextWord,
TextChanged, TextChanged,
Escape
] ]
); );
@ -116,6 +117,7 @@ pub fn init(cx: &mut App) {
KeyBinding::new("ctrl-delete", DeleteToNextWordEnd, Some(CONTEXT)), KeyBinding::new("ctrl-delete", DeleteToNextWordEnd, Some(CONTEXT)),
KeyBinding::new("enter", Enter { secondary: false }, Some(CONTEXT)), KeyBinding::new("enter", Enter { secondary: false }, Some(CONTEXT)),
KeyBinding::new("secondary-enter", Enter { secondary: true }, Some(CONTEXT)), KeyBinding::new("secondary-enter", Enter { secondary: true }, Some(CONTEXT)),
KeyBinding::new("escape", Escape, Some(CONTEXT)),
KeyBinding::new("up", Up, Some(CONTEXT)), KeyBinding::new("up", Up, Some(CONTEXT)),
KeyBinding::new("down", Down, Some(CONTEXT)), KeyBinding::new("down", Down, Some(CONTEXT)),
KeyBinding::new("left", Left, Some(CONTEXT)), KeyBinding::new("left", Left, Some(CONTEXT)),
@ -233,6 +235,7 @@ pub struct TextInput {
pub(super) mask_toggle: bool, pub(super) mask_toggle: bool,
pub(super) appearance: bool, pub(super) appearance: bool,
pub(super) cleanable: bool, pub(super) cleanable: bool,
pub(super) clean_on_escape: bool,
pub(super) size: Size, pub(super) size: Size,
pub(super) rows: usize, pub(super) rows: usize,
pub(super) min_rows: usize, pub(super) min_rows: usize,
@ -295,6 +298,7 @@ impl TextInput {
mask_toggle: false, mask_toggle: false,
appearance: true, appearance: true,
cleanable: false, cleanable: false,
clean_on_escape: false,
loading: false, loading: false,
prefix: None, prefix: None,
suffix: None, suffix: None,
@ -662,6 +666,12 @@ impl TextInput {
self self
} }
/// Set true to clear the input by pressing Escape key.
pub fn clean_on_escape(mut self) -> Self {
self.clean_on_escape = true;
self
}
/// Set true to not use gap between input and prefix, suffix, and clear button. /// Set true to not use gap between input and prefix, suffix, and clear button.
/// ///
/// Default: false /// Default: false
@ -1059,10 +1069,20 @@ impl TextInput {
cx.notify(); cx.notify();
} }
fn clean(&mut self, _: &ClickEvent, window: &mut Window, cx: &mut Context<Self>) { fn clean(&mut self, window: &mut Window, cx: &mut Context<Self>) {
self.replace_text("", window, cx); self.replace_text("", window, cx);
} }
fn escape(&mut self, _: &Escape, window: &mut Window, cx: &mut Context<Self>) {
if self.selected_range.len() > 0 {
return self.unselect(window, cx);
}
if self.clean_on_escape {
self.clean(window, cx);
}
}
fn on_mouse_down( fn on_mouse_down(
&mut self, &mut self,
event: &MouseDownEvent, event: &MouseDownEvent,
@ -1788,6 +1808,7 @@ impl Render for TextInput {
.on_action(cx.listener(Self::delete_previous_word)) .on_action(cx.listener(Self::delete_previous_word))
.on_action(cx.listener(Self::delete_next_word)) .on_action(cx.listener(Self::delete_next_word))
.on_action(cx.listener(Self::enter)) .on_action(cx.listener(Self::enter))
.on_action(cx.listener(Self::escape))
}) })
.on_action(cx.listener(Self::left)) .on_action(cx.listener(Self::left))
.on_action(cx.listener(Self::right)) .on_action(cx.listener(Self::right))
@ -1868,7 +1889,11 @@ impl Render for TextInput {
}) })
.children(self.render_toggle_mask_button(window, cx)) .children(self.render_toggle_mask_button(window, cx))
.when(show_clear_button, |this| { .when(show_clear_button, |this| {
this.child(clear_button(cx).on_click(cx.listener(Self::clean))) this.child(
clear_button(cx).on_click(cx.listener(|view, _, window, cx| {
view.clean(window, cx);
})),
)
}) })
.children(suffix), .children(suffix),
) )