linebender / druid

A data-first Rust-native UI design toolkit.
https://linebender.org/druid/
Apache License 2.0
9.53k stars 569 forks source link

TextBox loses its focus on Enter press when having a formatter #1975

Open Flone-dnb opened 3 years ago

Flone-dnb commented 3 years ago

I've created a simple formatter to limit the maximum number of characters on TextBox but my custom controller on this TextBox stopped working. My controller sends a message when Enter is pressed but now, with formatter, when I press Enter on this TextBox the item loses its focus so my controller is not seeing this Enter press.

The minimal example to reproduce this issue is provided below (see Maan2003's comment).

maan2003 commented 3 years ago

smaller example to reproduce; press enter in the textbox and it will lose focus

use druid::widget::TextBox;
use druid::{AppLauncher, PlatformError, WidgetExt, WindowDesc};

use druid::text::{Formatter, Selection, Validation, ValidationError};

pub struct NoopFormatter;

impl Formatter<String> for NoopFormatter {
    fn format(&self, value: &String) -> String {
        value.to_owned()
    }

    fn value(&self, input: &str) -> Result<String, ValidationError> {
        Ok(input.to_owned())
    }

    fn validate_partial_input(&self, _input: &str, _sel: &Selection) -> Validation {
        Validation::success()
    }
}

fn main() -> Result<(), PlatformError> {
    let main_window = WindowDesc::new(TextBox::new().with_formatter(NoopFormatter).center());
    AppLauncher::with_window(main_window)
        .log_to_console()
        .launch(String::new())
}
maan2003 commented 3 years ago

ok so this is explicit, it doesn't make much sense. @cmyr can you explain the reasoning, the commit message doesn't help much https://github.com/linebender/druid/blob/067b187d144e8e190b42319521161f6da2cd2204/druid/src/widget/value_textbox.rs#L247-L251

Flone-dnb commented 3 years ago

Look at this one also.

use druid::widget::{Widget, Flex, TextBox, Label};
use druid::{AppLauncher, Data, PlatformError, Lens, WidgetExt, WindowDesc};

use druid::text::{Formatter, Selection, Validation, ValidationError};

#[derive(Data, Clone, Lens)]
pub struct AppState
{
    pub text: String,
}

pub struct NoopFormatter;

impl Formatter<String> for NoopFormatter {
    fn format(&self, value: &String) -> String {
        value.to_owned()
    }

    fn value(&self, input: &str) -> Result<String, ValidationError> {
        Ok(input.to_owned())
    }

    fn validate_partial_input(&self, _input: &str, _sel: &Selection) -> Validation {
        Validation::success()
    }
}

fn main() -> Result<(), PlatformError> {
    let main_window = WindowDesc::new(ui_builder());
    AppLauncher::with_window(main_window)
        .log_to_console()
        .launch(AppState{text: "".to_string()})
}

fn ui_builder() -> impl Widget<AppState>{
    Flex::column()
    .with_child(TextBox::new().with_formatter(NoopFormatter{}).lens(AppState::text))
    .with_child(Label::new(|data: &AppState, _env: &_| format!("Text: {}", &data.text)))
}

With formatter you have to press Enter to update the data.text member, without pressing Enter it will not update. I just stumbled upon this. I don't want users to remember to press Enter after the text is changed.

cmyr commented 3 years ago

Have you seen the validate_while_editing flag you can set on ValueTextBox?

Flone-dnb commented 3 years ago

update_data_while_editing(true) fixed my issue (shouldn't that be the default?) related to update, the losing focus on Enter is still a thing (see @Maan2003 code sample).