use ratatui::layout::Position;
#[derive(Debug, Default)]
pub struct InputNumberState
{
pub(crate) input: String,
pub(crate) character_index: usize,
pub(crate) cursor: Position,
pub(crate) clear_on_cancel: bool,
pub(crate) clear_on_confirm: bool,
pub(crate) max_length: Option<usize>,
pub(crate) max_value: Option<u32>,
pub(crate) min_value: Option<u32>,
pub(crate) right_aligned: bool,
}
impl InputNumberState
{
pub fn new() -> Self
{
Self::default()
}
pub fn right_aligned(mut self) -> Self
{
self.right_aligned = true;
self
}
pub fn set(
&mut self,
value: u32,
)
{
let text = format!("{value}");
self.clear();
self.write(text);
}
pub fn set_str<S>(
&mut self,
value: S,
) where
S: AsRef<str>,
{
self.clear();
self.write(value);
}
pub fn max(
mut self,
value: u32,
) -> Self
{
self.max_value = Some(value);
self
}
pub fn min(
mut self,
value: u32,
) -> Self
{
self.min_value = Some(value);
self
}
pub fn with_default(
mut self,
value: u32,
) -> Self
{
if let Some(max_length) = self.max_length
{
self.input = format!(
"{value:0>max_length$}",
max_length = max_length
);
}
else
{
self.input = format!("{value}");
}
self
}
pub fn with_max_length(
mut self,
length: usize,
) -> Self
{
self.max_length = Some(length);
self
}
pub fn write<S>(
&mut self,
text: S,
) where
S: AsRef<str>,
{
for c in text
.as_ref()
.chars()
{
self.enter_char(c);
}
}
pub fn input(&self) -> Option<u32>
{
self.input
.parse()
.ok()
}
pub fn set_clear_on_cancel(
&mut self,
value: bool,
)
{
self.clear_on_cancel = value;
}
pub fn with_clear_on_cancel(mut self) -> Self
{
self.set_clear_on_cancel(true);
self
}
pub fn clear_on_cancel(&self) -> bool
{
self.clear_on_cancel
}
pub fn set_clear_on_confirm(
&mut self,
value: bool,
)
{
self.clear_on_confirm = value;
}
pub fn with_clear_on_confirm(mut self) -> Self
{
self.set_clear_on_confirm(true);
self
}
pub fn clear_on_confirm(&self) -> bool
{
self.clear_on_confirm
}
pub fn cursor(&self) -> Position
{
self.cursor
}
pub fn clear(&mut self)
{
self.input
.clear();
self.reset_cursor();
}
pub(crate) fn move_cursor_left(&mut self)
{
let cursor_moved_left = self
.character_index
.saturating_sub(1);
self.character_index = self.clamp_cursor(cursor_moved_left);
}
pub(crate) fn move_cursor_start(&mut self)
{
self.character_index = self.clamp_cursor(0);
}
pub(crate) fn move_cursor_right(&mut self)
{
let cursor_moved_right = self
.character_index
.saturating_add(1);
self.character_index = self.clamp_cursor(cursor_moved_right);
}
pub(crate) fn move_cursor_end(&mut self)
{
self.character_index = self.clamp_cursor(
self.input
.chars()
.count(),
)
}
pub(crate) fn enter_char(
&mut self,
new_char: char,
)
{
if let Some(max_length) = self.max_length
{
if self
.input
.chars()
.count()
>= max_length
{
return;
}
}
if (new_char as u8) < 48 || (new_char as u8) > 57
{
return;
}
let index = self.byte_index();
if let Some(max) = self.max_value
{
let mut test = self
.input
.clone();
test.insert(
index, new_char,
);
let new_value: u32 = test
.parse()
.unwrap_or_default();
if new_value > max
{
return;
}
}
self.input
.insert(
index, new_char,
);
self.move_cursor_right();
}
fn byte_index(&mut self) -> usize
{
self.input
.char_indices()
.map(|(i, _)| i)
.nth(self.character_index)
.unwrap_or(
self.input
.len(),
)
}
pub(crate) fn supp_char(&mut self)
{
let is_not_cursor_rightmost = self.character_index
!= self
.input
.chars()
.count();
if is_not_cursor_rightmost
{
let before = self
.input
.chars()
.take(self.character_index);
let after = self
.input
.chars()
.skip(self.character_index + 1);
self.input = before
.chain(after)
.collect();
}
}
pub(crate) fn delete_char(&mut self)
{
let is_not_cursor_leftmost = self.character_index != 0;
if is_not_cursor_leftmost
{
let current_index = self.character_index;
let from_left_to_current_index = current_index - 1;
let before_char_to_delete = self
.input
.chars()
.take(from_left_to_current_index);
let after_char_to_delete = self
.input
.chars()
.skip(current_index);
self.input = before_char_to_delete
.chain(after_char_to_delete)
.collect();
self.move_cursor_left();
}
}
fn clamp_cursor(
&self,
new_cursor_pos: usize,
) -> usize
{
new_cursor_pos.clamp(
0,
self.input
.chars()
.count(),
)
}
pub fn reset_cursor(&mut self)
{
self.character_index = 0;
}
}