use std::process::exit; use gooey::value::Dynamic; use gooey::widget::MakeWidget; use gooey::widgets::{Align, Button, Expand, Input, Label, Resize, Stack}; use gooey::{children, Run, WithClone}; use kludgine::figures::units::Lp; fn main() -> gooey::Result { let username = Dynamic::default(); let password = Dynamic::default(); let valid = setup_validation(&username, &password); Expand::new(Align::centered(Resize::width( // TODO We need a min/max range for the Resize widget Lp::points(400), Stack::rows(children![ Stack::columns(children![ Label::new("Username"), Expand::new(Align::centered(Input::new(username.clone())).fit_horizontally()), ]), Stack::columns(children![ Label::new("Password"), Expand::new( Align::centered( // TODO secure input Input::new(password.clone()) ) .fit_horizontally() ), ]), Stack::columns(children![ Button::new("Cancel") .on_click(|_| { eprintln!("Login cancelled"); exit(0) }) .into_escape(), Expand::empty(), Button::new("Log In") .enabled(valid) .on_click(move |_| { println!("Welcome, {}", username.get()); exit(0); }) .into_default(), // TODO enable/disable based on valid ]), ]), ))) .run() } fn validate(username: &String, password: &String) -> bool { !username.is_empty() && !password.is_empty() } fn setup_validation(username: &Dynamic, password: &Dynamic) -> Dynamic { // TODO This is absolutely horrible. The problem is that within for_each, // the value is still locked. Thus, we can't have a generic callback that // tries to lock the value that is being mapped in for_each. // // We might be able to make a genericized implementation for_each for // tuples, ie, (&Dynamic, &Dynamic).for_each(|(a, b)| ..). let valid = Dynamic::default(); username.for_each((&valid, password).with_clone(|(valid, password)| { move |username: &String| { password.map_ref(|password| valid.update(validate(username, password))) } })); password.for_each((&valid, username).with_clone(|(valid, username)| { move |password: &String| { username.map_ref(|username| valid.update(validate(username, password))) } })); valid }