mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 03:36:41 +01:00
wip: big flat pt.11, down to 12, update literal render macro
This commit is contained in:
parent
a0175dabc8
commit
b718e54d33
9 changed files with 302 additions and 239 deletions
|
|
@ -11,27 +11,88 @@ pub trait Output<E: Engine> {
|
||||||
fn render_in (&mut self, area: E::Area, widget: &impl Render<E>) -> Usually<()>;
|
fn render_in (&mut self, area: E::Area, widget: &impl Render<E>) -> Usually<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Something that can be represented by a renderable component.
|
||||||
|
pub trait Content<E: Engine>: Send + Sync {
|
||||||
|
fn content (&self) -> Option<impl Render<E>>;
|
||||||
|
}
|
||||||
|
impl<E: Engine, C: Content<E>> Content<E> for &C {
|
||||||
|
fn content (&self) -> Option<impl Render<E>> {
|
||||||
|
(*self).content()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Something that writes to an [Output].
|
||||||
|
pub trait Render<E: Engine>: Send + Sync {
|
||||||
|
/// Minimum size to use
|
||||||
|
fn min_size (&self, _: E::Size) -> Perhaps<E::Size> {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
/// Draw to output render target
|
||||||
|
fn render (&self, _: &mut E::Output) -> Usually<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<E: Engine> Render<E> for &dyn Render<E> {
|
||||||
|
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> { (*self).min_size(to) }
|
||||||
|
fn render (&self, to: &mut E::Output) -> Usually<()> { (*self).render(to) }
|
||||||
|
}
|
||||||
|
impl<E: Engine, R: Render<E>> Render<E> for &R {
|
||||||
|
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> { (*self).min_size(to) }
|
||||||
|
fn render (&self, to: &mut E::Output) -> Usually<()> { (*self).render(to) }
|
||||||
|
}
|
||||||
|
impl<E: Engine, R: Render<E>> Render<E> for Option<R> {
|
||||||
|
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
|
||||||
|
if let Some(content) = self {
|
||||||
|
content.min_size(to)
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn render (&self, to: &mut E::Output) -> Usually<()> {
|
||||||
|
if let Some(content) = self {
|
||||||
|
content.render(to)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Define custom content for a struct.
|
/// Define custom content for a struct.
|
||||||
|
///
|
||||||
|
/// This code wires the `Content` and `Render` traits together,
|
||||||
|
/// since the only way to have the cake and eat it too is by
|
||||||
|
/// implementing both traits for a given renderable.
|
||||||
#[macro_export] macro_rules! render {
|
#[macro_export] macro_rules! render {
|
||||||
|
|
||||||
// Implement for all engines
|
// Implement from [Content] for all [Engine]s
|
||||||
(|$self:ident:$Struct:ident$(<$($L:lifetime),*E$(,$T:ident$(:$U:path)?)*$(,)?>)?|$cb:expr) => {
|
(|$self:ident:$Struct:ident$(<$($L:lifetime),*E$(,$T:ident$(:$U:path)?)*$(,)?>)?|$cb:expr) => {
|
||||||
impl<E: Engine, $($($L),*$($T $(: $U)?),*)?> Content<E> for $Struct $(<$($L,)* E, $($T),*>)? {
|
impl<E: Engine, $($($L),*$($T $(: $U)?),*)?> Content<E> for $Struct $(<$($L,)* E, $($T),*>)? {
|
||||||
fn content (&$self) -> Option<impl Render<$E>> {
|
fn content (&$self) -> Option<impl Render<$E>> { Some($cb) }
|
||||||
Some($cb)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
impl<E: Engine, $($($L),*$($T $(: $U)?),*)?> Render<E> for $Struct $(<$($L,)* E, $($T),*>)? {
|
impl<E: Engine, $($($L),*$($T $(: $U)?),*)?> Render<E> for $Struct $(<$($L,)* E, $($T),*>)? {
|
||||||
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
|
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
|
||||||
self.content().map(|content|content.min_size(to)).unwrap_or(Ok(None))
|
self.content().unwrap().min_size(to)
|
||||||
}
|
}
|
||||||
fn render (&self, to: &mut E::Output) -> Usually<()> {
|
fn render (&self, to: &mut E::Output) -> Usually<()> {
|
||||||
self.content().map(|content|content.render(to)).unwrap_or(Ok(()))
|
self.content().unwrap().render(to)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Implement for a specific engine
|
// Implement from [min_size] and [render] callbacks for all engines
|
||||||
|
($Struct:ident$(<$($L:lifetime),*E$(,$T:ident$(:$U:path)?)*$(,)?>)?
|
||||||
|
|$self1:ident, $to1:ident|$min_size:expr,
|
||||||
|
|$self2:ident, $to2:ident|$render:expr) =>
|
||||||
|
{
|
||||||
|
impl<E: Engine, $($($L),*$($T $(: $U)?),*)?> Content<E> for $Struct $(<$($L,)* E, $($T),*>)? {
|
||||||
|
fn content (&self) -> Option<impl Render<E>> { Some(self) }
|
||||||
|
}
|
||||||
|
impl<E: Engine, $($($L),*$($T $(: $U)?),*)?> Render<E> for $Struct $(<$($L,)* E, $($T),*>)? {
|
||||||
|
fn min_size (&$self1, $to1: E::Size) -> Perhaps<E::Size> { $min_size }
|
||||||
|
fn render (&$self2, $to2: &mut E::Output) -> Usually<()> { $render }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Implement from [Content] for a particular [Engine]
|
||||||
(<$E:ty>|$self:ident:$Struct:ident$(<
|
(<$E:ty>|$self:ident:$Struct:ident$(<
|
||||||
$($($L:lifetime),+)?
|
$($($L:lifetime),+)?
|
||||||
$($($T:ident$(:$U:path)?),+)?
|
$($($T:ident$(:$U:path)?),+)?
|
||||||
|
|
@ -61,45 +122,35 @@ pub trait Output<E: Engine> {
|
||||||
self.content().map(|content|content.render(to)).unwrap_or(Ok(()))
|
self.content().map(|content|content.render(to)).unwrap_or(Ok(()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Implement from [min_size] and [render] callbacks for a particular [Engine]
|
||||||
|
(<$E:ty>($self:ident:$Struct:ident$(<$($L:lifetime),*$(,$T:ident$(:$U:path)?)*$(,)?>)?)
|
||||||
|
|$to1:ident|$min_size:expr, |$to2:ident|$render:expr) =>
|
||||||
|
{
|
||||||
|
impl $(<
|
||||||
|
$($L),* $($($T$(:$U)?),+)?
|
||||||
|
>)? Content<$E> for $Struct $(<
|
||||||
|
$($L),* $($($T),+)?
|
||||||
|
>)? {
|
||||||
|
fn content (&self) -> Option<impl Render<$E>> { Some(self) }
|
||||||
|
}
|
||||||
|
impl $(<
|
||||||
|
$($L),* $($($T$(:$U)?),+)?
|
||||||
|
>)? Render<$E> for $Struct $(<
|
||||||
|
$($L),* $($($T),+)?
|
||||||
|
>)? {
|
||||||
|
fn min_size (&$self, $to1: <$E as Engine>::Size) -> Perhaps<<$E as Engine>::Size> {
|
||||||
|
$min_size
|
||||||
|
}
|
||||||
|
fn render (&$self, $to2: &mut <$E as Engine>::Output) -> Usually<()> {
|
||||||
|
$render
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write content to output buffer.
|
|
||||||
pub trait Render<E: Engine>: Send + Sync {
|
|
||||||
/// Minimum size to use
|
|
||||||
fn min_size (&self, _: E::Size) -> Perhaps<E::Size> {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
/// Draw to output render target
|
|
||||||
fn render (&self, _: &mut E::Output) -> Usually<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Engine> Render<E> for &dyn Render<E> {}
|
|
||||||
//impl<E: Engine> Render<E> for &mut dyn Render<E> {}
|
|
||||||
//impl<E: Engine> Render<E> for Box<dyn Render<E>> {}
|
|
||||||
impl<E: Engine, R: Render<E>> Render<E> for &R {}
|
|
||||||
//impl<E: Engine, R: Render<E>> Render<E> for &mut R {}
|
|
||||||
impl<E: Engine, R: Render<E>> Render<E> for Option<R> {}
|
|
||||||
//impl<E: Engine, R: Render<E>> Render<E> for Arc<R> {}
|
|
||||||
//impl<E: Engine, R: Render<E>> Render<E> for Mutex<R> {}
|
|
||||||
//impl<E: Engine, R: Render<E>> Render<E> for RwLock<R> {}
|
|
||||||
|
|
||||||
/// Something that can be represented by a renderable component.
|
|
||||||
pub trait Content<E: Engine>: Send + Sync {
|
|
||||||
fn content (&self) -> Option<impl Render<E>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
//impl<E: Engine> Content<E> for &dyn Render<E> {}
|
|
||||||
//impl<E: Engine> Content<E> for &mut dyn Render<E> {}
|
|
||||||
//impl<E: Engine> Content<E> for Box<dyn Render<E>> {}
|
|
||||||
impl<E: Engine, C: Content<E>> Content<E> for &C {
|
|
||||||
fn content (&self) -> Option<impl Render<E>> {
|
|
||||||
(*self).content()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//impl<E: Engine, C: Content<E>> Content<E> for &mut C {}
|
//impl<E: Engine, C: Content<E>> Content<E> for &mut C {}
|
||||||
//impl<E: Engine, C: Content<E>> Content<E> for Option<C> {}
|
//impl<E: Engine, C: Content<E>> Content<E> for Option<C> {}
|
||||||
//impl<E: Engine, C: Content<E>> Content<E> for Arc<C> {}
|
//impl<E: Engine, C: Content<E>> Content<E> for Arc<C> {}
|
||||||
|
|
|
||||||
|
|
@ -25,57 +25,59 @@ from!(|args:(&ArrangerTui, usize)|ArrangerVCursor = Self {
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
render!(<Tui>|self: ArrangerVCursor|render(move|to: &mut TuiOutput|{
|
render!(<Tui>(self: ArrangerVCursor)
|
||||||
let area = to.area();
|
|layout|Ok([0, 0]),
|
||||||
let focused = true;
|
|render|{
|
||||||
let selected = self.selected;
|
let area = render.area();
|
||||||
let get_track_area = |t: usize| [
|
let focused = true;
|
||||||
self.scenes_w + area.x() + self.cols[t].1 as u16, area.y(),
|
let selected = self.selected;
|
||||||
self.cols[t].0 as u16, area.h(),
|
let get_track_area = |t: usize| [
|
||||||
];
|
self.scenes_w + area.x() + self.cols[t].1 as u16, area.y(),
|
||||||
let get_scene_area = |s: usize| [
|
self.cols[t].0 as u16, area.h(),
|
||||||
area.x(), HEADER_H + area.y() + (self.rows[s].1 / PPQ) as u16,
|
];
|
||||||
area.w(), (self.rows[s].0 / PPQ) as u16
|
let get_scene_area = |s: usize| [
|
||||||
];
|
area.x(), HEADER_H + area.y() + (self.rows[s].1 / PPQ) as u16,
|
||||||
let get_clip_area = |t: usize, s: usize| [
|
area.w(), (self.rows[s].0 / PPQ) as u16
|
||||||
(self.scenes_w + area.x() + self.cols[t].1 as u16).saturating_sub(1),
|
];
|
||||||
HEADER_H + area.y() + (self.rows[s].1/PPQ) as u16,
|
let get_clip_area = |t: usize, s: usize| [
|
||||||
self.cols[t].0 as u16 + 2,
|
(self.scenes_w + area.x() + self.cols[t].1 as u16).saturating_sub(1),
|
||||||
(self.rows[s].0 / PPQ) as u16
|
HEADER_H + area.y() + (self.rows[s].1/PPQ) as u16,
|
||||||
];
|
self.cols[t].0 as u16 + 2,
|
||||||
let mut track_area: Option<[u16;4]> = None;
|
(self.rows[s].0 / PPQ) as u16
|
||||||
let mut scene_area: Option<[u16;4]> = None;
|
];
|
||||||
let mut clip_area: Option<[u16;4]> = None;
|
let mut track_area: Option<[u16;4]> = None;
|
||||||
let area = match selected {
|
let mut scene_area: Option<[u16;4]> = None;
|
||||||
ArrangerSelection::Mix => area,
|
let mut clip_area: Option<[u16;4]> = None;
|
||||||
ArrangerSelection::Track(t) => {
|
let area = match selected {
|
||||||
track_area = Some(get_track_area(t));
|
ArrangerSelection::Mix => area,
|
||||||
area
|
ArrangerSelection::Track(t) => {
|
||||||
},
|
track_area = Some(get_track_area(t));
|
||||||
ArrangerSelection::Scene(s) => {
|
area
|
||||||
scene_area = Some(get_scene_area(s));
|
},
|
||||||
area
|
ArrangerSelection::Scene(s) => {
|
||||||
},
|
scene_area = Some(get_scene_area(s));
|
||||||
ArrangerSelection::Clip(t, s) => {
|
area
|
||||||
track_area = Some(get_track_area(t));
|
},
|
||||||
scene_area = Some(get_scene_area(s));
|
ArrangerSelection::Clip(t, s) => {
|
||||||
clip_area = Some(get_clip_area(t, s));
|
track_area = Some(get_track_area(t));
|
||||||
area
|
scene_area = Some(get_scene_area(s));
|
||||||
},
|
clip_area = Some(get_clip_area(t, s));
|
||||||
};
|
area
|
||||||
let bg = self.color.lighter.rgb;//Color::Rgb(0, 255, 0);
|
},
|
||||||
if let Some([x, y, width, height]) = track_area {
|
};
|
||||||
to.fill_fg([x, y, 1, height], bg);
|
let bg = self.color.lighter.rgb;//Color::Rgb(0, 255, 0);
|
||||||
to.fill_fg([x + width, y, 1, height], bg);
|
if let Some([x, y, width, height]) = track_area {
|
||||||
}
|
render.fill_fg([x, y, 1, height], bg);
|
||||||
if let Some([_, y, _, height]) = scene_area {
|
render.fill_fg([x + width, y, 1, height], bg);
|
||||||
to.fill_ul([area.x(), y - 1, area.w(), 1], bg);
|
}
|
||||||
to.fill_ul([area.x(), y + height - 1, area.w(), 1], bg);
|
if let Some([_, y, _, height]) = scene_area {
|
||||||
}
|
render.fill_ul([area.x(), y - 1, area.w(), 1], bg);
|
||||||
Ok(if focused {
|
render.fill_ul([area.x(), y + height - 1, area.w(), 1], bg);
|
||||||
to.render_in(if let Some(clip_area) = clip_area { clip_area }
|
}
|
||||||
else if let Some(track_area) = track_area { track_area.clip_h(HEADER_H) }
|
Ok(if focused {
|
||||||
else if let Some(scene_area) = scene_area { scene_area.clip_w(self.scenes_w) }
|
render.render_in(if let Some(clip_area) = clip_area { clip_area }
|
||||||
else { area.clip_w(self.scenes_w).clip_h(HEADER_H) }, &self.reticle)?
|
else if let Some(track_area) = track_area { track_area.clip_h(HEADER_H) }
|
||||||
})
|
else if let Some(scene_area) = scene_area { scene_area.clip_w(self.scenes_w) }
|
||||||
}));
|
else { area.clip_w(self.scenes_w).clip_h(HEADER_H) }, &self.reticle)?
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -6,43 +6,41 @@ pub struct ArrangerVColSep {
|
||||||
cols: Vec<(usize, usize)>,
|
cols: Vec<(usize, usize)>,
|
||||||
scenes_w: u16
|
scenes_w: u16
|
||||||
}
|
}
|
||||||
|
|
||||||
from!(|state:&ArrangerTui|ArrangerVColSep = Self {
|
from!(|state:&ArrangerTui|ArrangerVColSep = Self {
|
||||||
fg: TuiTheme::separator_fg(false),
|
fg: TuiTheme::separator_fg(false),
|
||||||
cols: ArrangerTrack::widths(&state.tracks),
|
cols: ArrangerTrack::widths(&state.tracks),
|
||||||
scenes_w: SCENES_W_OFFSET + ArrangerScene::longest_name(&state.scenes) as u16,
|
scenes_w: SCENES_W_OFFSET + ArrangerScene::longest_name(&state.scenes) as u16,
|
||||||
});
|
});
|
||||||
|
render!(<Tui>(self: ArrangerVColSep)
|
||||||
render!(<Tui>|self: ArrangerVColSep|render(move|to: &mut TuiOutput|{
|
|layout|Ok(Some([0, 0])),
|
||||||
let style = Some(Style::default().fg(self.fg));
|
|render|{
|
||||||
Ok(for x in self.cols.iter().map(|col|col.1) {
|
let style = Some(Style::default().fg(self.fg));
|
||||||
let x = self.scenes_w + to.area().x() + x as u16;
|
Ok(for x in self.cols.iter().map(|col|col.1) {
|
||||||
for y in to.area().y()..to.area().y2() {
|
let x = self.scenes_w + render.area().x() + x as u16;
|
||||||
to.blit(&"▎", x, y, style);
|
for y in render.area().y()..render.area().y2() {
|
||||||
}
|
render.blit(&"▎", x, y, style);
|
||||||
})
|
}
|
||||||
}));
|
})
|
||||||
|
});
|
||||||
|
|
||||||
pub struct ArrangerVRowSep {
|
pub struct ArrangerVRowSep {
|
||||||
fg: Color,
|
fg: Color,
|
||||||
rows: Vec<(usize, usize)>,
|
rows: Vec<(usize, usize)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
from!(|args:(&ArrangerTui, usize)|ArrangerVRowSep = Self {
|
from!(|args:(&ArrangerTui, usize)|ArrangerVRowSep = Self {
|
||||||
fg: TuiTheme::separator_fg(false),
|
fg: TuiTheme::separator_fg(false),
|
||||||
rows: ArrangerScene::ppqs(&args.0.scenes, args.1),
|
rows: ArrangerScene::ppqs(&args.0.scenes, args.1),
|
||||||
});
|
});
|
||||||
|
render!(<Tui>(self: ArrangerVRowSep)
|
||||||
render!(<Tui>|self: ArrangerVRowSep|render(move|to: &mut TuiOutput|{
|
|layout|Ok(Some([0, 0])),
|
||||||
Ok(for y in self.rows.iter().map(|row|row.1) {
|
|render|Ok(for y in self.rows.iter().map(|row|row.1) {
|
||||||
let y = to.area().y() + (y / PPQ) as u16 + 1;
|
let y = render.area().y() + (y / PPQ) as u16 + 1;
|
||||||
if y >= to.buffer.area.height { break }
|
if y >= render.buffer.area.height { break }
|
||||||
for x in to.area().x()..to.area().x2().saturating_sub(2) {
|
for x in render.area().x()..render.area().x2().saturating_sub(2) {
|
||||||
if x < to.buffer.area.x && y < to.buffer.area.y {
|
if x < render.buffer.area.x && y < render.buffer.area.y {
|
||||||
let cell = to.buffer.get_mut(x, y);
|
let cell = render.buffer.get_mut(x, y);
|
||||||
cell.modifier = Modifier::UNDERLINED;
|
cell.modifier = Modifier::UNDERLINED;
|
||||||
cell.underline_color = self.fg;
|
cell.underline_color = self.fg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}));
|
||||||
}));
|
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@ pub(crate) use ::tek_layout::{
|
||||||
tek_engine::{
|
tek_engine::{
|
||||||
Usually, Perhaps,
|
Usually, Perhaps,
|
||||||
Engine, Size, Area,
|
Engine, Size, Area,
|
||||||
Content, Render, render,
|
Output, Content, Render, render,
|
||||||
Handle, handle, kexp, key_pat, key_event_pat, key_event_expr,
|
Input, Handle, handle, kexp, key_pat, key_event_pat, key_event_expr,
|
||||||
Tui, TuiInput, TuiOutput,
|
Tui, TuiInput, TuiOutput,
|
||||||
crossterm::{
|
crossterm::{
|
||||||
self,
|
self,
|
||||||
|
|
|
||||||
|
|
@ -2,32 +2,34 @@ use crate::*;
|
||||||
use super::note_y_iter;
|
use super::note_y_iter;
|
||||||
|
|
||||||
pub struct PianoHorizontalCursor<'a>(pub(crate) &'a PianoHorizontal);
|
pub struct PianoHorizontalCursor<'a>(pub(crate) &'a PianoHorizontal);
|
||||||
render!(<Tui>|self: PianoHorizontalCursor<'a>|render(|to: &mut TuiOutput|Ok({
|
render!(<Tui>(self: PianoHorizontalCursor<'a>)
|
||||||
let style = Some(Style::default().fg(self.0.color.lightest.rgb));
|
|layout|Ok([0, 0]),
|
||||||
let note_hi = self.0.note_hi();
|
|render|{
|
||||||
let note_len = self.0.note_len();
|
let style = Some(Style::default().fg(self.0.color.lightest.rgb));
|
||||||
let note_lo = self.0.note_lo().get();
|
let note_hi = self.0.note_hi();
|
||||||
let note_point = self.0.note_point();
|
let note_len = self.0.note_len();
|
||||||
let time_point = self.0.time_point();
|
let note_lo = self.0.note_lo().get();
|
||||||
let time_start = self.0.time_start().get();
|
let note_point = self.0.note_point();
|
||||||
let time_zoom = self.0.time_zoom().get();
|
let time_point = self.0.time_point();
|
||||||
let [x0, y0, w, _] = to.area().xywh();
|
let time_start = self.0.time_start().get();
|
||||||
for (area_y, screen_y, note) in note_y_iter(note_lo, note_hi, y0) {
|
let time_zoom = self.0.time_zoom().get();
|
||||||
if note == note_point {
|
let [x0, y0, w, _] = render.area().xywh();
|
||||||
for x in 0..w {
|
for (area_y, screen_y, note) in note_y_iter(note_lo, note_hi, y0) {
|
||||||
let screen_x = x0 + x;
|
if note == note_point {
|
||||||
let time_1 = time_start + x as usize * time_zoom;
|
for x in 0..w {
|
||||||
let time_2 = time_1 + time_zoom;
|
let screen_x = x0 + x;
|
||||||
if time_1 <= time_point && time_point < time_2 {
|
let time_1 = time_start + x as usize * time_zoom;
|
||||||
to.blit(&"█", screen_x, screen_y, style);
|
let time_2 = time_1 + time_zoom;
|
||||||
let tail = note_len as u16 / time_zoom as u16;
|
if time_1 <= time_point && time_point < time_2 {
|
||||||
for x_tail in (screen_x + 1)..(screen_x + tail) {
|
render.blit(&"█", screen_x, screen_y, style);
|
||||||
to.blit(&"▂", x_tail, screen_y, style);
|
let tail = note_len as u16 / time_zoom as u16;
|
||||||
|
for x_tail in (screen_x + 1)..(screen_x + tail) {
|
||||||
|
render.blit(&"▂", x_tail, screen_y, style);
|
||||||
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
})));
|
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,18 @@ use crate::*;
|
||||||
use super::note_y_iter;
|
use super::note_y_iter;
|
||||||
|
|
||||||
pub struct PianoHorizontalKeys<'a>(pub(crate) &'a PianoHorizontal);
|
pub struct PianoHorizontalKeys<'a>(pub(crate) &'a PianoHorizontal);
|
||||||
render!(<Tui>|self: PianoHorizontalKeys<'a>|render(|to|Ok(render_keys_v(to, self))));
|
|
||||||
|
render!(<Tui>(self: PianoHorizontalKeys<'a>)
|
||||||
|
|layout|Ok(Some([0, 0])),
|
||||||
|
|render|Ok(render_keys_v(render, self)));
|
||||||
|
|
||||||
has_color!(|self: PianoHorizontalKeys<'a>|self.0.color.base);
|
has_color!(|self: PianoHorizontalKeys<'a>|self.0.color.base);
|
||||||
|
|
||||||
impl<'a> NoteRange for PianoHorizontalKeys<'a> {
|
impl<'a> NoteRange for PianoHorizontalKeys<'a> {
|
||||||
fn note_lo (&self) -> &AtomicUsize { &self.0.note_lo() }
|
fn note_lo (&self) -> &AtomicUsize { &self.0.note_lo() }
|
||||||
fn note_axis (&self) -> &AtomicUsize { &self.0.note_axis() }
|
fn note_axis (&self) -> &AtomicUsize { &self.0.note_axis() }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> NotePoint for PianoHorizontalKeys<'a> {
|
impl<'a> NotePoint for PianoHorizontalKeys<'a> {
|
||||||
fn note_len (&self) -> usize { self.0.note_len() }
|
fn note_len (&self) -> usize { self.0.note_len() }
|
||||||
fn set_note_len (&self, x: usize) { self.0.set_note_len(x) }
|
fn set_note_len (&self, x: usize) { self.0.set_note_len(x) }
|
||||||
|
|
|
||||||
|
|
@ -2,45 +2,33 @@ use crate::*;
|
||||||
use super::note_y_iter;
|
use super::note_y_iter;
|
||||||
|
|
||||||
pub struct PianoHorizontalNotes<'a>(pub(crate) &'a PianoHorizontal);
|
pub struct PianoHorizontalNotes<'a>(pub(crate) &'a PianoHorizontal);
|
||||||
render!(<Tui>|self: PianoHorizontalNotes<'a>|render(|to: &mut TuiOutput|Ok({
|
|
||||||
let time_start = self.0.time_start().get();
|
render!(<Tui>(self: PianoHorizontalNotes<'a>)
|
||||||
let note_lo = self.0.note_lo().get();
|
|layout|Ok(Some([0, 0])),
|
||||||
let note_hi = self.0.note_hi();
|
|render|Ok({
|
||||||
let note_point = self.0.note_point();
|
let time_start = self.0.time_start().get();
|
||||||
let source = &self.0.buffer;
|
let note_lo = self.0.note_lo().get();
|
||||||
let [x0, y0, w, h] = to.area().xywh();
|
let note_hi = self.0.note_hi();
|
||||||
if h as usize != self.0.note_axis().get() {
|
let note_point = self.0.note_point();
|
||||||
panic!("area height mismatch");
|
let source = &self.0.buffer;
|
||||||
}
|
let [x0, y0, w, h] = render.area().xywh();
|
||||||
for (area_x, screen_x) in (x0..x0+w).enumerate() {
|
if h as usize != self.0.note_axis().get() {
|
||||||
for (area_y, screen_y, note) in note_y_iter(note_lo, note_hi, y0) {
|
panic!("area height mismatch");
|
||||||
//if area_x % 10 == 0 {
|
}
|
||||||
//to.blit(&format!("{area_y} {note}"), screen_x, screen_y, None);
|
for (area_x, screen_x) in (x0..x0+w).enumerate() {
|
||||||
//}
|
for (area_y, screen_y, note) in note_y_iter(note_lo, note_hi, y0) {
|
||||||
let source_x = time_start + area_x;
|
let source_x = time_start + area_x;
|
||||||
let source_y = note_hi - area_y;
|
let source_y = note_hi - area_y;
|
||||||
////// TODO: enable loop rollover:
|
// TODO: enable loop rollover:
|
||||||
//////let source_x = (time_start + area_x) % source.width.max(1);
|
//let source_x = (time_start + area_x) % source.width.max(1);
|
||||||
//////let source_y = (note_hi - area_y) % source.height.max(1);
|
//let source_y = (note_hi - area_y) % source.height.max(1);
|
||||||
let is_in_x = source_x < source.width;
|
let is_in_x = source_x < source.width;
|
||||||
let is_in_y = source_y < source.height;
|
let is_in_y = source_y < source.height;
|
||||||
if is_in_x && is_in_y {
|
if is_in_x && is_in_y {
|
||||||
if let Some(source_cell) = source.get(source_x, source_y) {
|
if let Some(source_cell) = source.get(source_x, source_y) {
|
||||||
*to.buffer.get_mut(screen_x, screen_y) = source_cell.clone();
|
*render.buffer.get_mut(screen_x, screen_y) = source_cell.clone();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}));
|
||||||
//let debug = true;
|
|
||||||
//if debug {
|
|
||||||
//let x0=20+x0;
|
|
||||||
//to.blit(&format!("KYP "), x0, y0, None);
|
|
||||||
//to.blit(&format!("x0={x0} "), x0, y0+1, None);
|
|
||||||
//to.blit(&format!("y0={y0} "), x0, y0+2, None);
|
|
||||||
//to.blit(&format!("note_lo={note_lo}, {} ", to_note_name(note_lo)), x0, y0+3, None);
|
|
||||||
//to.blit(&format!("note_hi={note_hi}, {} ", to_note_name(note_hi)), x0, y0+4, None);
|
|
||||||
//to.blit(&format!("note_point={note_point}, {} ", to_note_name(note_point)), x0, y0+5, None);
|
|
||||||
//to.blit(&format!("time_start={time_start} "), x0, y0+6, None);
|
|
||||||
//return Ok(());
|
|
||||||
//}
|
|
||||||
})));
|
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,21 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub struct PianoHorizontalTimeline<'a>(pub(crate) &'a PianoHorizontal);
|
pub struct PianoHorizontalTimeline<'a>(pub(crate) &'a PianoHorizontal);
|
||||||
render!(<Tui>|self: PianoHorizontalTimeline<'a>|render(|to: &mut TuiOutput|{
|
render!(<Tui>(self: PianoHorizontalTimeline<'a>)
|
||||||
let [x, y, w, h] = to.area();
|
|layout|Ok(Some([0, 0])),
|
||||||
let style = Some(Style::default().dim());
|
|render|{
|
||||||
let length = self.0.phrase.as_ref().map(|p|p.read().unwrap().length).unwrap_or(1);
|
let [x, y, w, h] = render.area();
|
||||||
for (area_x, screen_x) in (0..w).map(|d|(d, d+x)) {
|
let style = Some(Style::default().dim());
|
||||||
let t = area_x as usize * self.0.time_zoom().get();
|
let length = self.0.phrase.as_ref().map(|p|p.read().unwrap().length).unwrap_or(1);
|
||||||
if t < length {
|
for (area_x, screen_x) in (0..w).map(|d|(d, d+x)) {
|
||||||
to.blit(&"|", screen_x, y, style);
|
let t = area_x as usize * self.0.time_zoom().get();
|
||||||
|
if t < length {
|
||||||
|
render.blit(&"|", screen_x, y, style);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
Ok(())
|
||||||
Ok(())
|
});
|
||||||
}));
|
|
||||||
//Tui::fg_bg(
|
//Tui::fg_bg(
|
||||||
//self.0.color.lightest.rgb,
|
//self.0.color.lightest.rgb,
|
||||||
//self.0.color.darkest.rgb,
|
//self.0.color.darkest.rgb,
|
||||||
|
|
|
||||||
|
|
@ -5,48 +5,61 @@ use ratatui::{prelude::Rect, widgets::{Widget, canvas::{Canvas, Points, Line}}};
|
||||||
const EMPTY: &[(f64, f64)] = &[(0., 0.), (1., 1.), (2., 2.), (0., 2.), (2., 0.)];
|
const EMPTY: &[(f64, f64)] = &[(0., 0.), (1., 1.), (2., 2.), (0., 2.), (2., 0.)];
|
||||||
|
|
||||||
pub struct SampleViewer(pub Option<Arc<RwLock<Sample>>>);
|
pub struct SampleViewer(pub Option<Arc<RwLock<Sample>>>);
|
||||||
render!(<Tui>|self: SampleViewer|render(|to: &mut TuiOutput|{
|
render!(<Tui>(self: SampleViewer)
|
||||||
let [x, y, width, height] = to.area();
|
|layout|Ok(Some([0, 0])),
|
||||||
let area = Rect { x, y, width, height };
|
|render|{
|
||||||
let min_db = -40.0;
|
|
||||||
let (x_bounds, y_bounds, lines): ([f64;2], [f64;2], Vec<Line>) =
|
let [x, y, width, height] = render.area();
|
||||||
if let Some(sample) = &self.0 {
|
|
||||||
let sample = sample.read().unwrap();
|
let area = Rect { x, y, width, height };
|
||||||
let start = sample.start as f64;
|
let min_db = -40.0;
|
||||||
let end = sample.end as f64;
|
|
||||||
let length = end - start;
|
let (x_bounds, y_bounds, lines): ([f64;2], [f64;2], Vec<Line>) =
|
||||||
let step = length / width as f64;
|
if let Some(sample) = &self.0 {
|
||||||
let mut t = start;
|
let sample = sample.read().unwrap();
|
||||||
let mut lines = vec![];
|
let start = sample.start as f64;
|
||||||
while t < end {
|
let end = sample.end as f64;
|
||||||
let chunk = &sample.channels[0][t as usize..((t + step) as usize).min(sample.end)];
|
let length = end - start;
|
||||||
let total: f32 = chunk.iter().map(|x|x.abs()).sum();
|
let step = length / width as f64;
|
||||||
let count = chunk.len() as f32;
|
let mut t = start;
|
||||||
let meter = 10. * (total / count).log10();
|
let mut lines = vec![];
|
||||||
let x = t as f64;
|
while t < end {
|
||||||
let y = meter as f64;
|
let chunk = &sample.channels[0][t as usize..((t + step) as usize).min(sample.end)];
|
||||||
lines.push(Line::new(x, min_db, x, y, Color::Green));
|
let total: f32 = chunk.iter().map(|x|x.abs()).sum();
|
||||||
t += step / 2.;
|
let count = chunk.len() as f32;
|
||||||
|
let meter = 10. * (total / count).log10();
|
||||||
|
let x = t as f64;
|
||||||
|
let y = meter as f64;
|
||||||
|
lines.push(Line::new(x, min_db, x, y, Color::Green));
|
||||||
|
t += step / 2.;
|
||||||
|
}
|
||||||
|
(
|
||||||
|
[sample.start as f64, sample.end as f64],
|
||||||
|
[min_db, 0.],
|
||||||
|
lines
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
[0.0, width as f64],
|
||||||
|
[0.0, height as f64],
|
||||||
|
vec![
|
||||||
|
Line::new(0.0, 0.0, width as f64, height as f64, Color::Red),
|
||||||
|
Line::new(width as f64, 0.0, 0.0, height as f64, Color::Red),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let draw = |ctx| {
|
||||||
|
for line in lines.iter() {
|
||||||
|
ctx.draw(line)
|
||||||
}
|
}
|
||||||
(
|
|
||||||
[sample.start as f64, sample.end as f64],
|
|
||||||
[min_db, 0.],
|
|
||||||
lines
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(
|
|
||||||
[0.0, width as f64],
|
|
||||||
[0.0, height as f64],
|
|
||||||
vec![
|
|
||||||
Line::new(0.0, 0.0, width as f64, height as f64, Color::Red),
|
|
||||||
Line::new(width as f64, 0.0, 0.0, height as f64, Color::Red),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
Canvas::default().x_bounds(x_bounds).y_bounds(y_bounds).paint(|ctx|{
|
|
||||||
for line in lines.iter() {
|
Canvas::default()
|
||||||
ctx.draw(line)
|
.x_bounds(x_bounds)
|
||||||
}
|
.y_bounds(y_bounds)
|
||||||
}).render(area, &mut to.buffer);
|
.paint(draw)
|
||||||
Ok(())
|
.render(area, &mut render.buffer);
|
||||||
}));
|
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue