use crate::data::StructuredNote; use ratatui::{ layout::Rect, style::{Color, Modifier, Style}, text::{Line, Span}, widgets::{Block, Borders, Paragraph}, Frame, }; use std::collections::HashMap; #[derive(Debug, PartialEq, Clone, Copy)] pub enum InputField { Switch1, Switch2, Switches, Select, Number, Value1, Value2, DirectSelect, MidiFunction, MidiChan, Config, ExpressionPedalA, ExpressionPedalB, Display1, Display2, Display3, ButtonLeds, ButtonPresses, } impl InputField { pub fn next(&self) -> Self { match self { InputField::Switch1 => InputField::Switch2, InputField::Switch2 => InputField::Switches, InputField::Switches => InputField::Select, InputField::Select => InputField::Number, InputField::Number => InputField::Value1, InputField::Value1 => InputField::Value2, InputField::Value2 => InputField::DirectSelect, InputField::DirectSelect => InputField::MidiFunction, InputField::MidiFunction => InputField::MidiChan, InputField::MidiChan => InputField::Config, InputField::Config => InputField::ExpressionPedalA, InputField::ExpressionPedalA => InputField::ExpressionPedalB, InputField::ExpressionPedalB => InputField::Display1, InputField::Display1 => InputField::Display2, InputField::Display2 => InputField::Display3, InputField::Display3 => InputField::ButtonLeds, InputField::ButtonLeds => InputField::ButtonPresses, InputField::ButtonPresses => InputField::Switch1, // Wrap around } } pub fn prev(&self) -> Self { match self { InputField::Switch1 => InputField::ButtonPresses, InputField::Switch2 => InputField::Switch1, InputField::Switches => InputField::Switch2, InputField::Select => InputField::Switches, InputField::Number => InputField::Select, InputField::Value1 => InputField::Number, InputField::Value2 => InputField::Value1, InputField::DirectSelect => InputField::Value2, InputField::MidiFunction => InputField::DirectSelect, InputField::MidiChan => InputField::MidiFunction, InputField::Config => InputField::MidiChan, InputField::ExpressionPedalA => InputField::Config, InputField::ExpressionPedalB => InputField::ExpressionPedalA, InputField::Display1 => InputField::ExpressionPedalB, InputField::Display2 => InputField::Display1, InputField::Display3 => InputField::Display2, InputField::ButtonLeds => InputField::Display3, InputField::ButtonPresses => InputField::ButtonLeds, } } pub fn name(&self) -> &str { match self { InputField::Switch1 => "Switch 1", InputField::Switch2 => "Switch 2", InputField::Switches => "Switches", InputField::Select => "Select", InputField::Number => "Number", InputField::Value1 => "Value 1", InputField::Value2 => "Value 2", InputField::DirectSelect => "Direct Select", InputField::MidiFunction => "MIDI Function", InputField::MidiChan => "MIDI Chan", InputField::Config => "Config", InputField::ExpressionPedalA => "Expression Pedal A", InputField::ExpressionPedalB => "Expression Pedal B", InputField::Display1 => "Display 1", InputField::Display2 => "Display 2", InputField::Display3 => "Display 3", InputField::ButtonLeds => "Button LEDs", InputField::ButtonPresses => "Button Presses", } } pub fn is_led_field(&self) -> bool { matches!( self, InputField::Switch1 | InputField::Switch2 | InputField::Switches | InputField::Select | InputField::Number | InputField::Value1 | InputField::Value2 | InputField::DirectSelect | InputField::MidiFunction | InputField::MidiChan | InputField::Config | InputField::ExpressionPedalA | InputField::ExpressionPedalB ) } pub fn is_text_field(&self) -> bool { matches!( self, InputField::Display1 | InputField::Display2 | InputField::Display3 | InputField::ButtonLeds | InputField::ButtonPresses ) } } pub struct StructuredInputForm { pub current_note: StructuredNote, pub current_field: InputField, pub cursor_position: usize, } impl StructuredInputForm { pub fn new(register_values: HashMap) -> Self { let mut current_note = StructuredNote::default(); current_note.register_values = register_values; Self { current_note, current_field: InputField::Switch1, cursor_position: 0, } } pub fn reset(&mut self, register_values: HashMap) { self.current_note = StructuredNote::default(); self.current_note.register_values = register_values; self.current_field = InputField::Switch1; self.cursor_position = 0; } pub fn get_led_value(&self, field: &InputField) -> Option { match field { InputField::Switch1 => self.current_note.switch_1, InputField::Switch2 => self.current_note.switch_2, InputField::Switches => self.current_note.switches, InputField::Select => self.current_note.select, InputField::Number => self.current_note.number, InputField::Value1 => self.current_note.value_1, InputField::Value2 => self.current_note.value_2, InputField::DirectSelect => self.current_note.direct_select, InputField::MidiFunction => self.current_note.midi_function, InputField::MidiChan => self.current_note.midi_chan, InputField::Config => self.current_note.config, InputField::ExpressionPedalA => self.current_note.expression_pedal_a, InputField::ExpressionPedalB => self.current_note.expression_pedal_b, _ => None, } } pub fn get_text_value(&self, field: &InputField) -> &str { match field { InputField::Display1 => &self.current_note.display_1, InputField::Display2 => &self.current_note.display_2, InputField::Display3 => &self.current_note.display_3, InputField::ButtonLeds => &self.current_note.button_leds, InputField::ButtonPresses => &self.current_note.button_presses, _ => "", } } pub fn set_led_value(&mut self, field: &InputField, value: Option) { match field { InputField::Switch1 => self.current_note.switch_1 = value, InputField::Switch2 => self.current_note.switch_2 = value, InputField::Switches => self.current_note.switches = value, InputField::Select => self.current_note.select = value, InputField::Number => self.current_note.number = value, InputField::Value1 => self.current_note.value_1 = value, InputField::Value2 => self.current_note.value_2 = value, InputField::DirectSelect => self.current_note.direct_select = value, InputField::MidiFunction => self.current_note.midi_function = value, InputField::MidiChan => self.current_note.midi_chan = value, InputField::Config => self.current_note.config = value, InputField::ExpressionPedalA => self.current_note.expression_pedal_a = value, InputField::ExpressionPedalB => self.current_note.expression_pedal_b = value, _ => {} } } pub fn get_text_value_mut(&mut self, field: &InputField) -> Option<&mut String> { match field { InputField::Display1 => Some(&mut self.current_note.display_1), InputField::Display2 => Some(&mut self.current_note.display_2), InputField::Display3 => Some(&mut self.current_note.display_3), InputField::ButtonLeds => Some(&mut self.current_note.button_leds), InputField::ButtonPresses => Some(&mut self.current_note.button_presses), _ => None, } } pub fn finalize_note(&mut self) -> StructuredNote { self.current_note.description = self.current_note.generate_description(); self.current_note.clone() } } pub fn draw_structured_input_form(f: &mut Frame, form: &StructuredInputForm, area: Rect) { let mut lines = Vec::new(); // Helper function to create field line let create_field_line = |field: &InputField, form: &StructuredInputForm| -> Line { let is_current = form.current_field == *field; let prefix = if is_current { "→ " } else { " " }; let style = if is_current { Style::default() .fg(Color::Yellow) .add_modifier(Modifier::BOLD) } else { Style::default() }; if field.is_led_field() { let value = form.get_led_value(field); let value_str = match value { Some(1) => "1", Some(0) => "0", _ => "─", }; Line::from(vec![Span::styled( format!("{}{}: {}", prefix, field.name().to_lowercase(), value_str), style, )]) } else { let text = form.get_text_value(field); let display_text = if is_current { let mut display = text.to_string(); if form.cursor_position <= display.len() { display.insert(form.cursor_position, '│'); } display } else if text.is_empty() { "".to_string() } else { text.to_string() }; Line::from(vec![Span::styled( format!( "{}{} (list of active segments): {}", prefix, field.name().to_lowercase(), display_text ), style, )]) } }; // Switch controls lines.push(create_field_line(&InputField::Switch1, form)); lines.push(create_field_line(&InputField::Switch2, form)); lines.push(Line::from("")); // Main LED controls lines.push(create_field_line(&InputField::Switches, form)); lines.push(create_field_line(&InputField::Select, form)); lines.push(create_field_line(&InputField::Number, form)); lines.push(create_field_line(&InputField::Value1, form)); lines.push(create_field_line(&InputField::Value2, form)); lines.push(Line::from("")); // Additional controls lines.push(create_field_line(&InputField::DirectSelect, form)); lines.push(create_field_line(&InputField::MidiFunction, form)); lines.push(create_field_line(&InputField::MidiChan, form)); lines.push(create_field_line(&InputField::Config, form)); lines.push(Line::from("")); // Expression pedals lines.push(create_field_line(&InputField::ExpressionPedalA, form)); lines.push(create_field_line(&InputField::ExpressionPedalB, form)); lines.push(Line::from("")); // Display segments lines.push(create_field_line(&InputField::Display1, form)); lines.push(create_field_line(&InputField::Display2, form)); lines.push(create_field_line(&InputField::Display3, form)); lines.push(Line::from("")); // Button controls lines.push(create_field_line(&InputField::ButtonLeds, form)); lines.push(create_field_line(&InputField::ButtonPresses, form)); let block = Paragraph::new(lines).block( Block::default() .borders(Borders::ALL) .title("Structured Note Input"), ); f.render_widget(block, area); }