mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
wip: add overlay for help/menu modals
This commit is contained in:
parent
fa2e08e81c
commit
38fb348d19
10 changed files with 323 additions and 234 deletions
|
|
@ -1,3 +1,6 @@
|
|||
(@esc menu)
|
||||
(@f1 help)
|
||||
|
||||
(@u undo 1)
|
||||
(@shift-u redo 1)
|
||||
(@space clock toggle)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,17 @@
|
|||
(@esc menu)
|
||||
(@f1 help)
|
||||
|
||||
(@u undo 1)
|
||||
(@shift-u redo 1)
|
||||
|
||||
(@space clock toggle)
|
||||
(@shift-space clock toggle 0)
|
||||
|
||||
(@c color)
|
||||
|
||||
(@q launch)
|
||||
|
||||
(@shift-I input add)
|
||||
(@shift-O output add)
|
||||
|
||||
(@r record/begin :sample)
|
||||
|
|
|
|||
|
|
@ -30,45 +30,141 @@ expose!([self: Tek] {
|
|||
}
|
||||
}
|
||||
[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),
|
||||
},
|
||||
":scene-next" => self.selected.scene_next(self.scenes.len()),
|
||||
":scene-prev" => self.selected.scene_prev(),
|
||||
":track-next" => self.selected.track_next(self.tracks.len()),
|
||||
":track-prev" => self.selected.track_prev(),
|
||||
}
|
||||
});
|
||||
|
||||
impose!([app: Tek] {
|
||||
|
||||
TekCommand => {
|
||||
("help" []
|
||||
Some(Self::ToggleHelp))
|
||||
("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<RwLock<MidiClip>>]
|
||||
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<Arc<RwLock<MidiClip>>>]
|
||||
Some(Self::Put(a.unwrap(), b.unwrap(), c.unwrap())))
|
||||
("enqueue" [a: usize, b: usize]
|
||||
Some(Self::Enqueue(a.unwrap(), b.unwrap())))
|
||||
("edit" [a: Option<Arc<RwLock<MidiClip>>>]
|
||||
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))
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
defcom! { |self, app: Tek|
|
||||
|
||||
TekCommand {
|
||||
|
||||
ToggleHelp => {
|
||||
app.modal = Some(Modal::Help);
|
||||
None
|
||||
}
|
||||
|
||||
Sampler(cmd: SamplerCommand) => {
|
||||
println!("\n\rtodo: {cmd:?}");
|
||||
None
|
||||
|
|
@ -380,120 +476,3 @@ defcom! { |self, app: Tek|
|
|||
|
||||
}
|
||||
}
|
||||
|
||||
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<RwLock<MidiClip>>]
|
||||
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<Arc<RwLock<MidiClip>>>]
|
||||
Some(Self::Put(a.unwrap(), b.unwrap(), c.unwrap())))
|
||||
("enqueue" [a: usize, b: usize]
|
||||
Some(Self::Enqueue(a.unwrap(), b.unwrap())))
|
||||
("edit" [a: Option<Arc<RwLock<MidiClip>>>]
|
||||
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))
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use crate::*;
|
||||
|
||||
#[derive(Default, Debug)] pub struct Tek {
|
||||
#[derive(Default, Debug)]
|
||||
pub struct Tek {
|
||||
/// Must not be dropped for the duration of the process
|
||||
pub jack: Jack,
|
||||
/// Source of time
|
||||
|
|
@ -50,50 +51,20 @@ use crate::*;
|
|||
// Cache of formatted strings
|
||||
pub view_cache: Arc<RwLock<ViewCache>>,
|
||||
// Input handler function
|
||||
pub handler: Option<fn(&mut Self, &TuiIn)->Result<Option<bool>, Box<(dyn std::error::Error + 'static)>>>
|
||||
pub handler: Option<fn(&mut Self, &TuiIn)->Result<Option<bool>, Box<(dyn std::error::Error + 'static)>>>,
|
||||
// Modal overlay
|
||||
pub modal: Option<Modal>
|
||||
}
|
||||
|
||||
impl Tek {
|
||||
|
||||
pub(crate) fn clip (&self) -> Option<Arc<RwLock<MidiClip>>> {
|
||||
self.scene()?.clips.get(self.selected().track()?)?.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn toggle_loop (&mut self) {
|
||||
if let Some(clip) = self.clip() {
|
||||
clip.write().unwrap().toggle_loop()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn activate (&mut self) -> Usually<()> {
|
||||
let selected = self.selected().clone();
|
||||
match selected {
|
||||
Selection::Scene(s) => {
|
||||
let mut clips = vec![];
|
||||
for (t, _) in self.tracks().iter().enumerate() {
|
||||
clips.push(self.scenes()[s].clips[t].clone());
|
||||
}
|
||||
for (t, track) in self.tracks_mut().iter_mut().enumerate() {
|
||||
if track.player.play_clip.is_some() || clips[t].is_some() {
|
||||
track.player.enqueue_next(clips[t].as_ref());
|
||||
}
|
||||
}
|
||||
if self.clock().is_stopped() {
|
||||
self.clock().play_from(Some(0))?;
|
||||
}
|
||||
},
|
||||
Selection::Clip(t, s) => {
|
||||
let clip = self.scenes()[s].clips[t].clone();
|
||||
self.tracks_mut()[t].player.enqueue_next(clip.as_ref());
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add multiple tracks
|
||||
pub fn tracks_add (
|
||||
&mut self, count: usize, width: Option<usize>,
|
||||
midi_from: &[PortConnect], midi_to: &[PortConnect],
|
||||
&mut self,
|
||||
count: usize,
|
||||
width: Option<usize>,
|
||||
midi_from: &[PortConnect],
|
||||
midi_to: &[PortConnect],
|
||||
) -> Usually<()> {
|
||||
let jack = self.jack().clone();
|
||||
let track_color_1 = ItemColor::random();
|
||||
|
|
@ -108,8 +79,11 @@ impl Tek {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Add a track
|
||||
pub fn track_add (
|
||||
&mut self, name: Option<&str>, color: Option<ItemPalette>,
|
||||
&mut self,
|
||||
name: Option<&str>,
|
||||
color: Option<ItemPalette>,
|
||||
midi_froms: &[PortConnect],
|
||||
midi_tos: &[PortConnect],
|
||||
) -> Usually<(usize, &mut Track)> {
|
||||
|
|
@ -139,6 +113,7 @@ impl Tek {
|
|||
Ok((index, &mut self.tracks_mut()[index]))
|
||||
}
|
||||
|
||||
/// Delete a track
|
||||
pub fn track_del (&mut self, index: usize) {
|
||||
self.tracks_mut().remove(index);
|
||||
for scene in self.scenes_mut().iter_mut() {
|
||||
|
|
@ -146,6 +121,7 @@ impl Tek {
|
|||
}
|
||||
}
|
||||
|
||||
/// Add multiple scenes
|
||||
pub fn scenes_add (&mut self, n: usize) -> Usually<()> {
|
||||
let scene_color_1 = ItemColor::random();
|
||||
let scene_color_2 = ItemColor::random();
|
||||
|
|
@ -157,6 +133,7 @@ impl Tek {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Add a scene
|
||||
pub fn scene_add (&mut self, name: Option<&str>, color: Option<ItemPalette>)
|
||||
-> Usually<(usize, &mut Scene)>
|
||||
{
|
||||
|
|
@ -170,6 +147,7 @@ impl Tek {
|
|||
Ok((index, &mut self.scenes_mut()[index]))
|
||||
}
|
||||
|
||||
/// Generate the default name for a new scene
|
||||
pub fn scene_default_name (&self) -> Arc<str> {
|
||||
format!("Sc{:3>}", self.scenes().len() + 1).into()
|
||||
}
|
||||
|
|
@ -216,6 +194,45 @@ impl Tek {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get the active clip
|
||||
pub(crate) fn clip (&self) -> Option<Arc<RwLock<MidiClip>>> {
|
||||
self.scene()?.clips.get(self.selected().track()?)?.clone()
|
||||
}
|
||||
|
||||
/// Toggle looping for the active clip
|
||||
pub(crate) fn toggle_loop (&mut self) {
|
||||
if let Some(clip) = self.clip() {
|
||||
clip.write().unwrap().toggle_loop()
|
||||
}
|
||||
}
|
||||
|
||||
/// Launch a clip or scene
|
||||
pub(crate) fn activate (&mut self) -> Usually<()> {
|
||||
let selected = self.selected().clone();
|
||||
match selected {
|
||||
Selection::Scene(s) => {
|
||||
let mut clips = vec![];
|
||||
for (t, _) in self.tracks().iter().enumerate() {
|
||||
clips.push(self.scenes()[s].clips[t].clone());
|
||||
}
|
||||
for (t, track) in self.tracks_mut().iter_mut().enumerate() {
|
||||
if track.player.play_clip.is_some() || clips[t].is_some() {
|
||||
track.player.enqueue_next(clips[t].as_ref());
|
||||
}
|
||||
}
|
||||
if self.clock().is_stopped() {
|
||||
self.clock().play_from(Some(0))?;
|
||||
}
|
||||
},
|
||||
Selection::Clip(t, s) => {
|
||||
let clip = self.scenes()[s].clips[t].clone();
|
||||
self.tracks_mut()[t].player.enqueue_next(clip.as_ref());
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
has_size!(<TuiOut>|self: Tek|&self.size);
|
||||
|
|
@ -242,8 +259,16 @@ pub trait HasSelection {
|
|||
fn selected_mut (&mut self) -> &mut Selection;
|
||||
}
|
||||
|
||||
/// Various possible modal overlays
|
||||
#[derive(PartialEq, Clone, Copy, Debug)]
|
||||
pub enum Modal {
|
||||
Help,
|
||||
Menu,
|
||||
}
|
||||
|
||||
/// Represents the current user selection in the arranger
|
||||
#[derive(PartialEq, Clone, Copy, Debug, Default)] pub enum Selection {
|
||||
#[derive(PartialEq, Clone, Copy, Debug, Default)]
|
||||
pub enum Selection {
|
||||
/// The whole mix is selected
|
||||
#[default] Mix,
|
||||
/// A track is selected.
|
||||
|
|
@ -272,10 +297,50 @@ impl Selection {
|
|||
use Selection::*;
|
||||
match self { Clip(t, _) => Some(*t), Track(t) => Some(*t), _ => None }
|
||||
}
|
||||
pub fn track_next (&self, len: usize) -> Self {
|
||||
match self {
|
||||
Selection::Mix => Selection::Track(0),
|
||||
Selection::Track(t) if t + 1 < len => Selection::Track(t + 1),
|
||||
Selection::Track(t) => Selection::Mix,
|
||||
Selection::Scene(s) => Selection::Clip(0, *s),
|
||||
Selection::Clip(t, s) if t + 1 < len => Selection::Clip(t + 1, *s),
|
||||
Selection::Clip(t, s) => Selection::Scene(*s),
|
||||
}
|
||||
}
|
||||
pub fn track_prev (&self) -> Self {
|
||||
match self {
|
||||
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),
|
||||
}
|
||||
}
|
||||
pub fn scene (&self) -> Option<usize> {
|
||||
use Selection::*;
|
||||
match self { Clip(_, s) => Some(*s), Scene(s) => Some(*s), _ => None }
|
||||
}
|
||||
pub fn scene_next (&self, len: usize) -> Self {
|
||||
match self {
|
||||
Selection::Mix => Selection::Scene(0),
|
||||
Selection::Track(t) => Selection::Clip(*t, 0),
|
||||
Selection::Scene(s) if s + 1 < len => Selection::Scene(s + 1),
|
||||
Selection::Scene(s) => Selection::Mix,
|
||||
Selection::Clip(t, s) if s + 1 < len => Selection::Clip(*t, s + 1),
|
||||
Selection::Clip(t, s) => Selection::Track(*t),
|
||||
}
|
||||
}
|
||||
pub fn scene_prev (&self) -> Self {
|
||||
match self {
|
||||
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),
|
||||
}
|
||||
}
|
||||
pub fn describe (&self, tracks: &[Track], scenes: &[Scene]) -> Arc<str> {
|
||||
format!("{}", match self {
|
||||
Self::Mix => "Everything".to_string(),
|
||||
|
|
|
|||
|
|
@ -2,9 +2,17 @@ use crate::*;
|
|||
pub(crate) use std::fmt::Write;
|
||||
pub(crate) use ::tengri::tui::ratatui::prelude::Position;
|
||||
|
||||
view!(TuiOut: |self: Tek| self.size.of(View(self, self.view)); {
|
||||
view!(TuiOut: |self: Tek| {
|
||||
self.size.of(View(self, self.view))
|
||||
}; {
|
||||
":nil" =>
|
||||
Box::new("nil"),
|
||||
":modal" =>
|
||||
When::new(self.modal.is_some(), Bsp::b(
|
||||
Fill::xy(Tui::fg_bg(Color::Rgb(64,64,64), Color::Rgb(32,32,32), "")),
|
||||
Fixed::xy(20, 10, Tui::fg_bg(Color::Rgb(255,255,255), Color::Rgb(16,16,16),
|
||||
Outer(true, Style::default().fg(Tui::g(96))).enclose(self.modal)))
|
||||
)).boxed(),
|
||||
":transport" =>
|
||||
self.view_transport().boxed(),
|
||||
":arranger" =>
|
||||
|
|
@ -859,3 +867,23 @@ impl ViewCache {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Content<TuiOut> for Modal {
|
||||
fn content (&self) -> impl Render<TuiOut> {
|
||||
match self {
|
||||
Self::Menu => self.view_menu().boxed(),
|
||||
Self::Help => self.view_help().boxed(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Modal {
|
||||
fn view_menu (&self) -> impl Content<TuiOut> {
|
||||
"menu"
|
||||
}
|
||||
fn view_help (&self) -> impl Content<TuiOut> {
|
||||
Bsp::s(Tui::bold(true, "Help"), Bsp::s("", Map::south(1,
|
||||
||0..5,
|
||||
|binding, _|Bsp::e(" key ", " command "))))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
(bsp/a :modal
|
||||
(bsp/s (fixed/y 1 :transport)
|
||||
(bsp/n (fixed/y 1 :status)
|
||||
(fill/xy (bsp/a
|
||||
(fill/xy (align/e :pool))
|
||||
:arranger))))
|
||||
:arranger)))))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
(bsp/a :modal
|
||||
(bsp/s (fixed/y 1 :transport)
|
||||
(bsp/s :sample
|
||||
(bsp/n (fixed/y 1 :status)
|
||||
(bsp/w (fixed/x :w-sidebar :pool)
|
||||
(bsp/e :samples-keys
|
||||
(fill/y :editor))))))
|
||||
(fill/y :editor)))))))
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
(bsp/a :modal
|
||||
(bsp/s (fixed/y 1 :transport)
|
||||
(bsp/n (fixed/y 1 :status)
|
||||
(fill/xy :samples-grid)))
|
||||
(fill/xy :samples-grid))))
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
(bsp/a :modal
|
||||
(bsp/s (fixed/y 1 :transport)
|
||||
(bsp/n (fixed/y 1 :status)
|
||||
(fill/xy (bsp/a
|
||||
(fill/xy (align/e :pool))
|
||||
:editor))))
|
||||
:editor)))))
|
||||
|
|
|
|||
|
|
@ -10,8 +10,10 @@ pub fn main () -> Usually<()> {
|
|||
#[derive(Debug, Parser)]
|
||||
#[command(version, about = Some(HEADER), long_about = Some(HEADER))]
|
||||
pub struct Cli {
|
||||
/// Which app to initialize
|
||||
#[command(subcommand)] mode: Mode,
|
||||
/// Pre-defined configuration modes.
|
||||
///
|
||||
/// TODO: Replace these with scripted configurations.
|
||||
#[command(subcommand)] mode: LaunchMode,
|
||||
/// Name of JACK client
|
||||
#[arg(short='n', long)] name: Option<String>,
|
||||
/// Whether to attempt to become transport master
|
||||
|
|
@ -42,7 +44,7 @@ pub struct Cli {
|
|||
|
||||
/// Application modes
|
||||
#[derive(Debug, Clone, Subcommand)]
|
||||
pub enum Mode {
|
||||
pub enum LaunchMode {
|
||||
/// ⏯️ A standalone transport clock.
|
||||
Clock,
|
||||
/// 🎼 A MIDI sequencer.
|
||||
|
|
@ -82,7 +84,7 @@ impl Cli {
|
|||
let audio_froms = &[left_froms.as_slice(), right_froms.as_slice()];
|
||||
let audio_tos = &[left_tos.as_slice(), right_tos.as_slice()];
|
||||
let clip = match mode {
|
||||
Mode::Sequencer | Mode::Groovebox => Some(Arc::new(RwLock::new(MidiClip::new(
|
||||
LaunchMode::Sequencer | LaunchMode::Groovebox => Some(Arc::new(RwLock::new(MidiClip::new(
|
||||
"Clip", true, 384usize, None, Some(ItemColor::random().into())),
|
||||
))),
|
||||
_ => None,
|
||||
|
|
@ -104,44 +106,44 @@ impl Cli {
|
|||
color: ItemPalette::random(),
|
||||
clock: Clock::new(jack, self.bpm)?,
|
||||
view: SourceIter(match mode {
|
||||
Mode::Clock => include_str!("./edn/transport.edn"),
|
||||
Mode::Sequencer => include_str!("./edn/sequencer.edn"),
|
||||
Mode::Groovebox => include_str!("./edn/groovebox.edn"),
|
||||
Mode::Arranger { .. } => include_str!("./edn/arranger.edn"),
|
||||
Mode::Sampler => include_str!("./edn/sampler.edn"),
|
||||
LaunchMode::Clock => include_str!("./edn/transport.edn"),
|
||||
LaunchMode::Sequencer => include_str!("./edn/sequencer.edn"),
|
||||
LaunchMode::Groovebox => include_str!("./edn/groovebox.edn"),
|
||||
LaunchMode::Arranger { .. } => include_str!("./edn/arranger.edn"),
|
||||
LaunchMode::Sampler => include_str!("./edn/sampler.edn"),
|
||||
_ => todo!("{mode:?}"),
|
||||
}),
|
||||
pool: match mode {
|
||||
Mode::Sequencer | Mode::Groovebox => clip.as_ref().map(Into::into),
|
||||
Mode::Arranger { .. } => Some(Default::default()),
|
||||
LaunchMode::Sequencer | LaunchMode::Groovebox => clip.as_ref().map(Into::into),
|
||||
LaunchMode::Arranger { .. } => Some(Default::default()),
|
||||
_ => None,
|
||||
},
|
||||
editor: match mode {
|
||||
Mode::Sequencer | Mode::Groovebox => clip.as_ref().map(Into::into),
|
||||
Mode::Arranger { .. } => Some(Default::default()),
|
||||
LaunchMode::Sequencer | LaunchMode::Groovebox => clip.as_ref().map(Into::into),
|
||||
LaunchMode::Arranger { .. } => Some(Default::default()),
|
||||
_ => None
|
||||
},
|
||||
midi_ins,
|
||||
midi_outs,
|
||||
midi_buf: match mode {
|
||||
Mode::Clock | Mode::Sampler => vec![],
|
||||
Mode::Sequencer | Mode::Groovebox | Mode::Arranger {..} => vec![vec![];65536],
|
||||
LaunchMode::Clock | LaunchMode::Sampler => vec![],
|
||||
LaunchMode::Sequencer | LaunchMode::Groovebox | LaunchMode::Arranger {..} => vec![vec![];65536],
|
||||
_ => todo!("{mode:?}"),
|
||||
},
|
||||
handler: Some(match mode {
|
||||
Mode::Sequencer => handle_sequencer,
|
||||
Mode::Groovebox => handle_groovebox,
|
||||
Mode::Sampler => handle_sampler,
|
||||
LaunchMode::Sequencer => handle_sequencer,
|
||||
LaunchMode::Groovebox => handle_groovebox,
|
||||
LaunchMode::Sampler => handle_sampler,
|
||||
_ => handle_arranger,
|
||||
}),
|
||||
tracks: match mode {
|
||||
Mode::Sequencer => vec![
|
||||
LaunchMode::Sequencer => vec![
|
||||
Track::new_sequencer()
|
||||
],
|
||||
Mode::Groovebox => vec![
|
||||
LaunchMode::Groovebox => vec![
|
||||
Track::new_groovebox(jack, midi_froms.as_slice(), audio_froms, audio_tos)?
|
||||
],
|
||||
Mode::Sampler => vec![
|
||||
LaunchMode::Sampler => vec![
|
||||
Track::new_sampler(jack, midi_froms.as_slice(), audio_froms, audio_tos)?
|
||||
],
|
||||
_ => vec![]
|
||||
|
|
@ -149,7 +151,7 @@ impl Cli {
|
|||
scenes,
|
||||
..Default::default()
|
||||
};
|
||||
if let &Mode::Arranger { scenes, tracks, track_width, .. } = mode {
|
||||
if let &LaunchMode::Arranger { scenes, tracks, track_width, .. } = mode {
|
||||
app.arranger = Default::default();
|
||||
app.selected = Selection::Clip(1, 1);
|
||||
app.scenes_add(scenes)?;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue