starting to look very much like something

This commit is contained in:
🪞👃🪞 2025-05-17 17:59:43 +03:00
parent f1f5ac63e1
commit 29b2789be6
7 changed files with 94 additions and 51 deletions

View file

@ -37,6 +37,8 @@ pub struct Arrangement {
pub arranger: Arc<RwLock<Buffer>>,
/// Display size
pub size: Measure<TuiOut>,
/// Display size of clips area
pub inner_size: Measure<TuiOut>,
}
has!(Jack: |self: Arrangement|self.jack);

View file

@ -9,23 +9,6 @@ pub trait HasScenes: Has<Vec<Scene>> + Send + Sync {
fn scenes_mut (&mut self) -> &mut Vec<Scene> {
Has::<Vec<Scene>>::get_mut(self)
}
fn scenes_with_sizes (
&self,
editing: bool,
height: usize,
larger: usize,
selected_track: Option<usize>,
selected_scene: Option<usize>,
) -> impl ScenesSizes<'_> {
let mut y = 0;
self.scenes().iter().enumerate().map(move|(s, scene)|{
let active = editing && selected_track.is_some() && selected_scene == Some(s);
let height = if active { larger } else { height };
let data = (s, scene, y, y + height);
y += height;
data
})
}
/// Generate the default name for a new scene
fn scene_default_name (&self) -> Arc<str> {
format!("s{:3>}", self.scenes().len() + 1).into()

View file

@ -1,12 +1,25 @@
use crate::*;
impl<T> TracksView for T
where T: ScenesView + HasMidiIns + HasMidiOuts + HasSize<TuiOut> + HasTrackScroll + HasSelection + HasMidiIns + HasEditor {}
pub trait HasClipsSize {
fn clips_size (&self) -> &Measure<TuiOut>;
}
impl HasClipsSize for Arrangement {
fn clips_size (&self) -> &Measure<TuiOut> { &self.inner_size }
}
impl TracksView for Arrangement {}
impl<T: TracksView + ScenesView + Send + Sync> ClipsView for T {}
pub trait TracksView:
ScenesView + HasMidiIns + HasMidiOuts + HasSize<TuiOut> + HasTrackScroll + HasSelection + HasMidiIns + HasEditor
ScenesView +
HasMidiIns +
HasMidiOuts +
HasSize<TuiOut> +
HasTrackScroll +
HasSelection +
HasEditor +
HasClipsSize
{
fn tracks_width_available (&self) -> u16 {
(self.width() as u16).saturating_sub(40)
@ -16,11 +29,15 @@ pub trait TracksView:
let editor_width = self.editor().map(|e|e.width());
let active_track = self.selection().track();
let mut x = 0;
self.tracks().iter().enumerate().map(move |(index, track)|{
let width = active_track.and_then(|_|editor_width).unwrap_or(track.width.max(8));
let data = (index, track, x, x + width);
x += width + Self::TRACK_SPACING;
data
self.tracks().iter().enumerate().map_while(move |(index, track)|{
if x < self.clips_size().w() {
let width = active_track.and_then(|_|editor_width).unwrap_or(track.width.max(8));
let data = (index, track, x, x + width);
x += width + Self::TRACK_SPACING;
Some(data)
} else {
None
}
})
}
fn tracks_with_sizes_scrolled <'t> (&'t self) -> impl TracksSizes<'t> {
@ -53,7 +70,7 @@ pub trait TracksView:
fn view_track_names (&self, theme: ItemTheme) -> impl Content<TuiOut> {
self.view_track_row_section(
theme,
button_2("t", "rack", false),
button_2("t", "rack", false),
button_2("T", "+", false),
Tui::bg(theme.darker.rgb, Fixed::y(1, Fill::x(
Stack::east(move|add: &mut dyn FnMut(&dyn Render<TuiOut>)|{
@ -130,7 +147,11 @@ pub trait TracksView:
add(&Fixed::xy(self.track_width(index, track), h + 1,
Align::nw(Bsp::s(
Tui::bg(track.color.base.rgb,
Fill::x(Align::w(format!("·mon ·rec ·dub")))),
Fill::x(Align::w(row!(
Either(track.sequencer.monitoring, Tui::fg(Green, "●mon "), "·mon "),
Either(track.sequencer.recording, Tui::fg(Red, "●rec "), "·rec "),
Either(track.sequencer.overdub, Tui::fg(Yellow, "●dub "), "·dub "),
)))),
Map::south(1, ||track.sequencer.midi_ins.iter(),
|port, index|Tui::fg_bg(Rgb(255, 255, 255), track.color.dark.rgb,
Fill::x(Align::w(format!("·i{index:02} {}", port.name())))))))));
@ -147,7 +168,14 @@ pub trait TracksView:
}
}
pub trait ScenesView: HasEditor + HasSelection + HasSceneScroll + Send + Sync {
pub trait ScenesView:
HasEditor +
HasSelection +
HasSceneScroll +
HasClipsSize +
Send +
Sync
{
/// Default scene height.
const H_SCENE: usize = 2;
/// Default editor height.
@ -155,9 +183,28 @@ pub trait ScenesView: HasEditor + HasSelection + HasSceneScroll + Send + Sync {
fn h_scenes (&self) -> u16;
fn w_side (&self) -> u16;
fn w_mid (&self) -> u16;
fn scenes_with_sizes (&self) -> impl ScenesSizes<'_> {
let editing = self.editor().is_some();
let height = Self::H_SCENE;
let larger = self.editor().map(|e|e.height()).unwrap_or(Self::H_SCENE);
let selected_track = self.selection().track();
let selected_scene = self.selection().scene();
let mut y = 0;
self.scenes().iter().enumerate().skip(self.scene_scroll()).map_while(move|(s, scene)|{
if y < self.clips_size().h() {
let active = editing && selected_track.is_some() && selected_scene == Some(s);
let height = if active { larger } else { height };
let data = (s, scene, y, y + height);
y += height;
Some(data)
} else {
None
}
})
}
fn view_scenes_names (&self) -> impl Content<TuiOut> {
Stack::south(move|add: &mut dyn FnMut(&dyn Render<TuiOut>)|{
for (index, scene) in self.scenes().iter().enumerate().skip(self.scene_scroll()) {
for (index, scene, ..) in self.scenes_with_sizes() {
add(&self.view_scene_name(index, scene));
}
})
@ -166,7 +213,7 @@ pub trait ScenesView: HasEditor + HasSelection + HasSceneScroll + Send + Sync {
let h = if self.selection().scene() == Some(index) && let Some(editor) = self.editor() {
(editor.height() as u16).max(12)
} else {
Self::H_SCENE as u16
Self::H_SCENE as u16
};
let bg = if self.selection().scene() == Some(index) {
scene.color.light.rgb
@ -185,30 +232,38 @@ pub trait ScenesView: HasEditor + HasSelection + HasSceneScroll + Send + Sync {
}
}
pub trait ClipsView: TracksView + ScenesView + Send + Sync {
pub trait ClipsView:
TracksView +
ScenesView +
HasClipsSize +
Send +
Sync
{
fn view_scenes_clips <'a> (&'a self)
-> impl Content<TuiOut> + 'a
{
Fill::xy(Stack::<TuiOut, _>::east(move|column: &mut dyn FnMut(&dyn Render<TuiOut>)|{
for (track_index, track, _, _) in self.tracks_with_sizes() {
//column(&Fixed::x(5, Fill::xy(Tui::bg(Green, "kyp"))));
column(&Fixed::x(
if self.selection().track() == Some(track_index)
&& let Some(editor) = self.editor()
{
editor.width().max(24).max(track.width)
} else {
track.width
} as u16,
Fill::y(self.view_track_clips(track_index, track))
))
}
}))
self.clips_size().of(Fill::xy(Bsp::a(
Fill::xy(Align::se(Tui::fg(Green, format!("{}x{}", self.clips_size().w(), self.clips_size().h())))),
Stack::<TuiOut, _>::east(move|column: &mut dyn FnMut(&dyn Render<TuiOut>)|{
for (track_index, track, _, _) in self.tracks_with_sizes() {
//column(&Fixed::x(5, Fill::xy(Tui::bg(Green, "kyp"))));
column(&Fixed::x(
if self.selection().track() == Some(track_index)
&& let Some(editor) = self.editor()
{
editor.width().max(24).max(track.width)
} else {
track.width
} as u16,
Fill::y(self.view_track_clips(track_index, track))
))
}
}))))
}
fn view_track_clips <'a> (&'a self, track_index: usize, track: &'a Track) -> impl Content<TuiOut> + 'a {
Stack::south(move|cell: &mut dyn FnMut(&dyn Render<TuiOut>)|{
for (scene_index, scene) in self.scenes().iter().enumerate().skip(self.scene_scroll()) {
for (scene_index, scene, ..) in self.scenes_with_sizes() {
let (name, theme): (Arc<str>, ItemTheme) = if let Some(Some(clip)) = &scene.clips.get(track_index) {
let clip = clip.read().unwrap();
(clip.name.clone(), clip.color)

View file

@ -51,7 +51,7 @@ impl Default for Sequencer {
play_clip: None,
next_clip: None,
recording: false,
monitoring: false,
monitoring: true,
overdub: false,
notes_in: RwLock::new([false;128]).into(),