tek/crates/tek_sequencer/src/arranger.rs

139 lines
4.4 KiB
Rust

//! Clip launcher and arrangement editor.
use crate::*;
/// Represents the tracks and scenes of the composition.
pub struct Arranger<E: Engine> {
/// Name of arranger
pub name: Arc<RwLock<String>>,
/// Collection of tracks.
pub tracks: Vec<Sequencer<E>>,
/// Collection of scenes.
pub scenes: Vec<Scene>,
/// Currently selected element.
pub selected: ArrangerFocus,
/// Display mode of arranger
pub mode: ArrangerViewMode,
/// Slot for modal dialog displayed on top of app.
pub modal: Option<Box<dyn ExitableComponent<E>>>,
/// Whether the arranger is currently focused
pub focused: bool
}
impl<E: Engine> Arranger<E> {
pub fn new (name: &str) -> Self {
Self {
name: Arc::new(RwLock::new(name.into())),
mode: ArrangerViewMode::VerticalCompact2,
selected: ArrangerFocus::Clip(0, 0),
scenes: vec![],
tracks: vec![],
modal: None,
focused: false
}
}
pub fn activate (&mut self) {
match self.selected {
ArrangerFocus::Scene(s) => {
for (track_index, track) in self.tracks.iter_mut().enumerate() {
track.sequence = self.scenes[s].clips[track_index];
track.reset = true;
}
},
ArrangerFocus::Clip(t, s) => {
self.tracks[t].sequence = self.scenes[s].clips[t];
self.tracks[t].reset = true;
},
_ => {}
}
}
pub fn sequencer (&self) -> Option<&Sequencer<E>> {
self.selected.track()
.map(|track|self.tracks.get(track))
.flatten()
}
pub fn sequencer_mut (&mut self) -> Option<&mut Sequencer<E>> {
self.selected.track()
.map(|track|self.tracks.get_mut(track))
.flatten()
}
pub fn show_phrase (&mut self) -> Usually<()> {
//unimplemented!()
//let phrase = self.phrase();
//self.sequencer.show(phrase)
Ok(())
}
pub fn is_first_row (&self) -> bool {
let selected = self.selected;
selected.is_mix() || selected.is_track() || match selected {
ArrangerFocus::Clip(_, s) =>
s == 0,
_ => false
}
}
pub fn is_last_row (&self) -> bool {
let selected = self.selected;
match selected {
ArrangerFocus::Scene(s) =>
s == self.scenes.len() - 1,
ArrangerFocus::Clip(_, s) =>
s == self.scenes.len() - 1,
_ => false
}
}
}
/// Display mode of arranger
pub enum ArrangerViewMode {
VerticalExpanded,
VerticalCompact1,
VerticalCompact2,
Horizontal,
}
/// Arranger display mode can be cycled
impl ArrangerViewMode {
/// Cycle arranger display mode
pub fn to_next (&mut self) {
*self = match self {
Self::VerticalExpanded => Self::VerticalCompact1,
Self::VerticalCompact1 => Self::VerticalCompact2,
Self::VerticalCompact2 => Self::Horizontal,
Self::Horizontal => Self::VerticalExpanded,
}
}
}
impl Widget for Arranger<Tui> {
type Engine = Tui;
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!()
}
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
let area = (|to|match self.mode {
ArrangerViewMode::Horizontal =>
super::arranger_view_h::draw(self, to),
ArrangerViewMode::VerticalCompact1 =>
super::arranger_view_v::draw_compact_1(self, to),
ArrangerViewMode::VerticalCompact2 =>
super::arranger_view_v::draw_compact_2(self, to),
ArrangerViewMode::VerticalExpanded =>
super::arranger_view_v::draw_expanded(self, to),
})(&mut to.alter_area(|[x, y, w, h]|[
x + 1,
y + 1,
w.saturating_sub(2),
h.saturating_sub(2),
]))?.unwrap();
Lozenge(Style::default().fg(Nord::BG2))
.draw(&mut to.alter_area(|[x, y, w, h]|[
x.saturating_sub(1),
y.saturating_sub(1),
w + 2,
h + 2,
]))
}
}
impl Focusable<Tui> for Arranger<Tui> {
fn is_focused (&self) -> bool {
self.focused
}
fn set_focused (&mut self, focused: bool) {
self.focused = focused
}
}