mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
wip9 (15e)
This commit is contained in:
parent
2f772eceef
commit
f9d1456897
11 changed files with 252 additions and 209 deletions
|
|
@ -1,8 +1,15 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
#[macro_export] macro_rules! lay {
|
#[macro_export] macro_rules! lay {
|
||||||
($(move)*|$add:ident|$expr:expr) => { Layers::new($(move)*|$add|$expr) };
|
([$($expr:expr),* $(,)?]) => {
|
||||||
($($expr:expr),* $(,)?) => { Layers::new(move|add|{ $(add(&$expr)?;)* Ok(()) }) }
|
Layers::new(move|add|{ $(add(&$expr)?;)* Ok(()) })
|
||||||
|
};
|
||||||
|
(![$($expr:expr),* $(,)?]) => {
|
||||||
|
Layers::new(|add|{ $(add(&$expr)?;)* Ok(()) })
|
||||||
|
};
|
||||||
|
($expr:expr) => {
|
||||||
|
Layers::new($expr)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Layers<
|
pub struct Layers<
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,22 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
#[macro_export] macro_rules! col {
|
#[macro_export] macro_rules! col {
|
||||||
($($move:ident)?|$add:ident|$expr:expr) => {
|
([$($expr:expr),* $(,)?]) => {
|
||||||
Stack::down($(move)?|$add|$expr)
|
|
||||||
};
|
|
||||||
($pat:pat in $collection:expr => $item:expr) => {
|
|
||||||
Stack::down(move |add|{
|
|
||||||
for $pat in $collection { add(&$item)?; }
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
};
|
|
||||||
($($expr:expr),* $(,)?) => {
|
|
||||||
Stack::down(move|add|{ $(add(&$expr)?;)* Ok(()) })
|
Stack::down(move|add|{ $(add(&$expr)?;)* Ok(()) })
|
||||||
};
|
};
|
||||||
|
(![$($expr:expr),* $(,)?]) => {
|
||||||
|
Stack::down(|add|{ $(add(&$expr)?;)* Ok(()) })
|
||||||
|
};
|
||||||
|
($expr:expr) => {
|
||||||
|
Stack::down($expr)
|
||||||
|
};
|
||||||
|
($pat:pat in $collection:expr => $item:expr) => {
|
||||||
|
Stack::down(move|add|{ for $pat in $collection { add(&$item)?; } Ok(()) })
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export] macro_rules! col_up {
|
#[macro_export] macro_rules! col_up {
|
||||||
($($move:ident)?|$add:ident|$expr:expr) => {
|
($(move)?|$add:ident|$expr:expr) => {
|
||||||
Stack::up($(move)?|$add|$expr)
|
Stack::up($(move)?|$add|$expr)
|
||||||
};
|
};
|
||||||
($pat:pat in $collection:expr => $item:expr) => {
|
($pat:pat in $collection:expr => $item:expr) => {
|
||||||
|
|
@ -24,17 +25,23 @@ use crate::*;
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
($($expr:expr),* $(,)?) => { Stack::up(move|add|{ $(add(&$expr)?;)* Ok(()) }) };
|
($($expr:expr),* $(,)?) => {
|
||||||
|
Stack::up(move|add|{ $(add(&$expr)?;)* Ok(()) })
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export] macro_rules! row {
|
#[macro_export] macro_rules! row {
|
||||||
($($move:ident)?|$add:ident|$expr:expr) => {
|
([$($expr:expr),* $(,)?]) => {
|
||||||
Stack::right($(move)?|$add|$expr)
|
Stack::right(move|add|{ $(add(&$expr)?;)* Ok(()) })
|
||||||
|
};
|
||||||
|
(![$($expr:expr),* $(,)?]) => {
|
||||||
|
Stack::right(|add|{ $(add(&$expr)?;)* Ok(()) })
|
||||||
|
};
|
||||||
|
($expr:expr) => {
|
||||||
|
Stack::right($expr)
|
||||||
};
|
};
|
||||||
($pat:pat in $collection:expr => $item:expr) => {
|
($pat:pat in $collection:expr => $item:expr) => {
|
||||||
Stack::right(move |add|{ for $pat in $collection { add(&$item)?; } Ok(()) })
|
Stack::right(move|add|{ for $pat in $collection { add(&$item)?; } Ok(()) })
|
||||||
};
|
|
||||||
($($expr:expr),* $(,)?) => {
|
|
||||||
Stack::right(move|add|{ $(add(&$expr)?;)* Ok(()) })
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ impl FocusWrap<TransportFocus> for TransportFocus {
|
||||||
let focused = focus == self;
|
let focused = focus == self;
|
||||||
let corners = focused.then_some(CORNERS);
|
let corners = focused.then_some(CORNERS);
|
||||||
//let highlight = focused.then_some(Tui::bg(Color::Rgb(60, 70, 50)));
|
//let highlight = focused.then_some(Tui::bg(Color::Rgb(60, 70, 50)));
|
||||||
lay!(corners, /*highlight,*/ *content)
|
lay!([corners, /*highlight,*/ *content])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,7 +67,7 @@ impl FocusWrap<TransportFocus> for Option<TransportFocus> {
|
||||||
let focused = Some(focus) == self;
|
let focused = Some(focus) == self;
|
||||||
let corners = focused.then_some(CORNERS);
|
let corners = focused.then_some(CORNERS);
|
||||||
//let highlight = focused.then_some(Background(Color::Rgb(60, 70, 50)));
|
//let highlight = focused.then_some(Background(Color::Rgb(60, 70, 50)));
|
||||||
lay!(corners, /*highlight,*/ *content)
|
lay!([corners, /*highlight,*/ *content])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,15 @@ pub fn buffer_update (buf: &mut Buffer, area: [u16;4], callback: &impl Fn(&mut C
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Render<Tui> for () {
|
||||||
|
fn min_size (&self, _: [u16;2]) -> Perhaps<[u16;2]> {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
fn render (&self, to: &mut TuiOutput) -> Usually<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Render<Tui> for &str {
|
impl Render<Tui> for &str {
|
||||||
fn min_size (&self, _: [u16;2]) -> Perhaps<[u16;2]> {
|
fn min_size (&self, _: [u16;2]) -> Perhaps<[u16;2]> {
|
||||||
// TODO: line breaks
|
// TODO: line breaks
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ render!(|self: ArrangerTui|{
|
||||||
Tui::to_south(
|
Tui::to_south(
|
||||||
self.splits[0],
|
self.splits[0],
|
||||||
Tui::to_south(
|
Tui::to_south(
|
||||||
lay!(
|
lay!([
|
||||||
Layers::new(move |add|{
|
Layers::new(move |add|{
|
||||||
match self.mode {
|
match self.mode {
|
||||||
ArrangerMode::Horizontal =>
|
ArrangerMode::Horizontal =>
|
||||||
|
|
@ -30,7 +30,7 @@ render!(|self: ArrangerTui|{
|
||||||
}))
|
}))
|
||||||
.fg(TuiTheme::title_fg(arranger_focused))
|
.fg(TuiTheme::title_fg(arranger_focused))
|
||||||
.push_x(1),
|
.push_x(1),
|
||||||
),
|
]),
|
||||||
Split::right(
|
Split::right(
|
||||||
self.splits[1],
|
self.splits[1],
|
||||||
PhraseListView::from(self),
|
PhraseListView::from(self),
|
||||||
|
|
@ -85,6 +85,14 @@ fn track_widths (tracks: &[ArrangerTrack]) -> Vec<(usize, usize)> {
|
||||||
widths
|
widths
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn any_size <E: Engine> (_: E::Size) -> Perhaps<E::Size>{
|
||||||
|
Ok(Some([0.into(),0.into()].into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn custom_render <F: Fn(&mut TuiOutput)->Usually<()>+Send+Sync> (render: F) -> impl Render<Tui> {
|
||||||
|
Widget::new(|_|Ok(Some([0u16,0u16].into())), render)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn arranger_content_vertical (
|
pub fn arranger_content_vertical (
|
||||||
view: &ArrangerTui,
|
view: &ArrangerTui,
|
||||||
factor: usize
|
factor: usize
|
||||||
|
|
@ -100,9 +108,8 @@ pub fn arranger_content_vertical (
|
||||||
let sep_fg = TuiTheme::separator_fg(false);
|
let sep_fg = TuiTheme::separator_fg(false);
|
||||||
let header_h = 3u16;//5u16;
|
let header_h = 3u16;//5u16;
|
||||||
let scenes_w = 3 + ArrangerScene::longest_name(scenes) as u16; // x of 1st track
|
let scenes_w = 3 + ArrangerScene::longest_name(scenes) as u16; // x of 1st track
|
||||||
let rows: &[(usize, usize)] = rows.as_ref();
|
//let rows: &[(usize, usize)] = rows_.as_ref();
|
||||||
let cols: &[(usize, usize)] = cols.as_ref();
|
//let cols: &[(usize, usize)] = cols_.as_ref();
|
||||||
let any_size = |_|Ok(Some([0,0]));
|
|
||||||
|
|
||||||
// track titles
|
// track titles
|
||||||
let header = row!((track, w) in tracks.iter().zip(cols.iter().map(|col|col.0)) => {
|
let header = row!((track, w) in tracks.iter().zip(cols.iter().map(|col|col.0)) => {
|
||||||
|
|
@ -135,12 +142,12 @@ pub fn arranger_content_vertical (
|
||||||
}).unwrap_or(String::from("▎"));
|
}).unwrap_or(String::from("▎"));
|
||||||
let timer = Tui::to_south(until_next, elapsed);
|
let timer = Tui::to_south(until_next, elapsed);
|
||||||
// name of active MIDI input
|
// name of active MIDI input
|
||||||
let input = format!("▎>{}", track.player.midi_ins().get(0)
|
let _input = format!("▎>{}", track.player.midi_ins().get(0)
|
||||||
.map(|port|port.short_name())
|
.map(|port|port.short_name())
|
||||||
.transpose()?
|
.transpose()?
|
||||||
.unwrap_or("(none)".into()));
|
.unwrap_or("(none)".into()));
|
||||||
// name of active MIDI output
|
// name of active MIDI output
|
||||||
let output = format!("▎<{}", track.player.midi_outs().get(0)
|
let _output = format!("▎<{}", track.player.midi_outs().get(0)
|
||||||
.map(|port|port.short_name())
|
.map(|port|port.short_name())
|
||||||
.transpose()?
|
.transpose()?
|
||||||
.unwrap_or("(none)".into()));
|
.unwrap_or("(none)".into()));
|
||||||
|
|
@ -189,93 +196,144 @@ pub fn arranger_content_vertical (
|
||||||
)
|
)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let arrangement = Tui::bg(bg.rgb, lay!(move|add|{
|
|
||||||
// column separators
|
|
||||||
add(&Widget::new(any_size, move|to: &mut TuiOutput|{
|
|
||||||
let style = Some(Style::default().fg(sep_fg));
|
|
||||||
Ok(for x in cols.iter().map(|col|col.1) {
|
|
||||||
let x = scenes_w + to.area().x() + x as u16;
|
|
||||||
for y in to.area().y()..to.area().y2() { to.blit(&"▎", x, y, style); }
|
|
||||||
})
|
|
||||||
}))?;
|
|
||||||
// row separators
|
|
||||||
add(&Widget::new(any_size, move|to: &mut TuiOutput|{
|
|
||||||
Ok(for y in rows.iter().map(|row|row.1) {
|
|
||||||
let y = to.area().y() + (y / PPQ) as u16 + 1;
|
|
||||||
if y >= to.buffer.area.height { break }
|
|
||||||
for x in to.area().x()..to.area().x2().saturating_sub(2) {
|
|
||||||
if x < to.buffer.area.x && y < to.buffer.area.y {
|
|
||||||
let cell = to.buffer.get_mut(x, y);
|
|
||||||
cell.modifier = Modifier::UNDERLINED;
|
|
||||||
cell.underline_color = sep_fg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}))?;
|
|
||||||
// full grid with header and footer
|
|
||||||
add(&Tui::to_south(header, content))?;
|
|
||||||
// cursor
|
|
||||||
add(&Widget::new(any_size, move|to: &mut TuiOutput|{
|
|
||||||
let area = to.area();
|
|
||||||
let focused = view.arranger_focused();
|
|
||||||
let selected = view.selected;
|
|
||||||
let get_track_area = |t: usize| [
|
|
||||||
scenes_w + area.x() + cols[t].1 as u16, area.y(),
|
|
||||||
cols[t].0 as u16, area.h(),
|
|
||||||
];
|
|
||||||
let get_scene_area = |s: usize| [
|
|
||||||
area.x(), header_h + area.y() + (rows[s].1 / PPQ) as u16,
|
|
||||||
area.w(), (rows[s].0 / PPQ) as u16
|
|
||||||
];
|
|
||||||
let get_clip_area = |t: usize, s: usize| [
|
|
||||||
scenes_w + area.x() + cols[t].1 as u16,
|
|
||||||
header_h + area.y() + (rows[s].1/PPQ) as u16,
|
|
||||||
cols[t].0 as u16,
|
|
||||||
(rows[s].0 / PPQ) as u16
|
|
||||||
];
|
|
||||||
let mut track_area: Option<[u16;4]> = None;
|
|
||||||
let mut scene_area: Option<[u16;4]> = None;
|
|
||||||
let mut clip_area: Option<[u16;4]> = None;
|
|
||||||
let area = match selected {
|
|
||||||
ArrangerSelection::Mix => area,
|
|
||||||
ArrangerSelection::Track(t) => {
|
|
||||||
track_area = Some(get_track_area(t));
|
|
||||||
area
|
|
||||||
},
|
|
||||||
ArrangerSelection::Scene(s) => {
|
|
||||||
scene_area = Some(get_scene_area(s));
|
|
||||||
area
|
|
||||||
},
|
|
||||||
ArrangerSelection::Clip(t, s) => {
|
|
||||||
track_area = Some(get_track_area(t));
|
|
||||||
scene_area = Some(get_scene_area(s));
|
|
||||||
clip_area = Some(get_clip_area(t, s));
|
|
||||||
area
|
|
||||||
},
|
|
||||||
};
|
|
||||||
let bg = TuiTheme::border_bg();
|
|
||||||
if let Some([x, y, width, height]) = track_area {
|
|
||||||
to.fill_fg([x, y, 1, height], bg);
|
|
||||||
to.fill_fg([x + width, y, 1, height], bg);
|
|
||||||
}
|
|
||||||
if let Some([_, y, _, height]) = scene_area {
|
|
||||||
to.fill_ul([area.x(), y - 1, area.w(), 1], bg);
|
|
||||||
to.fill_ul([area.x(), y + height - 1, area.w(), 1], bg);
|
|
||||||
}
|
|
||||||
Ok(if focused {
|
|
||||||
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) }
|
|
||||||
else if let Some(scene_area) = scene_area { scene_area.clip_w(scenes_w) }
|
|
||||||
else { area.clip_w(scenes_w).clip_h(header_h) }, &CORNERS)?
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
}));
|
|
||||||
let color = TuiTheme::title_fg(view.arranger_focused());
|
let color = TuiTheme::title_fg(view.arranger_focused());
|
||||||
let size = format!("{}x{}", view.size.w(), view.size.h());
|
lay!([
|
||||||
let lower_right = Tui::at_se(Tui::fill_xy(Tui::pull_x(1, Tui::fg(color, size))));
|
Tui::bg(bg.rgb, lay!(![
|
||||||
lay!(arrangement, lower_right)
|
ArrangerVerticalColumnSeparator::from(view),
|
||||||
|
ArrangerVerticalRowSeparator::from((view, factor)),
|
||||||
|
col!(![header, content]),
|
||||||
|
ArrangerCursor::from((view, factor)),
|
||||||
|
])),
|
||||||
|
Tui::at_se(Tui::fill_xy(Tui::pull_x(1, Tui::fg(color, format!("{}x{}", view.size.w(), view.size.h()))))),
|
||||||
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ArrangerVerticalColumnSeparator {
|
||||||
|
cols: Vec<(usize, usize)>,
|
||||||
|
scenes_w: u16,
|
||||||
|
sep_fg: Color,
|
||||||
|
}
|
||||||
|
impl From<&ArrangerTui> for ArrangerVerticalColumnSeparator {
|
||||||
|
fn from (state: &ArrangerTui) -> Self {
|
||||||
|
Self {
|
||||||
|
cols: track_widths(state.tracks()),
|
||||||
|
scenes_w: 3 + ArrangerScene::longest_name(state.scenes()) as u16,
|
||||||
|
sep_fg: TuiTheme::separator_fg(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
render!(|self: ArrangerVerticalColumnSeparator|custom_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;
|
||||||
|
for y in to.area().y()..to.area().y2() {
|
||||||
|
to.blit(&"▎", x, y, style);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}));
|
||||||
|
|
||||||
|
struct ArrangerVerticalRowSeparator {
|
||||||
|
rows: Vec<(usize, usize)>,
|
||||||
|
sep_fg: Color,
|
||||||
|
}
|
||||||
|
impl From<(&ArrangerTui, usize)> for ArrangerVerticalRowSeparator {
|
||||||
|
fn from ((state, factor): (&ArrangerTui, usize)) -> Self {
|
||||||
|
Self {
|
||||||
|
rows: ArrangerScene::ppqs(state.scenes(), factor),
|
||||||
|
sep_fg: TuiTheme::separator_fg(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render!(|self: ArrangerVerticalRowSeparator|custom_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 }
|
||||||
|
for x in to.area().x()..to.area().x2().saturating_sub(2) {
|
||||||
|
if x < to.buffer.area.x && y < to.buffer.area.y {
|
||||||
|
let cell = to.buffer.get_mut(x, y);
|
||||||
|
cell.modifier = Modifier::UNDERLINED;
|
||||||
|
cell.underline_color = self.sep_fg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}));
|
||||||
|
|
||||||
|
struct ArrangerCursor {
|
||||||
|
cols: Vec<(usize, usize)>,
|
||||||
|
rows: Vec<(usize, usize)>,
|
||||||
|
focused: bool,
|
||||||
|
selected: ArrangerSelection,
|
||||||
|
scenes_w: u16,
|
||||||
|
header_h: u16,
|
||||||
|
}
|
||||||
|
impl From<(&ArrangerTui, usize)> for ArrangerCursor {
|
||||||
|
fn from ((state, factor): (&ArrangerTui, usize)) -> Self {
|
||||||
|
Self {
|
||||||
|
cols: track_widths(state.tracks()),
|
||||||
|
rows: ArrangerScene::ppqs(state.scenes(), factor),
|
||||||
|
focused: state.arranger_focused(),
|
||||||
|
selected: state.selected,
|
||||||
|
scenes_w: 3 + ArrangerScene::longest_name(state.scenes()) as u16,
|
||||||
|
header_h: 3,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
render!(|self: ArrangerCursor|custom_render(move|to: &mut TuiOutput|{
|
||||||
|
let area = to.area();
|
||||||
|
let focused = self.focused;
|
||||||
|
let selected = self.selected;
|
||||||
|
let get_track_area = |t: usize| [
|
||||||
|
self.scenes_w + area.x() + self.cols[t].1 as u16, area.y(),
|
||||||
|
self.cols[t].0 as u16, area.h(),
|
||||||
|
];
|
||||||
|
let get_scene_area = |s: usize| [
|
||||||
|
area.x(), self.header_h + area.y() + (self.rows[s].1 / PPQ) as u16,
|
||||||
|
area.w(), (self.rows[s].0 / PPQ) as u16
|
||||||
|
];
|
||||||
|
let get_clip_area = |t: usize, s: usize| [
|
||||||
|
self.scenes_w + area.x() + self.cols[t].1 as u16,
|
||||||
|
self.header_h + area.y() + (self.rows[s].1/PPQ) as u16,
|
||||||
|
self.cols[t].0 as u16,
|
||||||
|
(self.rows[s].0 / PPQ) as u16
|
||||||
|
];
|
||||||
|
let mut track_area: Option<[u16;4]> = None;
|
||||||
|
let mut scene_area: Option<[u16;4]> = None;
|
||||||
|
let mut clip_area: Option<[u16;4]> = None;
|
||||||
|
let area = match selected {
|
||||||
|
ArrangerSelection::Mix => area,
|
||||||
|
ArrangerSelection::Track(t) => {
|
||||||
|
track_area = Some(get_track_area(t));
|
||||||
|
area
|
||||||
|
},
|
||||||
|
ArrangerSelection::Scene(s) => {
|
||||||
|
scene_area = Some(get_scene_area(s));
|
||||||
|
area
|
||||||
|
},
|
||||||
|
ArrangerSelection::Clip(t, s) => {
|
||||||
|
track_area = Some(get_track_area(t));
|
||||||
|
scene_area = Some(get_scene_area(s));
|
||||||
|
clip_area = Some(get_clip_area(t, s));
|
||||||
|
area
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let bg = TuiTheme::border_bg();
|
||||||
|
if let Some([x, y, width, height]) = track_area {
|
||||||
|
to.fill_fg([x, y, 1, height], bg);
|
||||||
|
to.fill_fg([x + width, y, 1, height], bg);
|
||||||
|
}
|
||||||
|
if let Some([_, y, _, height]) = scene_area {
|
||||||
|
to.fill_ul([area.x(), y - 1, area.w(), 1], bg);
|
||||||
|
to.fill_ul([area.x(), y + height - 1, area.w(), 1], bg);
|
||||||
|
}
|
||||||
|
Ok(if focused {
|
||||||
|
to.render_in(if let Some(clip_area) = clip_area { clip_area }
|
||||||
|
else if let Some(track_area) = track_area { track_area.clip_h(self.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(self.header_h) }, &CORNERS)?
|
||||||
|
})
|
||||||
|
}));
|
||||||
|
|
||||||
pub fn arranger_content_horizontal (
|
pub fn arranger_content_horizontal (
|
||||||
view: &ArrangerTui,
|
view: &ArrangerTui,
|
||||||
) -> impl Render<Tui> + use<'_> {
|
) -> impl Render<Tui> + use<'_> {
|
||||||
|
|
|
||||||
|
|
@ -63,31 +63,6 @@ render!(|self: PhraseView<'a>|{
|
||||||
//now: _,
|
//now: _,
|
||||||
..
|
..
|
||||||
} = self;
|
} = self;
|
||||||
let upper_left = format!(
|
|
||||||
"╭{note_hi} {note_hi_name} {}",
|
|
||||||
phrase.as_ref().map(|p|p.read().unwrap().name.clone()).unwrap_or(String::new())
|
|
||||||
);
|
|
||||||
let lower_left = format!(
|
|
||||||
"╰{note_lo} {note_lo_name}"
|
|
||||||
);
|
|
||||||
let mut lower_right = format!(
|
|
||||||
" {} ", size.format()
|
|
||||||
);
|
|
||||||
if *focused && *entered {
|
|
||||||
lower_right = format!("Note: {} ({}) {} {lower_right}",
|
|
||||||
note_point, to_note_name(*note_point), pulses_to_name(*note_len)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let mut upper_right = format!(
|
|
||||||
"[{}]",
|
|
||||||
if *entered {"■"} else {" "}
|
|
||||||
);
|
|
||||||
if let Some(phrase) = phrase {
|
|
||||||
upper_right = format!("Time: {}/{} {} {upper_right}",
|
|
||||||
time_point, phrase.read().unwrap().length, pulses_to_name(view_mode.time_zoom()),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
let title_color = if *focused{Color::Rgb(150, 160, 90)}else{Color::Rgb(120, 130, 100)};
|
|
||||||
// is it r6d that `to` is the receiver?
|
// is it r6d that `to` is the receiver?
|
||||||
let keys = move|to: &mut TuiOutput|{
|
let keys = move|to: &mut TuiOutput|{
|
||||||
Ok(if to.area().h() >= 2 { view_mode.render_keys(to, *note_hi, *note_lo) })
|
Ok(if to.area().h() >= 2 { view_mode.render_keys(to, *note_hi, *note_lo) })
|
||||||
|
|
@ -124,7 +99,24 @@ render!(|self: PhraseView<'a>|{
|
||||||
////}
|
////}
|
||||||
//Ok(())
|
//Ok(())
|
||||||
//};
|
//};
|
||||||
let indicators = lay!(|add|{
|
let indicators = lay!(move|add|{
|
||||||
|
let title_color = if *focused{Color::Rgb(150, 160, 90)}else{Color::Rgb(120, 130, 100)};
|
||||||
|
let upper_left = format!("╭{note_hi} {note_hi_name} {}",
|
||||||
|
phrase.as_ref().map(|p|p.read().unwrap().name.clone()).unwrap_or(String::new())
|
||||||
|
);
|
||||||
|
let lower_left = format!("╰{note_lo} {note_lo_name}");
|
||||||
|
let mut lower_right = format!(" {} ", size.format());
|
||||||
|
if *focused && *entered {
|
||||||
|
lower_right = format!("Note: {} ({}) {} {lower_right}",
|
||||||
|
note_point, to_note_name(*note_point), pulses_to_name(*note_len)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let mut upper_right = format!("[{}]", if *entered {"■"} else {" "});
|
||||||
|
if let Some(phrase) = phrase {
|
||||||
|
upper_right = format!("Time: {}/{} {} {upper_right}",
|
||||||
|
time_point, phrase.read().unwrap().length, pulses_to_name(view_mode.time_zoom()),
|
||||||
|
)
|
||||||
|
};
|
||||||
add(&Tui::at_nw(Tui::fg(title_color, upper_left)))?;
|
add(&Tui::at_nw(Tui::fg(title_color, upper_left)))?;
|
||||||
add(&Tui::at_sw(Tui::fg(title_color, lower_left)))?;
|
add(&Tui::at_sw(Tui::fg(title_color, lower_left)))?;
|
||||||
add(&Tui::fill_xy(Tui::at_ne(Tui::pull_x(1, Tui::fg(title_color, upper_right)))))?;
|
add(&Tui::fill_xy(Tui::at_ne(Tui::pull_x(1, Tui::fg(title_color, upper_right)))))?;
|
||||||
|
|
@ -133,12 +125,15 @@ render!(|self: PhraseView<'a>|{
|
||||||
});
|
});
|
||||||
let content = Tui::bg(Color::Rgb(40, 50, 30), Tui::fill_x(Tui::to_east(
|
let content = Tui::bg(Color::Rgb(40, 50, 30), Tui::fill_x(Tui::to_east(
|
||||||
Tui::push_y(1, Tui::fill_y(Widget::new(|to:[u16;2]|Ok(Some(to.clip_w(2))), keys))),
|
Tui::push_y(1, Tui::fill_y(Widget::new(|to:[u16;2]|Ok(Some(to.clip_w(2))), keys))),
|
||||||
Tui::fill_x(lay!(
|
Tui::fill_x(lay!([
|
||||||
Tui::push_y(1, Tui::fill_x(Widget::new(|to|Ok(Some(to)), notes))),
|
Tui::push_y(1, Tui::fill_x(Widget::new(|to|Ok(Some(to)), notes))),
|
||||||
Tui::push_y(1, Widget::new(|to|Ok(Some(to)), cursor))
|
Tui::push_y(1, Widget::new(|to|Ok(Some(to)), cursor))
|
||||||
)),
|
])),
|
||||||
)));
|
)));
|
||||||
lay!(indicators, content)
|
lay!([
|
||||||
|
indicators,
|
||||||
|
content
|
||||||
|
])
|
||||||
});
|
});
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,17 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
render!(|self:PhraseLength|{
|
render!(|self: PhraseLength|{
|
||||||
let bars = ||self.bars_string();
|
let bars = ||self.bars_string();
|
||||||
let beats = ||self.beats_string();
|
let beats = ||self.beats_string();
|
||||||
let ticks = ||self.ticks_string();
|
let ticks = ||self.ticks_string();
|
||||||
row!(|add|(match self.focus {
|
row!(move|add|match self.focus {
|
||||||
None =>
|
None =>
|
||||||
add(&row!(" ", bars(), "B", beats(), "b", ticks(), "T")),
|
add(&row!([" ", bars(), "B", beats(), "b", ticks(), "T"])),
|
||||||
Some(PhraseLengthFocus::Bar) =>
|
Some(PhraseLengthFocus::Bar) =>
|
||||||
add(&row!("[", bars(), "]", beats(), "b", ticks(), "T")),
|
add(&row!(["[", bars(), "]", beats(), "b", ticks(), "T"])),
|
||||||
Some(PhraseLengthFocus::Beat) =>
|
Some(PhraseLengthFocus::Beat) =>
|
||||||
add(&row!(" ", bars(), "[", beats(), "]", ticks(), "T")),
|
add(&row!([" ", bars(), "[", beats(), "]", ticks(), "T"])),
|
||||||
Some(PhraseLengthFocus::Tick) =>
|
Some(PhraseLengthFocus::Tick) =>
|
||||||
add(&row!(" ", bars(), "B", beats(), "[", ticks(), "]")),
|
add(&row!([" ", bars(), "B", beats(), "[", ticks(), "]"])),
|
||||||
}))
|
})
|
||||||
//Layers::new(move|add|{
|
|
||||||
//match self.focus {
|
|
||||||
//None => add(&row!(
|
|
||||||
//" ", self.bars_string(),
|
|
||||||
//".", self.beats_string(),
|
|
||||||
//".", self.ticks_string(),
|
|
||||||
//" "
|
|
||||||
//)),
|
|
||||||
//Some(PhraseLengthFocus::Bar) => add(&row!(
|
|
||||||
//"[", self.bars_string(),
|
|
||||||
//"]", self.beats_string(),
|
|
||||||
//".", self.ticks_string(),
|
|
||||||
//" "
|
|
||||||
//)),
|
|
||||||
//Some(PhraseLengthFocus::Beat) => add(&row!(
|
|
||||||
//" ", self.bars_string(),
|
|
||||||
//"[", self.beats_string(),
|
|
||||||
//"]", self.ticks_string(),
|
|
||||||
//" "
|
|
||||||
//)),
|
|
||||||
//Some(PhraseLengthFocus::Tick) => add(&row!(
|
|
||||||
//" ", self.bars_string(),
|
|
||||||
//".", self.beats_string(),
|
|
||||||
//"[", self.ticks_string(),
|
|
||||||
//"]"
|
|
||||||
//)),
|
|
||||||
//}
|
|
||||||
//})
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ impl<'a, T: HasPhraseList> From<&'a T> for PhraseListView<'a> {
|
||||||
// TODO: Display phrases always in order of appearance
|
// TODO: Display phrases always in order of appearance
|
||||||
render!(|self: PhraseListView<'a>|{
|
render!(|self: PhraseListView<'a>|{
|
||||||
let Self { title, focused, entered, phrases, index, mode } = self;
|
let Self { title, focused, entered, phrases, index, mode } = self;
|
||||||
let content = Stack::down(move|add|match mode {
|
let content = col!(|add|match mode {
|
||||||
Some(PhrasesMode::Import(_, ref browser)) => {
|
Some(PhrasesMode::Import(_, ref browser)) => {
|
||||||
add(browser)
|
add(browser)
|
||||||
},
|
},
|
||||||
|
|
@ -34,7 +34,7 @@ render!(|self: PhraseListView<'a>|{
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
for (i, phrase) in phrases.iter().enumerate() {
|
for (i, phrase) in phrases.iter().enumerate() {
|
||||||
add(&Layers::new(|add|{
|
add(&lay!(|add|{
|
||||||
let Phrase { ref name, color, length, .. } = *phrase.read().unwrap();
|
let Phrase { ref name, color, length, .. } = *phrase.read().unwrap();
|
||||||
let mut length = PhraseLength::new(length, None);
|
let mut length = PhraseLength::new(length, None);
|
||||||
if let Some(PhrasesMode::Length(phrase, new_length, focus)) = mode {
|
if let Some(PhrasesMode::Length(phrase, new_length, focus)) = mode {
|
||||||
|
|
@ -44,7 +44,7 @@ render!(|self: PhraseListView<'a>|{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let length = Tui::fill_x(Tui::at_e(length));
|
let length = Tui::fill_x(Tui::at_e(length));
|
||||||
let row1 = Tui::fill_x(lay!(Tui::fill_x(Tui::at_w(format!(" {i}"))), length));
|
let row1 = Tui::fill_x(lay!([Tui::fill_x(Tui::at_w(format!(" {i}"))), length]));
|
||||||
let mut row2 = format!(" {name}");
|
let mut row2 = format!(" {name}");
|
||||||
if let Some(PhrasesMode::Rename(phrase, _)) = mode {
|
if let Some(PhrasesMode::Rename(phrase, _)) = mode {
|
||||||
if *focused && i == *phrase {
|
if *focused && i == *phrase {
|
||||||
|
|
@ -63,13 +63,13 @@ render!(|self: PhraseListView<'a>|{
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let border_color = if *focused {Color::Rgb(100, 110, 40)} else {Color::Rgb(70, 80, 50)};
|
let border_color = if *focused {Color::Rgb(100, 110, 40)} else {Color::Rgb(70, 80, 50)};
|
||||||
let content = Tui::bg(Color::Rgb(28, 35, 25), Tui::fill_xy(content));
|
|
||||||
let title_color = if *focused {Color::Rgb(150, 160, 90)} else {Color::Rgb(120, 130, 100)};
|
let title_color = if *focused {Color::Rgb(150, 160, 90)} else {Color::Rgb(120, 130, 100)};
|
||||||
let upper_left = format!("[{}] {title}", if *entered {"■"} else {" "});
|
let upper_left = format!("[{}] {title}", if *entered {"■"} else {" "});
|
||||||
let upper_right = format!("({})", phrases.len());
|
let upper_right = format!("({})", phrases.len());
|
||||||
lay!(
|
lay!([
|
||||||
Lozenge(Style::default().bg(Color::Rgb(40, 50, 30)).fg(border_color)).wrap(content),
|
Lozenge(Style::default().bg(Color::Rgb(40, 50, 30)).fg(border_color))
|
||||||
|
.wrap(Tui::bg(Color::Rgb(28, 35, 25), Tui::fill_xy(content))),
|
||||||
Tui::fill_xy(Tui::at_nw(Tui::push_x(1, Tui::fg(title_color, upper_left.to_string())))),
|
Tui::fill_xy(Tui::at_nw(Tui::push_x(1, Tui::fg(title_color, upper_left.to_string())))),
|
||||||
Tui::fill_xy(Tui::at_ne(Tui::pull_x(1, Tui::fg(title_color, upper_right.to_string())))),
|
Tui::fill_xy(Tui::at_ne(Tui::pull_x(1, Tui::fg(title_color, upper_right.to_string())))),
|
||||||
)
|
])
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ render!(|self:PhraseSelector<'a>|{
|
||||||
let Phrase { ref name, color, length, .. } = *phrase.read().unwrap();
|
let Phrase { ref name, color, length, .. } = *phrase.read().unwrap();
|
||||||
let length = PhraseLength::new(length, None);
|
let length = PhraseLength::new(length, None);
|
||||||
let length = Tui::fill_x(Tui::at_e(length));
|
let length = Tui::fill_x(Tui::at_e(length));
|
||||||
let row1 = Tui::fill_x(lay!(Tui::fill_x(Tui::at_w(format!(" "))), length));
|
let row1 = Tui::fill_x(lay!([Tui::fill_x(Tui::at_w(format!(" "))), length]));
|
||||||
let row2 = format!(" {name}");
|
let row2 = format!(" {name}");
|
||||||
let row2 = Tui::bold(true, row2);
|
let row2 = Tui::bold(true, row2);
|
||||||
add(&Tui::bg(color.base.rgb, Tui::fill_x(Tui::to_south(row1, row2))))?;
|
add(&Tui::bg(color.base.rgb, Tui::fill_x(Tui::to_south(row1, row2))))?;
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,6 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
render!(|self: SequencerTui|{
|
render!(|self: SequencerTui|{
|
||||||
let play = PhraseSelector::play_phrase(
|
|
||||||
&self.player,
|
|
||||||
self.focused() == SequencerFocus::PhrasePlay,
|
|
||||||
self.entered()
|
|
||||||
);
|
|
||||||
let next = PhraseSelector::next_phrase(
|
|
||||||
&self.player,
|
|
||||||
self.focused() == SequencerFocus::PhraseNext,
|
|
||||||
self.entered()
|
|
||||||
);
|
|
||||||
Tui::to_north(
|
Tui::to_north(
|
||||||
SequencerStatusBar::from(self),
|
SequencerStatusBar::from(self),
|
||||||
Tui::to_south(
|
Tui::to_south(
|
||||||
|
|
@ -19,14 +9,19 @@ render!(|self: SequencerTui|{
|
||||||
Tui::min_y(
|
Tui::min_y(
|
||||||
20,
|
20,
|
||||||
Tui::to_east(
|
Tui::to_east(
|
||||||
20,
|
Tui::min_x(20, col!([
|
||||||
Tui::to_south(
|
Tui::fixed_y(4, PhraseSelector::play_phrase(
|
||||||
Tui::fixed_y(4, play),
|
&self.player,
|
||||||
Tui::to_south(
|
self.focused() == SequencerFocus::PhrasePlay,
|
||||||
Tui::fixed_y(4, next),
|
self.entered()
|
||||||
PhraseListView::from(self)
|
)),
|
||||||
)
|
Tui::fixed_y(4, PhraseSelector::next_phrase(
|
||||||
),
|
&self.player,
|
||||||
|
self.focused() == SequencerFocus::PhraseNext,
|
||||||
|
self.entered()
|
||||||
|
)),
|
||||||
|
PhraseListView::from(self)
|
||||||
|
])),
|
||||||
PhraseView::from(self)
|
PhraseView::from(self)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -45,7 +40,7 @@ render!(|self: SequencerStatusBar|{
|
||||||
Tui::to_east(
|
Tui::to_east(
|
||||||
Tui::bg(orange, Tui::fg(black, Tui::bold(true, self.mode))),
|
Tui::bg(orange, Tui::fg(black, Tui::bold(true, self.mode))),
|
||||||
Tui::bg(light, row!((prefix, hotkey, suffix) in self.help.iter() => {
|
Tui::bg(light, row!((prefix, hotkey, suffix) in self.help.iter() => {
|
||||||
row!(" ", prefix, Tui::fg(yellow, *hotkey), suffix)
|
row!([" ", prefix, Tui::fg(yellow, *hotkey), suffix])
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
@ -55,17 +50,17 @@ render!(|self: SequencerStatusBar|{
|
||||||
let cpu = &self.cpu;
|
let cpu = &self.cpu;
|
||||||
let res = &self.res;
|
let res = &self.res;
|
||||||
let size = &self.size;
|
let size = &self.size;
|
||||||
Tui::bg(dark, row!(
|
Tui::bg(dark, row!([
|
||||||
Tui::fg(orange, cpu),
|
Tui::fg(orange, cpu),
|
||||||
Tui::fg(orange, res),
|
Tui::fg(orange, res),
|
||||||
Tui::fg(orange, size),
|
Tui::fg(orange, size),
|
||||||
))
|
]))
|
||||||
};
|
};
|
||||||
|
|
||||||
lay!(|add|if self.width > 60 {
|
lay!(|add|if self.width > 60 {
|
||||||
add(&row!(modeline, statusbar))
|
add(&row!(![modeline, statusbar]))
|
||||||
} else if self.width > 0 {
|
} else if self.width > 0 {
|
||||||
add(&col!(modeline, statusbar))
|
add(&col!(![modeline, statusbar]))
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ pub trait StatusBar: Render<Tui> {
|
||||||
{
|
{
|
||||||
let hotkey_fg = Self::hotkey_fg();
|
let hotkey_fg = Self::hotkey_fg();
|
||||||
row!([a, b, c] in commands.iter() => {
|
row!([a, b, c] in commands.iter() => {
|
||||||
row!(a, Tui::fg(hotkey_fg, Tui::bold(true, b)), c)
|
row!([a, Tui::fg(hotkey_fg, Tui::bold(true, b)), c])
|
||||||
})
|
})
|
||||||
//Tui::reduce(commands.iter(), |prev, [a, b, c]|
|
//Tui::reduce(commands.iter(), |prev, [a, b, c]|
|
||||||
//Tui::to_east(prev,
|
//Tui::to_east(prev,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue