318 lines
12 KiB
Rust
318 lines
12 KiB
Rust
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<String, u8>) -> 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<String, u8>) {
|
|
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<u8> {
|
|
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<u8>) {
|
|
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);
|
|
}
|