mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
This commit is contained in:
parent
2c3bfe4ebb
commit
ef81b085a0
106 changed files with 6866 additions and 7106 deletions
191
device/scene.rs
Normal file
191
device/scene.rs
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
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()));
|
||||
Loading…
Add table
Add a link
Reference in a new issue