diff --git a/engine/src/output.rs b/engine/src/output.rs index 4becba8b..2433140d 100644 --- a/engine/src/output.rs +++ b/engine/src/output.rs @@ -8,7 +8,7 @@ pub trait Output { fn area (&self) -> E::Area; /// Mutable pointer to area fn area_mut (&mut self) -> &mut E::Area; - ///// Render widget in area + /// Render widget in area fn place (&mut self, area: E::Area, content: &impl Content); #[inline] fn x (&self) -> E::Unit { self.area().x() } diff --git a/engine/src/tui/tui_output.rs b/engine/src/tui/tui_output.rs index 876bad8d..bb11a0f2 100644 --- a/engine/src/tui/tui_output.rs +++ b/engine/src/tui/tui_output.rs @@ -9,14 +9,10 @@ impl Output for TuiOut { #[inline] fn area (&self) -> [u16;4] { self.area } #[inline] fn area_mut (&mut self) -> &mut [u16;4] { &mut self.area } #[inline] fn place (&mut self, area: [u16;4], content: &impl Content) { - let last = self.area().xywh().clone(); - //panic!("a {last:?} {area:?} {:?}", self.area); - *self.area_mut() = area.xywh().clone(); - //panic!("b {last:?} {area:?} {:?}", self.area); + let last = self.area(); + *self.area_mut() = area; content.render(self); - //panic!("c {last:?} {area:?} {:?}", self.area); *self.area_mut() = last; - //panic!("placed"); } } diff --git a/layout/src/align.rs b/layout/src/align.rs index 01b50f8a..7135643a 100644 --- a/layout/src/align.rs +++ b/layout/src/align.rs @@ -1,59 +1,77 @@ use crate::*; -#[derive(Default, Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Default)] pub enum Alignment { #[default] Center, X, Y, NW, N, NE, E, SE, S, SW, W } pub struct Align>(Alignment, T, PhantomData); impl> Align { - pub fn c (w: T) -> Self { Self(Alignment::Center, w, Default::default()) } - pub fn x (w: T) -> Self { Self(Alignment::X, w, Default::default()) } - pub fn y (w: T) -> Self { Self(Alignment::Y, w, Default::default()) } - pub fn n (w: T) -> Self { Self(Alignment::N, w, Default::default()) } - pub fn s (w: T) -> Self { Self(Alignment::S, w, Default::default()) } - pub fn e (w: T) -> Self { Self(Alignment::E, w, Default::default()) } - pub fn w (w: T) -> Self { Self(Alignment::W, w, Default::default()) } - pub fn nw (w: T) -> Self { Self(Alignment::NW, w, Default::default()) } - pub fn sw (w: T) -> Self { Self(Alignment::SW, w, Default::default()) } - pub fn ne (w: T) -> Self { Self(Alignment::NE, w, Default::default()) } - pub fn se (w: T) -> Self { Self(Alignment::SE, w, Default::default()) } + pub fn c (a: T) -> Self { Self(Alignment::Center, a, Default::default()) } + pub fn x (a: T) -> Self { Self(Alignment::X, a, Default::default()) } + pub fn y (a: T) -> Self { Self(Alignment::Y, a, Default::default()) } + pub fn n (a: T) -> Self { Self(Alignment::N, a, Default::default()) } + pub fn s (a: T) -> Self { Self(Alignment::S, a, Default::default()) } + pub fn e (a: T) -> Self { Self(Alignment::E, a, Default::default()) } + pub fn w (a: T) -> Self { Self(Alignment::W, a, Default::default()) } + pub fn nw (a: T) -> Self { Self(Alignment::NW, a, Default::default()) } + pub fn sw (a: T) -> Self { Self(Alignment::SW, a, Default::default()) } + pub fn ne (a: T) -> Self { Self(Alignment::NE, a, Default::default()) } + pub fn se (a: T) -> Self { Self(Alignment::SE, a, Default::default()) } } impl> Content for Align { - fn layout (&self, outer: E::Area) -> E::Area { - align_areas(self.0, outer.xywh(), Content::layout(&self.content(), outer).xywh()).into() + fn content (&self) -> impl Content { + &self.1 + } + fn layout (&self, on: E::Area) -> E::Area { + use Alignment::*; + let it = self.content().layout(on).xywh(); + let centered = on.center_xy(it.wh()); + let far_x = (on.x() + on.w()).minus(it.w()); + let far_y = (on.y() + on.h()).minus(it.h()); + let [x, y] = match self.0 { + NW => [on.x(), on.y()], + N => [centered.x(), on.y()], + NE => [far_x, on.y()], + E => [far_x, centered.y()], + SE => [far_x, far_y ], + S => [centered.x(), far_y ], + SW => [on.x(), far_y ], + W => [on.x(), centered.y()], + + Center => centered.xy(), + + X => [centered.x(), it.y()], + Y => [it.x(), centered.y()], + }; + [x, y, centered.w(), centered.h()].into() + //let [cfx, cfy, ..] = on.center(); + //let [cmx, cmy, ..] = it.center(); + ////let center = |cf, cm, m|if cf >= cm { m + (cf - cm) } else { m.minus(cm - cf) }; + //let center_x = center(cfx, cmx, it.x()); + //let center_y = center(cfy, cmy, it.y()); + //let east_x = on.x() + on.w().minus(it.w()); + //let south_y = on.y() + on.h().minus(it.h()); + //let [x, y] = match self.0 { + //Alignment::X => [center_x, it.y(), ], + //Alignment::Y => [it.x(), center_y,], + + //Alignment::NW => [on.x(), on.y(), ], + //Alignment::N => [center_x, on.y(), ], + //Alignment::NE => [east_x, on.y(), ], + //Alignment::E => [east_x, center_y,], + //Alignment::SE => [east_x, south_y, ], + //Alignment::S => [center_x, south_y, ], + //Alignment::SW => [on.x(), south_y, ], + //Alignment::W => [on.x(), center_y,], + //}; + //[x, y, it.w(), it.h()].into() } fn render (&self, render: &mut E::Output) { render.place(self.layout(render.area()), &self.content()) } } -pub fn align_areas(alignment: Alignment, on: [N;4], it: [N;4]) -> [N;4] { - let [cfx, cfy, ..] = on.center(); - let [cmx, cmy, ..] = it.center(); - let center = |cf, cm, m: N|if cf >= cm { m + (cf - cm) } else { m.minus(cm - cf) }; - let center_x = center(cfx, cmx, it.x()); - let center_y = center(cfy, cmy, it.y()); - let east_x = on.x() + on.w().minus(it.w()); - let south_y = on.y() + on.h().minus(it.h()); - let [x, y] = match alignment { - Alignment::Center => [center_x, center_y,], - - Alignment::X => [center_x, it.y(), ], - Alignment::Y => [it.x(), center_y,], - - Alignment::NW => [on.x(), on.y(), ], - Alignment::N => [center_x, on.y(), ], - Alignment::NE => [east_x, on.y(), ], - Alignment::E => [east_x, center_y,], - Alignment::SE => [east_x, south_y, ], - Alignment::S => [center_x, south_y, ], - Alignment::SW => [on.x(), south_y, ], - Alignment::W => [on.x(), center_y,], - }; - [x, y, it.w(), it.h()] -} - //fn align, N: Coordinate, R: Area + From<[N;4]>> (align: &Align, outer: R, content: R) -> Option { //if outer.w() < content.w() || outer.h() < content.h() { //None diff --git a/layout/src/lib.rs b/layout/src/lib.rs index e57aee7d..f3fb9d25 100644 --- a/layout/src/lib.rs +++ b/layout/src/lib.rs @@ -14,17 +14,29 @@ pub(crate) use std::marker::PhantomData; #[cfg(test)] #[test] fn test_layout () -> Usually<()> { use crate::tui::Tui; let area: [u16;4] = [10, 10, 20, 20]; - assert_eq!(Content::::layout(&(), area), - [20, 20, 0, 0]); - assert_eq!(Fill::::x(()).layout(area), - [10, 20, 20, 0]); - assert_eq!(Fill::::y(()).layout(area), - [20, 10, 0, 20]); - assert_eq!(Fill::::xy(()).layout(area), - area); - assert_eq!(Fixed::::xy(4, 4, ()).layout(area), - [18, 18, 4, 4]); - assert_eq!(Align::::c(()).layout(area), - [20, 20, 0, 0]); + + let unit = (); + + assert_eq!(Content::::layout(&unit, area), [20, 20, 0, 0]); + + assert_eq!(Fill::::x(unit).layout(area), [10, 20, 20, 0]); + assert_eq!(Fill::::y(unit).layout(area), [20, 10, 0, 20]); + assert_eq!(Fill::::xy(unit).layout(area), area); + + assert_eq!(Fixed::::x(4, unit).layout(area), [18, 20, 4, 0]); + assert_eq!(Fixed::::y(4, unit).layout(area), [20, 18, 0, 4]); + assert_eq!(Fixed::::xy(4, 4, unit).layout(area), [18, 18, 4, 4]); + + let four = ||Fixed::::xy(4, 4, unit); + + assert_eq!(Align::nw(four()).layout(area), [10, 10, 4, 4]); + assert_eq!(Align::n(four()).layout(area), [18, 10, 4, 4]); + assert_eq!(Align::ne(four()).layout(area), [26, 10, 4, 4]); + assert_eq!(Align::e(four()).layout(area), [26, 18, 4, 4]); + assert_eq!(Align::se(four()).layout(area), [26, 26, 4, 4]); + assert_eq!(Align::s(four()).layout(area), [18, 26, 4, 4]); + assert_eq!(Align::sw(four()).layout(area), [10, 26, 4, 4]); + assert_eq!(Align::w(four()).layout(area), [10, 18, 4, 4]); + Ok(()) } diff --git a/layout/src/ops.rs b/layout/src/ops.rs index 5e9bea67..3220ee4c 100644 --- a/layout/src/ops.rs +++ b/layout/src/ops.rs @@ -94,24 +94,30 @@ impl Content for Map where { fn layout (&self, area: E::Area) -> E::Area { let mut index = 0; - let mut max_w = 0; - let mut max_h = 0; + let [mut min_x, mut min_y] = area.center(); + let [mut max_x, mut max_y] = area.center(); for item in (self.1)() { - let [x, y, w, h] = (self.2)(item, index).layout(area).xywh(); - max_w = max_w.max((x + w).into()); - max_h = max_h.max((y + h).into()); + let area = (self.2)(item, index).layout(area).xywh(); + let [x,y,w,h] = area.xywh(); + min_x = min_x.min(x.into()); + min_y = min_y.min(y.into()); + max_x = max_x.max((x + w).into()); + max_y = max_y.max((y + h).into()); index += 1; } - align_areas( - Alignment::Center, - area.xywh(), - [0.into(), 0.into(), max_w.into(), max_h.into()] - ).into() + let w = max_x - min_x; + let h = max_y - min_y; + //[min_x.into(), min_y.into(), w.into(), h.into()].into() + area.center_xy([w.into(), h.into()].into()).into() } fn render (&self, to: &mut E::Output) { let mut index = 0; + let area = self.layout(to.area()); + //panic!("{area:?}"); + //to.blit(&format!("{area:?}"), 0, 0, None); for item in (self.1)() { - (self.2)(item, index).render(to); + let item = (self.2)(item, index); + to.place(area.into(), &item); index += 1; } } diff --git a/layout/src/transform_xy_unit.rs b/layout/src/transform_xy_unit.rs index 435fbf09..1b400c5e 100644 --- a/layout/src/transform_xy_unit.rs +++ b/layout/src/transform_xy_unit.rs @@ -42,16 +42,16 @@ macro_rules! transform_xy_unit { } transform_xy_unit!(|self: Fixed, area|{ - let [x, y, w, h] = self.content().layout(area.center_xy(match self { + let [w, h] = self.content().layout(area.center_xy(match self { Self::X(fw, _) => [*fw, area.h()], Self::Y(fh, _) => [area.w(), *fh], Self::XY(fw, fh, _) => [*fw, *fh], - }).into()).xywh(); - match self { - Self::X(fw, _) => [x, y, *fw, h], - Self::Y(fh, _) => [x, y, w, *fh], - Self::XY(fw, fh, _) => [x, y, *fw, *fh], - } + }).into()).wh(); + area.center_xy(match self { + Self::X(fw, _) => [*fw, h], + Self::Y(fh, _) => [w, *fh], + Self::XY(fw, fh, _) => [*fw, *fh], + }) }); transform_xy_unit!(|self: Shrink, area|self.content().layout([ @@ -78,10 +78,9 @@ transform_xy_unit!(|self: Max, area|{ Self::XY(mw, mh, _) => [area.x(), area.y(), area.w().min(*mw), area.h().min(*mh)], }}); -transform_xy_unit!(|self: Push, area|{ - let area = self.content().layout(area); - [area.x() + self.dx(), area.y() + self.dy(), area.w(), area.h()] -}); +transform_xy_unit!(|self: Push, area|self.content().layout([ + area.x() + self.dx(), area.y() + self.dy(), area.w(), area.h() +].into())); transform_xy_unit!(|self: Pull, area|{ let area = self.content().layout(area); diff --git a/src/pool.rs b/src/pool.rs index f7982dbf..597a7a7e 100644 --- a/src/pool.rs +++ b/src/pool.rs @@ -208,16 +208,18 @@ impl PoolModel { pub struct PoolView<'a>(pub(crate) &'a PoolModel); // TODO: Display phrases always in order of appearance render!(Tui: (self: PoolView<'a>) => { + //let content = "..."; + //let content = Tui::map(||["abc", "def", "ghi"].iter(), |item, index|Push::y(index as u16, item)); let PoolModel { phrases, mode, .. } = self.0; - let bg = TuiTheme::g(32); - let title_color = TuiTheme::ti1(); - let upper_left = "Pool:"; - let upper_right = format!("({})", phrases.len()); - let color = self.0.phrase().read().unwrap().color; - let with_files = |x|Tui::either(self.0.file_picker().is_some(), - Thunk::new(||self.0.file_picker().unwrap()), - Thunk::new(x)); - let content = with_files(||Tui::map(||phrases.iter(), |clip, i|{ + //let bg = TuiTheme::g(32); + //let title_color = TuiTheme::ti1(); + //let upper_left = "Pool:"; + //let upper_right = format!("({})", phrases.len()); + //let color = self.0.phrase().read().unwrap().color; + ////let with_files = |x|Tui::either(self.0.file_picker().is_some(), + ////Thunk::new(||self.0.file_picker().unwrap()), + ////Thunk::new(x)); + let content = Tui::map(||phrases.iter(), |clip, i|{ let MidiClip { ref name, color, length, .. } = *clip.read().unwrap(); //let mut length = PhraseLength::new(length, None); //if let Some(PoolMode::Length(clip, new_length, focus)) = self.0.mode { @@ -226,8 +228,10 @@ render!(Tui: (self: PoolView<'a>) => { //length.focus = Some(focus); //} //} - Push::y(1 + i as u16 * 2, Fill::x(Fixed::y(2, Tui::bg(color.base.rgb, - format!(" {i} {name} {length} ")))))/*, + Push::y(i as u16, format!(" {i} {name} {length} "))/* + //format!(" {i} {name} {length} ")[> + //Push::y(i as u16 * 2, Fixed::y(2, Tui::bg(color.base.rgb, Fill::x( + //format!(" {i} {name} {length} ")))))[>, name.clone()))))Bsp::s( Fill::x(Bsp::a( Align::w(format!(" {i}")), @@ -243,8 +247,8 @@ render!(Tui: (self: PoolView<'a>) => { row2 }), ))))//lay!(clip, Tui::when(i == self.0.clip_index(), CORNERS)))*/ - })); - content + }); + Tui::bg(Color::Red, content) //let border = Outer(Style::default().fg(color.base.rgb).bg(color.dark.rgb)); //let enclose = |x|lay!( //Fill::xy(border), diff --git a/src/transport.rs b/src/transport.rs index fabd1b6d..1055fe96 100644 --- a/src/transport.rs +++ b/src/transport.rs @@ -101,18 +101,18 @@ render!(Tui: (self: TransportView) => { Tui::fg_bg(color.lightest.rgb, color.darkest.rgb, format!("{:>10}", value)), Tui::fg_bg(color.darkest.rgb, color.base.rgb, "▌"), ); - Bsp::e( - Fixed::x(17, col!( + Fixed::x(40, Tui::bg(Color::Red, Bsp::e( + Tui::bg(Color::Green, Fixed::x(18, col!( transport_field(" Beat", self.beat.clone()), transport_field(" Time", format!("{:.1}s", self.current_second)), transport_field(" BPM", self.bpm.clone()), - )), - Fixed::x(17, col!( + ))), + Tui::bg(Color::Blue, Fixed::x(18, col!( transport_field(" Rate", format!("{}", self.sr)), transport_field(" Chunk", format!("{}", self.chunk)), transport_field(" Lag", format!("{:.3}ms", self.latency)), - )), - ) + ))), + ))) }); impl HasFocus for TransportTui { type Item = TransportFocus;