mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
wip: p.55, e=95
This commit is contained in:
parent
54fb5b6ece
commit
37ac7823af
10 changed files with 272 additions and 219 deletions
|
|
@ -35,4 +35,17 @@ pub trait ClockApi: Send + Sync {
|
||||||
fn ppq (&self) -> &PulsesPerQuaver {
|
fn ppq (&self) -> &PulsesPerQuaver {
|
||||||
&self.timebase().ppq
|
&self.timebase().ppq
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ pub trait HasPhrases {
|
||||||
fn phrases_mut (&mut self) -> &mut Vec<Arc<RwLock<Phrase>>>;
|
fn phrases_mut (&mut self) -> &mut Vec<Arc<RwLock<Phrase>>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum PhrasePoolCommand {
|
pub enum PhrasePoolCommand {
|
||||||
Add(usize),
|
Add(usize),
|
||||||
Delete(usize),
|
Delete(usize),
|
||||||
|
|
|
||||||
|
|
@ -256,15 +256,13 @@ impl ArrangerTui {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn activate (&mut self) {
|
pub fn activate (&mut self) {
|
||||||
let scenes = self.scenes();
|
let selected = self.selected;
|
||||||
let tracks = self.tracks_mut();
|
match selected {
|
||||||
match self.selected {
|
|
||||||
ArrangerSelection::Scene(s) => {
|
ArrangerSelection::Scene(s) => {
|
||||||
for (t, track) in tracks.iter_mut().enumerate() {
|
for (t, track) in self.tracks_mut().iter_mut().enumerate() {
|
||||||
let player = &mut track.player;
|
let clip = self.scenes()[s].clips[t].as_ref();
|
||||||
let clip = scenes[s].clips[t].as_ref();
|
if track.play_phrase.is_some() || clip.is_some() {
|
||||||
if player.phrase.is_some() || clip.is_some() {
|
track.enqueue_next(clip);
|
||||||
player.enqueue_next(clip);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO make transport available here, so that
|
// TODO make transport available here, so that
|
||||||
|
|
@ -274,7 +272,7 @@ impl ArrangerTui {
|
||||||
//}
|
//}
|
||||||
},
|
},
|
||||||
ArrangerSelection::Clip(t, s) => {
|
ArrangerSelection::Clip(t, s) => {
|
||||||
tracks[t].player.enqueue_next(scenes[s].clips[t]);
|
self.tracks_mut()[t].enqueue_next(self.scenes()[s].clips[t].as_ref());
|
||||||
},
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -97,20 +97,26 @@ impl<T: TransportControl> Command<T> for TransportCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command<SequencerTui> for SequencerCommand {
|
impl<T> Command<T> for SequencerCommand
|
||||||
fn execute (self, state: &mut SequencerTui) -> Perhaps<Self> {
|
where
|
||||||
|
T: FocusGrid + PhrasesControl + PhraseControl + ClockApi + PlayheadApi
|
||||||
|
{
|
||||||
|
fn execute (self, state: T) -> Perhaps<Self> {
|
||||||
use SequencerCommand::*;
|
use SequencerCommand::*;
|
||||||
match self {
|
match self {
|
||||||
Focus(cmd) => delegate(cmd, Focus, &mut state),
|
Focus(cmd) => delegate(cmd, Focus, state),
|
||||||
Phrases(cmd) => delegate(cmd, Phrases, &mut state.phrases),
|
Phrases(cmd) => delegate(cmd, Phrases, state),
|
||||||
Editor(cmd) => delegate(cmd, Editor, &mut state.editor),
|
Editor(cmd) => delegate(cmd, Editor, state),
|
||||||
Clock(cmd) => delegate(cmd, Clock, &mut state.transport),
|
Clock(cmd) => delegate(cmd, Clock, state),
|
||||||
Playhead(cmd) => delegate(cmd, Playhead, &mut state.transport)
|
Playhead(cmd) => delegate(cmd, Playhead, state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ArrangerControl + FocusGrid> Command<T> for ArrangerCommand {
|
impl<T> Command<T> for ArrangerCommand
|
||||||
|
where
|
||||||
|
T: FocusGrid + ArrangerControl + HasPhrases + PhraseControl + ClockApi + PlayheadApi
|
||||||
|
{
|
||||||
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
||||||
use ArrangerCommand::*;
|
use ArrangerCommand::*;
|
||||||
match self {
|
match self {
|
||||||
|
|
@ -134,71 +140,59 @@ impl<T: ArrangerControl + FocusGrid> Command<T> for ArrangerCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Command<T> for ArrangerSceneCommand {
|
impl<T: ArrangerControl> Command<T> for ArrangerSceneCommand {
|
||||||
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
||||||
todo!()
|
todo!();
|
||||||
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Command<T> for ArrangerTrackCommand {
|
impl<T: ArrangerControl> Command<T> for ArrangerTrackCommand {
|
||||||
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
||||||
todo!()
|
todo!();
|
||||||
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Command<T> for ArrangerClipCommand {
|
impl<T: ArrangerControl> Command<T> for ArrangerClipCommand {
|
||||||
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
||||||
todo!()
|
todo!();
|
||||||
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command<PhrasesTui> for PhrasesCommand {
|
impl<T: PhrasesControl> Command<T> for PhrasesCommand {
|
||||||
fn execute (self, view: &mut PhrasesTui) -> Perhaps<Self> {
|
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
||||||
use PhraseRenameCommand as Rename;
|
use PhraseRenameCommand as Rename;
|
||||||
use PhraseLengthCommand as Length;
|
use PhraseLengthCommand as Length;
|
||||||
match self {
|
match self {
|
||||||
Self::Select(phrase) => {
|
Self::Select(phrase) => {
|
||||||
view.phrase = phrase
|
state.phrase = phrase
|
||||||
},
|
},
|
||||||
Self::Edit(command) => {
|
Self::Edit(command) => {
|
||||||
return Ok(command.execute(&mut view)?.map(Self::Edit))
|
return Ok(command.execute(&mut state)?.map(Self::Edit))
|
||||||
}
|
}
|
||||||
Self::Rename(command) => match command {
|
Self::Rename(command) => match command {
|
||||||
Rename::Begin => {
|
Rename::Begin => self.phrases_rename_begin(),
|
||||||
view.mode = Some(PhrasesMode::Rename(
|
_ => return Ok(command.execute(state)?.map(Self::Rename)),
|
||||||
view.phrase,
|
|
||||||
view.phrases[view.phrase].read().unwrap().name.clone()
|
|
||||||
))
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
return Ok(command.execute(view)?.map(Self::Rename))
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
Self::Length(command) => match command {
|
Self::Length(command) => match command {
|
||||||
Length::Begin => {
|
Length::Begin => self.phrases_length_begin(),
|
||||||
view.mode = Some(PhrasesMode::Length(
|
_ => return Ok(command.execute(state)?.map(Self::Length)),
|
||||||
view.phrase,
|
|
||||||
view.phrases[view.phrase].read().unwrap().length,
|
|
||||||
PhraseLengthFocus::Bar
|
|
||||||
))
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
return Ok(command.execute(view)?.map(Self::Length))
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command<PhrasesTui> for PhraseLengthCommand {
|
impl<T: PhrasesControl> Command<T> for PhraseLengthCommand {
|
||||||
fn execute (self, view: &mut PhrasesTui) -> Perhaps<Self> {
|
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
||||||
use PhraseLengthFocus::*;
|
use PhraseLengthFocus::*;
|
||||||
use PhraseLengthCommand::*;
|
use PhraseLengthCommand::*;
|
||||||
if let Some(PhrasesMode::Length(phrase, ref mut length, ref mut focus)) = view.mode {
|
if let Some(PhrasesMode::Length(phrase, ref mut length, ref mut focus)) = state.mode {
|
||||||
match self {
|
match self {
|
||||||
Self::Cancel => {
|
Self::Cancel => {
|
||||||
view.mode = None;
|
state.mode = None;
|
||||||
},
|
},
|
||||||
Self::Prev => {
|
Self::Prev => {
|
||||||
focus.prev()
|
focus.prev()
|
||||||
|
|
@ -217,21 +211,17 @@ impl Command<PhrasesTui> for PhraseLengthCommand {
|
||||||
Tick => { *length = length.saturating_sub(1) },
|
Tick => { *length = length.saturating_sub(1) },
|
||||||
},
|
},
|
||||||
Self::Set(length) => {
|
Self::Set(length) => {
|
||||||
let mut phrase = view.phrases[phrase].write().unwrap();
|
let mut phrase = state.phrases[phrase].write().unwrap();
|
||||||
let old_length = phrase.length;
|
let old_length = phrase.length;
|
||||||
phrase.length = length;
|
phrase.length = length;
|
||||||
view.mode = None;
|
state.mode = None;
|
||||||
return Ok(Some(Self::Set(old_length)))
|
return Ok(Some(Self::Set(old_length)))
|
||||||
},
|
},
|
||||||
_ => unreachable!()
|
_ => unreachable!()
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else if self == Begin {
|
} else if self == Begin {
|
||||||
view.mode = Some(PhrasesMode::Length(
|
self.phrases_length_begin();
|
||||||
view.phrase,
|
|
||||||
view.phrases[view.phrase].read().unwrap().length,
|
|
||||||
PhraseLengthFocus::Bar
|
|
||||||
));
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
|
|
@ -239,32 +229,29 @@ impl Command<PhrasesTui> for PhraseLengthCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command<PhrasesTui> for PhraseRenameCommand {
|
impl<T: PhrasesControl> Command<T> for PhraseRenameCommand {
|
||||||
fn execute (self, view: &mut PhrasesTui) -> Perhaps<Self> {
|
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
||||||
use PhraseRenameCommand::*;
|
use PhraseRenameCommand::*;
|
||||||
if let Some(PhrasesMode::Rename(phrase, ref mut old_name)) = view.mode {
|
if let Some(PhrasesMode::Rename(phrase, ref mut old_name)) = state.mode {
|
||||||
match self {
|
match self {
|
||||||
Set(s) => {
|
Set(s) => {
|
||||||
view.phrases[phrase].write().unwrap().name = s.into();
|
state.phrases[phrase].write().unwrap().name = s.into();
|
||||||
return Ok(Some(Self::Set(old_name.clone())))
|
return Ok(Some(Self::Set(old_name.clone())))
|
||||||
},
|
},
|
||||||
Confirm => {
|
Confirm => {
|
||||||
let old_name = old_name.clone();
|
let old_name = old_name.clone();
|
||||||
view.mode = None;
|
state.mode = None;
|
||||||
return Ok(Some(Self::Set(old_name)))
|
return Ok(Some(Self::Set(old_name)))
|
||||||
},
|
},
|
||||||
Cancel => {
|
Cancel => {
|
||||||
let mut phrase = view.phrases[phrase].write().unwrap();
|
let mut phrase = state.phrases[phrase].write().unwrap();
|
||||||
phrase.name = old_name.clone();
|
phrase.name = old_name.clone();
|
||||||
},
|
},
|
||||||
_ => unreachable!()
|
_ => unreachable!()
|
||||||
};
|
};
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else if self == Begin {
|
} else if self == Begin {
|
||||||
view.mode = Some(PhrasesMode::Rename(
|
self.phrases_rename_begin();
|
||||||
view.phrase,
|
|
||||||
view.phrases[view.phrase].read().unwrap().name.clone()
|
|
||||||
));
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
|
|
@ -272,7 +259,7 @@ impl Command<PhrasesTui> for PhraseRenameCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command<PhraseTui> for PhraseCommand {
|
impl<T: PhraseControl> Command<T> for PhraseCommand {
|
||||||
//fn translate (self, state: &PhraseTui<E>) -> Self {
|
//fn translate (self, state: &PhraseTui<E>) -> Self {
|
||||||
//use PhraseCommand::*;
|
//use PhraseCommand::*;
|
||||||
//match self {
|
//match self {
|
||||||
|
|
@ -283,7 +270,7 @@ impl Command<PhraseTui> for PhraseCommand {
|
||||||
//_ => self
|
//_ => self
|
||||||
//}
|
//}
|
||||||
//}
|
//}
|
||||||
fn execute (self, state: &mut PhraseTui) -> Perhaps<Self> {
|
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
||||||
use PhraseCommand::*;
|
use PhraseCommand::*;
|
||||||
match self.translate(state) {
|
match self.translate(state) {
|
||||||
ToggleDirection => {
|
ToggleDirection => {
|
||||||
|
|
@ -296,58 +283,58 @@ impl Command<PhraseTui> for PhraseCommand {
|
||||||
state.entered = false;
|
state.entered = false;
|
||||||
},
|
},
|
||||||
TimeZoomOut => {
|
TimeZoomOut => {
|
||||||
let scale = state.time_axis.read().unwrap().scale;
|
let scale = state.time_axis().read().unwrap().scale;
|
||||||
state.time_axis.write().unwrap().scale = next_note_length(scale)
|
state.time_axis().write().unwrap().scale = next_note_length(scale)
|
||||||
},
|
},
|
||||||
TimeZoomIn => {
|
TimeZoomIn => {
|
||||||
let scale = state.time_axis.read().unwrap().scale;
|
let scale = state.time_axis().read().unwrap().scale;
|
||||||
state.time_axis.write().unwrap().scale = prev_note_length(scale)
|
state.time_axis().write().unwrap().scale = prev_note_length(scale)
|
||||||
},
|
},
|
||||||
TimeCursorDec => {
|
TimeCursorDec => {
|
||||||
let scale = state.time_axis.read().unwrap().scale;
|
let scale = state.time_axis().read().unwrap().scale;
|
||||||
state.time_axis.write().unwrap().point_dec(scale);
|
state.time_axis().write().unwrap().point_dec(scale);
|
||||||
},
|
},
|
||||||
TimeCursorInc => {
|
TimeCursorInc => {
|
||||||
let scale = state.time_axis.read().unwrap().scale;
|
let scale = state.time_axis().read().unwrap().scale;
|
||||||
state.time_axis.write().unwrap().point_inc(scale);
|
state.time_axis().write().unwrap().point_inc(scale);
|
||||||
},
|
},
|
||||||
TimeScrollDec => {
|
TimeScrollDec => {
|
||||||
let scale = state.time_axis.read().unwrap().scale;
|
let scale = state.time_axis().read().unwrap().scale;
|
||||||
state.time_axis.write().unwrap().start_dec(scale);
|
state.time_axis().write().unwrap().start_dec(scale);
|
||||||
},
|
},
|
||||||
TimeScrollInc => {
|
TimeScrollInc => {
|
||||||
let scale = state.time_axis.read().unwrap().scale;
|
let scale = state.time_axis().read().unwrap().scale;
|
||||||
state.time_axis.write().unwrap().start_inc(scale);
|
state.time_axis().write().unwrap().start_inc(scale);
|
||||||
},
|
},
|
||||||
NoteCursorDec => {
|
NoteCursorDec => {
|
||||||
let mut axis = state.note_axis.write().unwrap();
|
let mut axis = state.note_axis().write().unwrap();
|
||||||
axis.point_inc(1);
|
axis.point_inc(1);
|
||||||
if let Some(point) = axis.point { if point > 73 { axis.point = Some(73); } }
|
if let Some(point) = axis.point { if point > 73 { axis.point = Some(73); } }
|
||||||
},
|
},
|
||||||
NoteCursorInc => {
|
NoteCursorInc => {
|
||||||
let mut axis = state.note_axis.write().unwrap();
|
let mut axis = state.note_axis().write().unwrap();
|
||||||
axis.point_dec(1);
|
axis.point_dec(1);
|
||||||
if let Some(point) = axis.point { if point < axis.start { axis.start = (point / 2) * 2; } }
|
if let Some(point) = axis.point { if point < axis.start { axis.start = (point / 2) * 2; } }
|
||||||
},
|
},
|
||||||
NoteScrollDec => {
|
NoteScrollDec => {
|
||||||
state.note_axis.write().unwrap().start_inc(1);
|
state.note_axis().write().unwrap().start_inc(1);
|
||||||
},
|
},
|
||||||
NoteScrollInc => {
|
NoteScrollInc => {
|
||||||
state.note_axis.write().unwrap().start_dec(1);
|
state.note_axis().write().unwrap().start_dec(1);
|
||||||
},
|
},
|
||||||
NoteLengthDec => {
|
NoteLengthDec => {
|
||||||
state.note_len = prev_note_length(state.note_len)
|
*state.note_len_mut() = prev_note_length(state.note_len())
|
||||||
},
|
},
|
||||||
NoteLengthInc => {
|
NoteLengthInc => {
|
||||||
state.note_len = next_note_length(state.note_len)
|
*state.note_len_mut() = next_note_length(state.note_len())
|
||||||
},
|
},
|
||||||
NotePageUp => {
|
NotePageUp => {
|
||||||
let mut axis = state.note_axis.write().unwrap();
|
let mut axis = state.note_axis().write().unwrap();
|
||||||
axis.start_dec(3);
|
axis.start_dec(3);
|
||||||
axis.point_dec(3);
|
axis.point_dec(3);
|
||||||
},
|
},
|
||||||
NotePageDown => {
|
NotePageDown => {
|
||||||
let mut axis = state.note_axis.write().unwrap();
|
let mut axis = state.note_axis().write().unwrap();
|
||||||
axis.start_inc(3);
|
axis.start_inc(3);
|
||||||
axis.point_inc(3);
|
axis.point_inc(3);
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,9 @@ impl<'a, T: TransportViewState> Content for TransportView<'a, T> {
|
||||||
type Engine = Tui;
|
type Engine = Tui;
|
||||||
fn content (&self) -> impl Widget<Engine = Tui> {
|
fn content (&self) -> impl Widget<Engine = Tui> {
|
||||||
let state = self.0;
|
let state = self.0;
|
||||||
|
let focused = state.transport_focused();
|
||||||
lay!(
|
lay!(
|
||||||
state.focus().wrap(state.is_focused(), TransportFocus::PlayPause, &Styled(
|
state.transport_selected().wrap(focused, TransportFocus::PlayPause, &Styled(
|
||||||
None,
|
None,
|
||||||
match state.transport_state() {
|
match state.transport_state() {
|
||||||
Some(TransportState::Rolling) => "▶ PLAYING",
|
Some(TransportState::Rolling) => "▶ PLAYING",
|
||||||
|
|
@ -16,19 +17,19 @@ impl<'a, T: TransportViewState> Content for TransportView<'a, T> {
|
||||||
).min_xy(11, 2).push_x(1)).align_x().fill_x(),
|
).min_xy(11, 2).push_x(1)).align_x().fill_x(),
|
||||||
|
|
||||||
row!(
|
row!(
|
||||||
state.focus().wrap(state.is_focused(), TransportFocus::Bpm, &Outset::X(1u16, {
|
state.transport_selected().wrap(focused, TransportFocus::Bpm, &Outset::X(1u16, {
|
||||||
let bpm = state.bpm_value();
|
let bpm = state.bpm_value();
|
||||||
row! { "BPM ", format!("{}.{:03}", bpm as usize, (bpm * 1000.0) % 1000.0) }
|
row! { "BPM ", format!("{}.{:03}", bpm as usize, (bpm * 1000.0) % 1000.0) }
|
||||||
})),
|
})),
|
||||||
//let quant = state.focus().wrap(state.focused(), TransportFocus::Quant, &Outset::X(1u16, row! {
|
//let quant = state.focus().wrap(state.focused(), TransportFocus::Quant, &Outset::X(1u16, row! {
|
||||||
//"QUANT ", ppq_to_name(state.0.quant as usize)
|
//"QUANT ", ppq_to_name(state.0.quant as usize)
|
||||||
//})),
|
//})),
|
||||||
state.focus().wrap(state.is_focused(), TransportFocus::Sync, &Outset::X(1u16, row! {
|
state.transport_selected().wrap(focused, TransportFocus::Sync, &Outset::X(1u16, row! {
|
||||||
"SYNC ", pulses_to_name(state.sync_value() as usize)
|
"SYNC ", pulses_to_name(state.sync_value() as usize)
|
||||||
}))
|
}))
|
||||||
).align_w().fill_x(),
|
).align_w().fill_x(),
|
||||||
|
|
||||||
state.focus().wrap(state.is_focused(), TransportFocus::Clock, &{
|
state.transport_selected().wrap(focused, TransportFocus::Clock, &{
|
||||||
let time1 = state.format_beat();
|
let time1 = state.format_beat();
|
||||||
let time2 = state.format_msu();
|
let time2 = state.format_msu();
|
||||||
row!("B" ,time1.as_str(), " T", time2.as_str()).outset_x(1)
|
row!("B" ,time1.as_str(), " T", time2.as_str()).outset_x(1)
|
||||||
|
|
@ -55,6 +56,7 @@ impl Content for SequencerTui {
|
||||||
impl Content for ArrangerTui {
|
impl Content for ArrangerTui {
|
||||||
type Engine = Tui;
|
type Engine = Tui;
|
||||||
fn content (&self) -> impl Widget<Engine = Tui> {
|
fn content (&self) -> impl Widget<Engine = Tui> {
|
||||||
|
let arranger_focused = self.arranger_focused();
|
||||||
Split::up(
|
Split::up(
|
||||||
1,
|
1,
|
||||||
widget(&TransportView(self)),
|
widget(&TransportView(self)),
|
||||||
|
|
@ -73,14 +75,14 @@ impl Content for ArrangerTui {
|
||||||
.grow_y(1)
|
.grow_y(1)
|
||||||
.border(Lozenge(Style::default()
|
.border(Lozenge(Style::default()
|
||||||
.bg(TuiTheme::border_bg())
|
.bg(TuiTheme::border_bg())
|
||||||
.fg(TuiTheme::border_fg(ArrangerViewState::focused(self))))),
|
.fg(TuiTheme::border_fg(arranger_focused)))),
|
||||||
widget(&self.size),
|
widget(&self.size),
|
||||||
widget(&format!("[{}] Arranger", if self.entered {
|
widget(&format!("[{}] Arranger", if self.entered {
|
||||||
"■"
|
"■"
|
||||||
} else {
|
} else {
|
||||||
" "
|
" "
|
||||||
}))
|
}))
|
||||||
.fg(TuiTheme::title_fg(ArrangerViewState::focused(self)))
|
.fg(TuiTheme::title_fg(arranger_focused))
|
||||||
.push_x(1),
|
.push_x(1),
|
||||||
),
|
),
|
||||||
Split::right(
|
Split::right(
|
||||||
|
|
@ -97,7 +99,7 @@ impl Content for ArrangerTui {
|
||||||
impl<'a, T: PhrasesViewState> Content for PhrasesView<'a, T> {
|
impl<'a, T: PhrasesViewState> Content for PhrasesView<'a, T> {
|
||||||
type Engine = Tui;
|
type Engine = Tui;
|
||||||
fn content (&self) -> impl Widget<Engine = Tui> {
|
fn content (&self) -> impl Widget<Engine = Tui> {
|
||||||
let focused = self.0.focused();
|
let focused = self.0.phrases_focused();
|
||||||
let entered = self.0.entered();
|
let entered = self.0.entered();
|
||||||
let phrases = self.0.phrases();
|
let phrases = self.0.phrases();
|
||||||
let selected_phrase = self.0.phrase();
|
let selected_phrase = self.0.phrase();
|
||||||
|
|
@ -147,7 +149,7 @@ impl<'a, T: PhraseViewState> Content for PhraseView<'a, T> {
|
||||||
fn content (&self) -> impl Widget<Engine = Tui> {
|
fn content (&self) -> impl Widget<Engine = Tui> {
|
||||||
let phrase = self.0.phrase();
|
let phrase = self.0.phrase();
|
||||||
let size = self.0.size();
|
let size = self.0.size();
|
||||||
let focused = self.0.focused();
|
let focused = self.0.phrase_focused();
|
||||||
let entered = self.0.entered();
|
let entered = self.0.entered();
|
||||||
let keys = self.0.keys();
|
let keys = self.0.keys();
|
||||||
let buffer = self.0.buffer();
|
let buffer = self.0.buffer();
|
||||||
|
|
|
||||||
|
|
@ -1,58 +1,16 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub trait TransportControl {
|
pub trait TransportControl: ClockApi {}
|
||||||
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 {
|
impl TransportControl for TransportTui {}
|
||||||
fn bpm (&self) -> &BeatsPerMinute {
|
|
||||||
&self.current.timebase.bpm
|
|
||||||
}
|
|
||||||
fn quant (&self) -> &Quantize {
|
|
||||||
&self.quant
|
|
||||||
}
|
|
||||||
fn sync (&self) -> &LaunchSync {
|
|
||||||
&self.sync
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TransportControl for SequencerTui {
|
impl TransportControl for SequencerTui {}
|
||||||
fn bpm (&self) -> &BeatsPerMinute {
|
|
||||||
&self.current.timebase.bpm
|
|
||||||
}
|
|
||||||
fn quant (&self) -> &Quantize {
|
|
||||||
&self.quant
|
|
||||||
}
|
|
||||||
fn sync (&self) -> &LaunchSync {
|
|
||||||
&self.sync
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TransportControl for ArrangerTui {
|
impl TransportControl for ArrangerTui {}
|
||||||
fn bpm (&self) -> &BeatsPerMinute {
|
|
||||||
&self.current.timebase.bpm
|
pub trait SequencerControl {}
|
||||||
}
|
|
||||||
fn quant (&self) -> &Quantize {
|
impl SequencerControl for SequencerTui {}
|
||||||
&self.quant
|
|
||||||
}
|
|
||||||
fn sync (&self) -> &LaunchSync {
|
|
||||||
&self.sync
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ArrangerControl {
|
pub trait ArrangerControl {
|
||||||
fn selected (&self) -> ArrangerSelection;
|
fn selected (&self) -> ArrangerSelection;
|
||||||
|
|
@ -63,3 +21,93 @@ impl ArrangerControl for ArrangerTui {
|
||||||
self.selected
|
self.selected
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait PhrasesControl {
|
||||||
|
fn phrases_mode (&self) -> &Option<PhrasesMode>;
|
||||||
|
fn phrases_mode_mut (&mut self) -> &mut Option<PhrasesMode>;
|
||||||
|
fn phrase_rename_begin (&mut self) {
|
||||||
|
*self.phrases_mode_mut() = Some(PhrasesMode::Rename(
|
||||||
|
self.phrase,
|
||||||
|
self.phrases[self.phrase].read().unwrap().name.clone()
|
||||||
|
))
|
||||||
|
}
|
||||||
|
fn phrase_length_begin (&mut self) {
|
||||||
|
*self.phrases_mode_mut() = Some(PhrasesMode::Length(
|
||||||
|
self.phrase,
|
||||||
|
self.phrases[self.phrase].read().unwrap().length,
|
||||||
|
PhraseLengthFocus::Bar
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PhrasesControl for SequencerTui {
|
||||||
|
fn phrases_mode (&self) -> &Option<PhrasesMode> {
|
||||||
|
&self.phrases_mode
|
||||||
|
}
|
||||||
|
fn phrases_mode_mut (&mut self) -> &mut Option<PhrasesMode> {
|
||||||
|
&mut self.phrases_mode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PhrasesControl for ArrangerTui {
|
||||||
|
fn phrases_mode (&self) -> &Option<PhrasesMode> {
|
||||||
|
&self.phrases_mode
|
||||||
|
}
|
||||||
|
fn phrases_mode_mut (&mut self) -> &mut Option<PhrasesMode> {
|
||||||
|
&mut self.phrases_mode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PhrasesControl for PhrasesTui {
|
||||||
|
fn phrases_mode (&self) -> &Option<PhrasesMode> {
|
||||||
|
&self.mode
|
||||||
|
}
|
||||||
|
fn phrases_mode_mut (&mut self) -> &mut Option<PhrasesMode> {
|
||||||
|
&mut self.mode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub trait PhraseControl {
|
||||||
|
fn phrase_entered (&self) -> bool;
|
||||||
|
fn time_axis (&self) -> &RwLock<ScaledAxis<usize>>;
|
||||||
|
fn note_axis (&self) -> &RwLock<FixedAxis<usize>>;
|
||||||
|
fn note_len (&self) -> usize;
|
||||||
|
fn note_len_mut (&mut self) -> &mut usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PhraseControl for SequencerTui {
|
||||||
|
fn phrase_entered (&self) -> bool {
|
||||||
|
self.entered && self.focused() == SequencerFocus::PhraseEditor
|
||||||
|
}
|
||||||
|
fn time_axis (&self) -> &RwLock<ScaledAxis<usize>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn note_axis (&self) -> &RwLock<FixedAxis<usize>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn note_len (&self) -> usize {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn note_len_mut (&mut self) -> &mut usize {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PhraseControl for ArrangerTui {
|
||||||
|
fn phrase_entered (&self) -> bool {
|
||||||
|
self.entered && self.focused() == ArrangerFocus::PhraseEditor
|
||||||
|
}
|
||||||
|
fn time_axis (&self) -> &RwLock<ScaledAxis<usize>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn note_axis (&self) -> &RwLock<FixedAxis<usize>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn note_len (&self) -> usize {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn note_len_mut (&mut self) -> &mut usize {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ impl TryFrom<&Arc<RwLock<JackClient>>> for SequencerTui {
|
||||||
transport: jack.read().unwrap().transport(),
|
transport: jack.read().unwrap().transport(),
|
||||||
jack: jack.clone(),
|
jack: jack.clone(),
|
||||||
focused: false,
|
focused: false,
|
||||||
focus: TransportFocus::PlayPause,
|
|
||||||
size: Measure::new(),
|
size: Measure::new(),
|
||||||
}.into(), None, None))
|
}.into(), None, None))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,10 +60,10 @@ impl InputToCommand<Tui, SequencerTui> for SequencerCommand {
|
||||||
key!(KeyCode::Down) => Some(Self::Focus(Down)),
|
key!(KeyCode::Down) => Some(Self::Focus(Down)),
|
||||||
key!(KeyCode::Left) => Some(Self::Focus(Left)),
|
key!(KeyCode::Left) => Some(Self::Focus(Left)),
|
||||||
key!(KeyCode::Right) => Some(Self::Focus(Right)),
|
key!(KeyCode::Right) => Some(Self::Focus(Right)),
|
||||||
_ => Some(Self::App(match state.focused() {
|
_ => Some(match state.focused() {
|
||||||
SequencerFocus::Transport => {
|
SequencerFocus::Transport => {
|
||||||
use TransportCommand::{Clock, Playhead};
|
use TransportCommand::{Clock, Playhead};
|
||||||
match TransportCommand::input_to_command(view, input)? {
|
match TransportCommand::input_to_command(state, input)? {
|
||||||
Clock(command) => {
|
Clock(command) => {
|
||||||
todo!()
|
todo!()
|
||||||
},
|
},
|
||||||
|
|
@ -72,18 +72,20 @@ impl InputToCommand<Tui, SequencerTui> for SequencerCommand {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
SequencerFocus::Phrases =>
|
SequencerFocus::Phrases => {
|
||||||
PhrasesCommand::input_to_command(&state.phrases, input).map(Phrases),
|
PhrasesCommand::input_to_command(state, input).map(Phrases)
|
||||||
SequencerFocus::PhraseEditor =>
|
},
|
||||||
PhraseCommand::input_to_command(&state.editor, input).map(Editor),
|
SequencerFocus::PhraseEditor => {
|
||||||
|
PhraseCommand::input_to_command(state, input).map(Editor)
|
||||||
|
},
|
||||||
_ => return None,
|
_ => return None,
|
||||||
}))
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ArrangerControl> InputToCommand<Tui, T> for ArrangerCommand {
|
impl<T: ArrangerControl> InputToCommand<Tui, T> for ArrangerCommand {
|
||||||
fn input_to_command (view: &T, input: &TuiInput) -> Option<Self> {
|
fn input_to_command (state: &T, input: &TuiInput) -> Option<Self> {
|
||||||
use FocusCommand::*;
|
use FocusCommand::*;
|
||||||
use ArrangerCommand::*;
|
use ArrangerCommand::*;
|
||||||
Some(match input.event() {
|
Some(match input.event() {
|
||||||
|
|
@ -97,13 +99,11 @@ impl<T: ArrangerControl> InputToCommand<Tui, T> for ArrangerCommand {
|
||||||
key!(KeyCode::Right) => Self::Focus(Right),
|
key!(KeyCode::Right) => Self::Focus(Right),
|
||||||
key!(KeyCode::Enter) => Self::Focus(Enter),
|
key!(KeyCode::Enter) => Self::Focus(Enter),
|
||||||
key!(KeyCode::Esc) => Self::Focus(Exit),
|
key!(KeyCode::Esc) => Self::Focus(Exit),
|
||||||
key!(KeyCode::Char(' ')) => {
|
key!(KeyCode::Char(' ')) => Self::Playhead(PlayheadCommand::Play(None)),
|
||||||
Self::App(Playhead(PlayheadCommand::Play(None)))
|
_ => match state.focused() {
|
||||||
},
|
|
||||||
_ => Self::App(match view.focused() {
|
|
||||||
ArrangerFocus::Transport => {
|
ArrangerFocus::Transport => {
|
||||||
use TransportCommand::{Clock, Playhead};
|
use TransportCommand::{Clock, Playhead};
|
||||||
match TransportCommand::input_to_command(view, input)? {
|
match TransportCommand::input_to_command(state, input)? {
|
||||||
Clock(command) => {
|
Clock(command) => {
|
||||||
todo!()
|
todo!()
|
||||||
},
|
},
|
||||||
|
|
@ -113,14 +113,14 @@ impl<T: ArrangerControl> InputToCommand<Tui, T> for ArrangerCommand {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ArrangerFocus::PhraseEditor => Editor(
|
ArrangerFocus::PhraseEditor => Editor(
|
||||||
PhraseCommand::input_to_command(&view.editor, input)?
|
PhraseCommand::input_to_command(state, input)?
|
||||||
),
|
),
|
||||||
ArrangerFocus::PhrasePool => match input.event() {
|
ArrangerFocus::PhrasePool => match input.event() {
|
||||||
key!(KeyCode::Char('e')) => EditPhrase(
|
key!(KeyCode::Char('e')) => EditPhrase(
|
||||||
Some(view.phrase().clone())
|
Some(state.phrase().clone())
|
||||||
),
|
),
|
||||||
_ => Phrases(
|
_ => Phrases(
|
||||||
PhrasePoolCommand::input_to_command(view, input)?
|
PhrasePoolCommand::input_to_command(state, input)?
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
ArrangerFocus::Arranger => {
|
ArrangerFocus::Arranger => {
|
||||||
|
|
@ -129,32 +129,32 @@ impl<T: ArrangerControl> InputToCommand<Tui, T> for ArrangerCommand {
|
||||||
use ArrangerClipCommand as Clip;
|
use ArrangerClipCommand as Clip;
|
||||||
use ArrangerSceneCommand as Scene;
|
use ArrangerSceneCommand as Scene;
|
||||||
match input.event() {
|
match input.event() {
|
||||||
key!(KeyCode::Char('e')) => EditPhrase(view.phrase()),
|
key!(KeyCode::Char('e')) => EditPhrase(state.phrase()),
|
||||||
_ => match input.event() {
|
_ => match input.event() {
|
||||||
// FIXME: boundary conditions
|
// FIXME: boundary conditions
|
||||||
|
|
||||||
key!(KeyCode::Up) => match view.selected() {
|
key!(KeyCode::Up) => match state.selected() {
|
||||||
Select::Mix => return None,
|
Select::Mix => return None,
|
||||||
Select::Track(t) => return None,
|
Select::Track(t) => return None,
|
||||||
Select::Scene(s) => Select(Select::Scene(s - 1)),
|
Select::Scene(s) => Select(Select::Scene(s - 1)),
|
||||||
Select::Clip(t, s) => Select(Select::Clip(t, s - 1)),
|
Select::Clip(t, s) => Select(Select::Clip(t, s - 1)),
|
||||||
},
|
},
|
||||||
|
|
||||||
key!(KeyCode::Down) => match view.selected() {
|
key!(KeyCode::Down) => match state.selected() {
|
||||||
Select::Mix => Select(Select::Scene(0)),
|
Select::Mix => Select(Select::Scene(0)),
|
||||||
Select::Track(t) => Select(Select::Clip(t, 0)),
|
Select::Track(t) => Select(Select::Clip(t, 0)),
|
||||||
Select::Scene(s) => Select(Select::Scene(s + 1)),
|
Select::Scene(s) => Select(Select::Scene(s + 1)),
|
||||||
Select::Clip(t, s) => Select(Select::Clip(t, s + 1)),
|
Select::Clip(t, s) => Select(Select::Clip(t, s + 1)),
|
||||||
},
|
},
|
||||||
|
|
||||||
key!(KeyCode::Left) => match view.selected() {
|
key!(KeyCode::Left) => match state.selected() {
|
||||||
Select::Mix => return None,
|
Select::Mix => return None,
|
||||||
Select::Track(t) => Select(Select::Track(t - 1)),
|
Select::Track(t) => Select(Select::Track(t - 1)),
|
||||||
Select::Scene(s) => return None,
|
Select::Scene(s) => return None,
|
||||||
Select::Clip(t, s) => Select(Select::Clip(t - 1, s)),
|
Select::Clip(t, s) => Select(Select::Clip(t - 1, s)),
|
||||||
},
|
},
|
||||||
|
|
||||||
key!(KeyCode::Right) => match view.selected() {
|
key!(KeyCode::Right) => match state.selected() {
|
||||||
Select::Mix => return None,
|
Select::Mix => return None,
|
||||||
Select::Track(t) => Select(Select::Track(t + 1)),
|
Select::Track(t) => Select(Select::Track(t + 1)),
|
||||||
Select::Scene(s) => Select(Select::Clip(0, s)),
|
Select::Scene(s) => Select(Select::Clip(0, s)),
|
||||||
|
|
@ -169,44 +169,44 @@ impl<T: ArrangerControl> InputToCommand<Tui, T> for ArrangerCommand {
|
||||||
|
|
||||||
key!(KeyCode::Char('-')) => Zoom(0),
|
key!(KeyCode::Char('-')) => Zoom(0),
|
||||||
|
|
||||||
key!(KeyCode::Char('`')) => { todo!("toggle view mode") },
|
key!(KeyCode::Char('`')) => { todo!("toggle state mode") },
|
||||||
|
|
||||||
key!(KeyCode::Char(',')) => match view.selected() {
|
key!(KeyCode::Char(',')) => match state.selected() {
|
||||||
Select::Mix => Zoom(0),
|
Select::Mix => Zoom(0),
|
||||||
Select::Track(t) => Track(Track::Swap(t, t - 1)),
|
Select::Track(t) => Track(Track::Swap(t, t - 1)),
|
||||||
Select::Scene(s) => Scene(Scene::Swap(s, s - 1)),
|
Select::Scene(s) => Scene(Scene::Swap(s, s - 1)),
|
||||||
Select::Clip(t, s) => Clip(Clip::Set(t, s, None)),
|
Select::Clip(t, s) => Clip(Clip::Set(t, s, None)),
|
||||||
},
|
},
|
||||||
|
|
||||||
key!(KeyCode::Char('.')) => match view.selected() {
|
key!(KeyCode::Char('.')) => match state.selected() {
|
||||||
Select::Mix => Zoom(0),
|
Select::Mix => Zoom(0),
|
||||||
Select::Track(t) => Track(Track::Swap(t, t + 1)),
|
Select::Track(t) => Track(Track::Swap(t, t + 1)),
|
||||||
Select::Scene(s) => Scene(Scene::Swap(s, s + 1)),
|
Select::Scene(s) => Scene(Scene::Swap(s, s + 1)),
|
||||||
Select::Clip(t, s) => Clip(Clip::Set(t, s, None)),
|
Select::Clip(t, s) => Clip(Clip::Set(t, s, None)),
|
||||||
},
|
},
|
||||||
|
|
||||||
key!(KeyCode::Char('<')) => match view.selected() {
|
key!(KeyCode::Char('<')) => match state.selected() {
|
||||||
Select::Mix => Zoom(0),
|
Select::Mix => Zoom(0),
|
||||||
Select::Track(t) => Track(Track::Swap(t, t - 1)),
|
Select::Track(t) => Track(Track::Swap(t, t - 1)),
|
||||||
Select::Scene(s) => Scene(Scene::Swap(s, s - 1)),
|
Select::Scene(s) => Scene(Scene::Swap(s, s - 1)),
|
||||||
Select::Clip(t, s) => Clip(Clip::Set(t, s, None)),
|
Select::Clip(t, s) => Clip(Clip::Set(t, s, None)),
|
||||||
},
|
},
|
||||||
|
|
||||||
key!(KeyCode::Char('>')) => match view.selected() {
|
key!(KeyCode::Char('>')) => match state.selected() {
|
||||||
Select::Mix => Zoom(0),
|
Select::Mix => Zoom(0),
|
||||||
Select::Track(t) => Track(Track::Swap(t, t + 1)),
|
Select::Track(t) => Track(Track::Swap(t, t + 1)),
|
||||||
Select::Scene(s) => Scene(Scene::Swap(s, s + 1)),
|
Select::Scene(s) => Scene(Scene::Swap(s, s + 1)),
|
||||||
Select::Clip(t, s) => Clip(Clip::Set(t, s, None)),
|
Select::Clip(t, s) => Clip(Clip::Set(t, s, None)),
|
||||||
},
|
},
|
||||||
|
|
||||||
key!(KeyCode::Enter) => match view.selected() {
|
key!(KeyCode::Enter) => match state.selected() {
|
||||||
Select::Mix => return None,
|
Select::Mix => return None,
|
||||||
Select::Track(t) => return None,
|
Select::Track(t) => return None,
|
||||||
Select::Scene(s) => Scene(Scene::Play(s)),
|
Select::Scene(s) => Scene(Scene::Play(s)),
|
||||||
Select::Clip(t, s) => return None,
|
Select::Clip(t, s) => return None,
|
||||||
},
|
},
|
||||||
|
|
||||||
key!(KeyCode::Delete) => match view.selected() {
|
key!(KeyCode::Delete) => match state.selected() {
|
||||||
Select::Mix => Clear,
|
Select::Mix => Clear,
|
||||||
Select::Track(t) => Track(Track::Delete(t)),
|
Select::Track(t) => Track(Track::Delete(t)),
|
||||||
Select::Scene(s) => Scene(Scene::Delete(s)),
|
Select::Scene(s) => Scene(Scene::Delete(s)),
|
||||||
|
|
@ -215,12 +215,12 @@ impl<T: ArrangerControl> InputToCommand<Tui, T> for ArrangerCommand {
|
||||||
|
|
||||||
key!(KeyCode::Char('c')) => Clip(Clip::RandomColor),
|
key!(KeyCode::Char('c')) => Clip(Clip::RandomColor),
|
||||||
|
|
||||||
key!(KeyCode::Char('s')) => match view.selected() {
|
key!(KeyCode::Char('s')) => match state.selected() {
|
||||||
Select::Clip(t, s) => Clip(Clip::Set(t, s, None)),
|
Select::Clip(t, s) => Clip(Clip::Set(t, s, None)),
|
||||||
_ => return None,
|
_ => return None,
|
||||||
},
|
},
|
||||||
|
|
||||||
key!(KeyCode::Char('g')) => match view.selected() {
|
key!(KeyCode::Char('g')) => match state.selected() {
|
||||||
Select::Clip(t, s) => Clip(Clip::Get(t, s)),
|
Select::Clip(t, s) => Clip(Clip::Get(t, s)),
|
||||||
_ => return None,
|
_ => return None,
|
||||||
},
|
},
|
||||||
|
|
@ -235,13 +235,13 @@ impl<T: ArrangerControl> InputToCommand<Tui, T> for ArrangerCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputToCommand<Tui, PhrasesTui> for PhrasesCommand {
|
impl<T: PhrasesControl> InputToCommand<Tui, T> for PhrasesCommand {
|
||||||
fn input_to_command (state: &PhrasesTui, input: &TuiInput) -> Option<Self> {
|
fn input_to_command (state: &T, input: &TuiInput) -> Option<Self> {
|
||||||
use PhrasesCommand as Cmd;
|
use PhrasesCommand as Cmd;
|
||||||
use PhrasePoolCommand as Edit;
|
use PhrasePoolCommand as Edit;
|
||||||
use PhraseRenameCommand as Rename;
|
use PhraseRenameCommand as Rename;
|
||||||
|
|
@ -258,7 +258,7 @@ impl InputToCommand<Tui, PhrasesTui> for PhrasesCommand {
|
||||||
key!(KeyCode::Char('c')) => Some(Cmd::Edit(Edit::RandomColor(0))),
|
key!(KeyCode::Char('c')) => Some(Cmd::Edit(Edit::RandomColor(0))),
|
||||||
key!(KeyCode::Char('n')) => Some(Cmd::Rename(Rename::Begin)),
|
key!(KeyCode::Char('n')) => Some(Cmd::Rename(Rename::Begin)),
|
||||||
key!(KeyCode::Char('t')) => Some(Cmd::Length(Length::Begin)),
|
key!(KeyCode::Char('t')) => Some(Cmd::Length(Length::Begin)),
|
||||||
_ => match state.mode {
|
_ => match state.phrases_mode() {
|
||||||
Some(PhrasesMode::Rename(..)) => {
|
Some(PhrasesMode::Rename(..)) => {
|
||||||
Rename::input_to_command(state, input).map(Cmd::Rename)
|
Rename::input_to_command(state, input).map(Cmd::Rename)
|
||||||
},
|
},
|
||||||
|
|
@ -271,9 +271,9 @@ impl InputToCommand<Tui, PhrasesTui> for PhrasesCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputToCommand<Tui, PhrasesTui> for PhraseLengthCommand {
|
impl<T: PhrasesControl> InputToCommand<Tui, T> for PhraseLengthCommand {
|
||||||
fn input_to_command (view: &PhrasesTui, from: &TuiInput) -> Option<Self> {
|
fn input_to_command (state: &T, from: &TuiInput) -> Option<Self> {
|
||||||
if let Some(PhrasesMode::Length(_, length, _)) = view.mode {
|
if let Some(PhrasesMode::Length(_, length, _)) = state.phrases_mode() {
|
||||||
Some(match from.event() {
|
Some(match from.event() {
|
||||||
key!(KeyCode::Up) => Self::Inc,
|
key!(KeyCode::Up) => Self::Inc,
|
||||||
key!(KeyCode::Down) => Self::Dec,
|
key!(KeyCode::Down) => Self::Dec,
|
||||||
|
|
@ -289,9 +289,9 @@ impl InputToCommand<Tui, PhrasesTui> for PhraseLengthCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputToCommand<Tui, PhrasesTui> for PhraseRenameCommand {
|
impl<T: PhrasesControl> InputToCommand<Tui, T> for PhraseRenameCommand {
|
||||||
fn input_to_command (view: &PhrasesTui, from: &TuiInput) -> Option<Self> {
|
fn input_to_command (state: &T, from: &TuiInput) -> Option<Self> {
|
||||||
if let Some(PhrasesMode::Rename(_, ref old_name)) = view.mode {
|
if let Some(PhrasesMode::Rename(_, ref old_name)) = state.phrases_mode() {
|
||||||
Some(match from.event() {
|
Some(match from.event() {
|
||||||
key!(KeyCode::Char(c)) => {
|
key!(KeyCode::Char(c)) => {
|
||||||
let mut new_name = old_name.clone();
|
let mut new_name = old_name.clone();
|
||||||
|
|
@ -313,8 +313,8 @@ impl InputToCommand<Tui, PhrasesTui> for PhraseRenameCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputToCommand<Tui, PhraseTui> for PhraseCommand {
|
impl<T: PhraseControl> InputToCommand<Tui, T> for PhraseCommand {
|
||||||
fn input_to_command (state: &PhraseTui, from: &TuiInput) -> Option<Self> {
|
fn input_to_command (state: &T, from: &TuiInput) -> Option<Self> {
|
||||||
use PhraseCommand::*;
|
use PhraseCommand::*;
|
||||||
Some(match from.event() {
|
Some(match from.event() {
|
||||||
key!(KeyCode::Char('`')) => ToggleDirection,
|
key!(KeyCode::Char('`')) => ToggleDirection,
|
||||||
|
|
@ -330,19 +330,19 @@ impl InputToCommand<Tui, PhraseTui> for PhraseCommand {
|
||||||
key!(KeyCode::Char('+')) => TimeZoomSet(0),
|
key!(KeyCode::Char('+')) => TimeZoomSet(0),
|
||||||
key!(KeyCode::PageUp) => NoteScrollSet(0),
|
key!(KeyCode::PageUp) => NoteScrollSet(0),
|
||||||
key!(KeyCode::PageDown) => NoteScrollSet(0),
|
key!(KeyCode::PageDown) => NoteScrollSet(0),
|
||||||
key!(KeyCode::Up) => match state.entered {
|
key!(KeyCode::Up) => match state.phrase_entered() {
|
||||||
true => NoteCursorSet(0),
|
true => NoteCursorSet(0),
|
||||||
false => NoteScrollSet(0),
|
false => NoteScrollSet(0),
|
||||||
},
|
},
|
||||||
key!(KeyCode::Down) => match state.entered {
|
key!(KeyCode::Down) => match state.phrase_entered() {
|
||||||
true => NoteCursorSet(0),
|
true => NoteCursorSet(0),
|
||||||
false => NoteScrollSet(0),
|
false => NoteScrollSet(0),
|
||||||
},
|
},
|
||||||
key!(KeyCode::Left) => match state.entered {
|
key!(KeyCode::Left) => match state.phrase_entered() {
|
||||||
true => TimeCursorSet(0),
|
true => TimeCursorSet(0),
|
||||||
false => TimeScrollSet(0),
|
false => TimeScrollSet(0),
|
||||||
},
|
},
|
||||||
key!(KeyCode::Right) => match state.entered {
|
key!(KeyCode::Right) => match state.phrase_entered() {
|
||||||
true => TimeCursorSet(0),
|
true => TimeCursorSet(0),
|
||||||
false => TimeScrollSet(0),
|
false => TimeScrollSet(0),
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,10 @@ pub struct SequencerTui {
|
||||||
|
|
||||||
pub(crate) entered: bool,
|
pub(crate) entered: bool,
|
||||||
pub(crate) cursor: (usize, usize),
|
pub(crate) cursor: (usize, usize),
|
||||||
|
pub(crate) size: Measure<Tui>,
|
||||||
|
|
||||||
|
/// Mode switch
|
||||||
|
pub(crate) phrases_mode: Option<PhrasesMode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Root view for standalone `tek_arranger`
|
/// Root view for standalone `tek_arranger`
|
||||||
|
|
@ -148,7 +152,9 @@ pub struct ArrangerTrack {
|
||||||
/// Whether this widget is focused
|
/// Whether this widget is focused
|
||||||
pub(crate) focused: bool,
|
pub(crate) focused: bool,
|
||||||
/// Width and height of notes area at last render
|
/// Width and height of notes area at last render
|
||||||
pub(crate) size: Measure<Tui>
|
pub(crate) size: Measure<Tui>,
|
||||||
|
/// Mode switch
|
||||||
|
pub(crate) phrases_mode: Option<PhrasesMode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PhrasesTui {
|
pub struct PhrasesTui {
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ use crate::*;
|
||||||
pub struct TransportView<'a, T: TransportViewState>(pub &'a T);
|
pub struct TransportView<'a, T: TransportViewState>(pub &'a T);
|
||||||
|
|
||||||
pub trait TransportViewState: Send + Sync {
|
pub trait TransportViewState: Send + Sync {
|
||||||
fn focus (&self) -> TransportFocus;
|
fn transport_selected (&self) -> TransportFocus;
|
||||||
fn is_focused (&self) -> bool;
|
fn transport_focused (&self) -> bool;
|
||||||
fn transport_state (&self) -> Option<TransportState>;
|
fn transport_state (&self) -> Option<TransportState>;
|
||||||
fn bpm_value (&self) -> f64;
|
fn bpm_value (&self) -> f64;
|
||||||
fn sync_value (&self) -> f64;
|
fn sync_value (&self) -> f64;
|
||||||
|
|
@ -13,10 +13,10 @@ pub trait TransportViewState: Send + Sync {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransportViewState for TransportTui {
|
impl TransportViewState for TransportTui {
|
||||||
fn focus (&self) -> TransportFocus {
|
fn transport_selected (&self) -> TransportFocus {
|
||||||
self.focus
|
self.focus
|
||||||
}
|
}
|
||||||
fn is_focused (&self) -> bool {
|
fn transport_focused (&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
fn transport_state (&self) -> Option<TransportState> {
|
fn transport_state (&self) -> Option<TransportState> {
|
||||||
|
|
@ -37,10 +37,10 @@ impl TransportViewState for TransportTui {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransportViewState for SequencerTui {
|
impl TransportViewState for SequencerTui {
|
||||||
fn focus (&self) -> TransportFocus {
|
fn transport_selected (&self) -> TransportFocus {
|
||||||
self.focus
|
self.focus
|
||||||
}
|
}
|
||||||
fn is_focused (&self) -> bool {
|
fn transport_focused (&self) -> bool {
|
||||||
self.focused() == SequencerFocus::Transport
|
self.focused() == SequencerFocus::Transport
|
||||||
}
|
}
|
||||||
fn transport_state (&self) -> Option<TransportState> {
|
fn transport_state (&self) -> Option<TransportState> {
|
||||||
|
|
@ -61,10 +61,10 @@ impl TransportViewState for SequencerTui {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransportViewState for ArrangerTui {
|
impl TransportViewState for ArrangerTui {
|
||||||
fn focus (&self) -> TransportFocus {
|
fn transport_selected (&self) -> TransportFocus {
|
||||||
self.focus
|
self.focus
|
||||||
}
|
}
|
||||||
fn is_focused (&self) -> bool {
|
fn transport_focused (&self) -> bool {
|
||||||
self.focused() == ArrangerFocus::Transport
|
self.focused() == ArrangerFocus::Transport
|
||||||
}
|
}
|
||||||
fn transport_state (&self) -> Option<TransportState> {
|
fn transport_state (&self) -> Option<TransportState> {
|
||||||
|
|
@ -85,11 +85,11 @@ impl TransportViewState for ArrangerTui {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ArrangerViewState {
|
pub trait ArrangerViewState {
|
||||||
fn is_focused (&self) -> bool;
|
fn arranger_focused (&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ArrangerViewState for ArrangerTui {
|
impl ArrangerViewState for ArrangerTui {
|
||||||
fn is_focused (&self) -> bool {
|
fn arranger_focused (&self) -> bool {
|
||||||
self.focused() == ArrangerFocus::Arranger
|
self.focused() == ArrangerFocus::Arranger
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -97,7 +97,7 @@ impl ArrangerViewState for ArrangerTui {
|
||||||
pub struct PhrasesView<'a, T: PhrasesViewState>(pub &'a T);
|
pub struct PhrasesView<'a, T: PhrasesViewState>(pub &'a T);
|
||||||
|
|
||||||
pub trait PhrasesViewState: Send + Sync {
|
pub trait PhrasesViewState: Send + Sync {
|
||||||
fn focused (&self) -> bool;
|
fn phrases_focused (&self) -> bool;
|
||||||
fn entered (&self) -> bool;
|
fn entered (&self) -> bool;
|
||||||
fn phrases (&self) -> Vec<Arc<RwLock<Phrase>>>;
|
fn phrases (&self) -> Vec<Arc<RwLock<Phrase>>>;
|
||||||
fn phrase (&self) -> usize;
|
fn phrase (&self) -> usize;
|
||||||
|
|
@ -105,7 +105,7 @@ pub trait PhrasesViewState: Send + Sync {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PhrasesViewState for PhrasesTui {
|
impl PhrasesViewState for PhrasesTui {
|
||||||
fn focused (&self) -> bool {
|
fn phrases_focused (&self) -> bool {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
fn entered (&self) -> bool {
|
fn entered (&self) -> bool {
|
||||||
|
|
@ -123,7 +123,7 @@ impl PhrasesViewState for PhrasesTui {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PhrasesViewState for SequencerTui {
|
impl PhrasesViewState for SequencerTui {
|
||||||
fn focused (&self) -> bool {
|
fn phrases_focused (&self) -> bool {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
fn entered (&self) -> bool {
|
fn entered (&self) -> bool {
|
||||||
|
|
@ -136,12 +136,12 @@ impl PhrasesViewState for SequencerTui {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
fn mode (&self) -> &Option<PhrasesMode> {
|
fn mode (&self) -> &Option<PhrasesMode> {
|
||||||
&self.mode
|
&self.phrases_mode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PhrasesViewState for ArrangerTui {
|
impl PhrasesViewState for ArrangerTui {
|
||||||
fn focused (&self) -> bool {
|
fn phrases_focused (&self) -> bool {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
fn entered (&self) -> bool {
|
fn entered (&self) -> bool {
|
||||||
|
|
@ -154,14 +154,14 @@ impl PhrasesViewState for ArrangerTui {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
fn mode (&self) -> &Option<PhrasesMode> {
|
fn mode (&self) -> &Option<PhrasesMode> {
|
||||||
&self.mode
|
&self.phrases_mode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PhraseView<'a, T: PhraseViewState>(pub &'a T);
|
pub struct PhraseView<'a, T: PhraseViewState>(pub &'a T);
|
||||||
|
|
||||||
pub trait PhraseViewState: Send + Sync {
|
pub trait PhraseViewState: Send + Sync {
|
||||||
fn focused (&self) -> bool;
|
fn phrase_focused (&self) -> bool;
|
||||||
fn entered (&self) -> bool;
|
fn entered (&self) -> bool;
|
||||||
fn keys (&self) -> &Buffer;
|
fn keys (&self) -> &Buffer;
|
||||||
fn phrase (&self) -> &Option<Arc<RwLock<Phrase>>>;
|
fn phrase (&self) -> &Option<Arc<RwLock<Phrase>>>;
|
||||||
|
|
@ -174,7 +174,7 @@ pub trait PhraseViewState: Send + Sync {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PhraseViewState for PhraseTui {
|
impl PhraseViewState for PhraseTui {
|
||||||
fn focused (&self) -> bool {
|
fn phrase_focused (&self) -> bool {
|
||||||
self.focused
|
self.focused
|
||||||
}
|
}
|
||||||
fn entered (&self) -> bool {
|
fn entered (&self) -> bool {
|
||||||
|
|
@ -207,7 +207,7 @@ impl PhraseViewState for PhraseTui {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PhraseViewState for SequencerTui {
|
impl PhraseViewState for SequencerTui {
|
||||||
fn focused (&self) -> bool {
|
fn phrase_focused (&self) -> bool {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
fn entered (&self) -> bool {
|
fn entered (&self) -> bool {
|
||||||
|
|
@ -240,7 +240,7 @@ impl PhraseViewState for SequencerTui {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PhraseViewState for ArrangerTui {
|
impl PhraseViewState for ArrangerTui {
|
||||||
fn focused (&self) -> bool {
|
fn phrase_focused (&self) -> bool {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
fn entered (&self) -> bool {
|
fn entered (&self) -> bool {
|
||||||
|
|
@ -471,7 +471,7 @@ pub fn arranger_content_vertical (
|
||||||
// cursor
|
// cursor
|
||||||
add(&CustomWidget::new(any_size, move|to: &mut TuiOutput|{
|
add(&CustomWidget::new(any_size, move|to: &mut TuiOutput|{
|
||||||
let area = to.area();
|
let area = to.area();
|
||||||
let focused = view.is_focused();
|
let focused = view.arranger_focused();
|
||||||
let selected = view.selected;
|
let selected = view.selected;
|
||||||
let get_track_area = |t: usize| [
|
let get_track_area = |t: usize| [
|
||||||
scenes_w + area.x() + cols[t].1 as u16, area.y(),
|
scenes_w + area.x() + cols[t].1 as u16, area.y(),
|
||||||
|
|
@ -524,7 +524,7 @@ pub fn arranger_content_vertical (
|
||||||
})
|
})
|
||||||
}))
|
}))
|
||||||
}).bg(bg.rgb);
|
}).bg(bg.rgb);
|
||||||
let color = TuiTheme::title_fg(view.is_focused());
|
let color = TuiTheme::title_fg(view.arranger_focused());
|
||||||
let size = format!("{}x{}", view.size.w(), view.size.h());
|
let size = format!("{}x{}", view.size.w(), view.size.h());
|
||||||
let lower_right = TuiStyle::fg(size, color).pull_x(1).align_se().fill_xy();
|
let lower_right = TuiStyle::fg(size, color).pull_x(1).align_se().fill_xy();
|
||||||
lay!(arrangement, lower_right)
|
lay!(arrangement, lower_right)
|
||||||
|
|
@ -533,7 +533,7 @@ pub fn arranger_content_vertical (
|
||||||
pub fn arranger_content_horizontal (
|
pub fn arranger_content_horizontal (
|
||||||
view: &ArrangerTui,
|
view: &ArrangerTui,
|
||||||
) -> impl Widget<Engine = Tui> + use<'_> {
|
) -> impl Widget<Engine = Tui> + use<'_> {
|
||||||
let focused = view.is_focused();
|
let focused = view.arranger_focused();
|
||||||
let _tracks = view.tracks();
|
let _tracks = view.tracks();
|
||||||
lay!(
|
lay!(
|
||||||
focused.then_some(Background(TuiTheme::border_bg())),
|
focused.then_some(Background(TuiTheme::border_bg())),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue