wip: p.55, e=95

This commit is contained in:
🪞👃🪞 2024-11-18 22:05:11 +01:00
parent 54fb5b6ece
commit 37ac7823af
10 changed files with 272 additions and 219 deletions

View file

@ -35,4 +35,17 @@ pub trait ClockApi: Send + Sync {
fn ppq (&self) -> &PulsesPerQuaver {
&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
}
}

View file

@ -5,7 +5,7 @@ pub trait HasPhrases {
fn phrases_mut (&mut self) -> &mut Vec<Arc<RwLock<Phrase>>>;
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
pub enum PhrasePoolCommand {
Add(usize),
Delete(usize),

View file

@ -256,15 +256,13 @@ impl ArrangerTui {
}
pub fn activate (&mut self) {
let scenes = self.scenes();
let tracks = self.tracks_mut();
match self.selected {
let selected = self.selected;
match selected {
ArrangerSelection::Scene(s) => {
for (t, track) in tracks.iter_mut().enumerate() {
let player = &mut track.player;
let clip = scenes[s].clips[t].as_ref();
if player.phrase.is_some() || clip.is_some() {
player.enqueue_next(clip);
for (t, track) in self.tracks_mut().iter_mut().enumerate() {
let clip = self.scenes()[s].clips[t].as_ref();
if track.play_phrase.is_some() || clip.is_some() {
track.enqueue_next(clip);
}
}
// TODO make transport available here, so that
@ -274,7 +272,7 @@ impl ArrangerTui {
//}
},
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());
},
_ => {}
}

View file

@ -97,20 +97,26 @@ impl<T: TransportControl> Command<T> for TransportCommand {
}
}
impl Command<SequencerTui> for SequencerCommand {
fn execute (self, state: &mut SequencerTui) -> Perhaps<Self> {
impl<T> Command<T> for SequencerCommand
where
T: FocusGrid + PhrasesControl + PhraseControl + ClockApi + PlayheadApi
{
fn execute (self, state: T) -> Perhaps<Self> {
use SequencerCommand::*;
match self {
Focus(cmd) => delegate(cmd, Focus, &mut 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)
Focus(cmd) => delegate(cmd, Focus, state),
Phrases(cmd) => delegate(cmd, Phrases, state),
Editor(cmd) => delegate(cmd, Editor, state),
Clock(cmd) => delegate(cmd, Clock, state),
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> {
use ArrangerCommand::*;
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> {
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> {
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> {
todo!()
todo!();
Ok(None)
}
}
impl Command<PhrasesTui> for PhrasesCommand {
fn execute (self, view: &mut PhrasesTui) -> Perhaps<Self> {
impl<T: PhrasesControl> Command<T> for PhrasesCommand {
fn execute (self, state: &mut T) -> Perhaps<Self> {
use PhraseRenameCommand as Rename;
use PhraseLengthCommand as Length;
match self {
Self::Select(phrase) => {
view.phrase = phrase
state.phrase = phrase
},
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 {
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))
}
Rename::Begin => self.phrases_rename_begin(),
_ => return Ok(command.execute(state)?.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))
}
Length::Begin => self.phrases_length_begin(),
_ => return Ok(command.execute(state)?.map(Self::Length)),
},
}
Ok(None)
}
}
impl Command<PhrasesTui> for PhraseLengthCommand {
fn execute (self, view: &mut PhrasesTui) -> Perhaps<Self> {
impl<T: PhrasesControl> Command<T> for PhraseLengthCommand {
fn execute (self, state: &mut T) -> Perhaps<Self> {
use PhraseLengthFocus::*;
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 {
Self::Cancel => {
view.mode = None;
state.mode = None;
},
Self::Prev => {
focus.prev()
@ -217,21 +211,17 @@ impl Command<PhrasesTui> for PhraseLengthCommand {
Tick => { *length = length.saturating_sub(1) },
},
Self::Set(length) => {
let mut phrase = view.phrases[phrase].write().unwrap();
let mut phrase = state.phrases[phrase].write().unwrap();
let old_length = phrase.length;
phrase.length = length;
view.mode = None;
state.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
));
self.phrases_length_begin();
Ok(None)
} else {
unreachable!()
@ -239,32 +229,29 @@ impl Command<PhrasesTui> for PhraseLengthCommand {
}
}
impl Command<PhrasesTui> for PhraseRenameCommand {
fn execute (self, view: &mut PhrasesTui) -> Perhaps<Self> {
impl<T: PhrasesControl> Command<T> for PhraseRenameCommand {
fn execute (self, state: &mut T) -> Perhaps<Self> {
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 {
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())))
},
Confirm => {
let old_name = old_name.clone();
view.mode = None;
state.mode = None;
return Ok(Some(Self::Set(old_name)))
},
Cancel => {
let mut phrase = view.phrases[phrase].write().unwrap();
let mut phrase = state.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()
));
self.phrases_rename_begin();
Ok(None)
} else {
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 {
//use PhraseCommand::*;
//match self {
@ -283,7 +270,7 @@ impl Command<PhraseTui> for PhraseCommand {
//_ => self
//}
//}
fn execute (self, state: &mut PhraseTui) -> Perhaps<Self> {
fn execute (self, state: &mut T) -> Perhaps<Self> {
use PhraseCommand::*;
match self.translate(state) {
ToggleDirection => {
@ -296,58 +283,58 @@ impl Command<PhraseTui> for PhraseCommand {
state.entered = false;
},
TimeZoomOut => {
let scale = state.time_axis.read().unwrap().scale;
state.time_axis.write().unwrap().scale = next_note_length(scale)
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)
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);
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);
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);
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);
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();
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();
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);
state.note_axis().write().unwrap().start_inc(1);
},
NoteScrollInc => {
state.note_axis.write().unwrap().start_dec(1);
state.note_axis().write().unwrap().start_dec(1);
},
NoteLengthDec => {
state.note_len = prev_note_length(state.note_len)
*state.note_len_mut() = prev_note_length(state.note_len())
},
NoteLengthInc => {
state.note_len = next_note_length(state.note_len)
*state.note_len_mut() = next_note_length(state.note_len())
},
NotePageUp => {
let mut axis = state.note_axis.write().unwrap();
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();
let mut axis = state.note_axis().write().unwrap();
axis.start_inc(3);
axis.point_inc(3);
},

View file

@ -4,8 +4,9 @@ impl<'a, T: TransportViewState> Content for TransportView<'a, T> {
type Engine = Tui;
fn content (&self) -> impl Widget<Engine = Tui> {
let state = self.0;
let focused = state.transport_focused();
lay!(
state.focus().wrap(state.is_focused(), TransportFocus::PlayPause, &Styled(
state.transport_selected().wrap(focused, TransportFocus::PlayPause, &Styled(
None,
match state.transport_state() {
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(),
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();
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! {
//"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)
}))
).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 time2 = state.format_msu();
row!("B" ,time1.as_str(), " T", time2.as_str()).outset_x(1)
@ -55,6 +56,7 @@ impl Content for SequencerTui {
impl Content for ArrangerTui {
type Engine = Tui;
fn content (&self) -> impl Widget<Engine = Tui> {
let arranger_focused = self.arranger_focused();
Split::up(
1,
widget(&TransportView(self)),
@ -73,14 +75,14 @@ impl Content for ArrangerTui {
.grow_y(1)
.border(Lozenge(Style::default()
.bg(TuiTheme::border_bg())
.fg(TuiTheme::border_fg(ArrangerViewState::focused(self))))),
.fg(TuiTheme::border_fg(arranger_focused)))),
widget(&self.size),
widget(&format!("[{}] Arranger", if self.entered {
""
} else {
" "
}))
.fg(TuiTheme::title_fg(ArrangerViewState::focused(self)))
.fg(TuiTheme::title_fg(arranger_focused))
.push_x(1),
),
Split::right(
@ -97,7 +99,7 @@ impl Content for ArrangerTui {
impl<'a, T: PhrasesViewState> Content for PhrasesView<'a, T> {
type 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 phrases = self.0.phrases();
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> {
let phrase = self.0.phrase();
let size = self.0.size();
let focused = self.0.focused();
let focused = self.0.phrase_focused();
let entered = self.0.entered();
let keys = self.0.keys();
let buffer = self.0.buffer();

View file

@ -1,58 +1,16 @@
use crate::*;
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
}
}
pub trait TransportControl: ClockApi {}
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 TransportTui {}
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 SequencerTui {}
impl TransportControl for ArrangerTui {
fn bpm (&self) -> &BeatsPerMinute {
&self.current.timebase.bpm
}
fn quant (&self) -> &Quantize {
&self.quant
}
fn sync (&self) -> &LaunchSync {
&self.sync
}
}
impl TransportControl for ArrangerTui {}
pub trait SequencerControl {}
impl SequencerControl for SequencerTui {}
pub trait ArrangerControl {
fn selected (&self) -> ArrangerSelection;
@ -63,3 +21,93 @@ impl ArrangerControl for ArrangerTui {
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!()
}
}

View file

@ -24,7 +24,6 @@ impl TryFrom<&Arc<RwLock<JackClient>>> for SequencerTui {
transport: jack.read().unwrap().transport(),
jack: jack.clone(),
focused: false,
focus: TransportFocus::PlayPause,
size: Measure::new(),
}.into(), None, None))
}

View file

@ -60,10 +60,10 @@ impl InputToCommand<Tui, SequencerTui> for SequencerCommand {
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() {
_ => Some(match state.focused() {
SequencerFocus::Transport => {
use TransportCommand::{Clock, Playhead};
match TransportCommand::input_to_command(view, input)? {
match TransportCommand::input_to_command(state, input)? {
Clock(command) => {
todo!()
},
@ -72,18 +72,20 @@ impl InputToCommand<Tui, SequencerTui> for SequencerCommand {
},
}
},
SequencerFocus::Phrases =>
PhrasesCommand::input_to_command(&state.phrases, input).map(Phrases),
SequencerFocus::PhraseEditor =>
PhraseCommand::input_to_command(&state.editor, input).map(Editor),
SequencerFocus::Phrases => {
PhrasesCommand::input_to_command(state, input).map(Phrases)
},
SequencerFocus::PhraseEditor => {
PhraseCommand::input_to_command(state, input).map(Editor)
},
_ => return None,
}))
})
}
}
}
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 ArrangerCommand::*;
Some(match input.event() {
@ -97,13 +99,11 @@ impl<T: ArrangerControl> InputToCommand<Tui, T> for ArrangerCommand {
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() {
key!(KeyCode::Char(' ')) => Self::Playhead(PlayheadCommand::Play(None)),
_ => match state.focused() {
ArrangerFocus::Transport => {
use TransportCommand::{Clock, Playhead};
match TransportCommand::input_to_command(view, input)? {
match TransportCommand::input_to_command(state, input)? {
Clock(command) => {
todo!()
},
@ -113,14 +113,14 @@ impl<T: ArrangerControl> InputToCommand<Tui, T> for ArrangerCommand {
}
},
ArrangerFocus::PhraseEditor => Editor(
PhraseCommand::input_to_command(&view.editor, input)?
PhraseCommand::input_to_command(state, input)?
),
ArrangerFocus::PhrasePool => match input.event() {
key!(KeyCode::Char('e')) => EditPhrase(
Some(view.phrase().clone())
Some(state.phrase().clone())
),
_ => Phrases(
PhrasePoolCommand::input_to_command(view, input)?
PhrasePoolCommand::input_to_command(state, input)?
)
},
ArrangerFocus::Arranger => {
@ -129,32 +129,32 @@ impl<T: ArrangerControl> InputToCommand<Tui, T> for ArrangerCommand {
use ArrangerClipCommand as Clip;
use ArrangerSceneCommand as Scene;
match input.event() {
key!(KeyCode::Char('e')) => EditPhrase(view.phrase()),
key!(KeyCode::Char('e')) => EditPhrase(state.phrase()),
_ => match input.event() {
// FIXME: boundary conditions
key!(KeyCode::Up) => match view.selected() {
key!(KeyCode::Up) => match state.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() {
key!(KeyCode::Down) => match state.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() {
key!(KeyCode::Left) => match state.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() {
key!(KeyCode::Right) => match state.selected() {
Select::Mix => return None,
Select::Track(t) => Select(Select::Track(t + 1)),
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('`')) => { 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::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() {
key!(KeyCode::Char('.')) => match state.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() {
key!(KeyCode::Char('<')) => match state.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() {
key!(KeyCode::Char('>')) => match state.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() {
key!(KeyCode::Enter) => match state.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() {
key!(KeyCode::Delete) => match state.selected() {
Select::Mix => Clear,
Select::Track(t) => Track(Track::Delete(t)),
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('s')) => match view.selected() {
key!(KeyCode::Char('s')) => match state.selected() {
Select::Clip(t, s) => Clip(Clip::Set(t, s, 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)),
_ => return None,
},
@ -235,13 +235,13 @@ impl<T: ArrangerControl> InputToCommand<Tui, T> for ArrangerCommand {
}
}
}
})
}
})
}
}
impl InputToCommand<Tui, PhrasesTui> for PhrasesCommand {
fn input_to_command (state: &PhrasesTui, input: &TuiInput) -> Option<Self> {
impl<T: PhrasesControl> InputToCommand<Tui, T> for PhrasesCommand {
fn input_to_command (state: &T, input: &TuiInput) -> Option<Self> {
use PhrasesCommand as Cmd;
use PhrasePoolCommand as Edit;
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('n')) => Some(Cmd::Rename(Rename::Begin)),
key!(KeyCode::Char('t')) => Some(Cmd::Length(Length::Begin)),
_ => match state.mode {
_ => match state.phrases_mode() {
Some(PhrasesMode::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 {
fn input_to_command (view: &PhrasesTui, from: &TuiInput) -> Option<Self> {
if let Some(PhrasesMode::Length(_, length, _)) = view.mode {
impl<T: PhrasesControl> InputToCommand<Tui, T> for PhraseLengthCommand {
fn input_to_command (state: &T, from: &TuiInput) -> Option<Self> {
if let Some(PhrasesMode::Length(_, length, _)) = state.phrases_mode() {
Some(match from.event() {
key!(KeyCode::Up) => Self::Inc,
key!(KeyCode::Down) => Self::Dec,
@ -289,9 +289,9 @@ impl InputToCommand<Tui, PhrasesTui> for PhraseLengthCommand {
}
}
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 {
impl<T: PhrasesControl> InputToCommand<Tui, T> for PhraseRenameCommand {
fn input_to_command (state: &T, from: &TuiInput) -> Option<Self> {
if let Some(PhrasesMode::Rename(_, ref old_name)) = state.phrases_mode() {
Some(match from.event() {
key!(KeyCode::Char(c)) => {
let mut new_name = old_name.clone();
@ -313,8 +313,8 @@ impl InputToCommand<Tui, PhrasesTui> for PhraseRenameCommand {
}
}
impl InputToCommand<Tui, PhraseTui> for PhraseCommand {
fn input_to_command (state: &PhraseTui, from: &TuiInput) -> Option<Self> {
impl<T: PhraseControl> InputToCommand<Tui, T> for PhraseCommand {
fn input_to_command (state: &T, from: &TuiInput) -> Option<Self> {
use PhraseCommand::*;
Some(match from.event() {
key!(KeyCode::Char('`')) => ToggleDirection,
@ -330,19 +330,19 @@ impl InputToCommand<Tui, PhraseTui> for PhraseCommand {
key!(KeyCode::Char('+')) => TimeZoomSet(0),
key!(KeyCode::PageUp) => NoteScrollSet(0),
key!(KeyCode::PageDown) => NoteScrollSet(0),
key!(KeyCode::Up) => match state.entered {
key!(KeyCode::Up) => match state.phrase_entered() {
true => NoteCursorSet(0),
false => NoteScrollSet(0),
},
key!(KeyCode::Down) => match state.entered {
key!(KeyCode::Down) => match state.phrase_entered() {
true => NoteCursorSet(0),
false => NoteScrollSet(0),
},
key!(KeyCode::Left) => match state.entered {
key!(KeyCode::Left) => match state.phrase_entered() {
true => TimeCursorSet(0),
false => TimeScrollSet(0),
},
key!(KeyCode::Right) => match state.entered {
key!(KeyCode::Right) => match state.phrase_entered() {
true => TimeCursorSet(0),
false => TimeScrollSet(0),
},

View file

@ -72,6 +72,10 @@ pub struct SequencerTui {
pub(crate) entered: bool,
pub(crate) cursor: (usize, usize),
pub(crate) size: Measure<Tui>,
/// Mode switch
pub(crate) phrases_mode: Option<PhrasesMode>,
}
/// Root view for standalone `tek_arranger`
@ -148,7 +152,9 @@ pub struct ArrangerTrack {
/// Whether this widget is focused
pub(crate) focused: bool,
/// 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 {

View file

@ -3,8 +3,8 @@ use crate::*;
pub struct TransportView<'a, T: TransportViewState>(pub &'a T);
pub trait TransportViewState: Send + Sync {
fn focus (&self) -> TransportFocus;
fn is_focused (&self) -> bool;
fn transport_selected (&self) -> TransportFocus;
fn transport_focused (&self) -> bool;
fn transport_state (&self) -> Option<TransportState>;
fn bpm_value (&self) -> f64;
fn sync_value (&self) -> f64;
@ -13,10 +13,10 @@ pub trait TransportViewState: Send + Sync {
}
impl TransportViewState for TransportTui {
fn focus (&self) -> TransportFocus {
fn transport_selected (&self) -> TransportFocus {
self.focus
}
fn is_focused (&self) -> bool {
fn transport_focused (&self) -> bool {
true
}
fn transport_state (&self) -> Option<TransportState> {
@ -37,10 +37,10 @@ impl TransportViewState for TransportTui {
}
impl TransportViewState for SequencerTui {
fn focus (&self) -> TransportFocus {
fn transport_selected (&self) -> TransportFocus {
self.focus
}
fn is_focused (&self) -> bool {
fn transport_focused (&self) -> bool {
self.focused() == SequencerFocus::Transport
}
fn transport_state (&self) -> Option<TransportState> {
@ -61,10 +61,10 @@ impl TransportViewState for SequencerTui {
}
impl TransportViewState for ArrangerTui {
fn focus (&self) -> TransportFocus {
fn transport_selected (&self) -> TransportFocus {
self.focus
}
fn is_focused (&self) -> bool {
fn transport_focused (&self) -> bool {
self.focused() == ArrangerFocus::Transport
}
fn transport_state (&self) -> Option<TransportState> {
@ -85,11 +85,11 @@ impl TransportViewState for ArrangerTui {
}
pub trait ArrangerViewState {
fn is_focused (&self) -> bool;
fn arranger_focused (&self) -> bool;
}
impl ArrangerViewState for ArrangerTui {
fn is_focused (&self) -> bool {
fn arranger_focused (&self) -> bool {
self.focused() == ArrangerFocus::Arranger
}
}
@ -97,7 +97,7 @@ impl ArrangerViewState for ArrangerTui {
pub struct PhrasesView<'a, T: PhrasesViewState>(pub &'a T);
pub trait PhrasesViewState: Send + Sync {
fn focused (&self) -> bool;
fn phrases_focused (&self) -> bool;
fn entered (&self) -> bool;
fn phrases (&self) -> Vec<Arc<RwLock<Phrase>>>;
fn phrase (&self) -> usize;
@ -105,7 +105,7 @@ pub trait PhrasesViewState: Send + Sync {
}
impl PhrasesViewState for PhrasesTui {
fn focused (&self) -> bool {
fn phrases_focused (&self) -> bool {
todo!()
}
fn entered (&self) -> bool {
@ -123,7 +123,7 @@ impl PhrasesViewState for PhrasesTui {
}
impl PhrasesViewState for SequencerTui {
fn focused (&self) -> bool {
fn phrases_focused (&self) -> bool {
todo!()
}
fn entered (&self) -> bool {
@ -136,12 +136,12 @@ impl PhrasesViewState for SequencerTui {
todo!()
}
fn mode (&self) -> &Option<PhrasesMode> {
&self.mode
&self.phrases_mode
}
}
impl PhrasesViewState for ArrangerTui {
fn focused (&self) -> bool {
fn phrases_focused (&self) -> bool {
todo!()
}
fn entered (&self) -> bool {
@ -154,14 +154,14 @@ impl PhrasesViewState for ArrangerTui {
todo!()
}
fn mode (&self) -> &Option<PhrasesMode> {
&self.mode
&self.phrases_mode
}
}
pub struct PhraseView<'a, T: PhraseViewState>(pub &'a T);
pub trait PhraseViewState: Send + Sync {
fn focused (&self) -> bool;
fn phrase_focused (&self) -> bool;
fn entered (&self) -> bool;
fn keys (&self) -> &Buffer;
fn phrase (&self) -> &Option<Arc<RwLock<Phrase>>>;
@ -174,7 +174,7 @@ pub trait PhraseViewState: Send + Sync {
}
impl PhraseViewState for PhraseTui {
fn focused (&self) -> bool {
fn phrase_focused (&self) -> bool {
self.focused
}
fn entered (&self) -> bool {
@ -207,7 +207,7 @@ impl PhraseViewState for PhraseTui {
}
impl PhraseViewState for SequencerTui {
fn focused (&self) -> bool {
fn phrase_focused (&self) -> bool {
todo!()
}
fn entered (&self) -> bool {
@ -240,7 +240,7 @@ impl PhraseViewState for SequencerTui {
}
impl PhraseViewState for ArrangerTui {
fn focused (&self) -> bool {
fn phrase_focused (&self) -> bool {
todo!()
}
fn entered (&self) -> bool {
@ -471,7 +471,7 @@ pub fn arranger_content_vertical (
// cursor
add(&CustomWidget::new(any_size, move|to: &mut TuiOutput|{
let area = to.area();
let focused = view.is_focused();
let focused = view.arranger_focused();
let selected = view.selected;
let get_track_area = |t: usize| [
scenes_w + area.x() + cols[t].1 as u16, area.y(),
@ -524,7 +524,7 @@ pub fn arranger_content_vertical (
})
}))
}).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 lower_right = TuiStyle::fg(size, color).pull_x(1).align_se().fill_xy();
lay!(arrangement, lower_right)
@ -533,7 +533,7 @@ pub fn arranger_content_vertical (
pub fn arranger_content_horizontal (
view: &ArrangerTui,
) -> impl Widget<Engine = Tui> + use<'_> {
let focused = view.is_focused();
let focused = view.arranger_focused();
let _tracks = view.tracks();
lay!(
focused.then_some(Background(TuiTheme::border_bg())),