Add pattern and validate to Input for limit input format. (#59)

This commit is contained in:
Jason Lee 2024-07-23 15:36:08 +08:00 committed by GitHub
parent 3ec35929ae
commit db14f0a2e5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 54 additions and 6 deletions

2
Cargo.lock generated
View file

@ -5119,6 +5119,7 @@ dependencies = [
"fake",
"gpui",
"image",
"regex",
"serde",
"ui",
"workspace",
@ -5696,6 +5697,7 @@ dependencies = [
"once_cell",
"paste",
"raw-window-handle 0.6.2",
"regex",
"resvg",
"serde",
"serde_json",

View file

@ -13,6 +13,7 @@ workspace.workspace = true
charts-rs = "0.3"
image = "0.25.1"
wry = "0"
regex = "1"
[lints]
workspace = true

View file

@ -75,7 +75,8 @@ impl InputStory {
let suffix_input1 = cx.new_view(|cx| {
TextInput::new(cx)
.suffix(|_| IconName::Info)
.placeholder("Info here...")
.placeholder("This input only support [a-zA-Z0-9] characters.")
.pattern(regex::Regex::new(r"^[a-zA-Z0-9]*$").unwrap())
.cleanable(true)
});
let both_input1 = cx.new_view(|cx| {
@ -114,7 +115,8 @@ impl InputStory {
small_input: cx.new_view(|cx| {
TextInput::new(cx)
.size(Size::Small)
.placeholder("Small input")
.validate(|s| s.parse::<f32>().is_ok())
.placeholder("validate to limit float number.")
}),
prefix_input1,
suffix_input1,

View file

@ -28,6 +28,7 @@ raw-window-handle = "0.6.2"
winit = "0.30.3"
wry = "0"
smol = "1"
regex = "1"
[lints]
workspace = true

View file

@ -127,6 +127,8 @@ pub struct TextInput {
appearance: bool,
cleanable: bool,
size: Size,
pattern: Option<regex::Regex>,
validate: Option<Box<dyn Fn(&str) -> bool + 'static>>,
}
impl EventEmitter<InputEvent> for TextInput {}
@ -157,6 +159,8 @@ impl TextInput {
prefix: None,
suffix: None,
size: Size::Medium,
pattern: None,
validate: None,
};
// Observe the blink cursor to repaint the view when it changes.
@ -268,6 +272,18 @@ impl TextInput {
self
}
/// Set the regular expression pattern of the input field.
pub fn pattern(mut self, pattern: regex::Regex) -> Self {
self.pattern = Some(pattern);
self
}
/// Set the validation function of the input field.
pub fn validate(mut self, f: impl Fn(&str) -> bool + 'static) -> Self {
self.validate = Some(Box::new(f));
self
}
/// Set true to show indicator at the input right.
pub fn set_loading(&mut self, loading: bool, cx: &mut ViewContext<Self>) {
self.loading = loading;
@ -619,6 +635,23 @@ impl TextInput {
fn on_key_down_for_blink_cursor(&mut self, _: &KeyDownEvent, cx: &mut ViewContext<Self>) {
self.pause_blink_cursor(cx)
}
fn is_valid_input(&self, new_text: &str) -> bool {
if new_text.is_empty() {
return true;
}
if let Some(validate) = &self.validate {
if !validate(new_text) {
return false;
}
}
self.pattern
.as_ref()
.map(|p| p.is_match(new_text))
.unwrap_or(true)
}
}
impl ViewInputHandler for TextInput {
@ -661,9 +694,14 @@ impl ViewInputHandler for TextInput {
.or(self.marked_range.clone())
.unwrap_or(self.selected_range.clone());
self.push_history(&range, new_text, cx);
self.text =
let pending_text: SharedString =
(self.text[0..range.start].to_owned() + new_text + &self.text[range.end..]).into();
if !self.is_valid_input(&pending_text) {
return;
}
self.push_history(&range, new_text, cx);
self.text = pending_text;
self.selected_range = range.start + new_text.len()..range.start + new_text.len();
self.marked_range.take();
cx.emit(InputEvent::Change(self.text.clone()));
@ -686,10 +724,14 @@ impl ViewInputHandler for TextInput {
.map(|range_utf16| self.range_from_utf16(range_utf16))
.or(self.marked_range.clone())
.unwrap_or(self.selected_range.clone());
let pending_text: SharedString =
(self.text[0..range.start].to_owned() + new_text + &self.text[range.end..]).into();
if !self.is_valid_input(&pending_text) {
return;
}
self.push_history(&range, new_text, cx);
self.text =
(self.text[0..range.start].to_owned() + new_text + &self.text[range.end..]).into();
self.text = pending_text;
self.marked_range = Some(range.start..range.start + new_text.len());
self.selected_range = new_selected_range_utf16
.as_ref()