mirror of
https://github.com/danbulant/cushy
synced 2026-07-05 19:20:36 +00:00
Button.enabled
This commit is contained in:
parent
a8cd3df22c
commit
747813f6b8
2 changed files with 75 additions and 27 deletions
|
|
@ -10,23 +10,7 @@ fn main() -> gooey::Result {
|
||||||
let username = Dynamic::default();
|
let username = Dynamic::default();
|
||||||
let password = Dynamic::default();
|
let password = Dynamic::default();
|
||||||
|
|
||||||
// TODO This is absolutely horrible. The problem is that within for_each,
|
let valid = setup_validation(&username, &password);
|
||||||
// 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)))
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
Expand::new(Align::centered(Resize::width(
|
Expand::new(Align::centered(Resize::width(
|
||||||
// TODO We need a min/max range for the Resize widget
|
// TODO We need a min/max range for the Resize widget
|
||||||
|
|
@ -55,13 +39,10 @@ fn main() -> gooey::Result {
|
||||||
.into_escape(),
|
.into_escape(),
|
||||||
Expand::empty(),
|
Expand::empty(),
|
||||||
Button::new("Log In")
|
Button::new("Log In")
|
||||||
|
.enabled(valid)
|
||||||
.on_click(move |_| {
|
.on_click(move |_| {
|
||||||
if valid.get() {
|
println!("Welcome, {}", username.get());
|
||||||
println!("Welcome, {}", username.get());
|
exit(0);
|
||||||
exit(0);
|
|
||||||
} else {
|
|
||||||
eprintln!("Enter a username and password")
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.into_default(), // TODO enable/disable based on valid
|
.into_default(), // TODO enable/disable based on valid
|
||||||
]),
|
]),
|
||||||
|
|
@ -73,3 +54,24 @@ fn main() -> gooey::Result {
|
||||||
fn validate(username: &String, password: &String) -> bool {
|
fn validate(username: &String, password: &String) -> bool {
|
||||||
!username.is_empty() && !password.is_empty()
|
!username.is_empty() && !password.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn setup_validation(username: &Dynamic<String>, password: &Dynamic<String>) -> Dynamic<bool> {
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,9 @@ pub struct Button {
|
||||||
pub label: Value<String>,
|
pub label: Value<String>,
|
||||||
/// The callback that is invoked when the button is clicked.
|
/// The callback that is invoked when the button is clicked.
|
||||||
pub on_click: Option<Callback<()>>,
|
pub on_click: Option<Callback<()>>,
|
||||||
|
/// The enabled state of the button.
|
||||||
|
pub enabled: Value<bool>,
|
||||||
|
currently_enabled: bool,
|
||||||
buttons_pressed: usize,
|
buttons_pressed: usize,
|
||||||
background_color: Option<Dynamic<Color>>,
|
background_color: Option<Dynamic<Color>>,
|
||||||
background_color_animation: AnimationHandle,
|
background_color_animation: AnimationHandle,
|
||||||
|
|
@ -39,6 +42,8 @@ impl Button {
|
||||||
Self {
|
Self {
|
||||||
label: label.into_value(),
|
label: label.into_value(),
|
||||||
on_click: None,
|
on_click: None,
|
||||||
|
enabled: Value::Constant(true),
|
||||||
|
currently_enabled: true,
|
||||||
buttons_pressed: 0,
|
buttons_pressed: 0,
|
||||||
background_color: None,
|
background_color: None,
|
||||||
background_color_animation: AnimationHandle::default(),
|
background_color_animation: AnimationHandle::default(),
|
||||||
|
|
@ -57,9 +62,19 @@ impl Button {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the value to use for the button's enabled status.
|
||||||
|
#[must_use]
|
||||||
|
pub fn enabled(mut self, enabled: impl IntoValue<bool>) -> Self {
|
||||||
|
self.enabled = enabled.into_value();
|
||||||
|
self.currently_enabled = self.enabled.get();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
fn invoke_on_click(&mut self) {
|
fn invoke_on_click(&mut self) {
|
||||||
if let Some(on_click) = self.on_click.as_mut() {
|
if self.enabled.get() {
|
||||||
on_click.invoke(());
|
if let Some(on_click) = self.on_click.as_mut() {
|
||||||
|
on_click.invoke(());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,10 +83,13 @@ impl Button {
|
||||||
&ButtonActiveBackground,
|
&ButtonActiveBackground,
|
||||||
&ButtonBackground,
|
&ButtonBackground,
|
||||||
&ButtonHoverBackground,
|
&ButtonHoverBackground,
|
||||||
|
&ButtonDisabledBackground,
|
||||||
&PrimaryColor,
|
&PrimaryColor,
|
||||||
&Easing,
|
&Easing,
|
||||||
]);
|
]);
|
||||||
let background_color = if context.active() {
|
let background_color = if !self.enabled.get() {
|
||||||
|
styles.get_or_default(&ButtonDisabledBackground)
|
||||||
|
} else if context.active() {
|
||||||
styles.get_or_default(&ButtonActiveBackground)
|
styles.get_or_default(&ButtonActiveBackground)
|
||||||
} else if context.hovered() {
|
} else if context.hovered() {
|
||||||
styles.get_or_default(&ButtonHoverBackground)
|
styles.get_or_default(&ButtonHoverBackground)
|
||||||
|
|
@ -113,9 +131,18 @@ impl Button {
|
||||||
|
|
||||||
impl Widget for Button {
|
impl Widget for Button {
|
||||||
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) {
|
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) {
|
||||||
|
let enabled = self.enabled.get();
|
||||||
|
// TODO This seems ugly. It needs context, so it can't be moved into the
|
||||||
|
// dynamic system.
|
||||||
|
if self.currently_enabled != enabled {
|
||||||
|
self.update_background_color(context, false);
|
||||||
|
self.currently_enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
let size = context.graphics.region().size;
|
let size = context.graphics.region().size;
|
||||||
let center = Point::from(size) / 2;
|
let center = Point::from(size) / 2;
|
||||||
self.label.redraw_when_changed(context);
|
self.label.redraw_when_changed(context);
|
||||||
|
self.enabled.redraw_when_changed(context);
|
||||||
|
|
||||||
let styles = context.query_styles(&[
|
let styles = context.query_styles(&[
|
||||||
&TextColor,
|
&TextColor,
|
||||||
|
|
@ -155,7 +182,7 @@ impl Widget for Button {
|
||||||
|
|
||||||
fn accept_focus(&mut self, _context: &mut EventContext<'_, '_>) -> bool {
|
fn accept_focus(&mut self, _context: &mut EventContext<'_, '_>) -> bool {
|
||||||
// TODO this should be driven by a "focus_all_widgets" setting that hopefully can be queried from the OS.
|
// TODO this should be driven by a "focus_all_widgets" setting that hopefully can be queried from the OS.
|
||||||
true
|
self.enabled.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mouse_down(
|
fn mouse_down(
|
||||||
|
|
@ -354,3 +381,22 @@ impl ComponentDefinition for ButtonHoverBackground {
|
||||||
Color::new(40, 40, 40, 255)
|
Color::new(40, 40, 40, 255)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The background color of the button when the mouse cursor is hovering over
|
||||||
|
/// it.
|
||||||
|
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
|
||||||
|
pub struct ButtonDisabledBackground;
|
||||||
|
|
||||||
|
impl NamedComponent for ButtonDisabledBackground {
|
||||||
|
fn name(&self) -> Cow<'_, ComponentName> {
|
||||||
|
Cow::Owned(ComponentName::named::<Button>("disabled_background_color"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComponentDefinition for ButtonDisabledBackground {
|
||||||
|
type ComponentType = Color;
|
||||||
|
|
||||||
|
fn default_value(&self) -> Color {
|
||||||
|
Color::new(50, 30, 30, 255)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue