wip: refactor pt.30: 74 errors

This commit is contained in:
🪞👃🪞 2024-11-14 22:34:44 +01:00
parent ab85a86b6b
commit 28e15d3f56
12 changed files with 631 additions and 622 deletions

View file

@ -1,13 +1,246 @@
use crate::*;
impl TryFrom<&Arc<RwLock<JackClient>>> for ArrangerApp<Tui> {
type Error = Box<dyn std::error::Error>;
fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
Ok(Self::new(ArrangerModel {
name: Arc::new(RwLock::new(String::new())),
phrases: vec![],
scenes: vec![],
tracks: vec![],
transport: TransportModel {
metronome: false,
transport: jack.read().unwrap().transport(),
clock: Arc::new(Clock::from(Instant::default())),
jack: jack.clone(),
},
}.into(), None, None))
}
}
pub type ArrangerApp<E: Engine> = AppView<
E,
ArrangerView<E>,
ArrangerViewCommand,
ArrangerAppCommand,
ArrangerStatusBar
>;
/// Root level object for standalone `tek_arranger`
/// Handle top-level events in standalone arranger.
impl Handle<Tui> for ArrangerApp<Tui> {
fn handle (&mut self, i: &TuiInput) -> Perhaps<bool> {
ArrangerAppCommand::execute_with_state(self, i)
}
}
pub type ArrangerAppCommand = AppViewCommand<ArrangerViewCommand>;
#[derive(Clone, Debug)]
pub enum ArrangerViewCommand {
Edit(ArrangerCommand),
Select(ArrangerSelection),
Zoom(usize),
Transport(TransportCommand),
Phrases(PhrasePoolViewCommand),
Editor(PhraseEditorCommand),
EditPhrase(Option<Arc<RwLock<Phrase>>>),
}
impl InputToCommand<Tui, ArrangerApp<Tui>> for ArrangerAppCommand {
fn input_to_command (view: &ArrangerApp<Tui>, input: &TuiInput) -> Option<Self> {
use AppViewFocus::*;
use FocusCommand::*;
use ArrangerViewCommand::*;
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(Transport(TransportViewCommand::Transport(TransportCommand::Play(None))))
},
_ => match view.focused() {
Content(ArrangerViewFocus::Transport) => Transport(
TransportViewCommand::input_to_command(&view.sequencer.transport, input)?
),
Content(ArrangerViewFocus::PhraseEditor) => Editor(
PhraseEditorCommand::input_to_command(&view.sequencer.editor, input)?
),
Content(ArrangerViewFocus::PhrasePool) => match input.event() {
key!(KeyCode::Char('e')) => EditPhrase(
Some(view.sequencer.phrases.phrase().clone())
),
_ => Phrases(
PhrasePoolViewCommand::input_to_command(&view.sequencer.phrases, input)?
)
},
Content(ArrangerViewFocus::Arranger) => {
use ArrangerSelection as Focus;
use ArrangerCommand as Model;
use ArrangerTrackCommand as Track;
use ArrangerClipCommand as Clip;
use ArrangerSceneCommand as Scene;
match input.event() {
key!(KeyCode::Char('e')) => EditPhrase(
view.selected_phrase()
),
_ => match input.event() {
// FIXME: boundary conditions
key!(KeyCode::Up) => match view.selected {
ArrangerSelection::Mix => return None,
ArrangerSelection::Track(t) => return None,
ArrangerSelection::Scene(s) => Select(Focus::Scene(s - 1)),
ArrangerSelection::Clip(t, s) => Select(Focus::Clip(t, s - 1)),
},
key!(KeyCode::Down) => match view.selected {
ArrangerSelection::Mix => Select(Focus::Scene(0)),
ArrangerSelection::Track(t) => Select(Focus::Clip(t, 0)),
ArrangerSelection::Scene(s) => Select(Focus::Scene(s + 1)),
ArrangerSelection::Clip(t, s) => Select(Focus::Clip(t, s + 1)),
},
key!(KeyCode::Left) => match view.selected {
ArrangerSelection::Mix => return None,
ArrangerSelection::Track(t) => Select(Focus::Track(t - 1)),
ArrangerSelection::Scene(s) => return None,
ArrangerSelection::Clip(t, s) => Select(Focus::Clip(t - 1, s)),
},
key!(KeyCode::Right) => match view.selected {
ArrangerSelection::Mix => return None,
ArrangerSelection::Track(t) => Select(Focus::Track(t + 1)),
ArrangerSelection::Scene(s) => Select(Focus::Clip(0, s)),
ArrangerSelection::Clip(t, s) => Select(Focus::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 {
ArrangerSelection::Mix => Zoom(0),
ArrangerSelection::Track(t) => Edit(Model::Track(Track::Swap(t, t - 1))),
ArrangerSelection::Scene(s) => Edit(Model::Scene(Scene::Swap(s, s - 1))),
ArrangerSelection::Clip(t, s) => Edit(Model::Clip(Clip::Set(t, s, None))),
},
key!(KeyCode::Char('.')) => match view.selected {
ArrangerSelection::Mix => Zoom(0),
ArrangerSelection::Track(t) => Edit(Model::Track(Track::Swap(t, t + 1))),
ArrangerSelection::Scene(s) => Edit(Model::Scene(Scene::Swap(s, s + 1))),
ArrangerSelection::Clip(t, s) => Edit(Model::Clip(Clip::Set(t, s, None))),
},
key!(KeyCode::Char('<')) => match view.selected {
ArrangerSelection::Mix => Zoom(0),
ArrangerSelection::Track(t) => Edit(Model::Track(Track::Swap(t, t - 1))),
ArrangerSelection::Scene(s) => Edit(Model::Scene(Scene::Swap(s, s - 1))),
ArrangerSelection::Clip(t, s) => Edit(Model::Clip(Clip::Set(t, s, None))),
},
key!(KeyCode::Char('>')) => match view.selected {
ArrangerSelection::Mix => Zoom(0),
ArrangerSelection::Track(t) => Edit(Model::Track(Track::Swap(t, t + 1))),
ArrangerSelection::Scene(s) => Edit(Model::Scene(Scene::Swap(s, s + 1))),
ArrangerSelection::Clip(t, s) => Edit(Model::Clip(Clip::Set(t, s, None))),
},
key!(KeyCode::Enter) => match view.selected {
ArrangerSelection::Mix => return None,
ArrangerSelection::Track(t) => return None,
ArrangerSelection::Scene(s) => Edit(Model::Scene(Scene::Play(s))),
ArrangerSelection::Clip(t, s) => return None,
},
key!(KeyCode::Delete) => match view.selected {
ArrangerSelection::Mix => Edit(Model::Clear),
ArrangerSelection::Track(t) => Edit(Model::Track(Track::Delete(t))),
ArrangerSelection::Scene(s) => Edit(Model::Scene(Scene::Delete(s))),
ArrangerSelection::Clip(t, s) => Edit(Model::Clip(Clip::Set(t, s, None))),
},
key!(KeyCode::Char('c')) => Edit(Model::Clip(Clip::RandomColor)),
key!(KeyCode::Char('s')) => match view.selected {
ArrangerSelection::Clip(t, s) => Edit(Model::Clip(Clip::Set(t, s, None))),
_ => return None,
},
key!(KeyCode::Char('g')) => match view.selected {
ArrangerSelection::Clip(t, s) => Edit(Model::Clip(Clip::Get(t, s))),
_ => return None,
},
key!(Ctrl-KeyCode::Char('a')) => Edit(Model::Scene(Scene::Add)),
key!(Ctrl-KeyCode::Char('t')) => Edit(Model::Track(Track::Add)),
key!(KeyCode::Char('l')) => Edit(Model::Clip(Clip::SetLoop(false))),
_ => return None
}
}
}
}
})
}
}
impl Command<ArrangerApp<Tui>> for ArrangerAppCommand {
fn execute (self, state: &mut ArrangerApp<Tui>) -> Perhaps<Self> {
let undo = match self {
Self::Focus(cmd) => {
delegate(cmd, Self::Focus, state)
},
Self::App(cmd) => match cmd {
ArrangerViewCommand::Phrases(cmd) => {
delegate(cmd, |x|Self::App(ArrangerViewCommand::Phrases(x)), &mut state.app)
},
ArrangerViewCommand::Editor(cmd) => {
delegate(cmd, |x|Self::App(ArrangerViewCommand::Editor(x)), &mut state.app)
},
ArrangerViewCommand::Transport(cmd) => {
delegate(cmd, |x|Self::App(ArrangerViewCommand::Transport(x)), &mut state.app)
},
ArrangerViewCommand::Zoom(zoom) => {
todo!();
},
ArrangerViewCommand::Select(selected) => {
state.selected = selected;
Ok(None)
},
ArrangerViewCommand::Edit(command) => {
return Ok(command.execute(&mut state.model)?.map(ArrangerViewCommand::Edit))
},
ArrangerViewCommand::EditPhrase(phrase) => {
app.sequencer.editor.phrase = phrase.clone();
state.focus(ArrangerViewFocus::PhraseEditor);
state.focus_enter();
Ok(None)
}
},
_ => {todo!()}
}?;
state.show_phrase();
state.update_status();
return Ok(undo);
}
}
/// Root view for standalone `tek_arranger`
pub struct ArrangerView<E: Engine> {
pub model: ArrangerModel,
/// Sequencer component
@ -37,18 +270,6 @@ pub enum ArrangerMode {
Vertical(usize),
}
#[derive(Clone)]
pub enum ArrangerViewCommand {
Focus(FocusCommand),
Edit(ArrangerCommand),
Select(ArrangerSelection),
Zoom(usize),
Transport(TransportViewCommand),
Phrases(PhrasePoolViewCommand),
Editor(PhraseEditorCommand),
EditPhrase(Option<Arc<RwLock<Phrase>>>),
}
/// Sections in the arranger app that may be focused
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum ArrangerViewFocus {
@ -62,7 +283,7 @@ pub enum ArrangerViewFocus {
PhraseEditor,
}
#[derive(PartialEq, Clone, Copy)]
#[derive(PartialEq, Clone, Copy, Debug)]
/// Represents the current user selection in the arranger
pub enum ArrangerSelection {
/// The whole mix is selected
@ -76,7 +297,7 @@ pub enum ArrangerSelection {
}
/// Status bar for arranger app
#[derive(Copy, Clone)]
#[derive(Copy, Clone, Debug)]
pub enum ArrangerStatusBar {
Transport,
ArrangerMix,
@ -88,24 +309,6 @@ pub enum ArrangerStatusBar {
PhraseEdit,
}
impl TryFrom<&Arc<RwLock<JackClient>>> for ArrangerApp<Tui> {
type Error = Box<dyn std::error::Error>;
fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
Ok(Self::new(ArrangerModel {
name: Arc::new(RwLock::new(String::new())),
phrases: vec![],
scenes: vec![],
tracks: vec![],
transport: TransportModel {
metronome: false,
transport: jack.read().unwrap().transport(),
clock: Arc::new(Clock::from(Instant::default())),
jack: jack.clone(),
},
}.into(), None, None))
}
}
impl<E: Engine> From<ArrangerModel> for ArrangerView<E> {
fn from (model: ArrangerModel) -> Self {
let mut view = Self {
@ -262,7 +465,7 @@ impl<E: Engine> ArrangerView<E> {
}
}
impl Audio for ArrangerView<Tui> {
impl<E: Engine> Audio for ArrangerView<E> {
#[inline] fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control {
if self.model.process(client, scope) == Control::Quit {
return Control::Quit
@ -290,200 +493,6 @@ impl Audio for ArrangerView<Tui> {
}
}
/// Handle top-level events in standalone arranger.
impl Handle<Tui> for ArrangerView<Tui> {
fn handle (&mut self, i: &TuiInput) -> Perhaps<bool> {
ArrangerViewCommand::execute_with_state(self, i)
}
}
impl InputToCommand<Tui, ArrangerView<Tui>> for ArrangerViewCommand {
fn input_to_command (view: &ArrangerView<Tui>, input: &TuiInput) -> Option<Self> {
use FocusCommand::*;
use ArrangerViewCommand::*;
Some(match input.event() {
key!(KeyCode::Tab) => Focus(Next),
key!(Shift-KeyCode::Tab) => Focus(Prev),
key!(KeyCode::BackTab) => Focus(Prev),
key!(Shift-KeyCode::BackTab) => Focus(Prev),
key!(KeyCode::Up) => Focus(Up),
key!(KeyCode::Down) => Focus(Down),
key!(KeyCode::Left) => Focus(Left),
key!(KeyCode::Right) => Focus(Right),
key!(KeyCode::Enter) => Focus(Enter),
key!(KeyCode::Esc) => Focus(Exit),
key!(KeyCode::Char(' ')) => {
Transport(TransportViewCommand::Transport(TransportCommand::Play(None)))
},
_ => match view.focused() {
ArrangerViewFocus::Transport => Transport(
TransportViewCommand::input_to_command(&view.sequencer.transport, input)?
),
ArrangerViewFocus::PhraseEditor => Editor(
PhraseEditorCommand::input_to_command(&view.sequencer.editor, input)?
),
ArrangerViewFocus::PhrasePool => match input.event() {
key!(KeyCode::Char('e')) => EditPhrase(
Some(view.sequencer.phrases.phrase().clone())
),
_ => Phrases(
PhrasePoolViewCommand::input_to_command(&view.sequencer.phrases, input)?
)
},
ArrangerViewFocus::Arranger => {
use ArrangerSelection as Focus;
use ArrangerCommand as Model;
use ArrangerTrackCommand as Track;
use ArrangerClipCommand as Clip;
use ArrangerSceneCommand as Scene;
match input.event() {
key!(KeyCode::Char('e')) => EditPhrase(
view.selected_phrase()
),
_ => match input.event() {
// FIXME: boundary conditions
key!(KeyCode::Up) => match view.selected {
ArrangerSelection::Mix => return None,
ArrangerSelection::Track(t) => return None,
ArrangerSelection::Scene(s) => Select(Focus::Scene(s - 1)),
ArrangerSelection::Clip(t, s) => Select(Focus::Clip(t, s - 1)),
},
key!(KeyCode::Down) => match view.selected {
ArrangerSelection::Mix => Select(Focus::Scene(0)),
ArrangerSelection::Track(t) => Select(Focus::Clip(t, 0)),
ArrangerSelection::Scene(s) => Select(Focus::Scene(s + 1)),
ArrangerSelection::Clip(t, s) => Select(Focus::Clip(t, s + 1)),
},
key!(KeyCode::Left) => match view.selected {
ArrangerSelection::Mix => return None,
ArrangerSelection::Track(t) => Select(Focus::Track(t - 1)),
ArrangerSelection::Scene(s) => return None,
ArrangerSelection::Clip(t, s) => Select(Focus::Clip(t - 1, s)),
},
key!(KeyCode::Right) => match view.selected {
ArrangerSelection::Mix => return None,
ArrangerSelection::Track(t) => Select(Focus::Track(t + 1)),
ArrangerSelection::Scene(s) => Select(Focus::Clip(0, s)),
ArrangerSelection::Clip(t, s) => Select(Focus::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 {
ArrangerSelection::Mix => Zoom(0),
ArrangerSelection::Track(t) => Edit(Model::Track(Track::Swap(t, t - 1))),
ArrangerSelection::Scene(s) => Edit(Model::Scene(Scene::Swap(s, s - 1))),
ArrangerSelection::Clip(t, s) => Edit(Model::Clip(Clip::Set(t, s, None))),
},
key!(KeyCode::Char('.')) => match view.selected {
ArrangerSelection::Mix => Zoom(0),
ArrangerSelection::Track(t) => Edit(Model::Track(Track::Swap(t, t + 1))),
ArrangerSelection::Scene(s) => Edit(Model::Scene(Scene::Swap(s, s + 1))),
ArrangerSelection::Clip(t, s) => Edit(Model::Clip(Clip::Set(t, s, None))),
},
key!(KeyCode::Char('<')) => match view.selected {
ArrangerSelection::Mix => Zoom(0),
ArrangerSelection::Track(t) => Edit(Model::Track(Track::Swap(t, t - 1))),
ArrangerSelection::Scene(s) => Edit(Model::Scene(Scene::Swap(s, s - 1))),
ArrangerSelection::Clip(t, s) => Edit(Model::Clip(Clip::Set(t, s, None))),
},
key!(KeyCode::Char('>')) => match view.selected {
ArrangerSelection::Mix => Zoom(0),
ArrangerSelection::Track(t) => Edit(Model::Track(Track::Swap(t, t + 1))),
ArrangerSelection::Scene(s) => Edit(Model::Scene(Scene::Swap(s, s + 1))),
ArrangerSelection::Clip(t, s) => Edit(Model::Clip(Clip::Set(t, s, None))),
},
key!(KeyCode::Enter) => match view.selected {
ArrangerSelection::Mix => return None,
ArrangerSelection::Track(t) => return None,
ArrangerSelection::Scene(s) => Edit(Model::Scene(Scene::Play(s))),
ArrangerSelection::Clip(t, s) => return None,
},
key!(KeyCode::Delete) => match view.selected {
ArrangerSelection::Mix => Edit(Model::Clear),
ArrangerSelection::Track(t) => Edit(Model::Track(Track::Delete(t))),
ArrangerSelection::Scene(s) => Edit(Model::Scene(Scene::Delete(s))),
ArrangerSelection::Clip(t, s) => Edit(Model::Clip(Clip::Set(t, s, None))),
},
key!(KeyCode::Char('c')) => Edit(Model::Clip(Clip::RandomColor)),
key!(KeyCode::Char('s')) => match view.selected {
ArrangerSelection::Clip(t, s) => Edit(Model::Clip(Clip::Set(t, s, None))),
_ => return None,
},
key!(KeyCode::Char('g')) => match view.selected {
ArrangerSelection::Clip(t, s) => Edit(Model::Clip(Clip::Get(t, s))),
_ => return None,
},
key!(Ctrl-KeyCode::Char('a')) => Edit(Model::Scene(Scene::Add)),
key!(Ctrl-KeyCode::Char('t')) => Edit(Model::Track(Track::Add)),
key!(KeyCode::Char('l')) => Edit(Model::Clip(Clip::SetLoop(false))),
_ => return None
}
}
}
}
})
}
}
impl<E: Engine> Command<ArrangerView<E>> for ArrangerViewCommand {
fn execute (self, view: &mut ArrangerView<E>) -> Perhaps<Self> {
let undo = match self {
Self::Focus(cmd) =>
delegate(cmd, Self::Focus, view),
Self::Phrases(cmd) =>
delegate(cmd, Self::Phrases, &mut view.sequencer.phrases),
Self::Editor(cmd) =>
delegate(cmd, Self::Editor, &mut view.sequencer.editor),
Self::Transport(cmd) =>
delegate(cmd, Self::Transport, &mut view.sequencer.transport),
Self::Zoom(zoom) => {
todo!();
},
Self::Select(selected) => {
view.selected = selected;
Ok(None)
},
Self::Edit(command) => {
return Ok(command.execute(&mut view.model)?.map(Self::Edit))
},
Self::EditPhrase(phrase) => {
view.sequencer.editor.phrase = phrase.clone();
view.focus(ArrangerViewFocus::PhraseEditor);
view.focus_enter();
Ok(None)
}
}?;
view.show_phrase();
view.update_status();
return Ok(undo);
}
}
//pub fn phrase_next (&mut self) {
//if let ArrangerSelection::Clip(track, scene) = self.selected {
//if let Some(ref mut phrase) = self.model.scenes[scene].clips[track] {
@ -617,7 +626,7 @@ impl FocusGrid for ArrangerApp<Tui> {
self.app.sequencer.transport.focused = focused == Content(Transport);
self.app.sequencer.phrases.focused = focused == Content(PhrasePool);
self.app.sequencer.editor.focused = focused == Content(PhraseEditor);
if let Some(status_bar) = self.status_bar {
if let Some(mut status_bar) = self.status_bar {
status_bar.update(&(
self.focused(),
self.app.selected,