use crate::*; view!(TuiOut: |self: Tek| self.size.of(View(self, self.view)); { ":nil" => Box::new("nil"), ":transport" => self.view_transport().boxed(), ":arranger" => ArrangerView::new(self).boxed(), ":editor" => self.editor.as_ref() .map(|e|Bsp::s(Bsp::e(e.clip_status(), e.edit_status()), e)) .boxed(), ":sample" => ().boxed(),//self.view_sample(self.is_editing()).boxed(), ":sampler" => ().boxed(),//self.view_sampler(self.is_editing(), &self.editor).boxed(), ":samples-grid" => self.tracks[0].sampler(0).map(|s|s.view_grid()).boxed(), ":status" => self.view_status().boxed(), ":pool" => self.pool.as_ref() .map(|pool|Fixed::x(self.w_sidebar(), PoolView(self.is_editing(), pool))) .boxed(), }); expose!([self: Tek] { [bool] => {} [u16] => { ":h-ins" => self.h_inputs(), ":h-outs" => self.h_outputs(), ":h-sample" => if self.is_editing() { 0 } else { 5 }, ":w-samples" => if self.is_editing() { 4 } else { 11 }, ":w-sidebar" => self.w_sidebar(), ":y-ins" => (self.size.h() as u16).saturating_sub(self.h_inputs() + 1), ":y-outs" => (self.size.h() as u16).saturating_sub(self.h_outputs() + 1), ":y-samples" => if self.is_editing() { 1 } else { 0 }, } [usize] => { ":scene-last" => self.scenes.len(), ":track-last" => self.tracks.len(), } [isize] => {} [Option] => { ":scene" => self.selected.scene(), ":track" => self.selected.track(), } [Color] => {} [Arc>] => {} [Option>>] => { ":clip" => match self.selected { Selection::Clip(t, s) => self.scenes[s].clips[t].clone(), _ => None } } [Selection] => { ":scene-next" => match self.selected { Selection::Mix => Selection::Scene(0), Selection::Track(t) => Selection::Clip(t, 0), Selection::Scene(s) if s + 1 < self.scenes.len() => Selection::Scene(s + 1), Selection::Scene(s) => Selection::Mix, Selection::Clip(t, s) if s + 1 < self.scenes.len() => Selection::Clip(t, s + 1), Selection::Clip(t, s) => Selection::Track(t), }, ":scene-prev" => match self.selected { Selection::Mix => Selection::Mix, Selection::Track(t) => Selection::Track(t), Selection::Scene(0) => Selection::Mix, Selection::Scene(s) => Selection::Scene(s - 1), Selection::Clip(t, 0) => Selection::Track(t), Selection::Clip(t, s) => Selection::Clip(t, s - 1), }, ":track-next" => match self.selected { Selection::Mix => Selection::Track(0), Selection::Track(t) if t + 1 < self.tracks.len() => Selection::Track(t + 1), Selection::Track(t) => Selection::Mix, Selection::Scene(s) => Selection::Clip(0, s), Selection::Clip(t, s) if t + 1 < self.tracks.len() => Selection::Clip(t + 1, s), Selection::Clip(t, s) => Selection::Scene(s), }, ":track-prev" => match self.selected { Selection::Mix => Selection::Mix, Selection::Scene(s) => Selection::Scene(s), Selection::Track(0) => Selection::Mix, Selection::Track(t) => Selection::Track(t - 1), Selection::Clip(0, s) => Selection::Scene(s), Selection::Clip(t, s) => Selection::Clip(t - 1, s), }, } }); defcom! { |self, app: Tek| TekCommand { Sampler(cmd: SamplerCommand) => { println!("\n\rtodo: {cmd:?}"); None } Enqueue(clip: Option>>) => { println!("\n\rtodo: enqueue {clip:?}"); None } History(delta: isize) => { println!("\n\rtodo: {self:?}"); None } Zoom(zoom: Option) => { println!("\n\rtodo: {self:?}"); None } Scene(cmd: SceneCommand) => cmd.delegate(app, Self::Scene)? Track(cmd: TrackCommand) => cmd.delegate(app, Self::Track)? Output(cmd: OutputCommand) => cmd.delegate(app, Self::Output)? Input(cmd: InputCommand) => cmd.delegate(app, Self::Input)? Clip(cmd: ClipCommand) => cmd.delegate(app, Self::Clip)? Clock(cmd: ClockCommand) => cmd.delegate(app, Self::Clock)? Editor(cmd: MidiEditCommand) => app.editor.as_mut() .map(|editor|cmd.delegate(editor, Self::Editor)) .transpose()? .flatten() Pool(cmd: PoolCommand) => if let Some(pool) = app.pool.as_mut() { let undo = cmd.clone().delegate(pool, Self::Pool)?; if let Some(editor) = app.editor.as_mut() { match cmd { // autoselect: automatically load selected clip in editor // autocolor: update color in all places simultaneously PoolCommand::Select(_) | PoolCommand::Clip(PoolClipCommand::SetColor(_, _)) => editor.set_clip(pool.clip().as_ref()), _ => {} } }; undo } else { None } Color(palette: ItemPalette) => { use Selection::*; Some(Self::Color(match app.selected { Mix => { let old = app.color; app.color = palette; old }, Track(t) => { let old = app.tracks[t].color; app.tracks[t].color = palette; old } Scene(s) => { let old = app.scenes[s].color; app.scenes[s].color = palette; old } Clip(t, s) => { if let Some(ref clip) = app.scenes[s].clips[t] { let mut clip = clip.write().unwrap(); let old = clip.color; clip.color = palette; old } else { return Ok(None) } } })) } Edit(value: Option) => { if let Some(value) = value { if app.is_editing() != value { app.editing.store(value, Relaxed); } } else { app.editing.store(!app.is_editing(), Relaxed); }; // autocreate: create new clip from pool when entering empty cell if let Some(ref pool) = app.pool && app.is_editing() && let Selection::Clip(t, s) = app.selected && let Some(scene) = app.scenes.get_mut(s) && let Some(slot) = scene.clips.get_mut(t) && slot.is_none() { let (index, mut clip) = pool.add_new_clip(); // autocolor: new clip colors from scene and track color clip.write().unwrap().color = ItemColor::random_near( app.tracks[t].color.base.mix( scene.color.base, 0.5 ), 0.2 ).into(); if let Some(ref mut editor) = app.editor { editor.set_clip(Some(&clip)); } *slot = Some(clip); } None } Launch => { use Selection::*; match app.selected { Track(t) => app.tracks[t].player.enqueue_next(None), Clip(t, s) => app.tracks[t].player.enqueue_next(app.scenes[s].clips[t].as_ref()), Scene(s) => { for t in 0..app.tracks.len() { app.tracks[t].player.enqueue_next(app.scenes[s].clips[t].as_ref()) } }, _ => {} }; None } Select(s: Selection) => { app.selected = s; // autoedit: load focused clip in editor. if let Some(ref mut editor) = app.editor { editor.set_clip(match app.selected { Selection::Clip(t, s) if let Some(Some(Some(clip))) = app .scenes.get(s).map(|s|s.clips.get(t)) => Some(clip), _ => None }); } None } StopAll => { for track in 0..app.tracks.len(){app.tracks[track].player.enqueue_next(None);} None } } InputCommand { Add => { app.midi_ins.push(JackMidiIn::new(&app.jack, &format!("M/{}", app.midi_ins.len()), &[])?); None } } OutputCommand { Add => { app.midi_outs.push(JackMidiOut::new(&app.jack, &format!("{}/M", app.midi_outs.len()), &[])?); None } } TrackCommand { Swap(a: usize, b: usize) => { println!("\n\rtodo: {self:?}"); None } SetSize(t: usize) => { println!("\n\rtodo: {self:?}"); None } SetZoom(z: usize) => { println!("\n\rtodo: {self:?}"); None } Add => { use Selection::*; let index = app.track_add(None, None, &[], &[])?.0; app.selected = match app.selected { Track(t) => Track(index), Clip(t, s) => Clip(index, s), _ => app.selected }; Some(Self::Del(index)) } Del(index: usize) => { app.track_del(index); None } Stop(index: usize) => { app.tracks[index].player.enqueue_next(None); None } SetColor(index: usize, color: ItemPalette) => { let old = app.tracks[index].color; app.tracks[index].color = color; Some(Self::SetColor(index, old)) } TogglePlay => { Some(Self::TogglePlay) } ToggleSolo => { Some(Self::ToggleSolo) } ToggleRecord => { if let Some(t) = app.selected.track() { app.tracks[t-1].player.recording = !app.tracks[t-1].player.recording; } Some(Self::ToggleRecord) } ToggleMonitor => { if let Some(t) = app.selected.track() { app.tracks[t-1].player.monitoring = !app.tracks[t-1].player.monitoring; } Some(Self::ToggleMonitor) } } SceneCommand { Swap(a: usize, b: usize) => { println!("\n\rtodo: {self:?}"); None } SetSize(index: usize) => { println!("\n\rtodo: {self:?}"); None } SetZoom(zoom: usize) => { println!("\n\rtodo: {self:?}"); None } Add => { use Selection::*; let index = app.scene_add(None, None)?.0; app.selected = match app.selected { Scene(s) => Scene(index), Clip(t, s) => Clip(t, index), _ => app.selected }; Some(Self::Del(index)) } Del(index: usize) => { app.scene_del(index); None } SetColor(index: usize, color: ItemPalette) => { let old = app.scenes[index].color; app.scenes[index].color = color; Some(Self::SetColor(index, old)) } Enqueue(scene: usize) => { for track in 0..app.tracks.len() { app.tracks[track].player.enqueue_next(app.scenes[scene].clips[track].as_ref()); } None } } ClipCommand { Get(a: usize, b: usize) => { println!("\n\rtodo: {self:?}"); None } Edit(clip: Option>>) => { println!("\n\rtodo: edit {clip:?}"); None } SetLoop(track: usize, scene: usize, looped: bool) => { println!("\n\rtodo: {self:?}"); None } Put(track: usize, scene: usize, clip: Option>>) => { let old = app.scenes[scene].clips[track].clone(); app.scenes[scene].clips[track] = clip; Some(Self::Put(track, scene, old)) } Enqueue(track: usize, scene: usize) => { app.tracks[track].player.enqueue_next(app.scenes[scene].clips[track].as_ref()); None } SetColor(track: usize, scene: usize, color: ItemPalette) => { app.scenes[scene].clips[track].as_ref().map(|clip|{ let mut clip = clip.write().unwrap(); let old = clip.color.clone(); clip.color = color.clone(); panic!("{color:?} {old:?}"); Self::SetColor(track, scene, old) }) } } } impose!([app: Tek] { TekCommand => { ("stop" [] Some(Self::StopAll)) ("undo" [d: usize] Some(Self::History(-(d.unwrap_or(0)as isize)))) ("redo" [d: usize] Some(Self::History(d.unwrap_or(0) as isize))) ("zoom" [z: usize] Some(Self::Zoom(z))) ("edit" [] Some(Self::Edit(None))) ("edit" [c: bool] Some(Self::Edit(c))) ("color" [] Some(Self::Color(ItemPalette::random()))) ("color" [c: Color] Some(Self::Color(c.map(ItemPalette::from).expect("no color")))) ("enqueue" [c: Arc>] Some(Self::Enqueue(c))) ("launch" [] Some(Self::Launch)) ("clip" [,..a] ClipCommand::try_from_expr(app, a).map(Self::Clip)) ("clock" [,..a] ClockCommand::try_from_expr(app.clock(), a).map(Self::Clock)) ("editor" [,..a] MidiEditCommand::try_from_expr(app.editor.as_ref().expect("no editor"), a).map(Self::Editor)) ("pool" [,..a] PoolCommand::try_from_expr(app.pool.as_ref().expect("no pool"), a).map(Self::Pool)) //("sampler" [,..a] // Self::Sampler( //SamplerCommand::try_from_expr(app.sampler().as_ref().expect("no sampler"), a).expect("invalid command"))) ("scene" [,..a] SceneCommand::try_from_expr(app, a).map(Self::Scene)) ("track" [,..a] TrackCommand::try_from_expr(app, a).map(Self::Track)) ("input" [,..a] InputCommand::try_from_expr(app, a).map(Self::Input)) ("output" [,..a] OutputCommand::try_from_expr(app, a).map(Self::Output)) ("select" [t: Selection] Some(t.map(Self::Select).expect("no selection"))) ("select" [t: usize, s: usize] Some(match (t.expect("no track"), s.expect("no scene")) { (0, 0) => Self::Select(Selection::Mix), (t, 0) => Self::Select(Selection::Track(t)), (0, s) => Self::Select(Selection::Scene(s)), (t, s) => Self::Select(Selection::Clip(t, s)), })) } ClipCommand => { ("get" [a: usize, b: usize] Some(Self::Get(a.unwrap(), b.unwrap()))) ("put" [a: usize, b: usize, c: Option>>] Some(Self::Put(a.unwrap(), b.unwrap(), c.unwrap()))) ("enqueue" [a: usize, b: usize] Some(Self::Enqueue(a.unwrap(), b.unwrap()))) ("edit" [a: Option>>] Some(Self::Edit(a.unwrap()))) ("loop" [a: usize, b: usize, c: bool] Some(Self::SetLoop(a.unwrap(), b.unwrap(), c.unwrap()))) ("color" [a: usize, b: usize] Some(Self::SetColor(a.unwrap(), b.unwrap(), ItemPalette::random()))) } InputCommand => { ("add" [] Some(Self::Add)) } OutputCommand => { ("add" [] Some(Self::Add)) } SceneCommand => { ("add" [] Some(Self::Add)) ("del" [a: usize] Some(Self::Del(0))) ("zoom" [a: usize] Some(Self::SetZoom(a.unwrap()))) ("color" [a: usize] Some(Self::SetColor(a.unwrap(), ItemPalette::G[128]))) ("enqueue" [a: usize] Some(Self::Enqueue(a.unwrap()))) ("swap" [a: usize, b: usize] Some(Self::Swap(a.unwrap(), b.unwrap()))) } TrackCommand => { ("add" [] Some(Self::Add)) ("size" [a: usize] Some(Self::SetSize(a.unwrap()))) ("zoom" [a: usize] Some(Self::SetZoom(a.unwrap()))) ("color" [a: usize] Some(Self::SetColor(a.unwrap(), ItemPalette::random()))) ("del" [a: usize] Some(Self::Del(a.unwrap()))) ("stop" [a: usize] Some(Self::Stop(a.unwrap()))) ("swap" [a: usize, b: usize] Some(Self::Swap(a.unwrap(), b.unwrap()))) ("play" [] Some(Self::TogglePlay)) ("solo" [] Some(Self::ToggleSolo)) ("rec" [] Some(Self::ToggleRecord)) ("mon" [] Some(Self::ToggleMonitor)) } }); handle!(TuiIn: |self: Tek, input|Ok({ // If editing, editor keys take priority if self.is_editing() { if self.editor.handle(input)? == Some(true) { return Ok(Some(true)) } } // Handle from root keymap if let Some(command) = self.keys.command::<_, TekCommand, _>(self, input) { if let Some(undo) = command.execute(self)? { self.history.push(undo); } return Ok(Some(true)) } // Handle from selection-dependent keymaps if let Some(command) = match self.selected() { Selection::Clip(_, _) => self.keys_clip, Selection::Track(_) => self.keys_track, Selection::Scene(_) => self.keys_scene, Selection::Mix => self.keys_mix, }.command::<_, TekCommand, _>(self, input) { if let Some(undo) = command.execute(self)? { self.history.push(undo); } return Ok(Some(true)) } None }));