mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 03:36:41 +01:00
This commit is contained in:
parent
4ba88bfd6d
commit
9aeb792f7d
6 changed files with 197 additions and 176 deletions
|
|
@ -21,12 +21,11 @@
|
||||||
(bsp/s
|
(bsp/s
|
||||||
(fixed/y 8 (bsp/e
|
(fixed/y 8 (bsp/e
|
||||||
(fixed/x 20 (fill/y (align/n (bsp/s :view-status-v
|
(fixed/x 20 (fill/y (align/n (bsp/s :view-status-v
|
||||||
(bsp/s :view-audio-ports-status :view-editor-status)))))
|
(bsp/s (bsp/s :view-audio-ins-status :view-audio-outs-status)
|
||||||
|
:view-editor-status)))))
|
||||||
(fill/xy (align/n (bsp/s :view-arranger-track-names
|
(fill/xy (align/n (bsp/s :view-arranger-track-names
|
||||||
(bsp/s :view-arranger-track-outputs
|
(bsp/s :view-arranger-track-outputs
|
||||||
(bsp/s :view-arranger-track-devices :view-arranger-track-inputs)))))))
|
(bsp/s :view-arranger-track-devices :view-arranger-track-inputs)))))))
|
||||||
(bsp/w
|
(bsp/w
|
||||||
(fixed/x 20 :view-pool)
|
(fixed/x 20 :view-pool)
|
||||||
(bsp/e
|
:view-arranger-scenes)))
|
||||||
(fixed/x 20 (fill/xy (align/n :view-arranger-scene-names)))
|
|
||||||
(fill/xy (align/n :view-arranger-scene-clips)))))))
|
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,9 @@ impl App {
|
||||||
pub fn view_arranger (&self) -> impl Content<TuiOut> + use<'_> {
|
pub fn view_arranger (&self) -> impl Content<TuiOut> + use<'_> {
|
||||||
ArrangerView::new(&self.project, self.editor.as_ref())
|
ArrangerView::new(&self.project, self.editor.as_ref())
|
||||||
}
|
}
|
||||||
|
pub fn view_arranger_scenes (&self) -> impl Content<TuiOut> + use<'_> {
|
||||||
|
self.scenes_view(&self.editor)
|
||||||
|
}
|
||||||
pub fn view_arranger_scene_names <'a> (&'a self) -> impl Content<TuiOut> + use<'a> {
|
pub fn view_arranger_scene_names <'a> (&'a self) -> impl Content<TuiOut> + use<'a> {
|
||||||
let h = self.project.scenes.len() as u16 * 2;
|
let h = self.project.scenes.len() as u16 * 2;
|
||||||
let bg = self.color.darker.rgb;
|
let bg = self.color.darker.rgb;
|
||||||
|
|
@ -352,6 +355,33 @@ impl App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ArrangerSceneRows for App {
|
||||||
|
fn arrangement (&self) -> &Arrangement {
|
||||||
|
self.project
|
||||||
|
}
|
||||||
|
fn scenes_height (&self) -> u16 {
|
||||||
|
self.project.scenes_height
|
||||||
|
}
|
||||||
|
fn width_side (&self) -> u16 {
|
||||||
|
20
|
||||||
|
}
|
||||||
|
fn width_mid (&self) -> u16 {
|
||||||
|
self.width().saturating_sub(self.width_side() * 2)
|
||||||
|
}
|
||||||
|
fn scene_selected (&self) -> Option<usize> {
|
||||||
|
self.project.selection.scene()
|
||||||
|
}
|
||||||
|
fn scene_last (&self) -> usize {
|
||||||
|
self.project.scenes.len().saturating_sub(1)
|
||||||
|
}
|
||||||
|
fn track_selected (&self) -> Option<usize> {
|
||||||
|
self.project.selection.track()
|
||||||
|
}
|
||||||
|
fn is_editing (&self) -> bool {
|
||||||
|
self.is_editing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn heading <'a> (
|
pub(crate) fn heading <'a> (
|
||||||
key: &'a str,
|
key: &'a str,
|
||||||
label: &'a str,
|
label: &'a str,
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ impl<'a> ArrangerView<'a> {
|
||||||
.left(self.width_side,
|
.left(self.width_side,
|
||||||
io_ports(Tui::g(224), Tui::g(32), ||self.arrangement.midi_ins_with_sizes()))
|
io_ports(Tui::g(224), Tui::g(32), ||self.arrangement.midi_ins_with_sizes()))
|
||||||
.middle(self.width_mid,
|
.middle(self.width_mid,
|
||||||
per_track_top(self.width_mid, ||self.tracks_with_sizes_scrolled(),
|
per_track_top(||self.tracks_with_sizes_scrolled(),
|
||||||
move|_, &Track { color, .. }|io_conns(
|
move|_, &Track { color, .. }|io_conns(
|
||||||
color.dark.rgb,
|
color.dark.rgb,
|
||||||
color.darker.rgb,
|
color.darker.rgb,
|
||||||
|
|
@ -22,9 +22,7 @@ impl<'a> ArrangerView<'a> {
|
||||||
.right(self.width_side,
|
.right(self.width_side,
|
||||||
button_2("I", "add midi in", self.is_editing))
|
button_2("I", "add midi in", self.is_editing))
|
||||||
.middle(self.width_mid,
|
.middle(self.width_mid,
|
||||||
per_track_top(
|
per_track_top(||self.tracks_with_sizes_scrolled(),
|
||||||
self.width_mid,
|
|
||||||
||self.tracks_with_sizes_scrolled(),
|
|
||||||
move|t, track|{
|
move|t, track|{
|
||||||
let rec = track.sequencer.recording;
|
let rec = track.sequencer.recording;
|
||||||
let mon = track.sequencer.monitoring;
|
let mon = track.sequencer.monitoring;
|
||||||
|
|
@ -48,9 +46,7 @@ impl<'a> ArrangerView<'a> {
|
||||||
.left(self.width_side,
|
.left(self.width_side,
|
||||||
Bsp::s(Align::e("Input:"), Align::e("Into clip:")))
|
Bsp::s(Align::e("Input:"), Align::e("Into clip:")))
|
||||||
.middle(self.width_mid,
|
.middle(self.width_mid,
|
||||||
per_track_top(
|
per_track_top(||self.tracks_with_sizes_scrolled(),
|
||||||
self.width_mid,
|
|
||||||
||self.tracks_with_sizes_scrolled(),
|
|
||||||
|_, _|Tui::bg(Reset, Align::c(Bsp::s(OctaveVertical::default(), " ------ ")))))
|
|_, _|Tui::bg(Reset, Align::c(Bsp::s(OctaveVertical::default(), " ------ ")))))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -61,16 +57,14 @@ impl<'a> ArrangerView<'a> {
|
||||||
pub(crate) fn output_nexts (&self) -> impl Content<TuiOut> + '_ {
|
pub(crate) fn output_nexts (&self) -> impl Content<TuiOut> + '_ {
|
||||||
Tryptich::top(2)
|
Tryptich::top(2)
|
||||||
.left(self.width_side, Align::ne("From clip:"))
|
.left(self.width_side, Align::ne("From clip:"))
|
||||||
.middle(self.width_mid, per_track_top(
|
.middle(self.width_mid, per_track_top(||self.tracks_with_sizes_scrolled(),
|
||||||
self.width_mid,
|
|
||||||
||self.tracks_with_sizes_scrolled(),
|
|
||||||
|_, _|Tui::bg(Reset, Align::c(Bsp::s(" ------ ", OctaveVertical::default())))))
|
|_, _|Tui::bg(Reset, Align::c(Bsp::s(" ------ ", OctaveVertical::default())))))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn output_froms (&'a self) -> impl Content<TuiOut> + 'a {
|
pub(crate) fn output_froms (&'a self) -> impl Content<TuiOut> + 'a {
|
||||||
let label = Align::ne("Next clip:");
|
let label = Align::ne("Next clip:");
|
||||||
Tryptich::top(2).left(self.width_side, label).middle(self.width_mid, per_track_top(
|
Tryptich::top(2).left(self.width_side, label).middle(self.width_mid, per_track_top(
|
||||||
self.width_mid, ||self.tracks_with_sizes_scrolled(), |t, track|{
|
||self.tracks_with_sizes_scrolled(), |t, track|{
|
||||||
let queued = track.sequencer.next_clip.is_some();
|
let queued = track.sequencer.next_clip.is_some();
|
||||||
let queued_blank = Thunk::new(||Tui::bg(Reset, " ------ "));
|
let queued_blank = Thunk::new(||Tui::bg(Reset, " ------ "));
|
||||||
let queued_clip = Thunk::new(||{
|
let queued_clip = Thunk::new(||{
|
||||||
|
|
@ -109,7 +103,7 @@ impl<'a> ArrangerView<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn output_map (&'a self) -> impl Content<TuiOut> + 'a {
|
pub(crate) fn output_map (&'a self) -> impl Content<TuiOut> + 'a {
|
||||||
per_track_top(self.width_mid, ||self.tracks_with_sizes_scrolled(), move|i, t|{
|
per_track_top(||self.tracks_with_sizes_scrolled(), move|i, t|{
|
||||||
let mute = false;
|
let mute = false;
|
||||||
let solo = false;
|
let solo = false;
|
||||||
let mute = if mute { White } else { t.color.darkest.rgb };
|
let mute = if mute { White } else { t.color.darkest.rgb };
|
||||||
|
|
@ -130,9 +124,7 @@ impl<'a> ArrangerView<'a> {
|
||||||
Tryptich::top(self.arrangement.h_outputs())
|
Tryptich::top(self.arrangement.h_outputs())
|
||||||
.left(self.width_side, io_ports(
|
.left(self.width_side, io_ports(
|
||||||
Tui::g(224), Tui::g(32), ||self.arrangement.midi_outs_with_sizes()))
|
Tui::g(224), Tui::g(32), ||self.arrangement.midi_outs_with_sizes()))
|
||||||
.middle(self.width_mid, per_track_top(
|
.middle(self.width_mid, per_track_top(||self.tracks_with_sizes_scrolled(),
|
||||||
self.width_mid,
|
|
||||||
||self.tracks_with_sizes_scrolled(),
|
|
||||||
|_, t|io_conns(
|
|_, t|io_conns(
|
||||||
t.color.dark.rgb,
|
t.color.dark.rgb,
|
||||||
t.color.darker.rgb,
|
t.color.darker.rgb,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,156 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
|
pub type SceneWith<'a, T: Send + Sync> = (usize, &'a Scene, usize, usize, T);
|
||||||
|
|
||||||
|
pub trait ArrangerSceneRows: Send + Sync {
|
||||||
|
/// Default scene height.
|
||||||
|
const H_SCENE: usize = 2;
|
||||||
|
/// Default editor height.
|
||||||
|
const H_EDITOR: usize = 15;
|
||||||
|
/// Render scenes with clips
|
||||||
|
fn scenes_view <'a> (&'a self, editor: &'a Option<MidiEditor>) -> impl Content<TuiOut> + 'a {
|
||||||
|
Tryptich::center(self.scenes_height())
|
||||||
|
.left(self.width_side(), self.scenes_names())
|
||||||
|
.middle(self.width_mid(), self.scenes_clips(editor))
|
||||||
|
}
|
||||||
|
fn is_editing (&self) -> bool;
|
||||||
|
fn arrangement (&self) -> &Arrangement;
|
||||||
|
fn scene_last (&self) -> usize;
|
||||||
|
fn scene_selected (&self) -> Option<usize>;
|
||||||
|
fn track_selected (&self) -> Option<usize>;
|
||||||
|
fn scenes_height (&self) -> u16;
|
||||||
|
fn width_side (&self) -> u16;
|
||||||
|
fn width_mid (&self) -> u16;
|
||||||
|
fn scenes_names (&self) -> impl Content<TuiOut> {
|
||||||
|
Map::new(move||self.scenes_with_prev_color(),
|
||||||
|
move|(s, scene, y1, y2, previous): SceneWith<'_, Option<ItemTheme>>, _|{
|
||||||
|
let height = (1 + y2 - y1) as u16;
|
||||||
|
let name = Some(scene.name.clone());
|
||||||
|
let content = Fill::x(Align::w(Tui::bold(true, Bsp::e(" ⯈ ", name))));
|
||||||
|
let selected = self.scene_selected() == Some(s);
|
||||||
|
let neighbor = s > 0 && self.scene_selected() == Some(s - 1);
|
||||||
|
let is_last = self.scene_last() == s;
|
||||||
|
let theme = scene.color;
|
||||||
|
let fg = theme.lightest.rgb;
|
||||||
|
let bg = if selected { theme.light } else { theme.base }.rgb;
|
||||||
|
let hi = if let Some(previous) = previous {
|
||||||
|
if neighbor { previous.light.rgb } else { previous.base.rgb }
|
||||||
|
} else {
|
||||||
|
Reset
|
||||||
|
};
|
||||||
|
let lo = if is_last { Reset } else if selected {
|
||||||
|
theme.light.rgb
|
||||||
|
} else {
|
||||||
|
theme.base.rgb
|
||||||
|
};
|
||||||
|
Fill::x(map_south(y1 as u16, height, Fixed::y(height, Phat {
|
||||||
|
width: 0, height: 0, content, colors: [fg, bg, hi, lo]
|
||||||
|
})))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn scenes_with_prev_color (&self) -> impl Iterator<Item=SceneWith<Option<ItemTheme>>> + Send + Sync {
|
||||||
|
self.scenes_iter().map(|(s, scene, y1, y2)|(s, scene, y1, y2,
|
||||||
|
(s>0).then_some(self.arrangement().scenes()[s-1].color)))
|
||||||
|
}
|
||||||
|
fn scenes_clips <'a> (&'a self, editor: &'a Option<MidiEditor>)
|
||||||
|
-> impl Content<TuiOut> + 'a
|
||||||
|
{
|
||||||
|
per_track(||self.tracks_with_sizes_scrolled(),
|
||||||
|
move|track_index, track|Map::new(move||self.scenes_with_clip(track_index),
|
||||||
|
move|(s, scene, y1, y2, previous): SceneWith<'_, Option<ItemTheme>>, _|{
|
||||||
|
let (name, theme) = if let Some(clip) = &scene.clips[track_index] {
|
||||||
|
let clip = clip.read().unwrap();
|
||||||
|
(Some(clip.name.clone()), clip.color)
|
||||||
|
} else {
|
||||||
|
(None, ItemTheme::G[32])
|
||||||
|
};
|
||||||
|
let height = (1 + y2 - y1) as u16;
|
||||||
|
let content = Fill::x(Align::w(Tui::bold(true, Bsp::e(" ⏹ ", name))));
|
||||||
|
let same_track = self.track_selected() == Some(track_index);
|
||||||
|
let selected = same_track && self.scene_selected() == Some(s);
|
||||||
|
let neighbor = same_track && s > 0 && self.scene_selected() == Some(s - 1);
|
||||||
|
let is_last = self.scene_last() == s;
|
||||||
|
let fg = theme.lightest.rgb;
|
||||||
|
let bg = if selected { theme.light } else { theme.base }.rgb;
|
||||||
|
let hi = if let Some(previous) = previous {
|
||||||
|
if neighbor {
|
||||||
|
previous.light.rgb
|
||||||
|
} else {
|
||||||
|
previous.base.rgb
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Reset
|
||||||
|
};
|
||||||
|
let lo = if is_last {
|
||||||
|
Reset
|
||||||
|
} else if selected {
|
||||||
|
theme.light.rgb
|
||||||
|
} else {
|
||||||
|
theme.base.rgb
|
||||||
|
};
|
||||||
|
map_south(y1 as u16, height, Bsp::b(Fixed::y(height, Phat {
|
||||||
|
width: 0, height: 0, content, colors: [fg, bg, hi, lo]
|
||||||
|
}), When(
|
||||||
|
self.is_editing() && same_track && self.scene_selected() == Some(s),
|
||||||
|
editor
|
||||||
|
)))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
fn scenes_with_clip (&self, track_index: usize) -> impl Iterator<Item=SceneWith<'_, Option<ItemTheme>>> + Send + Sync {
|
||||||
|
self.scenes_iter().map(move|(s, scene, y1, y2)|(s, scene, y1, y2,
|
||||||
|
(s>0).then_some(self.arrangement().scenes()[s-1].clips[track_index].as_ref()
|
||||||
|
.map(|c|c.read().unwrap().color)
|
||||||
|
.unwrap_or(ItemTheme::G[32]))))
|
||||||
|
}
|
||||||
|
/// A scene with size and color.
|
||||||
|
fn scenes_iter (&self) -> impl Iterator<Item=(usize, &Scene, usize, usize,)> + Send + Sync {
|
||||||
|
let selection = Has::<Selection>::get(self.arrangement());
|
||||||
|
self.arrangement().scenes_with_sizes(
|
||||||
|
self.is_editing(),
|
||||||
|
Self::H_SCENE, Self::H_EDITOR,
|
||||||
|
selection.track(), selection.scene(),
|
||||||
|
).map_while(|(s, scene, y1, y2)|(y2<=self.scenes_height() as usize)
|
||||||
|
.then_some((s, scene, y1, y2)))
|
||||||
|
}
|
||||||
|
fn tracks_with_sizes_scrolled <'t> (&'t self) -> impl TracksSizes<'t> {
|
||||||
|
self.arrangement()
|
||||||
|
.tracks_with_sizes(
|
||||||
|
&self.arrangement().selection(),
|
||||||
|
self.is_editing().then_some(20/*FIXME*/)
|
||||||
|
)
|
||||||
|
.map_while(move|(t, track, x1, x2)|{
|
||||||
|
(self.width_mid() > x2 as u16).then_some((t, track, x1, x2))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ArrangerSceneRows for ArrangerView<'a> {
|
||||||
|
fn arrangement (&self) -> &Arrangement {
|
||||||
|
self.arrangement
|
||||||
|
}
|
||||||
|
fn scenes_height (&self) -> u16 {
|
||||||
|
self.scenes_height
|
||||||
|
}
|
||||||
|
fn width_side (&self) -> u16 {
|
||||||
|
self.width_side
|
||||||
|
}
|
||||||
|
fn width_mid (&self) -> u16 {
|
||||||
|
self.width_mid
|
||||||
|
}
|
||||||
|
fn scene_selected (&self) -> Option<usize> {
|
||||||
|
self.arrangement.selection.scene()
|
||||||
|
}
|
||||||
|
fn scene_last (&self) -> usize {
|
||||||
|
self.scene_last
|
||||||
|
}
|
||||||
|
fn track_selected (&self) -> Option<usize> {
|
||||||
|
self.arrangement.selection.track()
|
||||||
|
}
|
||||||
|
fn is_editing (&self) -> bool {
|
||||||
|
self.is_editing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: Has<Vec<Scene>> + Send + Sync> HasScenes for T {}
|
impl<T: Has<Vec<Scene>> + Send + Sync> HasScenes for T {}
|
||||||
|
|
||||||
pub trait HasScenes: Has<Vec<Scene>> + Send + Sync {
|
pub trait HasScenes: Has<Vec<Scene>> + Send + Sync {
|
||||||
|
|
|
||||||
|
|
@ -69,21 +69,6 @@ pub trait HasTracks: Has<Vec<Track>> + Send + Sync {
|
||||||
const TRACK_SPACING: usize = 0;
|
const TRACK_SPACING: usize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ArrangerView<'a> {
|
|
||||||
pub(crate) fn tracks_with_sizes_scrolled (&'a self)
|
|
||||||
-> impl TracksSizes<'a>
|
|
||||||
{
|
|
||||||
self.arrangement
|
|
||||||
.tracks_with_sizes(
|
|
||||||
&self.arrangement.selection(),
|
|
||||||
self.is_editing.then_some(20/*FIXME*/)
|
|
||||||
)
|
|
||||||
.map_while(move|(t, track, x1, x2)|{
|
|
||||||
(self.width_mid > x2 as u16).then_some((t, track, x1, x2))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Arrangement {
|
impl Arrangement {
|
||||||
/// Add multiple tracks
|
/// Add multiple tracks
|
||||||
pub fn tracks_add (
|
pub fn tracks_add (
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ impl<'a> Content<TuiOut> for ArrangerView<'a> {
|
||||||
let bg = |x|Tui::bg(Reset, x);
|
let bg = |x|Tui::bg(Reset, x);
|
||||||
//let track_scroll = |x|Bsp::s(&self.track_scroll, x);
|
//let track_scroll = |x|Bsp::s(&self.track_scroll, x);
|
||||||
//let scene_scroll = |x|Bsp::e(&self.scene_scroll, x);
|
//let scene_scroll = |x|Bsp::e(&self.scene_scroll, x);
|
||||||
self.arrangement.size.of(outs(tracks(devices(ins(bg(self.scenes(&None)))))))
|
self.arrangement.size.of(outs(tracks(devices(ins(bg(self.scenes_view(&None)))))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,7 +72,6 @@ impl<'a> ArrangerView<'a> {
|
||||||
Bsp::s(self.input_routes(), self.input_ports()),
|
Bsp::s(self.input_routes(), self.input_ports()),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render output matrix.
|
/// Render output matrix.
|
||||||
pub(crate) fn outputs (&'a self) -> impl Content<TuiOut> + 'a {
|
pub(crate) fn outputs (&'a self) -> impl Content<TuiOut> + 'a {
|
||||||
Tui::bg(Reset, Align::n(Bsp::s(
|
Tui::bg(Reset, Align::n(Bsp::s(
|
||||||
|
|
@ -80,14 +79,14 @@ impl<'a> ArrangerView<'a> {
|
||||||
Bsp::s(self.output_nexts(), self.output_froms()),
|
Bsp::s(self.output_nexts(), self.output_froms()),
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render track headers
|
/// Render track headers
|
||||||
pub(crate) fn tracks (&'a self) -> impl Content<TuiOut> + 'a {
|
pub(crate) fn tracks (&'a self) -> impl Content<TuiOut> + 'a {
|
||||||
let Self { width_side, width_mid, track_selected, is_editing, .. } = self;
|
let Self { width_side, width_mid, track_selected, is_editing, .. } = self;
|
||||||
Tryptich::center(3)
|
Tryptich::center(3)
|
||||||
.left(*width_side, button_3("t", "track", format!("{}", self.arrangement.tracks.len()), *is_editing))
|
.left(*width_side,
|
||||||
|
button_3("t", "track", format!("{}", self.arrangement.tracks.len()), *is_editing))
|
||||||
.right(*width_side, button_2("T", "add track", *is_editing))
|
.right(*width_side, button_2("T", "add track", *is_editing))
|
||||||
.middle(*width_mid, per_track(*width_mid, ||self.tracks_with_sizes_scrolled(),
|
.middle(*width_mid, per_track(||self.tracks_with_sizes_scrolled(),
|
||||||
|index, track|wrap(
|
|index, track|wrap(
|
||||||
if *track_selected == Some(index) {
|
if *track_selected == Some(index) {
|
||||||
track.color.light
|
track.color.light
|
||||||
|
|
@ -98,14 +97,13 @@ impl<'a> ArrangerView<'a> {
|
||||||
Tui::bold(true, Fill::xy(Align::nw(&track.name)))
|
Tui::bold(true, Fill::xy(Align::nw(&track.name)))
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render device switches.
|
/// Render device switches.
|
||||||
pub(crate) fn devices (&'a self) -> impl Content<TuiOut> + 'a {
|
pub(crate) fn devices (&'a self) -> impl Content<TuiOut> + 'a {
|
||||||
let Self { width_side, width_mid, track_selected, is_editing, .. } = self;
|
let Self { width_side, width_mid, track_selected, is_editing, .. } = self;
|
||||||
Tryptich::top(1)
|
Tryptich::top(1)
|
||||||
.left(*width_side, button_3("d", "devices", format!("{}", 0), *is_editing))
|
.left(*width_side, button_3("d", "devices", format!("{}", 0), *is_editing))
|
||||||
.right(*width_side, button_2("D", "add device", *is_editing))
|
.right(*width_side, button_2("D", "add device", *is_editing))
|
||||||
.middle(*width_mid, per_track_top(*width_mid, ||self.tracks_with_sizes_scrolled(),
|
.middle(*width_mid, per_track_top(||self.tracks_with_sizes_scrolled(),
|
||||||
move|index, track|{
|
move|index, track|{
|
||||||
let bg = if *track_selected == Some(index) {
|
let bg = if *track_selected == Some(index) {
|
||||||
track.color.light
|
track.color.light
|
||||||
|
|
@ -116,138 +114,9 @@ impl<'a> ArrangerView<'a> {
|
||||||
track.devices.get(0).map(|device|wrap(bg.rgb, fg, device.name()))
|
track.devices.get(0).map(|device|wrap(bg.rgb, fg, device.name()))
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Default scene height.
|
|
||||||
pub(crate) const H_SCENE: usize = 2;
|
|
||||||
|
|
||||||
/// Default editor height.
|
|
||||||
pub(crate) const H_EDITOR: usize = 15;
|
|
||||||
|
|
||||||
/// Render scenes with clips
|
|
||||||
pub(crate) fn scenes (&'a self, editor: &'a Option<MidiEditor>) -> impl Content<TuiOut> + 'a {
|
|
||||||
/// A scene with size and color.
|
|
||||||
type SceneWithColor<'a> = (usize, &'a Scene, usize, usize, Option<ItemTheme>);
|
|
||||||
let Self {
|
|
||||||
arrangement,
|
|
||||||
width, width_side, width_mid,
|
|
||||||
scenes_height, scene_last, scene_selected,
|
|
||||||
track_selected, is_editing, ..
|
|
||||||
} = self;
|
|
||||||
let selection = Has::<Selection>::get(self.arrangement);
|
|
||||||
let selected_track = selection.track();
|
|
||||||
let selected_scene = selection.scene();
|
|
||||||
Tryptich::center(*scenes_height)
|
|
||||||
|
|
||||||
.left(*width_side, Map::new(
|
|
||||||
move||arrangement.scenes_with_sizes(
|
|
||||||
*is_editing, Self::H_SCENE, Self::H_EDITOR, selected_track, selected_scene,
|
|
||||||
).map_while(|(s, scene, y1, y2)|if y2 as u16 > *scenes_height {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some((s, scene, y1, y2, if s == 0 {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(arrangement.scenes()[s-1].color)
|
|
||||||
}))
|
|
||||||
}),
|
|
||||||
move |(s, scene, y1, y2, previous): SceneWithColor, _|{
|
|
||||||
let height = (1 + y2 - y1) as u16;
|
|
||||||
let name = Some(scene.name.clone());
|
|
||||||
let content = Fill::x(Align::w(Tui::bold(true, Bsp::e(" ⯈ ", name))));
|
|
||||||
let same_track = true;
|
|
||||||
let selected = same_track && *scene_selected == Some(s);
|
|
||||||
let neighbor = same_track && s > 0 && *scene_selected == Some(s - 1);
|
|
||||||
let is_last = *scene_last == s;
|
|
||||||
let theme = scene.color;
|
|
||||||
let fg = theme.lightest.rgb;
|
|
||||||
let bg = if selected { theme.light } else { theme.base }.rgb;
|
|
||||||
let hi = if let Some(previous) = previous {
|
|
||||||
if neighbor {
|
|
||||||
previous.light.rgb
|
|
||||||
} else {
|
|
||||||
previous.base.rgb
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Reset
|
|
||||||
};
|
|
||||||
let lo = if is_last {
|
|
||||||
Reset
|
|
||||||
} else if selected {
|
|
||||||
theme.light.rgb
|
|
||||||
} else {
|
|
||||||
theme.base.rgb
|
|
||||||
};
|
|
||||||
Fill::x(map_south(y1 as u16, height, Fixed::y(height, Phat {
|
|
||||||
width: 0, height: 0, content, colors: [fg, bg, hi, lo]
|
|
||||||
})))
|
|
||||||
}))
|
|
||||||
|
|
||||||
.middle(*width_mid, per_track(
|
|
||||||
*width_mid,
|
|
||||||
||self.tracks_with_sizes_scrolled(),
|
|
||||||
move|track_index, track|Map::new(
|
|
||||||
move||arrangement.scenes_with_sizes(
|
|
||||||
self.is_editing,
|
|
||||||
Self::H_SCENE,
|
|
||||||
Self::H_EDITOR,
|
|
||||||
selected_track,
|
|
||||||
selected_scene,
|
|
||||||
).map_while(move|(s, scene, y1, y2)|if y2 as u16 > self.scenes_height {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some((s, scene, y1, y2, if s == 0 {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(self.arrangement.scenes[s-1].clips[track_index].as_ref()
|
|
||||||
.map(|c|c.read().unwrap().color)
|
|
||||||
.unwrap_or(ItemTheme::G[32]))
|
|
||||||
}))
|
|
||||||
}),
|
|
||||||
move|(s, scene, y1, y2, previous): SceneWithColor<'a>, _|{
|
|
||||||
let (name, theme) = if let Some(clip) = &scene.clips[track_index] {
|
|
||||||
let clip = clip.read().unwrap();
|
|
||||||
(Some(clip.name.clone()), clip.color)
|
|
||||||
} else {
|
|
||||||
(None, ItemTheme::G[32])
|
|
||||||
};
|
|
||||||
let height = (1 + y2 - y1) as u16;
|
|
||||||
let content = Fill::x(Align::w(Tui::bold(true, Bsp::e(" ⏹ ", name))));
|
|
||||||
let same_track = *track_selected == Some(track_index);
|
|
||||||
let selected = same_track && *scene_selected == Some(s);
|
|
||||||
let neighbor = same_track && s > 0 && *scene_selected == Some(s - 1);
|
|
||||||
let is_last = *scene_last == s;
|
|
||||||
let fg = theme.lightest.rgb;
|
|
||||||
let bg = if selected { theme.light } else { theme.base }.rgb;
|
|
||||||
let hi = if let Some(previous) = previous {
|
|
||||||
if neighbor {
|
|
||||||
previous.light.rgb
|
|
||||||
} else {
|
|
||||||
previous.base.rgb
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Reset
|
|
||||||
};
|
|
||||||
let lo = if is_last {
|
|
||||||
Reset
|
|
||||||
} else if selected {
|
|
||||||
theme.light.rgb
|
|
||||||
} else {
|
|
||||||
theme.base.rgb
|
|
||||||
};
|
|
||||||
map_south(y1 as u16, height, Bsp::b(Fixed::y(height, Phat {
|
|
||||||
width: 0, height: 0, content, colors: [fg, bg, hi, lo]
|
|
||||||
}), When(
|
|
||||||
*is_editing && same_track && *scene_selected == Some(s),
|
|
||||||
editor
|
|
||||||
)))
|
|
||||||
})))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn per_track_top <'a, T: Content<TuiOut> + 'a, U: TracksSizes<'a>> (
|
pub(crate) fn per_track_top <'a, T: Content<TuiOut> + 'a, U: TracksSizes<'a>> (
|
||||||
width: u16,
|
|
||||||
tracks: impl Fn() -> U + Send + Sync + 'a,
|
tracks: impl Fn() -> U + Send + Sync + 'a,
|
||||||
callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
|
callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
|
||||||
) -> impl Content<TuiOut> + 'a {
|
) -> impl Content<TuiOut> + 'a {
|
||||||
|
|
@ -261,13 +130,8 @@ pub(crate) fn per_track_top <'a, T: Content<TuiOut> + 'a, U: TracksSizes<'a>> (
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn per_track <'a, T: Content<TuiOut> + 'a, U: TracksSizes<'a>> (
|
pub(crate) fn per_track <'a, T: Content<TuiOut> + 'a, U: TracksSizes<'a>> (
|
||||||
width: u16,
|
|
||||||
tracks: impl Fn() -> U + Send + Sync + 'a,
|
tracks: impl Fn() -> U + Send + Sync + 'a,
|
||||||
callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
|
callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
|
||||||
) -> impl Content<TuiOut> + 'a {
|
) -> impl Content<TuiOut> + 'a {
|
||||||
per_track_top(
|
per_track_top(tracks, move|index, track|Fill::y(Align::y(callback(index, track))))
|
||||||
width,
|
|
||||||
tracks,
|
|
||||||
move|index, track|Fill::y(Align::y(callback(index, track)))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue