Add pattern and validate to Input for limit input format. (#59)
This commit is contained in:
parent
3ec35929ae
commit
db14f0a2e5
5 changed files with 54 additions and 6 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -5119,6 +5119,7 @@ dependencies = [
|
||||||
"fake",
|
"fake",
|
||||||
"gpui",
|
"gpui",
|
||||||
"image",
|
"image",
|
||||||
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
"ui",
|
"ui",
|
||||||
"workspace",
|
"workspace",
|
||||||
|
|
@ -5696,6 +5697,7 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"paste",
|
"paste",
|
||||||
"raw-window-handle 0.6.2",
|
"raw-window-handle 0.6.2",
|
||||||
|
"regex",
|
||||||
"resvg",
|
"resvg",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ workspace.workspace = true
|
||||||
charts-rs = "0.3"
|
charts-rs = "0.3"
|
||||||
image = "0.25.1"
|
image = "0.25.1"
|
||||||
wry = "0"
|
wry = "0"
|
||||||
|
regex = "1"
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,8 @@ impl InputStory {
|
||||||
let suffix_input1 = cx.new_view(|cx| {
|
let suffix_input1 = cx.new_view(|cx| {
|
||||||
TextInput::new(cx)
|
TextInput::new(cx)
|
||||||
.suffix(|_| IconName::Info)
|
.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)
|
.cleanable(true)
|
||||||
});
|
});
|
||||||
let both_input1 = cx.new_view(|cx| {
|
let both_input1 = cx.new_view(|cx| {
|
||||||
|
|
@ -114,7 +115,8 @@ impl InputStory {
|
||||||
small_input: cx.new_view(|cx| {
|
small_input: cx.new_view(|cx| {
|
||||||
TextInput::new(cx)
|
TextInput::new(cx)
|
||||||
.size(Size::Small)
|
.size(Size::Small)
|
||||||
.placeholder("Small input")
|
.validate(|s| s.parse::<f32>().is_ok())
|
||||||
|
.placeholder("validate to limit float number.")
|
||||||
}),
|
}),
|
||||||
prefix_input1,
|
prefix_input1,
|
||||||
suffix_input1,
|
suffix_input1,
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ raw-window-handle = "0.6.2"
|
||||||
winit = "0.30.3"
|
winit = "0.30.3"
|
||||||
wry = "0"
|
wry = "0"
|
||||||
smol = "1"
|
smol = "1"
|
||||||
|
regex = "1"
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,8 @@ pub struct TextInput {
|
||||||
appearance: bool,
|
appearance: bool,
|
||||||
cleanable: bool,
|
cleanable: bool,
|
||||||
size: Size,
|
size: Size,
|
||||||
|
pattern: Option<regex::Regex>,
|
||||||
|
validate: Option<Box<dyn Fn(&str) -> bool + 'static>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventEmitter<InputEvent> for TextInput {}
|
impl EventEmitter<InputEvent> for TextInput {}
|
||||||
|
|
@ -157,6 +159,8 @@ impl TextInput {
|
||||||
prefix: None,
|
prefix: None,
|
||||||
suffix: None,
|
suffix: None,
|
||||||
size: Size::Medium,
|
size: Size::Medium,
|
||||||
|
pattern: None,
|
||||||
|
validate: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Observe the blink cursor to repaint the view when it changes.
|
// Observe the blink cursor to repaint the view when it changes.
|
||||||
|
|
@ -268,6 +272,18 @@ impl TextInput {
|
||||||
self
|
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.
|
/// Set true to show indicator at the input right.
|
||||||
pub fn set_loading(&mut self, loading: bool, cx: &mut ViewContext<Self>) {
|
pub fn set_loading(&mut self, loading: bool, cx: &mut ViewContext<Self>) {
|
||||||
self.loading = loading;
|
self.loading = loading;
|
||||||
|
|
@ -619,6 +635,23 @@ impl TextInput {
|
||||||
fn on_key_down_for_blink_cursor(&mut self, _: &KeyDownEvent, cx: &mut ViewContext<Self>) {
|
fn on_key_down_for_blink_cursor(&mut self, _: &KeyDownEvent, cx: &mut ViewContext<Self>) {
|
||||||
self.pause_blink_cursor(cx)
|
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 {
|
impl ViewInputHandler for TextInput {
|
||||||
|
|
@ -661,9 +694,14 @@ impl ViewInputHandler for TextInput {
|
||||||
.or(self.marked_range.clone())
|
.or(self.marked_range.clone())
|
||||||
.unwrap_or(self.selected_range.clone());
|
.unwrap_or(self.selected_range.clone());
|
||||||
|
|
||||||
self.push_history(&range, new_text, cx);
|
let pending_text: SharedString =
|
||||||
self.text =
|
|
||||||
(self.text[0..range.start].to_owned() + new_text + &self.text[range.end..]).into();
|
(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.selected_range = range.start + new_text.len()..range.start + new_text.len();
|
||||||
self.marked_range.take();
|
self.marked_range.take();
|
||||||
cx.emit(InputEvent::Change(self.text.clone()));
|
cx.emit(InputEvent::Change(self.text.clone()));
|
||||||
|
|
@ -686,10 +724,14 @@ impl ViewInputHandler for TextInput {
|
||||||
.map(|range_utf16| self.range_from_utf16(range_utf16))
|
.map(|range_utf16| self.range_from_utf16(range_utf16))
|
||||||
.or(self.marked_range.clone())
|
.or(self.marked_range.clone())
|
||||||
.unwrap_or(self.selected_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.push_history(&range, new_text, cx);
|
||||||
self.text =
|
self.text = pending_text;
|
||||||
(self.text[0..range.start].to_owned() + new_text + &self.text[range.end..]).into();
|
|
||||||
self.marked_range = Some(range.start..range.start + new_text.len());
|
self.marked_range = Some(range.start..range.start + new_text.len());
|
||||||
self.selected_range = new_selected_range_utf16
|
self.selected_range = new_selected_range_utf16
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue