use super::{Filter, InterfaceItem, InterfaceItemBase};
use std::iter::repeat;
use crate::text_processing::{Processable, ProcessedChar, TextProcessor};
use crate::{Events, MouseButton, TextBuffer, TextStyle, VirtualKeyCode};
#[derive(Debug, Clone)]
pub struct TextInput {
pub unfocused_style: TextStyle,
pub focused_style: TextStyle,
pub button_press_inputs: Vec<VirtualKeyCode>,
pub mouse_button_press_inputs: Vec<MouseButton>,
base: InterfaceItemBase,
min_width: Option<u32>,
max_width: Option<u32>,
character_limit: Option<u32>,
text: String,
prefix: String,
suffix: String,
processed_text: Vec<ProcessedChar>,
needs_processing: bool,
text_width: u32,
pub filter: Filter,
was_just_pressed: bool,
pub caret: f32,
caret_timer: f32,
caret_showing: bool,
}
impl TextInput {
pub fn new<T: Into<Option<u32>>, U: Into<Option<u32>>>(
min_width: T,
max_width: U,
) -> TextInput {
let mut actual_min_width = None;
let mut actual_max_width = None;
if let Some(min_w) = min_width.into() {
actual_min_width = Some(min_w.max(1));
}
if let Some(max_w) = max_width.into() {
actual_max_width = Some(max_w.max(1));
}
TextInput {
unfocused_style: TextStyle {
bg_color: [0.0, 0.0, 0.0, 0.0],
fg_color: [0.8, 0.8, 0.8, 1.0],
..Default::default()
},
focused_style: TextStyle {
bg_color: [0.8, 0.8, 0.8, 1.0],
fg_color: [0.2, 0.2, 0.2, 1.0],
..Default::default()
},
base: InterfaceItemBase::new(true),
min_width: actual_min_width,
max_width: actual_max_width,
character_limit: None,
text: String::new(),
prefix: String::new(),
suffix: String::new(),
filter: Filter::empty_filter(),
processed_text: Vec::new(),
needs_processing: true,
text_width: 0,
button_press_inputs: vec![VirtualKeyCode::Return],
mouse_button_press_inputs: Vec::new(),
was_just_pressed: false,
caret: 0.5,
caret_timer: 0.0,
caret_showing: false,
}
}
with_base!(TextInput);
with_set_pressable!(TextInput);
with_style!(TextInput);
pub fn with_width<T: Into<Option<u32>>, U: Into<Option<u32>>>(
mut self,
min_width: T,
max_width: U,
) -> TextInput {
let mut actual_min_width = None;
let mut actual_max_width = None;
if let Some(min_w) = min_width.into() {
actual_min_width = Some(min_w.max(1));
}
if let Some(max_w) = max_width.into() {
actual_max_width = Some(max_w.max(1));
}
self.min_width = actual_min_width;
self.max_width = actual_max_width;
self
}
pub fn with_text<T: Into<String>>(mut self, text: T) -> TextInput {
self.text = text.into();
self.text_width = self.text.chars().count() as u32;
self
}
pub fn with_prefix<T: Into<String>>(mut self, prefix: T) -> TextInput {
self.prefix = prefix.into();
self
}
pub fn with_suffix<T: Into<String>>(mut self, suffix: T) -> TextInput {
self.suffix = suffix.into();
self
}
pub fn with_filter(mut self, filter: Filter) -> TextInput {
self.filter = filter;
self
}
pub fn with_caret(mut self, delay: f32) -> TextInput {
self.caret = delay;
self
}
pub fn with_character_limit<T: Into<Option<u32>>>(mut self, char_limit: T) -> TextInput {
self.character_limit = char_limit.into();
self
}
pub fn set_width<T: Into<Option<u32>>, U: Into<Option<u32>>>(
mut self,
min_width: T,
max_width: U,
) {
let mut actual_min_width = None;
let mut actual_max_width = None;
if let Some(min_w) = min_width.into() {
actual_min_width = Some(min_w.max(1));
}
if let Some(max_w) = max_width.into() {
actual_max_width = Some(max_w.max(1));
}
self.min_width = actual_min_width;
self.max_width = actual_max_width;
self.needs_processing = true;
self.base.dirty = true;
}
pub fn set_character_limit<T: Into<Option<u32>>>(mut self, char_limit: T) {
self.character_limit = char_limit.into();
}
pub fn set_text<T: Into<String>>(&mut self, text: T) {
self.text = text.into();
self.text_width = self.text.chars().count() as u32;
self.needs_processing = true;
self.base.dirty = true;
}
pub fn get_text(&self) -> String {
self.text.clone()
}
#[cfg(test)]
pub fn caret_showing(&self) -> bool {
self.caret_showing
}
}
impl InterfaceItem for TextInput {
fn get_base(&self) -> &InterfaceItemBase {
&self.base
}
fn get_mut_base(&mut self) -> &mut InterfaceItemBase {
&mut self.base
}
fn get_total_width(&self) -> u32 {
let text_width;
if let Some(max_width) = self.max_width {
text_width = max_width
} else if let Some(min_width) = self.min_width {
text_width = self.text_width.max(min_width);
} else {
text_width = self.text_width as u32;
}
(self.prefix.chars().count() + self.suffix.chars().count()) as u32 + text_width
}
fn get_total_height(&self) -> u32 {
1
}
fn draw(&mut self, text_buffer: &mut TextBuffer) {
self.base.dirty = false;
text_buffer.cursor.style = if self.base.is_focused() {
self.focused_style
} else {
self.unfocused_style
};
text_buffer.cursor.move_to(self.base.x, self.base.y);
text_buffer.write_processed(&self.processed_text);
}
fn handle_events(&mut self, events: &Events) -> bool {
self.was_just_pressed = false;
let mut handled = false;
if self.base.is_focused() {
for curr in &self.button_press_inputs {
if events.keyboard.was_just_pressed(*curr) {
self.was_just_pressed = true;
break;
}
}
for curr in &self.mouse_button_press_inputs {
if events.mouse.was_just_pressed(*curr) {
self.was_just_pressed = true;
break;
}
}
for character in events.chars.get_chars() {
if character == '\u{8}' {
self.text.pop();
}
if (self.character_limit.is_none()
|| self.character_limit.unwrap() > self.text_width)
&& self.filter.has(character)
{
self.text.push(character);
}
self.base.dirty = true;
self.needs_processing = true;
handled = true;
self.text_width = self.text.chars().count() as u32;
}
}
handled
}
fn update(&mut self, delta: f32, processor: &TextProcessor) {
if !self.base.is_focused() || self.caret == 0.0 {
if self.caret_showing {
self.needs_processing = true;
}
self.caret_timer = 0.0;
self.caret_showing = false;
} else {
self.caret_timer += delta;
if self.caret_timer >= self.caret {
self.caret_timer -= self.caret;
self.caret_showing = !self.caret_showing;
self.base.dirty = true;
self.needs_processing = true;
}
}
if self.needs_processing {
let text_w_offset: u32;
if self.base.is_focused() && self.caret != 0.0 {
text_w_offset = 1
} else {
text_w_offset = 0
}
let space_offset = if self.caret_showing { 1 } else { 0 };
let text_width;
let field_width;
if let (Some(min_width), Some(max_width)) = (self.min_width, self.max_width) {
text_width = (max_width - text_w_offset).min(self.text_width);
field_width = min_width.max(self.text_width).min(max_width);
} else if let Some(min_width) = self.min_width {
text_width = self.text_width;
field_width = min_width.max(self.text_width + text_w_offset);
} else if let Some(max_width) = self.max_width {
text_width = (max_width - text_w_offset).min(self.text_width);
field_width = max_width.min(self.text_width + 1);
} else {
text_width = self.text_width;
field_width = (self.text_width + text_w_offset).max(1);
}
let mut text: String = self.text.chars().take(text_width as usize).collect();
if self.caret_showing {
text.push('_');
}
let spaces: String = repeat(" ")
.take((field_width - text_width - space_offset) as usize)
.collect();
let text = text + &*spaces;
self.processed_text = processor.process(vec![
self.prefix.clone().into(),
Processable::NoProcess(text),
self.suffix.clone().into(),
]);
self.needs_processing = false;
}
}
}