use macroquad::prelude::*;
#[derive(Clone, Default, PartialEq)]
pub enum InputState {
#[default]
Default,
Hovering,
Pressed,
}
#[derive(Clone, Default)]
pub struct TextBoxBase {
pub rect: Rect,
pub color: Color,
pub text: String,
pub state: InputState,
pub editing: bool,
pub placeholder: String,
pub edit_color: Color,
pub last_key: (Option<KeyCode>, i32),
font: Option<Font>,
pub last_click_time: f64,
}
impl TextBoxBase {
pub fn new(rect: Rect, placeholder: &str, font: Option<Font>) -> Self {
Self {
font,
rect,
color: Color::from_vec(Vec4::splat(0.3)),
edit_color: Color::new(0.4, 0.3, 0.2, 0.5),
placeholder: placeholder.to_string(),
..Default::default()
}
}
pub fn update(&mut self, selected: bool) {
self.update_state(selected);
if self.editing {
self.take_input()
}
}
fn update_state(&mut self, selected: bool) {
if is_mouse_button_released(MouseButton::Left) {
self.state = InputState::Default;
}
if self.rect.contains(mouse_position().into()) {
if is_mouse_button_pressed(MouseButton::Left) && selected {
self.state = InputState::Pressed;
self.editing = true;
self.last_click_time = get_time() * 2.;
} else {
if !is_mouse_button_down(MouseButton::Left) {
self.state = InputState::Hovering;
}
}
} else {
if self.state != InputState::Pressed {
self.state = InputState::Default
}
if is_mouse_button_pressed(MouseButton::Left) {
self.editing = false;
}
}
}
fn take_input(&mut self) {
if let Some(key) = get_last_key_pressed() {
self.last_key.1 = -30;
self.last_key.0 = Some(key);
if let Some(ch) = get_char_pressed() {
self.key_pressed(key, ch.is_uppercase());
} else {
self.key_pressed(key, false);
}
}
self.last_key.1 += 1;
if let Some(key) = self.last_key.0 {
if self.last_key.1 > 3 && is_key_down(key) {
if let Some(ch) = get_char_pressed() {
self.key_pressed(key, ch.is_uppercase());
} else {
self.key_pressed(key, false);
}
}
}
}
pub fn key_pressed(&mut self, key: KeyCode, upper: bool) {
let key_name = format!("{:?}", key).to_lowercase();
if key_name == "backspace" {
self.text.pop();
return;
}
let str_key = Self::handle_shift(
&key_name,
match key_name.as_str() {
"space" => " ",
"minus" => "-",
"equal" => "=",
"leftbracket" => "[",
"rightbracket" => "]",
"semicolon" => ";",
"apostrophe" => "'",
"backslash" => "\\",
"comma" => ",",
"period" => ".",
"slash" => "/",
"graveaccent" => "`",
other => other,
},
upper,
);
let character = str_key.chars().last().unwrap();
if str_key.starts_with("key") || str_key.len() == 1 {
self.text.push(character);
}
}
pub fn handle_shift(key_name: &String, str_key: &str, upper: bool) -> String {
if !(upper || is_key_down(KeyCode::LeftShift) || is_key_down(KeyCode::RightShift)) {
return str_key.to_string();
}
match key_name.as_str() {
"key1" => "!",
"key2" => "@",
"key3" => "#",
"key4" => "$",
"key5" => "%",
"key6" => "^",
"key7" => "&",
"key8" => "*",
"key9" => "(",
"key0" => ")",
"a" => "A",
"b" => "B",
"c" => "C",
"d" => "D",
"e" => "E",
"f" => "F",
"g" => "G",
"h" => "H",
"i" => "I",
"j" => "J",
"k" => "K",
"l" => "L",
"m" => "M",
"n" => "N",
"o" => "O",
"p" => "P",
"q" => "Q",
"r" => "R",
"s" => "S",
"t" => "T",
"u" => "U",
"v" => "V",
"w" => "W",
"x" => "X",
"y" => "Y",
"z" => "Z",
"minus" => "_",
"equal" => "+",
"leftbracket" => "{",
"rightbracket" => "}",
"semicolon" => ":",
"apostrophe" => "\"",
"backslash" => "|",
"comma" => "<",
"period" => ">",
"slash" => "?",
"graveaccent" => "~",
_ => str_key,
}
.to_string()
}
pub fn render(&self) {
let mut bg_color = match self.state {
InputState::Hovering => mul_color(self.color, 1.3),
InputState::Pressed => mul_color(self.color, 0.7),
_ => self.color,
};
if self.editing {
bg_color = self.edit_color
}
draw_rectangle(self.rect.x, self.rect.y, self.rect.w, self.rect.h, bg_color);
draw_rectangle_lines(
self.rect.x,
self.rect.y,
self.rect.w,
self.rect.h,
2.,
BLACK,
);
let drawn_text = match self.text.len() > 0 {
true => &self.text,
_ => &self.placeholder
};
let font_size = self.rect.h as u16;
if let Some(_) = self.font {
draw_text_ex(
drawn_text,
(self.rect.x + 10.).floor(),
(self.rect.y + self.rect.h - 5.).floor(),
TextParams {
font_size: font_size - 5,
color: match self.text.len() > 0 {
true => WHITE,
_ => Color::new(1.,1.,1.,0.5)
},
font: Some(&self.font.clone().unwrap()),
..Default::default()
},
);
} else {
draw_text(
drawn_text,
(self.rect.x + 10.).floor(),
(self.rect.y + self.rect.h - 5.).floor(),
font_size as f32,
match self.text.len() > 0 {
true => WHITE,
_ => Color::new(1.,1.,1.,0.5)
}
);
}
if self.editing {
if (get_time() * 2. + self.last_click_time) as usize % 2 > 0 {
let text_dimensions = match &self.font {
Some(f) => measure_text(
&self.text,
Some(&f.clone()),
font_size - 5,
1.,
),
_ => measure_text(
&self.text,
None,
font_size,
1.,
)
};
let caret_x = self.rect.x + 10. + text_dimensions.width;
draw_line(
caret_x,
self.rect.y + self.rect.h - 3.0,
caret_x,
self.rect.y + 3.0,
1.,
WHITE,
);
}
}
}
}
fn mul_color(color: Color, mul: f32) -> Color {
Color::from_vec(color.to_vec() * vec4(mul, mul, mul, 1.))
}