mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
check pass, test pass.. but does it run?
This commit is contained in:
parent
1de163d0d3
commit
9f7b23a252
10 changed files with 65 additions and 48 deletions
|
|
@ -1,28 +1,47 @@
|
|||
//! Groupings of elements.
|
||||
|
||||
mod split; pub use self::split::*;
|
||||
mod stack; pub use self::stack::*;
|
||||
//mod stack; pub use self::stack::*;
|
||||
|
||||
use crate::*;
|
||||
|
||||
use std::sync::RwLock;
|
||||
|
||||
/// Conditional rendering, in unary and binary forms.
|
||||
pub struct Cond;
|
||||
impl<E: Engine> Layout<E> for E {}
|
||||
|
||||
impl Cond {
|
||||
pub trait Layout<E: Engine> {
|
||||
/// Content `item` when `cond` is true.
|
||||
pub fn when <E: Engine, A: Content<E>> (cond: bool, item: A) -> When<E, A> {
|
||||
fn when <A: Content<E>> (cond: bool, item: A) -> When<E, A> {
|
||||
When(cond, item, Default::default())
|
||||
}
|
||||
/// Content `item` if `cond` is true, otherwise render `other`.
|
||||
pub fn either <E: Engine, A: Content<E>, B: Content<E>> (cond: bool, a: A, b: B) -> Either<E, A, B> {
|
||||
fn either <A: Content<E>, B: Content<E>> (cond: bool, a: A, b: B)
|
||||
-> Either<E, A, B>
|
||||
{
|
||||
Either(cond, a, b, Default::default())
|
||||
}
|
||||
|
||||
pub fn opt <E: Engine, A, F: Fn(A)->R, R: Content<E>> (option: Option<A>, cb: F) -> Opt<E, A, F, R> {
|
||||
/// Maps an [Option<T>] through a callback `F`
|
||||
fn opt <A, F: Fn(A)->R, R: Content<E>> (option: Option<A>, cb: F)
|
||||
-> Opt<E, A, F, R>
|
||||
{
|
||||
Opt(option, cb, Default::default())
|
||||
}
|
||||
fn map <T, I, R, F>(iterator: I, callback: F) -> Map<E, T, I, R, F> where
|
||||
E: Engine,
|
||||
I: Iterator<Item = T> + Send + Sync,
|
||||
R: Content<E>,
|
||||
F: Fn(T, usize)->R + Send + Sync
|
||||
{
|
||||
Map(Default::default(), RwLock::new(iterator), callback)
|
||||
}
|
||||
//pub fn reduce <E, T, I, R, F>(iterator: I, callback: F) -> Reduce<E, T, I, R, F> where
|
||||
//E: Engine,
|
||||
//I: Iterator<Item = T> + Send + Sync,
|
||||
//R: Content<E>,
|
||||
//F: Fn(R, T, usize) -> R + Send + Sync
|
||||
//{
|
||||
//Reduce(Default::default(), iterator, callback)
|
||||
//}
|
||||
}
|
||||
|
||||
pub struct Opt<E: Engine, A, F: Fn(A)->R, R: Content<E>>(Option<A>, F, PhantomData<E>);
|
||||
|
|
@ -63,33 +82,31 @@ impl<E: Engine, A: Content<E>, B: Content<E>> Content<E> for Either<E, A, B> {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait Render<E: Engine> {
|
||||
fn area (&self, to: E::Area) -> E::Area;
|
||||
fn render (&self, to: &mut E::Output);
|
||||
}
|
||||
|
||||
impl<E: Engine, C: Content<E>> Render<E> for C {
|
||||
fn area (&self, to: E::Area) -> E::Area {
|
||||
Content::area(self, to)
|
||||
}
|
||||
fn render (&self, to: &mut E::Output) {
|
||||
Content::render(self, to)
|
||||
}
|
||||
}
|
||||
|
||||
/// A function or closure that emits renderables.
|
||||
pub trait Collector<E: Engine>: Send + Sync + Fn(&mut dyn FnMut(&dyn Content<E>)) {}
|
||||
pub trait Collector<E: Engine>: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<E>)) {}
|
||||
|
||||
/// Any function or closure that emits renderables for the given engine matches [CollectCallback].
|
||||
impl<E, F> Collector<E> for F
|
||||
where E: Engine, F: Send + Sync + Fn(&mut dyn FnMut(&dyn Content<E>)) {}
|
||||
where E: Engine, F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<E>)) {}
|
||||
|
||||
/// Rendering of iterable collections, one-to-one and many-to one.
|
||||
pub struct Coll;
|
||||
|
||||
impl Coll {
|
||||
pub fn map <E, T, I, R, F>(iterator: I, callback: F) -> Map<E, T, I, R, F> where
|
||||
E: Engine,
|
||||
I: Iterator<Item = T> + Send + Sync,
|
||||
R: Content<E>,
|
||||
F: Fn(T, usize)->R + Send + Sync
|
||||
{
|
||||
Map(Default::default(), RwLock::new(iterator), callback)
|
||||
}
|
||||
//pub fn reduce <E, T, I, R, F>(iterator: I, callback: F) -> Reduce<E, T, I, R, F> where
|
||||
//E: Engine,
|
||||
//I: Iterator<Item = T> + Send + Sync,
|
||||
//R: Content<E>,
|
||||
//F: Fn(R, T, usize) -> R + Send + Sync
|
||||
//{
|
||||
//Reduce(Default::default(), iterator, callback)
|
||||
//}
|
||||
}
|
||||
|
||||
pub struct Map<E, T, I, R, F>(PhantomData<E>, RwLock<I>, F) where
|
||||
|
|
|
|||
|
|
@ -66,11 +66,11 @@ impl<E: Engine> Measure<E> {
|
|||
}
|
||||
}
|
||||
|
||||
/// A scrollable area.
|
||||
pub struct Scroll<E, F>(pub F, pub Direction, pub u64, PhantomData<E>)
|
||||
where
|
||||
E: Engine,
|
||||
F: Send + Sync + Fn(&mut dyn FnMut(&dyn Content<E>)->Usually<()>)->Usually<()>;
|
||||
///// A scrollable area.
|
||||
//pub struct Scroll<E, F>(pub F, pub Direction, pub u64, PhantomData<E>)
|
||||
//where
|
||||
//E: Engine,
|
||||
//F: Send + Sync + Fn(&mut dyn FnMut(&dyn Content<E>)->Usually<()>)->Usually<()>;
|
||||
|
||||
//pub trait ContentDebug<E: Engine> {
|
||||
//fn debug <W: Content<E>> (other: W) -> DebugOverlay<E, W> {
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ macro_rules! transform_xy {
|
|||
|
||||
transform_xy!(self: Fill |to|{
|
||||
let [x0, y0, wmax, hmax] = to.xywh();
|
||||
let [x, y, w, h] = self.content().area(to).xywh();
|
||||
let [x, y, w, h] = Content::area(&self.content(), to).xywh();
|
||||
return match self {
|
||||
Self::X(_) => [x0, y, wmax, h],
|
||||
Self::Y(_) => [x, y0, w, hmax],
|
||||
|
|
@ -174,10 +174,10 @@ fn align<E: Engine, T: Content<E>, N: Coordinate, R: Area<N> + From<[N;4]>> (ali
|
|||
|
||||
impl<E: Engine, T: Content<E>> Content<E> for Align<E, T> {
|
||||
fn render (&self, to: &mut E::Output) {
|
||||
let outer_area = to.area();
|
||||
let content = self.content();
|
||||
let inner_area = content.area(outer_area);
|
||||
if let Some(aligned) = align(&self, outer_area.into(), inner_area.into()) {
|
||||
let outer = to.area();
|
||||
let content = self.content();
|
||||
let inner = Content::area(&content, outer);
|
||||
if let Some(aligned) = align(&self, outer.into(), inner.into()) {
|
||||
to.place(aligned, &content)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ impl<'a> ArrangerVClips<'a> {
|
|||
impl<'a> Content<Tui> for ArrangerVClips<'a> {
|
||||
fn content (&self) -> impl Content<Tui> {
|
||||
let iter = self.scenes.iter().zip(self.rows.iter().map(|row|row.0));
|
||||
let col = Coll::map(iter, |(scene, pulses), i|Self::format_scene(self.tracks, scene, pulses));
|
||||
let col = Tui::map(iter, |(scene, pulses), i|Self::format_scene(self.tracks, scene, pulses));
|
||||
Fill::xy(col)
|
||||
}
|
||||
}
|
||||
|
|
@ -37,7 +37,7 @@ impl<'a> ArrangerVClips<'a> {
|
|||
let name = Tui::fg_bg(scene.color.lightest.rgb, scene.color.base.rgb,
|
||||
Expand::x(1, Tui::bold(true, scene.name.read().unwrap().clone()))
|
||||
);
|
||||
let clips = Coll::map(ArrangerTrack::with_widths(tracks), move|(index, track, x1, x2), _|
|
||||
let clips = Tui::map(ArrangerTrack::with_widths(tracks), move|(index, track, x1, x2), _|
|
||||
Push::x((x2 - x1) as u16, Self::format_clip(scene, index, track, (x2 - x1) as u16, height))
|
||||
);
|
||||
Fixed::y(height, row!(icon, name, clips))
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ render!(Tui: (self: ArrangerVHead<'a>) => {
|
|||
row!(Tui::fg(color.light.rgb, "▎"), Tui::fg(color.lightest.rgb, field))
|
||||
}
|
||||
Some(Push::x(self.scenes_w,
|
||||
Coll::map(ArrangerTrack::with_widths(self.tracks), |(_, track, x1, x2), i| {
|
||||
Tui::map(ArrangerTrack::with_widths(self.tracks), |(_, track, x1, x2), i| {
|
||||
let (w, h) = (ArrangerTrack::MIN_WIDTH.max(x2 - x1), HEADER_H);
|
||||
let color = track.color();
|
||||
let input = Self::format_input(track);
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ pub enum FileBrowserCommand {
|
|||
Chdir(PathBuf),
|
||||
Filter(String),
|
||||
}
|
||||
render!(Tui: (self: FileBrowser) => Stack::down(|add|{
|
||||
render!(Tui: (self: FileBrowser) => /*Stack::down(|add|{
|
||||
let mut i = 0;
|
||||
for (_, name) in self.dirs.iter() {
|
||||
if i >= self.scroll {
|
||||
|
|
@ -36,7 +36,7 @@ render!(Tui: (self: FileBrowser) => Stack::down(|add|{
|
|||
}
|
||||
add(&format!("{}/{i}", self.index))?;
|
||||
Ok(())
|
||||
}));
|
||||
})*/"todo");
|
||||
impl FileBrowser {
|
||||
pub fn new (cwd: Option<PathBuf>) -> Usually<Self> {
|
||||
let cwd = if let Some(cwd) = cwd { cwd } else { std::env::current_dir()? };
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ render!(Tui: (self: GrooveboxSamples<'a>) => {
|
|||
let note_lo = self.0.editor.note_lo().load(Relaxed);
|
||||
let note_pt = self.0.editor.note_point();
|
||||
let note_hi = self.0.editor.note_hi();
|
||||
Fill::xy(Coll::map((note_lo..=note_hi).rev(), move|note, i| {
|
||||
Fill::xy(Tui::map((note_lo..=note_hi).rev(), move|note, i| {
|
||||
let mut bg = if note == note_pt { TuiTheme::g(64) } else { Color::Reset };
|
||||
let mut fg = TuiTheme::g(160);
|
||||
if let Some((index, _)) = self.0.sampler.recording {
|
||||
|
|
|
|||
|
|
@ -216,10 +216,10 @@ render!(Tui: (self: PoolView<'a>) => {
|
|||
let color = self.0.phrase().read().unwrap().color;
|
||||
let border = Fill::xy(Outer(Style::default().fg(color.base.rgb).bg(bg)));
|
||||
let enclose = |x|lay!(border, Padding::xy(0, 1, Tui::bg(bg, x)));
|
||||
let content = Cond::either(
|
||||
let content = Tui::either(
|
||||
self.0.file_picker().is_some(),
|
||||
Thunk::new(||self.0.file_picker().unwrap()),
|
||||
Thunk::new(||Coll::map(phrases.iter(), |phrase, i|{
|
||||
Thunk::new(||Tui::map(phrases.iter(), |phrase, i|{
|
||||
let MidiClip { ref name, color, length, .. } = *phrase.read().unwrap();
|
||||
let mut length = PhraseLength::new(length, None);
|
||||
if let Some(PoolMode::Length(phrase, new_length, focus)) = self.0.mode {
|
||||
|
|
@ -243,7 +243,7 @@ render!(Tui: (self: PoolView<'a>) => {
|
|||
row2
|
||||
}),
|
||||
)));
|
||||
Push::y(i as u16 * 2, lay!(clip, Cond::when(i == self.0.phrase_index(), CORNERS)))
|
||||
Push::y(i as u16 * 2, lay!(clip, Tui::when(i == self.0.phrase_index(), CORNERS)))
|
||||
})));
|
||||
enclose(lay!(
|
||||
//add(&Lozenge(Style::default().bg(border_bg).fg(border_color)))?;
|
||||
|
|
|
|||
|
|
@ -56,12 +56,12 @@ render!(Tui: (self: SequencerTui) => {
|
|||
p.as_ref().map(|p|p.read().unwrap().color)
|
||||
).flatten().clone();
|
||||
|
||||
let toolbar = Cond::when(self.transport, row!(
|
||||
let toolbar = Tui::when(self.transport, row!(
|
||||
PlayPause(self.clock.is_rolling()),
|
||||
TransportView::new(self, color, true),
|
||||
));
|
||||
|
||||
let play_queue = Cond::when(self.selectors, row!(
|
||||
let play_queue = Tui::when(self.selectors, row!(
|
||||
PhraseSelector::play_phrase(&self.player),
|
||||
PhraseSelector::next_phrase(&self.player),
|
||||
));
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ render!(Tui: (self: TransportField) => row!(
|
|||
pub struct PlayPause(pub bool);
|
||||
render!(Tui: (self: PlayPause) => Tui::bg(
|
||||
if self.0{Color::Rgb(0,128,0)}else{Color::Rgb(128,64,0)},
|
||||
Fixed::x(5, Cond::either(self.0, Tui::fg(Color::Rgb(0, 255, 0), col!(
|
||||
Fixed::x(5, Tui::either(self.0, Tui::fg(Color::Rgb(0, 255, 0), col!(
|
||||
" 🭍🭑🬽 ",
|
||||
" 🭞🭜🭘 ",
|
||||
)), Tui::fg(Color::Rgb(255, 128, 0), col!(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue