parameterize render! macro

This commit is contained in:
🪞👃🪞 2024-12-17 20:02:47 +01:00
parent 914b569839
commit 3e4d75ea40
17 changed files with 176 additions and 65 deletions

120
crates/tek/examples/bsp.rs Normal file
View file

@ -0,0 +1,120 @@
use tek_core::*;
use tek_core::jack::*;
fn main () -> Usually<()> {
Tui::run(Arc::new(RwLock::new(Demo::new())))?;
Ok(())
}
pub struct BspDemo<E: Engine>;
impl Content for Demo<Tui> {
type Engine = Tui;
fn content (&self) -> dyn Render<Engine = Tui> {
let border_style = Style::default().fg(Color::Rgb(0,0,0));
Align::Center(Layers::new(move|add|{
add(&Background(Color::Rgb(0,128,128)))?;
add(&Outset::XY(1, 1, Stack::down(|add|{
add(&Layers::new(|add|{
add(&Background(Color::Rgb(128,96,0)))?;
add(&Border(Square(border_style)))?;
add(&Outset::XY(2, 1, "..."))?;
Ok(())
}).debug())?;
add(&Layers::new(|add|{
add(&Background(Color::Rgb(128,64,0)))?;
add(&Border(Lozenge(border_style)))?;
add(&Outset::XY(4, 2, "---"))?;
Ok(())
}).debug())?;
add(&Layers::new(|add|{
add(&Background(Color::Rgb(96,64,0)))?;
add(&Border(SquareBold(border_style)))?;
add(&Outset::XY(6, 3, "~~~"))?;
Ok(())
}).debug())?;
Ok(())
})).debug())?;
Ok(())
}))
//Align::Center(Outset::X(1, Layers::new(|add|{
//add(&Background(Color::Rgb(128,0,0)))?;
//add(&Stack::down(|add|{
//add(&Outset::Y(1, Layers::new(|add|{
//add(&Background(Color::Rgb(0,128,0)))?;
//add(&Align::Center("12345"))?;
//add(&Align::Center("FOO"))
//})))?;
//add(&Outset::XY(1, 1, Layers::new(|add|{
//add(&Align::Center("1234567"))?;
//add(&Align::Center("BAR"))?;
//add(&Background(Color::Rgb(0,0,128)))
//})))
//}))
//})))
//Align::Y(Layers::new(|add|{
//add(&Background(Color::Rgb(128,0,0)))?;
//add(&Outset::X(1, Align::Center(Stack::down(|add|{
//add(&Align::X(Outset::Y(1, Layers::new(|add|{
//add(&Background(Color::Rgb(0,128,0)))?;
//add(&Align::Center("12345"))?;
//add(&Align::Center("FOO"))
//})))?;
//add(&Outset::XY(1, 1, Layers::new(|add|{
//add(&Align::Center("1234567"))?;
//add(&Align::Center("BAR"))?;
//add(&Background(Color::Rgb(0,0,128)))
//})))?;
//Ok(())
//})))))
//}))
}
}
impl Handle<Tui> for Demo<Tui> {
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
match from.event() {
key!(KeyCode::PageUp) => {
self.index = (self.index + 1) % self.items.len();
},
key!(KeyCode::PageDown) => {
self.index = if self.index > 1 {
self.index - 1
} else {
self.items.len() - 1
};
},
_ => return Ok(None)
}
Ok(Some(true))
}
}
//lisp!(CONTENT Demo (LET
//(BORDER-STYLE (STYLE (FG (RGB 0 0 0))))
//(BG-COLOR-0 (RGB 0 128 128))
//(BG-COLOR-1 (RGB 128 96 0))
//(BG-COLOR-2 (RGB 128 64 0))
//(BG-COLOR-3 (RGB 96 64 0))
//(CENTER (LAYERS
//(BACKGROUND BG-COLOR-0)
//(OUTSET-XY 1 1 (SPLIT-DOWN
//(LAYERS (BACKGROUND BG-COLOR-1)
//(BORDER SQUARE BORDER-STYLE)
//(OUTSET-XY 2 1 "..."))
//(LAYERS (BACKGROUND BG-COLOR-2)
//(BORDER LOZENGE BORDER-STYLE)
//(OUTSET-XY 4 2 "---"))
//(LAYERS (BACKGROUND BG-COLOR-3)
//(BORDER SQUARE-BOLD BORDER-STYLE)
//(OUTSET-XY 2 1 "~~~"))))))))

