/////////////////////////////////////////////////////////////////////////////////////////////////// //pub fn view_nil (_: &App) -> TuiCb { //|to|to.place(&Fill::XY("ยท")) //} //Bsp::s("", //Map::south(1, //move||app.config.binds.layers.iter() //.filter_map(|a|(a.0)(app).then_some(a.1)) //.flat_map(|a|a) //.filter_map(|x|if let Value::Exp(_, iter)=x.value{ Some(iter) } else { None }) //.skip(offset) //.take(20), //|mut b,i|Fixed::X(60, Align::w(Bsp::e("(", Bsp::e( //b.next().map(|t|Fixed::X(16, Align::w(Tui::fg(Rgb(64,224,0), format!("{}", t.value))))), //Bsp::e(" ", Align::w(format!("{}", b.0.0.trim()))))))))))), //Dialog::Browse(BrowseTarget::Load, browser) => { //"bobcat".boxed() ////Bsp::s( ////Fill::X(Align::w(Margin::XY(1, 1, Bsp::e( ////Tui::bold(true, " Load project: "), ////Shrink::X(3, Fixed::Y(1, RepeatH("๐Ÿญป"))))))), ////Outer(true, Style::default().fg(Tui::g(96))) ////.enclose(Fill::XY(browser))) //}, //Dialog::Browse(BrowseTarget::Export, browser) => { //"bobcat".boxed() ////Bsp::s( ////Fill::X(Align::w(Margin::XY(1, 1, Bsp::e( ////Tui::bold(true, " Export: "), ////Shrink::X(3, Fixed::Y(1, RepeatH("๐Ÿญป"))))))), ////Outer(true, Style::default().fg(Tui::g(96))) ////.enclose(Fill::XY(browser))) //}, //Dialog::Browse(BrowseTarget::Import, browser) => { //"bobcat".boxed() ////Bsp::s( ////Fill::X(Align::w(Margin::XY(1, 1, Bsp::e( ////Tui::bold(true, " Import: "), ////Shrink::X(3, Fixed::Y(1, RepeatH("๐Ÿญป"))))))), ////Outer(true, Style::default().fg(Tui::g(96))) ////.enclose(Fill::XY(browser))) //}, // //pub fn view_history (&self) -> impl Content { //Fixed::Y(1, Fill::X(Align::w(FieldH(self.color, //format!("History ({})", self.history.len()), //self.history.last().map(|last|Fill::X(Align::w(format!("{:?}", last.0)))))))) //} //pub fn view_status_h2 (&self) -> impl Content { //self.update_clock(); //let theme = self.color; //let clock = self.clock(); //let playing = clock.is_rolling(); //let cache = clock.view_cache.clone(); ////let selection = self.selection().describe(self.tracks(), self.scenes()); //let hist_len = self.history.len(); //let hist_last = self.history.last(); //Fixed::Y(2, Stack::east(move|add: &mut dyn FnMut(&dyn Draw)|{ //add(&Fixed::X(5, Tui::bg(if playing { Rgb(0, 128, 0) } else { Rgb(128, 64, 0) }, //Either::new(false, // TODO //Thunk::new(move||Fixed::X(9, Either::new(playing, //Tui::fg(Rgb(0, 255, 0), " PLAYING "), //Tui::fg(Rgb(255, 128, 0), " STOPPED "))) //), //Thunk::new(move||Fixed::X(5, Either::new(playing, //Tui::fg(Rgb(0, 255, 0), Bsp::s(" ๐Ÿญ๐Ÿญ‘๐Ÿฌฝ ", " ๐Ÿญž๐Ÿญœ๐Ÿญ˜ ",)), //Tui::fg(Rgb(255, 128, 0), Bsp::s(" โ–—โ–„โ–– ", " โ–โ–€โ–˜ ",)))) //) //) //))); //add(&" "); //{ //let cache = cache.read().unwrap(); //add(&Fixed::X(15, Align::w(Bsp::s( //FieldH(theme, "Beat", cache.beat.view.clone()), //FieldH(theme, "Time", cache.time.view.clone()), //)))); //add(&Fixed::X(13, Align::w(Bsp::s( //Fill::X(Align::w(FieldH(theme, "BPM", cache.bpm.view.clone()))), //Fill::X(Align::w(FieldH(theme, "SR ", cache.sr.view.clone()))), //)))); //add(&Fixed::X(12, Align::w(Bsp::s( //Fill::X(Align::w(FieldH(theme, "Buf", cache.buf.view.clone()))), //Fill::X(Align::w(FieldH(theme, "Lat", cache.lat.view.clone()))), //)))); ////add(&Bsp::s( //////Fill::X(Align::w(FieldH(theme, "Selected", Align::w(selection)))), ////Fill::X(Align::w(FieldH(theme, format!("History ({})", hist_len), ////hist_last.map(|last|Fill::X(Align::w(format!("{:?}", last.0))))))), ////"" ////)); //////if let Some(last) = self.history.last() { //////add(&FieldV(theme, format!("History ({})", self.history.len()), //////Fill::X(Align::w(format!("{:?}", last.0))))); //////} //} //})) //} //pub fn view_status_v (&self) -> impl Content + use<'_> { //self.update_clock(); //let cache = self.project.clock.view_cache.read().unwrap(); //let theme = self.color; //let playing = self.clock().is_rolling(); //Tui::bg(theme.darker.rgb, Fixed::XY(20, 5, Outer(true, Style::default().fg(Tui::g(96))).enclose( //col!( //Fill::X(Align::w(Bsp::e( //Align::w(Tui::bg(if playing { Rgb(0, 128, 0) } else { Rgb(128, 64, 0) }, //Either::new(false, // TODO //Thunk::new(move||Fixed::X(9, Either::new(playing, //Tui::fg(Rgb(0, 255, 0), " PLAYING "), //Tui::fg(Rgb(255, 128, 0), " STOPPED "))) //), //Thunk::new(move||Fixed::X(5, Either::new(playing, //Tui::fg(Rgb(0, 255, 0), Bsp::s(" ๐Ÿญ๐Ÿญ‘๐Ÿฌฝ ", " ๐Ÿญž๐Ÿญœ๐Ÿญ˜ ",)), //Tui::fg(Rgb(255, 128, 0), Bsp::s(" โ–—โ–„โ–– ", " โ–โ–€โ–˜ ",)))) //) //) //)), //Bsp::s( //FieldH(theme, "Beat", cache.beat.view.clone()), //FieldH(theme, "Time", cache.time.view.clone()), //), //))), //Fill::X(Align::w(FieldH(theme, "BPM", cache.bpm.view.clone()))), //Fill::X(Align::w(FieldH(theme, "SR ", cache.sr.view.clone()))), //Fill::X(Align::w(FieldH(theme, "Buf", Bsp::e(cache.buf.view.clone(), Bsp::e(" = ", cache.lat.view.clone()))))), //)))) //} //pub fn view_status (&self) -> impl Content + use<'_> { //self.update_clock(); //let cache = self.project.clock.view_cache.read().unwrap(); //view_status(Some(self.project.selection.describe(self.tracks(), self.scenes())), //cache.sr.view.clone(), cache.buf.view.clone(), cache.lat.view.clone()) //} //pub fn view_transport (&self) -> impl Content + use<'_> { //self.update_clock(); //let cache = self.project.clock.view_cache.read().unwrap(); //view_transport(self.project.clock.is_rolling(), //cache.bpm.view.clone(), cache.beat.view.clone(), cache.time.view.clone()) //} //pub fn view_editor (&self) -> impl Content + use<'_> { //let bg = self.editor() //.and_then(|editor|editor.clip().clone()) //.map(|clip|clip.read().unwrap().color.darker) //.unwrap_or(self.color.darker); //Fill::XY(Tui::bg(bg.rgb, self.editor())) //} //pub fn view_editor_status (&self) -> impl Content + use<'_> { //self.editor().map(|e|Fixed::X(20, Outer(true, Style::default().fg(Tui::g(96))).enclose( //Fill::Y(Align::n(Bsp::s(e.clip_status(), e.edit_status())))))) //} //pub fn view_midi_ins_status (&self) -> impl Content + use<'_> { //self.project.view_midi_ins_status(self.color) //} //pub fn view_midi_outs_status (&self) -> impl Content + use<'_> { //self.project.view_midi_outs_status(self.color) //} //pub fn view_audio_ins_status (&self) -> impl Content + use<'_> { //self.project.view_audio_ins_status(self.color) //} //pub fn view_audio_outs_status (&self) -> impl Content + use<'_> { //self.project.view_audio_outs_status(self.color) //} //pub fn view_scenes (&self) -> impl Content + use<'_> { //Bsp::e( //Fixed::X(20, Align::nw(self.project.view_scenes_names())), //self.project.view_scenes_clips(), //) //} //pub fn view_scenes_names (&self) -> impl Content + use<'_> { //self.project.view_scenes_names() //} //pub fn view_scenes_clips (&self) -> impl Content + use<'_> { //self.project.view_scenes_clips() //} //pub fn view_tracks_inputs <'a> (&'a self) -> impl Content + use<'a> { //Fixed::Y(1 + self.project.midi_ins.len() as u16, //self.project.view_inputs(self.color)) //} //pub fn view_tracks_outputs <'a> (&'a self) -> impl Content + use<'a> { //self.project.view_outputs(self.color) //} //pub fn view_tracks_devices <'a> (&'a self) -> impl Content + use<'a> { //Fixed::Y(4, self.project.view_track_devices(self.color)) //} //pub fn view_tracks_names <'a> (&'a self) -> impl Content + use<'a> { //Fixed::Y(2, self.project.view_track_names(self.color)) //} //pub fn view_pool (&self) -> impl Content + use<'_> { //Fixed::X(20, Bsp::s( //Fill::X(Align::w(FieldH(self.color, "Clip pool:", ""))), //Fill::Y(Align::n(Tui::bg(Rgb(0, 0, 0), Outer(true, Style::default().fg(Tui::g(96))) //.enclose(PoolView(&self.pool))))))) //} //pub fn view_samples_keys (&self) -> impl Content + use<'_> { //self.project.sampler().map(|s|s.view_list(true, self.editor().unwrap())) //} //pub fn view_samples_grid (&self) -> impl Content + use<'_> { //self.project.sampler().map(|s|s.view_grid()) //} //pub fn view_sample_viewer (&self) -> impl Content + use<'_> { //self.project.sampler().map(|s|s.view_sample(self.editor().unwrap().get_note_pos())) //} //pub fn view_sample_info (&self) -> impl Content + use<'_> { //self.project.sampler().map(|s|s.view_sample_info(self.editor().unwrap().get_note_pos())) //} //pub fn view_sample_status (&self) -> impl Content + use<'_> { //self.project.sampler().map(|s|Outer(true, Style::default().fg(Tui::g(96))).enclose( //Fill::Y(Align::n(s.view_sample_status(self.editor().unwrap().get_note_pos()))))) //} ////let options = ||["Projects", "Settings", "Help", "Quit"].iter(); ////let option = |a,i|Tui::fg(Rgb(255,255,255), format!("{}", a)); ////Bsp::s(Tui::bold(true, "tek!"), Bsp::s("", Map::south(1, options, option))) //AppCommand => { //("x/inc" / //("stop-all") => todo!(),//app.project.stop_all(), //("enqueue", clip: Option>>) => todo!(), //("history", delta: isize) => todo!(), //("zoom", zoom: usize) => todo!(), //("select", selection: Selection) => todo!(), //("dialog" / command: DialogCommand) => todo!(), //("project" / command: ArrangementCommand) => todo!(), //("clock" / command: ClockCommand) => todo!(), //("sampler" / command: SamplerCommand) => todo!(), //("pool" / command: PoolCommand) => todo!(), //("edit" / editor: MidiEditCommand) => todo!(), //}; //DialogCommand; //ArrangementCommand; //ClockCommand; //SamplerCommand; //PoolCommand; //MidiEditCommand; //take!(DialogCommand |state: App, iter|Take::take(&state.dialog, iter)); //#[derive(Clone, Debug)] //pub enum DialogCommand { //Open { dialog: Dialog }, //Close //} //impl Command> for DialogCommand { //fn execute (self, state: &mut Option) -> Perhaps { //match self { //Self::Open { dialog } => { //*state = Some(dialog); //}, //Self::Close => { //*state = None; //} //}; //Ok(None) //} //} //dsl!(DialogCommand: |self: Dialog, iter|todo!()); //Dsl::take(&mut self.dialog, iter)); //#[tengri_proc::command(Option)]//Nope. //impl DialogCommand { //fn open (dialog: &mut Option, new: Dialog) -> Perhaps { //*dialog = Some(new); //Ok(None) //} //fn close (dialog: &mut Option) -> Perhaps { //*dialog = None; //Ok(None) //} //} // //dsl_bind!(AppCommand: App { //enqueue = |app, clip: Option>>| { todo!() }; //history = |app, delta: isize| { todo!() }; //zoom = |app, zoom: usize| { todo!() }; //stop_all = |app| { app.tracks_stop_all(); Ok(None) }; ////dialog = |app, command: DialogCommand| ////Ok(command.delegate(&mut app.dialog, |c|Self::Dialog{command: c})?); //project = |app, command: ArrangementCommand| //Ok(command.delegate(&mut app.project, |c|Self::Project{command: c})?); //clock = |app, command: ClockCommand| //Ok(command.execute(app.clock_mut())?.map(|c|Self::Clock{command: c})); //sampler = |app, command: SamplerCommand| //Ok(app.project.sampler_mut().map(|s|command.delegate(s, |command|Self::Sampler{command})) //.transpose()?.flatten()); //pool = |app, command: PoolCommand| { //let undo = command.clone().delegate(&mut app.pool, |command|AppCommand::Pool{command})?; //// update linked editor after pool action //match command { //// autoselect: automatically load selected clip in editor //PoolCommand::Select { .. } | //// autocolor: update color in all places simultaneously //PoolCommand::Clip { command: PoolClipCommand::SetColor { .. } } => { //let clip = app.pool.clip().clone(); //app.editor_mut().map(|editor|editor.set_clip(clip.as_ref())) //}, //_ => None //}; //Ok(undo) //}; //select = |app, selection: Selection| { //*app.project.selection_mut() = selection; ////todo! ////if let Some(ref mut editor) = app.editor_mut() { ////editor.set_clip(match selection { ////Selection::TrackClip { track, scene } if let Some(Some(Some(clip))) = app ////.project ////.scenes.get(scene) ////.map(|s|s.clips.get(track)) ////=> ////Some(clip), ////_ => ////None ////}); ////} //Ok(None) ////("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::TrackClip { track: t, scene: s }) }))) //// autoedit: load focused clip in editor. //}; ////fn color (app: &mut App, theme: ItemTheme) -> Perhaps { ////Ok(app.set_color(Some(theme)).map(|theme|Self::Color{theme})) ////} ////fn launch (app: &mut App) -> Perhaps { ////app.project.launch(); ////Ok(None) ////} //toggle_editor = |app, value: bool|{ app.toggle_editor(Some(value)); Ok(None) }; //editor = |app, command: MidiEditCommand| Ok(if let Some(editor) = app.editor_mut() { //let undo = command.clone().delegate(editor, |command|AppCommand::Editor{command})?; //// update linked sampler after editor action //app.project.sampler_mut().map(|sampler|match command { //// autoselect: automatically select sample in sampler //MidiEditCommand::SetNotePos { pos } => { sampler.set_note_pos(pos); }, //_ => {} //}); //undo //} else { //None //}); //}); //take!(ClockCommand |state: App, iter|Take::take(state.clock(), iter)); //take!(MidiEditCommand |state: App, iter|Ok(state.editor().map(|x|Take::take(x, iter)).transpose()?.flatten())); //take!(PoolCommand |state: App, iter|Take::take(&state.pool, iter)); //take!(SamplerCommand |state: App, iter|Ok(state.project.sampler().map(|x|Take::take(x, iter)).transpose()?.flatten())); //take!(ArrangementCommand |state: App, iter|Take::take(&state.project, iter)); /////////////////////////////////////////////////////////////////////////////////////////////////// //has_editor!(|self: App|{ //editor = self.editor; //editor_w = { //let size = self.size.w(); //let editor = self.editor.as_ref().expect("missing editor"); //let time_len = editor.time_len().get(); //let time_zoom = editor.time_zoom().get().max(1); //(5 + (time_len / time_zoom)).min(size.saturating_sub(20)).max(16) //}; //editor_h = 15; //is_editing = self.editor.is_some(); //}); //fn begin (browse: &mut Browse) => { //unreachable!(); //} //fn cancel (browse: &mut Browse) => { //todo!() ////browse.mode = None; ////Ok(None) //} //fn confirm (browse: &mut Browse) => { //todo!() ////Ok(match browse.mode { ////Some(PoolMode::Import(index, ref mut browse)) => { ////if browse.is_file() { ////let path = browse.path(); ////browse.mode = None; ////let _undo = PoolClipCommand::import(browse, index, path)?; ////None ////} else if browse.is_dir() { ////browse.mode = Some(PoolMode::Import(index, browse.chdir()?)); ////None ////} else { ////None ////} ////}, ////Some(PoolMode::Export(index, ref mut browse)) => { ////todo!() ////}, ////_ => unreachable!(), ////}) //} //fn select (browse: &mut Browse, index: usize) => { //todo!() ////Ok(match browse.mode { ////Some(PoolMode::Import(index, ref mut browse)) => { ////browse.index = index; ////None ////}, ////Some(PoolMode::Export(index, ref mut browse)) => { ////browse.index = index; ////None ////}, ////_ => unreachable!(), ////}) //} //fn chdir (browse: &mut Browse, dir: PathBuf) => { //todo!() ////Ok(match browse.mode { ////Some(PoolMode::Import(index, ref mut browse)) => { ////browse.mode = Some(PoolMode::Import(index, Browse::new(Some(dir))?)); ////None ////}, ////Some(PoolMode::Export(index, ref mut browse)) => { ////browse.mode = Some(PoolMode::Export(index, Browse::new(Some(dir))?)); ////None ////}, ////_ => unreachable!(), ////}) //} //fn filter (browse: &mut Browse, filter: Arc) => { //todo!() //} //def_command!(ArrangementCommand: |arranger: Arrangement| { //Home => { arranger.editor = None; Ok(None) }, //Edit => { //let selection = arranger.selection().clone(); //arranger.editor = if arranger.editor.is_some() { //None //} else { //match selection { //Selection::TrackClip { track, scene } => { //let clip = &mut arranger.scenes_mut()[scene].clips[track]; //if clip.is_none() { ////app.clip_auto_create(); //*clip = Some(Arc::new(RwLock::new(MidiClip::new( //&format!("t{track:02}s{scene:02}"), //false, 384, None, Some(ItemTheme::random()) //)))); //} //clip.as_ref().map(|c|c.into()) //} //_ => { //None //} //} //}; //if let Some(editor) = arranger.editor.as_mut() { //if let Some(clip) = editor.clip() { //let length = clip.read().unwrap().length.max(1); //let width = arranger.size_inner.w().saturating_sub(20).max(1); //editor.set_time_zoom(length / width); //editor.redraw(); //} //} //Ok(None) //}, ////// Set the selection //Select { selection: Selection } => { *arranger.selection_mut() = *selection; Ok(None) }, ////// Launch the selected clip or scene //Launch => { //match *arranger.selection() { //Selection::Track(t) => { //arranger.tracks[t].sequencer.enqueue_next(None) //}, //Selection::TrackClip { track, scene } => { //arranger.tracks[track].sequencer.enqueue_next(arranger.scenes[scene].clips[track].as_ref()) //}, //Selection::Scene(s) => { //for t in 0..arranger.tracks.len() { //arranger.tracks[t].sequencer.enqueue_next(arranger.scenes[s].clips[t].as_ref()) //} //}, //_ => {} //}; //Ok(None) //}, ////// Set the color of the selected entity //SetColor { palette: Option } => { //let mut palette = palette.unwrap_or_else(||ItemTheme::random()); //let selection = *arranger.selection(); //Ok(Some(Self::SetColor { palette: Some(match selection { //Selection::Mix => { //std::mem::swap(&mut palette, &mut arranger.color); //palette //}, //Selection::Scene(s) => { //std::mem::swap(&mut palette, &mut arranger.scenes[s].color); //palette //} //Selection::Track(t) => { //std::mem::swap(&mut palette, &mut arranger.tracks[t].color); //palette //} //Selection::TrackClip { track, scene } => { //if let Some(ref clip) = arranger.scenes[scene].clips[track] { //let mut clip = clip.write().unwrap(); //std::mem::swap(&mut palette, &mut clip.color); //palette //} else { //return Ok(None) //} //}, //_ => todo!() //}) })) //}, //Track { track: TrackCommand } => { todo!("delegate") }, //TrackAdd => { //let index = arranger.track_add(None, None, &[], &[])?.0; //*arranger.selection_mut() = match arranger.selection() { //Selection::Track(_) => Selection::Track(index), //Selection::TrackClip { track: _, scene } => Selection::TrackClip { //track: index, scene: *scene //}, //_ => *arranger.selection() //}; //Ok(Some(Self::TrackDelete { index })) //}, //TrackSwap { index: usize, other: usize } => { //let index = *index; //let other = *other; //Ok(Some(Self::TrackSwap { index, other })) //}, //TrackDelete { index: usize } => { //let index = *index; //let exists = arranger.tracks().get(index).is_some(); //if exists { //let track = arranger.tracks_mut().remove(index); //let Track { sequencer: Sequencer { midi_ins, midi_outs, .. }, .. } = track; //for port in midi_ins.into_iter() { //port.close()?; //} //for port in midi_outs.into_iter() { //port.close()?; //} //for scene in arranger.scenes_mut().iter_mut() { //scene.clips.remove(index); //} //} //Ok(None) ////TODO:Ok(Some(Self::TrackAdd ( index, track: Some(deleted_track) }) //}, //MidiIn { input: MidiInputCommand } => { //todo!("delegate"); Ok(None) //}, //MidiInAdd => { //arranger.midi_in_add()?; //Ok(None) //}, //MidiOut { output: MidiOutputCommand } => { //todo!("delegate"); //Ok(None) //}, //MidiOutAdd => { //arranger.midi_out_add()?; //Ok(None) //}, //Device { command: DeviceCommand } => { //todo!("delegate"); //Ok(None) //}, //DeviceAdd { index: usize } => { //todo!("delegate"); //Ok(None) //}, //Scene { scene: SceneCommand } => { //todo!("delegate"); //Ok(None) //}, //OutputAdd => { //arranger.midi_outs.push(MidiOutput::new( //arranger.jack(), //&format!("/M{}", arranger.midi_outs.len() + 1), //&[] //)?); //Ok(None) //}, //InputAdd => { //arranger.midi_ins.push(MidiInput::new( //arranger.jack(), //&format!("M{}/", arranger.midi_ins.len() + 1), //&[] //)?); //Ok(None) //}, //SceneAdd => { //let index = arranger.scene_add(None, None)?.0; //*arranger.selection_mut() = match arranger.selection() { //Selection::Scene(_) => Selection::Scene(index), //Selection::TrackClip { track, scene } => Selection::TrackClip { //track: *track, //scene: index //}, //_ => *arranger.selection() //}; //Ok(None) // TODO //}, //SceneSwap { index: usize, other: usize } => { //let index = *index; //let other = *other; //Ok(Some(Self::SceneSwap { index, other })) //}, //SceneDelete { index: usize } => { //let index = *index; //let scenes = arranger.scenes_mut(); //Ok(if scenes.get(index).is_some() { //let _scene = scenes.remove(index); //None //} else { //None //}) //}, //SceneLaunch { index: usize } => { //let index = *index; //for track in 0..arranger.tracks.len() { //let clip = arranger.scenes[index].clips[track].as_ref(); //arranger.tracks[track].sequencer.enqueue_next(clip); //} //Ok(None) //}, //Clip { scene: ClipCommand } => { //todo!("delegate") //}, //ClipGet { a: usize, b: usize } => { ////(Get [a: usize, b: usize] cmd_todo!("\n\rtodo: clip: get: {a} {b}")) ////("get" [a: usize, b: usize] Some(Self::Get(a.unwrap(), b.unwrap()))) //todo!() //}, //ClipPut { a: usize, b: usize } => { ////(Put [t: usize, s: usize, c: MaybeClip] ////Some(Self::Put(t, s, arranger.clip_put(t, s, c)))) ////("put" [a: usize, b: usize, c: MaybeClip] Some(Self::Put(a.unwrap(), b.unwrap(), c.unwrap()))) //todo!() //}, //ClipDel { a: usize, b: usize } => { ////("delete" [a: usize, b: usize] Some(Self::Put(a.unwrap(), b.unwrap(), None)))) //todo!() //}, //ClipEnqueue { a: usize, b: usize } => { ////(Enqueue [t: usize, s: usize] ////cmd!(arranger.tracks[t].sequencer.enqueue_next(arranger.scenes[s].clips[t].as_ref()))) ////("enqueue" [a: usize, b: usize] Some(Self::Enqueue(a.unwrap(), b.unwrap()))) //todo!() //}, //ClipSwap { a: usize, b: usize }=> { ////(Edit [clip: MaybeClip] cmd_todo!("\n\rtodo: clip: edit: {clip:?}")) ////("edit" [a: MaybeClip] Some(Self::Edit(a.unwrap()))) //todo!() //}, //}); // Update sequencer playhead indicator //self.now().set(0.); //if let Some((ref started_at, Some(ref playing))) = self.sequencer.play_clip { //let clip = clip.read().unwrap(); //if *playing.read().unwrap() == *clip { //let pulse = self.current().pulse.get(); //let start = started_at.pulse.get(); //let now = (pulse - start) % clip.length as f64; //self.now().set(now); //} //} //fn jack_from_lv2 (name: &str, plugin: &::livi::Plugin) -> Usually { //let counts = plugin.port_counts(); //let mut jack = Jack::new(name)?; //for i in 0..counts.atom_sequence_inputs { //jack = jack.midi_in(&format!("midi-in-{i}")) //} //for i in 0..counts.atom_sequence_outputs { //jack = jack.midi_out(&format!("midi-out-{i}")); //} //for i in 0..counts.audio_inputs { //jack = jack.audio_in(&format!("audio-in-{i}")); //} //for i in 0..counts.audio_outputs { //jack = jack.audio_out(&format!("audio-out-{i}")); //} //Ok(jack) //} //handle!(TuiIn: |self:Plugin, from|{ //match from.event() { //kpat!(KeyCode::Up) => { //self.selected = self.selected.saturating_sub(1); //Ok(Some(true)) //}, //kpat!(KeyCode::Down) => { //self.selected = (self.selected + 1).min(match &self.plugin { //Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => port_list.len() - 1, //_ => unimplemented!() //}); //Ok(Some(true)) //}, //kpat!(KeyCode::PageUp) => { //self.selected = self.selected.saturating_sub(8); //Ok(Some(true)) //}, //kpat!(KeyCode::PageDown) => { //self.selected = (self.selected + 10).min(match &self.plugin { //Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => port_list.len() - 1, //_ => unimplemented!() //}); //Ok(Some(true)) //}, //kpat!(KeyCode::Char(',')) => { //match self.plugin.as_mut() { //Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => { //let index = port_list[self.selected].index; //if let Some(value) = instance.control_input(index) { //instance.set_control_input(index, value - 0.01); //} //}, //_ => {} //} //Ok(Some(true)) //}, //kpat!(KeyCode::Char('.')) => { //match self.plugin.as_mut() { //Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => { //let index = port_list[self.selected].index; //if let Some(value) = instance.control_input(index) { //instance.set_control_input(index, value + 0.01); //} //}, //_ => {} //} //Ok(Some(true)) //}, //kpat!(KeyCode::Char('g')) => { //match self.plugin { ////Some(PluginKind::LV2(ref mut plugin)) => { ////plugin.ui_thread = Some(run_lv2_ui(LV2PluginUI::new()?)?); ////}, //Some(_) => unreachable!(), //None => {} //} //Ok(Some(true)) //}, //_ => Ok(None) //} //}); //from_atom!("plugin/lv2" => |jack: &Jack, args| -> Plugin { //let mut name = String::new(); //let mut path = String::new(); //atom!(atom in args { //Atom::Map(map) => { //if let Some(Atom::Str(n)) = map.get(&Atom::Key(":name")) { //name = String::from(*n); //} //if let Some(Atom::Str(p)) = map.get(&Atom::Key(":path")) { //path = String::from(*p); //} //}, //_ => panic!("unexpected in lv2 '{name}'"), //}); //Plugin::new_lv2(jack, &name, &path) //}); //pub struct LV2PluginUI { //write: (), //controller: (), //widget: (), //features: (), //transfer: (), //} //take!(BrowseCommand |state: Pool, iter|Ok(state.browse.as_ref() //.map(|p|Take::take(p, iter)) //.transpose()? //.flatten())); //fn file_browser_filter (&self) -> Arc { //todo!() //} //fn file_browser_path (&self) -> PathBuf { //todo!(); //} ///// Immutable reference to sample at cursor. //fn sample_selected (&self) -> Option>> { //for (i, sample) in self.mapped.iter().enumerate() { //if i == self.cursor().0 { //return sample.as_ref() //} //} //for (i, sample) in self.unmapped.iter().enumerate() { //if i + self.mapped.len() == self.cursor().0 { //return Some(sample) //} //} //None //} //fn sample_gain (&self) -> f32 { //todo!() //} //fn sample_above () -> usize { //self.note_pos().min(119) + 8 //} //fn sample_below () -> usize { //self.note_pos().max(8) - 8 //} //fn sample_to_left () -> usize { //self.note_pos().min(126) + 1 //} //fn sample_to_right () -> usize { //self.note_pos().max(1) - 1 //} //fn selected_pitch () -> u7 { //(self.note_pos() as u8).into() // TODO //} //select (&self, state: &mut Sampler, i: usize) -> Option { //Self::Select(state.set_note_pos(i)) //} ///// Assign sample to slot //set (&self, slot: u7, sample: Option>>) -> Option { //let i = slot.as_int() as usize; //let old = self.mapped[i].clone(); //self.mapped[i] = sample; //Some(Self::Set(old)) //} //set_start (&self, state: &mut Sampler, slot: u7, frame: usize) -> Option { //todo!() //} //set_gain (&self, state: &mut Sampler, slot: u7, g: f32) -> Option { //todo!() //} //note_on (&self, state: &mut Sampler, slot: u7, v: u7) -> Option { //todo!() //} //note_off (&self, state: &mut Sampler, slot: u7) -> Option { //todo!() //} //set_sample (&self, state: &mut Sampler, slot: u7, s: Option>>) -> Option { //Some(Self::SetSample(p, state.set_sample(p, s))) //} //import (&self, state: &mut Sampler, c: FileBrowserCommand) -> Option { //match c { //FileBrowserCommand::Begin => { ////let voices = &state.state.voices; ////let sample = Arc::new(RwLock::new(Sample::new("", 0, 0, vec![]))); //state.mode = Some(SamplerMode::Import(0, FileBrowser::new(None)?)); //None //}, //_ => { //println!("\n\rtodo: import: filebrowser: {c:?}"); //None //} //} //} ////(Select [i: usize] Some(Self::Select(state.set_note_pos(i)))) ////(RecordBegin [p: u7] cmd!(state.begin_recording(p.as_int() as usize))) ////(RecordCancel [] cmd!(state.cancel_recording())) ////(RecordFinish [] cmd!(state.finish_recording())) ////(SetStart [p: u7, frame: usize] cmd_todo!("\n\rtodo: {self:?}")) ////(SetGain [p: u7, gain: f32] cmd_todo!("\n\rtodo: {self:?}")) ////(NoteOn [p: u7, velocity: u7] cmd_todo!("\n\rtodo: {self:?}")) ////(NoteOff [p: u7] cmd_todo!("\n\rtodo: {self:?}")) ////(SetSample [p: u7, s: Option>>] Some(Self::SetSample(p, state.set_sample(p, s)))) ////(Import [c: FileBrowserCommand] match c { ////FileBrowserCommand::Begin => { //////let voices = &state.state.voices; //////let sample = Arc::new(RwLock::new(Sample::new("", 0, 0, vec![]))); ////state.mode = Some(SamplerMode::Import(0, FileBrowser::new(None)?)); ////None ////}, ////_ => { ////println!("\n\rtodo: import: filebrowser: {c:?}"); ////None ////} ////}))); ////("import" [,..a] ////FileBrowserCommand::try_from_expr(state, a).map(Self::Import)) ////("select" [i: usize] ////Some(Self::Select(i.expect("no index")))) ////("record/begin" [i: u7] ////Some(Self::RecordBegin(i.expect("no index")))) ////("record/cancel" [] ////Some(Self::RecordCancel)) ////("record/finish" [] ////Some(Self::RecordFinish)) ////("set/sample" [i: u7, s: Option>>] ////Some(Self::SetSample(i.expect("no index"), s.expect("no sampler")))) ////("set/start" [i: u7, s: usize] ////Some(Self::SetStart(i.expect("no index"), s.expect("no start")))) ////("set/gain" [i: u7, g: f32] ////Some(Self::SetGain(i.expect("no index"), g.expect("no gain")))) ////("note/on" [p: u7, v: u7] ////Some(Self::NoteOn(p.expect("no slot"), v.expect("no velocity")))) ////("note/off" [p: u7] ////Some(Self::NoteOff(p.expect("no slot")))))); // TODO: //for port in midi_in.iter() { //for event in port.iter() { //match event { //(time, Ok(LiveEvent::Midi {message, ..})) => match message { //MidiMessage::NoteOn {ref key, ..} if let Some(editor) = self.editor.as_ref() => { //editor.set_note_pos(key.as_int() as usize); //}, //MidiMessage::Controller {controller, value} if let (Some(editor), Some(sampler)) = ( //self.editor.as_ref(), //self.sampler.as_ref(), //) => { //// TODO: give sampler its own cursor //if let Some(sample) = &sampler.mapped[editor.note_pos()] { //sample.write().unwrap().handle_cc(*controller, *value) //} //} //_ =>{} //}, //_ =>{} //} //} //} //scene_scroll: Fill::Y(Fixed::X(1, ScrollbarV { //offset: arrangement.scene_scroll, //length: h_scenes_area as usize, //total: h_scenes as usize, //})), //take!(SceneCommand |state: Arrangement, iter|state.selected_scene().as_ref() //.map(|t|Take::take(t, iter)).transpose().map(|x|x.flatten())); //pub(crate) fn io_conns <'a, T: PortsSizes<'a>> ( //fg: Color, bg: Color, iter: &mut impl Iterator, &'a [Connect], usize, usize)> //) -> impl Content + 'a { //Fill::XY(Thunk::new(move|to: &mut TuiOut|for (_, _, connections, y, y2) in &mut *iter { //to.place(&map_south(y as u16, (y2-y) as u16, Bsp::s( //Fill::Y(Tui::bold(true, wrap(bg, fg, Fill::Y(Align::w(&"โ–žโ–žโ–žโ–ž โ–žโ–žโ–žโ–ž"))))), //Thunk::new(|to: &mut TuiOut|for (index, _connection) in connections.iter().enumerate() { //to.place(&map_south(index as u16, 1, Fill::Y(Align::w(Tui::bold(false, //wrap(bg, fg, Fill::Y(&""))))))) //}) //))) //})) //} //track_scroll: Fill::Y(Fixed::Y(1, ScrollbarH { //offset: arrangement.track_scroll, //length: h_tracks_area as usize, //total: h_scenes as usize, //})), //take!(TrackCommand |state: Arrangement, iter|state.selected_track().as_ref() //.map(|t|Take::take(t, iter)).transpose().map(|x|x.flatten())); //macro_rules! impl_port { //($Name:ident : $Spec:ident -> $Pair:ident |$jack:ident, $name:ident|$port:expr) => { //#[derive(Debug)] pub struct $Name { ///// Handle to JACK client, for receiving reconnect events. //jack: Jack<'static>, ///// Port name //name: Arc, ///// Port handle. //port: Port<$Spec>, ///// List of ports to connect to. //conn: Vec //} //impl AsRef> for $Name { //fn as_ref (&self) -> &Port<$Spec> { &self.port } //} //impl $Name { //pub fn new ($jack: &Jack, name: impl AsRef, connect: &[PortConnect]) //-> Usually //{ //let $name = name.as_ref(); //let jack = $jack.clone(); //let port = $port?; //let name = $name.into(); //let conn = connect.to_vec(); //let port = Self { jack, port, name, conn }; //port.connect_to_matching()?; //Ok(port) //} //pub fn name (&self) -> &Arc { &self.name } //pub fn port (&self) -> &Port<$Spec> { &self.port } //pub fn port_mut (&mut self) -> &mut Port<$Spec> { &mut self.port } //pub fn into_port (self) -> Port<$Spec> { self.port } //pub fn close (self) -> Usually<()> { //let Self { jack, port, .. } = self; //Ok(jack.with_client(|client|client.unregister_port(port))?) //} //} //impl HasJack<'static> for $Name { //fn jack (&self) -> &'static Jack<'static> { &self.jack } //} //impl JackPort<'static> for $Name { //type Port = $Spec; //type Pair = $Pair; //fn port (&self) -> &Port<$Spec> { &self.port } //} //impl ConnectTo<'static, &str> for $Name { //fn connect_to (&self, to: &str) -> Usually { //self.with_client(|c|if let Some(ref port) = c.port_by_name(to.as_ref()) { //self.connect_to(port) //} else { //Ok(Missing) //}) //} //} //impl ConnectTo<'static, &Port> for $Name { //fn connect_to (&self, port: &Port) -> Usually { //self.with_client(|c|Ok(if let Ok(_) = c.connect_ports(&self.port, port) { //Connected //} else if let Ok(_) = c.connect_ports(port, &self.port) { //Connected //} else { //Mismatch //})) //} //} //impl ConnectTo<'static, &Port<$Pair>> for $Name { //fn connect_to (&self, port: &Port<$Pair>) -> Usually { //self.with_client(|c|Ok(if let Ok(_) = c.connect_ports(&self.port, port) { //Connected //} else if let Ok(_) = c.connect_ports(port, &self.port) { //Connected //} else { //Mismatch //})) //} //} //impl ConnectAuto<'static> for $Name { //fn connections (&self) -> &[PortConnect] { //&self.conn //} //} //}; //}