move_timer 0.1.0

a small, portable desktop app reminding you to move a bit during ctivities like programming
use xilem::{
    WidgetView,
    view::{GridExt, GridParams, button, grid, label, text_input},
};

use crate::{BUTTON_TEXT_SIZE, data::AppData};

//TODO: enforce invariant `AppData::input_duration() != Duration::ZERO` application-wide
pub(crate) fn time_input(data: &mut AppData) -> impl WidgetView<AppData> + use<> {
    grid(
        (
            grid(
                (
                    text_input(
                        data.hour_input.clone(),
                        |data: &mut AppData, new_content: String| {
                            let mut numbers = keep_digits(new_content);

                            //0 if it's an empty string or overflow
                            data.hour_parsed = numbers.as_str().parse().unwrap_or(0);

                            if !numbers.is_empty() {
                                numbers.push('h');
                            }
                            data.hour_input = numbers;
                        },
                    )
                    .grid_item(GridParams::new(0, 0, 1, 1)),
                    text_input(
                        data.min_input.clone(),
                        |data: &mut AppData, new_content: String| {
                            let mut numbers = keep_digits(new_content);

                            //0 if it's an empty string or overflow
                            data.min_parsed = numbers.as_str().parse().unwrap_or(0);

                            if !numbers.is_empty() {
                                numbers.push('m');
                            }
                            data.min_input = numbers;
                        },
                    )
                    .grid_item(GridParams::new(0, 1, 1, 1)),
                    text_input(
                        data.sec_input.clone(),
                        |data: &mut AppData, new_content: String| {
                            let mut numbers = keep_digits(new_content);

                            //0 if it's an empty string or overflow
                            data.sec_parsed = numbers.as_str().parse().unwrap_or(0);

                            if !numbers.is_empty() {
                                numbers.push('s');
                            }
                            data.sec_input = numbers;
                        },
                    )
                    .grid_item(GridParams::new(0, 2, 1, 1)),
                ),
                1,
                3,
            )
            .grid_item(GridParams::new(0, 0, 1, 1)),
            button(
                label("Apply").text_size(BUTTON_TEXT_SIZE),
                |data: &mut AppData| {
                    data.set_new_duration(data.input_duration());
                },
            )
            .disabled(data.total == data.input_duration() || data.input_duration().is_zero())
            .grid_item(GridParams::new(0, 1, 1, 1)),
        ),
        1,
        2,
    )
}

//extremely efficient
fn keep_digits(input: String) -> String {
    let mut output = String::new();

    for character in input.chars() {
        if ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'].contains(&character) {
            output.push(character);
        }
    }

    output
}