pool and browser as devices
Some checks are pending
/ build (push) Waiting to run

This commit is contained in:
🪞👃🪞 2025-05-13 20:25:17 +03:00
parent b0ef0cfd21
commit fa73821a0b
29 changed files with 831 additions and 650 deletions

View file

@ -2,6 +2,7 @@ use crate::*;
pub(crate) use std::fmt::Write;
pub(crate) use ::tengri::tui::ratatui::prelude::Position;
mod view_dialog; pub use self::view_dialog::*;
mod view_output; pub use self::view_output::*;
#[tengri_proc::view(TuiOut)]
@ -49,85 +50,11 @@ impl App {
self.sampler().map(|s|s.view_meters_output())
}
pub fn view_dialog (&self) -> impl Content<TuiOut> + use<'_> {
When::new(self.dialog.is_some(), Bsp::b(
Fill::xy(Tui::fg_bg(Rgb(64,64,64), Rgb(32,32,32), "")),
Fixed::xy(30, 15, Tui::fg_bg(Rgb(255,255,255), Rgb(16,16,16), Bsp::b(
Repeat(" "),
Outer(true, Style::default().fg(Tui::g(96)))
.enclose(self.dialog.as_ref().map(|dialog|match dialog {
Dialog::Menu => self.view_dialog_menu().boxed(),
Dialog::Help => self.view_dialog_help().boxed(),
Dialog::Save => self.view_dialog_save().boxed(),
Dialog::Load => self.view_dialog_load().boxed(),
Dialog::Options => self.view_dialog_options().boxed(),
Dialog::Device(index) => self.view_dialog_device(*index).boxed(),
Dialog::Message(message) => self.view_dialog_message(message).boxed(),
}))
)))
))
view_dialog(self)
}
}
impl App {
fn view_dialog_menu (&self) -> impl Content<TuiOut> {
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)))
}
fn view_dialog_help (&self) -> impl Content<TuiOut> + use<'_> {
let bindings = ||self.config.keys.layers.iter()
.filter_map(|a|(a.0)(self).then_some(a.1))
.flat_map(|a|a)
.filter_map(|x|if let Value::Exp(_, iter)=x.value{
Some(iter)
} else {
None
});
//let binding = ;[> Bsp::e(
//Fixed::x(15, Align::w(Tui::bold(true, Tui::fg(Rgb(255,192,0), if let Some(Token {
//value: Value::Sym(key), ..
//}) = binding.next() {
//Some(key.to_string())
//} else {
//None
//})))),
//Bsp::e(" ", Tui::fg(Rgb(255,255,255), if let Some(Token {
//value: Value::Key(command), ..
//}) = binding.next() {
//Some(command.to_string())
//} else {
//None
//})),
//);*/
Bsp::s(Tui::bold(true, "Help"), Bsp::s("", Map::south(1, bindings, |b,i|format!("{i}:{b:?}"))))
//|mut binding: TokenIter, _|Map::east(5, move||binding.clone(), |_,_|"kyp"))))
}
fn view_dialog_device (&self, index: usize) -> impl Content<TuiOut> + use<'_> {
let choices = ||self.device_kinds().iter();
let choice = move|label, i|
Fill::x(Tui::bg(if i == index { Rgb(64,128,32) } else { Rgb(0,0,0) },
Bsp::e(if i == index { "[ " } else { " " },
Bsp::w(if i == index { " ]" } else { " " },
label))));
Bsp::s(Tui::bold(true, "Add device"), Map::south(1, choices, choice))
}
fn view_dialog_message <'a> (&'a self, message: &'a Message) -> impl Content<TuiOut> + use<'a> {
Bsp::s(message, Bsp::s("", "[ OK ]"))
}
fn view_dialog_save <'a> (&'a self) -> impl Content<TuiOut> + use<'a> {
"WIP: SAVE"
}
fn view_dialog_load <'a> (&'a self) -> impl Content<TuiOut> + use<'a> {
"WIP: LOAD"
}
fn view_dialog_options <'a> (&'a self) -> impl Content<TuiOut> + use<'a> {
"WIP: OPTIONS"
}
/// Spacing between tracks.
pub(crate) const TRACK_SPACING: usize = 0;
@ -752,44 +679,3 @@ impl ViewCache {
}
}
}
pub struct PoolView<'a>(pub bool, pub &'a MidiPool);
content!(TuiOut: |self: PoolView<'a>| {
let Self(compact, model) = self;
let MidiPool { clips, .. } = self.1;
//let color = self.1.clip().map(|c|c.read().unwrap().color).unwrap_or_else(||Tui::g(32).into());
let on_bg = |x|x;//Bsp::b(Repeat(" "), Tui::bg(color.darkest.rgb, x));
let border = |x|x;//Outer(Style::default().fg(color.dark.rgb).bg(color.darkest.rgb)).enclose(x);
let iter = | |model.clips().clone().into_iter();
let height = clips.read().unwrap().len() as u16;
Tui::bg(Reset, Fixed::y(height, on_bg(border(Map::new(iter, move|clip: Arc<RwLock<MidiClip>>, i|{
let item_height = 1;
let item_offset = i as u16 * item_height;
let selected = i == model.clip_index();
let MidiClip { ref name, color, length, .. } = *clip.read().unwrap();
let bg = if selected { color.light.rgb } else { color.base.rgb };
let fg = color.lightest.rgb;
let name = if *compact { format!(" {i:>3}") } else { format!(" {i:>3} {name}") };
let length = if *compact { String::default() } else { format!("{length} ") };
Fixed::y(1, map_south(item_offset, item_height, Tui::bg(bg, lay!(
Fill::x(Align::w(Tui::fg(fg, Tui::bold(selected, name)))),
Fill::x(Align::e(Tui::fg(fg, Tui::bold(selected, length)))),
Fill::x(Align::w(When::new(selected, Tui::bold(true, Tui::fg(Tui::g(255), ""))))),
Fill::x(Align::e(When::new(selected, Tui::bold(true, Tui::fg(Tui::g(255), ""))))),
))))
})))))
});
content!(TuiOut: |self: ClipLength| {
use ClipLengthFocus::*;
let bars = ||self.bars_string();
let beats = ||self.beats_string();
let ticks = ||self.ticks_string();
match self.focus {
None => row!(" ", bars(), ".", beats(), ".", ticks()),
Some(Bar) => row!("[", bars(), "]", beats(), ".", ticks()),
Some(Beat) => row!(" ", bars(), "[", beats(), "]", ticks()),
Some(Tick) => row!(" ", bars(), ".", beats(), "[", ticks()),
}
});