input: Improve clear button position in Input. (#782)

| Before | After |
| -- | -- |
| <img width="529" alt="image"
src="https://github.com/user-attachments/assets/02fdba71-7e83-43b9-ba4c-476eb21dcf28"
/> | <img width="530" alt="image"
src="https://github.com/user-attachments/assets/ad7b6378-c9ff-4ea4-943a-87af53a2ac79"
/> |
This commit is contained in:
Jason Lee 2025-04-11 19:13:17 +08:00 committed by GitHub
parent d04866eb77
commit 73be54a0f7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 63 additions and 31 deletions

View file

@ -11,7 +11,7 @@ use gpui_component::{
checkbox::Checkbox,
h_flex,
input::{InputEvent, NumberInput, NumberInputEvent, OtpInput, StepAction, TextInput},
v_flex, FocusableCycle, IconName, Sizable,
v_flex, FocusableCycle, Icon, IconName, Sizable,
};
actions!(input_story, [Tab, TabPrev]);
@ -139,21 +139,33 @@ impl InputStory {
let prefix_input1 = cx.new(|cx| {
TextInput::new(window, cx)
.prefix(|_, _| div().child(IconName::Search).ml_3())
.prefix(|_, _| div().child(Icon::new(IconName::Search).small()).ml_3())
.placeholder("Search some thing...")
.cleanable()
});
let suffix_input1 = cx.new(|cx| {
TextInput::new(window, cx)
.suffix(|_, _| div().child(IconName::Info).mr_3())
.suffix(|_, _| {
Button::new("info")
.ghost()
.icon(IconName::Info)
.xsmall()
.mr_3()
})
.placeholder("This input only support [a-zA-Z0-9] characters.")
.pattern(regex::Regex::new(r"^[a-zA-Z0-9]*$").unwrap())
.cleanable()
});
let both_input1 = cx.new(|cx| {
TextInput::new(window, cx)
.prefix(|_, _| div().child(IconName::Search).ml_3())
.suffix(|_, _| div().child(IconName::Info).mr_3())
.prefix(|_, _| div().child(Icon::new(IconName::Search).small()).ml_3())
.suffix(|_, _| {
Button::new("info")
.ghost()
.icon(IconName::Info)
.xsmall()
.mr_3()
})
.cleanable()
.placeholder("This input have prefix and suffix.")
});

View file

@ -149,3 +149,9 @@ Modal:
zh-CN: 取消
zh-HK: 取消
it: Annulla
List:
search_placeholder:
en: Search...
zh-CN: 搜索...
zh-HK: 搜索...
it: Ricerca...

View file

@ -355,6 +355,7 @@ impl RenderOnce for Button {
self.base
.id(self.id)
.cursor_default()
.flex()
.items_center()
.justify_center()

View file

@ -33,7 +33,7 @@ use crate::history::History;
use crate::indicator::Indicator;
use crate::input::clear_button;
use crate::scroll::{Scrollbar, ScrollbarAxis, ScrollbarState};
use crate::StyledExt;
use crate::{h_flex, StyledExt};
use crate::{ActiveTheme, Root};
use crate::{IconName, Size};
use crate::{Sizable, StyleSized};
@ -1756,6 +1756,13 @@ impl Render for TextInput {
let prefix = self.prefix.as_ref().map(|build| build(window, cx));
let suffix = self.suffix.as_ref().map(|build| build(window, cx));
let show_clear_button =
self.cleanable && !self.loading && !self.text.is_empty() && self.is_single_line();
let bg = if self.disabled {
cx.theme().muted
} else {
cx.theme().background
};
div()
.flex()
@ -1815,40 +1822,45 @@ impl Render for TextInput {
.when_some(self.height, |this, height| this.h(height))
})
.when(self.appearance, |this| {
this.bg(if self.disabled {
cx.theme().muted
} else {
cx.theme().background
})
.border_color(cx.theme().input)
.border_1()
.rounded(cx.theme().radius)
.when(cx.theme().shadow, |this| this.shadow_sm())
.when(focused, |this| this.focused_border(cx))
this.bg(bg)
.border_color(cx.theme().input)
.border_1()
.rounded(cx.theme().radius)
.when(cx.theme().shadow, |this| this.shadow_sm())
.when(focused, |this| this.focused_border(cx))
})
.when(prefix.is_none(), |this| this.input_pl(self.size))
.when(suffix.is_none(), |this| this.input_pr(self.size))
.children(prefix)
.gap(gap_x)
.input_pr(self.size)
.items_center()
.gap(gap_x)
.children(prefix)
.child(
div()
.id("TextElement")
.id("text-element")
.flex_1()
.when(self.is_multi_line(), |this| this.h_full())
.flex_grow()
.overflow_x_hidden()
.child(TextElement::new(cx.entity().clone())),
)
.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))),
.child(
h_flex()
.id("suffix")
.absolute()
.gap(gap_x)
.bg(bg)
.items_center()
.when(suffix.is_none(), |this| this.pr_1())
.right_0()
.when(self.loading, |this| {
this.child(Indicator::new().color(cx.theme().muted_foreground))
})
.children(self.render_toggle_mask_button(window, cx))
.when(show_clear_button, |this| {
this.child(clear_button(cx).on_click(cx.listener(Self::clean)))
})
.children(suffix),
)
.children(suffix)
.when(self.is_multi_line(), |this| {
let entity_id = cx.entity().entity_id();
if self.last_layout.is_some() {

View file

@ -164,8 +164,8 @@ impl Render for NumberInput {
// Sync size to input at first.
self.sync_size_to_input_if_needed(window, cx);
let btn_size = match self.size {
Size::XSmall | Size::Small => Size::XSmall,
_ => Size::Small,
Size::XSmall | Size::Small => Size::Size(px(16.)),
_ => Size::XSmall,
};
h_flex()

View file

@ -15,6 +15,7 @@ use gpui::{
Window,
};
use gpui::{px, App, Context, EventEmitter, MouseDownEvent, ScrollStrategy, Subscription};
use rust_i18n::t;
use smol::Timer;
use super::loading::Loading;
@ -173,7 +174,7 @@ where
TextInput::new(window, cx)
.appearance(false)
.prefix(|_, cx| Icon::new(IconName::Search).text_color(cx.theme().muted_foreground))
.placeholder("Search...")
.placeholder(t!("List.search_placeholder"))
.cleanable()
});