View file

@ -1,5 +1,18 @@
use crate::*;
#[macro_export] macro_rules! render {
(<$E:ty>|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => {
impl $(<$($L),*$($T $(: $U)?),*>)? Render<$E> for $Struct $(<$($L),*$($T),*>)? {
fn min_size (&$self, to: <$E as Engine>::Size) -> Perhaps<<$E as Engine>::Size> {
$cb.min_size(to)
}
fn render (&$self, to: &mut <$E as Engine>::Output) -> Usually<()> {
$cb.render(to)
}
}
}
}
/// Rendering target
pub trait Output<E: Engine> {
/// Current output area

View file

@ -67,28 +67,28 @@ impl<E: Engine, X: Render<E>, Y: Render<E>> Render<E> for Bsp<E, X, Y> {
Self::Null(_) => {},
Self::S(a, b) => {
let s_a = a.min_size(s)?.unwrap_or(n);
//let s_b = b.min_size(s)?.unwrap_or(n);
let _ = b.min_size(s)?.unwrap_or(n);
let h = s_a.h().into();
to.render_in(to.area().clip_h(h).into(), a)?;
to.render_in(to.area().push_y(h).shrink_y(h).into(), b)?;
to.render_in(to.area().shrink_y(h).push_y(h).into(), b)?;
},
Self::E(a, b) => {
let s_a = a.min_size(s)?.unwrap_or(n);
//let s_b = b.min_size(s)?.unwrap_or(n);
let _ = b.min_size(s)?.unwrap_or(n);
let w = s_a.w().into();
to.render_in(to.area().clip_w(w).into(), a)?;
to.render_in(to.area().push_x(w).shrink_x(w).into(), b)?;
},
Self::W(a, b) => {
let s_a = a.min_size(s)?.unwrap_or(n);
//let s_b = b.min_size(s)?.unwrap_or(n);
let _ = b.min_size(s)?.unwrap_or(n);
let w = (to.area().w() - s_a.w()).into();
to.render_in(to.area().push_x(w).into(), a)?;
to.render_in(to.area().shrink_x(w).into(), b)?;
},
Self::N(a, b) => {
let s_a = a.min_size(s)?.unwrap_or(n);
//let s_b = b.min_size(s)?.unwrap_or(n);
let _ = b.min_size(s)?.unwrap_or(n);
let h = to.area().h() - s_a.h();
to.render_in(to.area().push_y(h).into(), a)?;
to.render_in(to.area().shrink_y(h).into(), b)?;

View file

@ -84,7 +84,7 @@ impl Measure<Tui> {
}
pub struct ShowMeasure<'a>(&'a Measure<Tui>);
render!(|self: ShowMeasure<'a>|render(|to|Ok({
render!(<Tui>|self: ShowMeasure<'a>|render(|to|Ok({
let w = self.0.w();
let h = self.0.h();
to.blit(&format!(" {w} x {h} "), to.area.x(), to.area.y(), Some(

View file

@ -27,19 +27,6 @@ mod port_select; pub(crate) use port_select::*;
////////////////////////////////////////////////////////
#[macro_export] macro_rules! render {
(|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => {
impl $(<$($L),*$($T $(: $U)?),*>)? Render<Tui> for $Struct $(<$($L),*$($T),*>)? {
fn min_size (&$self, to: [u16;2]) -> Perhaps<[u16;2]> {
$cb.min_size(to)
}
fn render (&$self, to: &mut TuiOutput) -> Usually<()> {
$cb.render(to)
}
}
}
}
pub fn render <F: Fn(&mut TuiOutput)->Usually<()>+Send+Sync> (render: F) -> impl Render<Tui> {
Widget::new(|_|Ok(Some([0u16,0u16].into())), render)
}

View file

@ -60,7 +60,7 @@ has_clock!(|self:ArrangerTui|&self.clock);
has_phrases!(|self:ArrangerTui|self.phrases.phrases);
has_editor!(|self:ArrangerTui|self.editor);
handle!(<Tui>|self: ArrangerTui, input|ArrangerCommand::execute_with_state(self, input));
render!(|self: ArrangerTui|{
render!(<Tui>|self: ArrangerTui|{
let arranger_focused = self.arranger_focused();
let transport_focused = if let ArrangerFocus::Transport(_) = self.focus.inner() {
true
@ -506,7 +506,7 @@ impl StatusBar for ArrangerStatus {
}
}
render!(|self: ArrangerStatus|{
render!(<Tui>|self: ArrangerStatus|{
let label = match self {
Self::Transport => "TRANSPORT",
@ -674,7 +674,7 @@ impl From<&ArrangerTui> for ArrangerVerticalColumnSeparator {
}
}
}
render!(|self: ArrangerVerticalColumnSeparator|render(move|to: &mut TuiOutput|{
render!(<Tui>|self: ArrangerVerticalColumnSeparator|render(move|to: &mut TuiOutput|{
let style = Some(Style::default().fg(self.sep_fg));
Ok(for x in self.cols.iter().map(|col|col.1) {
let x = self.scenes_w + to.area().x() + x as u16;
@ -697,7 +697,7 @@ impl From<(&ArrangerTui, usize)> for ArrangerVerticalRowSeparator {
}
}
render!(|self: ArrangerVerticalRowSeparator|render(move|to: &mut TuiOutput|{
render!(<Tui>|self: ArrangerVerticalRowSeparator|render(move|to: &mut TuiOutput|{
Ok(for y in self.rows.iter().map(|row|row.1) {
let y = to.area().y() + (y / PPQ) as u16 + 1;
if y >= to.buffer.area.height { break }
@ -731,7 +731,7 @@ impl From<(&ArrangerTui, usize)> for ArrangerVerticalCursor {
}
}
}
render!(|self: ArrangerVerticalCursor|render(move|to: &mut TuiOutput|{
render!(<Tui>|self: ArrangerVerticalCursor|render(move|to: &mut TuiOutput|{
let area = to.area();
let focused = self.focused;
let selected = self.selected;
@ -810,7 +810,7 @@ impl<'a> From<&'a ArrangerTui> for ArrangerVerticalHeader<'a> {
}
}
}
render!(|self: ArrangerVerticalHeader<'a>|row!(
render!(<Tui>|self: ArrangerVerticalHeader<'a>|row!(
(track, w) in self.tracks.iter().zip(self.cols.iter().map(|col|col.0)) => {
// name and width of track
let name = track.name().read().unwrap();
@ -877,7 +877,7 @@ impl<'a> From<(&'a ArrangerTui, usize)> for ArrangerVerticalContent<'a> {
}
}
}
render!(|self: ArrangerVerticalContent<'a>|Fixed::h(
render!(<Tui>|self: ArrangerVerticalContent<'a>|Fixed::h(
(self.size.h() as u16).saturating_sub(self.header_h),
col!((scene, pulses) in self.scenes.iter().zip(self.rows.iter().map(|row|row.0)) => {
let height = 1.max((pulses / PPQ) as u16);

View file

@ -26,7 +26,7 @@ pub enum GrooveboxCommand {
Sampler(SamplerCommand),
}
render!(|self:GrooveboxTui|Bsp::n(
render!(<Tui>|self:GrooveboxTui|Bsp::n(
Fixed::h(2, SequencerStatusBar::from(&self.sequencer)),
Bsp::s(Fixed::h(self.split, &self.sequencer), &self.sampler),
));

View file

@ -65,7 +65,7 @@ pub enum SamplerFocus {
}
audio!(|self: SamplerTui, _client, _scope|Control::Continue);
render!(|self: SamplerTui|{
render!(<Tui>|self: SamplerTui|{
Fill::wh(lay!([
Fill::wh(render(|to|{ // border

View file

@ -136,7 +136,7 @@ has_size!(<Tui>|self:SequencerTui|&self.size);
has_clock!(|self:SequencerTui|&self.clock);
has_phrases!(|self:SequencerTui|self.phrases.phrases);
has_editor!(|self:SequencerTui|self.editor);
render!(|self: SequencerTui|{
render!(<Tui>|self: SequencerTui|{
let w = self.size.w();
let phrase_w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 };
let pool_w = if self.show_pool { phrase_w } else { 0 };
@ -225,7 +225,7 @@ impl From<&SequencerTui> for SequencerStatusBar {
}
}
render!(|self: SequencerStatusBar|Fixed::h(2, lay!([
render!(<Tui>|self: SequencerStatusBar|Fixed::h(2, lay!([
{
let single = |binding, command|row!([" ", col!([
Tui::fg(TuiTheme::yellow(), binding),

View file

@ -40,7 +40,7 @@ impl std::fmt::Debug for TransportTui {
has_clock!(|self:TransportTui|&self.clock);
audio!(|self:TransportTui,client,scope|ClockAudio(self).process(client, scope));
handle!(<Tui>|self:TransportTui,from|TransportCommand::execute_with_state(self, from));
render!(|self: TransportTui|TransportView::from((self, None, true)));
render!(<Tui>|self: TransportTui|TransportView::from((self, None, true)));
pub struct TransportView {
color: ItemPalette,
@ -97,12 +97,12 @@ impl<T: HasClock> From<(&T, Option<ItemPalette>, bool)> for TransportView {
}
render!(|self: TransportView|{
render!(<Tui>|self: TransportView|{
let color = self.color;
struct Field<'a>(&'a str, &'a str, &'a ItemPalette);
render!(|self: Field<'a>|row!([
render!(<Tui>|self: Field<'a>|row!([
Tui::fg_bg(self.2.lightest.rgb, self.2.darkest.rgb, Tui::bold(true, self.0)),
Tui::fg_bg(self.2.lighter.rgb, self.2.darkest.rgb, ""),
Tui::fg_bg(self.2.lighter.rgb, self.2.base.rgb, format!("{:>10}", self.1)),
@ -124,7 +124,7 @@ render!(|self: TransportView|{
});
pub struct PlayPause(pub bool);
render!(|self: PlayPause|Tui::bg(
render!(<Tui>|self: PlayPause|Tui::bg(
if self.0{Color::Rgb(0,128,0)}else{Color::Rgb(128,64,0)},
Fixed::w(5, col!(|add|if self.0 {
add(&Tui::fg(Color::Rgb(0, 255, 0), col!([
@ -200,7 +200,7 @@ impl StatusBar for TransportStatusBar {
}
}
render!(|self: TransportStatusBar|"todo");
render!(<Tui>|self: TransportStatusBar|"todo");
pub trait TransportControl<T>: HasClock + {
fn transport_focused (&self) -> Option<TransportFocus>;

View file

@ -15,7 +15,7 @@ pub struct FileBrowser {
pub size: Measure<Tui>
}
render!(|self: FileBrowser|{
render!(<Tui>|self: FileBrowser|{
Stack::down(|add|{
let mut i = 0;
for (_, name) in self.dirs.iter() {

View file

@ -119,8 +119,8 @@ impl Default for PhraseEditorModel {
}
has_size!(<Tui>|self:PhraseEditorModel|&self.size);
render!(|self: PhraseEditorModel|&self.mode);
//render!(|self: PhraseEditorModel|lay!(|add|{add(&self.size)?;add(self.mode)}));//bollocks
render!(<Tui>|self: PhraseEditorModel|&self.mode);
//render!(<Tui>|self: PhraseEditorModel|lay!(|add|{add(&self.size)?;add(self.mode)}));//bollocks
pub trait PhraseViewMode: Render<Tui> + HasSize<Tui> + MidiRange + MidiPoint + Debug + Send + Sync {
fn buffer_size (&self, phrase: &Phrase) -> (usize, usize);
@ -231,7 +231,7 @@ impl std::fmt::Debug for PhraseEditorModel {
}
pub struct PhraseEditStatus<'a>(pub &'a PhraseEditorModel);
render!(|self:PhraseEditStatus<'a>|row!(|add|{
render!(<Tui>|self:PhraseEditStatus<'a>|row!(|add|{
let (color, name, length, looped) = if let Some(phrase) = self.0.phrase().as_ref().map(|p|p.read().unwrap()) {
(phrase.color, phrase.name.clone(), phrase.length, phrase.looped)
} else {

View file

@ -68,8 +68,7 @@ impl PhraseLengthFocus {
}
}
render!(|self: PhraseLength|{
render!(<Tui>|self: PhraseLength|{
let bars = ||self.bars_string();
let beats = ||self.beats_string();
let ticks = ||self.ticks_string();

View file

@ -206,7 +206,7 @@ pub trait HasPhraseList: HasPhrases {
pub struct PhraseListView<'a>(pub(crate) &'a PhraseListModel);
// TODO: Display phrases always in order of appearance
render!(|self: PhraseListView<'a>|{
render!(<Tui>|self: PhraseListView<'a>|{
let PhraseListModel { phrases, mode, .. } = self.0;
let bg = TuiTheme::g(32);
let title_color = TuiTheme::ti1();
@ -263,7 +263,7 @@ pub struct PhraseSelector {
}
// TODO: Display phrases always in order of appearance
render!(|self: PhraseSelector|Fixed::wh(24, 1, row!([
render!(<Tui>|self: PhraseSelector|Fixed::wh(24, 1, row!([
Tui::fg(self.color.lightest.rgb, Tui::bold(true, &self.title)),
Tui::fg_bg(self.color.lighter.rgb, self.color.base.rgb, row!([
format!("{:8}", &self.name[0..8.min(self.name.len())]),

View file

@ -59,32 +59,24 @@ impl PianoHorizontal {
}
}
render!(|self: PianoHorizontal|{
render!(<Tui>|self: PianoHorizontal|{
let color = self.color;
let keys = move||PianoHorizontalKeys(&self);
let timeline = move||PianoHorizontalTimeline(&self);
let notes = move||PianoHorizontalNotes(&self);
let cursor = move||PianoHorizontalCursor(&self);
let keys_width = 5;
Fill::wh(Tui::bg(color.darker.rgb,
Bsp::s(
Fixed::h(1, Bsp::e(
Fixed::w(keys_width, ""),
Fill::w(timeline()),
)),
Bsp::e(
Fixed::w(keys_width, keys()),
Fill::wh(lay!([&self.size, Fill::wh(lay!([
Fill::wh(notes()),
Fill::wh(cursor()),
]))])),
),
)
))
Tui::bg(color.darker.rgb, Fill::wh(Bsp::s(
Fixed::h(1, Bsp::e(Fixed::w(keys_width, ""), Fill::w(timeline()),)),
Bsp::e(
Fixed::w(keys_width, keys()),
Fill::wh(lay!([&self.size, Fill::wh(lay!([Fill::wh(notes()), Fill::wh(cursor()),]))])),
),
)))
});
pub struct PianoHorizontalTimeline<'a>(&'a PianoHorizontal);
render!(|self: PianoHorizontalTimeline<'a>|render(|to|{
render!(<Tui>|self: PianoHorizontalTimeline<'a>|render(|to|{
let [x, y, w, h] = to.area();
let style = Some(Style::default().dim());
let length = self.0.phrase.as_ref().map(|p|p.read().unwrap().length).unwrap_or(1);
@ -103,7 +95,7 @@ render!(|self: PianoHorizontalTimeline<'a>|render(|to|{
//));
pub struct PianoHorizontalKeys<'a>(&'a PianoHorizontal);
render!(|self: PianoHorizontalKeys<'a>|render(|to|Ok({
render!(<Tui>|self: PianoHorizontalKeys<'a>|render(|to|Ok({
let color = self.0.color;
let note_lo = self.0.note_lo();
let note_hi = self.0.note_hi();
@ -136,7 +128,7 @@ render!(|self: PianoHorizontalKeys<'a>|render(|to|Ok({
})));
pub struct PianoHorizontalCursor<'a>(&'a PianoHorizontal);
render!(|self: PianoHorizontalCursor<'a>|render(|to|Ok({
render!(<Tui>|self: PianoHorizontalCursor<'a>|render(|to|Ok({
let note_hi = self.0.note_hi();
let note_len = self.0.note_len();
let note_lo = self.0.note_lo();
@ -167,7 +159,7 @@ render!(|self: PianoHorizontalCursor<'a>|render(|to|Ok({
})));
pub struct PianoHorizontalNotes<'a>(&'a PianoHorizontal);
render!(|self: PianoHorizontalNotes<'a>|render(|to|Ok({
render!(<Tui>|self: PianoHorizontalNotes<'a>|render(|to|Ok({
let time_start = self.0.time_start();
let note_hi = self.0.note_hi();
let note_lo = self.0.note_lo();

View file

@ -4,4 +4,4 @@ pub struct PortSelector {
pub(crate) title: &'static str,
}
render!(|self: PortSelector|{});
render!(<Tui>|self: PortSelector|{});

View file

@ -2,7 +2,7 @@ use crate::*;
pub struct Bordered<S: BorderStyle, W: Render<Tui>>(pub S, pub W);
render!(|self: Bordered<S: BorderStyle, W: Render<Tui>>|{
render!(<Tui>|self: Bordered<S: BorderStyle, W: Render<Tui>>|{
Fill::wh(lay!([Border(self.0), Tui::inset_xy(1, 1, widget(&self.1))]))
});