mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-08 20:56:43 +01:00
wip: p.53, e=118, fixed focus trait loop
This commit is contained in:
parent
9b996878c2
commit
76af9d9bac
15 changed files with 737 additions and 727 deletions
|
|
@ -1,738 +0,0 @@
|
|||
use crate::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum TransportCommand {
|
||||
Focus(FocusCommand),
|
||||
Clock(ClockCommand),
|
||||
Playhead(PlayheadCommand),
|
||||
}
|
||||
|
||||
impl<T: TransportControl> InputToCommand<Tui, T> for TransportCommand {
|
||||
fn input_to_command (state: &T, input: &TuiInput) -> Option<Self> {
|
||||
use KeyCode::Char;
|
||||
use ClockCommand::{SetBpm, SetQuant, SetSync};
|
||||
use TransportFocus as Focused;
|
||||
use TransportCommand::{Focus, Clock, Playhead};
|
||||
let focused = state.focused();
|
||||
Some(match input.event() {
|
||||
key!(Left) => Focus(FocusCommand::Prev),
|
||||
key!(Right) => Focus(FocusCommand::Next),
|
||||
key!(Char('.')) => match focused {
|
||||
Focused::Bpm => Clock(SetBpm(state.bpm().get() + 1.0)),
|
||||
Focused::Quant => Clock(SetQuant(state.next_quant())),
|
||||
Focused::Sync => Clock(SetSync(state.next_sync())),
|
||||
Focused::PlayPause => Playhead(todo!()),
|
||||
Focused::Clock => Playhead(todo!()),
|
||||
_ => {todo!()}
|
||||
},
|
||||
key!(KeyCode::Char(',')) => match focused {
|
||||
Focused::Bpm => Clock(SetBpm(state.bpm().get() - 1.0)),
|
||||
Focused::Quant => Clock(SetQuant(state.prev_quant())),
|
||||
Focused::Sync => Clock(SetSync(state.prev_sync())),
|
||||
Focused::PlayPause => Playhead(todo!()),
|
||||
Focused::Clock => Playhead(todo!()),
|
||||
_ => {todo!()}
|
||||
},
|
||||
key!(KeyCode::Char('>')) => match focused {
|
||||
Focused::Bpm => Clock(SetBpm(state.bpm().get() + 0.001)),
|
||||
Focused::Quant => Clock(SetQuant(state.next_quant())),
|
||||
Focused::Sync => Clock(SetSync(state.next_sync())),
|
||||
Focused::PlayPause => Playhead(todo!()),
|
||||
Focused::Clock => Playhead(todo!()),
|
||||
_ => {todo!()}
|
||||
},
|
||||
key!(KeyCode::Char('<')) => match focused {
|
||||
Focused::Bpm => Clock(SetBpm(state.bpm().get() - 0.001)),
|
||||
Focused::Quant => Clock(SetQuant(state.prev_quant())),
|
||||
Focused::Sync => Clock(SetSync(state.prev_sync())),
|
||||
Focused::PlayPause => Playhead(todo!()),
|
||||
Focused::Clock => Playhead(todo!()),
|
||||
_ => {todo!()}
|
||||
},
|
||||
_ => return None
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TransportControl {
|
||||
fn quant (&self) -> &Quantize;
|
||||
fn bpm (&self) -> &BeatsPerMinute;
|
||||
fn next_quant (&self) -> f64 {
|
||||
next_note_length(self.quant().get() as usize) as f64
|
||||
}
|
||||
fn prev_quant (&self) -> f64 {
|
||||
prev_note_length(self.quant().get() as usize) as f64
|
||||
}
|
||||
fn sync (&self) -> &LaunchSync;
|
||||
fn next_sync (&self) -> f64 {
|
||||
next_note_length(self.sync().get() as usize) as f64
|
||||
}
|
||||
fn prev_sync (&self) -> f64 {
|
||||
prev_note_length(self.sync().get() as usize) as f64
|
||||
}
|
||||
}
|
||||
|
||||
impl TransportControl for TransportTui {
|
||||
fn bpm (&self) -> &BeatsPerMinute {
|
||||
self.bpm()
|
||||
}
|
||||
fn quant (&self) -> &Quantize {
|
||||
self.quant()
|
||||
}
|
||||
fn sync (&self) -> &LaunchSync {
|
||||
self.sync()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransportControl> Command<T> for TransportCommand {
|
||||
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
||||
use TransportCommand::{Focus, Clock, Playhead};
|
||||
use ClockCommand::{SetBpm, SetQuant, SetSync};
|
||||
Ok(Some(match self {
|
||||
Focus(Next) => { todo!() }
|
||||
Focus(Prev) => { todo!() },
|
||||
Focus(_) => { unimplemented!() },
|
||||
Clock(SetBpm(bpm)) => Clock(SetBpm(state.bpm().set(bpm))),
|
||||
Clock(SetQuant(quant)) => Clock(SetQuant(state.quant().set(quant))),
|
||||
Clock(SetSync(sync)) => Clock(SetSync(state.sync().set(sync))),
|
||||
_ => return Ok(None)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum SequencerCommand {
|
||||
Focus(FocusCommand),
|
||||
Undo,
|
||||
Redo,
|
||||
Clear,
|
||||
Clock(ClockCommand),
|
||||
Playhead(PlayheadCommand),
|
||||
Phrases(PhrasesCommand),
|
||||
Editor(PhraseCommand),
|
||||
}
|
||||
|
||||
impl InputToCommand<Tui, SequencerTui> for SequencerCommand {
|
||||
fn input_to_command (state: &SequencerTui, input: &TuiInput) -> Option<Self> {
|
||||
use FocusCommand::*;
|
||||
use SequencerCommand::*;
|
||||
match input.event() {
|
||||
key!(KeyCode::Tab) => Some(Self::Focus(Next)),
|
||||
key!(Shift-KeyCode::Tab) => Some(Self::Focus(Prev)),
|
||||
key!(KeyCode::BackTab) => Some(Self::Focus(Prev)),
|
||||
key!(Shift-KeyCode::BackTab) => Some(Self::Focus(Prev)),
|
||||
key!(KeyCode::Up) => Some(Self::Focus(Up)),
|
||||
key!(KeyCode::Down) => Some(Self::Focus(Down)),
|
||||
key!(KeyCode::Left) => Some(Self::Focus(Left)),
|
||||
key!(KeyCode::Right) => Some(Self::Focus(Right)),
|
||||
_ => Some(Self::App(match state.focused() {
|
||||
SequencerFocus::Transport =>
|
||||
TransportCommand::input_to_command(&state, input).map(Transport),
|
||||
SequencerFocus::Phrases =>
|
||||
PhrasesCommand::input_to_command(&state.phrases, input).map(Phrases),
|
||||
SequencerFocus::PhraseEditor =>
|
||||
PhraseCommand::input_to_command(&state.editor, input).map(Editor),
|
||||
_ => return None,
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Command<SequencerTui> for SequencerCommand {
|
||||
fn execute (self, state: &mut SequencerTui) -> Perhaps<Self> {
|
||||
use SequencerCommand::*;
|
||||
match self {
|
||||
Focus(cmd) => delegate(cmd, Focus, state),
|
||||
Phrases(cmd) => delegate(cmd, Phrases, &mut state.phrases),
|
||||
Editor(cmd) => delegate(cmd, Editor, &mut state.editor),
|
||||
Clock(cmd) => delegate(cmd, Clock, &mut state.transport),
|
||||
Playhead(cmd) => delegate(cmd, Playhead, &mut state.transport)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TransportControl for SequencerTui {
|
||||
fn bpm (&self) -> &BeatsPerMinute {
|
||||
self.app.bpm()
|
||||
}
|
||||
fn quant (&self) -> &Quantize {
|
||||
self.app.quant()
|
||||
}
|
||||
fn sync (&self) -> &LaunchSync {
|
||||
self.app.sync()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ArrangerCommand {
|
||||
Focus(FocusCommand),
|
||||
Undo,
|
||||
Redo,
|
||||
Clear,
|
||||
Clock(ClockCommand),
|
||||
Playhead(PlayheadCommand),
|
||||
Scene(ArrangerSceneCommand),
|
||||
Track(ArrangerTrackCommand),
|
||||
Clip(ArrangerClipCommand),
|
||||
Select(ArrangerSelection),
|
||||
Zoom(usize),
|
||||
Phrases(PhrasePoolCommand),
|
||||
Editor(PhraseCommand),
|
||||
EditPhrase(Option<Arc<RwLock<Phrase>>>),
|
||||
}
|
||||
|
||||
pub trait ArrangerControl {
|
||||
}
|
||||
|
||||
impl ArrangerControl for ArrangerTui {
|
||||
}
|
||||
|
||||
impl<T: ArrangerControl> InputToCommand<Tui, T> for ArrangerCommand {
|
||||
fn input_to_command (view: &T, input: &TuiInput) -> Option<Self> {
|
||||
use FocusCommand::*;
|
||||
use ArrangerCommand::*;
|
||||
Some(match input.event() {
|
||||
key!(KeyCode::Tab) => Self::Focus(Next),
|
||||
key!(Shift-KeyCode::Tab) => Self::Focus(Prev),
|
||||
key!(KeyCode::BackTab) => Self::Focus(Prev),
|
||||
key!(Shift-KeyCode::BackTab) => Self::Focus(Prev),
|
||||
key!(KeyCode::Up) => Self::Focus(Up),
|
||||
key!(KeyCode::Down) => Self::Focus(Down),
|
||||
key!(KeyCode::Left) => Self::Focus(Left),
|
||||
key!(KeyCode::Right) => Self::Focus(Right),
|
||||
key!(KeyCode::Enter) => Self::Focus(Enter),
|
||||
key!(KeyCode::Esc) => Self::Focus(Exit),
|
||||
key!(KeyCode::Char(' ')) => {
|
||||
Self::App(Playhead(PlayheadCommand::Play(None)))
|
||||
},
|
||||
_ => Self::App(match view.focused() {
|
||||
ArrangerFocus::Transport => {
|
||||
use TransportCommand::{Clock, Playhead};
|
||||
match TransportCommand::input_to_command(view, input)? {
|
||||
Clock(command) => {
|
||||
todo!()
|
||||
},
|
||||
Playhead(command) => {
|
||||
todo!()
|
||||
},
|
||||
}
|
||||
},
|
||||
ArrangerFocus::PhraseEditor => Editor(
|
||||
PhraseCommand::input_to_command(&view.editor, input)?
|
||||
),
|
||||
ArrangerFocus::PhrasePool => match input.event() {
|
||||
key!(KeyCode::Char('e')) => EditPhrase(
|
||||
Some(view.phrase().clone())
|
||||
),
|
||||
_ => Phrases(
|
||||
PhrasePoolCommand::input_to_command(view, input)?
|
||||
)
|
||||
},
|
||||
ArrangerFocus::Arranger => {
|
||||
use ArrangerSelection as Select;
|
||||
use ArrangerTrackCommand as Track;
|
||||
use ArrangerClipCommand as Clip;
|
||||
use ArrangerSceneCommand as Scene;
|
||||
match input.event() {
|
||||
key!(KeyCode::Char('e')) => EditPhrase(view.phrase()),
|
||||
_ => match input.event() {
|
||||
// FIXME: boundary conditions
|
||||
|
||||
key!(KeyCode::Up) => match view.selected {
|
||||
Select::Mix => return None,
|
||||
Select::Track(t) => return None,
|
||||
Select::Scene(s) => Select(Select::Scene(s - 1)),
|
||||
Select::Clip(t, s) => Select(Select::Clip(t, s - 1)),
|
||||
},
|
||||
|
||||
key!(KeyCode::Down) => match view.selected {
|
||||
Select::Mix => Select(Select::Scene(0)),
|
||||
Select::Track(t) => Select(Select::Clip(t, 0)),
|
||||
Select::Scene(s) => Select(Select::Scene(s + 1)),
|
||||
Select::Clip(t, s) => Select(Select::Clip(t, s + 1)),
|
||||
},
|
||||
|
||||
key!(KeyCode::Left) => match view.selected {
|
||||
Select::Mix => return None,
|
||||
Select::Track(t) => Select(Select::Track(t - 1)),
|
||||
Select::Scene(s) => return None,
|
||||
Select::Clip(t, s) => Select(Select::Clip(t - 1, s)),
|
||||
},
|
||||
|
||||
key!(KeyCode::Right) => match view.selected {
|
||||
Select::Mix => return None,
|
||||
Select::Track(t) => Select(Select::Track(t + 1)),
|
||||
Select::Scene(s) => Select(Select::Clip(0, s)),
|
||||
Select::Clip(t, s) => Select(Select::Clip(t, s - 1)),
|
||||
},
|
||||
|
||||
key!(KeyCode::Char('+')) => Zoom(0),
|
||||
|
||||
key!(KeyCode::Char('=')) => Zoom(0),
|
||||
|
||||
key!(KeyCode::Char('_')) => Zoom(0),
|
||||
|
||||
key!(KeyCode::Char('-')) => Zoom(0),
|
||||
|
||||
key!(KeyCode::Char('`')) => { todo!("toggle view mode") },
|
||||
|
||||
key!(KeyCode::Char(',')) => match view.selected {
|
||||
Select::Mix => Zoom(0),
|
||||
Select::Track(t) => Track(Track::Swap(t, t - 1)),
|
||||
Select::Scene(s) => Scene(Scene::Swap(s, s - 1)),
|
||||
Select::Clip(t, s) => Clip(Clip::Set(t, s, None)),
|
||||
},
|
||||
|
||||
key!(KeyCode::Char('.')) => match view.selected {
|
||||
Select::Mix => Zoom(0),
|
||||
Select::Track(t) => Track(Track::Swap(t, t + 1)),
|
||||
Select::Scene(s) => Scene(Scene::Swap(s, s + 1)),
|
||||
Select::Clip(t, s) => Clip(Clip::Set(t, s, None)),
|
||||
},
|
||||
|
||||
key!(KeyCode::Char('<')) => match view.selected {
|
||||
Select::Mix => Zoom(0),
|
||||
Select::Track(t) => Track(Track::Swap(t, t - 1)),
|
||||
Select::Scene(s) => Scene(Scene::Swap(s, s - 1)),
|
||||
Select::Clip(t, s) => Clip(Clip::Set(t, s, None)),
|
||||
},
|
||||
|
||||
key!(KeyCode::Char('>')) => match view.selected {
|
||||
Select::Mix => Zoom(0),
|
||||
Select::Track(t) => Track(Track::Swap(t, t + 1)),
|
||||
Select::Scene(s) => Scene(Scene::Swap(s, s + 1)),
|
||||
Select::Clip(t, s) => Clip(Clip::Set(t, s, None)),
|
||||
},
|
||||
|
||||
key!(KeyCode::Enter) => match view.selected {
|
||||
Select::Mix => return None,
|
||||
Select::Track(t) => return None,
|
||||
Select::Scene(s) => Scene(Scene::Play(s)),
|
||||
Select::Clip(t, s) => return None,
|
||||
},
|
||||
|
||||
key!(KeyCode::Delete) => match view.selected {
|
||||
Select::Mix => Clear,
|
||||
Select::Track(t) => Track(Track::Delete(t)),
|
||||
Select::Scene(s) => Scene(Scene::Delete(s)),
|
||||
Select::Clip(t, s) => Clip(Clip::Set(t, s, None)),
|
||||
},
|
||||
|
||||
key!(KeyCode::Char('c')) => Clip(Clip::RandomColor),
|
||||
|
||||
key!(KeyCode::Char('s')) => match view.selected {
|
||||
Select::Clip(t, s) => Clip(Clip::Set(t, s, None)),
|
||||
_ => return None,
|
||||
},
|
||||
|
||||
key!(KeyCode::Char('g')) => match view.selected {
|
||||
Select::Clip(t, s) => Clip(Clip::Get(t, s)),
|
||||
_ => return None,
|
||||
},
|
||||
|
||||
key!(Ctrl-KeyCode::Char('a')) => Scene(Scene::Add),
|
||||
|
||||
key!(Ctrl-KeyCode::Char('t')) => Track(Track::Add),
|
||||
|
||||
key!(KeyCode::Char('l')) => Clip(Clip::SetLoop(false)),
|
||||
|
||||
_ => return None
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ArrangerControl> Command<T> for ArrangerCommand {
|
||||
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
||||
use ArrangerCommand::*;
|
||||
match self {
|
||||
Focus(cmd) => { delegate(cmd, Focus, state) },
|
||||
Scene(cmd) => { delegate(cmd, Scene, &mut state) },
|
||||
Track(cmd) => { delegate(cmd, Track, &mut state) },
|
||||
Clip(cmd) => { delegate(cmd, Clip, &mut state) },
|
||||
Phrases(cmd) => { delegate(cmd, Phrases, &mut state) },
|
||||
Editor(cmd) => { delegate(cmd, Editor, &mut state) },
|
||||
Clock(cmd) => { delegate(cmd, Clock, &mut state) },
|
||||
Playhead(cmd) => { delegate(cmd, Playhead, &mut state) },
|
||||
Zoom(zoom) => { todo!(); },
|
||||
Select(selected) => { state.selected = selected; Ok(None) },
|
||||
EditPhrase(phrase) => {
|
||||
state.editor.phrase = phrase.clone();
|
||||
state.focus(ArrangerFocus::PhraseEditor);
|
||||
state.focus_enter();
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub enum PhrasesCommand {
|
||||
Select(usize),
|
||||
Edit(PhrasePoolCommand),
|
||||
Rename(PhraseRenameCommand),
|
||||
Length(PhraseLengthCommand),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum PhraseRenameCommand {
|
||||
Begin,
|
||||
Set(String),
|
||||
Confirm,
|
||||
Cancel,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum PhraseLengthCommand {
|
||||
Begin,
|
||||
Next,
|
||||
Prev,
|
||||
Inc,
|
||||
Dec,
|
||||
Set(usize),
|
||||
Cancel,
|
||||
}
|
||||
|
||||
impl InputToCommand<Tui, PhrasesTui> for PhrasesCommand {
|
||||
fn input_to_command (state: &PhrasesTui, input: &TuiInput) -> Option<Self> {
|
||||
use PhrasesCommand as Cmd;
|
||||
use PhrasePoolCommand as Edit;
|
||||
use PhraseRenameCommand as Rename;
|
||||
use PhraseLengthCommand as Length;
|
||||
match input.event() {
|
||||
key!(KeyCode::Up) => Some(Cmd::Select(0)),
|
||||
key!(KeyCode::Down) => Some(Cmd::Select(0)),
|
||||
key!(KeyCode::Char(',')) => Some(Cmd::Edit(Edit::Swap(0, 0))),
|
||||
key!(KeyCode::Char('.')) => Some(Cmd::Edit(Edit::Swap(0, 0))),
|
||||
key!(KeyCode::Delete) => Some(Cmd::Edit(Edit::Delete(0))),
|
||||
key!(KeyCode::Char('a')) => Some(Cmd::Edit(Edit::Add(0))),
|
||||
key!(KeyCode::Char('i')) => Some(Cmd::Edit(Edit::Add(0))),
|
||||
key!(KeyCode::Char('d')) => Some(Cmd::Edit(Edit::Duplicate(0))),
|
||||
key!(KeyCode::Char('c')) => Some(Cmd::Edit(Edit::RandomColor(0))),
|
||||
key!(KeyCode::Char('n')) => Some(Cmd::Rename(Rename::Begin)),
|
||||
key!(KeyCode::Char('t')) => Some(Cmd::Length(Length::Begin)),
|
||||
_ => match state.mode {
|
||||
Some(PhrasesMode::Rename(..)) => {
|
||||
Rename::input_to_command(state, input).map(Cmd::Rename)
|
||||
},
|
||||
Some(PhrasesMode::Length(..)) => {
|
||||
Length::input_to_command(state, input).map(Cmd::Length)
|
||||
},
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Command<PhrasesTui> for PhrasesCommand {
|
||||
fn execute (self, view: &mut PhrasesTui) -> Perhaps<Self> {
|
||||
use PhraseRenameCommand as Rename;
|
||||
use PhraseLengthCommand as Length;
|
||||
match self {
|
||||
Self::Select(phrase) => {
|
||||
view.phrase = phrase
|
||||
},
|
||||
Self::Edit(command) => {
|
||||
return Ok(command.execute(&mut view)?.map(Self::Edit))
|
||||
}
|
||||
Self::Rename(command) => match command {
|
||||
Rename::Begin => {
|
||||
view.mode = Some(PhrasesMode::Rename(
|
||||
view.phrase,
|
||||
view.phrases[view.phrase].read().unwrap().name.clone()
|
||||
))
|
||||
},
|
||||
_ => {
|
||||
return Ok(command.execute(view)?.map(Self::Rename))
|
||||
}
|
||||
},
|
||||
Self::Length(command) => match command {
|
||||
Length::Begin => {
|
||||
view.mode = Some(PhrasesMode::Length(
|
||||
view.phrase,
|
||||
view.phrases[view.phrase].read().unwrap().length,
|
||||
PhraseLengthFocus::Bar
|
||||
))
|
||||
},
|
||||
_ => {
|
||||
return Ok(command.execute(view)?.map(Self::Length))
|
||||
}
|
||||
},
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl InputToCommand<Tui, PhrasesTui> for PhraseLengthCommand {
|
||||
fn input_to_command (view: &PhrasesTui, from: &TuiInput) -> Option<Self> {
|
||||
if let Some(PhrasesMode::Length(_, length, _)) = view.mode {
|
||||
Some(match from.event() {
|
||||
key!(KeyCode::Up) => Self::Inc,
|
||||
key!(KeyCode::Down) => Self::Dec,
|
||||
key!(KeyCode::Right) => Self::Next,
|
||||
key!(KeyCode::Left) => Self::Prev,
|
||||
key!(KeyCode::Enter) => Self::Set(length),
|
||||
key!(KeyCode::Esc) => Self::Cancel,
|
||||
_ => return None
|
||||
})
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Command<PhrasesTui> for PhraseLengthCommand {
|
||||
fn execute (self, view: &mut PhrasesTui) -> Perhaps<Self> {
|
||||
use PhraseLengthFocus::*;
|
||||
use PhraseLengthCommand::*;
|
||||
if let Some(PhrasesMode::Length(phrase, ref mut length, ref mut focus)) = view.mode {
|
||||
match self {
|
||||
Self::Cancel => {
|
||||
view.mode = None;
|
||||
},
|
||||
Self::Prev => {
|
||||
focus.prev()
|
||||
},
|
||||
Self::Next => {
|
||||
focus.next()
|
||||
},
|
||||
Self::Inc => match focus {
|
||||
Bar => { *length += 4 * PPQ },
|
||||
Beat => { *length += PPQ },
|
||||
Tick => { *length += 1 },
|
||||
},
|
||||
Self::Dec => match focus {
|
||||
Bar => { *length = length.saturating_sub(4 * PPQ) },
|
||||
Beat => { *length = length.saturating_sub(PPQ) },
|
||||
Tick => { *length = length.saturating_sub(1) },
|
||||
},
|
||||
Self::Set(length) => {
|
||||
let mut phrase = view.phrases[phrase].write().unwrap();
|
||||
let old_length = phrase.length;
|
||||
phrase.length = length;
|
||||
view.mode = None;
|
||||
return Ok(Some(Self::Set(old_length)))
|
||||
},
|
||||
_ => unreachable!()
|
||||
}
|
||||
Ok(None)
|
||||
} else if self == Begin {
|
||||
view.mode = Some(PhrasesMode::Length(
|
||||
view.phrase,
|
||||
view.phrases[view.phrase].read().unwrap().length,
|
||||
PhraseLengthFocus::Bar
|
||||
));
|
||||
Ok(None)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InputToCommand<Tui, PhrasesTui> for PhraseRenameCommand {
|
||||
fn input_to_command (view: &PhrasesTui, from: &TuiInput) -> Option<Self> {
|
||||
if let Some(PhrasesMode::Rename(_, ref old_name)) = view.mode {
|
||||
Some(match from.event() {
|
||||
key!(KeyCode::Char(c)) => {
|
||||
let mut new_name = old_name.clone();
|
||||
new_name.push(*c);
|
||||
Self::Set(new_name)
|
||||
},
|
||||
key!(KeyCode::Backspace) => {
|
||||
let mut new_name = old_name.clone();
|
||||
new_name.pop();
|
||||
Self::Set(new_name)
|
||||
},
|
||||
key!(KeyCode::Enter) => Self::Confirm,
|
||||
key!(KeyCode::Esc) => Self::Cancel,
|
||||
_ => return None
|
||||
})
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Command<PhrasesTui> for PhraseRenameCommand {
|
||||
fn execute (self, view: &mut PhrasesTui) -> Perhaps<Self> {
|
||||
use PhraseRenameCommand::*;
|
||||
if let Some(PhrasesMode::Rename(phrase, ref mut old_name)) = view.mode {
|
||||
match self {
|
||||
Set(s) => {
|
||||
view.phrases[phrase].write().unwrap().name = s.into();
|
||||
return Ok(Some(Self::Set(old_name.clone())))
|
||||
},
|
||||
Confirm => {
|
||||
let old_name = old_name.clone();
|
||||
view.mode = None;
|
||||
return Ok(Some(Self::Set(old_name)))
|
||||
},
|
||||
Cancel => {
|
||||
let mut phrase = view.phrases[phrase].write().unwrap();
|
||||
phrase.name = old_name.clone();
|
||||
},
|
||||
_ => unreachable!()
|
||||
};
|
||||
Ok(None)
|
||||
} else if self == Begin {
|
||||
view.mode = Some(PhrasesMode::Rename(
|
||||
view.phrase,
|
||||
view.phrases[view.phrase].read().unwrap().name.clone()
|
||||
));
|
||||
Ok(None)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub enum PhraseCommand {
|
||||
// TODO: 1-9 seek markers that by default start every 8th of the phrase
|
||||
ToggleDirection,
|
||||
EnterEditMode,
|
||||
ExitEditMode,
|
||||
NoteAppend,
|
||||
NoteSet,
|
||||
NoteCursorSet(usize),
|
||||
NoteLengthSet(usize),
|
||||
NoteScrollSet(usize),
|
||||
TimeCursorSet(usize),
|
||||
TimeScrollSet(usize),
|
||||
TimeZoomSet(usize),
|
||||
}
|
||||
|
||||
impl InputToCommand<Tui, PhraseTui> for PhraseCommand {
|
||||
fn input_to_command (state: &PhraseTui, from: &TuiInput) -> Option<Self> {
|
||||
use PhraseCommand::*;
|
||||
Some(match from.event() {
|
||||
key!(KeyCode::Char('`')) => ToggleDirection,
|
||||
key!(KeyCode::Enter) => EnterEditMode,
|
||||
key!(KeyCode::Esc) => ExitEditMode,
|
||||
key!(KeyCode::Char('[')) => NoteLengthSet(0),
|
||||
key!(KeyCode::Char(']')) => NoteLengthSet(0),
|
||||
key!(KeyCode::Char('a')) => NoteAppend,
|
||||
key!(KeyCode::Char('s')) => NoteSet,
|
||||
key!(KeyCode::Char('-')) => TimeZoomSet(0),
|
||||
key!(KeyCode::Char('_')) => TimeZoomSet(0),
|
||||
key!(KeyCode::Char('=')) => TimeZoomSet(0),
|
||||
key!(KeyCode::Char('+')) => TimeZoomSet(0),
|
||||
key!(KeyCode::PageUp) => NoteScrollSet(0),
|
||||
key!(KeyCode::PageDown) => NoteScrollSet(0),
|
||||
key!(KeyCode::Up) => match state.entered {
|
||||
true => NoteCursorSet(0),
|
||||
false => NoteScrollSet(0),
|
||||
},
|
||||
key!(KeyCode::Down) => match state.entered {
|
||||
true => NoteCursorSet(0),
|
||||
false => NoteScrollSet(0),
|
||||
},
|
||||
key!(KeyCode::Left) => match state.entered {
|
||||
true => TimeCursorSet(0),
|
||||
false => TimeScrollSet(0),
|
||||
},
|
||||
key!(KeyCode::Right) => match state.entered {
|
||||
true => TimeCursorSet(0),
|
||||
false => TimeScrollSet(0),
|
||||
},
|
||||
_ => return None
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Command<PhraseTui> for PhraseCommand {
|
||||
//fn translate (self, state: &PhraseTui<E>) -> Self {
|
||||
//use PhraseCommand::*;
|
||||
//match self {
|
||||
//GoUp => match state.entered { true => NoteCursorInc, false => NoteScrollInc, },
|
||||
//GoDown => match state.entered { true => NoteCursorDec, false => NoteScrollDec, },
|
||||
//GoLeft => match state.entered { true => TimeCursorDec, false => TimeScrollDec, },
|
||||
//GoRight => match state.entered { true => TimeCursorInc, false => TimeScrollInc, },
|
||||
//_ => self
|
||||
//}
|
||||
//}
|
||||
fn execute (self, state: &mut PhraseTui) -> Perhaps<Self> {
|
||||
use PhraseCommand::*;
|
||||
match self.translate(state) {
|
||||
ToggleDirection => {
|
||||
state.mode = !state.mode;
|
||||
},
|
||||
EnterEditMode => {
|
||||
state.entered = true;
|
||||
},
|
||||
ExitEditMode => {
|
||||
state.entered = false;
|
||||
},
|
||||
TimeZoomOut => {
|
||||
let scale = state.time_axis.read().unwrap().scale;
|
||||
state.time_axis.write().unwrap().scale = next_note_length(scale)
|
||||
},
|
||||
TimeZoomIn => {
|
||||
let scale = state.time_axis.read().unwrap().scale;
|
||||
state.time_axis.write().unwrap().scale = prev_note_length(scale)
|
||||
},
|
||||
TimeCursorDec => {
|
||||
let scale = state.time_axis.read().unwrap().scale;
|
||||
state.time_axis.write().unwrap().point_dec(scale);
|
||||
},
|
||||
TimeCursorInc => {
|
||||
let scale = state.time_axis.read().unwrap().scale;
|
||||
state.time_axis.write().unwrap().point_inc(scale);
|
||||
},
|
||||
TimeScrollDec => {
|
||||
let scale = state.time_axis.read().unwrap().scale;
|
||||
state.time_axis.write().unwrap().start_dec(scale);
|
||||
},
|
||||
TimeScrollInc => {
|
||||
let scale = state.time_axis.read().unwrap().scale;
|
||||
state.time_axis.write().unwrap().start_inc(scale);
|
||||
},
|
||||
NoteCursorDec => {
|
||||
let mut axis = state.note_axis.write().unwrap();
|
||||
axis.point_inc(1);
|
||||
if let Some(point) = axis.point { if point > 73 { axis.point = Some(73); } }
|
||||
},
|
||||
NoteCursorInc => {
|
||||
let mut axis = state.note_axis.write().unwrap();
|
||||
axis.point_dec(1);
|
||||
if let Some(point) = axis.point { if point < axis.start { axis.start = (point / 2) * 2; } }
|
||||
},
|
||||
NoteScrollDec => {
|
||||
state.note_axis.write().unwrap().start_inc(1);
|
||||
},
|
||||
NoteScrollInc => {
|
||||
state.note_axis.write().unwrap().start_dec(1);
|
||||
},
|
||||
NoteLengthDec => {
|
||||
state.note_len = prev_note_length(state.note_len)
|
||||
},
|
||||
NoteLengthInc => {
|
||||
state.note_len = next_note_length(state.note_len)
|
||||
},
|
||||
NotePageUp => {
|
||||
let mut axis = state.note_axis.write().unwrap();
|
||||
axis.start_dec(3);
|
||||
axis.point_dec(3);
|
||||
},
|
||||
NotePageDown => {
|
||||
let mut axis = state.note_axis.write().unwrap();
|
||||
axis.start_inc(3);
|
||||
axis.point_inc(3);
|
||||
},
|
||||
NoteAppend => {
|
||||
if state.entered {
|
||||
state.put();
|
||||
state.time_cursor_advance();
|
||||
}
|
||||
},
|
||||
NoteSet => {
|
||||
if state.entered { state.put(); }
|
||||
},
|
||||
_ => unreachable!()
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue