use a7n_ipc::{Edge, LayoutInfo, LayoutPlacement, Placement, validate};
use anyhow::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
const MOVE_STEP: i32 = 100;
const FINE_MOVE_STEP: i32 = 10;
const SEGMENT_COUNT: u8 = 12;
const EDGES: [Edge; 4] = [Edge::Left, Edge::Top, Edge::Right, Edge::Bottom];
#[derive(Debug)]
pub(super) struct Editor {
pub(super) placements: Vec<Placement>,
pub(super) names: Vec<String>,
pub(super) selected: usize,
pub(super) lost_edit: bool,
segment: u8,
}
#[derive(Debug)]
pub(super) enum Action {
Continue,
Quit,
Save(Vec<LayoutPlacement>),
}
impl Editor {
pub(super) fn new(info: LayoutInfo) -> Result<Self> {
let draft = a7n_ipc::layout::LayoutDraft::try_from(info)?;
Ok(Self {
placements: draft.placements,
names: draft.names,
selected: 0,
segment: 0,
lost_edit: draft.lost_edit,
})
}
pub(super) fn handle(&mut self, key: KeyEvent) -> Action {
if key.kind == KeyEventKind::Release {
return Action::Continue;
}
match key.code {
KeyCode::Esc | KeyCode::Char('q') => return Action::Quit,
KeyCode::Enter | KeyCode::Char('s') if validate(&self.placements).is_ok() => {
return Action::Save(
self.placements
.iter()
.map(|p| LayoutPlacement {
machine: p.id,
x: p.x,
y: p.y,
width: p.width,
height: p.height,
blocked: p.blocked,
})
.collect(),
);
}
_ => {}
}
if self.placements.is_empty() {
return Action::Continue;
}
let step = if key.modifiers.contains(KeyModifiers::SHIFT) {
FINE_MOVE_STEP
} else {
MOVE_STEP
};
let (edge, third) = self.focused_segment();
let placement = &mut self.placements[self.selected];
match key.code {
KeyCode::BackTab => self.select_previous(),
KeyCode::Tab if key.modifiers.contains(KeyModifiers::SHIFT) => self.select_previous(),
KeyCode::Tab => {
self.selected = (self.selected + 1) % self.placements.len();
self.segment = 0;
}
KeyCode::Char('e' | 'E') => {
let backwards =
key.code == KeyCode::Char('E') || key.modifiers.contains(KeyModifiers::SHIFT);
self.segment =
(self.segment + if backwards { SEGMENT_COUNT - 1 } else { 1 }) % SEGMENT_COUNT;
}
KeyCode::Char('x') => {
placement.toggle_blocked(edge, third);
}
KeyCode::Left => placement.x = placement.x.saturating_sub(step),
KeyCode::Right => placement.x = placement.x.saturating_add(step),
KeyCode::Up => placement.y = placement.y.saturating_sub(step),
KeyCode::Down => placement.y = placement.y.saturating_add(step),
_ => {}
}
Action::Continue
}
pub(super) fn focused_segment(&self) -> (Edge, u8) {
(EDGES[usize::from(self.segment / 3)], self.segment % 3)
}
fn select_previous(&mut self) {
self.segment = 0;
self.selected = (self.selected + self.placements.len() - 1) % self.placements.len();
}
}
#[cfg(test)]
mod tests;