fix (?) Inset; add arranger cursor description

This commit is contained in:
🪞👃🪞 2024-09-30 09:41:43 +03:00
parent 7df9cc930d
commit 3c8d9668fe
4 changed files with 137 additions and 151 deletions

View file

@ -81,7 +81,7 @@ impl Content for ArrangerStandalone<Tui> {
) {
let arranger = &self.arranger as &dyn Widget<Engine = Tui>;
let sequencer = sequencer as &dyn Widget<Engine = Tui>;
add(&Split::new(direction, 40, arranger, sequencer.min_y(20)))
add(&Split::new(direction, 20, arranger, sequencer.min_y(20)))
} else {
add(&self.arranger)
}

View file

@ -306,9 +306,15 @@ impl Handle<Tui> for Arranger<Tui> {
impl Content for Arranger<Tui> {
type Engine = Tui;
fn content (&self) -> impl Widget<Engine = Tui> {
Layers::new(move |add|match self.mode {
ArrangerViewMode::Horizontal => add(&HorizontalArranger(&self)),
ArrangerViewMode::Vertical(factor) => add(&VerticalArranger(&self, factor))
Layers::new(move |add|{
match self.mode {
ArrangerViewMode::Horizontal => add(&HorizontalArranger(&self)),
ArrangerViewMode::Vertical(factor) => add(&VerticalArranger(&self, factor))
}?;
add(&Align::SE(self.selected.description(
&self.tracks,
&self.scenes,
).as_str()))
})
}
}
@ -368,6 +374,41 @@ pub enum ArrangerFocus {
/// Focus identification methods
impl ArrangerFocus {
pub fn description <E: Engine> (
&self,
tracks: &Vec<Sequencer<E>>,
scenes: &Vec<Scene>,
) -> String {
format!("Selected: {}", match self {
Self::Mix => format!("Everything"),
Self::Track(t) => if let Some(track) = tracks.get(*t) {
format!("T{t}: {}", &track.name.read().unwrap())
} else {
format!("T??")
},
Self::Scene(s) => if let Some(scene) = scenes.get(*s) {
format!("S{s}: {}", &scene.name.read().unwrap())
} else {
format!("S??")
},
Self::Clip(t, s) => if let (Some(track), Some(scene)) = (
tracks.get(*t),
scenes.get(*s),
) {
if let Some(Some(slot)) = scene.clips.get(*t) {
if let Some(clip) = track.phrases.get(*slot) {
format!("T{t} S{s} C{slot} ({})", &clip.read().unwrap().name.read().unwrap())
} else {
format!("T{t} S{s}: Empty")
}
} else {
format!("T{t} S{s}: Empty")
}
} else {
format!("T{t} S{s}: Empty")
}
})
}
pub fn is_mix (&self) -> bool {
match self { Self::Mix => true, _ => false }
}
@ -505,7 +546,7 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
}).fixed_xy(offset.saturating_sub(1), height))?;
for (track, (w, _x)) in cols.iter().enumerate() {
add(&Layers::new(move |add|{
let mut color = COLOR_BG0;
let mut color = Color::Rgb(40, 50, 30);
if let (Some(track), Some(Some(clip))) = (
tracks.get(track),
scene.clips.get(track),
@ -531,10 +572,10 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
add(&col!(track_titles, track_clips))?;
Ok(())
})
.bg(Color::Rgb(35,40,25))
.bg(Color::Rgb(35, 40, 25))
.border(Lozenge(Style::default()
.bg(Color::Rgb(40,50,30))
.fg(Color::Rgb(70,80,50))))
.bg(Color::Rgb(40, 50, 30))
.fg(Color::Rgb(70, 80, 50))))
}
}
@ -612,7 +653,7 @@ impl<'a> Widget for VerticalArrangerCursor<'a> {
let area = match selected {
ArrangerFocus::Mix => {
if focused {
to.fill_bg(area, COLOR_BG0);
to.fill_bg(area, Color::Rgb(40, 50, 30));
}
area
},
@ -632,21 +673,21 @@ impl<'a> Widget for VerticalArrangerCursor<'a> {
},
};
if let Some([x, y, width, height]) = track_area {
to.fill_fg([x, y, 1, height], COLOR_BG5);
to.fill_fg([x + width, y, 1, height], COLOR_BG5);
to.fill_fg([x, y, 1, height], Color::Rgb(70, 80, 50));
to.fill_fg([x + width, y, 1, height], Color::Rgb(70, 80, 50));
}
if let Some([_, y, _, height]) = scene_area {
to.fill_ul([area.x(), y - 1, area.w(), 1], COLOR_BG5);
to.fill_ul([area.x(), y + height - 1, area.w(), 1], COLOR_BG5);
to.fill_ul([area.x(), y - 1, area.w(), 1], Color::Rgb(70, 80, 50));
to.fill_ul([area.x(), y + height - 1, area.w(), 1], Color::Rgb(70, 80, 50));
}
if focused {
if let Some(clip_area) = clip_area {
to.render_in(clip_area, &CORNERS)?;
to.fill_bg(clip_area, COLOR_BG0);
to.fill_bg(clip_area, Color::Rgb(40, 50, 30));
} else if let Some(track_area) = track_area {
to.fill_bg(track_area, COLOR_BG0);
to.fill_bg(track_area, Color::Rgb(40, 50, 30));
} else if let Some(scene_area) = scene_area {
to.fill_bg(scene_area, COLOR_BG0);
to.fill_bg(scene_area, Color::Rgb(40, 50, 30));
}
}
//Ok(Some(area))
@ -666,7 +707,7 @@ impl<'a> Content for HorizontalArranger<'a, Tui> {
let Arranger { tracks, focused, selected, scenes, .. } = self.0;
let tracks = tracks.as_slice();
Layers::new(|add|{
add(&focused.then_some(Background(COLOR_BG0)))?;
add(&focused.then_some(Background(Color::Rgb(40, 50, 30))))?;
add(&Stack::right(|add|{
add(&TrackNameColumn(tracks, *selected))?;
add(&TrackMonitorColumn(tracks))?;
@ -1330,38 +1371,17 @@ impl Content for Sequencer<Tui> {
type Engine = Tui;
fn content (&self) -> impl Widget<Engine = Tui> {
let toolbar = col!(
col! { "Name"
, self.name.read().unwrap().as_str(),
}.min_xy(10, 4),
col! { "Start: ", " 1.1.1"
, "End: ", " 2.1.1",
}.min_xy(10, 6),
col! { "Loop [ ]"
, "From: ", " 1.1.1"
, "Length: ", " 1.0.0",
}.min_xy(10, 7),
col! { "Notes: "
, "C#0-C#9 "
, "[ /2 ]"
, "[ x2 ]"
, "[ Rev ]"
, "[ Inv ]"
, "[ Dup ]"
}.min_xy(10, 9),
col! { "Name", self.name.read().unwrap().as_str(), }.min_xy(10, 4),
col! { "Start: ", " 1.1.1", "End: ", " 2.1.1", }.min_xy(10, 6),
col! { "Loop [ ]", "From: ", " 1.1.1", "Length: ", " 1.0.0", }.min_xy(10, 7),
col! { "Notes: ", "C#0-C#9 ", "[ /2 ]", "[ x2 ]"
, "[ Rev ]", "[ Inv ]", "[ Dup ]" }.min_xy(10, 9),
);
let content = lay!(
// keys
CustomWidget::new(|to|Ok(Some([32,4])), |to: &mut TuiOutput|{
let area = to.area();
if area.h() < 2 {
return Ok(())
}
let area = [area.x(), area.y(), 5, area.h() - 2];
to.buffer_update(area, &|cell, x, y|{
CustomWidget::new(|_|Ok(Some([32,4])), |to: &mut TuiOutput|{
if to.area().h() < 2 { return Ok(()) }
to.buffer_update(to.area().set_w(5).shrink_y(2), &|cell, x, y|{
let y = y + self.note_axis.start as u16;
if x < self.keys.area.width && y < self.keys.area.height {
*cell = self.keys.get(x, y).clone()
@ -1369,66 +1389,66 @@ impl Content for Sequencer<Tui> {
});
Ok(())
}).fill_y(),
self.phrase.as_ref().map(|phrase|SequenceTimer(&self, phrase.clone()).fill_x()),
// notes
CustomWidget::new(|to|Ok(Some([32,4])), |to: &mut TuiOutput|{
let area = to.area();
if area.h() < 2 {
return Ok(())//Some(area))
// playhead
CustomWidget::new(|_|Ok(Some([32,2])), |to: &mut TuiOutput|{
if let Some(phrase) = self.phrase.as_ref() {
let time_0 = self.time_axis.start;
let time_z = self.time_axis.scale;
let now = self.now % phrase.read().unwrap().length;
let [x, y, width, _] = to.area();
let x2 = x as usize + Sequencer::H_KEYS_OFFSET;
let x3 = x as usize + width as usize;
for x in x2..x3 {
let step = (time_0 + x2) * time_z;
let next_step = (time_0 + x2 + 1) * time_z;
let style = Sequencer::<Tui>::style_timer_step(now, step as usize, next_step as usize);
to.blit(&"-", x as u16, y, Some(style));
}
}
let area = [
area.x() + Sequencer::H_KEYS_OFFSET as u16,
area.y() + 1,
area.w().saturating_sub(Sequencer::H_KEYS_OFFSET as u16),
area.h().saturating_sub(2),
];
Ok(())
}).fill_x(),
// notes
CustomWidget::new(|_|Ok(Some([32,4])), |to: &mut TuiOutput|{
let offset = Sequencer::H_KEYS_OFFSET as u16;
if to.area().h() < 2 || to.area().w() < offset { return Ok(()) }
let area = to.area().push_x(offset).shrink_x(offset).shrink_y(2);
to.buffer_update(area, &move |cell, x, y|{
cell.set_bg(Color::Rgb(20, 20, 20));
let src_x = ((x as usize + self.time_axis.start) * self.time_axis.scale) as usize;
let src_y = (y as usize + self.note_axis.start) as usize;
if src_x < self.buffer.width && src_y < self.buffer.height - 1 {
let src = self.buffer.get(src_x, self.buffer.height - src_y);
src.map(|src|{
cell.set_symbol(src.symbol());
cell.set_fg(src.fg);
});
src.map(|src|{ cell.set_symbol(src.symbol()); cell.set_fg(src.fg); });
}
});
Ok(())//Some(area))
Ok(())
}).fill_x(),
// cursor
CustomWidget::new(|to|Ok(Some([1,1])), |to: &mut TuiOutput|{
// note cursor
CustomWidget::new(|_|Ok(Some([1,1])), |to: &mut TuiOutput|{
let area = to.area();
if let (Some(time), Some(note)) = (self.time_axis.point, self.note_axis.point) {
let x = area.x() + Sequencer::H_KEYS_OFFSET as u16 + time as u16;
let y = area.y() + 1 + note as u16 / 2;
let c = if note % 2 == 0 { "" } else { "" };
to.blit(&c, x, y, self.style_focus());
Ok(())
} else {
//Ok(Some([0,0,0,0]))
Ok(())
}
}),
//zoom
CustomWidget::new(|to|Ok(Some([10,1])), |to: &mut TuiOutput|{
let area = to.area();
let quant = ppq_to_name(self.time_axis.scale);
let quant_x = area.x() + area.w() - 1 - quant.len() as u16;
let quant_y = area.y() + area.h() - 2;
to.blit(&quant, quant_x, quant_y, self.style_focus());
Ok(())
}),
//zoom
CustomWidget::new(|_|Ok(Some([10,1])), |to: &mut TuiOutput|{
let [x, y, w, h] = to.area.xywh();
let quant = ppq_to_name(self.time_axis.scale);
let x = x + w - 1 - quant.len() as u16;
let y = y + h - 2;
to.blit(&quant, x, y, self.style_focus());
Ok(())
}),
);
row!(toolbar, content).fill_x()
.bg(Color::Rgb(40,50,30))
.bg(Color::Rgb(40, 50, 30))
.border(Lozenge(Style::default()
.bg(Color::Rgb(40,50,30))
.fg(Color::Rgb(70,80,50))))
.bg(Color::Rgb(40, 50, 30))
.fg(Color::Rgb(70, 80, 50))))
}
}
@ -1479,7 +1499,7 @@ impl Handle<Tui> for Sequencer<Tui> {
fn nth_octave (index: u16) -> &'static str {
match index {
0 => "-1",
0 => "-1",
1 => "0",
2 => "1",
3 => "2",
@ -1496,12 +1516,12 @@ fn nth_octave (index: u16) -> &'static str {
fn key_colors (index: u16) -> (Color, Color) {
match index % 6 {
0 => (Color::White, Color::Black),
1 => (Color::White, Color::Black),
2 => (Color::White, Color::White),
3 => (Color::Black, Color::White),
4 => (Color::Black, Color::White),
5 => (Color::Black, Color::White),
0 => (Color::Rgb(255, 255, 255), Color::Rgb(0, 0, 0)),
1 => (Color::Rgb(255, 255, 255), Color::Rgb(0, 0, 0)),
2 => (Color::Rgb(255, 255, 255), Color::Rgb(255, 255, 255)),
3 => (Color::Rgb(0, 0, 0), Color::Rgb(255, 255, 255)),
4 => (Color::Rgb(0, 0, 0), Color::Rgb(255, 255, 255)),
5 => (Color::Rgb(0, 0, 0), Color::Rgb(255, 255, 255)),
_ => unreachable!()
}
}
@ -1620,34 +1640,6 @@ pub(crate) fn keys_vert () -> Buffer {
//////////////////////////////////////////////////////////////////////////////////////////////////
struct SequenceTimer<'a>(&'a Sequencer<Tui>, Arc<RwLock<Phrase>>);
impl<'a> Widget for SequenceTimer<'a> {
type Engine = Tui;
fn layout (&self, _to: [u16;2]) -> Perhaps<[u16;2]> {
Ok(Some([32,2]))
}
fn render (&self, to: &mut TuiOutput) -> Usually<()> {
let area = to.area();
let phrase = self.1.read().unwrap();
let (time0, time_z, now) = (
self.0.time_axis.start, self.0.time_axis.scale, self.0.now % phrase.length
);
let [x, _, width, _] = area;
let x2 = x as usize + Sequencer::H_KEYS_OFFSET;
let x3 = x as usize + width as usize;
for x in x2..x3 {
let step = (time0 + x2) * time_z;
let next_step = (time0 + x2 + 1) * time_z;
let style = Sequencer::<Tui>::style_timer_step(now, step as usize, next_step as usize);
to.blit(&"-", x as u16, area.y(), Some(style));
}
//return Ok(Some([area.x(), area.y(), area.w(), 1]))
Ok(())
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////
/// A collection of phrases to play on each track.
#[derive(Default)]
pub struct Scene {
@ -2013,21 +2005,17 @@ impl Handle<Tui> for TransportToolbar<Tui> {
impl Content for TransportToolbar<Tui> {
type Engine = Tui;
fn content (&self) -> impl Widget<Engine = Tui> {
Stack::right(|add|{
let focus_wrap = |focused, component|Layers::new(move |add|{
if focused {
add(&CORNERS)?;
add(&Background(COLOR_BG1))?;
}
add(component)
});
add(&focus_wrap(self.focused && self.playing.focused, &self.playing))?;
add(&focus_wrap(self.focused && self.bpm.focused, &self.bpm))?;
add(&focus_wrap(self.focused && self.quant.focused, &self.quant))?;
add(&focus_wrap(self.focused && self.sync.focused, &self.sync))?;
add(&focus_wrap(self.focused && self.clock.focused, &self.clock))?;
Ok(())
})
let focus_wrap = |focused, component|Layers::new(move |add|{
if focused { add(&CORNERS)?; add(&Background(COLOR_BG1))?; }
add(component)
});
row! {
focus_wrap(self.focused && self.playing.focused, &self.playing),
focus_wrap(self.focused && self.bpm.focused, &self.bpm),
focus_wrap(self.focused && self.quant.focused, &self.quant),
focus_wrap(self.focused && self.sync.focused, &self.sync),
focus_wrap(self.focused && self.clock.focused, &self.clock),
}.fill_x().bg(Color::Rgb(25, 30, 20))
}
}