mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
252 lines
10 KiB
Rust
252 lines
10 KiB
Rust
use crate::*;
|
|
|
|
#[derive(Clone)]
|
|
pub enum ArrangerAppCommand {
|
|
Focus(FocusCommand),
|
|
Transport(TransportCommand),
|
|
Phrases(PhrasePoolCommand),
|
|
Editor(PhraseEditorCommand),
|
|
Arrangement(ArrangementCommand),
|
|
EditPhrase(Option<Arc<RwLock<Phrase>>>),
|
|
}
|
|
|
|
/// Handle top-level events in standalone arranger.
|
|
impl Handle<Tui> for ArrangerApp<Tui> {
|
|
fn handle (&mut self, i: &TuiInput) -> Perhaps<bool> {
|
|
if let Some(entered) = self.entered() {
|
|
use ArrangerAppFocus::*;
|
|
if let Some(true) = match entered {
|
|
Transport => self.transport.as_mut().map(|t|t.handle(i)).transpose()?.flatten(),
|
|
Arrangement => self.arrangement.handle(i)?,
|
|
PhrasePool => self.phrases.write().unwrap().handle(i)?,
|
|
PhraseEditor => self.editor.handle(i)?,
|
|
} {
|
|
return Ok(Some(true))
|
|
}
|
|
}
|
|
ArrangerAppCommand::execute_with_state(self, i)
|
|
}
|
|
}
|
|
|
|
impl InputToCommand<Tui, ArrangerApp<Tui>> for ArrangerAppCommand {
|
|
fn input_to_command (state: &ArrangerApp<Tui>, input: &TuiInput) -> Option<Self> {
|
|
use FocusCommand::*;
|
|
use ArrangerAppCommand::*;
|
|
match input.event() {
|
|
key!(KeyCode::Tab) => Some(Focus(Next)),
|
|
key!(Shift-KeyCode::Tab) => Some(Focus(Prev)),
|
|
key!(KeyCode::BackTab) => Some(Focus(Prev)),
|
|
key!(Shift-KeyCode::BackTab) => Some(Focus(Prev)),
|
|
key!(KeyCode::Up) => Some(Focus(Up)),
|
|
key!(KeyCode::Down) => Some(Focus(Down)),
|
|
key!(KeyCode::Left) => Some(Focus(Left)),
|
|
key!(KeyCode::Right) => Some(Focus(Right)),
|
|
key!(KeyCode::Enter) => Some(Focus(Enter)),
|
|
key!(KeyCode::Esc) => Some(Focus(Exit)),
|
|
key!(KeyCode::Char(' ')) => Some(Transport(TransportCommand::PlayToggle)),
|
|
_ => match state.focused() {
|
|
ArrangerAppFocus::Transport => state.transport.as_ref()
|
|
.map(|t|TransportCommand::input_to_command(&*t.read().unwrap(), input)
|
|
.map(Transport))
|
|
.flatten(),
|
|
ArrangerAppFocus::PhrasePool => {
|
|
let phrases = state.phrases.read().unwrap();
|
|
match input.event() {
|
|
key!(KeyCode::Char('e')) => Some(EditPhrase(Some(phrases.phrase().clone()))),
|
|
_ => PhrasePoolCommand::input_to_command(&*phrases, input)
|
|
.map(Phrases)
|
|
}
|
|
},
|
|
ArrangerAppFocus::PhraseEditor =>
|
|
PhraseEditorCommand::input_to_command(&state.editor, input)
|
|
.map(Editor),
|
|
ArrangerAppFocus::Arrangement => match input.event() {
|
|
key!(KeyCode::Char('e')) => Some(EditPhrase(state.arrangement.phrase())),
|
|
_ => ArrangementCommand::input_to_command(&state.arrangement, &input)
|
|
.map(Arrangement)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Handle events for arrangement.
|
|
impl Handle<Tui> for Arrangement<Tui> {
|
|
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
|
ArrangementCommand::execute_with_state(self, from)
|
|
}
|
|
}
|
|
|
|
impl InputToCommand<Tui, Arrangement<Tui>> for ArrangementCommand {
|
|
fn input_to_command (state: &Arrangement<Tui>, input: &TuiInput) -> Option<Self> {
|
|
use ArrangementCommand::*;
|
|
match input.event() {
|
|
key!(KeyCode::Char('`')) => Some(ToggleViewMode),
|
|
key!(KeyCode::Delete) => Some(Delete),
|
|
key!(KeyCode::Enter) => Some(Activate),
|
|
key!(KeyCode::Char('.')) => Some(Increment),
|
|
key!(KeyCode::Char(',')) => Some(Decrement),
|
|
key!(KeyCode::Char('+')) => Some(ZoomIn),
|
|
key!(KeyCode::Char('=')) => Some(ZoomOut),
|
|
key!(KeyCode::Char('_')) => Some(ZoomOut),
|
|
key!(KeyCode::Char('-')) => Some(ZoomOut),
|
|
key!(KeyCode::Char('<')) => Some(MoveBack),
|
|
key!(KeyCode::Char('>')) => Some(MoveForward),
|
|
key!(KeyCode::Char('c')) => Some(RandomColor),
|
|
key!(KeyCode::Char('s')) => Some(Put),
|
|
key!(KeyCode::Char('g')) => Some(Get),
|
|
key!(KeyCode::Char('e')) => Some(Edit(state.phrase())),
|
|
key!(Ctrl-KeyCode::Char('a')) => Some(AddScene),
|
|
key!(Ctrl-KeyCode::Char('t')) => Some(AddTrack),
|
|
key!(KeyCode::Char('l')) => Some(ToggleLoop),
|
|
key!(KeyCode::Up) => Some(GoUp),
|
|
key!(KeyCode::Down) => Some(GoDown),
|
|
key!(KeyCode::Left) => Some(GoLeft),
|
|
key!(KeyCode::Right) => Some(GoRight),
|
|
_ => None
|
|
}
|
|
}
|
|
}
|
|
|
|
//impl ArrangerApp<Tui> {
|
|
///// Helper for event passthru to focused component
|
|
//fn handle_focused (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
|
//match self.focused() {
|
|
//ArrangerAppFocus::Transport => self.transport.handle(from),
|
|
//ArrangerAppFocus::PhrasePool => self.handle_pool(from),
|
|
//ArrangerAppFocus::PhraseEditor => self.editor.handle(from),
|
|
//ArrangerAppFocus::Arrangement => self.handle_arrangement(from)
|
|
//.and_then(|result|{self.show_phrase();Ok(result)}),
|
|
//}
|
|
//}
|
|
///// Helper for phrase event passthru when phrase pool is focused
|
|
//fn handle_pool (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
|
//match from.event() {
|
|
//key!(KeyCode::Char('<')) => {
|
|
//self.phrases_split = self.phrases_split.saturating_sub(1).max(12);
|
|
//},
|
|
//key!(KeyCode::Char('>')) => {
|
|
//self.phrases_split = self.phrases_split + 1;
|
|
//},
|
|
//_ => return self.phrases.handle(from)
|
|
//}
|
|
//Ok(Some(true))
|
|
//}
|
|
///// Helper for phrase event passthru when arrangement is focused
|
|
//fn handle_arrangement (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
|
//let mut handle_phrase = ||{
|
|
//let result = self.phrases.handle(from);
|
|
//self.arrangement.phrase_put();
|
|
//result
|
|
//};
|
|
//match from.event() {
|
|
//key!(KeyCode::Char('a')) => return handle_phrase(),
|
|
//key!(KeyCode::Char('i')) => return handle_phrase(),
|
|
//key!(KeyCode::Char('d')) => return handle_phrase(),
|
|
//key!(KeyCode::Char('<')) => if self.arrangement.selected == ArrangementFocus::Mix {
|
|
//self.arrangement_split = self.arrangement_split.saturating_sub(1).max(12);
|
|
//} else {
|
|
//return self.arrangement.handle(from)
|
|
//},
|
|
//key!(KeyCode::Char('>')) => if self.arrangement.selected == ArrangementFocus::Mix {
|
|
//self.arrangement_split = self.arrangement_split + 1;
|
|
//} else {
|
|
//return self.arrangement.handle(from)
|
|
//},
|
|
//_ => return self.arrangement.handle(from)
|
|
//}
|
|
//self.show_phrase();
|
|
//Ok(Some(true))
|
|
//}
|
|
//}
|
|
|
|
#[derive(Clone)]
|
|
pub enum ArrangementCommand {
|
|
New,
|
|
Load,
|
|
Save,
|
|
ToggleViewMode,
|
|
Delete,
|
|
Activate,
|
|
Increment,
|
|
Decrement,
|
|
ZoomIn,
|
|
ZoomOut,
|
|
MoveBack,
|
|
MoveForward,
|
|
RandomColor,
|
|
Put,
|
|
Get,
|
|
AddScene,
|
|
AddTrack,
|
|
ToggleLoop,
|
|
GoUp,
|
|
GoDown,
|
|
GoLeft,
|
|
GoRight,
|
|
Edit(Option<Arc<RwLock<Phrase>>>),
|
|
}
|
|
|
|
impl<E: Engine> Command<ArrangerApp<E>> for ArrangerAppCommand {
|
|
fn execute (self, state: &mut ArrangerApp<E>) -> Perhaps<Self> {
|
|
let undo = match self {
|
|
Self::Focus(cmd) => {
|
|
delegate(cmd, Self::Focus, state)
|
|
},
|
|
Self::Phrases(cmd) => {
|
|
delegate(cmd, Self::Phrases, &mut*state.phrases.write().unwrap())
|
|
},
|
|
Self::Editor(cmd) => {
|
|
delegate(cmd, Self::Editor, &mut state.editor)
|
|
},
|
|
Self::Arrangement(cmd) => {
|
|
delegate(cmd, Self::Arrangement, &mut state.arrangement)
|
|
},
|
|
Self::Transport(cmd) => if let Some(ref transport) = state.transport {
|
|
delegate(cmd, Self::Transport, &mut*transport.write().unwrap())
|
|
} else {
|
|
Ok(None)
|
|
},
|
|
Self::EditPhrase(phrase) => {
|
|
state.editor.phrase = phrase.clone();
|
|
state.focus(ArrangerAppFocus::PhraseEditor);
|
|
state.focus_enter();
|
|
Ok(None)
|
|
}
|
|
}?;
|
|
state.show_phrase();
|
|
state.update_status();
|
|
return Ok(undo);
|
|
}
|
|
}
|
|
impl<E: Engine> Command<Arrangement<E>> for ArrangementCommand {
|
|
fn execute (self, state: &mut Arrangement<E>) -> Perhaps<Self> {
|
|
use ArrangementCommand::*;
|
|
match self {
|
|
New => todo!(),
|
|
Load => todo!(),
|
|
Save => todo!(),
|
|
Edit(phrase) => { state.phrase = phrase.clone() },
|
|
ToggleViewMode => { state.mode.to_next(); },
|
|
Delete => { state.delete(); },
|
|
Activate => { state.activate(); },
|
|
Increment => { state.increment(); },
|
|
Decrement => { state.decrement(); },
|
|
ZoomIn => { state.zoom_in(); },
|
|
ZoomOut => { state.zoom_out(); },
|
|
MoveBack => { state.move_back(); },
|
|
MoveForward => { state.move_forward(); },
|
|
RandomColor => { state.randomize_color(); },
|
|
Put => { state.phrase_put(); },
|
|
Get => { state.phrase_get(); },
|
|
AddScene => { state.scene_add(None, None)?; },
|
|
AddTrack => { state.track_add(None, None)?; },
|
|
ToggleLoop => { state.toggle_loop() },
|
|
GoUp => { state.go_up() },
|
|
GoDown => { state.go_down() },
|
|
GoLeft => { state.go_left() },
|
|
GoRight => { state.go_right() },
|
|
};
|
|
Ok(None)
|
|
}
|
|
}
|