mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
app trait impls
This commit is contained in:
parent
af2e237b94
commit
93fa3c26b4
4 changed files with 156 additions and 137 deletions
|
|
@ -1,6 +1,10 @@
|
|||
use crate::*;
|
||||
pub trait HasEditor {
|
||||
fn editor (&self) -> &MidiEditor;
|
||||
fn editor (&self) -> &Option<MidiEditor>;
|
||||
fn editor_mut (&mut self) -> &Option<MidiEditor>;
|
||||
fn is_editing (&self) -> bool { true }
|
||||
fn editor_w (&self) -> usize { 0 }
|
||||
fn editor_h (&self) -> usize { 0 }
|
||||
}
|
||||
#[macro_export] macro_rules! has_editor {
|
||||
(|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => {
|
||||
|
|
|
|||
22
sampler/src/has_sampler.rs
Normal file
22
sampler/src/has_sampler.rs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
use crate::*;
|
||||
|
||||
pub trait HasSampler {
|
||||
fn sampler (&self) -> &Option<Sampler>;
|
||||
fn sampler_mut (&mut self) -> &mut Option<Sampler>;
|
||||
fn sample_index (&self) -> usize;
|
||||
fn view_sample <'a> (&'a self, compact: bool) -> impl Content<TuiOut> + 'a {
|
||||
self.sampler().as_ref().map(|sampler|Max::y(
|
||||
if compact { 0u16 } else { 5 }.into(),
|
||||
Fill::x(sampler.viewer(self.sample_index()))
|
||||
))
|
||||
}
|
||||
fn view_sampler <'a> (&'a self, compact: bool, editor: &Option<MidiEditor>) -> impl Content<TuiOut> + 'a {
|
||||
self.sampler().as_ref().map(|sampler|Fixed::x(
|
||||
if compact { 4u16 } else { 40 }.into(),
|
||||
Push::y(
|
||||
if compact { 1u16 } else { 0 }.into(),
|
||||
editor.as_ref().map(|e|Fill::y(sampler.list(compact, e)))
|
||||
)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
mod sampler; pub use self::sampler::*;
|
||||
mod sampler_tui; pub use self::sampler_tui::*;
|
||||
mod sampler_cmd; pub use self::sampler_cmd::*;
|
||||
mod has_sampler; pub use self::has_sampler::*;
|
||||
|
||||
pub(crate) use ::tek_jack::{*, jack::*};
|
||||
pub(crate) use ::tek_midi::{*, midly::{*, live::*, num::*}};
|
||||
|
|
|
|||
264
tek/src/lib.rs
264
tek/src/lib.rs
|
|
@ -52,13 +52,38 @@ pub(crate) use std::sync::{Arc, RwLock};
|
|||
pub perf: PerfModel,
|
||||
pub compact: bool,
|
||||
}
|
||||
impl HasJack for App { fn jack (&self) -> &Arc<RwLock<JackConnection>> { &self.jack } }
|
||||
has_size!(<TuiOut>|self: App|&self.size);
|
||||
has_clock!(|self: App|&self.clock);
|
||||
has_clips!(|self: App|self.pool.as_ref().expect("no clip pool").clips);
|
||||
has_clock!(|self:Track|self.player.clock());
|
||||
has_player!(|self:Track|self.player);
|
||||
has_editor!(|self: App|self.editor.as_ref().expect("no editor"));
|
||||
//has_editor!(|self: App|self.editor.as_ref().expect("no editor"));
|
||||
impl HasJack for App {
|
||||
fn jack (&self) -> &Arc<RwLock<JackConnection>> { &self.jack }
|
||||
}
|
||||
impl HasSampler for App {
|
||||
fn sampler (&self) -> &Option<Sampler> { &self.sampler }
|
||||
fn sampler_mut (&mut self) -> &mut Option<Sampler> { &mut self.sampler }
|
||||
fn sample_index (&self) -> usize { self.editor.as_ref().map(|e|e.note_point()).unwrap_or(0) }
|
||||
}
|
||||
impl HasEditor for App {
|
||||
fn editor (&self) -> &Option<MidiEditor> {
|
||||
&self.editor
|
||||
}
|
||||
fn editor_mut (&mut self) -> &Option<MidiEditor> {
|
||||
&mut self.editor
|
||||
}
|
||||
fn is_editing (&self) -> bool {
|
||||
self.editing.load(Relaxed)
|
||||
}
|
||||
fn editor_w (&self) -> usize {
|
||||
let editor = self.editor.as_ref().expect("missing editor");
|
||||
(5 + (editor.time_len().get() / editor.time_zoom().get()))
|
||||
.min(self.size.w().saturating_sub(20))
|
||||
.max(16)
|
||||
}
|
||||
fn editor_h (&self) -> usize {
|
||||
15
|
||||
}
|
||||
}
|
||||
edn_provide!(u16: |self: App|{
|
||||
":sample-h" => if self.compact() { 0 } else { 5 },
|
||||
":samples-w" => if self.compact() { 4 } else { 11 },
|
||||
|
|
@ -68,28 +93,25 @@ edn_provide!(u16: |self: App|{
|
|||
if w > 60 { 20 } else if w > 40 { 15 } else { 10 }
|
||||
}
|
||||
});
|
||||
edn_provide!(Color: |self: App| { _ => return None });
|
||||
edn_provide!(bool: |self: App| { _ => return None });
|
||||
edn_provide!(usize: |self: App| { _ => return None });
|
||||
edn_provide!(isize: |self: App| { _ => return None });
|
||||
edn_provide!(bool: |self: App| { _ => return None });
|
||||
edn_provide!(Color: |self: App| { _ => return None });
|
||||
edn_provide!(Selection: |self: App| { _ => return None });
|
||||
edn_provide!(Arc<RwLock<MidiClip>>: |self: App| { _ => return None });
|
||||
edn_provide!(Option<Arc<RwLock<MidiClip>>>: |self: App| { _ => return None });
|
||||
edn_provide!('a: Box<dyn Render<TuiOut> + 'a>: |self: App|{
|
||||
":editor" => (&self.editor).boxed(),
|
||||
":pool" => self.pool.as_ref().map(|pool|Align::e(Fixed::x(self.sidebar_w(), PoolView(self.compact(), pool)))).boxed(),
|
||||
":sample" => self.sample().boxed(),
|
||||
":sampler" => self.sampler().boxed(),
|
||||
":sample" => self.view_sample(self.is_editing()).boxed(),
|
||||
":sampler" => self.view_sampler(self.is_editing(), &self.editor).boxed(),
|
||||
":status" => self.editor.as_ref().map(|e|Bsp::e(e.clip_status(), e.edit_status())).boxed(),
|
||||
":toolbar" => Fill::x(Fixed::y(2, Align::x(ClockView::new(true, &self.clock)))).boxed(),
|
||||
":tracks" => self.row(self.w(), 3,
|
||||
self.track_header(), self.track_cells()).boxed(),
|
||||
":inputs" => self.row(self.w(), 3,
|
||||
input_header(&self), input_cells(&self)).boxed(),
|
||||
":outputs" => self.row(self.w(), 3,
|
||||
output_header(&self), output_cells(&self)).boxed(),
|
||||
":tracks" => self.row(self.w(), 3, self.track_header(), self.track_cells()).boxed(),
|
||||
":inputs" => self.row(self.w(), 3, self.input_header(), self.input_cells()).boxed(),
|
||||
":outputs" => self.row(self.w(), 3, self.output_header(), self.output_cells()).boxed(),
|
||||
":scenes" => self.row(self.w(), self.size.h().saturating_sub(9) as u16,
|
||||
self.scene_header(), self.scene_cells()).boxed(),
|
||||
self.scene_header(), self.scene_cells(self.is_editing())).boxed(),
|
||||
});
|
||||
render!(TuiOut: (self: App) => self.size.of(EdnView::from_source(self, self.edn.as_ref())));
|
||||
handle!(TuiIn: |self: App, input| Ok(None));
|
||||
|
|
@ -184,8 +206,10 @@ pub trait HasSelection {
|
|||
/// Outputs of last device
|
||||
audio_outs: Vec<JackPort<AudioOut>>,
|
||||
/// Device chain
|
||||
devices: Vec<Device>,
|
||||
devices: Vec<Box<dyn Device>>,
|
||||
}
|
||||
has_clock!(|self: Track|self.player.clock());
|
||||
has_player!(|self: Track|self.player);
|
||||
impl Track {
|
||||
const MIN_WIDTH: usize = 9;
|
||||
fn longest_name (tracks: &[Self]) -> usize {
|
||||
|
|
@ -204,14 +228,12 @@ impl HasTracks for App {
|
|||
fn tracks (&self) -> &Vec<Track> { &self.tracks }
|
||||
fn tracks_mut (&mut self) -> &mut Vec<Track> { &mut self.tracks }
|
||||
}
|
||||
pub trait HasTracks: HasSelection + HasClock + HasJack {
|
||||
pub trait HasTracks: HasSelection + HasClock + HasJack + HasEditor + Send + Sync {
|
||||
fn tracks (&self) -> &Vec<Track>;
|
||||
fn tracks_mut (&mut self) -> &mut Vec<Track>;
|
||||
fn tracks_sizes (
|
||||
&self,
|
||||
editing: bool,
|
||||
bigger: usize
|
||||
) -> impl Iterator<Item=(usize,&Track,usize,usize)> {
|
||||
fn tracks_sizes <'a> (&'a self, editing: bool, bigger: usize)
|
||||
-> impl Iterator<Item=(usize, &'a Track, usize, usize)> + Send + Sync + 'a
|
||||
{
|
||||
let mut x = 0;
|
||||
let active = match self.selected() {
|
||||
Selection::Track(t) if editing => Some(t),
|
||||
|
|
@ -241,7 +263,7 @@ pub trait HasTracks: HasSelection + HasClock + HasJack {
|
|||
)).boxed()).into()
|
||||
}
|
||||
fn track_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> {
|
||||
let iter = ||self.tracks_with_sizes();
|
||||
let iter = ||self.tracks_sizes(self.is_editing(), self.editor_w());
|
||||
(move||Align::x(Map::new(iter, move|(_, track, x1, x2), i| {
|
||||
let name = Push::x(1, &track.name);
|
||||
let color = track.color;
|
||||
|
|
@ -256,7 +278,7 @@ pub trait HasTracks: HasSelection + HasClock + HasJack {
|
|||
})).boxed()).into()
|
||||
}
|
||||
}
|
||||
trait Device {}
|
||||
trait Device: Send + Sync + std::fmt::Debug {}
|
||||
impl Device for Sampler {}
|
||||
impl Device for Plugin {}
|
||||
#[derive(Debug, Default)] struct Scene {
|
||||
|
|
@ -321,15 +343,12 @@ impl HasScenes for App {
|
|||
fn scenes (&self) -> &Vec<Scene> { &self.scenes }
|
||||
fn scenes_mut (&mut self) -> &mut Vec<Scene> { &mut self.scenes }
|
||||
}
|
||||
pub trait HasScenes: HasSelection {
|
||||
pub trait HasScenes: HasSelection + HasEditor + Send + Sync {
|
||||
fn scenes (&self) -> &Vec<Scene>;
|
||||
fn scenes_mut (&mut self) -> &mut Vec<Scene>;
|
||||
fn scenes_sizes(
|
||||
&self,
|
||||
editing: bool,
|
||||
scene_height: usize,
|
||||
scene_larger: usize,
|
||||
) -> impl Iterator<Item = (usize, &Scene, usize, usize)> {
|
||||
fn scenes_sizes (&self, editing: bool, height: usize, larger: usize,)
|
||||
-> impl Iterator<Item = (usize, &Scene, usize, usize)> + Send + Sync
|
||||
{
|
||||
let mut y = 0;
|
||||
let (selected_track, selected_scene) = match self.selected() {
|
||||
Selection::Clip(t, s) => (Some(t), Some(s)),
|
||||
|
|
@ -337,7 +356,7 @@ pub trait HasScenes: HasSelection {
|
|||
};
|
||||
self.scenes().iter().enumerate().map(move|(s, scene)|{
|
||||
let active = editing && selected_track.is_some() && selected_scene == Some(&s);
|
||||
let height = if active { scene_larger } else { scene_height };
|
||||
let height = if active { larger } else { height };
|
||||
let data = (s, scene, y, y + height);
|
||||
y += height;
|
||||
data
|
||||
|
|
@ -353,13 +372,14 @@ pub trait HasScenes: HasSelection {
|
|||
self.selected().scene().and_then(|s|self.scenes_mut().get_mut(s))
|
||||
}
|
||||
fn scene_del (&mut self, index: usize) {
|
||||
todo!("delete scene");
|
||||
self.selected().scene().and_then(|s|Some(self.scenes_mut().remove(index)));
|
||||
}
|
||||
fn scene_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> {
|
||||
(||{
|
||||
(move||{
|
||||
let last_color = Arc::new(RwLock::new(ItemPalette::from(Color::Rgb(0, 0, 0))));
|
||||
let selected = self.selected().scene();
|
||||
Fill::y(Align::c(Map::new(||self.scenes_with_sizes(2), move|(_, scene, y1, y2), i| {
|
||||
let selected = self.selected().scene();
|
||||
let iter = ||self.scenes_sizes(self.is_editing(), 2, 15);
|
||||
Fill::y(Align::c(Map::new(iter, move|(_, scene, y1, y2), i| {
|
||||
let h = (y2 - y1) as u16;
|
||||
let name = format!("🭬{}", &scene.name);
|
||||
let color = scene.color;
|
||||
|
|
@ -431,27 +451,8 @@ impl App {
|
|||
}
|
||||
fn compact (&self) -> bool { false }
|
||||
fn editor (&self) -> impl Content<TuiOut> + '_ { &self.editor }
|
||||
fn w (&self) -> u16 { self.tracks_with_sizes().last().map(|x|x.3 as u16).unwrap_or(0) }
|
||||
fn w (&self) -> u16 { self.tracks_sizes(self.is_editing(), self.editor_w()).last().map(|x|x.3 as u16).unwrap_or(0) }
|
||||
fn pool (&self) -> impl Content<TuiOut> + use<'_> { self.pool.as_ref().map(|pool|Align::e(Fixed::x(self.sidebar_w(), PoolView(self.compact(), pool)))) }
|
||||
fn sample <'a> (&'a self) -> impl Content<TuiOut> + 'a {
|
||||
let compact = self.is_editing();
|
||||
if let (Some(editor), Some(sampler)) = (&self.editor, &self.sampler) {
|
||||
let note_pt = editor.note_point();
|
||||
let sample_h = if compact { 0 } else { 5 };
|
||||
return Some(Max::y(sample_h, Fill::x(sampler.viewer(note_pt))))
|
||||
}
|
||||
None
|
||||
}
|
||||
fn sampler (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
let compact = self.is_editing();
|
||||
if let (Some(editor), Some(sampler)) = (&self.editor, &self.sampler) {
|
||||
let note_pt = editor.note_point();
|
||||
let w = if compact { 4 } else { 40 };
|
||||
let y = if compact { 1 } else { 0 };
|
||||
return Some(Fixed::x(w, Push::y(y, Fill::y(sampler.list(compact, editor)))))
|
||||
}
|
||||
None
|
||||
}
|
||||
fn row <'a> (
|
||||
&'a self, w: u16, h: u16, a: impl Content<TuiOut> + 'a, b: impl Content<TuiOut> + 'a
|
||||
) -> impl Content<TuiOut> + 'a {
|
||||
|
|
@ -460,27 +461,69 @@ impl App {
|
|||
Fill::x(Align::c(Fixed::xy(w, h, b)))
|
||||
))
|
||||
}
|
||||
fn tracks_with_sizes (&self) -> impl Iterator<Item = (usize, &Track, usize, usize)> {
|
||||
self.tracks_sizes(self.is_editing(), self.editor_w())
|
||||
}
|
||||
fn scenes_with_sizes (&self, h: usize) -> impl Iterator<Item = (usize, &Scene, usize, usize)> {
|
||||
self.scenes_sizes(self.is_editing(), 2, 15)
|
||||
}
|
||||
fn is_editing (&self) -> bool {
|
||||
self.editing.load(Relaxed)
|
||||
}
|
||||
fn editor_w (&self) -> usize {
|
||||
let editor = self.editor.as_ref().expect("missing editor");
|
||||
(5 + (editor.time_len().get() / editor.time_zoom().get()))
|
||||
.min(self.size.w().saturating_sub(20))
|
||||
.max(16)
|
||||
}
|
||||
fn sidebar_w (&self) -> u16 {
|
||||
let w = self.size.w();
|
||||
let w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 };
|
||||
let w = if self.is_editing() { 8 } else { w };
|
||||
w
|
||||
}
|
||||
fn input_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> {
|
||||
let fg = TuiTheme::g(224);
|
||||
let bg = TuiTheme::g(64);
|
||||
(move||Bsp::s(help_tag("midi ", "I", "ns"), self.midi_ins.get(0).map(|inp|Bsp::s(
|
||||
Fill::x(Tui::bold(true, Tui::fg_bg(fg, bg, Align::w(inp.name.clone())))),
|
||||
inp.connect.get(0).map(|connect|Fill::x(Align::w(Tui::bold(false,
|
||||
Tui::fg_bg(fg, bg, connect.info()))))),
|
||||
))).boxed()).into()
|
||||
}
|
||||
fn input_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> {
|
||||
(move||Align::x(Map::new(||self.tracks_sizes(self.is_editing(), self.editor_w()), move|(_, track, x1, x2), i| {
|
||||
let w = (x2 - x1) as u16;
|
||||
let color: ItemPalette = track.color.dark.into();
|
||||
map_east(x1 as u16, w, Fixed::x(w, cell(color, Bsp::n(
|
||||
Self::rec_mon(color.base.rgb, false, false),
|
||||
phat_hi(color.base.rgb, color.dark.rgb)
|
||||
))))
|
||||
})).boxed()).into()
|
||||
}
|
||||
fn rec_mon (bg: Color, rec: bool, mon: bool) -> impl Content<TuiOut> {
|
||||
row!(
|
||||
Tui::fg_bg(if rec { Color::Red } else { bg }, bg, "▐"),
|
||||
Tui::fg_bg(if rec { Color::White } else { Color::Rgb(0,0,0) }, bg, "REC"),
|
||||
Tui::fg_bg(if rec { Color::White } else { bg }, bg, "▐"),
|
||||
Tui::fg_bg(if mon { Color::White } else { Color::Rgb(0,0,0) }, bg, "MON"),
|
||||
Tui::fg_bg(if mon { Color::White } else { bg }, bg, "▌"),
|
||||
)
|
||||
}
|
||||
fn output_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> {
|
||||
let fg = TuiTheme::g(224);
|
||||
let bg = TuiTheme::g(64);
|
||||
(move||Bsp::s(help_tag("midi ", "O", "uts"), self.midi_outs.get(0).map(|out|Bsp::s(
|
||||
Fill::x(Tui::bold(true, Tui::fg_bg(fg, bg, Align::w(out.name.clone())))),
|
||||
out.connect.get(0).map(|connect|Fill::x(Align::w(Tui::bold(false,
|
||||
Tui::fg_bg(fg, bg, connect.info()))))),
|
||||
))).boxed()).into()
|
||||
}
|
||||
fn output_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> {
|
||||
(move||Align::x(Map::new(||self.tracks_sizes(self.is_editing(), self.editor_w()), move|(_, track, x1, x2), i| {
|
||||
let w = (x2 - x1) as u16;
|
||||
let color: ItemPalette = track.color.dark.into();
|
||||
map_east(x1 as u16, w, Fixed::x(w, cell(color, Bsp::n(
|
||||
Self::mute_solo(color.base.rgb, false, false),
|
||||
phat_hi(color.dark.rgb, color.darker.rgb)
|
||||
))))
|
||||
})).boxed()).into()
|
||||
}
|
||||
fn mute_solo (bg: Color, mute: bool, solo: bool) -> impl Content<TuiOut> {
|
||||
row!(
|
||||
Tui::fg_bg(if mute { Color::White } else { Color::Rgb(0,0,0) }, bg, "MUTE"),
|
||||
Tui::fg_bg(if mute { Color::White } else { bg }, bg, "▐"),
|
||||
Tui::fg_bg(if solo { Color::White } else { Color::Rgb(0,0,0) }, bg, "SOLO"),
|
||||
)
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug)] pub enum AppCommand {
|
||||
Clear,
|
||||
|
|
@ -777,44 +820,6 @@ fn help_tag <'a> (before: &'a str, key: &'a str, after: &'a str) -> impl Content
|
|||
let hi = TuiTheme::orange();
|
||||
Tui::bold(true, row!(Tui::fg(lo, before), Tui::fg(hi, key), Tui::fg(lo, after)))
|
||||
}
|
||||
fn input_header <'a> (state: &'a App) -> BoxThunk<'a, TuiOut> {
|
||||
let fg = TuiTheme::g(224);
|
||||
let bg = TuiTheme::g(64);
|
||||
(move||Bsp::s(help_tag("midi ", "I", "ns"), state.midi_ins.get(0).map(|inp|Bsp::s(
|
||||
Fill::x(Tui::bold(true, Tui::fg_bg(fg, bg, Align::w(inp.name.clone())))),
|
||||
inp.connect.get(0).map(|connect|Fill::x(Align::w(Tui::bold(false,
|
||||
Tui::fg_bg(fg, bg, connect.info()))))),
|
||||
))).boxed()).into()
|
||||
}
|
||||
fn input_cells <'a> (state: &'a App) -> BoxThunk<'a, TuiOut> {
|
||||
(move||Align::x(Map::new(||state.tracks_with_sizes(), move|(_, track, x1, x2), i| {
|
||||
let w = (x2 - x1) as u16;
|
||||
let color: ItemPalette = track.color.dark.into();
|
||||
map_east(x1 as u16, w, Fixed::x(w, cell(color, Bsp::n(
|
||||
rec_mon(color.base.rgb, false, false),
|
||||
phat_hi(color.base.rgb, color.dark.rgb)
|
||||
))))
|
||||
})).boxed()).into()
|
||||
}
|
||||
fn output_header <'a> (state: &'a App) -> BoxThunk<'a, TuiOut> {
|
||||
let fg = TuiTheme::g(224);
|
||||
let bg = TuiTheme::g(64);
|
||||
(move||Bsp::s(help_tag("midi ", "O", "uts"), state.midi_outs.get(0).map(|out|Bsp::s(
|
||||
Fill::x(Tui::bold(true, Tui::fg_bg(fg, bg, Align::w(out.name.clone())))),
|
||||
out.connect.get(0).map(|connect|Fill::x(Align::w(Tui::bold(false,
|
||||
Tui::fg_bg(fg, bg, connect.info()))))),
|
||||
))).boxed()).into()
|
||||
}
|
||||
fn output_cells <'a> (state: &'a App) -> BoxThunk<'a, TuiOut> {
|
||||
(move||Align::x(Map::new(||state.tracks_with_sizes(), move|(_, track, x1, x2), i| {
|
||||
let w = (x2 - x1) as u16;
|
||||
let color: ItemPalette = track.color.dark.into();
|
||||
map_east(x1 as u16, w, Fixed::x(w, cell(color, Bsp::n(
|
||||
mute_solo(color.base.rgb, false, false),
|
||||
phat_hi(color.dark.rgb, color.darker.rgb)
|
||||
))))
|
||||
})).boxed()).into()
|
||||
}
|
||||
fn cell_clip <'a> (
|
||||
scene: &'a Scene, index: usize, track: &'a Track, w: u16, h: u16
|
||||
) -> impl Content<TuiOut> + use<'a> {
|
||||
|
|
@ -833,36 +838,24 @@ fn cell_clip <'a> (
|
|||
Fixed::xy(w, h, &Tui::bg(bg, Push::x(1, Fixed::x(w, &name.as_str()[0..max_w]))));
|
||||
}))
|
||||
}
|
||||
fn rec_mon (bg: Color, rec: bool, mon: bool) -> impl Content<TuiOut> {
|
||||
row!(
|
||||
Tui::fg_bg(if rec { Color::Red } else { bg }, bg, "▐"),
|
||||
Tui::fg_bg(if rec { Color::White } else { Color::Rgb(0,0,0) }, bg, "REC"),
|
||||
Tui::fg_bg(if rec { Color::White } else { bg }, bg, "▐"),
|
||||
Tui::fg_bg(if mon { Color::White } else { Color::Rgb(0,0,0) }, bg, "MON"),
|
||||
Tui::fg_bg(if mon { Color::White } else { bg }, bg, "▌"),
|
||||
)
|
||||
}
|
||||
fn mute_solo (bg: Color, mute: bool, solo: bool) -> impl Content<TuiOut> {
|
||||
row!(
|
||||
Tui::fg_bg(if mute { Color::White } else { Color::Rgb(0,0,0) }, bg, "MUTE"),
|
||||
Tui::fg_bg(if mute { Color::White } else { bg }, bg, "▐"),
|
||||
Tui::fg_bg(if solo { Color::White } else { Color::Rgb(0,0,0) }, bg, "SOLO"),
|
||||
)
|
||||
}
|
||||
fn cell <T: Content<TuiOut>> (color: ItemPalette, field: T) -> impl Content<TuiOut> {
|
||||
Tui::fg_bg(color.lightest.rgb, color.base.rgb, Fixed::y(1, field))
|
||||
}
|
||||
impl Arrangement for App {}
|
||||
pub trait Arrangement: HasTracks + HasScenes + HasSelection + HasClock + HasJack {
|
||||
fn track_add (&mut self, name: Option<&str>, color: Option<ItemPalette>)
|
||||
-> Usually<&mut Track>
|
||||
{
|
||||
impl<T> Arrangement for T where T:
|
||||
HasEditor + HasTracks + HasScenes + HasSelection + HasClock + HasJack {}
|
||||
pub trait Arrangement: HasEditor + HasTracks + HasScenes + HasSelection + HasClock + HasJack {
|
||||
fn track_add (
|
||||
&mut self,
|
||||
name: Option<&str>,
|
||||
color: Option<ItemPalette>
|
||||
) -> Usually<&mut Track> {
|
||||
let name = name.map_or_else(||self.track_next_name(), |x|x.to_string().into());
|
||||
let track = Track {
|
||||
width: (name.len() + 2).max(9),
|
||||
color: color.unwrap_or_else(ItemPalette::random),
|
||||
player: MidiPlayer::from(self.clock()),
|
||||
name,
|
||||
..Default::default()
|
||||
};
|
||||
self.tracks_mut().push(track);
|
||||
let len = self.tracks().len();
|
||||
|
|
@ -876,8 +869,8 @@ pub trait Arrangement: HasTracks + HasScenes + HasSelection + HasClock + HasJack
|
|||
}
|
||||
fn tracks_add (
|
||||
&mut self,
|
||||
count: usize,
|
||||
width: usize,
|
||||
count: usize,
|
||||
width: usize,
|
||||
midi_from: &[PortConnection],
|
||||
midi_to: &[PortConnection],
|
||||
) -> Usually<()> {
|
||||
|
|
@ -923,12 +916,11 @@ pub trait Arrangement: HasTracks + HasScenes + HasSelection + HasClock + HasJack
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
fn scene_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> {
|
||||
let editing = self.is_editing();
|
||||
let tracks = move||self.tracks_with_sizes();
|
||||
let scenes = ||self.scenes_with_sizes(2);
|
||||
let selected_track = self.selected.track();
|
||||
let selected_scene = self.selected.scene();
|
||||
fn scene_cells <'a> (&'a self, editing: bool) -> BoxThunk<'a, TuiOut> {
|
||||
let tracks = move||self.tracks_sizes(self.is_editing(), self.editor_w());
|
||||
let scenes = ||self.scenes_sizes(self.is_editing(), 2, 15);
|
||||
let selected_track = self.selected().track();
|
||||
let selected_scene = self.selected().scene();
|
||||
(move||Fill::y(Align::c(Map::new(tracks, move|(_, track, x1, x2), t| {
|
||||
let w = (x2 - x1) as u16;
|
||||
let color: ItemPalette = track.color.dark.into();
|
||||
|
|
@ -944,7 +936,7 @@ pub trait Arrangement: HasTracks + HasScenes + HasSelection + HasClock + HasJack
|
|||
};
|
||||
let last = last_color.read().unwrap().clone();
|
||||
let active = editing && selected_scene == Some(s) && selected_track == Some(t);
|
||||
let editor = Thunk::new(||&self.editor);
|
||||
let editor = Thunk::new(||self.editor());
|
||||
let cell = Thunk::new(move||phat_sel_3(
|
||||
selected_track == Some(t) && selected_scene == Some(s),
|
||||
Tui::fg(fg, Push::x(1, Tui::bold(true, name.to_string()))),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue