mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
Some checks failed
/ build (push) Has been cancelled
the long-standing architectural issues around how the Draw, Layout, and Content traits from Tengri should, well, actually work - that has subsided for now. somewhat. going to amend this commit with fixes to the remaining import not founds, and then... what?
191 lines
6.5 KiB
Rust
191 lines
6.5 KiB
Rust
use crate::*;
|
|
|
|
def_sizes_iter!(ScenesSizes => Scene);
|
|
|
|
impl<T: Has<Vec<Scene>> + Send + Sync> HasScenes for T {}
|
|
|
|
pub trait HasScenes: Has<Vec<Scene>> + Send + Sync {
|
|
fn scenes (&self) -> &Vec<Scene> {
|
|
Has::<Vec<Scene>>::get(self)
|
|
}
|
|
fn scenes_mut (&mut self) -> &mut Vec<Scene> {
|
|
Has::<Vec<Scene>>::get_mut(self)
|
|
}
|
|
/// Generate the default name for a new scene
|
|
fn scene_default_name (&self) -> Arc<str> {
|
|
format!("s{:3>}", self.scenes().len() + 1).into()
|
|
}
|
|
fn scene_longest_name (&self) -> usize {
|
|
self.scenes().iter().map(|s|s.name.len()).fold(0, usize::max)
|
|
}
|
|
}
|
|
|
|
pub trait HasSceneScroll: HasScenes {
|
|
fn scene_scroll (&self) -> usize;
|
|
}
|
|
impl HasSceneScroll for Arrangement {
|
|
fn scene_scroll (&self) -> usize { self.scene_scroll }
|
|
}
|
|
|
|
pub type SceneWith<'a, T> = (usize, &'a Scene, usize, usize, T);
|
|
|
|
impl<T: HasScenes + HasTracks> AddScene for T {}
|
|
|
|
pub trait AddScene: HasScenes + HasTracks {
|
|
/// Add multiple scenes
|
|
fn scenes_add (&mut self, n: usize) -> Usually<()> {
|
|
let scene_color_1 = ItemColor::random();
|
|
let scene_color_2 = ItemColor::random();
|
|
for i in 0..n {
|
|
let _ = self.scene_add(None, Some(
|
|
scene_color_1.mix(scene_color_2, i as f32 / n as f32).into()
|
|
))?;
|
|
}
|
|
Ok(())
|
|
}
|
|
/// Add a scene
|
|
fn scene_add (&mut self, name: Option<&str>, color: Option<ItemTheme>)
|
|
-> Usually<(usize, &mut Scene)>
|
|
{
|
|
let scene = Scene {
|
|
name: name.map_or_else(||self.scene_default_name(), |x|x.to_string().into()),
|
|
clips: vec![None;self.tracks().len()],
|
|
color: color.unwrap_or_else(ItemTheme::random),
|
|
};
|
|
self.scenes_mut().push(scene);
|
|
let index = self.scenes().len() - 1;
|
|
Ok((index, &mut self.scenes_mut()[index]))
|
|
}
|
|
}
|
|
|
|
impl Scene {
|
|
fn _todo_opt_bool_stub_ (&self) -> Option<bool> { todo!() }
|
|
fn _todo_usize_stub_ (&self) -> usize { todo!() }
|
|
fn _todo_arc_str_stub_ (&self) -> Arc<str> { todo!() }
|
|
fn _todo_item_theme_stub (&self) -> ItemTheme { todo!() }
|
|
}
|
|
|
|
def_command!(SceneCommand: |scene: Scene| {
|
|
SetSize { size: usize } => { todo!() },
|
|
SetZoom { size: usize } => { todo!() },
|
|
SetName { name: Arc<str> } =>
|
|
swap_value(&mut scene.name, name, |name|Self::SetName{name}),
|
|
SetColor { color: ItemTheme } =>
|
|
swap_value(&mut scene.color, color, |color|Self::SetColor{color}),
|
|
});
|
|
|
|
impl<T: Has<Option<Scene>> + Send + Sync> HasScene for T {}
|
|
|
|
pub trait HasScene: Has<Option<Scene>> + Send + Sync {
|
|
fn scene (&self) -> Option<&Scene> {
|
|
Has::<Option<Scene>>::get(self).as_ref()
|
|
}
|
|
fn scene_mut (&mut self) -> &mut Option<Scene> {
|
|
Has::<Option<Scene>>::get_mut(self)
|
|
}
|
|
}
|
|
|
|
pub trait ScenesView:
|
|
HasEditor +
|
|
HasSelection +
|
|
HasSceneScroll +
|
|
HasClipsSize +
|
|
Send +
|
|
Sync
|
|
{
|
|
/// Default scene height.
|
|
const H_SCENE: usize = 2;
|
|
/// Default editor height.
|
|
const H_EDITOR: usize = 15;
|
|
fn h_scenes (&self) -> u16;
|
|
fn w_side (&self) -> u16;
|
|
fn w_mid (&self) -> u16;
|
|
fn scenes_with_sizes (&self) -> impl ScenesSizes<'_> {
|
|
let mut y = 0;
|
|
self.scenes().iter().enumerate().skip(self.scene_scroll()).map_while(move|(s, scene)|{
|
|
let height = if self.selection().scene() == Some(s) && self.editor().is_some() {
|
|
8
|
|
} else {
|
|
Self::H_SCENE
|
|
};
|
|
if y + height <= self.clips_size().h() {
|
|
let data = (s, scene, y, y + height);
|
|
y += height;
|
|
Some(data)
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
}
|
|
fn view_scenes_names (&self) -> impl Content<TuiOut> {
|
|
Fixed::X(20, Thunk::new(|to: &mut TuiOut|for (index, scene, ..) in self.scenes_with_sizes() {
|
|
to.place(&self.view_scene_name(index, scene));
|
|
}))
|
|
}
|
|
fn view_scene_name <'a> (&'a self, index: usize, scene: &'a Scene) -> impl Content<TuiOut> + 'a {
|
|
let h = if self.selection().scene() == Some(index) && let Some(_editor) = self.editor() {
|
|
7
|
|
} else {
|
|
Self::H_SCENE as u16
|
|
};
|
|
let bg = if self.selection().scene() == Some(index) {
|
|
scene.color.light.rgb
|
|
} else {
|
|
scene.color.base.rgb
|
|
};
|
|
let a = Fill::X(Align::w(Bsp::e(format!("·s{index:02} "),
|
|
Tui::fg(Tui::g(255), Tui::bold(true, &scene.name)))));
|
|
let b = When::new(self.selection().scene() == Some(index) && self.is_editing(),
|
|
Fill::XY(Align::nw(Bsp::s(
|
|
self.editor().as_ref().map(|e|e.clip_status()),
|
|
self.editor().as_ref().map(|e|e.edit_status())))));
|
|
Fixed::XY(20, h, Tui::bg(bg, Align::nw(Bsp::s(a, b))))
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct Scene {
|
|
/// Name of scene
|
|
pub name: Arc<str>,
|
|
/// Identifying color of scene
|
|
pub color: ItemTheme,
|
|
/// Clips in scene, one per track
|
|
pub clips: Vec<Option<Arc<RwLock<MidiClip>>>>,
|
|
}
|
|
|
|
impl Scene {
|
|
/// Returns the pulse length of the longest clip in the scene
|
|
pub fn pulses (&self) -> usize {
|
|
self.clips.iter().fold(0, |a, p|{
|
|
a.max(p.as_ref().map(|q|q.read().unwrap().length).unwrap_or(0))
|
|
})
|
|
}
|
|
/// Returns true if all clips in the scene are
|
|
/// currently playing on the given collection of tracks.
|
|
pub fn is_playing (&self, tracks: &[Track]) -> bool {
|
|
self.clips.iter().any(|clip|clip.is_some()) && self.clips.iter().enumerate()
|
|
.all(|(track_index, clip)|match clip {
|
|
Some(c) => tracks
|
|
.get(track_index)
|
|
.map(|track|{
|
|
if let Some((_, Some(clip))) = track.sequencer().play_clip() {
|
|
*clip.read().unwrap() == *c.read().unwrap()
|
|
} else {
|
|
false
|
|
}
|
|
})
|
|
.unwrap_or(false),
|
|
None => true
|
|
})
|
|
}
|
|
pub fn clip (&self, index: usize) -> Option<&Arc<RwLock<MidiClip>>> {
|
|
match self.clips.get(index) { Some(Some(clip)) => Some(clip), _ => None }
|
|
}
|
|
}
|
|
//scene_scroll: Fill::Y(Fixed::X(1, ScrollbarV {
|
|
//offset: arrangement.scene_scroll,
|
|
//length: h_scenes_area as usize,
|
|
//total: h_scenes as usize,
|
|
//})),
|
|
//take!(SceneCommand |state: Arrangement, iter|state.selected_scene().as_ref()
|
|
//.map(|t|Take::take(t, iter)).transpose().map(|x|x.flatten()));
|