diff --git a/examples/input_validation/Cargo.toml b/examples/input_validation/Cargo.toml new file mode 100644 index 00000000..732bfe32 --- /dev/null +++ b/examples/input_validation/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "input_validation" +version = "0.1.0" +edition = "2021" + +[dependencies] +libcosmic = { path = "../..", features = ["wayland", "wgpu"] } +regex = "1.10.4" diff --git a/examples/input_validation/src/main.rs b/examples/input_validation/src/main.rs new file mode 100644 index 00000000..aaba2b8c --- /dev/null +++ b/examples/input_validation/src/main.rs @@ -0,0 +1,80 @@ +use cosmic::app::{Application, Command, Core, Settings}; +use cosmic::iced::executor; +use cosmic::iced_core::Size; +use cosmic::widget::{column, text_input}; +use cosmic::Element; +use regex::Regex; + +#[derive(Debug, Clone)] +pub enum Message { + InputName(String), + InputEmail(String), +} + +struct Window { + core: Core, + name: String, + email: String, +} + +impl Application for Window { + type Executor = executor::Default; + type Flags = (); + type Message = Message; + const APP_ID: &'static str = "com.system76.InputValidation"; + + fn core(&self) -> &Core { + &self.core + } + + fn core_mut(&mut self) -> &mut Core { + &mut self.core + } + + fn init(core: Core, _flags: Self::Flags) -> (Self, Command) { + ( + Self { + core, + name: String::new(), + email: String::new(), + }, + Command::none(), + ) + } + + fn update(&mut self, message: Self::Message) -> Command { + match message { + Message::InputName(name) => self.name = name, + Message::InputEmail(email) => self.email = email, + } + Command::none() + } + + fn view(&self) -> Element { + let emailregex = Regex::new(r#"(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])"#).unwrap(); + let validation_condition = emailregex.is_match(&self.email); + + let name_input = text_input("Name", &self.name).on_input(Message::InputName); + let email_input = text_input("E-mail", &self.email) + .validation_condition(validation_condition) + .on_input(Message::InputEmail); + + column() + .push(name_input) + .push(email_input) + .spacing(10) + .into() + } +} + +fn main() -> std::io::Result<()> { + let mut settings = Settings::default(); + settings = settings.size(Size { + width: 480., + height: 480., + }); + + let _ = cosmic::app::run::(settings, ()); + + Ok(()) +} diff --git a/src/widget/text_input/input.rs b/src/widget/text_input/input.rs index 5bf4f27a..3372ad7d 100644 --- a/src/widget/text_input/input.rs +++ b/src/widget/text_input/input.rs @@ -185,6 +185,7 @@ pub struct TextInput<'a, Message> { is_secure: bool, is_editable: bool, is_read_only: bool, + is_validated: Option, font: Option<::Font>, width: Length, padding: Padding, @@ -228,6 +229,7 @@ where is_secure: false, is_editable: false, is_read_only: false, + is_validated: None, font: None, width: Length::Fill, padding: [spacing, spacing, spacing, spacing].into(), @@ -298,6 +300,11 @@ where self } + pub fn validation_condition(mut self, validation_condition: bool) -> Self { + self.is_validated = Some(validation_condition); + self + } + /// Sets the message that should be produced when some text is typed into /// the [`TextInput`]. /// @@ -415,6 +422,7 @@ where self.font, self.on_input.is_none(), self.is_secure, + self.is_validated, self.leading_icon.as_ref(), self.trailing_icon.as_ref(), &self.style, @@ -806,6 +814,7 @@ where self.font, self.on_input.is_none(), self.is_secure, + self.is_validated, self.leading_icon.as_ref(), self.trailing_icon.as_ref(), &self.style, @@ -1961,6 +1970,7 @@ pub fn draw<'a, Message>( font: Option<::Font>, is_disabled: bool, is_secure: bool, + is_validated: Option, icon: Option<&Element<'a, Message, crate::Theme, crate::Renderer>>, trailing_icon: Option<&Element<'a, Message, crate::Theme, crate::Renderer>>, style: &::Style, @@ -2022,6 +2032,7 @@ pub fn draw<'a, Message>( let mut icon_color = appearance.icon_color.unwrap_or(renderer_style.icon_color); let mut text_color = appearance.text_color.unwrap_or(renderer_style.text_color); + let mut border_color = appearance.border_color; // TODO: iced will not render alpha itself on text or icon colors. if is_disabled { @@ -2030,6 +2041,12 @@ pub fn draw<'a, Message>( text_color = text_color.blend_alpha(background, 0.5); } + if is_validated.is_some() { + if !is_validated.unwrap_or_default() { + border_color = Color::from(palette::Srgb::new(0.890, 0.145, 0.420)); + } + } + // draw background and its border if let Some(border_offset) = appearance.border_offset { let offset_bounds = Rectangle { @@ -2058,7 +2075,7 @@ pub fn draw<'a, Message>( bounds: offset_bounds, border: Border { width: appearance.border_width, - color: appearance.border_color, + color: border_color, radius: appearance.border_radius, }, shadow: Shadow {