mirror of
https://codeberg.org/unspeaker/tek.git
synced 2026-10-08 18:56:42 +02:00
This commit is contained in:
parent
a4931d8e4f
commit
def7a1b210
17 changed files with 2209 additions and 1790 deletions
623
src/tek.rs
623
src/tek.rs
|
|
@ -239,8 +239,8 @@ fn run_new_plain (config: Config) -> Usually<()> {
|
|||
connect_audio_outs(&jack, &"R".to_string(), &right_to, None)?.into_iter()
|
||||
));
|
||||
//&jack, Clock::new(&jack, *bpm)?, &lf, <, &rf, &rt, &mf, &mt, &mfr, &mtr)?;
|
||||
proj.tracks_add(tracks.unwrap_or(0), None, &[], &[])?;
|
||||
proj.scenes_add(scenes.unwrap_or(0))?;
|
||||
proj.tracks_add_many(tracks.unwrap_or(0), None, [].into(), [].into())?;
|
||||
proj.scenes_add_many(scenes.unwrap_or(0))?;
|
||||
//if matches!(self, Action::Status) {
|
||||
//// Show status and exit
|
||||
//tek_print_status(&proj);
|
||||
|
|
@ -298,49 +298,6 @@ mod config {
|
|||
/// Collection of custom view definitions.
|
||||
pub type Views = Arc<RwLock<BTreeMap<Arc<str>, Arc<str>>>>;
|
||||
|
||||
/// Collection of input bindings.
|
||||
pub type Binds = Arc<RwLock<BTreeMap<Arc<str>, Bind<TuiEvent, Arc<str>>>>>;
|
||||
|
||||
/// An map of input events (e.g. [TuiEvent]) to [Binding]s.
|
||||
///
|
||||
/// ```
|
||||
/// let lang = "(@x (nop)) (@y (nop) (nop))";
|
||||
/// let bind = tek::Bind::<tek::tengri::TuiEvent, std::sync::Arc<str>>::load(&lang).unwrap();
|
||||
/// assert_eq!(bind.query(&'x'.into()).map(|x|x.len()), Some(1));
|
||||
/// //assert_eq!(bind.query(&'y'.into()).map(|x|x.len()), Some(2));
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct Bind<E, C>(
|
||||
/// Map of each event (e.g. key combination) to
|
||||
/// all command expressions bound to it by
|
||||
/// all loaded input layers.
|
||||
pub BTreeMap<E, Vec<Binding<C>>>
|
||||
);
|
||||
|
||||
/// A sequence of zero or more commands (e.g. [AppCommand]),
|
||||
/// optionally filtered by [Condition] to form layers.
|
||||
///
|
||||
/// ```
|
||||
/// //FIXME: Why does it overflow?
|
||||
/// //let binding: Binding<()> = tek::Binding { ..Default::default() };
|
||||
/// ```
|
||||
#[derive(Debug, Clone)] pub struct Binding<C> {
|
||||
pub commands: Arc<[C]>,
|
||||
pub condition: Option<Condition>,
|
||||
pub description: Option<Arc<str>>,
|
||||
pub source: Option<Arc<PathBuf>>,
|
||||
}
|
||||
|
||||
/// Condition that must evaluate to true in order to enable an input layer.
|
||||
///
|
||||
/// ```
|
||||
/// let condition = tek::Condition(std::sync::Arc::new(Box::new(||{true})));
|
||||
/// ```
|
||||
#[derive(Clone)]
|
||||
pub struct Condition(
|
||||
pub Arc<Box<dyn Fn()->bool + Send + Sync>>
|
||||
);
|
||||
|
||||
/// Collection of UI modes.
|
||||
///
|
||||
/// ```
|
||||
|
|
@ -651,14 +608,10 @@ mod app {
|
|||
primitive!(usize: try_to_usize);
|
||||
primitive!(isize: try_to_isize);
|
||||
impl_has!(Clock: |self: App|self.project.clock);
|
||||
impl_has!(Vec<MidiInput>: |self: App|self.project.midi_ins);
|
||||
impl_has!(Vec<MidiOutput>: |self: App|self.project.midi_outs);
|
||||
impl_has!(Dialog: |self: App|self.dialog);
|
||||
impl_has!(Jack<'static>: |self: App|self.jack);
|
||||
impl_has!(Pool: |self: App|self.pool);
|
||||
impl_has!(Selection: |self: App|self.project.selection);
|
||||
impl_as_ref!(Vec<Scene>: |self: App|self.project.as_ref());
|
||||
impl_as_mut!(Vec<Scene>: |self: App|self.project.as_mut());
|
||||
impl_as_ref_opt!(MidiEditor: |self: App|self.project.as_ref_opt());
|
||||
impl_as_mut_opt!(MidiEditor: |self: App|self.project.as_mut_opt());
|
||||
impl_has_clips!( |self: App|self.pool.clips);
|
||||
|
|
@ -700,6 +653,7 @@ mod app {
|
|||
#[namespace(Option<usize> App::get_opt_usize)]
|
||||
#[namespace(Option<Arc<RwLock<MidiClip>>> App::get_clip)]
|
||||
#[namespace(Dialog App::get_dialog)]
|
||||
#[namespace(ControlAxis App::get_axis)]
|
||||
pub struct App {
|
||||
/// Exit flag
|
||||
pub exit: Exit,
|
||||
|
|
@ -815,7 +769,8 @@ mod app {
|
|||
if let Some(expr) = src.expr()? {
|
||||
match (expr.head()?, expr.tail()?) {
|
||||
(Some("g"), Some(tail)) => {
|
||||
let n = try_to_u8(tail.head().map_err(Into::into))?.ok_or(LanguageError::Domain("not gray"))?;
|
||||
let n = try_to_u8(tail.head().map_err(Into::into))?
|
||||
.ok_or(LanguageError::Domain("not gray"))?;
|
||||
Ok(Some(Color::Rgb(n, n, n)))
|
||||
},
|
||||
(Some("rgb"), Some(tail)) => {
|
||||
|
|
@ -870,6 +825,14 @@ mod app {
|
|||
})).transpose()
|
||||
}
|
||||
|
||||
fn get_axis (&self, src: impl Language) -> Perhaps<ControlAxis> {
|
||||
Ok(src.word()?.map(|word|Ok(match word {
|
||||
"x" => ControlAxis::X,
|
||||
"y" => ControlAxis::Y,
|
||||
_ => return Err(format!("unknown axis {word}"))
|
||||
})).transpose()?)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -877,6 +840,49 @@ pub use self::bind::*;
|
|||
mod bind {
|
||||
use crate::*;
|
||||
|
||||
/// Collection of input bindings.
|
||||
pub type Binds = Arc<RwLock<BTreeMap<Arc<str>, Bind<TuiEvent, Arc<str>>>>>;
|
||||
|
||||
/// An map of input events (e.g. [TuiEvent]) to [Binding]s.
|
||||
///
|
||||
/// ```
|
||||
/// let lang = "(@x (nop)) (@y (nop) (nop))";
|
||||
/// let bind = tek::Bind::<tek::tengri::TuiEvent, std::sync::Arc<str>>::load(&lang).unwrap();
|
||||
/// assert_eq!(bind.query(&'x'.into()).map(|x|x.len()), Some(1));
|
||||
/// //assert_eq!(bind.query(&'y'.into()).map(|x|x.len()), Some(2));
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct Bind<E, C>(
|
||||
/// Map of each event (e.g. key combination) to
|
||||
/// all command expressions bound to it by
|
||||
/// all loaded input layers.
|
||||
pub BTreeMap<E, Vec<Binding<C>>>
|
||||
);
|
||||
|
||||
/// A sequence of zero or more commands (e.g. [AppCommand]),
|
||||
/// optionally filtered by [Condition] to form layers.
|
||||
///
|
||||
/// ```
|
||||
/// //FIXME: Why does it overflow?
|
||||
/// //let binding: Binding<()> = tek::Binding { ..Default::default() };
|
||||
/// ```
|
||||
#[derive(Debug, Clone)] pub struct Binding<C> {
|
||||
pub commands: Arc<[C]>,
|
||||
pub condition: Option<Condition>,
|
||||
pub description: Option<Arc<str>>,
|
||||
pub source: Option<Arc<PathBuf>>,
|
||||
}
|
||||
|
||||
/// Condition that must evaluate to true in order to enable an input layer.
|
||||
///
|
||||
/// ```
|
||||
/// let condition = tek::Condition(std::sync::Arc::new(Box::new(||{true})));
|
||||
/// ```
|
||||
#[derive(Clone)]
|
||||
pub struct Condition(
|
||||
pub Arc<Box<dyn Fn()->bool + Send + Sync>>
|
||||
);
|
||||
|
||||
tui_keys!(self: App, input {
|
||||
let commands = collect_commands(self, input)?;
|
||||
let results = execute_commands(self, commands)?;
|
||||
|
|
@ -888,24 +894,21 @@ mod bind {
|
|||
-> Usually<Vec<AppCommand>>
|
||||
{
|
||||
let mut commands = vec![];
|
||||
app.mode
|
||||
.as_ref()
|
||||
.and_then(|m|app.config.get_mode(m))
|
||||
.map(|mode|{
|
||||
for id in mode.keys.iter() {
|
||||
if let Some(event_map) = app.config.binds.clone().read().unwrap().get(id.as_ref())
|
||||
&& let Some(bindings) = event_map.query(input) {
|
||||
for binding in bindings {
|
||||
for command in binding.commands.iter() {
|
||||
if let Some(command) = app.namespace(command)? as Option<AppCommand> {
|
||||
commands.push(command)
|
||||
}
|
||||
app.mode.as_ref().and_then(|m|app.config.get_mode(m)).map(|mode|{
|
||||
for id in mode.keys.iter() {
|
||||
if let Some(event_map) = app.config.binds.clone().read().unwrap().get(id.as_ref())
|
||||
&& let Some(bindings) = event_map.query(input) {
|
||||
for binding in bindings {
|
||||
for command in binding.commands.iter() {
|
||||
if let Some(command) = app.namespace(command)? as Option<AppCommand> {
|
||||
commands.push(command)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok::<_, Box<dyn Error>>(())
|
||||
}).transpose()?;
|
||||
}
|
||||
Ok::<_, Box<dyn Error>>(())
|
||||
}).transpose()?;
|
||||
Ok(commands)
|
||||
}
|
||||
|
||||
|
|
@ -914,9 +917,16 @@ mod bind {
|
|||
{
|
||||
let mut history = vec![];
|
||||
for command in commands.into_iter() {
|
||||
let result = command.act(app);
|
||||
match result { Err(err) => { history.push((command, None)); return Err(err) }
|
||||
Ok(undo) => { history.push((command, undo)); } };
|
||||
let result = command.clone().act(app);
|
||||
match result {
|
||||
Err(err) => {
|
||||
history.push((command, None));
|
||||
return Err(err)
|
||||
},
|
||||
Ok(undo) => {
|
||||
history.push((command, undo));
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok(history)
|
||||
}
|
||||
|
|
@ -1002,45 +1012,62 @@ mod bind {
|
|||
|
||||
impl_debug!(Condition |self, w| { write!(w, "*") });
|
||||
|
||||
#[tek_proc::command(App)]
|
||||
#[tek_proc::keyword(App)]
|
||||
#[derive(Debug, Default)]
|
||||
pub enum AppCommand {
|
||||
#[default]
|
||||
#[command(App::nop)]
|
||||
#[keyword("nop")]
|
||||
Nop,
|
||||
|
||||
#[command(App::cancel)]
|
||||
#[keyword("cancel")]
|
||||
Cancel,
|
||||
|
||||
#[command(App::confirm)]
|
||||
#[keyword("confirm")]
|
||||
Confirm,
|
||||
|
||||
#[command(App::inc)]
|
||||
#[keyword("x/inc", ControlAxis::X)]
|
||||
#[keyword("y/inc", ControlAxis::Y)]
|
||||
Inc(ControlAxis),
|
||||
|
||||
#[command(App::dec)]
|
||||
#[keyword("x/dec", ControlAxis::X)]
|
||||
#[keyword("y/dec", ControlAxis::Y)]
|
||||
Dec(ControlAxis),
|
||||
|
||||
#[command(App::set_dialog | Self::SetDialog)]
|
||||
#[keyword("dialog")]
|
||||
SetDialog(Dialog),
|
||||
}
|
||||
|
||||
#[tek_proc::commands(AppCommand = "app")]
|
||||
impl App {
|
||||
fn nop (&mut self) -> Perhaps<AppCommand> {
|
||||
|
||||
#[command(Nop = "nop")]
|
||||
pub fn nop (&mut self) -> Perhaps<AppCommand> {
|
||||
Ok(None)
|
||||
}
|
||||
fn cancel (&mut self) -> Perhaps<AppCommand> {
|
||||
|
||||
#[command(Cancel = "cancel")]
|
||||
pub fn cancel (&mut self) -> Perhaps<AppCommand> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[command(Inc = "inc")]
|
||||
pub fn inc (&mut self, axis: ControlAxis) -> Perhaps<AppCommand> {
|
||||
Ok(match (&self.dialog, axis) {
|
||||
(Dialog::None, _) => todo!(),
|
||||
(Dialog::Menu(_, _), ControlAxis::Y) =>
|
||||
AppCommand::SetDialog { dialog: self.dialog.menu_next() }.act(self)?,
|
||||
_ => todo!()
|
||||
})
|
||||
}
|
||||
|
||||
#[command(Dec = "dec")]
|
||||
pub fn dec (&mut self, axis: ControlAxis) -> Perhaps<AppCommand> {
|
||||
Ok(match (&self.dialog, axis) {
|
||||
(Dialog::None, _) => None,
|
||||
(Dialog::Menu(_, _), ControlAxis::Y) =>
|
||||
AppCommand::SetDialog { dialog: self.dialog.menu_prev() }.act(self)?,
|
||||
_ => todo!()
|
||||
})
|
||||
}
|
||||
|
||||
#[command(Confirm = "confirm")]
|
||||
pub fn confirm (&mut self) -> Perhaps<AppCommand> {
|
||||
Ok(match &self.dialog {
|
||||
Dialog::Menu(index, items) => {
|
||||
let callback = items.0[*index].1.clone();
|
||||
callback(self)?;
|
||||
None
|
||||
},
|
||||
_ => todo!(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Swap currently active modal dialog.
|
||||
///
|
||||
/// ```
|
||||
/// let _ = tek::App::default().set_dialog(tek::Dialog::welcome());
|
||||
/// ```
|
||||
#[command(SetDialog = "dialog")]
|
||||
pub fn set_dialog (&mut self, dialog: Dialog) -> Perhaps<AppCommand> {
|
||||
let mut dialog = dialog.clone();
|
||||
std::mem::swap(&mut self.dialog, &mut dialog);
|
||||
Ok(Some(AppCommand::SetDialog { dialog }))
|
||||
}
|
||||
}
|
||||
|
||||
//impl<'a> Namespace<'a, AppCommand> for App {
|
||||
|
|
@ -1135,7 +1162,7 @@ mod device {
|
|||
editor.set_note_pos(pitch.as_int() as usize);
|
||||
}
|
||||
}
|
||||
let result = state.project.process_tracks(client, scope);
|
||||
let result = state.project.tracks_jack_process(client, scope);
|
||||
state.perf.update_from_jack_scope(t0, scope);
|
||||
result
|
||||
}
|
||||
|
|
@ -1279,46 +1306,6 @@ mod device {
|
|||
|
||||
#[cfg(feature = "plugin")] pub mod plugin;
|
||||
#[cfg(feature = "plugin")] pub use self::plugin::*;
|
||||
|
||||
pub struct Junction<T: JackPort>(T);
|
||||
|
||||
impl<T: JackPort> View<Tui> for Junction<T> {
|
||||
fn view (&self) -> impl Draw<Tui> {
|
||||
T::KIND
|
||||
}
|
||||
}
|
||||
|
||||
#[tek_proc::command(AudioInput)]
|
||||
#[tek_proc::keyword(AudioInput)]
|
||||
#[derive(Debug)]
|
||||
pub enum AudioInputCommand {
|
||||
Close,
|
||||
Connect(Arc<str>),
|
||||
}
|
||||
|
||||
#[tek_proc::command(AudioOutput)]
|
||||
#[tek_proc::keyword(AudioOutput)]
|
||||
#[derive(Debug)]
|
||||
pub enum AudioOutputCommand {
|
||||
Close,
|
||||
Connect(Arc<str>),
|
||||
}
|
||||
|
||||
#[tek_proc::command(MidiInput)]
|
||||
#[tek_proc::keyword(MidiInput)]
|
||||
#[derive(Debug)]
|
||||
pub enum MidiInputCommand {
|
||||
Close,
|
||||
Connect(Arc<str>),
|
||||
}
|
||||
|
||||
#[tek_proc::command(MidiOutput)]
|
||||
#[tek_proc::keyword(MidiOutput)]
|
||||
#[derive(Debug)]
|
||||
pub enum MidiOutputCommand {
|
||||
Close,
|
||||
Connect(Arc<str>),
|
||||
}
|
||||
}
|
||||
|
||||
//pub fn tui (
|
||||
|
|
@ -1374,57 +1361,61 @@ mod draw {
|
|||
fn view (&self) -> impl Draw<Tui> {
|
||||
self.perf.cycle(&mut |_|{
|
||||
draw(|to: &mut Tui|{
|
||||
let xywh = to.area().into();
|
||||
|
||||
if let Some(e) = self.error.read().unwrap().as_ref() {
|
||||
e.as_ref().align_c().draw(to)?;
|
||||
}
|
||||
|
||||
self.mode
|
||||
.as_ref()
|
||||
.and_then(|m|self.config.get_mode(m))
|
||||
.map(|mode|{
|
||||
|
||||
//south!(
|
||||
//format!("Mode: {:?}", self.mode.as_ref()),
|
||||
//format!("Time: {}", self.config.stamp.load(Relaxed)),
|
||||
//iter_south(||mode.view.iter().enumerate().map(|(index, line)|{
|
||||
//format!("View #{index} {line}")
|
||||
//}))
|
||||
//).align_nw().draw(to)?;
|
||||
|
||||
for (index, dsl) in mode.view.iter().enumerate() {
|
||||
match self.interpret(to, dsl) {
|
||||
Ok(Some(XYWH(.., w, h))) => {
|
||||
self.size.0.store(w as usize, Relaxed);
|
||||
self.size.1.store(h as usize, Relaxed);
|
||||
},
|
||||
Err(e) => {
|
||||
let src = &dsl.src().unwrap_or(Some("<source error>")).unwrap_or("<no source>");
|
||||
let message = format!("Mode: {:?}\n\nLayer: #{index}\n\nError: {e}\n\nSource:\n{}", &mode.name, &src);
|
||||
*self.error.write().unwrap() = Some(message.into());
|
||||
return Err(e);
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
*self.error.write().unwrap() = None;
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
east(
|
||||
format!("{}x{} ", self.size.0.load(Relaxed), self.size.1.load(Relaxed)),
|
||||
format!("{}% {} ", self.perf.percentage().unwrap_or_default(), self.perf.clock.raw() / 1000000000),
|
||||
).align_se().draw(to)?;
|
||||
|
||||
Ok(Some(xywh))
|
||||
self.draw_error(to)?;
|
||||
self.draw_mode(to)?;
|
||||
self.draw_debug(to)?;
|
||||
Ok(Some(to.area().into()))
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl App {
|
||||
fn draw_error (&self, to: &mut Tui) -> Usually<()> {
|
||||
if let Some(e) = self.error.read().unwrap().as_ref() {
|
||||
e.as_ref().align_c().draw(to)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn draw_mode (&self, to: &mut Tui) -> Usually<()> {
|
||||
self.mode.as_ref().and_then(|m|self.config.get_mode(m)).map(|mode|{
|
||||
//south!(
|
||||
//format!("Mode: {:?}", self.mode.as_ref()),
|
||||
//format!("Time: {}", self.config.stamp.load(Relaxed)),
|
||||
//iter_south(||mode.view.iter().enumerate().map(|(index, line)|{
|
||||
//format!("View #{index} {line}")
|
||||
//}))
|
||||
//).align_nw().draw(to)?;
|
||||
for (index, dsl) in mode.view.iter().enumerate() {
|
||||
match self.interpret(to, dsl) {
|
||||
Ok(Some(XYWH(.., w, h))) => {
|
||||
self.size.0.store(w as usize, Relaxed);
|
||||
self.size.1.store(h as usize, Relaxed);
|
||||
},
|
||||
Err(e) => {
|
||||
let src = &dsl.src().unwrap_or(Some("<source error>")).unwrap_or("<no source>");
|
||||
let message = format!("Mode: {:?}\n\nLayer: #{index}\n\nError: {e}\n\nSource:\n{}", &mode.name, &src);
|
||||
*self.error.write().unwrap() = Some(message.into());
|
||||
return Err(e);
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
*self.error.write().unwrap() = None;
|
||||
Ok(())
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn draw_debug (&self, to: &mut Tui) -> Drawn<u16> {
|
||||
east(
|
||||
format!("{}x{} ", self.size.0.load(Relaxed), self.size.1.load(Relaxed)),
|
||||
format!("{}% {} ", self.perf.percentage().unwrap_or_default(), self.perf.clock.raw() / 1000000000),
|
||||
).align_se().draw(to)
|
||||
}
|
||||
}
|
||||
|
||||
impl Interpret<Tui, Option<XYWH<u16>>> for App {
|
||||
fn interpret_expr <'a> (&'a self, to: &mut Tui, lang: &'a impl Expression) -> Drawn<u16> {
|
||||
self.keyword(to, lang)
|
||||
|
|
@ -1433,12 +1424,12 @@ mod draw {
|
|||
let mut frags = lang.src()?.unwrap().split("/");
|
||||
match frags.next() {
|
||||
//Some(":logo") => view_logo().draw(to),
|
||||
Some(":meters") => match frags.next() {
|
||||
Some(":meters") => match frags.next() {
|
||||
Some("input") => bg(Rgb(30, 30, 30), "Input Meters".align_s().full_h()).draw(to),
|
||||
Some("output") => bg(Rgb(30, 30, 30), "Output Meters".align_s().full_h()).draw(to),
|
||||
_ => panic!()
|
||||
},
|
||||
Some(":tracks") => match frags.next() {
|
||||
Some(":tracks") => match frags.next() {
|
||||
None => "TODO tracks".draw(to),
|
||||
Some("names") => self.project.view_track_names(self.color.clone()).draw(to),//bg(Rgb(40, 40, 40), full_w(align_w("Track Names")))),
|
||||
Some("inputs") => self.project.view_track_inputs(self.color.clone()).draw(to),//bg(Rgb(40, 40, 40), "Track Inputs".align_w().full_w()).draw(to),
|
||||
|
|
@ -1446,7 +1437,7 @@ mod draw {
|
|||
Some("outputs") => self.project.view_track_outputs(self.color.clone(), 0).draw(to),
|
||||
_ => panic!()
|
||||
},
|
||||
Some(":scenes") => match frags.next() {
|
||||
Some(":scenes") => match frags.next() {
|
||||
None => "TODO Scenes".draw(to),
|
||||
Some(":scenes/names") => "TODO Scene Names".draw(to),
|
||||
_ => panic!()
|
||||
|
|
@ -1506,27 +1497,6 @@ mod draw {
|
|||
}).min_w(30).exact_h(height)
|
||||
}
|
||||
|
||||
//pub fn per_track <'a, T: Draw<Tui> + 'a, U: TracksSizes<'a>> (
|
||||
//tracks: impl Fn() -> U + Send + Sync + 'a,
|
||||
//callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
|
||||
//) -> impl Draw<Tui> + 'a {
|
||||
//per_track_top(tracks, move|index, track|callback(index, track).full_h().align_y())
|
||||
//}
|
||||
|
||||
//pub fn per_track_top <'a, T: Draw<Tui> + 'a, U: TracksSizes<'a>> (
|
||||
//tracks: impl Fn() -> U + Send + Sync + 'a,
|
||||
//callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
|
||||
//) -> impl Draw<Tui> + 'a {
|
||||
//bg(Reset, iter_east(||tracks()
|
||||
//.map(move|(index, track, x1, x2): (usize, &'a Track, usize, usize)|{
|
||||
//fg_bg(
|
||||
//track.color.lightest.term,
|
||||
//track.color.base.term,
|
||||
//callback(index, track)
|
||||
//).exact_w((x2 - x1) as u16)
|
||||
//})).align_x())
|
||||
//}
|
||||
|
||||
pub fn field_h <T: Screen> (
|
||||
_theme: ItemTheme, _head: impl Draw<T>, _body: impl Draw<T>
|
||||
) -> impl Draw<T> {
|
||||
|
|
@ -1552,17 +1522,6 @@ mod draw {
|
|||
}).min_w(w).exact_h(h)
|
||||
}
|
||||
|
||||
pub fn view_browse_title (state: &App) -> impl Draw<Tui> {
|
||||
field_v(ItemTheme::default(), match state.dialog.browser_target().unwrap() {
|
||||
BrowseTarget::SaveProject => "Save project:",
|
||||
BrowseTarget::LoadProject => "Load project:",
|
||||
BrowseTarget::ImportSample(_) => "Import sample:",
|
||||
BrowseTarget::ExportSample(_) => "Export sample:",
|
||||
BrowseTarget::ImportClip(_) => "Import clip:",
|
||||
BrowseTarget::ExportClip(_) => "Export clip:",
|
||||
}, fg(g(96), x_repeat("🭻")).exact_h(1)).align_w().full_w()
|
||||
}
|
||||
|
||||
pub fn view_device (state: &App) -> impl Draw<Tui> {
|
||||
let selected = state.dialog.device_kind().unwrap();
|
||||
south(
|
||||
|
|
@ -1629,21 +1588,6 @@ mod draw {
|
|||
)
|
||||
}
|
||||
|
||||
#[cfg(feature = "track")] pub fn view_track_row_section (
|
||||
_theme: ItemTheme,
|
||||
button: impl Draw<Tui>,
|
||||
button_add: impl Draw<Tui>,
|
||||
content: impl Draw<Tui>,
|
||||
) -> impl Draw<Tui> {
|
||||
west(
|
||||
button_add.align_nw().exact_w(4).full_h(),
|
||||
east(
|
||||
button.align_nw().full_h().exact_w(20),
|
||||
content.align_c().full_wh()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/// ```
|
||||
/// let bg = tengri::ratatui::style::Color::Red;
|
||||
/// let fg = tengri::ratatui::style::Color::Green;
|
||||
|
|
@ -1687,211 +1631,10 @@ mod draw {
|
|||
south(left, right)
|
||||
}
|
||||
|
||||
pub fn view_sample_info (sample: Option<&Arc<RwLock<Sample>>>) -> impl Draw<Tui> + use<'_> {
|
||||
when(sample.is_some(), draw(move|to: &mut Tui|{
|
||||
let sample = sample.unwrap().read().unwrap();
|
||||
let theme = sample.color;
|
||||
east!(
|
||||
field_h(theme, "Name", format!("{:<10}", sample.name.clone())),
|
||||
field_h(theme, "Length", format!("{:<8}", sample.channels[0].len())),
|
||||
field_h(theme, "Start", format!("{:<8}", sample.start)),
|
||||
field_h(theme, "End", format!("{:<8}", sample.end)),
|
||||
field_h(theme, "Trans", "0"),
|
||||
field_h(theme, "Gain", format!("{}", sample.gain)),
|
||||
).draw(to)
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn view_sample_info_v (sample: Option<&Arc<RwLock<Sample>>>) -> impl Draw<Tui> + use<'_> {
|
||||
let a = draw(move|to: &mut Tui|{
|
||||
let sample = sample.unwrap().read().unwrap();
|
||||
let theme = sample.color;
|
||||
south!(
|
||||
field_h(theme, "Name ", format!("{:<10}", sample.name.clone())) .align_w().full_w(),
|
||||
field_h(theme, "Length", format!("{:<8}", sample.channels[0].len())).align_w().full_w(),
|
||||
field_h(theme, "Start ", format!("{:<8}", sample.start)) .align_w().full_w(),
|
||||
field_h(theme, "End ", format!("{:<8}", sample.end)) .align_w().full_w(),
|
||||
field_h(theme, "Trans ", "0") .align_w().full_w(),
|
||||
field_h(theme, "Gain ", format!("{}", sample.gain)) .align_w().full_w(),
|
||||
).exact_w(20).draw(to)
|
||||
});
|
||||
|
||||
let b = draw(|to: &mut Tui|fg(Red, south!(
|
||||
bold(true, "× No sample."),
|
||||
"[r] record",
|
||||
"[Shift-F9] import",
|
||||
)).draw(to));
|
||||
|
||||
either(sample.is_some(), a, b)
|
||||
}
|
||||
|
||||
pub fn view_sample_status (sample: Option<&Arc<RwLock<Sample>>>) -> impl Draw<Tui> {
|
||||
bold(true, fg(g(224), sample
|
||||
.map(|sample|{
|
||||
let sample = sample.read().unwrap();
|
||||
format!("Sample {}-{}", sample.start, sample.end)
|
||||
})
|
||||
.unwrap_or_else(||"No sample".to_string())))
|
||||
}
|
||||
|
||||
pub fn view_track_header (theme: ItemTheme, content: impl Draw<Tui>) -> impl Draw<Tui> {
|
||||
pub fn view_track_header (theme: ItemTheme, content: impl Draw<Tui>) -> impl Draw<Tui> {
|
||||
bg(theme.darker.term, content.align_e().full_w()).exact_w(12)
|
||||
}
|
||||
|
||||
pub fn view_ports_status <'a, T: JackPort> (theme: ItemTheme, title: &'a str, ports: &'a [T])
|
||||
-> impl Draw<Tui> + use<'a, T>
|
||||
{
|
||||
let ins = ports.len() as u16;
|
||||
let frame = Outer(true, Style::default().fg(g(96)));
|
||||
let names = iter_south(move||ports.iter().enumerate().map(|(index, port)|format!(
|
||||
" {index} {}", port.port_name()
|
||||
).align_w().full_h()));
|
||||
let field = field_v(theme, title, names);
|
||||
border(true, frame, field.exact_wh(20, 1 + ins)).exact_wh(20, 1 + ins)
|
||||
}
|
||||
|
||||
pub fn view_io_ports <'a, T: PortsSizes<'a>> (
|
||||
fg: Color, bg: Color, items: impl Fn()->T + Send + Sync + 'a
|
||||
) -> impl Draw<Tui> + 'a {
|
||||
type Item<'a> = (usize, &'a Arc<str>, &'a [Connect], usize, usize);
|
||||
iter(items,
|
||||
move|(_index, name, connections, y, y2): Item<'a>, _| south(
|
||||
bold(true, fg_bg(fg, bg, east(" ", name).align_w())).full_h(),
|
||||
iter(||connections.iter(), move|connect: &'a Connect, index|{
|
||||
bold(false, fg_bg(fg, bg, &connect.info)).exact_h(1).align_w().push_y(index as u16)
|
||||
})
|
||||
).exact_h((y2 - y) as u16).push_y(y as u16))
|
||||
}
|
||||
|
||||
pub fn view_scenes_clips <'a, S: ScenesSizes<'a>> (
|
||||
scenes: impl Fn()->S,
|
||||
tracks: impl TracksSizes<'a>,
|
||||
select: &Selection,
|
||||
editor: Option<&MidiEditor>,
|
||||
size: &Sizer,
|
||||
editing: bool,
|
||||
) -> impl Draw<Tui> {
|
||||
let status = fg(Green, format!("{}x{}", size.w(), size.h())).align_se().full_wh();
|
||||
let tracks = iter_once(tracks, move|(track_index, track, _, _), _| {
|
||||
let scenes = iter_once(scenes(), move|(scene_index, scene, _, _), _| {
|
||||
let (name, theme): (Arc<str>, ItemTheme) = scene_name_theme(scene, track_index);
|
||||
let f = theme.lightest.term;
|
||||
let (b, o) = scene_bg(theme, select, track_index, scene_index);
|
||||
let w = scene_w(track, select, track_index, editor);
|
||||
let y = scene_y(select, scene_index, editor);
|
||||
let is_selected = scene_sel(select, track_index, scene_index, editing);
|
||||
below(
|
||||
Outer(true, Style::default().fg(o)).full_wh(),
|
||||
below(
|
||||
below(
|
||||
fg_bg(o, b, "".full_wh()),
|
||||
fg_bg(f, b, bold(true, name)).align_nw().full_wh(),
|
||||
),
|
||||
when(is_selected, editor.map(|e|e.view())).full_wh()
|
||||
).full_wh()
|
||||
).exact_wh(w, y)
|
||||
});
|
||||
scenes.full_h().exact_w(track.width as u16)
|
||||
});
|
||||
|
||||
return size.of(above(status, tracks).full_wh());
|
||||
|
||||
fn scene_name_theme (scene: &Scene, track_index: usize) -> (Arc<str>, ItemTheme) {
|
||||
if let Some(Some(clip)) = &scene.clips.get(track_index) {
|
||||
let clip = clip.read().unwrap();
|
||||
(format!(" ⏹ {}", &clip.name).into(), clip.color)
|
||||
} else {
|
||||
(" ⏹ -- ".into(), ItemTheme::G[32])
|
||||
}
|
||||
}
|
||||
|
||||
fn scene_bg (
|
||||
theme: ItemTheme, select: &Selection, track_index: usize, scene_index: usize
|
||||
) -> (Color, Color) {
|
||||
let mut outline = theme.base.term;
|
||||
(if select.track() == Some(track_index) && select.scene() == Some(scene_index) {
|
||||
outline = theme.lighter.term;
|
||||
theme.light.term
|
||||
} else if select.track() == Some(track_index) || select.scene() == Some(scene_index) {
|
||||
outline = theme.darkest.term;
|
||||
theme.base.term
|
||||
} else {
|
||||
theme.dark.term
|
||||
}, outline)
|
||||
}
|
||||
|
||||
fn scene_w (
|
||||
track: &Track, select: &Selection, track_index: usize, editor: Option<&MidiEditor>
|
||||
) -> u16 {
|
||||
if select.track() == Some(track_index) && let Some(editor) = editor {
|
||||
(editor.size.w() as usize).max(24).max(track.width) as u16
|
||||
} else {
|
||||
track.width as u16
|
||||
}
|
||||
}
|
||||
|
||||
fn scene_y (
|
||||
select: &Selection, scene_index: usize, editor: Option<&MidiEditor>
|
||||
) -> u16 {
|
||||
if select.scene() == Some(scene_index) && let Some(editor) = editor {
|
||||
editor.size.h().max(12)
|
||||
} else {
|
||||
H_SCENE as u16
|
||||
}
|
||||
}
|
||||
|
||||
fn scene_sel (select: &Selection, track_index: usize, scene_index: usize, editing: bool) -> bool {
|
||||
editing && select.track() == Some(track_index) && select.scene() == Some(scene_index)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn view_scene_name (
|
||||
select: &Selection,
|
||||
editor: Option<&MidiEditor>,
|
||||
index: usize,
|
||||
scene: &Scene,
|
||||
editing: bool
|
||||
) -> impl Draw<Tui> {
|
||||
let h = if select.scene() == Some(index) && let Some(_editor) = editor {
|
||||
7
|
||||
} else {
|
||||
H_SCENE as u16
|
||||
};
|
||||
let a = east(format!("·s{index:02} "),
|
||||
fg(g(255), bold(true, &scene.name))).align_w().full_w();
|
||||
let b = when(select.scene() == Some(index) && editing, south(
|
||||
editor.as_ref().map(|e|e.clip_status()),
|
||||
editor.as_ref().map(|e|e.edit_status())).align_nw().full_wh());
|
||||
let c = if select.scene() == Some(index) {
|
||||
scene.color.light.term
|
||||
} else {
|
||||
scene.color.base.term
|
||||
};
|
||||
bg(c, south(a, b).align_nw()).exact_wh(20, h)
|
||||
}
|
||||
|
||||
pub fn view_midi_ins_status (theme: ItemTheme, track: Option<&Track>) -> impl Draw<Tui> {
|
||||
track.map(move|track|view_ports_status(theme, "MIDI ins: ", &track.sequencer.midi_ins))
|
||||
}
|
||||
|
||||
pub fn view_midi_outs_status (theme: ItemTheme, track: Option<&Track>) -> impl Draw<Tui> {
|
||||
track.map(move|track|view_ports_status(theme, "MIDI outs: ", &track.sequencer.midi_outs))
|
||||
}
|
||||
|
||||
pub fn view_audio_ins_status (theme: ItemTheme, track: Option<&Track>) -> impl Draw<Tui> {
|
||||
track.map(move|track|view_ports_status(theme, "Audio ins: ", &track.audio_ins()))
|
||||
}
|
||||
|
||||
pub fn view_audio_outs_status (theme: ItemTheme, track: Option<&Track>) -> impl Draw<Tui> {
|
||||
track.map(move|track|view_ports_status(theme, "Audio outs:", &track.audio_outs()))
|
||||
}
|
||||
|
||||
//pub fn view_track_per <'a, T: Draw<Tui> + 'a, U: TracksSizes<'a>> (
|
||||
//tracks: impl Fn() -> U + Send + Sync + 'a,
|
||||
//callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
|
||||
//) -> impl Draw<Tui> {
|
||||
//}
|
||||
|
||||
/// ```
|
||||
/// let _ = tek::button_2("", "", true);
|
||||
/// let _ = tek::button_2("", "", false);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue