New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added conditional function 'disabled_if' to disable the button based on a true boolean #2365
base: master
Are you sure you want to change the base?
Conversation
…on a true boolean
Rust's let enabled = false;
button("hi").on_press_maybe(enabled.then_some(Message::Hi)); |
Here's a brief explanation of what motivated me in this PR let user_input = String::new(); // it will most likely already exist somewhere
button("hi").on_press(Message::Hi).disabled_if(user_input.is_empty()); with on_press_maybe I would need to do this in its cleanest form let user_input = String::new();
button("hi").on_press_maybe((!user_input.is_empty()).then_some(Message::Hi)); This implies a conversion from true to false, which in the end ends up being a more "complicated" solution. For me, "disabled if" is more natural and concise, as it fits well with button modeling |
Ah I see. While you can use variables to help reduce cognitive load in the latter example: let password_entered = !password.is_empty();
button("Login")
.on_press_maybe(password_entered.then_some(Message::Login)) I do think your proposed method would definitely improve developer experience. Actually, I really like the explicit separation: button("Login")
.disable_if(password.is_empty())
.on_press(Message::Login) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR seems to have the changes from #2361. Can we get rid of those here?
That's not going to work the way you think it will. That won't disable the button. The One way to do it would be by adding a pub struct Button<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer>
where
Renderer: crate::core::Renderer,
Theme: Catalog,
{
content: Element<'a, Message, Theme, Renderer>,
on_press: Option<Message>,
width: Length,
height: Length,
padding: Padding,
clip: bool,
disabled: bool,
class: Theme::Class<'a>,
} /// Creates a new [`Button`] with the given content.
pub fn new(
content: impl Into<Element<'a, Message, Theme, Renderer>>,
) -> Self {
let content = content.into();
let size = content.as_widget().size_hint();
Button {
content,
on_press: None,
width: size.width.fluid(),
height: size.height.fluid(),
padding: DEFAULT_PADDING,
clip: false,
disabled: false,
class: Theme::default(),
}
} And then the pub fn disable_if(mut self, disable: bool) -> Self {
self.disabled = disable;
self
} Of course that would mean that you would have to change the fn on_event(...) -> ... {
...
match event {
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
| Event::Touch(touch::Event::FingerPressed { .. }) => {
if self.on_press.is_some() { //HERE needs to change to `if !self.disabled && self.on_press.is_some() {`
....
}
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
| Event::Touch(touch::Event::FingerLifted { .. }) => {
if let Some(on_press) = self.on_press.clone() { //HERE this entire if statement needs to be wrapped on another `if !self.disabled {`
....
}
...
}
fn draw(...) {
....
let status = if self.on_press.is_none() { //HERE needs to change to `if self.disabled || self.on_press.is_none()`
Status::Disabled
} else if is_mouse_over {
....
}
fn mouse_interaction(...) -> ... {
let is_mouse_over = cursor.is_over(layout.bounds());
if is_mouse_over && self.on_press.is_some() { //HERE needs to change to `if is_mouse_over && !self.disabled && self.on_press.is_some() {`
mouse::Interaction::Pointer
} else {
mouse::Interaction::default()
}
} Even though this is a bigger change I feel it would result in a better experience for the developer, because you could setup the I also think the name should be |
Oh right! Thanks for pointing that out. Instead of having these throughout the codebase: self.disabled || self.on_press.is_none()
// or
!self.disabled && self.on_press.is_some() We can introduce a private helper method to get the fn message(&self) -> Option<Message> {
(!self.disabled)
.then_some(self.on_press)
.flatten()
} So hopefully the only thing we need to do is use the |
This might not be the best solution because we would have to clone the option fn is_disabled(&self) -> bool {
self.disabled || self.on_press.is_none()
} Then we would just use it as: self.is_disabled()
// or
!self.is_disabled() Or we can also always have an self.is_disabled()
// or
self.is_enabled() |
Ah, I forgot to add fn message(&self) -> Option<&Message> {
(!self.disabled)
.then_some(self.on_press.as_ref())
.flatten()
} I still think using |
I think it's as simple as making |
That is definitely much simpler! 😄 The only difference is that it wouldn't keep the |
Yeah, this approach is so much better. |
Yes, of course you can! |
Hahaha, I have difficulty expressing myself sometimes, my native language is Brazilian Portuguese, for me it is natural as "desabilitado se (for verdadeiro)". About the call order being ..on_press(..).disabled_if(...) is mentioned in the documentation, in my opinion, it is something extremely simple, I thought about including something more elaborate to be used in any order, but the real It's just that this type of behavior replacement happens frequently and in several libs and languages in general, adjusting the way you said would be adding more stress to the processor and RAM for something that the next programmer can control in a simpler way, I'll say it again , this is specified in the documentation, so in my humble opinion it is not a problem. |
For me it's fine either way, but I prefer to use the verb since when reading the code it would read like a sentence. In Portuguese it would be 'desabilitar_se(verdadeiro)'. Maybe @hecrj can give his opinion.
Most people don't read the docs so I bet you there will be a couple of issues showing up saying "hey disable_if doesn't work", so every time we can prevent non-issues from being created is a plus. Also there's the possibility that in some complex apps a mut button can be created somewhere with The solution proposed by @hecrj is really simple and solves this problems. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed files from previous pull request that included an example for keyed_column
Added function to disable the button in a more elegant way, simply entering the Boolean value true to make the button inactive.
If false is entered, nothing will be affected.