rtlibs-tui 0.1.5

rtools library: ratatui widgets
Documentation
use ratatui::layout::Position;

use crate::widgets::InputNumberState;

#[derive(Debug)]
pub(crate) enum InputCurrencyFocus
{
    Integral,
    Decimal,
}

#[derive(Debug)]
pub struct InputCurrencyState
{
    pub(crate) label: Option<String>,
    pub(crate) input_mode: InputCurrencyFocus,
    pub(crate) is_negative: bool,
    pub(crate) input_integral: InputNumberState,
    pub(crate) input_decimal: InputNumberState,
    pub(crate) min_value: Option<i32>,
    pub(crate) max_value: Option<i32>,
}

impl std::default::Default for InputCurrencyState
{
    fn default() -> Self
    {
        Self::new()
    }
}

impl InputCurrencyState
{
    pub fn new() -> Self
    {
        Self {
            label: None,
            input_mode: InputCurrencyFocus::Integral,
            is_negative: false,
            input_integral: InputNumberState::new()
                .right_aligned()
                .with_max_length(8),
            input_decimal: InputNumberState::new().with_max_length(2),
            min_value: None,
            max_value: None,
        }
    }

    pub fn input(&self) -> Option<i32>
    {
        let integral = match self
            .input_integral
            .input()
            .and_then(|i| i.checked_mul(100))
        {
            Some(value) => value,
            None => return None,
        };

        let decimal = match self
            .input_decimal
            .input()
            .filter(|i| *i <= 99)
        {
            Some(value) => value,
            None => return None,
        };

        let mut value: i32 = match integral
            .checked_add(decimal)
            .and_then(
                |i| {
                    i.try_into()
                        .ok()
                },
            )
        {
            Some(value) => value,
            None => return None,
        };

        if self.is_negative
        {
            value *= -1;
        }

        if let Some(min) = self.min_value
        {
            if value < min
            {
                return None;
            }
        }

        if let Some(max) = self.max_value
        {
            if value > max
            {
                return None;
            }
        }

        Some(value)
    }

    pub fn set_label<S>(
        &mut self,
        label: S,
    ) where
        S: AsRef<str>,
    {
        self.label = Some(
            label
                .as_ref()
                .to_string(),
        );
    }

    pub fn remove_label(&mut self)
    {
        self.label
            .take();
    }

    pub fn with_label<S>(
        mut self,
        label: S,
    ) -> Self
    where
        S: AsRef<str>,
    {
        self.set_label(label);
        self
    }

    pub fn max(
        mut self,
        value: i32,
    ) -> Self
    {
        self.max_value = Some(value);
        self
    }

    pub fn min(
        mut self,
        value: i32,
    ) -> Self
    {
        self.min_value = Some(value);
        self
    }

    pub fn set(
        &mut self,
        value: i32,
    )
    {
        let integral = (value / 100).unsigned_abs();
        let decimal = value.unsigned_abs() - (integral * 100);
        let is_negative = value < 0;

        self.is_negative = is_negative;
        self.input_mode = InputCurrencyFocus::Integral;
        self.input_integral
            .set(integral);
        self.input_decimal
            .set_str(format!("{decimal:02}"));
        if integral == 0
        {
            self.input_integral
                .set_str("");
        }
        if decimal == 0
        {
            self.input_decimal
                .set_str("");
        }
    }

    pub fn reset_cursor(&mut self)
    {
        self.input_integral
            .reset_cursor();
        self.input_decimal
            .reset_cursor();
    }

    pub fn with_default(
        mut self,
        value: i32,
    ) -> Self
    {
        self.set(value);
        self
    }

    pub fn cursor(&self) -> Position
    {
        match self.input_mode
        {
            InputCurrencyFocus::Integral => self
                .input_integral
                .cursor(),
            InputCurrencyFocus::Decimal => self
                .input_decimal
                .cursor(),
        }
    }

    pub fn clear(&mut self)
    {
        self.input_integral
            .clear();
        self.input_decimal
            .clear();
    }
}