mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
cmdsys: HandleKey -> MatchInput
This commit is contained in:
parent
1aaad23691
commit
2b163e9e27
7 changed files with 389 additions and 348 deletions
|
|
@ -3,56 +3,18 @@ use crate::*;
|
||||||
pub trait Command<S>: Sized {
|
pub trait Command<S>: Sized {
|
||||||
fn run (&self, state: &mut S) -> Perhaps<Self>;
|
fn run (&self, state: &mut S) -> Perhaps<Self>;
|
||||||
}
|
}
|
||||||
|
pub trait MatchInput<E: Engine, S>: Sized {
|
||||||
pub trait HandleKey<C: Command<Self> + Clone + 'static>: Sized {
|
fn match_input (state: &S, input: &E::Input) -> Option<Self>;
|
||||||
const HANDLE_KEY_MAP: &'static [(KeyEvent, C)] = &[]; // FIXME: needs to be method
|
|
||||||
#[inline] fn match_input_static (from: &TuiInput) -> Option<C> {
|
|
||||||
if let TuiEvent::Input(crossterm::event::Event::Key(key)) = from.event() {
|
|
||||||
return Self::match_key_static(&key)
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
#[inline] fn match_key_static (key: &KeyEvent) -> Option<C> {
|
|
||||||
for (binding, command) in Self::HANDLE_KEY_MAP.iter() {
|
|
||||||
if key == binding {
|
|
||||||
return Some(command.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
#[inline] fn match_key (&self, key: &KeyEvent) -> Option<C> {
|
|
||||||
Self::match_key_static(key)
|
|
||||||
}
|
|
||||||
#[inline] fn match_input (&self, input: &TuiInput) -> Option<C> {
|
|
||||||
Self::match_input_static(input)
|
|
||||||
}
|
|
||||||
#[inline] fn handle_input (&mut self, input: &TuiInput) -> Perhaps<C> {
|
|
||||||
if let Some(command) = self.match_input(input) {
|
|
||||||
command.run(self)
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[inline] fn handle_key (&mut self, key: &KeyEvent) -> Perhaps<C> {
|
|
||||||
if let Some(command) = self.match_key(key) {
|
|
||||||
command.run(self)
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Menu<E: Engine, S: Handle<E>, C: Command<S>> {
|
pub struct Menu<E: Engine, S: Handle<E>, C: Command<S>> {
|
||||||
pub items: Vec<MenuItem<E, S, C>>,
|
pub items: Vec<MenuItem<E, S, C>>,
|
||||||
pub index: usize,
|
pub index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine, S: Handle<E>, C: Command<S>> Menu<E, S, C> {
|
impl<E: Engine, S: Handle<E>, C: Command<S>> Menu<E, S, C> {
|
||||||
pub const fn item (command: C, name: &'static str, key: &'static str) -> MenuItem<E, S, C> {
|
pub const fn item (command: C, name: &'static str, key: &'static str) -> MenuItem<E, S, C> {
|
||||||
MenuItem::Command(command, name, key)
|
MenuItem::Command(command, name, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum MenuItem<E: Engine, S: Handle<E>, C: Command<S>> {
|
pub enum MenuItem<E: Engine, S: Handle<E>, C: Command<S>> {
|
||||||
/// Unused.
|
/// Unused.
|
||||||
__(PhantomData<E>, PhantomData<S>),
|
__(PhantomData<E>, PhantomData<S>),
|
||||||
|
|
@ -61,3 +23,11 @@ pub enum MenuItem<E: Engine, S: Handle<E>, C: Command<S>> {
|
||||||
/// A menu item with command, description and hotkey.
|
/// A menu item with command, description and hotkey.
|
||||||
Command(C, &'static str, &'static str)
|
Command(C, &'static str, &'static str)
|
||||||
}
|
}
|
||||||
|
impl<E: Engine, S: Handle<E>, C: Command<S>> MenuItem<E, S, C> {
|
||||||
|
pub fn sep () -> Self {
|
||||||
|
Self::Separator
|
||||||
|
}
|
||||||
|
pub fn cmd (command: C, text: &'static str, hotkey: &'static str) -> Self {
|
||||||
|
Self::Command(command, text, hotkey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
enum ArrangerCommand {
|
pub enum ArrangerCommand {
|
||||||
FocusNext,
|
FocusNext,
|
||||||
FocusPrev,
|
FocusPrev,
|
||||||
FocusUp,
|
FocusUp,
|
||||||
|
|
@ -9,13 +9,12 @@ enum ArrangerCommand {
|
||||||
FocusLeft,
|
FocusLeft,
|
||||||
FocusRight,
|
FocusRight,
|
||||||
Transport(TransportCommand),
|
Transport(TransportCommand),
|
||||||
Phrase(PhrasePoolCommand),
|
Phrases(PhrasePoolCommand),
|
||||||
Editor(PhraseEditorCommand),
|
Editor(PhraseEditorCommand),
|
||||||
Arrangement(ArrangementCommand),
|
Arrangement(ArrangementCommand),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
enum ArrangementCommand {
|
pub enum ArrangementCommand {
|
||||||
ToggleViewMode,
|
ToggleViewMode,
|
||||||
Delete,
|
Delete,
|
||||||
Activate,
|
Activate,
|
||||||
|
|
@ -36,108 +35,58 @@ enum ArrangementCommand {
|
||||||
GoLeft,
|
GoLeft,
|
||||||
GoRight,
|
GoRight,
|
||||||
}
|
}
|
||||||
|
impl <E: Engine> Command<Arranger<E>> for ArrangerCommand {
|
||||||
/// Handle top-level events in standalone arranger.
|
fn run (&self, state: &mut Arranger<E>) -> Perhaps<Self> {
|
||||||
impl Handle<Tui> for Arranger<Tui> {
|
use ArrangerCommand::*;
|
||||||
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
match self {
|
||||||
if !self.handle_focused(from)?.unwrap_or(false) {
|
FocusNext => { state.focus_next(); },
|
||||||
match from.event() {
|
FocusPrev => { state.focus_prev(); },
|
||||||
key!(KeyCode::Tab) => { self.focus_next(); },
|
FocusUp => { state.focus_up(); },
|
||||||
key!(Shift-KeyCode::Tab) => { self.focus_prev(); },
|
FocusDown => { state.focus_down(); },
|
||||||
key!(KeyCode::BackTab) => { self.focus_prev(); },
|
FocusLeft => { state.focus_left(); },
|
||||||
key!(Shift-KeyCode::BackTab) => { self.focus_prev(); },
|
FocusRight => { state.focus_right(); },
|
||||||
key!(KeyCode::Up) => { self.focus_up(); },
|
Transport(command) => if let Some(ref transport) = state.transport {
|
||||||
key!(KeyCode::Down) => { self.focus_down(); },
|
return command.run(&mut*transport.write().unwrap()).map(|x|x.map(Transport))
|
||||||
key!(KeyCode::Left) => { self.focus_left(); },
|
},
|
||||||
key!(KeyCode::Right) => { self.focus_right(); },
|
Phrases(command) => {
|
||||||
key!(KeyCode::Char('e')) => { self.edit_phrase(); },
|
return command.run(&mut*state.phrases.write().unwrap()).map(|x|x.map(Phrases))
|
||||||
key!(KeyCode::Char(' ')) => { self.toggle_play()?; },
|
},
|
||||||
key!(KeyCode::Char('n')) => { self.rename_selected(); },
|
Editor(command) => {
|
||||||
_ => return Ok(None)
|
return command.run(&mut state.editor).map(|x|x.map(Editor))
|
||||||
}
|
},
|
||||||
|
Arrangement(command) => {
|
||||||
|
return command.run(&mut state.arrangement).map(|x|x.map(Arrangement))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
state.show_phrase();
|
||||||
|
state.update_status();
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl <E: Engine> Command<Arrangement<E>> for ArrangementCommand {
|
||||||
|
fn run (&self, state: &mut Arrangement<E>) -> Perhaps<Self> {
|
||||||
|
use ArrangementCommand::*;
|
||||||
|
match self {
|
||||||
|
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() },
|
||||||
};
|
};
|
||||||
self.update_status();
|
Ok(None)
|
||||||
Ok(Some(true))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Arranger<Tui> {
|
|
||||||
/// Helper for event passthru to focused component
|
|
||||||
fn handle_focused (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
|
||||||
match self.focused() {
|
|
||||||
ArrangerFocus::Transport => self.transport.handle(from),
|
|
||||||
ArrangerFocus::PhrasePool => self.handle_pool(from),
|
|
||||||
ArrangerFocus::PhraseEditor => self.editor.handle(from),
|
|
||||||
ArrangerFocus::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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Handle events for arrangement.
|
|
||||||
impl Handle<Tui> for Arrangement<Tui> {
|
|
||||||
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
|
||||||
match from.event() {
|
|
||||||
key!(KeyCode::Char('`')) => { self.mode.to_next(); },
|
|
||||||
key!(KeyCode::Delete) => { self.delete(); },
|
|
||||||
key!(KeyCode::Enter) => { self.activate(); },
|
|
||||||
key!(KeyCode::Char('.')) => { self.increment(); },
|
|
||||||
key!(KeyCode::Char(',')) => { self.decrement(); },
|
|
||||||
key!(KeyCode::Char('+')) => { self.zoom_in(); },
|
|
||||||
key!(KeyCode::Char('=')) => { self.zoom_in(); },
|
|
||||||
key!(KeyCode::Char('_')) => { self.zoom_out(); },
|
|
||||||
key!(KeyCode::Char('-')) => { self.zoom_out(); },
|
|
||||||
key!(KeyCode::Char('<')) => { self.move_back(); },
|
|
||||||
key!(KeyCode::Char('>')) => { self.move_forward(); },
|
|
||||||
key!(KeyCode::Char('c')) => { self.randomize_color(); },
|
|
||||||
key!(KeyCode::Char('s')) => { self.phrase_put(); },
|
|
||||||
key!(KeyCode::Char('g')) => { self.phrase_get(); },
|
|
||||||
key!(Ctrl-KeyCode::Char('a')) => { self.scene_add(None, None)?; },
|
|
||||||
key!(Ctrl-KeyCode::Char('t')) => { self.track_add(None, None)?; },
|
|
||||||
key!(KeyCode::Char('l')) => { self.toggle_loop() },
|
|
||||||
key!(KeyCode::Up) => { self.go_up() },
|
|
||||||
key!(KeyCode::Down) => { self.go_down() },
|
|
||||||
key!(KeyCode::Left) => { self.go_left() },
|
|
||||||
key!(KeyCode::Right) => { self.go_right() },
|
|
||||||
_ => return Ok(None)
|
|
||||||
}
|
|
||||||
Ok(Some(true))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -502,3 +502,132 @@ impl<'a> Content for HorizontalArranger<'a, Tui> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Handle top-level events in standalone arranger.
|
||||||
|
impl Handle<Tui> for Arranger<Tui> {
|
||||||
|
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
||||||
|
if let Some(command) = ArrangerCommand::match_input(self, from) {
|
||||||
|
let _undo = command.run(self)?;
|
||||||
|
return Ok(Some(true))
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Handle events for arrangement.
|
||||||
|
impl Handle<Tui> for Arrangement<Tui> {
|
||||||
|
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
||||||
|
if let Some(command) = ArrangementCommand::match_input(self, from) {
|
||||||
|
let _undo = command.run(self)?;
|
||||||
|
return Ok(Some(true))
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl MatchInput<Tui, Arranger<Tui>> for ArrangerCommand {
|
||||||
|
fn match_input (state: &Arranger<Tui>, input: &TuiInput) -> Option<Self> {
|
||||||
|
match input.event() {
|
||||||
|
key!(KeyCode::Tab) => Some(Self::FocusNext),
|
||||||
|
key!(Shift-KeyCode::Tab) => Some(Self::FocusPrev),
|
||||||
|
key!(KeyCode::BackTab) => Some(Self::FocusPrev),
|
||||||
|
key!(Shift-KeyCode::BackTab) => Some(Self::FocusPrev),
|
||||||
|
key!(KeyCode::Up) => Some(Self::FocusUp),
|
||||||
|
key!(KeyCode::Down) => Some(Self::FocusDown),
|
||||||
|
key!(KeyCode::Left) => Some(Self::FocusLeft),
|
||||||
|
key!(KeyCode::Right) => Some(Self::FocusRight),
|
||||||
|
key!(KeyCode::Char(' ')) => Some(Self::Transport(TransportCommand::TogglePlay)),
|
||||||
|
_ => match state.focused() {
|
||||||
|
ArrangerFocus::Transport => state.transport.as_ref()
|
||||||
|
.map(|t|TransportCommand::match_input(&*t.read().unwrap(), input)
|
||||||
|
.map(Self::Transport))
|
||||||
|
.flatten(),
|
||||||
|
ArrangerFocus::PhrasePool =>
|
||||||
|
PhrasePoolCommand::match_input(&*state.phrases.read().unwrap(), input)
|
||||||
|
.map(Self::Phrases),
|
||||||
|
ArrangerFocus::PhraseEditor =>
|
||||||
|
PhraseEditorCommand::match_input(&state.editor, input)
|
||||||
|
.map(Self::Editor),
|
||||||
|
ArrangerFocus::Arrangement =>
|
||||||
|
ArrangementCommand::match_input(&state.arrangement, &input)
|
||||||
|
.map(Self::Arrangement)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl MatchInput<Tui, Arrangement<Tui>> for ArrangementCommand {
|
||||||
|
fn match_input (_: &Arrangement<Tui>, input: &TuiInput) -> Option<Self> {
|
||||||
|
match input.event() {
|
||||||
|
key!(KeyCode::Char('`')) => Some(Self::ToggleViewMode),
|
||||||
|
key!(KeyCode::Delete) => Some(Self::Delete),
|
||||||
|
key!(KeyCode::Enter) => Some(Self::Activate),
|
||||||
|
key!(KeyCode::Char('.')) => Some(Self::Increment),
|
||||||
|
key!(KeyCode::Char(',')) => Some(Self::Decrement),
|
||||||
|
key!(KeyCode::Char('+')) => Some(Self::ZoomIn),
|
||||||
|
key!(KeyCode::Char('=')) => Some(Self::ZoomOut),
|
||||||
|
key!(KeyCode::Char('_')) => Some(Self::ZoomOut),
|
||||||
|
key!(KeyCode::Char('-')) => Some(Self::ZoomOut),
|
||||||
|
key!(KeyCode::Char('<')) => Some(Self::MoveBack),
|
||||||
|
key!(KeyCode::Char('>')) => Some(Self::MoveForward),
|
||||||
|
key!(KeyCode::Char('c')) => Some(Self::RandomColor),
|
||||||
|
key!(KeyCode::Char('s')) => Some(Self::Put),
|
||||||
|
key!(KeyCode::Char('g')) => Some(Self::Get),
|
||||||
|
key!(Ctrl-KeyCode::Char('a')) => Some(Self::AddScene),
|
||||||
|
key!(Ctrl-KeyCode::Char('t')) => Some(Self::AddTrack),
|
||||||
|
key!(KeyCode::Char('l')) => Some(Self::ToggleLoop),
|
||||||
|
key!(KeyCode::Up) => Some(Self::GoUp),
|
||||||
|
key!(KeyCode::Down) => Some(Self::GoDown),
|
||||||
|
key!(KeyCode::Left) => Some(Self::GoLeft),
|
||||||
|
key!(KeyCode::Right) => Some(Self::GoRight),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//impl Arranger<Tui> {
|
||||||
|
///// Helper for event passthru to focused component
|
||||||
|
//fn handle_focused (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
||||||
|
//match self.focused() {
|
||||||
|
//ArrangerFocus::Transport => self.transport.handle(from),
|
||||||
|
//ArrangerFocus::PhrasePool => self.handle_pool(from),
|
||||||
|
//ArrangerFocus::PhraseEditor => self.editor.handle(from),
|
||||||
|
//ArrangerFocus::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))
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
enum SequencerCommand {
|
pub enum SequencerCommand {
|
||||||
FocusNext,
|
FocusNext,
|
||||||
FocusPrev,
|
FocusPrev,
|
||||||
FocusUp,
|
FocusUp,
|
||||||
|
|
@ -66,156 +66,24 @@ pub enum PhraseEditorCommand {
|
||||||
GoLeft,
|
GoLeft,
|
||||||
GoRight,
|
GoRight,
|
||||||
}
|
}
|
||||||
impl HandleKey<SequencerCommand> for Sequencer<Tui> {
|
|
||||||
fn match_input (&self, from: &TuiInput) -> Option<SequencerCommand> {
|
|
||||||
match from.event() {
|
|
||||||
key!(KeyCode::Tab) => Some(SequencerCommand::FocusNext),
|
|
||||||
key!(Shift-KeyCode::Tab) => Some(SequencerCommand::FocusPrev),
|
|
||||||
key!(KeyCode::BackTab) => Some(SequencerCommand::FocusPrev),
|
|
||||||
key!(Shift-KeyCode::BackTab) => Some(SequencerCommand::FocusPrev),
|
|
||||||
key!(KeyCode::Up) => Some(SequencerCommand::FocusUp),
|
|
||||||
key!(KeyCode::Down) => Some(SequencerCommand::FocusDown),
|
|
||||||
key!(KeyCode::Left) => Some(SequencerCommand::FocusLeft),
|
|
||||||
key!(KeyCode::Right) => Some(SequencerCommand::FocusRight),
|
|
||||||
key!(KeyCode::Char(' ')) => Some(SequencerCommand::Transport(
|
|
||||||
TransportCommand::TogglePlay)), // FIXME go through transport
|
|
||||||
_ => match self.focused() {
|
|
||||||
SequencerFocus::Transport => self.transport.as_ref()
|
|
||||||
.map(|t|t.read().unwrap().match_input(from).map(SequencerCommand::Transport))
|
|
||||||
.flatten(),
|
|
||||||
SequencerFocus::PhrasePool => self.phrases.read().unwrap()
|
|
||||||
.match_input(from)
|
|
||||||
.map(SequencerCommand::Phrases),
|
|
||||||
SequencerFocus::PhraseEditor => self.editor
|
|
||||||
.match_input(from)
|
|
||||||
.map(SequencerCommand::Editor),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl HandleKey<PhrasePoolCommand> for PhrasePool<Tui> {
|
|
||||||
fn match_input (&self, from: &TuiInput) -> Option<PhrasePoolCommand> {
|
|
||||||
match from.event() {
|
|
||||||
key!(KeyCode::Up) => Some(PhrasePoolCommand::Prev),
|
|
||||||
key!(KeyCode::Down) => Some(PhrasePoolCommand::Next),
|
|
||||||
key!(KeyCode::Char(',')) => Some(PhrasePoolCommand::MoveUp),
|
|
||||||
key!(KeyCode::Char('.')) => Some(PhrasePoolCommand::MoveDown),
|
|
||||||
key!(KeyCode::Delete) => Some(PhrasePoolCommand::Delete),
|
|
||||||
key!(KeyCode::Char('a')) => Some(PhrasePoolCommand::Append),
|
|
||||||
key!(KeyCode::Char('i')) => Some(PhrasePoolCommand::Insert),
|
|
||||||
key!(KeyCode::Char('d')) => Some(PhrasePoolCommand::Duplicate),
|
|
||||||
key!(KeyCode::Char('c')) => Some(PhrasePoolCommand::RandomColor),
|
|
||||||
key!(KeyCode::Char('n')) => Some(PhrasePoolCommand::Rename(PhraseRenameCommand::Begin)),
|
|
||||||
key!(KeyCode::Char('t')) => Some(PhrasePoolCommand::Length(PhraseLengthCommand::Begin)),
|
|
||||||
_ => match self.mode {
|
|
||||||
Some(PhrasePoolMode::Rename(..)) => HandleKey::<PhraseRenameCommand>
|
|
||||||
::match_input(self, from).map(PhrasePoolCommand::Rename),
|
|
||||||
Some(PhrasePoolMode::Length(..)) => HandleKey::<PhraseLengthCommand>
|
|
||||||
::match_input(self, from).map(PhrasePoolCommand::Length),
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl HandleKey<PhraseRenameCommand> for PhrasePool<Tui> {
|
|
||||||
fn match_input (&self, from: &TuiInput) -> Option<PhraseRenameCommand> {
|
|
||||||
match from.event() {
|
|
||||||
key!(KeyCode::Backspace) => Some(PhraseRenameCommand::Backspace),
|
|
||||||
key!(KeyCode::Enter) => Some(PhraseRenameCommand::Confirm),
|
|
||||||
key!(KeyCode::Esc) => Some(PhraseRenameCommand::Cancel),
|
|
||||||
key!(KeyCode::Char(c)) => Some(PhraseRenameCommand::Append(*c)),
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl HandleKey<PhraseLengthCommand> for PhrasePool<Tui> {
|
|
||||||
fn match_input (&self, from: &TuiInput) -> Option<PhraseLengthCommand> {
|
|
||||||
match from.event() {
|
|
||||||
key!(KeyCode::Up) => Some(PhraseLengthCommand::Inc),
|
|
||||||
key!(KeyCode::Down) => Some(PhraseLengthCommand::Dec),
|
|
||||||
key!(KeyCode::Right) => Some(PhraseLengthCommand::Next),
|
|
||||||
key!(KeyCode::Left) => Some(PhraseLengthCommand::Prev),
|
|
||||||
key!(KeyCode::Enter) => Some(PhraseLengthCommand::Confirm),
|
|
||||||
key!(KeyCode::Esc) => Some(PhraseLengthCommand::Cancel),
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl HandleKey<PhraseEditorCommand> for PhraseEditor<Tui> {
|
|
||||||
fn match_input (&self, from: &TuiInput) -> Option<PhraseEditorCommand> {
|
|
||||||
match from.event() {
|
|
||||||
key!(KeyCode::Char('`')) => Some(PhraseEditorCommand::ToggleDirection),
|
|
||||||
key!(KeyCode::Enter) => Some(PhraseEditorCommand::EnterEditMode),
|
|
||||||
key!(KeyCode::Esc) => Some(PhraseEditorCommand::ExitEditMode),
|
|
||||||
key!(KeyCode::Char('[')) => Some(PhraseEditorCommand::NoteLengthDec),
|
|
||||||
key!(KeyCode::Char(']')) => Some(PhraseEditorCommand::NoteLengthInc),
|
|
||||||
key!(KeyCode::Char('a')) => Some(PhraseEditorCommand::NoteAppend),
|
|
||||||
key!(KeyCode::Char('s')) => Some(PhraseEditorCommand::NoteSet),
|
|
||||||
key!(KeyCode::Char('-')) => Some(PhraseEditorCommand::TimeZoomOut),
|
|
||||||
key!(KeyCode::Char('_')) => Some(PhraseEditorCommand::TimeZoomOut),
|
|
||||||
key!(KeyCode::Char('=')) => Some(PhraseEditorCommand::TimeZoomIn),
|
|
||||||
key!(KeyCode::Char('+')) => Some(PhraseEditorCommand::TimeZoomIn),
|
|
||||||
key!(KeyCode::PageUp) => Some(PhraseEditorCommand::NotePageUp),
|
|
||||||
key!(KeyCode::PageDown) => Some(PhraseEditorCommand::NotePageDown),
|
|
||||||
key!(KeyCode::Up) => Some(PhraseEditorCommand::GoUp),
|
|
||||||
key!(KeyCode::Down) => Some(PhraseEditorCommand::GoDown),
|
|
||||||
key!(KeyCode::Left) => Some(PhraseEditorCommand::GoLeft),
|
|
||||||
key!(KeyCode::Right) => Some(PhraseEditorCommand::GoRight),
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Handle top-level events in standalone sequencer.
|
|
||||||
impl Handle<Tui> for Sequencer<Tui> {
|
|
||||||
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
|
||||||
if let Some(command) = self.match_input(from) {
|
|
||||||
let _undo = command.run(self)?;
|
|
||||||
return Ok(Some(true))
|
|
||||||
}
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Handle<Tui> for PhrasePool<Tui> {
|
|
||||||
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
|
||||||
if let Some(command) = HandleKey::<PhrasePoolCommand>::match_input(self, from) {
|
|
||||||
let _undo = command.run(self)?;
|
|
||||||
return Ok(Some(true))
|
|
||||||
}
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Handle<Tui> for PhraseEditor<Tui> {
|
|
||||||
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
|
||||||
if let Some(command) = self.match_input(from) {
|
|
||||||
let _undo = command.run(self)?;
|
|
||||||
return Ok(Some(true))
|
|
||||||
}
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<E: Engine> Command<Sequencer<E>> for SequencerCommand {
|
impl<E: Engine> Command<Sequencer<E>> for SequencerCommand {
|
||||||
fn run (&self, state: &mut Sequencer<E>) -> Perhaps<Self> {
|
fn run (&self, state: &mut Sequencer<E>) -> Perhaps<Self> {
|
||||||
|
use SequencerCommand::*;
|
||||||
match self {
|
match self {
|
||||||
Self::FocusNext => { state.focus_next(); },
|
FocusNext => { state.focus_next(); },
|
||||||
Self::FocusPrev => { state.focus_prev(); },
|
FocusPrev => { state.focus_prev(); },
|
||||||
Self::FocusUp => { state.focus_up(); },
|
FocusUp => { state.focus_up(); },
|
||||||
Self::FocusDown => { state.focus_down(); },
|
FocusDown => { state.focus_down(); },
|
||||||
Self::FocusLeft => { state.focus_left(); },
|
FocusLeft => { state.focus_left(); },
|
||||||
Self::FocusRight => { state.focus_right(); },
|
FocusRight => { state.focus_right(); },
|
||||||
Self::Transport(command) => if let Some(ref transport) = state.transport {
|
Transport(command) => if let Some(ref transport) = state.transport {
|
||||||
return command
|
return command.run(&mut*transport.write().unwrap()).map(|x|x.map(Transport))
|
||||||
.run(&mut*transport.write().unwrap())
|
|
||||||
.map(|x|x.map(SequencerCommand::Transport))
|
|
||||||
},
|
},
|
||||||
Self::Phrases(command) => {
|
Phrases(command) => {
|
||||||
return command
|
return command.run(&mut*state.phrases.write().unwrap()).map(|x|x.map(Phrases))
|
||||||
.run(&mut*state.phrases.write().unwrap())
|
|
||||||
.map(|x|x.map(SequencerCommand::Phrases))
|
|
||||||
},
|
},
|
||||||
Self::Editor(command) => {
|
Editor(command) => {
|
||||||
return command
|
return command.run(&mut state.editor).map(|x|x.map(Editor))
|
||||||
.run(&mut state.editor)
|
|
||||||
.map(|x|x.map(SequencerCommand::Editor))
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,42 @@ impl Content for Sequencer<Tui> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Handle<Tui> for Sequencer<Tui> {
|
||||||
|
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
||||||
|
if let Some(command) = SequencerCommand::match_input(self, from) {
|
||||||
|
let _undo = command.run(self)?;
|
||||||
|
return Ok(Some(true))
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl MatchInput<Tui, Sequencer<Tui>> for SequencerCommand {
|
||||||
|
fn match_input (state: &Sequencer<Tui>, input: &TuiInput) -> Option<Self> {
|
||||||
|
match input.event() {
|
||||||
|
key!(KeyCode::Tab) => Some(Self::FocusNext),
|
||||||
|
key!(Shift-KeyCode::Tab) => Some(Self::FocusPrev),
|
||||||
|
key!(KeyCode::BackTab) => Some(Self::FocusPrev),
|
||||||
|
key!(Shift-KeyCode::BackTab) => Some(Self::FocusPrev),
|
||||||
|
key!(KeyCode::Up) => Some(Self::FocusUp),
|
||||||
|
key!(KeyCode::Down) => Some(Self::FocusDown),
|
||||||
|
key!(KeyCode::Left) => Some(Self::FocusLeft),
|
||||||
|
key!(KeyCode::Right) => Some(Self::FocusRight),
|
||||||
|
key!(KeyCode::Char(' ')) => Some(Self::Transport(TransportCommand::TogglePlay)),
|
||||||
|
_ => match state.focused() {
|
||||||
|
SequencerFocus::Transport => state.transport.as_ref()
|
||||||
|
.map(|t|TransportCommand::match_input(&*t.read().unwrap(), input)
|
||||||
|
.map(Self::Transport))
|
||||||
|
.flatten(),
|
||||||
|
SequencerFocus::PhrasePool =>
|
||||||
|
PhrasePoolCommand::match_input(&*state.phrases.read().unwrap(), input)
|
||||||
|
.map(Self::Phrases),
|
||||||
|
SequencerFocus::PhraseEditor =>
|
||||||
|
PhraseEditorCommand::match_input(&state.editor, input)
|
||||||
|
.map(Self::Editor),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// TODO: Display phrases always in order of appearance
|
// TODO: Display phrases always in order of appearance
|
||||||
impl Content for PhrasePool<Tui> {
|
impl Content for PhrasePool<Tui> {
|
||||||
type Engine = Tui;
|
type Engine = Tui;
|
||||||
|
|
@ -45,6 +81,96 @@ impl Content for PhrasePool<Tui> {
|
||||||
Layers::new(move|add|{ add(&content)?; Ok(add(&title)?) })
|
Layers::new(move|add|{ add(&content)?; Ok(add(&title)?) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Handle<Tui> for PhrasePool<Tui> {
|
||||||
|
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
||||||
|
if let Some(command) = PhrasePoolCommand::match_input(self, from) {
|
||||||
|
let _undo = command.run(self)?;
|
||||||
|
return Ok(Some(true))
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl MatchInput<Tui, PhrasePool<Tui>> for PhrasePoolCommand {
|
||||||
|
fn match_input (state: &PhrasePool<Tui>, input: &TuiInput) -> Option<Self> {
|
||||||
|
match input.event() {
|
||||||
|
key!(KeyCode::Up) => Some(Self::Prev),
|
||||||
|
key!(KeyCode::Down) => Some(Self::Next),
|
||||||
|
key!(KeyCode::Char(',')) => Some(Self::MoveUp),
|
||||||
|
key!(KeyCode::Char('.')) => Some(Self::MoveDown),
|
||||||
|
key!(KeyCode::Delete) => Some(Self::Delete),
|
||||||
|
key!(KeyCode::Char('a')) => Some(Self::Append),
|
||||||
|
key!(KeyCode::Char('i')) => Some(Self::Insert),
|
||||||
|
key!(KeyCode::Char('d')) => Some(Self::Duplicate),
|
||||||
|
key!(KeyCode::Char('c')) => Some(Self::RandomColor),
|
||||||
|
key!(KeyCode::Char('n')) => Some(Self::Rename(PhraseRenameCommand::Begin)),
|
||||||
|
key!(KeyCode::Char('t')) => Some(Self::Length(PhraseLengthCommand::Begin)),
|
||||||
|
_ => match state.mode {
|
||||||
|
Some(PhrasePoolMode::Rename(..)) => PhraseRenameCommand::match_input(state, input)
|
||||||
|
.map(Self::Rename),
|
||||||
|
Some(PhrasePoolMode::Length(..)) => PhraseLengthCommand::match_input(state, input)
|
||||||
|
.map(Self::Length),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl MatchInput<Tui, PhrasePool<Tui>> for PhraseRenameCommand {
|
||||||
|
fn match_input (_: &PhrasePool<Tui>, from: &TuiInput) -> Option<Self> {
|
||||||
|
match from.event() {
|
||||||
|
key!(KeyCode::Backspace) => Some(Self::Backspace),
|
||||||
|
key!(KeyCode::Enter) => Some(Self::Confirm),
|
||||||
|
key!(KeyCode::Esc) => Some(Self::Cancel),
|
||||||
|
key!(KeyCode::Char(c)) => Some(Self::Append(*c)),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl MatchInput<Tui, PhrasePool<Tui>> for PhraseLengthCommand {
|
||||||
|
fn match_input (_: &PhrasePool<Tui>, from: &TuiInput) -> Option<Self> {
|
||||||
|
match from.event() {
|
||||||
|
key!(KeyCode::Up) => Some(Self::Inc),
|
||||||
|
key!(KeyCode::Down) => Some(Self::Dec),
|
||||||
|
key!(KeyCode::Right) => Some(Self::Next),
|
||||||
|
key!(KeyCode::Left) => Some(Self::Prev),
|
||||||
|
key!(KeyCode::Enter) => Some(Self::Confirm),
|
||||||
|
key!(KeyCode::Esc) => Some(Self::Cancel),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Content for PhraseLength<Tui> {
|
||||||
|
type Engine = Tui;
|
||||||
|
fn content (&self) -> impl Widget<Engine = Tui> {
|
||||||
|
Layers::new(move|add|{
|
||||||
|
match self.focus {
|
||||||
|
None => add(&row!(
|
||||||
|
" ", self.bars_string(),
|
||||||
|
".", self.beats_string(),
|
||||||
|
".", self.ticks_string(),
|
||||||
|
" "
|
||||||
|
)),
|
||||||
|
Some(PhraseLengthFocus::Bar) => add(&row!(
|
||||||
|
"[", self.bars_string(),
|
||||||
|
"]", self.beats_string(),
|
||||||
|
".", self.ticks_string(),
|
||||||
|
" "
|
||||||
|
)),
|
||||||
|
Some(PhraseLengthFocus::Beat) => add(&row!(
|
||||||
|
" ", self.bars_string(),
|
||||||
|
"[", self.beats_string(),
|
||||||
|
"]", self.ticks_string(),
|
||||||
|
" "
|
||||||
|
)),
|
||||||
|
Some(PhraseLengthFocus::Tick) => add(&row!(
|
||||||
|
" ", self.bars_string(),
|
||||||
|
".", self.beats_string(),
|
||||||
|
"[", self.ticks_string(),
|
||||||
|
"]"
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
impl Content for PhraseEditor<Tui> {
|
impl Content for PhraseEditor<Tui> {
|
||||||
type Engine = Tui;
|
type Engine = Tui;
|
||||||
fn content (&self) -> impl Widget<Engine = Tui> {
|
fn content (&self) -> impl Widget<Engine = Tui> {
|
||||||
|
|
@ -307,36 +433,36 @@ pub(crate) fn keys_vert () -> Buffer {
|
||||||
});
|
});
|
||||||
buffer
|
buffer
|
||||||
}
|
}
|
||||||
impl Content for PhraseLength<Tui> {
|
impl Handle<Tui> for PhraseEditor<Tui> {
|
||||||
type Engine = Tui;
|
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
||||||
fn content (&self) -> impl Widget<Engine = Tui> {
|
if let Some(command) = PhraseEditorCommand::match_input(self, from) {
|
||||||
Layers::new(move|add|{
|
let _undo = command.run(self)?;
|
||||||
match self.focus {
|
return Ok(Some(true))
|
||||||
None => add(&row!(
|
}
|
||||||
" ", self.bars_string(),
|
Ok(None)
|
||||||
".", self.beats_string(),
|
}
|
||||||
".", self.ticks_string(),
|
}
|
||||||
" "
|
impl MatchInput<Tui, PhraseEditor<Tui>> for PhraseEditorCommand {
|
||||||
)),
|
fn match_input (_: &PhraseEditor<Tui>, from: &TuiInput) -> Option<Self> {
|
||||||
Some(PhraseLengthFocus::Bar) => add(&row!(
|
match from.event() {
|
||||||
"[", self.bars_string(),
|
key!(KeyCode::Char('`')) => Some(Self::ToggleDirection),
|
||||||
"]", self.beats_string(),
|
key!(KeyCode::Enter) => Some(Self::EnterEditMode),
|
||||||
".", self.ticks_string(),
|
key!(KeyCode::Esc) => Some(Self::ExitEditMode),
|
||||||
" "
|
key!(KeyCode::Char('[')) => Some(Self::NoteLengthDec),
|
||||||
)),
|
key!(KeyCode::Char(']')) => Some(Self::NoteLengthInc),
|
||||||
Some(PhraseLengthFocus::Beat) => add(&row!(
|
key!(KeyCode::Char('a')) => Some(Self::NoteAppend),
|
||||||
" ", self.bars_string(),
|
key!(KeyCode::Char('s')) => Some(Self::NoteSet),
|
||||||
"[", self.beats_string(),
|
key!(KeyCode::Char('-')) => Some(Self::TimeZoomOut),
|
||||||
"]", self.ticks_string(),
|
key!(KeyCode::Char('_')) => Some(Self::TimeZoomOut),
|
||||||
" "
|
key!(KeyCode::Char('=')) => Some(Self::TimeZoomIn),
|
||||||
)),
|
key!(KeyCode::Char('+')) => Some(Self::TimeZoomIn),
|
||||||
Some(PhraseLengthFocus::Tick) => add(&row!(
|
key!(KeyCode::PageUp) => Some(Self::NotePageUp),
|
||||||
" ", self.bars_string(),
|
key!(KeyCode::PageDown) => Some(Self::NotePageDown),
|
||||||
".", self.beats_string(),
|
key!(KeyCode::Up) => Some(Self::GoUp),
|
||||||
"[", self.ticks_string(),
|
key!(KeyCode::Down) => Some(Self::GoDown),
|
||||||
"]"
|
key!(KeyCode::Left) => Some(Self::GoLeft),
|
||||||
)),
|
key!(KeyCode::Right) => Some(Self::GoRight),
|
||||||
}
|
_ => None
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,4 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
impl Handle<Tui> for TransportToolbar<Tui> {
|
|
||||||
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
|
||||||
if let TuiEvent::Input(crossterm::event::Event::Key(event)) = from.event() {
|
|
||||||
let _undo = self.handle_key(event)?;
|
|
||||||
return Ok(Some(true))
|
|
||||||
}
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub enum TransportCommand {
|
pub enum TransportCommand {
|
||||||
FocusNext,
|
FocusNext,
|
||||||
|
|
@ -27,20 +16,6 @@ pub enum TransportCommand {
|
||||||
SetQuant(f64),
|
SetQuant(f64),
|
||||||
SetSync(f64),
|
SetSync(f64),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HandleKey<TransportCommand> for TransportToolbar<Tui> {
|
|
||||||
const HANDLE_KEY_MAP: &'static [(KeyEvent, TransportCommand)] = &[
|
|
||||||
( key(KeyCode::Char(' ')), TransportCommand::FocusPrev),
|
|
||||||
(shift(key(KeyCode::Char(' '))), TransportCommand::FocusPrev),
|
|
||||||
( key(KeyCode::Left), TransportCommand::FocusPrev),
|
|
||||||
( key(KeyCode::Right), TransportCommand::FocusNext),
|
|
||||||
( key(KeyCode::Char('.')), TransportCommand::Increment),
|
|
||||||
( key(KeyCode::Char(',')), TransportCommand::Decrement),
|
|
||||||
( key(KeyCode::Char('>')), TransportCommand::FineIncrement),
|
|
||||||
( key(KeyCode::Char('<')), TransportCommand::FineDecrement),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Engine> Command<TransportToolbar<E>> for TransportCommand {
|
impl<E: Engine> Command<TransportToolbar<E>> for TransportCommand {
|
||||||
fn run (&self, state: &mut TransportToolbar<E>) -> Perhaps<Self> {
|
fn run (&self, state: &mut TransportToolbar<E>) -> Perhaps<Self> {
|
||||||
match self {
|
match self {
|
||||||
|
|
|
||||||
|
|
@ -45,3 +45,27 @@ impl TransportToolbarFocus {
|
||||||
lay!(corners, highlight, *widget)
|
lay!(corners, highlight, *widget)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Handle<Tui> for TransportToolbar<Tui> {
|
||||||
|
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
||||||
|
if let Some(command) = TransportCommand::match_input(self, from) {
|
||||||
|
let _undo = command.run(self)?;
|
||||||
|
return Ok(Some(true))
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl MatchInput<Tui, TransportToolbar<Tui>> for TransportCommand {
|
||||||
|
fn match_input (_: &TransportToolbar<Tui>, input: &TuiInput) -> Option<Self> {
|
||||||
|
match input.event() {
|
||||||
|
key!(KeyCode::Char(' ')) => Some(Self::FocusPrev),
|
||||||
|
key!(Shift-KeyCode::Char(' ')) => Some(Self::FocusPrev),
|
||||||
|
key!(KeyCode::Left) => Some(Self::FocusPrev),
|
||||||
|
key!(KeyCode::Right) => Some(Self::FocusNext),
|
||||||
|
key!(KeyCode::Char('.')) => Some(Self::Increment),
|
||||||
|
key!(KeyCode::Char(',')) => Some(Self::Decrement),
|
||||||
|
key!(KeyCode::Char('>')) => Some(Self::FineIncrement),
|
||||||
|
key!(KeyCode::Char('<')) => Some(Self::FineDecrement),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue