use crossterm::event::{KeyEvent, KeyCode};
use tty_interface::{Interface, Position, pos};
use crate::{Control, Form};
pub trait Step {
fn initialize(&mut self);
fn render(&mut self, position: Position, interface: &mut Interface);
fn handle_input(&mut self, event: KeyEvent) -> Option<InputResult>;
fn add_to_form(self, form: &mut Form);
}
pub enum InputResult {
AdvanceForm,
RetreatForm,
}
pub struct CompoundStep {
controls: Vec<Box<dyn Control>>,
max_line_length: Option<u8>,
active_control: usize,
}
impl CompoundStep {
pub fn new() -> Self {
Self {
controls: Vec::new(),
max_line_length: None,
active_control: 0,
}
}
pub fn add_control(&mut self, control: Box<dyn Control>) {
self.controls.push(control);
}
pub fn set_max_line_length(&mut self, max_length: u8) {
self.max_line_length = Some(max_length);
}
fn advance_control(&mut self) -> bool {
loop {
if self.active_control + 1 >= self.controls.len() {
return true;
}
self.active_control += 1;
if self.controls[self.active_control].is_focusable() {
break
}
}
false
}
fn retreat_control(&mut self) -> bool {
loop {
if self.active_control == 0 {
return true;
}
self.active_control -= 1;
if self.controls[self.active_control].is_focusable() {
break
}
}
false
}
}
impl Step for CompoundStep {
fn initialize(&mut self) {
if !self.controls[0].is_focusable() {
self.advance_control();
}
}
fn render(&mut self, mut position: Position, interface: &mut Interface) {
let mut cursor_position = None;
for (control_index, control) in self.controls.iter().enumerate() {
let (text, cursor_offset) = control.get_text();
if control_index == self.active_control {
if let Some(offset) = cursor_offset {
cursor_position = Some(pos!(position.x() + offset, position.y()));
}
}
interface.set(position, &text);
position = pos!(position.x() + text.len() as u16, position.y());
}
interface.set_cursor(cursor_position);
}
fn handle_input(&mut self, key_event: KeyEvent) -> Option<InputResult> {
match (key_event.modifiers, key_event.code) {
(_, KeyCode::Enter) => {
if self.advance_control() {
return Some(InputResult::AdvanceForm);
}
}
(_, KeyCode::Esc) => {
if self.retreat_control() {
return Some(InputResult::RetreatForm);
}
}
_ => self.controls[self.active_control].handle_input(key_event),
}
None
}
fn add_to_form(self, form: &mut Form) {
form.add_step(Box::new(self));
}
}
pub struct TextBlockStep {
prompt: String,
max_line_length: Option<u8>,
}
impl TextBlockStep {
pub fn new(prompt: &str) -> Self {
Self {
prompt: prompt.to_string(),
max_line_length: None,
}
}
pub fn set_max_line_length(&mut self, max_length: u8) {
self.max_line_length = Some(max_length);
}
}
impl Step for TextBlockStep {
fn initialize(&mut self) {}
fn render(&mut self, position: Position, interface: &mut Interface) {
interface.set(position, "TextBlockStep");
}
fn handle_input(&mut self, event: KeyEvent) -> Option<InputResult> {
match (event.modifiers, event.code) {
(_, KeyCode::Enter) => {
Some(InputResult::AdvanceForm)
}
(_, KeyCode::Esc) => {
Some(InputResult::RetreatForm)
}
_ => None,
}
}
fn add_to_form(self, form: &mut Form) {
form.add_step(Box::new(self));
}
}