mirror of
https://codeberg.org/unspeaker/tek.git
synced 2026-07-17 07:46:57 +02:00
restruct: 45e
This commit is contained in:
parent
b3e9cde8a1
commit
027f69cd50
9 changed files with 264 additions and 270 deletions
|
|
@ -3,6 +3,7 @@ pub mod bind; pub use self::bind::*;
|
||||||
pub mod cli; pub use self::cli::*;
|
pub mod cli; pub use self::cli::*;
|
||||||
pub mod config; pub use self::config::*;
|
pub mod config; pub use self::config::*;
|
||||||
pub mod view; pub use self::view::*;
|
pub mod view; pub use self::view::*;
|
||||||
|
pub mod mode; pub use self::mode::*;
|
||||||
|
|
||||||
/// Total application state.
|
/// Total application state.
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,13 @@ impl Config {
|
||||||
const CONFIG_SUB: &'static str = "v0";
|
const CONFIG_SUB: &'static str = "v0";
|
||||||
const CONFIG: &'static str = "tek.edn";
|
const CONFIG: &'static str = "tek.edn";
|
||||||
const DEFAULTS: &'static str = include_str!("../tek.edn");
|
const DEFAULTS: &'static str = include_str!("../tek.edn");
|
||||||
|
|
||||||
|
pub fn watch <T> (callback: impl Fn(Self)->T) -> Usually<T> {
|
||||||
|
let config = Self::init_new(None)?;
|
||||||
|
let result = callback(config);
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn init_new (dirs: Option<BaseDirectories>) -> Usually<Self> {
|
pub fn init_new (dirs: Option<BaseDirectories>) -> Usually<Self> {
|
||||||
let mut config = Self::new(None);
|
let mut config = Self::new(None);
|
||||||
config.init()?;
|
config.init()?;
|
||||||
|
|
@ -98,95 +105,3 @@ impl Config {
|
||||||
self.modes.clone().read().unwrap().get(mode.as_ref()).cloned()
|
self.modes.clone().read().unwrap().get(mode.as_ref()).cloned()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn load_mode (modes: &Modes, name: &impl AsRef<str>, body: &impl Language) -> Usually<()> {
|
|
||||||
let mut mode = Mode::default();
|
|
||||||
body.each(|item|mode.add(item))?;
|
|
||||||
modes.write().unwrap().insert(name.as_ref().into(), Arc::new(mode));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Collection of interaction modes.
|
|
||||||
pub type Modes = Arc<RwLock<BTreeMap<Arc<str>, Arc<Mode<Arc<str>>>>>>;
|
|
||||||
|
|
||||||
impl Mode<Arc<str>> {
|
|
||||||
/// Add a definition to the mode.
|
|
||||||
///
|
|
||||||
/// Supported definitions:
|
|
||||||
///
|
|
||||||
/// - (name ...) -> name
|
|
||||||
/// - (info ...) -> description
|
|
||||||
/// - (keys ...) -> key bindings
|
|
||||||
/// - (mode ...) -> submode
|
|
||||||
/// - ... -> view
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// let mut mode: tek::Mode<std::sync::Arc<str>> = Default::default();
|
|
||||||
/// mode.add("(name hello)").unwrap();
|
|
||||||
/// ```
|
|
||||||
pub fn add (&mut self, dsl: impl Language) -> Usually<()> {
|
|
||||||
Ok(if let Ok(Some(expr)) = dsl.expr() && let Ok(Some(head)) = expr.head() {
|
|
||||||
//println!("Mode::add: {head} {:?}", expr.tail());
|
|
||||||
let tail = expr.tail()?.map(|x|x.trim()).unwrap_or("");
|
|
||||||
match head {
|
|
||||||
"name" => self.add_name(tail)?,
|
|
||||||
"info" => self.add_info(tail)?,
|
|
||||||
"keys" => self.add_keys(tail)?,
|
|
||||||
"mode" => self.add_mode(tail)?,
|
|
||||||
_ => self.add_view(tail)?,
|
|
||||||
};
|
|
||||||
} else if let Ok(Some(word)) = dsl.word() {
|
|
||||||
self.add_view(word);
|
|
||||||
} else {
|
|
||||||
return Err(format!("Mode::add: unexpected: {dsl:?}").into());
|
|
||||||
})
|
|
||||||
|
|
||||||
//DslParse(dsl, ||Err(format!("Mode::add: unexpected: {dsl:?}").into()))
|
|
||||||
//.word(|word|self.add_view(word))
|
|
||||||
//.expr(|expr|expr.head(|head|{
|
|
||||||
////println!("Mode::add: {head} {:?}", expr.tail());
|
|
||||||
//let tail = expr.tail()?.map(|x|x.trim()).unwrap_or("");
|
|
||||||
//match head {
|
|
||||||
//"name" => self.add_name(tail),
|
|
||||||
//"info" => self.add_info(tail),
|
|
||||||
//"keys" => self.add_keys(tail)?,
|
|
||||||
//"mode" => self.add_mode(tail)?,
|
|
||||||
//_ => self.add_view(tail),
|
|
||||||
//};
|
|
||||||
//}))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_name (&mut self, dsl: impl Language) -> Perhaps<()> {
|
|
||||||
Ok(dsl.src()?.map(|src|self.name.push(src.into())))
|
|
||||||
}
|
|
||||||
fn add_info (&mut self, dsl: impl Language) -> Perhaps<()> {
|
|
||||||
Ok(dsl.src()?.map(|src|self.info.push(src.into())))
|
|
||||||
}
|
|
||||||
fn add_view (&mut self, dsl: impl Language) -> Perhaps<()> {
|
|
||||||
Ok(dsl.src()?.map(|src|self.view.push(src.into())))
|
|
||||||
}
|
|
||||||
fn add_keys (&mut self, dsl: impl Language) -> Perhaps<()> {
|
|
||||||
Ok(Some(dsl.each(|expr|{ self.keys.push(expr.trim().into()); Ok(()) })?))
|
|
||||||
}
|
|
||||||
fn add_mode (&mut self, dsl: impl Language) -> Perhaps<()> {
|
|
||||||
Ok(Some(if let Some(id) = dsl.head()? {
|
|
||||||
load_mode(&self.modes, &id, &dsl.tail())?;
|
|
||||||
} else {
|
|
||||||
return Err(format!("Mode::add: self: incomplete: {dsl:?}").into());
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Group of view and keys definitions.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// let mode = tek::Mode::<std::sync::Arc<str>>::default();
|
|
||||||
/// ```
|
|
||||||
#[derive(Default, Debug)] pub struct Mode<D: Language + Ord> {
|
|
||||||
pub path: PathBuf,
|
|
||||||
pub name: Vec<D>,
|
|
||||||
pub info: Vec<D>,
|
|
||||||
pub view: Vec<D>,
|
|
||||||
pub keys: Vec<D>,
|
|
||||||
pub modes: Modes,
|
|
||||||
}
|
|
||||||
|
|
|
||||||
93
src/app/mode.rs
Normal file
93
src/app/mode.rs
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
pub(crate) fn load_mode (modes: &Modes, name: &impl AsRef<str>, body: &impl Language) -> Usually<()> {
|
||||||
|
let mut mode = Mode::default();
|
||||||
|
body.each(|item|mode.add(item))?;
|
||||||
|
modes.write().unwrap().insert(name.as_ref().into(), Arc::new(mode));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Collection of interaction modes.
|
||||||
|
pub type Modes = Arc<RwLock<BTreeMap<Arc<str>, Arc<Mode<Arc<str>>>>>>;
|
||||||
|
|
||||||
|
impl Mode<Arc<str>> {
|
||||||
|
/// Add a definition to the mode.
|
||||||
|
///
|
||||||
|
/// Supported definitions:
|
||||||
|
///
|
||||||
|
/// - (name ...) -> name
|
||||||
|
/// - (info ...) -> description
|
||||||
|
/// - (keys ...) -> key bindings
|
||||||
|
/// - (mode ...) -> submode
|
||||||
|
/// - ... -> view
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let mut mode: tek::Mode<std::sync::Arc<str>> = Default::default();
|
||||||
|
/// mode.add("(name hello)").unwrap();
|
||||||
|
/// ```
|
||||||
|
pub fn add (&mut self, dsl: impl Language) -> Usually<()> {
|
||||||
|
Ok(if let Ok(Some(expr)) = dsl.expr() && let Ok(Some(head)) = expr.head() {
|
||||||
|
//println!("Mode::add: {head} {:?}", expr.tail());
|
||||||
|
let tail = expr.tail()?.map(|x|x.trim()).unwrap_or("");
|
||||||
|
match head {
|
||||||
|
"name" => self.add_name(tail)?,
|
||||||
|
"info" => self.add_info(tail)?,
|
||||||
|
"keys" => self.add_keys(tail)?,
|
||||||
|
"mode" => self.add_mode(tail)?,
|
||||||
|
_ => self.add_view(tail)?,
|
||||||
|
};
|
||||||
|
} else if let Ok(Some(word)) = dsl.word() {
|
||||||
|
self.add_view(word);
|
||||||
|
} else {
|
||||||
|
return Err(format!("Mode::add: unexpected: {dsl:?}").into());
|
||||||
|
})
|
||||||
|
|
||||||
|
//DslParse(dsl, ||Err(format!("Mode::add: unexpected: {dsl:?}").into()))
|
||||||
|
//.word(|word|self.add_view(word))
|
||||||
|
//.expr(|expr|expr.head(|head|{
|
||||||
|
////println!("Mode::add: {head} {:?}", expr.tail());
|
||||||
|
//let tail = expr.tail()?.map(|x|x.trim()).unwrap_or("");
|
||||||
|
//match head {
|
||||||
|
//"name" => self.add_name(tail),
|
||||||
|
//"info" => self.add_info(tail),
|
||||||
|
//"keys" => self.add_keys(tail)?,
|
||||||
|
//"mode" => self.add_mode(tail)?,
|
||||||
|
//_ => self.add_view(tail),
|
||||||
|
//};
|
||||||
|
//}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_name (&mut self, dsl: impl Language) -> Perhaps<()> {
|
||||||
|
Ok(dsl.src()?.map(|src|self.name.push(src.into())))
|
||||||
|
}
|
||||||
|
fn add_info (&mut self, dsl: impl Language) -> Perhaps<()> {
|
||||||
|
Ok(dsl.src()?.map(|src|self.info.push(src.into())))
|
||||||
|
}
|
||||||
|
fn add_view (&mut self, dsl: impl Language) -> Perhaps<()> {
|
||||||
|
Ok(dsl.src()?.map(|src|self.view.push(src.into())))
|
||||||
|
}
|
||||||
|
fn add_keys (&mut self, dsl: impl Language) -> Perhaps<()> {
|
||||||
|
Ok(Some(dsl.each(|expr|{ self.keys.push(expr.trim().into()); Ok(()) })?))
|
||||||
|
}
|
||||||
|
fn add_mode (&mut self, dsl: impl Language) -> Perhaps<()> {
|
||||||
|
Ok(Some(if let Some(id) = dsl.head()? {
|
||||||
|
load_mode(&self.modes, &id, &dsl.tail())?;
|
||||||
|
} else {
|
||||||
|
return Err(format!("Mode::add: self: incomplete: {dsl:?}").into());
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Group of view and keys definitions.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let mode = tek::Mode::<std::sync::Arc<str>>::default();
|
||||||
|
/// ```
|
||||||
|
#[derive(Default, Debug)] pub struct Mode<D: Language + Ord> {
|
||||||
|
pub path: PathBuf,
|
||||||
|
pub name: Vec<D>,
|
||||||
|
pub info: Vec<D>,
|
||||||
|
pub view: Vec<D>,
|
||||||
|
pub keys: Vec<D>,
|
||||||
|
pub modes: Modes,
|
||||||
|
}
|
||||||
178
src/app/view.rs
178
src/app/view.rs
|
|
@ -30,7 +30,7 @@ pub fn view_logo () -> impl Draw<Tui> {
|
||||||
pub fn view_transport (play: bool, bpm: &str, beat: &str, time: &str) -> impl Draw<Tui> {
|
pub fn view_transport (play: bool, bpm: &str, beat: &str, time: &str) -> impl Draw<Tui> {
|
||||||
let theme = ItemTheme::G[96];
|
let theme = ItemTheme::G[96];
|
||||||
bg(Black, east!(above(
|
bg(Black, east!(above(
|
||||||
wh_full(origin_w(button_play_pause(play))),
|
wh_full(origin_w(button_play_pause(play, false))),
|
||||||
wh_full(origin_e(east!(
|
wh_full(origin_e(east!(
|
||||||
field_h(theme, "BPM", bpm),
|
field_h(theme, "BPM", bpm),
|
||||||
field_h(theme, "Beat", beat),
|
field_h(theme, "Beat", beat),
|
||||||
|
|
@ -56,10 +56,12 @@ pub fn view_status (sel: Option<&str>, sr: &str, buf: &str, lat: &str) -> impl D
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ```
|
/// ```
|
||||||
/// let _ = tek::button_play_pause(true);
|
/// let _ = tek::button_play_pause(true, true);
|
||||||
|
/// let _ = tek::button_play_pause(true, false);
|
||||||
|
/// let _ = tek::button_play_pause(false, true);
|
||||||
|
/// let _ = tek::button_play_pause(false, false);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn button_play_pause (playing: bool) -> impl Draw<Tui> {
|
pub fn button_play_pause (playing: bool, compact: bool) -> impl Draw<Tui> {
|
||||||
let compact = true;//self.is_editing();
|
|
||||||
bg(if playing { Rgb(0, 128, 0) } else { Rgb(128, 64, 0) },
|
bg(if playing { Rgb(0, 128, 0) } else { Rgb(128, 64, 0) },
|
||||||
either(compact,
|
either(compact,
|
||||||
thunk(move|to: &mut Tui|w_exact(9, either(playing,
|
thunk(move|to: &mut Tui|w_exact(9, either(playing,
|
||||||
|
|
@ -202,87 +204,84 @@ pub fn view_io_ports <'a, T: PortsSizes<'a>> (
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn view_scenes_clips <'a> (
|
pub fn view_scenes_clips <'a, S: ScenesSizes<'a>> (
|
||||||
scenes: impl ScenesSizes<'a>,
|
scenes: impl Fn()->S,
|
||||||
tracks: impl TracksSizes<'a>,
|
tracks: impl TracksSizes<'a>,
|
||||||
select: &Selection,
|
select: &Selection,
|
||||||
editor: Option<&MidiEditor>,
|
editor: Option<&MidiEditor>,
|
||||||
size: &Size,
|
size: &Size,
|
||||||
is_editing: bool,
|
editing: bool,
|
||||||
) -> impl Draw<Tui> {
|
) -> impl Draw<Tui> {
|
||||||
size.of(wh_full(above(wh_full(origin_se(fg(Green, format!("{}x{}", size.w(), size.h())))),
|
let status = wh_full(origin_se(fg(Green, format!("{}x{}", size.w(), size.h()))));
|
||||||
thunk(move |to: &mut Tui|{
|
let tracks = iter_once(tracks, move|(track_index, track, _, _), _| {
|
||||||
for (index, track, _, _) in tracks {
|
let scenes = iter_once(scenes(), move|(scene_index, scene, _, _), _| {
|
||||||
let clips = view_track_clips(&scenes, select, editor, index, track, is_editing);
|
let (name, theme): (Arc<str>, ItemTheme) = scene_name_theme(scene, track_index);
|
||||||
w_exact(track.width as u16, h_full(clips)).draw(to);
|
let f = theme.lightest.term;
|
||||||
}
|
let (b, o) = scene_bg(theme, select, track_index, scene_index);
|
||||||
Ok(XYWH(0, 0, 0, 0))
|
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);
|
||||||
|
wh_exact(Some(w), Some(y), below(
|
||||||
|
wh_full(Outer(true, Style::default().fg(o))),
|
||||||
|
wh_full(below(
|
||||||
|
below(
|
||||||
|
fg_bg(o, b, wh_full("")),
|
||||||
|
wh_full(origin_nw(fg_bg(f, b, bold(true, name)))),
|
||||||
|
),
|
||||||
|
wh_full(when(is_selected, editor.map(|e|e.view())))))))
|
||||||
|
});
|
||||||
|
w_exact(track.width as u16, h_full(scenes))
|
||||||
|
});
|
||||||
|
|
||||||
pub fn view_track_clips <'a> (
|
return size.of(wh_full(above(status, tracks)));
|
||||||
scenes: &impl ScenesSizes<'a>,
|
|
||||||
select: &Selection,
|
|
||||||
editor: Option<&MidiEditor>,
|
|
||||||
index: usize,
|
|
||||||
track: &Track,
|
|
||||||
is_editing: bool,
|
|
||||||
) -> impl Draw<Tui> {
|
|
||||||
thunk(move|to: &mut Tui|{
|
|
||||||
|
|
||||||
for (scene_index, scene, ..) in scenes {
|
fn scene_name_theme (scene: &Scene, track_index: usize) -> (Arc<str>, ItemTheme) {
|
||||||
|
if let Some(Some(clip)) = &scene.clips.get(track_index) {
|
||||||
let (
|
|
||||||
name, theme
|
|
||||||
): (Arc<str>, ItemTheme) = if let Some(Some(clip)) = &scene.clips.get(index) {
|
|
||||||
let clip = clip.read().unwrap();
|
let clip = clip.read().unwrap();
|
||||||
(format!(" ⏹ {}", &clip.name).into(), clip.color)
|
(format!(" ⏹ {}", &clip.name).into(), clip.color)
|
||||||
} else {
|
} else {
|
||||||
(" ⏹ -- ".into(), ItemTheme::G[32])
|
(" ⏹ -- ".into(), ItemTheme::G[32])
|
||||||
};
|
}
|
||||||
|
}
|
||||||
let fg = theme.lightest.term;
|
|
||||||
|
|
||||||
|
fn scene_bg (
|
||||||
|
theme: ItemTheme, select: &Selection, track_index: usize, scene_index: usize
|
||||||
|
) -> (Color, Color) {
|
||||||
let mut outline = theme.base.term;
|
let mut outline = theme.base.term;
|
||||||
|
(if select.track() == Some(track_index) && select.scene() == Some(scene_index) {
|
||||||
let bg = if select.track() == Some(index) && select.scene() == Some(scene_index) {
|
|
||||||
outline = theme.lighter.term;
|
outline = theme.lighter.term;
|
||||||
theme.light.term
|
theme.light.term
|
||||||
} else if select.track() == Some(index) || select.scene() == Some(scene_index) {
|
} else if select.track() == Some(track_index) || select.scene() == Some(scene_index) {
|
||||||
outline = theme.darkest.term;
|
outline = theme.darkest.term;
|
||||||
theme.base.term
|
theme.base.term
|
||||||
} else {
|
} else {
|
||||||
theme.dark.term
|
theme.dark.term
|
||||||
};
|
}, outline)
|
||||||
|
|
||||||
let w = if select.track() == Some(index) && let Some(editor) = editor {
|
|
||||||
(editor.size.w() as usize).max(24).max(track.width)
|
|
||||||
} else {
|
|
||||||
track.width
|
|
||||||
} as u16;
|
|
||||||
|
|
||||||
let y = if select.scene() == Some(scene_index) && let Some(editor) = editor {
|
|
||||||
(editor.size.h() as usize).max(12)
|
|
||||||
} else {
|
|
||||||
Self::H_SCENE as usize
|
|
||||||
} as u16;
|
|
||||||
|
|
||||||
let is_selected = is_editing && select.track() == Some(index) && select.scene() == Some(scene_index);
|
|
||||||
|
|
||||||
wh_exact(Some(w), Some(y), below(
|
|
||||||
wh_full(Outer(true, Style::default().fg(outline))),
|
|
||||||
wh_full(below(
|
|
||||||
below(
|
|
||||||
fg_bg(outline, bg, wh_full("")),
|
|
||||||
wh_full(origin_nw(fg_bg(fg, bg, bold(true, &name)))),
|
|
||||||
),
|
|
||||||
wh_full(when(is_selected, editor.map(|e|e.view()))))))
|
|
||||||
).draw(to);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(XYWH(0, 0, 0, 0))
|
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_track_names (
|
pub fn view_track_names (
|
||||||
|
|
@ -318,9 +317,7 @@ pub fn view_track_names (
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn view_track_outputs (
|
pub fn view_track_outputs (
|
||||||
theme: ItemTheme,
|
theme: ItemTheme, tracks: impl TracksSizes<'_>, midi_outs: impl Iterator<Item = &MidiOutput>,
|
||||||
tracks: impl TracksSizes<'_>,
|
|
||||||
midi_outs: impl Iterator<Item = &MidiOutput>,
|
|
||||||
) -> impl Draw<Tui> {
|
) -> impl Draw<Tui> {
|
||||||
view_track_row_section(theme,
|
view_track_row_section(theme,
|
||||||
south(w_full(origin_w(button_2("o", "utput", false))),
|
south(w_full(origin_w(button_2("o", "utput", false))),
|
||||||
|
|
@ -345,9 +342,7 @@ pub fn view_track_outputs (
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn view_track_inputs (
|
pub fn view_track_inputs (
|
||||||
theme: ItemTheme,
|
theme: ItemTheme, tracks: impl TracksSizes<'_>, height: u16,
|
||||||
tracks: impl TracksSizes<'_>,
|
|
||||||
height: u16,
|
|
||||||
) -> impl Draw<Tui> {
|
) -> impl Draw<Tui> {
|
||||||
view_track_row_section(theme, button_2("i", "nput", false), button_2("I", "+", false),
|
view_track_row_section(theme, button_2("i", "nput", false), button_2("I", "+", false),
|
||||||
bg(theme.darker.term, origin_w(thunk(move|to: &mut Tui|{
|
bg(theme.darker.term, origin_w(thunk(move|to: &mut Tui|{
|
||||||
|
|
@ -393,7 +388,7 @@ pub fn view_scene_name (
|
||||||
let h = if select.scene() == Some(index) && let Some(_editor) = editor {
|
let h = if select.scene() == Some(index) && let Some(_editor) = editor {
|
||||||
7
|
7
|
||||||
} else {
|
} else {
|
||||||
Self::H_SCENE as u16
|
H_SCENE as u16
|
||||||
};
|
};
|
||||||
let a = w_full(origin_w(east(format!("·s{index:02} "),
|
let a = w_full(origin_w(east(format!("·s{index:02} "),
|
||||||
fg(g(255), bold(true, &scene.name)))));
|
fg(g(255), bold(true, &scene.name)))));
|
||||||
|
|
@ -442,40 +437,31 @@ pub fn view_per_track () -> impl Draw<Tui> {}
|
||||||
pub fn view_per_track_top () -> impl Draw<Tui> {}
|
pub fn view_per_track_top () -> impl Draw<Tui> {}
|
||||||
|
|
||||||
pub fn view_inputs (tracks: impl TracksSizes<'_>, midi_ins: &[MidiInput]) -> impl Draw<Tui> {
|
pub fn view_inputs (tracks: impl TracksSizes<'_>, midi_ins: &[MidiInput]) -> impl Draw<Tui> {
|
||||||
let header = h_exact(1, view_inputs_header(tracks, midi_ins));
|
let title_1 = wh_exact(Some(20), Some(1), origin_w(button_3("i", "nput ", format!("{}", midi_ins.len()), false)));
|
||||||
south(header, thunk(move |to: &mut Tui|{
|
let title_2 = wh_exact(Some(4), Some(1), w_exact(4, button_2("I", "+", false)));
|
||||||
for (index, port) in midi_ins.iter().enumerate() {
|
east(title_1, west(title_2, thunk(move|to: &mut Tui|{
|
||||||
x_push(index as u16 * 10, h_exact(1, view_inputs_row(tracks, port))).draw(to);
|
|
||||||
}
|
|
||||||
todo!()
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn view_inputs_header (tracks: impl TracksSizes<'_>, midi_ins: &[MidiInput]) -> impl Draw<Tui> {
|
|
||||||
east(w_exact(20, origin_w(button_3("i", "nput ", format!("{}", midi_ins.len()), false))),
|
|
||||||
west(w_exact(4, button_2("I", "+", false)), thunk(move|to: &mut Tui|{
|
|
||||||
for (_index, track, x1, _x2) in tracks {
|
for (_index, track, x1, _x2) in tracks {
|
||||||
#[cfg(feature = "track")]
|
south(
|
||||||
x_push(x1 as u16, bg(track.color.dark.term, origin_w(w_exact(track.width as u16, east!(
|
x_push(x1 as u16, bg(track.color.dark.term, origin_w(w_exact(track.width as u16, east!(
|
||||||
either(track.sequencer.monitoring, fg(Green, "mon "), "mon "),
|
either(track.sequencer.monitoring, fg(Green, "mon "), "mon "),
|
||||||
either(track.sequencer.recording, fg(Red, "rec "), "rec "),
|
either(track.sequencer.recording, fg(Red, "rec "), "rec "),
|
||||||
either(track.sequencer.overdub, fg(Yellow, "dub "), "dub "),
|
either(track.sequencer.overdub, fg(Yellow, "dub "), "dub "),
|
||||||
))))).draw(to);
|
))))),
|
||||||
}
|
thunk(move |to: &mut Tui|{
|
||||||
todo!()
|
for (index, port) in midi_ins.iter().enumerate() {
|
||||||
})))
|
x_push(index as u16 * 10, h_exact(1, east(w_exact(20, origin_w(east(" ● ", bold(true, fg(Rgb(255,255,255), port.port_name()))))),
|
||||||
}
|
|
||||||
|
|
||||||
pub fn view_inputs_row (tracks: impl TracksSizes<'_>, port: &MidiInput) -> impl Draw<Tui> {
|
|
||||||
east(w_exact(20, origin_w(east(" ● ", bold(true, fg(Rgb(255,255,255), port.port_name()))))),
|
|
||||||
west(w_exact(4, ()), thunk(move|to: &mut Tui|{
|
west(w_exact(4, ()), thunk(move|to: &mut Tui|{
|
||||||
for (_index, track, _x1, _x2) in tracks {
|
|
||||||
#[cfg(feature = "track")]
|
|
||||||
bg(track.color.darker.term, origin_w(w_exact(track.width as u16, east!(
|
bg(track.color.darker.term, origin_w(w_exact(track.width as u16, east!(
|
||||||
either(track.sequencer.monitoring, fg(Green, " ● "), " · "),
|
either(track.sequencer.monitoring, fg(Green, " ● "), " · "),
|
||||||
either(track.sequencer.recording, fg(Red, " ● "), " · "),
|
either(track.sequencer.recording, fg(Red, " ● "), " · "),
|
||||||
either(track.sequencer.overdub, fg(Yellow, " ● "), " · "),
|
either(track.sequencer.overdub, fg(Yellow, " ● "), " · "),
|
||||||
)))).draw(to);
|
)))).draw(to);
|
||||||
|
todo!()
|
||||||
|
}))))).draw(to);
|
||||||
|
}
|
||||||
|
todo!()
|
||||||
|
})
|
||||||
|
).draw(to);
|
||||||
}
|
}
|
||||||
todo!()
|
todo!()
|
||||||
})))
|
})))
|
||||||
|
|
@ -539,14 +525,14 @@ pub fn view_outputs (
|
||||||
|
|
||||||
pub fn view_track_devices (
|
pub fn view_track_devices (
|
||||||
theme: ItemTheme,
|
theme: ItemTheme,
|
||||||
tracks: &impl TracksSizes<'_>,
|
tracks: impl TracksSizes<'_>,
|
||||||
track: Option<&Track>,
|
track: Option<&Track>,
|
||||||
h: u16,
|
h: u16,
|
||||||
) -> impl Draw<Tui> {
|
) -> impl Draw<Tui> {
|
||||||
view_track_row_section(theme,
|
view_track_row_section(theme,
|
||||||
button_3("d", "evice", format!("{}", track.map(|t|t.devices.len()).unwrap_or(0)), false),
|
button_3("d", "evice", format!("{}", track.map(|t|t.devices.len()).unwrap_or(0)), false),
|
||||||
button_2("D", "+", false),
|
button_2("D", "+", false),
|
||||||
iter(tracks, move|(_, track, _x1, _x2), index| wh_exact(
|
iter_once(tracks, move|(_, track, _x1, _x2), index| wh_exact(
|
||||||
Some(track_width(index, track)),
|
Some(track_width(index, track)),
|
||||||
Some(h + 1),
|
Some(h + 1),
|
||||||
bg(track.color.dark.term, origin_nw(iter_south(move||0..h,
|
bg(track.color.dark.term, origin_nw(iter_south(move||0..h,
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ pub trait ClipsView: TracksView + ScenesView {
|
||||||
/// Draw clips per scene
|
/// Draw clips per scene
|
||||||
fn view_scenes_clips <'a> (&'a self) -> impl Draw<Tui> + 'a {
|
fn view_scenes_clips <'a> (&'a self) -> impl Draw<Tui> + 'a {
|
||||||
crate::view::view_scenes_clips(
|
crate::view::view_scenes_clips(
|
||||||
self.scenes_with_sizes(),
|
||self.scenes_with_sizes(),
|
||||||
self.tracks_with_sizes(),
|
self.tracks_with_sizes(),
|
||||||
self.selection(),
|
self.selection(),
|
||||||
self.editor(),
|
self.editor(),
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,11 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
|
/// Default scene height.
|
||||||
|
pub const H_SCENE: usize = 2;
|
||||||
|
|
||||||
|
/// Default editor height.
|
||||||
|
pub const H_EDITOR: usize = 15;
|
||||||
|
|
||||||
/// A scene consists of a set of clips to play together.
|
/// A scene consists of a set of clips to play together.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
|
@ -51,10 +57,6 @@ impl Scene {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ScenesView: HasEditor + HasSelection + HasSceneScroll + HasClipsSize + Send + Sync {
|
pub trait ScenesView: HasEditor + HasSelection + HasSceneScroll + HasClipsSize + Send + Sync {
|
||||||
/// Default scene height.
|
|
||||||
const H_SCENE: usize = 2;
|
|
||||||
/// Default editor height.
|
|
||||||
const H_EDITOR: usize = 15;
|
|
||||||
fn h_scenes (&self) -> u16;
|
fn h_scenes (&self) -> u16;
|
||||||
fn w_side (&self) -> u16;
|
fn w_side (&self) -> u16;
|
||||||
fn w_mid (&self) -> u16;
|
fn w_mid (&self) -> u16;
|
||||||
|
|
@ -70,7 +72,7 @@ pub trait ScenesView: HasEditor + HasSelection + HasSceneScroll + HasClipsSize +
|
||||||
let height = if self.selection().scene() == Some(s) && self.editor().is_some() {
|
let height = if self.selection().scene() == Some(s) && self.editor().is_some() {
|
||||||
8
|
8
|
||||||
} else {
|
} else {
|
||||||
Self::H_SCENE
|
H_SCENE
|
||||||
};
|
};
|
||||||
if y + height <= self.clips_size().h() as usize {
|
if y + height <= self.clips_size().h() as usize {
|
||||||
let data = (s, scene, y, y + height);
|
let data = (s, scene, y, y + height);
|
||||||
|
|
|
||||||
|
|
@ -223,71 +223,66 @@ impl HasTrackScroll for Arrangement {
|
||||||
|
|
||||||
def_command!(TrackCommand: |track: Track| {
|
def_command!(TrackCommand: |track: Track| {
|
||||||
Stop => { track.sequencer.enqueue_next(None); Ok(None) },
|
Stop => { track.sequencer.enqueue_next(None); Ok(None) },
|
||||||
|
SetRec { rec: Option<bool> } => toggle_bool(&mut track.sequencer.recording, rec, |rec|Self::SetRec { rec }),
|
||||||
|
SetMon { mon: Option<bool> } => toggle_bool(&mut track.sequencer.monitoring, mon, |mon|Self::SetMon { mon }),
|
||||||
SetMute { mute: Option<bool> } => todo!(),
|
SetMute { mute: Option<bool> } => todo!(),
|
||||||
SetSolo { solo: Option<bool> } => todo!(),
|
SetSolo { solo: Option<bool> } => todo!(),
|
||||||
SetSize { size: usize } => todo!(),
|
SetSize { size: usize } => todo!(),
|
||||||
SetZoom { zoom: usize } => todo!(),
|
SetZoom { zoom: usize } => todo!(),
|
||||||
SetName { name: Arc<str> } =>
|
SetName { name: Arc<str> } => swap_value(&mut track.name, name, |name|Self::SetName { name }),
|
||||||
swap_value(&mut track.name, name, |name|Self::SetName { name }),
|
SetColor { color: ItemTheme } => swap_value(&mut track.color, color, |color|Self::SetColor { color }),
|
||||||
SetColor { color: ItemTheme } =>
|
|
||||||
swap_value(&mut track.color, color, |color|Self::SetColor { color }),
|
|
||||||
SetRec { rec: Option<bool> } =>
|
|
||||||
toggle_bool(&mut track.sequencer.recording, rec, |rec|Self::SetRec { rec }),
|
|
||||||
SetMon { mon: Option<bool> } =>
|
|
||||||
toggle_bool(&mut track.sequencer.monitoring, mon, |mon|Self::SetMon { mon }),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
impl<T: AsRef<Vec<Track>>+AsMut<Vec<Track>>> HasTracks for T {}
|
impl<T: AsRef<Vec<Track>>+AsMut<Vec<Track>>> HasTracks for T {}
|
||||||
impl<T: AsRefOpt<Track>+AsMutOpt<Track>+Send+Sync> HasTrack for T {}
|
impl<T: AsRefOpt<Track>+AsMutOpt<Track>+Send+Sync> HasTrack for T {}
|
||||||
impl<T: ScenesView+HasMidiIns+HasMidiOuts+HasTrackScroll> TracksView for T {}
|
impl<T: ScenesView+HasMidiIns+HasMidiOuts+HasTrackScroll> TracksView for T {}
|
||||||
|
impl_has!(Vec<Track>: |self: Arrangement| self.tracks);
|
||||||
|
impl_as_ref_opt!(Track: |self: Arrangement| self.selected_track());
|
||||||
|
impl_as_mut_opt!(Track: |self: Arrangement| self.selected_track_mut());
|
||||||
impl_as_ref!(Vec<Track>: |self: App| self.project.as_ref());
|
impl_as_ref!(Vec<Track>: |self: App| self.project.as_ref());
|
||||||
impl_as_mut!(Vec<Track>: |self: App| self.project.as_mut());
|
impl_as_mut!(Vec<Track>: |self: App| self.project.as_mut());
|
||||||
#[cfg(feature = "select")] impl_as_ref_opt!(Track: |self: App| self.project.as_ref_opt());
|
#[cfg(feature = "select")] impl_as_ref_opt!(Track: |self: App| self.project.as_ref_opt());
|
||||||
#[cfg(feature = "select")] impl_as_mut_opt!(Track: |self: App| self.project.as_mut_opt());
|
#[cfg(feature = "select")] impl_as_mut_opt!(Track: |self: App| self.project.as_mut_opt());
|
||||||
impl_has!(Vec<Track>: |self: Arrangement| self.tracks);
|
|
||||||
impl_as_ref_opt!(Track: |self: Arrangement| self.selected_track());
|
|
||||||
impl_as_mut_opt!(Track: |self: Arrangement| self.selected_track_mut());
|
|
||||||
|
|
||||||
impl Arrangement {
|
impl Arrangement {
|
||||||
#[cfg(feature = "track")]
|
|
||||||
pub fn view_inputs (&self, _theme: ItemTheme) -> impl Draw<Tui> + '_ {
|
pub fn view_inputs (&self, _theme: ItemTheme) -> impl Draw<Tui> + '_ {
|
||||||
crate::view::view_inputs(self.tracks_with_sizes(), self.midi_ins().as_slice())
|
crate::view::view_inputs(
|
||||||
|
self.tracks_with_sizes(), self.midi_ins().as_slice()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "track")]
|
|
||||||
pub fn view_inputs_header (&self) -> impl Draw<Tui> + '_ {
|
|
||||||
crate::view::view_inputs_header(self.tracks_with_sizes(), self.midi_ins().as_slice())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "track")]
|
|
||||||
pub fn view_inputs_row (&self, port: &MidiInput) -> impl Draw<Tui> {
|
|
||||||
crate::view::view_inputs_row(port)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "track")]
|
|
||||||
pub fn view_outputs (&self, theme: ItemTheme) -> impl Draw<Tui> {
|
pub fn view_outputs (&self, theme: ItemTheme) -> impl Draw<Tui> {
|
||||||
|
crate::view::view_outputs(
|
||||||
|
theme, self.tracks_with_sizes(), self.midi_outs(), self.outputs_height()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn view_track_devices (&self, theme: ItemTheme) -> impl Draw<Tui> {
|
||||||
|
crate::view::view_track_devices(
|
||||||
|
theme, self.tracks_with_sizes(), self.track(), self.devices_height()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn devices_height (&self) -> u16 {
|
||||||
|
let mut h = 2;
|
||||||
|
for track in self.tracks().iter() {
|
||||||
|
h = h.max(track.devices.len() * 2);
|
||||||
|
}
|
||||||
|
h as u16
|
||||||
|
}
|
||||||
|
|
||||||
|
fn outputs_height (&self) -> u16 {
|
||||||
let mut h = 1;
|
let mut h = 1;
|
||||||
for output in self.midi_outs().iter() {
|
for output in self.midi_outs().iter() {
|
||||||
h += 1 + output.connections.len();
|
h += 1 + output.connections.len();
|
||||||
}
|
}
|
||||||
let h = h as u16;
|
h as u16
|
||||||
crate::view::view_outputs(self.tracks_with_sizes())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "track")]
|
|
||||||
pub fn view_track_devices (&self, theme: ItemTheme) -> impl Draw<Tui> {
|
|
||||||
let mut h = 2u16;
|
|
||||||
for track in self.tracks().iter() {
|
|
||||||
h = h.max(track.devices.len() as u16 * 2);
|
|
||||||
}
|
|
||||||
crate::view::view_track_devices(||self.tracks_with_sizes(), self.track(), h)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add multiple tracks
|
/// Add multiple tracks
|
||||||
#[cfg(feature = "track")] pub fn tracks_add (
|
pub fn tracks_add (
|
||||||
&mut self,
|
&mut self, count: usize, width: Option<usize>, mins: &[Connect], mouts: &[Connect],
|
||||||
count: usize, width: Option<usize>,
|
|
||||||
mins: &[Connect], mouts: &[Connect],
|
|
||||||
) -> Usually<()> {
|
) -> Usually<()> {
|
||||||
let track_color_1 = ItemColor::random();
|
let track_color_1 = ItemColor::random();
|
||||||
let track_color_2 = ItemColor::random();
|
let track_color_2 = ItemColor::random();
|
||||||
|
|
@ -302,10 +297,12 @@ impl Arrangement {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a track
|
/// Add a track
|
||||||
#[cfg(feature = "track")] pub fn track_add (
|
pub fn track_add (
|
||||||
&mut self,
|
&mut self,
|
||||||
name: Option<&str>, color: Option<ItemTheme>,
|
name: Option<&str>,
|
||||||
mins: &[Connect], mouts: &[Connect],
|
color: Option<ItemTheme>,
|
||||||
|
mins: &[Connect],
|
||||||
|
mouts: &[Connect],
|
||||||
) -> Usually<(usize, &mut Track)> {
|
) -> Usually<(usize, &mut Track)> {
|
||||||
let name: Arc<str> = name.map_or_else(
|
let name: Arc<str> = name.map_or_else(
|
||||||
||format!("trk{:02}", self.track_last).into(),
|
||format!("trk{:02}", self.track_last).into(),
|
||||||
|
|
|
||||||
|
|
@ -89,9 +89,9 @@ pub(crate) use tengri::{
|
||||||
#[cfg(feature = "cli")]
|
#[cfg(feature = "cli")]
|
||||||
pub fn main () -> Usually<()> {
|
pub fn main () -> Usually<()> {
|
||||||
Config::watch(|config|{
|
Config::watch(|config|{
|
||||||
Exit::enter(|exit|{
|
Exit::run(|exit|{
|
||||||
Jack::connect("tek", |jack|{
|
Jack::new_run("tek", |jack|{
|
||||||
let project = Arrangement::new(&jack, &Clock::new(&jack, 51));
|
let project = Arrangement::new(&jack, &Clock::new(&jack, Some(51.)));
|
||||||
let state = App::new_shared(jack, config, project, ":menu");
|
let state = App::new_shared(jack, config, project, ":menu");
|
||||||
let keyboard = tui_input(&exit, &state, Duration::from_millis(100))?;
|
let keyboard = tui_input(&exit, &state, Duration::from_millis(100))?;
|
||||||
let terminal = tui_output(&exit, &state, Duration::from_millis(10))?;
|
let terminal = tui_output(&exit, &state, Duration::from_millis(10))?;
|
||||||
|
|
|
||||||
2
tengri
2
tengri
|
|
@ -1 +1 @@
|
||||||
Subproject commit e0781571c42cb72e7445a2adbdb7a99e37600562
|
Subproject commit 0273d2ac75b8a144ea03b4bdd94d08003f3589df
|
||||||
Loading…
Add table
Add a link
Reference in a new issue