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_component::{
alert::Alert,
button::{Button, ButtonGroup},
v_flex, IconName, Selectable as _, Sizable as _, Size,
};
use story::{AlertStory, Assets};
pub struct Example {

View file

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

View file

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