mirror of
https://codeberg.org/unspeaker/tek.git
synced 2026-02-01 08:36:42 +01:00
wip: borrow checker battles
This commit is contained in:
parent
1d4db3c629
commit
7fbb40fad6
38 changed files with 778 additions and 708 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
/// A component that may contain [Focusable] components.
|
/// A component that may contain [Focusable] components.
|
||||||
pub trait Focus <const N: usize, T, U>: Render<T, U> + Handle {
|
pub trait Focus <'a, const N: usize, T, U>: Render<'a, T, U> + Handle {
|
||||||
fn focus (&self) -> usize;
|
fn focus (&self) -> usize;
|
||||||
fn focus_mut (&mut self) -> &mut usize;
|
fn focus_mut (&mut self) -> &mut usize;
|
||||||
fn focusable (&self) -> [&dyn Focusable<T, U>;N];
|
fn focusable (&self) -> [&dyn Focusable<T, U>;N];
|
||||||
|
|
@ -33,13 +33,13 @@ pub trait Focus <const N: usize, T, U>: Render<T, U> + Handle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A component that may be focused.
|
/// A component that may be focused.
|
||||||
pub trait Focusable<T, U>: Render<T, U> + Handle {
|
pub trait Focusable<'a, T, U>: Render<'a, T, U> + Handle {
|
||||||
fn is_focused (&self) -> bool;
|
fn is_focused (&self) -> bool;
|
||||||
fn set_focused (&mut self, focused: bool);
|
fn set_focused (&mut self, focused: bool);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: Focusable<T, U>, T, U> Focusable<T, U> for Option<F>
|
impl<'a, F: Focusable<'a, T, U>, T, U> Focusable<'a, T, U> for Option<F>
|
||||||
where Option<F>: Render<T, U>
|
where Option<F>: Render<'a, T, U>
|
||||||
{
|
{
|
||||||
fn is_focused (&self) -> bool {
|
fn is_focused (&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
|
|
@ -59,21 +59,21 @@ impl<F: Focusable<T, U>, T, U> Focusable<T, U> for Option<F>
|
||||||
($struct:ident ($focus:ident) : $count:expr => [
|
($struct:ident ($focus:ident) : $count:expr => [
|
||||||
$($focusable:ident),*
|
$($focusable:ident),*
|
||||||
]) => {
|
]) => {
|
||||||
impl Focus<$count> for $struct {
|
impl Focus<$count, T, U> for $struct {
|
||||||
fn focus (&self) -> usize {
|
fn focus (&self) -> usize {
|
||||||
self.$focus
|
self.$focus
|
||||||
}
|
}
|
||||||
fn focus_mut (&mut self) -> &mut usize {
|
fn focus_mut (&mut self) -> &mut usize {
|
||||||
&mut self.$focus
|
&mut self.$focus
|
||||||
}
|
}
|
||||||
fn focusable (&self) -> [&dyn Focusable;$count] {
|
fn focusable (&self) -> [&dyn Focusable<T, U>;$count] {
|
||||||
[
|
[
|
||||||
$(&self.$focusable as &dyn Focusable,)*
|
$(&self.$focusable as &dyn Focusable<T, U>,)*
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
fn focusable_mut (&mut self) -> [&mut dyn Focusable;$count] {
|
fn focusable_mut (&mut self) -> [&mut dyn Focusable<T, U>;$count] {
|
||||||
[
|
[
|
||||||
$(&mut self.$focusable as &mut dyn Focusable,)*
|
$(&mut self.$focusable as &mut dyn Focusable<T, U>,)*
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,3 +32,9 @@ pub fn handle_keymap <T> (
|
||||||
] as &'static [KeyBinding<$T>]
|
] as &'static [KeyBinding<$T>]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export] macro_rules! key {
|
||||||
|
($k:ident $(($char:literal))?, $m:ident, $n: literal, $d: literal, $f: expr) => {
|
||||||
|
(KeyCode::$k $(($char))?, KeyModifiers::$m, $n, $d, &$f as &dyn Fn()->Usually<bool>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
use crate::{*, jack::*};
|
use crate::{*, jack::*};
|
||||||
|
|
||||||
/// A UI component that may be associated with a JACK client by the `Jack` factory.
|
/// A UI component that may be associated with a JACK client by the `Jack` factory.
|
||||||
pub trait Device<T, U>: Render<T, U> + Handle + Process + Send + Sync {
|
pub trait Device<'a, T, U>: Render<'a, T, U> + Handle + Process + Send + Sync {
|
||||||
/// Perform type erasure for collecting heterogeneous devices.
|
/// Perform type erasure for collecting heterogeneous devices.
|
||||||
fn boxed (self) -> Box<dyn Device<T, U>> where Self: Sized + 'static {
|
fn boxed (self) -> Box<dyn Device<'a, T, U>> where Self: Sized + 'static {
|
||||||
Box::new(self)
|
Box::new(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// All things that implement the required traits can be treated as `Device`.
|
/// All things that implement the required traits can be treated as `Device`.
|
||||||
impl<D, T, U> Device<T, U> for D where D: Render<T, U> + Handle + Process {}
|
impl<'a, D, T, U> Device<'a, T, U> for D where D: Render<'a, T, U> + Handle + Process {}
|
||||||
|
|
||||||
impl<T, U> Render<T, U> for Box<dyn Device<T, U>> {
|
impl<'a, T, U> Render<'a, T, U> for Box<dyn Device<'a, T, U>> {
|
||||||
fn render (&self, to: &mut T) -> Result<Option<U>, Box<dyn std::error::Error>> {
|
fn render (&self, to: &'a mut T) -> Result<Option<U>, Box<dyn std::error::Error>> {
|
||||||
(**self).render(to)
|
(**self).render(to)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -143,8 +143,10 @@ impl Jack {
|
||||||
)?.0,
|
)?.0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn run <D, T, U> (self, state: impl FnOnce(JackPorts)->Box<D>) -> Usually<JackDevice<T, U>>
|
pub fn run <'a: 'static, D, T, U> (
|
||||||
where D: Device<T, U> + Process + Sized + 'static,
|
self, state: impl FnOnce(JackPorts)->Box<D>
|
||||||
|
) -> Usually<JackDevice<'a, T, U>>
|
||||||
|
where D: Device<'a, T, U> + Process + Sized + 'static,
|
||||||
T: 'static,
|
T: 'static,
|
||||||
U: 'static
|
U: 'static
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,31 @@
|
||||||
use crate::{*, jack::*};
|
use crate::{*, jack::*};
|
||||||
|
|
||||||
/// A [Device] bound to a JACK client and a set of ports.
|
/// A [Device] bound to a JACK client and a set of ports.
|
||||||
pub struct JackDevice<T, U> {
|
pub struct JackDevice<'a, T, U> {
|
||||||
/// The active JACK client of this device.
|
/// The active JACK client of this device.
|
||||||
pub client: DynamicAsyncClient,
|
pub client: DynamicAsyncClient,
|
||||||
/// The device state, encapsulated for sharing between threads.
|
/// The device state, encapsulated for sharing between threads.
|
||||||
pub state: Arc<RwLock<Box<dyn Device<T, U>>>>,
|
pub state: Arc<RwLock<Box<dyn Device<'a, T, U>>>>,
|
||||||
/// Unowned copies of the device's JACK ports, for connecting to the device.
|
/// Unowned copies of the device's JACK ports, for connecting to the device.
|
||||||
/// The "real" readable/writable `Port`s are owned by the `state`.
|
/// The "real" readable/writable `Port`s are owned by the `state`.
|
||||||
pub ports: UnownedJackPorts,
|
pub ports: UnownedJackPorts,
|
||||||
}
|
}
|
||||||
impl<T, U> std::fmt::Debug for JackDevice<T, U> {
|
impl<'a, T, U> std::fmt::Debug for JackDevice<'a, T, U> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("JackDevice").field("ports", &self.ports).finish()
|
f.debug_struct("JackDevice").field("ports", &self.ports).finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'a, T, U> Render<T, U> for JackDevice<T, U> {
|
impl<'a, T, U> Render<'a, T, U> for JackDevice<'a, T, U> {
|
||||||
fn render (&self, to: &mut T) -> Perhaps<U> {
|
fn render (&self, to: &'a mut T) -> Perhaps<U> {
|
||||||
self.state.read().unwrap().render(to)
|
self.state.read().unwrap().render(to)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T, U> Handle for JackDevice<T, U> {
|
impl<'a, T, U> Handle for JackDevice<'a, T, U> {
|
||||||
fn handle (&mut self, event: &AppEvent) -> Usually<bool> {
|
fn handle (&mut self, event: &AppEvent) -> Usually<bool> {
|
||||||
self.state.write().unwrap().handle(event)
|
self.state.write().unwrap().handle(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T, U> Ports for JackDevice<T, U> {
|
impl<'a, T, U> Ports for JackDevice<'a, T, U> {
|
||||||
fn audio_ins (&self) -> Usually<Vec<&Port<Unowned>>> {
|
fn audio_ins (&self) -> Usually<Vec<&Port<Unowned>>> {
|
||||||
Ok(self.ports.audio_ins.values().collect())
|
Ok(self.ports.audio_ins.values().collect())
|
||||||
}
|
}
|
||||||
|
|
@ -39,13 +39,13 @@ impl<T, U> Ports for JackDevice<T, U> {
|
||||||
Ok(self.ports.midi_outs.values().collect())
|
Ok(self.ports.midi_outs.values().collect())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T, U> JackDevice<T, U> {
|
impl<'a, T, U> JackDevice<'a, T, U> {
|
||||||
/// Returns a locked mutex of the state's contents.
|
/// Returns a locked mutex of the state's contents.
|
||||||
pub fn state (&self) -> LockResult<RwLockReadGuard<Box<dyn Device<T, U>>>> {
|
pub fn state (&self) -> LockResult<RwLockReadGuard<Box<dyn Device<'a, T, U>>>> {
|
||||||
self.state.read()
|
self.state.read()
|
||||||
}
|
}
|
||||||
/// Returns a locked mutex of the state's contents.
|
/// Returns a locked mutex of the state's contents.
|
||||||
pub fn state_mut (&self) -> LockResult<RwLockWriteGuard<Box<dyn Device<T, U>>>> {
|
pub fn state_mut (&self) -> LockResult<RwLockWriteGuard<Box<dyn Device<'a, T, U>>>> {
|
||||||
self.state.write()
|
self.state.write()
|
||||||
}
|
}
|
||||||
pub fn connect_midi_in (&self, index: usize, port: &Port<Unowned>) -> Usually<()> {
|
pub fn connect_midi_in (&self, index: usize, port: &Port<Unowned>) -> Usually<()> {
|
||||||
|
|
|
||||||
|
|
@ -54,14 +54,13 @@ submod! {
|
||||||
jack_ports
|
jack_ports
|
||||||
render
|
render
|
||||||
render_axis
|
render_axis
|
||||||
render_border
|
|
||||||
render_buffer
|
render_buffer
|
||||||
render_collect
|
render_collect
|
||||||
render_layered
|
render_layered
|
||||||
render_split
|
render_split
|
||||||
render_tui
|
render_tui
|
||||||
render_tui_fill
|
|
||||||
render_tui_theme
|
render_tui_theme
|
||||||
|
render_tui_border
|
||||||
time_base
|
time_base
|
||||||
time_note
|
time_note
|
||||||
time_tick
|
time_tick
|
||||||
|
|
@ -86,28 +85,28 @@ pub type Usually<T> = Result<T, Box<dyn Error>>;
|
||||||
pub type Perhaps<T> = Result<Option<T>, Box<dyn Error>>;
|
pub type Perhaps<T> = Result<Option<T>, Box<dyn Error>>;
|
||||||
|
|
||||||
/// A UI component.
|
/// A UI component.
|
||||||
pub trait Component<T, U>: Render<T, U> + Handle + Sync {
|
pub trait Component<'a, T, U>: Render<'a, T, U> + Handle + Sync {
|
||||||
/// Perform type erasure for collecting heterogeneous components.
|
/// Perform type erasure for collecting heterogeneous components.
|
||||||
fn boxed (self) -> Box<dyn Component<T, U>> where Self: Sized + 'static {
|
fn boxed (self) -> Box<dyn Component<'a, T, U>> where Self: Sized + 'static {
|
||||||
Box::new(self)
|
Box::new(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C, T, U> Component<T, U> for C where C: Render<T, U> + Handle + Sync {}
|
impl<'a, C, T, U> Component<'a, T, U> for C where C: Render<'a, T, U> + Handle + Sync {}
|
||||||
|
|
||||||
/// Marker trait for [Component]s that can [Exit]
|
/// Marker trait for [Component]s that can [Exit]
|
||||||
pub trait ExitableComponent<T, U>: Exit + Component<T, U> {
|
pub trait ExitableComponent<'a, T, U>: Exit + Component<'a, T, U> {
|
||||||
/// Perform type erasure for collecting heterogeneous components.
|
/// Perform type erasure for collecting heterogeneous components.
|
||||||
fn boxed (self) -> Box<dyn ExitableComponent<T, U>> where Self: Sized + 'static {
|
fn boxed (self) -> Box<dyn ExitableComponent<'a, T, U>> where Self: Sized + 'static {
|
||||||
Box::new(self)
|
Box::new(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Exit + Component<T, U>, T, U> ExitableComponent<T, U> for E {}
|
impl<'a, E: Exit + Component<'a, T, U>, T, U> ExitableComponent<'a, T, U> for E {}
|
||||||
|
|
||||||
/// Run the main loop.
|
/// Run the main loop.
|
||||||
pub fn run <R> (state: Arc<RwLock<R>>) -> Usually<Arc<RwLock<R>>>
|
pub fn run <R> (state: Arc<RwLock<R>>) -> Usually<Arc<RwLock<R>>>
|
||||||
where R: for <'a> Render<TuiOutput<'a>, Rect> + Handle + Sized + 'static
|
where R: for <'a> Render<'a, TuiOutput<'a>, Rect> + Handle + Sized + 'static
|
||||||
{
|
{
|
||||||
let exited = Arc::new(AtomicBool::new(false));
|
let exited = Arc::new(AtomicBool::new(false));
|
||||||
let _input_thread = input_thread(&exited, &state);
|
let _input_thread = input_thread(&exited, &state);
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,13 @@ use crate::*;
|
||||||
pub(crate) use ratatui::prelude::CrosstermBackend;
|
pub(crate) use ratatui::prelude::CrosstermBackend;
|
||||||
|
|
||||||
/// Trait for things that are displayed to the user.
|
/// Trait for things that are displayed to the user.
|
||||||
pub trait Render<T, U>: Send + Sync {
|
pub trait Render<'a, T, U>: Send + Sync {
|
||||||
fn render (&self, to: &mut T) -> Perhaps<U>;
|
fn render (&self, to: &'a mut T) -> Perhaps<U>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Options can be rendered optionally.
|
/// Options can be rendered optionally.
|
||||||
impl<R, T, U> Render<T, U> for Option<R> where R: Render<T, U> {
|
impl<'a, R, T, U> Render<'a, T, U> for Option<R> where R: Render<'a, T, U> {
|
||||||
fn render (&self, to: &mut T) -> Perhaps<U> {
|
fn render (&self, to: &'a mut T) -> Perhaps<U> {
|
||||||
match self {
|
match self {
|
||||||
Some(component) => component.render(to),
|
Some(component) => component.render(to),
|
||||||
None => Ok(None)
|
None => Ok(None)
|
||||||
|
|
@ -19,43 +19,43 @@ impl<R, T, U> Render<T, U> for Option<R> where R: Render<T, U> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Boxed references can be rendered.
|
/// Boxed references can be rendered.
|
||||||
impl<'a, T, U> Render<T, U> for Box<dyn Render<T, U> + 'a> {
|
impl<'a, T, U> Render<'a, T, U> for Box<dyn Render<'a, T, U> + 'a> {
|
||||||
fn render (&self, to: &mut T) -> Perhaps<U> {
|
fn render (&self, to: &'a mut T) -> Perhaps<U> {
|
||||||
(**self).render(to)
|
(**self).render(to)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Immutable references can be rendered.
|
/// Immutable references can be rendered.
|
||||||
impl<R, T, U> Render<T, U> for &R where R: Render<T, U> {
|
impl<'a, R, T, U> Render<'a, T, U> for &R where R: Render<'a, T, U> {
|
||||||
fn render (&self, to: &mut T) -> Perhaps<U> {
|
fn render (&self, to: &'a mut T) -> Perhaps<U> {
|
||||||
(*self).render(to)
|
(*self).render(to)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mutable references can be rendered.
|
/// Mutable references can be rendered.
|
||||||
impl<R, T, U> Render<T, U> for &mut R where R: Render<T, U> {
|
impl<'a, R, T, U> Render<'a, T, U> for &mut R where R: Render<'a, T, U> {
|
||||||
fn render (&self, to: &mut T) -> Perhaps<U> {
|
fn render (&self, to: &'a mut T) -> Perhaps<U> {
|
||||||
(**self).render(to)
|
(**self).render(to)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Counted references can be rendered.
|
/// Counted references can be rendered.
|
||||||
impl<R, T, U> Render<T, U> for Arc<R> where R: Render<T, U> {
|
impl<'a, R, T, U> Render<'a, T, U> for Arc<R> where R: Render<'a, T, U> {
|
||||||
fn render (&self, to: &mut T) -> Perhaps<U> {
|
fn render (&self, to: &'a mut T) -> Perhaps<U> {
|
||||||
self.as_ref().render(to)
|
self.as_ref().render(to)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// References behind a [Mutex] can be rendered.
|
/// References behind a [Mutex] can be rendered.
|
||||||
impl<R, T, U> Render<T, U> for Mutex<R> where R: Render<T, U> {
|
impl<'a, R, T, U> Render<'a, T, U> for Mutex<R> where R: Render<'a, T, U> {
|
||||||
fn render (&self, to: &mut T) -> Perhaps<U> {
|
fn render (&self, to: &'a mut T) -> Perhaps<U> {
|
||||||
self.lock().unwrap().render(to)
|
self.lock().unwrap().render(to)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// References behind a [RwLock] can be rendered.
|
/// References behind a [RwLock] can be rendered.
|
||||||
impl<R, T, U> Render<T, U> for RwLock<R> where R: Render<T, U> {
|
impl<'a, R, T, U> Render<'a, T, U> for RwLock<R> where R: Render<'a, T, U> {
|
||||||
fn render (&self, to: &mut T) -> Perhaps<U> {
|
fn render (&self, to: &'a mut T) -> Perhaps<U> {
|
||||||
self.read().unwrap().render(to)
|
self.read().unwrap().render(to)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -65,7 +65,7 @@ impl<R, T, U> Render<T, U> for RwLock<R> where R: Render<T, U> {
|
||||||
/// Rendering unboxed closures should also be possible;
|
/// Rendering unboxed closures should also be possible;
|
||||||
/// but in practice implementing the trait for an unboxed
|
/// but in practice implementing the trait for an unboxed
|
||||||
/// `Fn` closure causes an impl conflict.
|
/// `Fn` closure causes an impl conflict.
|
||||||
impl<'a, T, U> Render<T, U> for Box<dyn Fn(&mut T) -> Perhaps<U> + Send + Sync + 'a> {
|
impl<'a, T, U> Render<'a, T, U> for Box<dyn Fn(&mut T) -> Perhaps<U> + Send + Sync + 'a> {
|
||||||
fn render (&self, to: &mut T) -> Perhaps<U> {
|
fn render (&self, to: &mut T) -> Perhaps<U> {
|
||||||
(*self)(to)
|
(*self)(to)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub enum Collected<'a, T, U> {
|
pub enum Collected<'a, T, U> {
|
||||||
Box(Box<dyn Render<T, U> + 'a>),
|
Box(Box<dyn Render<'a, T, U> + 'a>),
|
||||||
Ref(&'a (dyn Render<T, U> + 'a)),
|
Ref(&'a (dyn Render<'a, T, U> + 'a)),
|
||||||
}
|
}
|
||||||
impl<'a, T, U> Render<T, U> for Collected<'a, T, U> {
|
impl<'a, T, U> Render<'a, T, U> for Collected<'a, T, U> {
|
||||||
fn render (&self, to: &mut T) -> Perhaps<U> {
|
fn render (&self, to: &'a mut T) -> Perhaps<U> {
|
||||||
match self {
|
match self {
|
||||||
Self::Box(item) => (*item).render(to),
|
Self::Box(item) => (*item).render(to),
|
||||||
Self::Ref(item) => (*item).render(to),
|
Self::Ref(item) => (*item).render(to),
|
||||||
|
|
@ -21,20 +21,20 @@ impl<'a, T, U> Collection<'a, T, U> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub trait Collect<'a, T, U> {
|
pub trait Collect<'a, T, U> {
|
||||||
fn add_box (self, item: Box<dyn Render<T, U> + 'a>) -> Self;
|
fn add_box (self, item: Box<dyn Render<'a, T, U> + 'a>) -> Self;
|
||||||
fn add_ref (self, item: &'a dyn Render<T, U>) -> Self;
|
fn add_ref (self, item: &'a dyn Render<'a, T, U>) -> Self;
|
||||||
fn add <R: Render<T, U> + Sized + 'a> (self, item: R) -> Self
|
fn add <R: Render<'a, T, U> + Sized + 'a> (self, item: R) -> Self
|
||||||
where Self: Sized
|
where Self: Sized
|
||||||
{
|
{
|
||||||
self.add_box(Box::new(item))
|
self.add_box(Box::new(item))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'a, T, U> Collect<'a, T, U> for Collection<'a, T, U> {
|
impl<'a, T, U> Collect<'a, T, U> for Collection<'a, T, U> {
|
||||||
fn add_box (mut self, item: Box<dyn Render<T, U> + 'a>) -> Self {
|
fn add_box (mut self, item: Box<dyn Render<'a, T, U> + 'a>) -> Self {
|
||||||
self.0.push(Collected::Box(item));
|
self.0.push(Collected::Box(item));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
fn add_ref (mut self, item: &'a dyn Render<T, U>) -> Self {
|
fn add_ref (mut self, item: &'a dyn Render<'a, T, U>) -> Self {
|
||||||
self.0.push(Collected::Ref(item));
|
self.0.push(Collected::Ref(item));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,18 +9,18 @@ impl<'a, T, U> Layered<'a, T, U> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T, U> Collect<'a, T, U> for Layered<'a, T, U> {
|
impl<'a, T, U> Collect<'a, T, U> for Layered<'a, T, U> {
|
||||||
fn add_box (mut self, item: Box<dyn Render<T, U> + 'a>) -> Self {
|
fn add_box (mut self, item: Box<dyn Render<'a, T, U> + 'a>) -> Self {
|
||||||
self.0 = self.0.add_box(item);
|
self.0 = self.0.add_box(item);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
fn add_ref (mut self, item: &'a dyn Render<T, U>) -> Self {
|
fn add_ref (mut self, item: &'a dyn Render<'a, T, U>) -> Self {
|
||||||
self.0 = self.0.add_ref(item);
|
self.0 = self.0.add_ref(item);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> Render<TuiOutput<'b>, Rect> for Layered<'a, TuiOutput<'b>, Rect> {
|
impl<'a, 'b> Render<'a, TuiOutput<'b>, Rect> for Layered<'a, TuiOutput<'b>, Rect> {
|
||||||
fn render (&self, to: &mut TuiOutput<'b>) -> Perhaps<Rect> {
|
fn render (&self, to: &'a mut TuiOutput<'b>) -> Perhaps<Rect> {
|
||||||
let area = to.area;
|
let area = to.area;
|
||||||
for layer in self.0.0.iter() {
|
for layer in self.0.0.iter() {
|
||||||
layer.render(to)?;
|
layer.render(to)?;
|
||||||
|
|
|
||||||
|
|
@ -44,17 +44,17 @@ impl<'a, T, U> Split<'a, T, U> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T, U> Collect<'a, T, U> for Split<'a, T, U> {
|
impl<'a, T, U> Collect<'a, T, U> for Split<'a, T, U> {
|
||||||
fn add_box (mut self, item: Box<dyn Render<T, U> + 'a>) -> Self {
|
fn add_box (mut self, item: Box<dyn Render<'a, T, U> + 'a>) -> Self {
|
||||||
self.items = self.items.add_box(item);
|
self.items = self.items.add_box(item);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
fn add_ref (mut self, item: &'a dyn Render<T, U>) -> Self {
|
fn add_ref (mut self, item: &'a dyn Render<'a, T, U>) -> Self {
|
||||||
self.items = self.items.add_ref(item);
|
self.items = self.items.add_ref(item);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Render<TuiOutput<'a>, Rect> for Split<'a, TuiOutput<'a>, Rect> {
|
impl<'a> Render<'a, TuiOutput<'a>, Rect> for Split<'a, TuiOutput<'a>, Rect> {
|
||||||
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
Ok(None)//Rect::default())//Some(self.render_areas(to)?.0))
|
Ok(None)//Rect::default())//Some(self.render_areas(to)?.0))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,17 @@ pub struct TuiOutput<'a> {
|
||||||
pub buffer: &'a mut Buffer,
|
pub buffer: &'a mut Buffer,
|
||||||
pub area: Rect
|
pub area: Rect
|
||||||
}
|
}
|
||||||
|
impl<'a> TuiOutput<'a> {
|
||||||
|
pub fn area (&'a mut self, area: Rect) -> Self {
|
||||||
|
Self { buffer: self.buffer, area }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Main thread render loop
|
/// Main thread render loop
|
||||||
pub fn tui_render_thread <T> (exited: &Arc<AtomicBool>, device: &Arc<RwLock<T>>)
|
pub fn tui_render_thread <T> (exited: &Arc<AtomicBool>, device: &Arc<RwLock<T>>)
|
||||||
-> Usually<JoinHandle<()>>
|
-> Usually<JoinHandle<()>>
|
||||||
where
|
where
|
||||||
T: for <'a> Render<TuiOutput<'a>, Rect> + 'static
|
T: for <'a> Render<'a, TuiOutput<'a>, Rect> + 'static
|
||||||
{
|
{
|
||||||
let exited = exited.clone();
|
let exited = exited.clone();
|
||||||
let device = device.clone();
|
let device = device.clone();
|
||||||
|
|
@ -29,7 +34,7 @@ where
|
||||||
let previous_buffer = &buffers[1 - index];
|
let previous_buffer = &buffers[1 - index];
|
||||||
let current_buffer = &buffers[index];
|
let current_buffer = &buffers[index];
|
||||||
let updates = previous_buffer.diff(current_buffer);
|
let updates = previous_buffer.diff(current_buffer);
|
||||||
backend.draw(updates.into_iter());
|
backend.draw(updates.into_iter()).expect("failed to render");
|
||||||
buffers[1 - index].reset();
|
buffers[1 - index].reset();
|
||||||
index = 1 - index;
|
index = 1 - index;
|
||||||
}
|
}
|
||||||
|
|
@ -82,7 +87,7 @@ impl<T: AsRef<str>> Blit for T {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Rendering unit struct to Ratatui returns zero-sized [Rect] at render coordinates.
|
/// Rendering unit struct to Ratatui returns zero-sized [Rect] at render coordinates.
|
||||||
impl<'a> Render<TuiOutput<'a>, Rect> for () {
|
impl<'a> Render<'a, TuiOutput<'a>, Rect> for () {
|
||||||
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
Ok(Some(Rect { x: to.area.x, y: to.area.y, width: 0, height: 0 }))
|
Ok(Some(Rect { x: to.area.x, y: to.area.y, width: 0, height: 0 }))
|
||||||
}
|
}
|
||||||
|
|
@ -104,3 +109,53 @@ pub fn half_block (lower: bool, upper: bool) -> Option<char> {
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct FillBg(pub Color);
|
||||||
|
|
||||||
|
impl<'a> Render<'a, TuiOutput<'a>, Rect> for FillBg {
|
||||||
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
|
fill_bg(to.buffer, to.area, self.0);
|
||||||
|
Ok(Some(to.area))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_dim (buf: &mut Buffer) {
|
||||||
|
for cell in buf.content.iter_mut() {
|
||||||
|
cell.bg = ratatui::style::Color::Rgb(30,30,30);
|
||||||
|
cell.fg = ratatui::style::Color::Rgb(100,100,100);
|
||||||
|
cell.modifier = ratatui::style::Modifier::DIM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn buffer_update (
|
||||||
|
buf: &mut Buffer, area: Rect, callback: &impl Fn(&mut Cell, u16, u16)
|
||||||
|
) {
|
||||||
|
for row in 0..area.height {
|
||||||
|
let y = area.y + row;
|
||||||
|
for col in 0..area.width {
|
||||||
|
let x = area.x + col;
|
||||||
|
if x < buf.area.width && y < buf.area.height {
|
||||||
|
callback(buf.get_mut(x, y), col, row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fill_fg (buf: &mut Buffer, area: Rect, color: Color) {
|
||||||
|
buffer_update(buf, area, &|cell,_,_|{cell.set_fg(color);})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fill_bg (buf: &mut Buffer, area: Rect, color: Color) {
|
||||||
|
buffer_update(buf, area, &|cell,_,_|{cell.set_bg(color);})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fill_ul (buf: &mut Buffer, area: Rect, color: Color) {
|
||||||
|
buffer_update(buf, area, &|cell,_,_|{
|
||||||
|
cell.modifier = ratatui::prelude::Modifier::UNDERLINED;
|
||||||
|
cell.underline_color = color;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fill_char (buf: &mut Buffer, area: Rect, c: char) {
|
||||||
|
buffer_update(buf, area, &|cell,_,_|{cell.set_char(c);})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,11 @@ pub trait BorderStyle {
|
||||||
const W: &'static str = "";
|
const W: &'static str = "";
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn draw (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
fn draw <'a> (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
self.draw_horizontal(buf, area, None)?;
|
self.draw_horizontal(to.buffer, to.area, None)?;
|
||||||
self.draw_vertical(buf, area, None)?;
|
self.draw_vertical(to.buffer, to.area, None)?;
|
||||||
self.draw_corners(buf, area, None)?;
|
self.draw_corners(to.buffer, to.area, None)?;
|
||||||
Ok(area)
|
Ok(Some(to.area))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
|
|
||||||
pub struct FillBg(pub Color);
|
|
||||||
|
|
||||||
impl<'a> Render<TuiOutput<'a>, Rect> for FillBg {
|
|
||||||
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
|
||||||
fill_bg(to.buffer, to.area, self.0);
|
|
||||||
Ok(Some(to.area))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn make_dim (buf: &mut Buffer) {
|
|
||||||
for cell in buf.content.iter_mut() {
|
|
||||||
cell.bg = ratatui::style::Color::Rgb(30,30,30);
|
|
||||||
cell.fg = ratatui::style::Color::Rgb(100,100,100);
|
|
||||||
cell.modifier = ratatui::style::Modifier::DIM;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn buffer_update (
|
|
||||||
buf: &mut Buffer, area: Rect, callback: &impl Fn(&mut Cell, u16, u16)
|
|
||||||
) {
|
|
||||||
for row in 0..area.height {
|
|
||||||
let y = area.y + row;
|
|
||||||
for col in 0..area.width {
|
|
||||||
let x = area.x + col;
|
|
||||||
if x < buf.area.width && y < buf.area.height {
|
|
||||||
callback(buf.get_mut(x, y), col, row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fill_fg (buf: &mut Buffer, area: Rect, color: Color) {
|
|
||||||
buffer_update(buf, area, &|cell,_,_|{cell.set_fg(color);})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fill_bg (buf: &mut Buffer, area: Rect, color: Color) {
|
|
||||||
buffer_update(buf, area, &|cell,_,_|{cell.set_bg(color);})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fill_ul (buf: &mut Buffer, area: Rect, color: Color) {
|
|
||||||
buffer_update(buf, area, &|cell,_,_|{
|
|
||||||
cell.modifier = ratatui::prelude::Modifier::UNDERLINED;
|
|
||||||
cell.underline_color = color;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fill_char (buf: &mut Buffer, area: Rect, c: char) {
|
|
||||||
buffer_update(buf, area, &|cell,_,_|{cell.set_char(c);})
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
pub(crate) use tek_core::*;
|
pub(crate) use tek_core::*;
|
||||||
pub(crate) use tek_core::ratatui::prelude::*;
|
|
||||||
pub(crate) use tek_core::crossterm::event::{KeyCode, KeyModifiers};
|
pub(crate) use tek_core::crossterm::event::{KeyCode, KeyModifiers};
|
||||||
pub(crate) use tek_core::midly::{num::u7, live::LiveEvent, MidiMessage};
|
pub(crate) use tek_core::midly::{num::u7, live::LiveEvent, MidiMessage};
|
||||||
pub(crate) use tek_core::jack::*;
|
pub(crate) use tek_core::jack::*;
|
||||||
|
|
@ -14,7 +13,6 @@ submod! {
|
||||||
mixer
|
mixer
|
||||||
mixer_cli
|
mixer_cli
|
||||||
mixer_handle
|
mixer_handle
|
||||||
mixer_render
|
|
||||||
track
|
track
|
||||||
track_view
|
track_view
|
||||||
track_handle
|
track_handle
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub struct Mixer {
|
pub struct Mixer<T, U> {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub tracks: Vec<Track>,
|
pub tracks: Vec<Track<T, U>>,
|
||||||
pub selected_track: usize,
|
pub selected_track: usize,
|
||||||
pub selected_column: usize,
|
pub selected_column: usize,
|
||||||
}
|
}
|
||||||
|
impl<T, U> Mixer<T, U> {
|
||||||
impl Mixer {
|
|
||||||
pub fn new (name: &str) -> Usually<Self> {
|
pub fn new (name: &str) -> Usually<Self> {
|
||||||
let (client, _status) = Client::new(name, ClientOptions::NO_START_SERVER)?;
|
let (client, _status) = Client::new(name, ClientOptions::NO_START_SERVER)?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
|
@ -22,17 +21,21 @@ impl Mixer {
|
||||||
self.tracks.push(track);
|
self.tracks.push(track);
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
pub fn track (&self) -> Option<&Track> {
|
pub fn track (&self) -> Option<&Track<T, U>> {
|
||||||
self.tracks.get(self.selected_track)
|
self.tracks.get(self.selected_track)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl<'a> Render<TuiOutput<'a>, Rect> for Mixer<TuiOutput<'a>, Rect> {
|
||||||
process!(Mixer = process);
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
|
let mut tracks = Split::right();
|
||||||
fn process (
|
for channel in self.tracks.iter() {
|
||||||
_: &mut Mixer,
|
tracks = tracks.add_ref(channel)
|
||||||
_: &Client,
|
}
|
||||||
_: &ProcessScope
|
tracks.render(to)
|
||||||
) -> Control {
|
}
|
||||||
Control::Continue
|
}
|
||||||
|
impl<T, U> Process for Mixer<T, U> {
|
||||||
|
fn process (&mut self, _: &Client, _: &ProcessScope) -> Control {
|
||||||
|
Control::Continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
use tek_core::clap::{self, Parser};
|
use tek_core::clap::{self, Parser};
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
#[command(version, about, long_about = None)]
|
#[command(version, about, long_about = None)]
|
||||||
pub struct MixerCli {
|
pub struct MixerCli {
|
||||||
|
|
@ -9,8 +8,7 @@ pub struct MixerCli {
|
||||||
/// Number of tracks
|
/// Number of tracks
|
||||||
#[arg(short, long)] channels: Option<usize>,
|
#[arg(short, long)] channels: Option<usize>,
|
||||||
}
|
}
|
||||||
|
impl<T, U> Mixer<T, U> {
|
||||||
impl Mixer {
|
|
||||||
pub fn from_args () -> Usually<Self> {
|
pub fn from_args () -> Usually<Self> {
|
||||||
let args = MixerCli::parse();
|
let args = MixerCli::parse();
|
||||||
let mut mix = Self::new("")?;
|
let mut mix = Self::new("")?;
|
||||||
|
|
|
||||||
|
|
@ -1,56 +1,57 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
handle!(Mixer = handle_mixer);
|
impl <T, U> Handle for Mixer<T, U> {
|
||||||
|
fn handle (&mut self, event: &AppEvent) -> Usually<bool> {
|
||||||
|
if let AppEvent::Input(crossterm::event::Event::Key(event)) = event {
|
||||||
|
|
||||||
|
match event.code {
|
||||||
|
//KeyCode::Char('c') => {
|
||||||
|
//if event.modifiers == KeyModifiers::CONTROL {
|
||||||
|
//self.exit();
|
||||||
|
//}
|
||||||
|
//},
|
||||||
|
KeyCode::Down => {
|
||||||
|
self.selected_track = (self.selected_track + 1) % self.tracks.len();
|
||||||
|
println!("{}", self.selected_track);
|
||||||
|
return Ok(true)
|
||||||
|
},
|
||||||
|
KeyCode::Up => {
|
||||||
|
if self.selected_track == 0 {
|
||||||
|
self.selected_track = self.tracks.len() - 1;
|
||||||
|
} else {
|
||||||
|
self.selected_track -= 1;
|
||||||
|
}
|
||||||
|
println!("{}", self.selected_track);
|
||||||
|
return Ok(true)
|
||||||
|
},
|
||||||
|
KeyCode::Left => {
|
||||||
|
if self.selected_column == 0 {
|
||||||
|
self.selected_column = 6
|
||||||
|
} else {
|
||||||
|
self.selected_column -= 1;
|
||||||
|
}
|
||||||
|
return Ok(true)
|
||||||
|
},
|
||||||
|
KeyCode::Right => {
|
||||||
|
if self.selected_column == 6 {
|
||||||
|
self.selected_column = 0
|
||||||
|
} else {
|
||||||
|
self.selected_column += 1;
|
||||||
|
}
|
||||||
|
return Ok(true)
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
println!("\n{event:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//pub const ACTIONS: [(&'static str, &'static str);2] = [
|
//pub const ACTIONS: [(&'static str, &'static str);2] = [
|
||||||
//("+/-", "Adjust"),
|
//("+/-", "Adjust"),
|
||||||
//("Ins/Del", "Add/remove track"),
|
//("Ins/Del", "Add/remove track"),
|
||||||
//];
|
//];
|
||||||
|
|
||||||
pub fn handle_mixer (state: &mut Mixer, event: &AppEvent) -> Usually<bool> {
|
|
||||||
if let AppEvent::Input(crossterm::event::Event::Key(event)) = event {
|
|
||||||
|
|
||||||
match event.code {
|
|
||||||
//KeyCode::Char('c') => {
|
|
||||||
//if event.modifiers == KeyModifiers::CONTROL {
|
|
||||||
//state.exit();
|
|
||||||
//}
|
|
||||||
//},
|
|
||||||
KeyCode::Down => {
|
|
||||||
state.selected_track = (state.selected_track + 1) % state.tracks.len();
|
|
||||||
println!("{}", state.selected_track);
|
|
||||||
return Ok(true)
|
|
||||||
},
|
|
||||||
KeyCode::Up => {
|
|
||||||
if state.selected_track == 0 {
|
|
||||||
state.selected_track = state.tracks.len() - 1;
|
|
||||||
} else {
|
|
||||||
state.selected_track -= 1;
|
|
||||||
}
|
|
||||||
println!("{}", state.selected_track);
|
|
||||||
return Ok(true)
|
|
||||||
},
|
|
||||||
KeyCode::Left => {
|
|
||||||
if state.selected_column == 0 {
|
|
||||||
state.selected_column = 6
|
|
||||||
} else {
|
|
||||||
state.selected_column -= 1;
|
|
||||||
}
|
|
||||||
return Ok(true)
|
|
||||||
},
|
|
||||||
KeyCode::Right => {
|
|
||||||
if state.selected_column == 6 {
|
|
||||||
state.selected_column = 0
|
|
||||||
} else {
|
|
||||||
state.selected_column += 1;
|
|
||||||
}
|
|
||||||
return Ok(true)
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
println!("\n{event:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
|
|
||||||
impl<'a> Render<TuiOutput<'a>, Rect> for Mixer {
|
|
||||||
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
|
||||||
let mut x = 0;
|
|
||||||
for channel in self.tracks.iter() {
|
|
||||||
x = x + channel.render(buf, Rect {
|
|
||||||
x: area.x + x,
|
|
||||||
y: area.y,
|
|
||||||
width: area.width,
|
|
||||||
height: area.height
|
|
||||||
})?.width;
|
|
||||||
if x >= area.width {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(area)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -12,12 +12,12 @@ pub struct Plugin {
|
||||||
handle!(Plugin |self, e| handle_keymap(self, e, KEYMAP_PLUGIN));
|
handle!(Plugin |self, e| handle_keymap(self, e, KEYMAP_PLUGIN));
|
||||||
process!(Plugin = Plugin::process);
|
process!(Plugin = Plugin::process);
|
||||||
impl<'a> Render<TuiOutput<'a>, Rect> for Plugin {
|
impl<'a> Render<TuiOutput<'a>, Rect> for Plugin {
|
||||||
fn render (&self, to: &mut TuiOutput<'a>) -> Usually<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
let Rect { x, y, height, .. } = area;
|
let Rect { x, y, height, .. } = to.area;
|
||||||
let mut width = 20u16;
|
let mut width = 20u16;
|
||||||
match &state.plugin {
|
match &self.plugin {
|
||||||
Some(PluginKind::LV2(LV2Plugin { port_list, instance, .. })) => {
|
Some(PluginKind::LV2(LV2Plugin { port_list, instance, .. })) => {
|
||||||
let start = state.selected.saturating_sub((height as usize / 2).saturating_sub(1));
|
let start = self.selected.saturating_sub((height as usize / 2).saturating_sub(1));
|
||||||
let end = start + height as usize - 2;
|
let end = start + height as usize - 2;
|
||||||
//draw_box(buf, Rect { x, y, width, height });
|
//draw_box(buf, Rect { x, y, width, height });
|
||||||
for i in start..end {
|
for i in start..end {
|
||||||
|
|
@ -30,11 +30,12 @@ impl<'a> Render<TuiOutput<'a>, Rect> for Plugin {
|
||||||
//let label = &format!("C·· M·· {:25} = {value:.03}", port.name);
|
//let label = &format!("C·· M·· {:25} = {value:.03}", port.name);
|
||||||
let label = &format!("{:25} = {value:.03}", port.name);
|
let label = &format!("{:25} = {value:.03}", port.name);
|
||||||
width = width.max(label.len() as u16 + 4);
|
width = width.max(label.len() as u16 + 4);
|
||||||
label.blit(buf, x + 2, y + 1 + i as u16 - start as u16, if i == state.selected {
|
let style = if i == self.selected {
|
||||||
Some(Style::default().green())
|
Some(Style::default().green())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
})?;
|
} ;
|
||||||
|
label.blit(to.buffer, x + 2, y + 1 + i as u16 - start as u16, style)?;
|
||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
@ -42,8 +43,8 @@ impl<'a> Render<TuiOutput<'a>, Rect> for Plugin {
|
||||||
},
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
draw_header(state, buf, area.x, area.y, width)?;
|
draw_header(self, to.buffer, to.area.x, to.area.y, width)?;
|
||||||
Ok(Some(Rect { width, ..area }))
|
Ok(Some(Rect { width, ..to.area }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,7 +57,7 @@ pub enum PluginKind {
|
||||||
VST3,
|
VST3,
|
||||||
}
|
}
|
||||||
impl Plugin {
|
impl Plugin {
|
||||||
pub fn new_lv2 (name: &str, path: &str) -> Usually<JackDevice> {
|
pub fn new_lv2 <T, U> (name: &str, path: &str) -> Usually<JackDevice<T, U>> {
|
||||||
let plugin = LV2Plugin::new(path)?;
|
let plugin = LV2Plugin::new(path)?;
|
||||||
jack_from_lv2(name, &plugin.plugin)?
|
jack_from_lv2(name, &plugin.plugin)?
|
||||||
.run(|ports|Box::new(Self {
|
.run(|ports|Box::new(Self {
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ pub struct LV2Plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LV2Plugin {
|
impl LV2Plugin {
|
||||||
pub fn from_edn <'e> (args: &[Edn<'e>]) -> Usually<JackDevice> {
|
pub fn from_edn <'e, T, U> (args: &[Edn<'e>]) -> Usually<JackDevice<T, U>> {
|
||||||
let mut name = String::new();
|
let mut name = String::new();
|
||||||
let mut path = String::new();
|
let mut path = String::new();
|
||||||
edn!(edn in args {
|
edn!(edn in args {
|
||||||
|
|
|
||||||
|
|
@ -23,20 +23,20 @@ pub struct AddSampleModal {
|
||||||
exit!(AddSampleModal);
|
exit!(AddSampleModal);
|
||||||
|
|
||||||
impl<'a> Render<TuiOutput<'a>, Rect> for AddSampleModal {
|
impl<'a> Render<TuiOutput<'a>, Rect> for AddSampleModal {
|
||||||
fn render (&self, to: &mut TuiOutput<'a>) -> Usually<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
make_dim(to.buffer);
|
make_dim(to.buffer);
|
||||||
let area = center_box(
|
let area = center_box(
|
||||||
to.area,
|
to.area,
|
||||||
64.max(to.area.width.saturating_sub(8)),
|
64.max(to.area.width.saturating_sub(8)),
|
||||||
20.max(to.area.width.saturating_sub(8)),
|
20.max(to.area.width.saturating_sub(8)),
|
||||||
);
|
);
|
||||||
fill_fg(buf, area, Color::Reset);
|
fill_fg(to.buffer, area, Color::Reset);
|
||||||
fill_bg(buf, area, Nord::bg_lo(true, true));
|
fill_bg(to.buffer, area, Nord::bg_lo(true, true));
|
||||||
fill_char(buf, area, ' ');
|
fill_char(to.buffer, area, ' ');
|
||||||
format!("{}", &self.dir.to_string_lossy())
|
format!("{}", &self.dir.to_string_lossy())
|
||||||
.blit(buf, area.x+2, area.y+1, Some(Style::default().bold()))?;
|
.blit(to.buffer, area.x+2, area.y+1, Some(Style::default().bold()))?;
|
||||||
"Select sample:"
|
"Select sample:"
|
||||||
.blit(buf, area.x+2, area.y+2, Some(Style::default().bold()))?;
|
.blit(to.buffer, area.x+2, area.y+2, Some(Style::default().bold()))?;
|
||||||
for (i, (is_dir, name)) in self.subdirs.iter()
|
for (i, (is_dir, name)) in self.subdirs.iter()
|
||||||
.map(|path|(true, path))
|
.map(|path|(true, path))
|
||||||
.chain(self.files.iter().map(|path|(false, path)))
|
.chain(self.files.iter().map(|path|(false, path)))
|
||||||
|
|
@ -49,13 +49,13 @@ impl<'a> Render<TuiOutput<'a>, Rect> for AddSampleModal {
|
||||||
let t = if is_dir { "" } else { "" };
|
let t = if is_dir { "" } else { "" };
|
||||||
let line = format!("{t} {}", name.to_string_lossy());
|
let line = format!("{t} {}", name.to_string_lossy());
|
||||||
let line = &line[..line.len().min(area.width as usize - 4)];
|
let line = &line[..line.len().min(area.width as usize - 4)];
|
||||||
line.blit(buf, area.x + 2, area.y + 3 + i as u16, Some(if i == self.cursor {
|
line.blit(to.buffer, area.x + 2, area.y + 3 + i as u16, Some(if i == self.cursor {
|
||||||
Style::default().green()
|
Style::default().green()
|
||||||
} else {
|
} else {
|
||||||
Style::default().white()
|
Style::default().white()
|
||||||
}))?;
|
}))?;
|
||||||
}
|
}
|
||||||
Lozenge(Style::default()).draw(buf, area)
|
Lozenge(Style::default()).draw(to)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,11 @@ process!(Sampler = Sampler::process);
|
||||||
handle!(Sampler |self, event| handle_keymap(self, event, KEYMAP_SAMPLER));
|
handle!(Sampler |self, event| handle_keymap(self, event, KEYMAP_SAMPLER));
|
||||||
|
|
||||||
impl<'a> Render<TuiOutput<'a>, Rect> for Sampler {
|
impl<'a> Render<TuiOutput<'a>, Rect> for Sampler {
|
||||||
fn render (&self, to: &mut TuiOutput<'a>) -> Usually<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
let Rect { x, y, height, .. } = area;
|
let Rect { x, y, height, .. } = to.area;
|
||||||
let style = Style::default().gray();
|
let style = Style::default().gray();
|
||||||
let title = format!(" {} ({})", self.name, self.voices.read().unwrap().len());
|
let title = format!(" {} ({})", self.name, self.voices.read().unwrap().len());
|
||||||
title.blit(buf, x+1, y, Some(style.white().bold().not_dim()))?;
|
title.blit(to.buffer, x+1, y, Some(style.white().bold().not_dim()))?;
|
||||||
let mut width = title.len() + 2;
|
let mut width = title.len() + 2;
|
||||||
let mut y1 = 1;
|
let mut y1 = 1;
|
||||||
let mut j = 0;
|
let mut j = 0;
|
||||||
|
|
@ -35,12 +35,14 @@ impl<'a> Render<TuiOutput<'a>, Rect> for Sampler {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
let active = j == self.cursor.0;
|
let active = j == self.cursor.0;
|
||||||
width = width.max(draw_sample(buf, x, y + y1, note, &*sample.read().unwrap(), active)?);
|
width = width.max(
|
||||||
|
draw_sample(to.buffer, x, y + y1, note, &*sample.read().unwrap(), active)?
|
||||||
|
);
|
||||||
y1 = y1 + 1;
|
y1 = y1 + 1;
|
||||||
j = j + 1;
|
j = j + 1;
|
||||||
}
|
}
|
||||||
let height = ((2 + y1) as u16).min(height);
|
let height = ((2 + y1) as u16).min(height);
|
||||||
Ok(Rect { x, y, width: (width as u16).min(area.width), height })
|
Ok(Some(Rect { x, y, width: (width as u16).min(to.area.width), height }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -103,7 +105,7 @@ pub const KEYMAP_SAMPLER: &'static [KeyBinding<Sampler>] = keymap!(Sampler {
|
||||||
});
|
});
|
||||||
|
|
||||||
impl Sampler {
|
impl Sampler {
|
||||||
pub fn from_edn <'e> (args: &[Edn<'e>]) -> Usually<JackDevice> {
|
pub fn from_edn <'e, T, U> (args: &[Edn<'e>]) -> Usually<JackDevice<T, U>> {
|
||||||
let mut name = String::new();
|
let mut name = String::new();
|
||||||
let mut dir = String::new();
|
let mut dir = String::new();
|
||||||
let mut samples = BTreeMap::new();
|
let mut samples = BTreeMap::new();
|
||||||
|
|
@ -132,7 +134,9 @@ impl Sampler {
|
||||||
Self::new(&name, Some(samples))
|
Self::new(&name, Some(samples))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new (name: &str, mapped: Option<BTreeMap<u7, Arc<RwLock<Sample>>>>) -> Usually<JackDevice> {
|
pub fn new <T, U> (
|
||||||
|
name: &str, mapped: Option<BTreeMap<u7, Arc<RwLock<Sample>>>>
|
||||||
|
) -> Usually<JackDevice<T, U>> {
|
||||||
Jack::new(name)?
|
Jack::new(name)?
|
||||||
.midi_in("midi")
|
.midi_in("midi")
|
||||||
.audio_in("recL")
|
.audio_in("recL")
|
||||||
|
|
|
||||||
|
|
@ -3,18 +3,16 @@ use tek_core::edn;
|
||||||
|
|
||||||
/// A sequencer track.
|
/// A sequencer track.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Track {
|
pub struct Track<T, U> {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// Inputs and outputs of 1st and last device
|
/// Inputs and outputs of 1st and last device
|
||||||
pub ports: JackPorts,
|
pub ports: JackPorts,
|
||||||
/// Device chain
|
/// Device chain
|
||||||
pub devices: Vec<JackDevice>,
|
pub devices: Vec<JackDevice<T, U>>,
|
||||||
/// Device selector
|
/// Device selector
|
||||||
pub device: usize,
|
pub device: usize,
|
||||||
}
|
}
|
||||||
|
impl<T, U> Track<T, U> {
|
||||||
impl Track {
|
|
||||||
|
|
||||||
pub fn new (name: &str) -> Usually<Self> {
|
pub fn new (name: &str) -> Usually<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
|
|
@ -23,15 +21,14 @@ impl Track {
|
||||||
device: 0,
|
device: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
fn get_device_mut (&self, i: usize) -> Option<RwLockWriteGuard<Box<dyn Device<T, U>>>> {
|
||||||
fn get_device_mut (&self, i: usize) -> Option<RwLockWriteGuard<Box<dyn Device>>> {
|
|
||||||
self.devices.get(i).map(|d|d.state.write().unwrap())
|
self.devices.get(i).map(|d|d.state.write().unwrap())
|
||||||
}
|
}
|
||||||
pub fn device_mut (&self) -> Option<RwLockWriteGuard<Box<dyn Device>>> {
|
pub fn device_mut (&self) -> Option<RwLockWriteGuard<Box<dyn Device<T, U>>>> {
|
||||||
self.get_device_mut(self.device)
|
self.get_device_mut(self.device)
|
||||||
}
|
}
|
||||||
/// Add a device to the end of the chain.
|
/// Add a device to the end of the chain.
|
||||||
pub fn append_device (&mut self, device: JackDevice) -> Usually<&mut JackDevice> {
|
pub fn append_device (&mut self, device: JackDevice<T, U>) -> Usually<&mut JackDevice<T, U>> {
|
||||||
self.devices.push(device);
|
self.devices.push(device);
|
||||||
let index = self.devices.len() - 1;
|
let index = self.devices.len() - 1;
|
||||||
Ok(&mut self.devices[index])
|
Ok(&mut self.devices[index])
|
||||||
|
|
@ -57,7 +54,7 @@ impl Track {
|
||||||
let mut _gain = 0.0f64;
|
let mut _gain = 0.0f64;
|
||||||
let mut track = Self::new("")?;
|
let mut track = Self::new("")?;
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
let mut devices: Vec<JackDevice> = vec![];
|
let mut devices: Vec<JackDevice<T, U>> = vec![];
|
||||||
edn!(edn in args {
|
edn!(edn in args {
|
||||||
Edn::Map(map) => {
|
Edn::Map(map) => {
|
||||||
if let Some(Edn::Str(n)) = map.get(&Edn::Key(SYM_NAME)) {
|
if let Some(Edn::Str(n)) = map.get(&Edn::Key(SYM_NAME)) {
|
||||||
|
|
@ -98,8 +95,7 @@ impl Track {
|
||||||
}
|
}
|
||||||
Ok(track)
|
Ok(track)
|
||||||
}
|
}
|
||||||
|
pub fn add_device (&mut self, device: JackDevice<T, U>) {
|
||||||
pub fn add_device (&mut self, device: JackDevice) {
|
|
||||||
self.devices.push(device);
|
self.devices.push(device);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,42 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
handle!(Track |self, event| handle_keymap(self, event, KEYMAP_TRACK));
|
impl <T, U> Handle for Track<T, U> {
|
||||||
|
fn handle (&mut self, event: &AppEvent) -> Usually<bool> {
|
||||||
/// Key bindings for chain section.
|
match event {
|
||||||
pub const KEYMAP_TRACK: &'static [KeyBinding<Track>] = keymap!(Track {
|
AppEvent::Input(crossterm::event::Event::Key(event)) => {
|
||||||
[Up, NONE, "chain_cursor_up", "move cursor up", |_: &mut Track| {
|
for (code, modifiers, _, _, command) in [
|
||||||
Ok(true)
|
key!(Up, NONE, "chain_cursor_up", "move cursor up", || {
|
||||||
}],
|
Ok(true)
|
||||||
[Down, NONE, "chain_cursor_down", "move cursor down", |_: &mut Track| {
|
}),
|
||||||
Ok(true)
|
key!(Down, NONE, "chain_cursor_down", "move cursor down", || {
|
||||||
}],
|
Ok(true)
|
||||||
[Left, NONE, "chain_cursor_left", "move cursor left", |app: &mut Track| {
|
}),
|
||||||
//if let Some(track) = app.arranger.track_mut() {
|
key!(Left, NONE, "chain_cursor_left", "move cursor left", || {
|
||||||
//track.device = track.device.saturating_sub(1);
|
//if let Some(track) = app.arranger.track_mut() {
|
||||||
//return Ok(true)
|
//track.device = track.device.saturating_sub(1);
|
||||||
//}
|
//return Ok(true)
|
||||||
Ok(false)
|
//}
|
||||||
}],
|
Ok(false)
|
||||||
[Right, NONE, "chain_cursor_right", "move cursor right", |app: &mut Track| {
|
}),
|
||||||
//if let Some(track) = app.arranger.track_mut() {
|
key!(Right, NONE, "chain_cursor_right", "move cursor right", || {
|
||||||
//track.device = (track.device + 1).min(track.devices.len().saturating_sub(1));
|
//if let Some(track) = app.arranger.track_mut() {
|
||||||
//return Ok(true)
|
//track.device = (track.device + 1).min(track.devices.len().saturating_sub(1));
|
||||||
//}
|
//return Ok(true)
|
||||||
Ok(false)
|
//}
|
||||||
}],
|
Ok(false)
|
||||||
[Char('`'), NONE, "chain_mode_switch", "switch the display mode", |app: &mut Track| {
|
}),
|
||||||
//app.chain_mode = !app.chain_mode;
|
key!(Char('`'), NONE, "chain_mode_switch", "switch the display mode", || {
|
||||||
Ok(true)
|
//app.chain_mode = !app.chain_mode;
|
||||||
}],
|
Ok(true)
|
||||||
});
|
}),
|
||||||
|
].iter() {
|
||||||
|
if *code == event.code && modifiers.bits() == event.modifiers.bits() {
|
||||||
|
return command()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(false)
|
||||||
|
},
|
||||||
|
_ => Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use tek_core::Direction;
|
use tek_core::Direction;
|
||||||
|
|
||||||
impl<'a> Render<TuiOutput<'a>, Rect> for Track {
|
impl<'a> Render<TuiOutput<'a>, Rect> for Track<TuiOutput<'a>, Rect> {
|
||||||
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
TrackView {
|
TrackView {
|
||||||
chain: Some(&self),
|
chain: Some(&self),
|
||||||
|
|
@ -24,37 +24,37 @@ impl<'a> Render<TuiOutput<'a>, Rect> for Track {
|
||||||
}.render(to)
|
}.render(to)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub struct TrackView<'a> {
|
pub struct TrackView<'a, T, U> {
|
||||||
pub chain: Option<&'a Track>,
|
pub chain: Option<&'a Track<T, U>>,
|
||||||
pub direction: Direction,
|
pub direction: Direction,
|
||||||
pub focused: bool,
|
pub focused: bool,
|
||||||
pub entered: bool,
|
pub entered: bool,
|
||||||
}
|
}
|
||||||
impl<'a> Render<TuiOutput<'a>, Rect> for TrackView<'a> {
|
impl<'a> Render<TuiOutput<'a>, Rect> for TrackView<'a, TuiOutput<'a>, Rect> {
|
||||||
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
if let Some(chain) = self.chain {
|
if let Some(chain) = self.chain {
|
||||||
match self.direction {
|
match self.direction {
|
||||||
Direction::Down => area.width = area.width.min(40),
|
Direction::Down => to.area.width = to.area.width.min(40),
|
||||||
Direction::Right => area.width = area.width.min(10),
|
Direction::Right => to.area.width = to.area.width.min(10),
|
||||||
_ => { unimplemented!() },
|
_ => { unimplemented!() },
|
||||||
}
|
}
|
||||||
fill_bg(buf, area, Nord::bg_lo(self.focused, self.entered));
|
fill_bg(to.buffer, to.area, Nord::bg_lo(self.focused, self.entered));
|
||||||
let mut split = Split::new(self.direction);
|
let mut split = Split::new(self.direction);
|
||||||
for device in chain.devices.as_slice().iter() {
|
for device in chain.devices.as_slice().iter() {
|
||||||
split = split.add_ref(device);
|
split = split.add_ref(device);
|
||||||
}
|
}
|
||||||
let (area, areas) = split.render_areas(buf, area)?;
|
let (area, areas) = split.render_areas(to)?;
|
||||||
if self.focused && self.entered && areas.len() > 0 {
|
if self.focused && self.entered && areas.len() > 0 {
|
||||||
Corners(Style::default().green().not_dim()).draw(buf, areas[0])?;
|
Corners(Style::default().green().not_dim()).draw(to.buffer, areas[0])?;
|
||||||
}
|
}
|
||||||
Ok(area)
|
Ok(Some(area))
|
||||||
} else {
|
} else {
|
||||||
let Rect { x, y, width, height } = area;
|
let Rect { x, y, width, height } = to.area;
|
||||||
let label = "No chain selected";
|
let label = "No chain selected";
|
||||||
let x = x + (width - label.len() as u16) / 2;
|
let x = x + (width - label.len() as u16) / 2;
|
||||||
let y = y + height / 2;
|
let y = y + height / 2;
|
||||||
label.blit(buf, x, y, Some(Style::default().dim().bold()))?;
|
label.blit(to.buffer, x, y, Some(Style::default().dim().bold()))?;
|
||||||
Ok(area)
|
Ok(Some(to.area))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
/// Represents the tracks and scenes of the composition.
|
/// Represents the tracks and scenes of the composition.
|
||||||
pub struct Arranger {
|
pub struct Arranger<T, U> {
|
||||||
/// Name of arranger
|
/// Name of arranger
|
||||||
pub name: Arc<RwLock<String>>,
|
pub name: Arc<RwLock<String>>,
|
||||||
/// Collection of tracks.
|
/// Collection of tracks.
|
||||||
|
|
@ -14,14 +14,11 @@ pub struct Arranger {
|
||||||
/// Display mode of arranger
|
/// Display mode of arranger
|
||||||
pub mode: ArrangerViewMode,
|
pub mode: ArrangerViewMode,
|
||||||
/// Slot for modal dialog displayed on top of app.
|
/// Slot for modal dialog displayed on top of app.
|
||||||
pub modal: Option<Box<dyn ExitableComponent>>,
|
pub modal: Option<Box<dyn ExitableComponent<T, U>>>,
|
||||||
/// Whether the arranger is currently focused
|
/// Whether the arranger is currently focused
|
||||||
pub focused: bool
|
pub focused: bool
|
||||||
}
|
}
|
||||||
|
impl<T, U> Arranger<T, U> {
|
||||||
focusable!(Arranger (focused));
|
|
||||||
|
|
||||||
impl Arranger {
|
|
||||||
pub fn new (name: &str) -> Self {
|
pub fn new (name: &str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: Arc::new(RwLock::new(name.into())),
|
name: Arc::new(RwLock::new(name.into())),
|
||||||
|
|
@ -83,3 +80,56 @@ impl Arranger {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Render arranger to terminal
|
||||||
|
impl<'a> Render<TuiOutput<'a>, Rect> for Arranger<TuiOutput<'a>, Rect> {
|
||||||
|
fn render (&'a self, to: &'a mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
|
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.area(Rect {
|
||||||
|
x: to.area.x + 1,
|
||||||
|
width: to.area.width - 2,
|
||||||
|
y: to.area.y + 1,
|
||||||
|
height: to.area.height - 2
|
||||||
|
}))?.unwrap();
|
||||||
|
Lozenge(Style::default().fg(Nord::BG2)).draw(&mut to.area(Rect {
|
||||||
|
x: area.x.saturating_sub(1),
|
||||||
|
width: area.width + 2,
|
||||||
|
y: area.y.saturating_sub(1),
|
||||||
|
height: area.height + 2,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a> Focusable<TuiOutput<'a>, Rect> for Arranger<TuiOutput<'a>, Rect> {
|
||||||
|
fn is_focused (&self) -> bool {
|
||||||
|
self.focused
|
||||||
|
}
|
||||||
|
fn set_focused (&mut self, focused: bool) {
|
||||||
|
self.focused = focused
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,86 +1,71 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
handle!(Arranger |self, e| {
|
impl<T, U> Handle for Arranger<T, U> {
|
||||||
match self.modal.as_mut() {
|
fn handle (&mut self, event: &AppEvent) -> Usually<bool> {
|
||||||
Some(modal) => {
|
match self.modal.as_mut() {
|
||||||
let result = modal.handle(e)?;
|
Some(modal) => {
|
||||||
if modal.exited() {
|
let result = modal.handle(event)?;
|
||||||
self.modal = None;
|
if modal.exited() {
|
||||||
|
self.modal = None;
|
||||||
|
}
|
||||||
|
Ok(result)
|
||||||
|
},
|
||||||
|
None => match event {
|
||||||
|
AppEvent::Input(crossterm::event::Event::Key(event)) => {
|
||||||
|
for (code, modifiers, _, _, command) in [
|
||||||
|
key!(Char('`'), NONE, "mode_switch", "switch the display mode", || {
|
||||||
|
self.mode.to_next();
|
||||||
|
Ok(true)
|
||||||
|
}),
|
||||||
|
key!(Up, NONE, "cursor_up", "move cursor up", || {
|
||||||
|
match self.mode {
|
||||||
|
ArrangerViewMode::Horizontal => self.track_prev(),
|
||||||
|
_ => self.scene_prev(),
|
||||||
|
};
|
||||||
|
self.show_phrase()?;
|
||||||
|
Ok(true)
|
||||||
|
}),
|
||||||
|
key!(Down, NONE, "cursor_down", "move cursor down", || {
|
||||||
|
match self.mode {
|
||||||
|
ArrangerViewMode::Horizontal => self.track_next(),
|
||||||
|
_ => self.scene_next(),
|
||||||
|
};
|
||||||
|
self.show_phrase()?;
|
||||||
|
Ok(true)
|
||||||
|
}),
|
||||||
|
key!(Left, NONE, "cursor_left", "move cursor left", || {
|
||||||
|
match self.mode {
|
||||||
|
ArrangerViewMode::Horizontal => self.scene_prev(),
|
||||||
|
_ => self.track_prev(),
|
||||||
|
};
|
||||||
|
self.show_phrase()?;
|
||||||
|
Ok(true)
|
||||||
|
}),
|
||||||
|
key!(Right, NONE, "cursor_right", "move cursor right", || {
|
||||||
|
match self.mode {
|
||||||
|
ArrangerViewMode::Horizontal => self.scene_next(),
|
||||||
|
_ => self.track_next(),
|
||||||
|
};
|
||||||
|
self.show_phrase()?;
|
||||||
|
Ok(true)
|
||||||
|
}),
|
||||||
|
key!(Char('.'), NONE, "increment", "set next clip at cursor", || { self.phrase_next(); Ok(true) }),
|
||||||
|
key!(Char(','), NONE, "decrement", "set previous clip at cursor", || { self.phrase_prev(); Ok(true) }),
|
||||||
|
key!(Enter, NONE, "activate", "activate item at cursor", || { self.activate(); Ok(true) }),
|
||||||
|
key!(Char('a'), CONTROL, "scene_add", "add a new scene", || { self.scene_add(None)?; Ok(true) }),
|
||||||
|
key!(Char('t'), CONTROL, "track_add", "add a new track", || { self.track_add(None)?; Ok(true) }),
|
||||||
|
key!(Char('n'), NONE, "rename", "rename item at cursor", || { self.rename_selected(); Ok(true) }),
|
||||||
|
key!(Char('l'), NONE, "length", "set length of item at cursor", || { todo!(); Ok(true) }),
|
||||||
|
key!(Char('c'), NONE, "color", "set color of item at cursor", || { todo!(); Ok(true) })
|
||||||
|
].iter() {
|
||||||
|
if *code == event.code && modifiers.bits() == event.modifiers.bits() {
|
||||||
|
return command()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(false)
|
||||||
|
},
|
||||||
|
_ => Ok(false)
|
||||||
}
|
}
|
||||||
Ok(result)
|
}
|
||||||
},
|
|
||||||
None => handle_keymap(self, e, KEYMAP_ARRANGER)
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
/// Key bindings for arranger section.
|
|
||||||
pub const KEYMAP_ARRANGER: &'static [KeyBinding<Arranger>] = keymap!(Arranger {
|
|
||||||
[Char('`'), NONE, "mode_switch", "switch the display mode", |arranger: &mut Arranger| {
|
|
||||||
arranger.mode.to_next();
|
|
||||||
Ok(true)
|
|
||||||
}],
|
|
||||||
[Up, NONE, "cursor_up", "move cursor up", |arranger: &mut Arranger| {
|
|
||||||
match arranger.mode {
|
|
||||||
ArrangerViewMode::Horizontal => arranger.track_prev(),
|
|
||||||
_ => arranger.scene_prev(),
|
|
||||||
};
|
|
||||||
arranger.show_phrase()?;
|
|
||||||
Ok(true)
|
|
||||||
}],
|
|
||||||
[Down, NONE, "cursor_down", "move cursor down", |arranger: &mut Arranger| {
|
|
||||||
match arranger.mode {
|
|
||||||
ArrangerViewMode::Horizontal => arranger.track_next(),
|
|
||||||
_ => arranger.scene_next(),
|
|
||||||
};
|
|
||||||
arranger.show_phrase()?;
|
|
||||||
Ok(true)
|
|
||||||
}],
|
|
||||||
[Left, NONE, "cursor_left", "move cursor left", |arranger: &mut Arranger| {
|
|
||||||
match arranger.mode {
|
|
||||||
ArrangerViewMode::Horizontal => arranger.scene_prev(),
|
|
||||||
_ => arranger.track_prev(),
|
|
||||||
};
|
|
||||||
arranger.show_phrase()?;
|
|
||||||
Ok(true)
|
|
||||||
}],
|
|
||||||
[Right, NONE, "cursor_right", "move cursor right", |arranger: &mut Arranger| {
|
|
||||||
match arranger.mode {
|
|
||||||
ArrangerViewMode::Horizontal => arranger.scene_next(),
|
|
||||||
_ => arranger.track_next(),
|
|
||||||
};
|
|
||||||
arranger.show_phrase()?;
|
|
||||||
Ok(true)
|
|
||||||
}],
|
|
||||||
[Char('.'), NONE, "increment", "set next clip at cursor", |arranger: &mut Arranger| {
|
|
||||||
arranger.phrase_next();
|
|
||||||
Ok(true)
|
|
||||||
}],
|
|
||||||
[Char(','), NONE, "decrement", "set previous clip at cursor", |arranger: &mut Arranger| {
|
|
||||||
arranger.phrase_prev();
|
|
||||||
Ok(true)
|
|
||||||
}],
|
|
||||||
[Enter, NONE, "activate", "activate item at cursor", |arranger: &mut Arranger| {
|
|
||||||
arranger.activate();
|
|
||||||
Ok(true)
|
|
||||||
}],
|
|
||||||
[Char('a'), CONTROL, "scene_add", "add a new scene", |arranger: &mut Arranger| {
|
|
||||||
arranger.scene_add(None)?;
|
|
||||||
Ok(true)
|
|
||||||
}],
|
|
||||||
[Char('t'), CONTROL, "track_add", "add a new track", |arranger: &mut Arranger| {
|
|
||||||
arranger.track_add(None)?;
|
|
||||||
Ok(true)
|
|
||||||
}],
|
|
||||||
[Char('n'), NONE, "rename", "rename item at cursor", |arranger: &mut Arranger| {
|
|
||||||
arranger.rename_selected();
|
|
||||||
Ok(true)
|
|
||||||
}],
|
|
||||||
[Char('l'), NONE, "length", "set length of item at cursor", |arranger: &mut Arranger| {
|
|
||||||
todo!();
|
|
||||||
Ok(true)
|
|
||||||
}],
|
|
||||||
[Char('c'), NONE, "color", "set color of item at cursor", |arranger: &mut Arranger| {
|
|
||||||
todo!();
|
|
||||||
Ok(true)
|
|
||||||
}]
|
|
||||||
});
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
impl<'a> Arranger<TuiOutput<'a>, Rect> {
|
||||||
impl Arranger {
|
|
||||||
pub fn rename_selected (&mut self) {
|
pub fn rename_selected (&mut self) {
|
||||||
self.modal = Some(Box::new(ArrangerRenameModal::new(
|
self.modal = Some(Box::new(ArrangerRenameModal::new(
|
||||||
self.selected,
|
self.selected,
|
||||||
|
|
@ -13,7 +12,6 @@ impl Arranger {
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Appears on first run (i.e. if state dir is missing).
|
/// Appears on first run (i.e. if state dir is missing).
|
||||||
pub struct ArrangerRenameModal {
|
pub struct ArrangerRenameModal {
|
||||||
done: bool,
|
done: bool,
|
||||||
|
|
@ -22,7 +20,6 @@ pub struct ArrangerRenameModal {
|
||||||
result: Arc<RwLock<String>>,
|
result: Arc<RwLock<String>>,
|
||||||
cursor: usize
|
cursor: usize
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ArrangerRenameModal {
|
impl ArrangerRenameModal {
|
||||||
pub fn new (target: ArrangerFocus, value: &Arc<RwLock<String>>) -> Self {
|
pub fn new (target: ArrangerFocus, value: &Arc<RwLock<String>>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -34,18 +31,17 @@ impl ArrangerRenameModal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Render<TuiOutput<'a>, Rect> for ArrangerRenameModal {
|
impl<'a> Render<TuiOutput<'a>, Rect> for ArrangerRenameModal {
|
||||||
fn render (&self, to: &mut TuiOutput<'a>) -> Usually<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
let y = area.y + area.height / 2;
|
let y = to.area.y + to.area.height / 2;
|
||||||
let bg_area = Rect {
|
let bg_area = Rect {
|
||||||
x: 1,
|
x: 1,
|
||||||
y: y - 1,
|
y: y - 1,
|
||||||
width: area.width - 2,
|
width: to.area.width - 2,
|
||||||
height: 3
|
height: 3
|
||||||
};
|
};
|
||||||
fill_bg(buf, bg_area, Nord::BG0);
|
fill_bg(to.buffer, bg_area, Nord::BG0);
|
||||||
Lozenge(Style::default().bold().white().dim()).draw(buf, bg_area)?;
|
Lozenge(Style::default().bold().white().dim()).draw(to.buffer, bg_area)?;
|
||||||
let label = match self.target {
|
let label = match self.target {
|
||||||
ArrangerFocus::Mix => "Rename project:",
|
ArrangerFocus::Mix => "Rename project:",
|
||||||
ArrangerFocus::Track(_) => "Rename track:",
|
ArrangerFocus::Track(_) => "Rename track:",
|
||||||
|
|
@ -53,15 +49,14 @@ impl<'a> Render<TuiOutput<'a>, Rect> for ArrangerRenameModal {
|
||||||
ArrangerFocus::Clip(_, _) => "Rename clip:",
|
ArrangerFocus::Clip(_, _) => "Rename clip:",
|
||||||
};
|
};
|
||||||
let style = Some(Style::default().not_bold().white().not_dim());
|
let style = Some(Style::default().not_bold().white().not_dim());
|
||||||
label.blit(buf, area.x + 3, y, style)?;
|
label.blit(to.buffer, to.area.x + 3, y, style)?;
|
||||||
let style = Some(Style::default().bold().white().not_dim());
|
let style = Some(Style::default().bold().white().not_dim());
|
||||||
self.value.blit(buf, area.x + 3 + label.len() as u16 + 1, y, style)?;
|
self.value.blit(to.buffer, to.area.x + 3 + label.len() as u16 + 1, y, style)?;
|
||||||
let style = Some(Style::default().bold().white().not_dim().reversed());
|
let style = Some(Style::default().bold().white().not_dim().reversed());
|
||||||
"▂".blit(buf, area.x + 3 + label.len() as u16 + 1 + self.cursor as u16, y, style)?;
|
"▂".blit(to.buffer, to.area.x + 3 + label.len() as u16 + 1 + self.cursor as u16, y, style)?;
|
||||||
Ok(area)
|
Ok(Some(to.area))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handle!(ArrangerRenameModal |self, e| {
|
handle!(ArrangerRenameModal |self, e| {
|
||||||
match e {
|
match e {
|
||||||
AppEvent::Input(Event::Key(k)) => {
|
AppEvent::Input(Event::Key(k)) => {
|
||||||
|
|
@ -98,7 +93,6 @@ handle!(ArrangerRenameModal |self, e| {
|
||||||
_ => Ok(false),
|
_ => Ok(false),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
impl Exit for ArrangerRenameModal {
|
impl Exit for ArrangerRenameModal {
|
||||||
fn exited (&self) -> bool {
|
fn exited (&self) -> bool {
|
||||||
self.done
|
self.done
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use crate::*;
|
||||||
use super::Arranger;
|
use super::Arranger;
|
||||||
|
|
||||||
/// Track management methods
|
/// Track management methods
|
||||||
impl Arranger {
|
impl<T, U> Arranger<T, U> {
|
||||||
pub fn track (&self) -> Option<&Sequencer> {
|
pub fn track (&self) -> Option<&Sequencer> {
|
||||||
self.selected.track().map(|t|self.tracks.get(t)).flatten()
|
self.selected.track().map(|t|self.tracks.get(t)).flatten()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
|
|
||||||
/// Display mode of arranger
|
|
||||||
pub enum ArrangerViewMode {
|
|
||||||
VerticalExpanded,
|
|
||||||
VerticalCompact1,
|
|
||||||
VerticalCompact2,
|
|
||||||
Horizontal,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ArrangerViewMode {
|
|
||||||
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<'a> Render<TuiOutput<'a>, Rect> for Arranger {
|
|
||||||
fn render (&self, to: &mut TuiOutput<'a>) -> Usually<Rect> {
|
|
||||||
let area = Rect {
|
|
||||||
x: to.area.x + 1,
|
|
||||||
width: to.area.width - 2,
|
|
||||||
y: to.area.y + 1,
|
|
||||||
height: to.area.height - 2
|
|
||||||
};
|
|
||||||
let area = match self.mode {
|
|
||||||
ArrangerViewMode::Horizontal =>
|
|
||||||
super::arranger_view_h::draw(self, to.buffer, area),
|
|
||||||
ArrangerViewMode::VerticalCompact1 =>
|
|
||||||
super::arranger_view_v::draw_compact_1(self, to.buffer, area),
|
|
||||||
ArrangerViewMode::VerticalCompact2 =>
|
|
||||||
super::arranger_view_v::draw_compact_2(self, to.buffer, area),
|
|
||||||
ArrangerViewMode::VerticalExpanded =>
|
|
||||||
super::arranger_view_v::draw_expanded(self, to.buffer, area),
|
|
||||||
}?;
|
|
||||||
let area = Rect {
|
|
||||||
x: area.x - 1,
|
|
||||||
width: area.width + 2,
|
|
||||||
y: area.y - 1,
|
|
||||||
height: area.height + 2,
|
|
||||||
};
|
|
||||||
Lozenge(Style::default().fg(Nord::BG2)).draw(to.buffer, area)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub fn draw (state: &Arranger, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> {
|
pub fn draw <'a> (
|
||||||
|
state: &Arranger<TuiOutput<'a>, Rect>, to: &mut TuiOutput<'a>
|
||||||
|
) -> Perhaps<Rect> {
|
||||||
|
let mut area = to.area;
|
||||||
area.height = area.height.min((2 + state.tracks.len() * 2) as u16);
|
area.height = area.height.min((2 + state.tracks.len() * 2) as u16);
|
||||||
let tracks = state.tracks.as_slice();
|
let tracks = state.tracks.as_slice();
|
||||||
Layered::new()
|
Layered::new()
|
||||||
|
|
@ -13,51 +16,53 @@ pub fn draw (state: &Arranger, buf: &mut Buffer, mut area: Rect) -> Usually<Rect
|
||||||
.add(TrackEraseColumn(tracks))
|
.add(TrackEraseColumn(tracks))
|
||||||
.add(TrackGainColumn(tracks))
|
.add(TrackGainColumn(tracks))
|
||||||
.add(TrackScenesColumn(tracks, state.scenes.as_slice(), state.selected)))
|
.add(TrackScenesColumn(tracks, state.scenes.as_slice(), state.selected)))
|
||||||
.render(buf, area)
|
.render(to)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TrackNameColumn<'a>(&'a [Sequencer], ArrangerFocus);
|
struct TrackNameColumn<'a>(&'a [Sequencer], ArrangerFocus);
|
||||||
|
|
||||||
impl<'a> Render for TrackNameColumn<'a> {
|
impl<'a> Render<TuiOutput<'a>, Rect> for TrackNameColumn<'a> {
|
||||||
fn render (&self, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
let Self(tracks, selected) = self;
|
let Self(tracks, selected) = self;
|
||||||
|
let mut area = to.area;
|
||||||
let yellow = Some(Style::default().yellow().bold().not_dim());
|
let yellow = Some(Style::default().yellow().bold().not_dim());
|
||||||
let white = Some(Style::default().white().bold().not_dim());
|
let white = Some(Style::default().white().bold().not_dim());
|
||||||
area.width = 3 + 5.max(track_name_max_len(tracks)) as u16;
|
area.width = 3 + 5.max(track_name_max_len(tracks)) as u16;
|
||||||
let offset = 0; // track scroll offset
|
let offset = 0; // track scroll offset
|
||||||
for y in 0..area.height {
|
for y in 0..area.height {
|
||||||
if y == 0 {
|
if y == 0 {
|
||||||
"Mixer".blit(buf, area.x + 1, area.y + y, Some(DIM))?;
|
"Mixer".blit(to.buffer, area.x + 1, area.y + y, Some(DIM))?;
|
||||||
} else if y % 2 == 0 {
|
} else if y % 2 == 0 {
|
||||||
let index = (y as usize - 2) / 2 + offset;
|
let index = (y as usize - 2) / 2 + offset;
|
||||||
if let Some(track) = tracks.get(index) {
|
if let Some(track) = tracks.get(index) {
|
||||||
let selected = selected.track() == Some(index);
|
let selected = selected.track() == Some(index);
|
||||||
let style = if selected { yellow } else { white };
|
let style = if selected { yellow } else { white };
|
||||||
format!(" {index:>02} ").blit(buf, area.x, area.y + y, style)?;
|
format!(" {index:>02} ").blit(to.buffer, area.x, area.y + y, style)?;
|
||||||
track.name.read().unwrap().blit(buf, area.x + 4, area.y + y, style)?;
|
track.name.read().unwrap().blit(to.buffer, area.x + 4, area.y + y, style)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(area)
|
Ok(Some(area))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TrackMonitorColumn<'a>(&'a [Sequencer]);
|
struct TrackMonitorColumn<'a>(&'a [Sequencer]);
|
||||||
|
|
||||||
impl<'a> Render for TrackMonitorColumn<'a> {
|
impl<'a> Render<TuiOutput<'a>, Rect> for TrackMonitorColumn<'a> {
|
||||||
fn render (&self, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
let Self(tracks) = self;
|
let Self(tracks) = self;
|
||||||
|
let mut area = to.area;
|
||||||
let on = Some(Style::default().not_dim().green().bold());
|
let on = Some(Style::default().not_dim().green().bold());
|
||||||
let off = Some(DIM);
|
let off = Some(DIM);
|
||||||
area.x += 1;
|
area.x += 1;
|
||||||
for y in 0..area.height {
|
for y in 0..area.height {
|
||||||
if y == 0 {
|
if y == 0 {
|
||||||
//" MON ".blit(buf, area.x, area.y + y, style2)?;
|
//" MON ".blit(to.buffer, area.x, area.y + y, style2)?;
|
||||||
} else if y % 2 == 0 {
|
} else if y % 2 == 0 {
|
||||||
let index = (y as usize - 2) / 2;
|
let index = (y as usize - 2) / 2;
|
||||||
if let Some(track) = tracks.get(index) {
|
if let Some(track) = tracks.get(index) {
|
||||||
let style = if track.monitoring { on } else { off };
|
let style = if track.monitoring { on } else { off };
|
||||||
" MON ".blit(buf, area.x, area.y + y, style)?;
|
" MON ".blit(to.buffer, area.x, area.y + y, style)?;
|
||||||
} else {
|
} else {
|
||||||
area.height = y;
|
area.height = y;
|
||||||
break
|
break
|
||||||
|
|
@ -65,26 +70,27 @@ impl<'a> Render for TrackMonitorColumn<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
area.width = 4;
|
area.width = 4;
|
||||||
Ok(area)
|
Ok(Some(area))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TrackRecordColumn<'a>(&'a [Sequencer]);
|
struct TrackRecordColumn<'a>(&'a [Sequencer]);
|
||||||
|
|
||||||
impl<'a> Render for TrackRecordColumn<'a> {
|
impl<'a> Render<TuiOutput<'a>, Rect> for TrackRecordColumn<'a> {
|
||||||
fn render (&self, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
let Self(tracks) = self;
|
let Self(tracks) = self;
|
||||||
|
let mut area = to.area;
|
||||||
let on = Some(Style::default().not_dim().red().bold());
|
let on = Some(Style::default().not_dim().red().bold());
|
||||||
let off = Some(Style::default().dim());
|
let off = Some(Style::default().dim());
|
||||||
area.x += 1;
|
area.x += 1;
|
||||||
for y in 0..area.height {
|
for y in 0..area.height {
|
||||||
if y == 0 {
|
if y == 0 {
|
||||||
//" REC ".blit(buf, area.x, area.y + y, style2)?;
|
//" REC ".blit(to.buffer, area.x, area.y + y, style2)?;
|
||||||
} else if y % 2 == 0 {
|
} else if y % 2 == 0 {
|
||||||
let index = (y as usize - 2) / 2;
|
let index = (y as usize - 2) / 2;
|
||||||
if let Some(track) = tracks.get(index) {
|
if let Some(track) = tracks.get(index) {
|
||||||
let style = if track.recording { on } else { off };
|
let style = if track.recording { on } else { off };
|
||||||
" REC ".blit(buf, area.x, area.y + y, style)?;
|
" REC ".blit(to.buffer, area.x, area.y + y, style)?;
|
||||||
} else {
|
} else {
|
||||||
area.height = y;
|
area.height = y;
|
||||||
break
|
break
|
||||||
|
|
@ -92,25 +98,26 @@ impl<'a> Render for TrackRecordColumn<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
area.width = 4;
|
area.width = 4;
|
||||||
Ok(area)
|
Ok(Some(area))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TrackOverdubColumn<'a>(&'a [Sequencer]);
|
struct TrackOverdubColumn<'a>(&'a [Sequencer]);
|
||||||
|
|
||||||
impl<'a> Render for TrackOverdubColumn<'a> {
|
impl<'a> Render<TuiOutput<'a>, Rect> for TrackOverdubColumn<'a> {
|
||||||
fn render (&self, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
let Self(tracks) = self;
|
let Self(tracks) = self;
|
||||||
|
let mut area = to.area;
|
||||||
let on = Some(Style::default().not_dim().yellow().bold());
|
let on = Some(Style::default().not_dim().yellow().bold());
|
||||||
let off = Some(Style::default().dim());
|
let off = Some(Style::default().dim());
|
||||||
area.x = area.x + 1;
|
area.x = area.x + 1;
|
||||||
for y in 0..area.height {
|
for y in 0..area.height {
|
||||||
if y == 0 {
|
if y == 0 {
|
||||||
//" OVR ".blit(buf, area.x, area.y + y, style2)?;
|
//" OVR ".blit(to.buffer, area.x, area.y + y, style2)?;
|
||||||
} else if y % 2 == 0 {
|
} else if y % 2 == 0 {
|
||||||
let index = (y as usize - 2) / 2;
|
let index = (y as usize - 2) / 2;
|
||||||
if let Some(track) = tracks.get(index) {
|
if let Some(track) = tracks.get(index) {
|
||||||
" OVR ".blit(buf, area.x, area.y + y, if track.overdub {
|
" OVR ".blit(to.buffer, area.x, area.y + y, if track.overdub {
|
||||||
on
|
on
|
||||||
} else {
|
} else {
|
||||||
off
|
off
|
||||||
|
|
@ -122,24 +129,25 @@ impl<'a> Render for TrackOverdubColumn<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
area.width = 4;
|
area.width = 4;
|
||||||
Ok(area)
|
Ok(Some(area))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TrackEraseColumn<'a>(&'a [Sequencer]);
|
struct TrackEraseColumn<'a>(&'a [Sequencer]);
|
||||||
|
|
||||||
impl<'a> Render for TrackEraseColumn<'a> {
|
impl<'a> Render<TuiOutput<'a>, Rect> for TrackEraseColumn<'a> {
|
||||||
fn render (&self, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
let Self(tracks) = self;
|
let Self(tracks) = self;
|
||||||
|
let mut area = to.area;
|
||||||
let off = Some(Style::default().dim());
|
let off = Some(Style::default().dim());
|
||||||
area.x = area.x + 1;
|
area.x = area.x + 1;
|
||||||
for y in 0..area.height {
|
for y in 0..area.height {
|
||||||
if y == 0 {
|
if y == 0 {
|
||||||
//" DEL ".blit(buf, area.x, area.y + y, style2)?;
|
//" DEL ".blit(to.buffer, area.x, area.y + y, style2)?;
|
||||||
} else if y % 2 == 0 {
|
} else if y % 2 == 0 {
|
||||||
let index = (y as usize - 2) / 2;
|
let index = (y as usize - 2) / 2;
|
||||||
if let Some(_) = tracks.get(index) {
|
if let Some(_) = tracks.get(index) {
|
||||||
" DEL ".blit(buf, area.x, area.y + y, off)?;
|
" DEL ".blit(to.buffer, area.x, area.y + y, off)?;
|
||||||
} else {
|
} else {
|
||||||
area.height = y;
|
area.height = y;
|
||||||
break
|
break
|
||||||
|
|
@ -147,24 +155,25 @@ impl<'a> Render for TrackEraseColumn<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
area.width = 4;
|
area.width = 4;
|
||||||
Ok(area)
|
Ok(Some(area))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TrackGainColumn<'a>(&'a [Sequencer]);
|
struct TrackGainColumn<'a>(&'a [Sequencer]);
|
||||||
|
|
||||||
impl<'a> Render for TrackGainColumn<'a> {
|
impl<'a> Render<TuiOutput<'a>, Rect> for TrackGainColumn<'a> {
|
||||||
fn render (&self, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
let Self(tracks) = self;
|
let Self(tracks) = self;
|
||||||
|
let mut area = to.area;
|
||||||
let off = Some(Style::default().dim());
|
let off = Some(Style::default().dim());
|
||||||
area.x = area.x + 1;
|
area.x = area.x + 1;
|
||||||
for y in 0..area.height {
|
for y in 0..area.height {
|
||||||
if y == 0 {
|
if y == 0 {
|
||||||
//" GAIN ".blit(buf, area.x, area.y + y, style2)?;
|
//" GAIN ".blit(to.buffer, area.x, area.y + y, style2)?;
|
||||||
} else if y % 2 == 0 {
|
} else if y % 2 == 0 {
|
||||||
let index = (y as usize - 2) / 2;
|
let index = (y as usize - 2) / 2;
|
||||||
if let Some(_) = tracks.get(index) {
|
if let Some(_) = tracks.get(index) {
|
||||||
" +0.0 ".blit(buf, area.x, area.y + y, off)?;
|
" +0.0 ".blit(to.buffer, area.x, area.y + y, off)?;
|
||||||
} else {
|
} else {
|
||||||
area.height = y;
|
area.height = y;
|
||||||
break
|
break
|
||||||
|
|
@ -172,15 +181,16 @@ impl<'a> Render for TrackGainColumn<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
area.width = 7;
|
area.width = 7;
|
||||||
Ok(area)
|
Ok(Some(area))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TrackScenesColumn<'a>(&'a [Sequencer], &'a [Scene], ArrangerFocus);
|
struct TrackScenesColumn<'a>(&'a [Sequencer], &'a [Scene], ArrangerFocus);
|
||||||
|
|
||||||
impl<'a> Render for TrackScenesColumn<'a> {
|
impl<'a> Render<TuiOutput<'a>, Rect> for TrackScenesColumn<'a> {
|
||||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
let Self(tracks, scenes, selected) = self;
|
let Self(tracks, scenes, selected) = self;
|
||||||
|
let area = to.area;
|
||||||
let mut x2 = 0;
|
let mut x2 = 0;
|
||||||
let Rect { x, y, height, .. } = area;
|
let Rect { x, y, height, .. } = area;
|
||||||
for (scene_index, scene) in scenes.iter().enumerate() {
|
for (scene_index, scene) in scenes.iter().enumerate() {
|
||||||
|
|
@ -191,11 +201,11 @@ impl<'a> Render for TrackScenesColumn<'a> {
|
||||||
Style::default().dim()
|
Style::default().dim()
|
||||||
});
|
});
|
||||||
for y in y+1..y+height {
|
for y in y+1..y+height {
|
||||||
"│".blit(buf, x + x2, y, sep)?;
|
"│".blit(to.buffer, x + x2, y, sep)?;
|
||||||
}
|
}
|
||||||
let name = scene.name.read().unwrap();
|
let name = scene.name.read().unwrap();
|
||||||
let mut x3 = name.len() as u16;
|
let mut x3 = name.len() as u16;
|
||||||
name.blit(buf, x + x2, y, sep)?;
|
name.blit(to.buffer, x + x2, y, sep)?;
|
||||||
for (i, clip) in scene.clips.iter().enumerate() {
|
for (i, clip) in scene.clips.iter().enumerate() {
|
||||||
let active_track = selected.track() == Some(i);
|
let active_track = selected.track() == Some(i);
|
||||||
if let Some(clip) = clip {
|
if let Some(clip) = clip {
|
||||||
|
|
@ -204,7 +214,7 @@ impl<'a> Render for TrackScenesColumn<'a> {
|
||||||
Some(phrase) => &format!("{}", phrase.read().unwrap().name.read().unwrap()),
|
Some(phrase) => &format!("{}", phrase.read().unwrap().name.read().unwrap()),
|
||||||
None => "...."
|
None => "...."
|
||||||
};
|
};
|
||||||
label.blit(buf, x + x2, y2, Some(if active_track && active_scene {
|
label.blit(to.buffer, x + x2, y2, Some(if active_track && active_scene {
|
||||||
Style::default().not_dim().yellow().bold()
|
Style::default().not_dim().yellow().bold()
|
||||||
} else {
|
} else {
|
||||||
Style::default().not_dim()
|
Style::default().not_dim()
|
||||||
|
|
@ -214,6 +224,6 @@ impl<'a> Render for TrackScenesColumn<'a> {
|
||||||
}
|
}
|
||||||
x2 = x2 + x3 + 1;
|
x2 = x2 + x3 + 1;
|
||||||
}
|
}
|
||||||
Ok(Rect { x, y, height, width: x2 })
|
Ok(Some(Rect { x, y, height, width: x2 }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,39 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
/// Draw arranger with 1 row per scene.
|
/// Draw arranger with 1 row per scene.
|
||||||
pub fn draw_compact_1 (state: &Arranger, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
pub fn draw_compact_1 <'a> (
|
||||||
|
state: &Arranger<TuiOutput<'a>, Rect>, to: &mut TuiOutput<'a>
|
||||||
|
) -> Perhaps<Rect> {
|
||||||
let track_cols = track_clip_name_lengths(state.tracks.as_slice());
|
let track_cols = track_clip_name_lengths(state.tracks.as_slice());
|
||||||
let scene_rows = (0..=state.scenes.len()).map(|i|(96, 96*i)).collect::<Vec<_>>();
|
let scene_rows = (0..=state.scenes.len()).map(|i|(96, 96*i)).collect::<Vec<_>>();
|
||||||
draw(state, buf, area, track_cols.as_slice(), scene_rows.as_slice())
|
draw(state, to, track_cols.as_slice(), scene_rows.as_slice())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw arranger with 2 rows per scene.
|
/// Draw arranger with 2 rows per scene.
|
||||||
pub fn draw_compact_2 (state: &Arranger, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
pub fn draw_compact_2 <'a> (
|
||||||
|
state: &Arranger<TuiOutput<'a>, Rect>, to: &mut TuiOutput<'a>
|
||||||
|
) -> Perhaps<Rect> {
|
||||||
let track_cols = track_clip_name_lengths(state.tracks.as_slice());
|
let track_cols = track_clip_name_lengths(state.tracks.as_slice());
|
||||||
let scene_rows = (0..=state.scenes.len()).map(|i|(192, 192*i)).collect::<Vec<_>>();
|
let scene_rows = (0..=state.scenes.len()).map(|i|(192, 192*i)).collect::<Vec<_>>();
|
||||||
draw(state, buf, area, track_cols.as_slice(), scene_rows.as_slice())
|
draw(state, to, track_cols.as_slice(), scene_rows.as_slice())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw arranger with number of rows per scene proportional to duration of scene.
|
/// Draw arranger with number of rows per scene proportional to duration of scene.
|
||||||
pub fn draw_expanded (state: &Arranger, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
pub fn draw_expanded <'a> (
|
||||||
|
state: &Arranger<TuiOutput<'a>, Rect>, to: &mut TuiOutput<'a>
|
||||||
|
) -> Perhaps<Rect> {
|
||||||
let track_cols = track_clip_name_lengths(state.tracks.as_slice());
|
let track_cols = track_clip_name_lengths(state.tracks.as_slice());
|
||||||
let scene_rows = scene_ppqs(state.tracks.as_slice(), state.scenes.as_slice());
|
let scene_rows = scene_ppqs(state.tracks.as_slice(), state.scenes.as_slice());
|
||||||
draw(state, buf, area, track_cols.as_slice(), scene_rows.as_slice())
|
draw(state, to, track_cols.as_slice(), scene_rows.as_slice())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw (
|
pub fn draw <'a, 'b> (
|
||||||
state: &Arranger,
|
state: &Arranger<TuiOutput<'a>, Rect>,
|
||||||
buf: &mut Buffer,
|
to: &mut TuiOutput<'a>,
|
||||||
mut area: Rect,
|
cols: &'b [(usize, usize)],
|
||||||
cols: &[(usize, usize)],
|
rows: &'b [(usize, usize)],
|
||||||
rows: &[(usize, usize)],
|
) -> Perhaps<Rect> {
|
||||||
) -> Usually<Rect> {
|
let mut area = to.area;
|
||||||
area.height = 2 + (rows[rows.len() - 1].1 / 96) as u16;
|
area.height = 2 + (rows[rows.len() - 1].1 / 96) as u16;
|
||||||
let offset = 3 + scene_name_max_len(state.scenes.as_ref()) as u16;
|
let offset = 3 + scene_name_max_len(state.scenes.as_ref()) as u16;
|
||||||
let tracks = state.tracks.as_ref();
|
let tracks = state.tracks.as_ref();
|
||||||
|
|
@ -40,42 +46,44 @@ pub fn draw (
|
||||||
.add_ref(&Split::down()
|
.add_ref(&Split::down()
|
||||||
.add_ref(&TracksHeader(offset, cols, tracks))
|
.add_ref(&TracksHeader(offset, cols, tracks))
|
||||||
.add_ref(&SceneRows(offset, cols, rows, tracks, scenes)))
|
.add_ref(&SceneRows(offset, cols, rows, tracks, scenes)))
|
||||||
.render(buf, area)
|
.render(to)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ColumnSeparators<'a>(u16, &'a [(usize, usize)]);
|
struct ColumnSeparators<'a>(u16, &'a [(usize, usize)]);
|
||||||
|
|
||||||
impl<'a> Render for ColumnSeparators<'a> {
|
impl<'a> Render<TuiOutput<'a>, Rect> for ColumnSeparators<'a> {
|
||||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
|
let area = to.area;
|
||||||
let Self(offset, cols) = self;
|
let Self(offset, cols) = self;
|
||||||
let style = Some(Style::default().fg(Nord::SEPARATOR));
|
let style = Some(Style::default().fg(Nord::SEPARATOR));
|
||||||
for (_, x) in cols.iter() {
|
for (_, x) in cols.iter() {
|
||||||
let x = offset + area.x + *x as u16 - 1;
|
let x = offset + area.x + *x as u16 - 1;
|
||||||
for y in area.y..area.height+area.y {
|
for y in area.y..area.height+area.y {
|
||||||
"▎".blit(buf, x, y, style)?;
|
"▎".blit(to.buffer, x, y, style)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(area)
|
Ok(Some(area))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RowSeparators<'a>(&'a [(usize, usize)]);
|
struct RowSeparators<'a>(&'a [(usize, usize)]);
|
||||||
|
|
||||||
impl<'a> Render for RowSeparators<'a> {
|
impl<'a> Render<TuiOutput<'a>, Rect> for RowSeparators<'a> {
|
||||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
|
let mut area = to.area;
|
||||||
let Self(rows) = self;
|
let Self(rows) = self;
|
||||||
for (_, y) in rows.iter() {
|
for (_, y) in rows.iter() {
|
||||||
let y = area.y + (*y / 96) as u16 + 1;
|
let y = area.y + (*y / 96) as u16 + 1;
|
||||||
if y >= buf.area.height {
|
if y >= to.buffer.area.height {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
for x in area.x..area.width+area.y-2 {
|
for x in area.x..area.width+area.y-2 {
|
||||||
let cell = buf.get_mut(x, y);
|
let cell = to.buffer.get_mut(x, y);
|
||||||
cell.modifier = Modifier::UNDERLINED;
|
cell.modifier = Modifier::UNDERLINED;
|
||||||
cell.underline_color = Nord::SEPARATOR;
|
cell.underline_color = Nord::SEPARATOR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(area)
|
Ok(Some(area))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -83,8 +91,9 @@ struct CursorFocus<'a>(
|
||||||
ArrangerFocus, u16, &'a [(usize, usize)], &'a [(usize, usize)]
|
ArrangerFocus, u16, &'a [(usize, usize)], &'a [(usize, usize)]
|
||||||
);
|
);
|
||||||
|
|
||||||
impl<'a> Render for CursorFocus<'a> {
|
impl<'a> Render<TuiOutput<'a>, Rect> for CursorFocus<'a> {
|
||||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
|
let mut area = to.area;
|
||||||
let Self(selected, offset, cols, rows) = *self;
|
let Self(selected, offset, cols, rows) = *self;
|
||||||
let get_track_area = |t: usize| Rect {
|
let get_track_area = |t: usize| Rect {
|
||||||
x: offset + area.x + cols[t].1 as u16 - 1,
|
x: offset + area.x + cols[t].1 as u16 - 1,
|
||||||
|
|
@ -109,7 +118,7 @@ impl<'a> Render for CursorFocus<'a> {
|
||||||
let mut clip_area: Option<Rect> = None;
|
let mut clip_area: Option<Rect> = None;
|
||||||
let area = match selected {
|
let area = match selected {
|
||||||
ArrangerFocus::Mix => {
|
ArrangerFocus::Mix => {
|
||||||
fill_bg(buf, area, COLOR_BG0);
|
fill_bg(to.buffer, area, COLOR_BG0);
|
||||||
area
|
area
|
||||||
},
|
},
|
||||||
ArrangerFocus::Track(t) => {
|
ArrangerFocus::Track(t) => {
|
||||||
|
|
@ -128,19 +137,19 @@ impl<'a> Render for CursorFocus<'a> {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
if let Some(Rect { x, y, width, height }) = track_area {
|
if let Some(Rect { x, y, width, height }) = track_area {
|
||||||
fill_fg(buf, Rect { x, y, width: 1, height }, COLOR_BG5);
|
fill_fg(to.buffer, Rect { x, y, width: 1, height }, COLOR_BG5);
|
||||||
fill_fg(buf, Rect { x: x + width, y, width: 1, height }, COLOR_BG5);
|
fill_fg(to.buffer, Rect { x: x + width, y, width: 1, height }, COLOR_BG5);
|
||||||
}
|
}
|
||||||
if let Some(Rect { y, height, .. }) = scene_area {
|
if let Some(Rect { y, height, .. }) = scene_area {
|
||||||
fill_ul(buf, Rect { x: area.x, y: y - 1, width: area.width, height: 1 }, COLOR_BG5);
|
fill_ul(to.buffer, Rect { x: area.x, y: y - 1, width: area.width, height: 1 }, COLOR_BG5);
|
||||||
fill_ul(buf, Rect { x: area.x, y: y + height - 1, width: area.width, height: 1 }, COLOR_BG5);
|
fill_ul(to.buffer, Rect { x: area.x, y: y + height - 1, width: area.width, height: 1 }, COLOR_BG5);
|
||||||
}
|
}
|
||||||
if let Some(clip_area) = clip_area {
|
if let Some(clip_area) = clip_area {
|
||||||
fill_bg(buf, clip_area, COLOR_BG0);
|
fill_bg(to.buffer, clip_area, COLOR_BG0);
|
||||||
} else if let Some(track_area) = track_area {
|
} else if let Some(track_area) = track_area {
|
||||||
fill_bg(buf, track_area, COLOR_BG0);
|
fill_bg(to.buffer, track_area, COLOR_BG0);
|
||||||
} else if let Some(scene_area) = scene_area {
|
} else if let Some(scene_area) = scene_area {
|
||||||
fill_bg(buf, scene_area, COLOR_BG0);
|
fill_bg(to.buffer, scene_area, COLOR_BG0);
|
||||||
}
|
}
|
||||||
Ok(area)
|
Ok(area)
|
||||||
}
|
}
|
||||||
|
|
@ -148,8 +157,9 @@ impl<'a> Render for CursorFocus<'a> {
|
||||||
|
|
||||||
struct TracksHeader<'a>(u16, &'a[(usize, usize)], &'a [Sequencer]);
|
struct TracksHeader<'a>(u16, &'a[(usize, usize)], &'a [Sequencer]);
|
||||||
|
|
||||||
impl<'a> Render for TracksHeader<'a> {
|
impl<'a> Render<TuiOutput<'a>, Rect> for TracksHeader<'a> {
|
||||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
|
let mut area = to.area;
|
||||||
let Self(offset, track_cols, tracks) = *self;
|
let Self(offset, track_cols, tracks) = *self;
|
||||||
let Rect { y, width, .. } = area;
|
let Rect { y, width, .. } = area;
|
||||||
for (track, (w, x)) in tracks.iter().zip(track_cols) {
|
for (track, (w, x)) in tracks.iter().zip(track_cols) {
|
||||||
|
|
@ -158,17 +168,18 @@ impl<'a> Render for TracksHeader<'a> {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
let name = track.name.read().unwrap();
|
let name = track.name.read().unwrap();
|
||||||
fill_bg(buf, Rect { x: offset + x, y, width: *w as u16, height: 2 }, COLOR_BG1);
|
fill_bg(to.buffer, Rect { x: offset + x, y, width: *w as u16, height: 2 }, COLOR_BG1);
|
||||||
name.blit(buf, offset + x + 1, y, Some(Style::default().white()))?;
|
name.blit(to.buffer, offset + x + 1, y, Some(Style::default().white()))?;
|
||||||
}
|
}
|
||||||
Ok(Rect { x: area.x, y, width, height: 2 })
|
Ok(Some(Rect { x: area.x, y, width, height: 2 }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SceneRows<'a>(u16, &'a[(usize, usize)], &'a[(usize, usize)], &'a[Sequencer], &'a[Scene]);
|
struct SceneRows<'a>(u16, &'a[(usize, usize)], &'a[(usize, usize)], &'a[Sequencer], &'a[Scene]);
|
||||||
|
|
||||||
impl<'a> Render for SceneRows<'a> {
|
impl<'a> Render<TuiOutput<'a>, Rect> for SceneRows<'a> {
|
||||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
|
let area = to.area;
|
||||||
let Self(offset, track_cols, scene_rows, tracks, scenes) = *self;
|
let Self(offset, track_cols, scene_rows, tracks, scenes) = *self;
|
||||||
let black = Some(Style::default().fg(Nord::SEPARATOR));
|
let black = Some(Style::default().fg(Nord::SEPARATOR));
|
||||||
let Rect { mut y, height, .. } = area;
|
let Rect { mut y, height, .. } = area;
|
||||||
|
|
@ -176,7 +187,7 @@ impl<'a> Render for SceneRows<'a> {
|
||||||
let x = *x as u16;
|
let x = *x as u16;
|
||||||
if x > 0 {
|
if x > 0 {
|
||||||
for y in area.y-2..y-2 {
|
for y in area.y-2..y-2 {
|
||||||
"▎".blit(buf, x - 1, y, black)?;
|
"▎".blit(to.buffer, x - 1, y, black)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -185,28 +196,27 @@ impl<'a> Render for SceneRows<'a> {
|
||||||
//break
|
//break
|
||||||
//}
|
//}
|
||||||
let h = 1.max((pulses / 96) as u16);
|
let h = 1.max((pulses / 96) as u16);
|
||||||
SceneRow(tracks, scene, track_cols, offset).render(buf, Rect {
|
SceneRow(tracks, scene, track_cols, offset).render(&mut TuiOutput {
|
||||||
x: area.x,
|
buffer: to.buffer,
|
||||||
y,
|
area: Rect { x: area.x, y, width: area.width, height: h, }
|
||||||
width: area.width,
|
|
||||||
height: h,//.min(area.height - y)
|
|
||||||
})?;
|
})?;
|
||||||
y = y + h
|
y = y + h
|
||||||
}
|
}
|
||||||
Ok(area)
|
Ok(Some(area))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SceneRow<'a>(&'a[Sequencer], &'a Scene, &'a[(usize, usize)], u16);
|
struct SceneRow<'a>(&'a[Sequencer], &'a Scene, &'a[(usize, usize)], u16);
|
||||||
|
|
||||||
impl<'a> Render for SceneRow<'a> {
|
impl<'a> Render<TuiOutput<'a>, Rect> for SceneRow<'a> {
|
||||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
|
let mut area = to.area;
|
||||||
let Self(tracks, scene, track_cols, offset) = self;
|
let Self(tracks, scene, track_cols, offset) = self;
|
||||||
let Rect { x, y, width, .. } = area;
|
let Rect { x, y, width, .. } = area;
|
||||||
let playing = scene.is_playing(tracks);
|
let playing = scene.is_playing(tracks);
|
||||||
(if playing { "▶" } else { " " }).blit(buf, x, y, None)?;
|
(if playing { "▶" } else { " " }).blit(to.buffer, x, y, None)?;
|
||||||
scene.name.read().unwrap().blit(buf, x + 1, y, Some(Style::default().white()))?;
|
scene.name.read().unwrap().blit(to.buffer, x + 1, y, Some(Style::default().white()))?;
|
||||||
fill_bg(buf, Rect { x: x, y, width: offset.saturating_sub(1), height: area.height }, COLOR_BG1);
|
fill_bg(to.buffer, Rect { x: x, y, width: offset.saturating_sub(1), height: area.height }, COLOR_BG1);
|
||||||
|
|
||||||
for (track, (w, x)) in track_cols.iter().enumerate() {
|
for (track, (w, x)) in track_cols.iter().enumerate() {
|
||||||
let x = *x as u16 + offset;
|
let x = *x as u16 + offset;
|
||||||
|
|
@ -217,33 +227,36 @@ impl<'a> Render for SceneRow<'a> {
|
||||||
if let (Some(track), Some(Some(clip))) = (
|
if let (Some(track), Some(Some(clip))) = (
|
||||||
tracks.get(track), scene.clips.get(track)
|
tracks.get(track), scene.clips.get(track)
|
||||||
) {
|
) {
|
||||||
let area = Rect { x, y, width: *w as u16, height: area.height, };
|
SceneClip(track, *clip).render(&mut TuiOutput {
|
||||||
SceneClip(track, *clip).render(buf, area)?;
|
buffer: to.buffer,
|
||||||
|
area: Rect { x, y, width: *w as u16, height: area.height, }
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(area)
|
Ok(Some(area))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SceneClip<'a>(&'a Sequencer, usize);
|
struct SceneClip<'a>(&'a Sequencer, usize);
|
||||||
|
|
||||||
impl<'a> Render for SceneClip<'a> {
|
impl<'a> Render<TuiOutput<'a>, Rect> for SceneClip<'a> {
|
||||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
|
let area = to.area;
|
||||||
let Self(track, clip) = self;
|
let Self(track, clip) = self;
|
||||||
let style = Some(Style::default().white());
|
let style = Some(Style::default().white());
|
||||||
if let Some(phrase) = track.phrases.get(*clip) {
|
if let Some(phrase) = track.phrases.get(*clip) {
|
||||||
let phrase = phrase.read().unwrap();
|
let phrase = phrase.read().unwrap();
|
||||||
let name = phrase.name.read().unwrap();
|
let name = phrase.name.read().unwrap();
|
||||||
format!("{clip:02} {name}").blit(buf, area.x + 1, area.y, style)?;
|
format!("{clip:02} {name}").blit(to.buffer, area.x + 1, area.y, style)?;
|
||||||
fill_bg(buf, area, if track.sequence == Some(*clip) {
|
fill_bg(to.buffer, area, if track.sequence == Some(*clip) {
|
||||||
Nord::PLAYING
|
Nord::PLAYING
|
||||||
} else {
|
} else {
|
||||||
COLOR_BG1
|
COLOR_BG1
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
fill_bg(buf, area, COLOR_BG0)
|
fill_bg(to.buffer, area, COLOR_BG0)
|
||||||
}
|
}
|
||||||
Ok(area)
|
Ok(Some(area))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ submod! {
|
||||||
arranger_focus
|
arranger_focus
|
||||||
arranger_handle
|
arranger_handle
|
||||||
arranger_track
|
arranger_track
|
||||||
arranger_view
|
|
||||||
arranger_rename
|
arranger_rename
|
||||||
midi
|
midi
|
||||||
phrase
|
phrase
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ impl Phrase {
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Arranger {
|
impl<T, U> Arranger<T, U> {
|
||||||
pub fn phrase (&self) -> Option<&Arc<RwLock<Phrase>>> {
|
pub fn phrase (&self) -> Option<&Arc<RwLock<Phrase>>> {
|
||||||
let track_id = self.selected.track()?;
|
let track_id = self.selected.track()?;
|
||||||
self.tracks.get(track_id)?.phrases.get((*self.scene()?.clips.get(track_id)?)?)
|
self.tracks.get(track_id)?.phrases.get((*self.scene()?.clips.get(track_id)?)?)
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ pub fn scene_ppqs (tracks: &[Sequencer], scenes: &[Scene]) -> Vec<(usize, usize)
|
||||||
scenes
|
scenes
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Arranger {
|
impl<T, U> Arranger<T, U> {
|
||||||
pub fn scene (&self) -> Option<&Scene> {
|
pub fn scene (&self) -> Option<&Scene> {
|
||||||
self.selected.scene().map(|s|self.scenes.get(s)).flatten()
|
self.selected.scene().map(|s|self.scenes.get(s)).flatten()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
impl<'a> Render<TuiOutput<'a>, Rect> for Sequencer {
|
impl<'a> Render<TuiOutput<'a>, Rect> for Sequencer {
|
||||||
fn render (&self, target: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
self.horizontal_draw(target)?;
|
self.horizontal_draw(to)?;
|
||||||
if self.focused && self.entered {
|
if self.focused && self.entered {
|
||||||
Corners(Style::default().green().not_dim()).draw(buf, area)?;
|
Corners(Style::default().green().not_dim()).draw(to.buffer, to.area)?;
|
||||||
}
|
}
|
||||||
Ok(area)
|
Ok(Some(to.area))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,17 +4,26 @@ impl Sequencer {
|
||||||
|
|
||||||
const H_KEYS_OFFSET: usize = 5;
|
const H_KEYS_OFFSET: usize = 5;
|
||||||
|
|
||||||
pub(crate) fn horizontal_draw (&self, buf: &mut Buffer, mut area: Rect) -> Usually<()> {
|
pub(crate) fn horizontal_draw <'a> (&self, to: &mut TuiOutput<'a>) -> Usually<()> {
|
||||||
|
let mut area = to.area;
|
||||||
Split::down()
|
Split::down()
|
||||||
.add_ref(&SequenceName(&self))
|
.add_ref(&SequenceName(&self))
|
||||||
.add_ref(&SequenceRange)
|
.add_ref(&SequenceRange)
|
||||||
.add_ref(&SequenceLoopRange)
|
.add_ref(&SequenceLoopRange)
|
||||||
.add_ref(&SequenceNoteRange)
|
.add_ref(&SequenceNoteRange)
|
||||||
.render(buf, Rect { x: area.x, y: area.y, width: 10, height: area.height })?;
|
.render(&mut TuiOutput {
|
||||||
|
buffer: to.buffer,
|
||||||
|
area: Rect {
|
||||||
|
x: area.x,
|
||||||
|
y: area.y,
|
||||||
|
height: area.height,
|
||||||
|
width: 10,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
area.x = area.x + 10;
|
area.x = area.x + 10;
|
||||||
area.width = area.width.saturating_sub(10);
|
area.width = area.width.saturating_sub(10);
|
||||||
area.height = area.height.min(66);
|
area.height = area.height.min(66);
|
||||||
Lozenge(Style::default().fg(Nord::BG2)).draw(buf, area)?;
|
Lozenge(Style::default().fg(Nord::BG2)).draw(to.buffer, area)?;
|
||||||
area.x = area.x + 1;
|
area.x = area.x + 1;
|
||||||
area.width = area.width.saturating_sub(1);
|
area.width = area.width.saturating_sub(1);
|
||||||
Layered::new()
|
Layered::new()
|
||||||
|
|
@ -23,7 +32,7 @@ impl Sequencer {
|
||||||
.add_ref(&SequenceNotes(&self))
|
.add_ref(&SequenceNotes(&self))
|
||||||
.add_ref(&SequenceCursor(&self))
|
.add_ref(&SequenceCursor(&self))
|
||||||
.add_ref(&SequenceZoom(&self))
|
.add_ref(&SequenceZoom(&self))
|
||||||
.render(buf, area)?;
|
.render(&mut TuiOutput { buffer: to.buffer, area: area })?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -48,102 +57,102 @@ const STYLE_VALUE: Option<Style> = Some(Style {
|
||||||
struct SequenceName<'a>(&'a Sequencer);
|
struct SequenceName<'a>(&'a Sequencer);
|
||||||
|
|
||||||
impl<'a> Render<TuiOutput<'a>, Rect> for SequenceName<'a> {
|
impl<'a> Render<TuiOutput<'a>, Rect> for SequenceName<'a> {
|
||||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
let Rect { x, y, .. } = area;
|
let Rect { x, y, .. } = to.area;
|
||||||
let frame = Rect { x, y, width: 10, height: 4 };
|
let frame = Rect { x, y, width: 10, height: 4 };
|
||||||
Lozenge(Style::default().fg(Nord::BG2)).draw(buf, frame)?;
|
Lozenge(Style::default().fg(Nord::BG2)).draw(to.buffer, frame)?;
|
||||||
"Name:".blit(buf, x + 1, y + 1, STYLE_LABEL)?;
|
"Name:".blit(to.buffer, x + 1, y + 1, STYLE_LABEL)?;
|
||||||
self.0.name.read().unwrap().blit(buf, x + 1, y + 2, STYLE_VALUE)?;
|
self.0.name.read().unwrap().blit(to.buffer, x + 1, y + 2, STYLE_VALUE)?;
|
||||||
Ok(frame)
|
Ok(Some(frame))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SequenceRange;
|
struct SequenceRange;
|
||||||
|
|
||||||
impl<'a> Render<TuiOutput<'a>, Rect> for SequenceRange {
|
impl<'a> Render<TuiOutput<'a>, Rect> for SequenceRange {
|
||||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
let Rect { x, y, .. } = area;
|
let Rect { x, y, .. } = to.area;
|
||||||
let frame = Rect { x, y, width: 10, height: 6 };
|
let frame = Rect { x, y, width: 10, height: 6 };
|
||||||
Lozenge(Style::default().fg(Nord::BG2)).draw(buf, frame)?;
|
Lozenge(Style::default().fg(Nord::BG2)).draw(to.buffer, frame)?;
|
||||||
"Start: ".blit(buf, x + 1, y + 1, STYLE_LABEL)?;
|
"Start: ".blit(to.buffer, x + 1, y + 1, STYLE_LABEL)?;
|
||||||
" 1.1.1".blit(buf, x + 1, y + 2, STYLE_VALUE)?;
|
" 1.1.1".blit(to.buffer, x + 1, y + 2, STYLE_VALUE)?;
|
||||||
"End: ".blit(buf, x + 1, y + 3, STYLE_LABEL)?;
|
"End: ".blit(to.buffer, x + 1, y + 3, STYLE_LABEL)?;
|
||||||
" 2.1.1".blit(buf, x + 1, y + 4, STYLE_VALUE)?;
|
" 2.1.1".blit(to.buffer, x + 1, y + 4, STYLE_VALUE)?;
|
||||||
Ok(frame)
|
Ok(Some(frame))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SequenceLoopRange;
|
struct SequenceLoopRange;
|
||||||
|
|
||||||
impl<'a> Render<TuiOutput<'a>, Rect> for SequenceLoopRange {
|
impl<'a> Render<TuiOutput<'a>, Rect> for SequenceLoopRange {
|
||||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
let Rect { x, y, .. } = area;
|
let Rect { x, y, .. } = to.area;
|
||||||
let range = Rect { x, y, width: 10, height: 7 };
|
let range = Rect { x, y, width: 10, height: 7 };
|
||||||
Lozenge(Style::default().fg(Nord::BG2)).draw(buf, range)?;
|
Lozenge(Style::default().fg(Nord::BG2)).draw(to.buffer, range)?;
|
||||||
"Loop [ ]".blit(buf, x + 1, y + 1, STYLE_LABEL)?;
|
"Loop [ ]".blit(to.buffer, x + 1, y + 1, STYLE_LABEL)?;
|
||||||
"From: ".blit(buf, x + 1, y + 2, STYLE_LABEL)?;
|
"From: ".blit(to.buffer, x + 1, y + 2, STYLE_LABEL)?;
|
||||||
" 1.1.1".blit(buf, x + 1, y + 3, STYLE_VALUE)?;
|
" 1.1.1".blit(to.buffer, x + 1, y + 3, STYLE_VALUE)?;
|
||||||
"Length: ".blit(buf, x + 1, y + 4, STYLE_LABEL)?;
|
"Length: ".blit(to.buffer, x + 1, y + 4, STYLE_LABEL)?;
|
||||||
" 1.0.0".blit(buf, x + 1, y + 5, STYLE_VALUE)?;
|
" 1.0.0".blit(to.buffer, x + 1, y + 5, STYLE_VALUE)?;
|
||||||
Ok(range)
|
Ok(Some(range))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SequenceNoteRange;
|
struct SequenceNoteRange;
|
||||||
|
|
||||||
impl<'a> Render<TuiOutput<'a>, Rect> for SequenceNoteRange {
|
impl<'a> Render<TuiOutput<'a>, Rect> for SequenceNoteRange {
|
||||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
let Rect { x, y, .. } = area;
|
let Rect { x, y, .. } = to.area;
|
||||||
let range = Rect { x, y, width: 10, height: 9 };
|
let range = Rect { x, y, width: 10, height: 9 };
|
||||||
Lozenge(Style::default().fg(Nord::BG2)).draw(buf, range)?;
|
Lozenge(Style::default().fg(Nord::BG2)).draw(to.buffer, range)?;
|
||||||
"Notes: ".blit(buf, x + 1, y + 1, STYLE_LABEL)?;
|
"Notes: ".blit(to.buffer, x + 1, y + 1, STYLE_LABEL)?;
|
||||||
"C#0-C#9 ".blit(buf, x + 1, y + 2, STYLE_VALUE)?;
|
"C#0-C#9 ".blit(to.buffer, x + 1, y + 2, STYLE_VALUE)?;
|
||||||
"[ /2 ]".blit(buf, x + 1, y + 3, STYLE_LABEL)?;
|
"[ /2 ]".blit(to.buffer, x + 1, y + 3, STYLE_LABEL)?;
|
||||||
"[ x2 ]".blit(buf, x + 1, y + 4, STYLE_LABEL)?;
|
"[ x2 ]".blit(to.buffer, x + 1, y + 4, STYLE_LABEL)?;
|
||||||
"[ Rev ]".blit(buf, x + 1, y + 5, STYLE_LABEL)?;
|
"[ Rev ]".blit(to.buffer, x + 1, y + 5, STYLE_LABEL)?;
|
||||||
"[ Inv ]".blit(buf, x + 1, y + 6, STYLE_LABEL)?;
|
"[ Inv ]".blit(to.buffer, x + 1, y + 6, STYLE_LABEL)?;
|
||||||
"[ Dup ]".blit(buf, x + 1, y + 7, STYLE_LABEL)?;
|
"[ Dup ]".blit(to.buffer, x + 1, y + 7, STYLE_LABEL)?;
|
||||||
Ok(area)
|
Ok(Some(to.area))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SequenceKeys<'a>(&'a Sequencer);
|
struct SequenceKeys<'a>(&'a Sequencer);
|
||||||
|
|
||||||
impl<'a> Render<TuiOutput<'a>, Rect> for SequenceKeys<'a> {
|
impl<'a> Render<TuiOutput<'a>, Rect> for SequenceKeys<'a> {
|
||||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
if area.height < 2 {
|
if to.area.height < 2 {
|
||||||
return Ok(area)
|
return Ok(Some(to.area))
|
||||||
}
|
}
|
||||||
let area = Rect {
|
let area = Rect {
|
||||||
x: area.x,
|
x: to.area.x,
|
||||||
y: area.y + 1,
|
y: to.area.y + 1,
|
||||||
width: 5,
|
width: 5,
|
||||||
height: area.height - 2
|
height: to.area.height - 2
|
||||||
};
|
};
|
||||||
buffer_update(buf, area, &|cell, x, y|{
|
buffer_update(to.buffer, area, &|cell, x, y|{
|
||||||
let y = y + self.0.note_axis.start as u16;
|
let y = y + self.0.note_axis.start as u16;
|
||||||
if x < self.0.keys.area.width && y < self.0.keys.area.height {
|
if x < self.0.keys.area.width && y < self.0.keys.area.height {
|
||||||
*cell = self.0.keys.get(x, y).clone()
|
*cell = self.0.keys.get(x, y).clone()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Ok(area)
|
Ok(Some(to.area))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SequenceNotes<'a>(&'a Sequencer);
|
struct SequenceNotes<'a>(&'a Sequencer);
|
||||||
|
|
||||||
impl<'a> Render<TuiOutput<'a>, Rect> for SequenceNotes<'a> {
|
impl<'a> Render<TuiOutput<'a>, Rect> for SequenceNotes<'a> {
|
||||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
if area.height < 2 {
|
if to.area.height < 2 {
|
||||||
return Ok(area)
|
return Ok(Some(to.area))
|
||||||
}
|
}
|
||||||
let area = Rect {
|
let area = Rect {
|
||||||
x: area.x + Sequencer::H_KEYS_OFFSET as u16,
|
x: to.area.x + Sequencer::H_KEYS_OFFSET as u16,
|
||||||
y: area.y + 1,
|
y: to.area.y + 1,
|
||||||
width: area.width - Sequencer::H_KEYS_OFFSET as u16,
|
width: to.area.width - Sequencer::H_KEYS_OFFSET as u16,
|
||||||
height: area.height - 2
|
height: to.area.height - 2
|
||||||
};
|
};
|
||||||
buffer_update(buf, area, &move |cell, x, y|{
|
buffer_update(to.buffer, area, &move |cell, x, y|{
|
||||||
let src_x = ((x as usize + self.0.time_axis.start) * self.0.time_axis.scale) as usize;
|
let src_x = ((x as usize + self.0.time_axis.start) * self.0.time_axis.scale) as usize;
|
||||||
let src_y = (y as usize + self.0.note_axis.start) as usize;
|
let src_y = (y as usize + self.0.note_axis.start) as usize;
|
||||||
if src_x < self.0.buffer.width && src_y < self.0.buffer.height - 1 {
|
if src_x < self.0.buffer.width && src_y < self.0.buffer.height - 1 {
|
||||||
|
|
@ -154,21 +163,21 @@ impl<'a> Render<TuiOutput<'a>, Rect> for SequenceNotes<'a> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Ok(area)
|
Ok(Some(to.area))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SequenceCursor<'a>(&'a Sequencer);
|
struct SequenceCursor<'a>(&'a Sequencer);
|
||||||
|
|
||||||
impl<'a> Render<TuiOutput<'a>, Rect> for SequenceCursor<'a> {
|
impl<'a> Render<TuiOutput<'a>, Rect> for SequenceCursor<'a> {
|
||||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
if let (Some(time), Some(note)) = (self.0.time_axis.point, self.0.note_axis.point) {
|
if let (Some(time), Some(note)) = (self.0.time_axis.point, self.0.note_axis.point) {
|
||||||
let x = area.x + Sequencer::H_KEYS_OFFSET as u16 + time as u16;
|
let x = to.area.x + Sequencer::H_KEYS_OFFSET as u16 + time as u16;
|
||||||
let y = area.y + 1 + note as u16 / 2;
|
let y = to.area.y + 1 + note as u16 / 2;
|
||||||
let c = if note % 2 == 0 { "▀" } else { "▄" };
|
let c = if note % 2 == 0 { "▀" } else { "▄" };
|
||||||
c.blit(buf, x, y, self.0.style_focus())
|
c.blit(to.buffer, x, y, self.0.style_focus())
|
||||||
} else {
|
} else {
|
||||||
Ok(Rect::default())
|
Ok(Some(Rect::default()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -176,31 +185,31 @@ impl<'a> Render<TuiOutput<'a>, Rect> for SequenceCursor<'a> {
|
||||||
struct SequenceZoom<'a>(&'a Sequencer);
|
struct SequenceZoom<'a>(&'a Sequencer);
|
||||||
|
|
||||||
impl<'a> Render<TuiOutput<'a>, Rect> for SequenceZoom<'a> {
|
impl<'a> Render<TuiOutput<'a>, Rect> for SequenceZoom<'a> {
|
||||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
let quant = ppq_to_name(self.0.time_axis.scale);
|
let quant = ppq_to_name(self.0.time_axis.scale);
|
||||||
let quant_x = area.x + area.width - 1 - quant.len() as u16;
|
let quant_x = to.area.x + to.area.width - 1 - quant.len() as u16;
|
||||||
let quant_y = area.y + area.height - 2;
|
let quant_y = to.area.y + to.area.height - 2;
|
||||||
quant.blit(buf, quant_x, quant_y, self.0.style_focus())
|
quant.blit(to.buffer, quant_x, quant_y, self.0.style_focus())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SequenceTimer<'a>(&'a Sequencer, Arc<RwLock<Phrase>>);
|
struct SequenceTimer<'a>(&'a Sequencer, Arc<RwLock<Phrase>>);
|
||||||
|
|
||||||
impl<'a> Render<TuiOutput<'a>, Rect> for SequenceTimer<'a> {
|
impl<'a> Render<TuiOutput<'a>, Rect> for SequenceTimer<'a> {
|
||||||
fn render (&self, to: &mut TuiOutput<'a>) -> Usually<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
let phrase = self.1.read().unwrap();
|
let phrase = self.1.read().unwrap();
|
||||||
let (time0, time_z, now) = (
|
let (time0, time_z, now) = (
|
||||||
self.0.time_axis.start, self.0.time_axis.scale, self.0.now % phrase.length
|
self.0.time_axis.start, self.0.time_axis.scale, self.0.now % phrase.length
|
||||||
);
|
);
|
||||||
let Rect { x, width, .. } = area;
|
let Rect { x, width, .. } = to.area;
|
||||||
let x2 = x as usize + Sequencer::H_KEYS_OFFSET;
|
let x2 = x as usize + Sequencer::H_KEYS_OFFSET;
|
||||||
let x3 = x as usize + width as usize;
|
let x3 = x as usize + width as usize;
|
||||||
for x in x2..x3 {
|
for x in x2..x3 {
|
||||||
let step = (time0 + x2) * time_z;
|
let step = (time0 + x2) * time_z;
|
||||||
let next_step = (time0 + x2 + 1) * time_z;
|
let next_step = (time0 + x2 + 1) * time_z;
|
||||||
let style = Sequencer::style_timer_step(now, step as usize, next_step as usize);
|
let style = Sequencer::style_timer_step(now, step as usize, next_step as usize);
|
||||||
"-".blit(buf, x as u16, area.y, Some(style))?;
|
"-".blit(to.buffer, x as u16, to.area.y, Some(style))?;
|
||||||
}
|
}
|
||||||
return Ok(Rect { x: area.x, y: area.y, width: area.width, height: 1 })
|
return Ok(Some(Rect { x: to.area.x, y: to.area.y, width: to.area.width, height: 1 }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,52 +22,7 @@ pub struct TransportToolbar {
|
||||||
pub sync: TransportSync,
|
pub sync: TransportSync,
|
||||||
pub clock: TransportClock,
|
pub clock: TransportClock,
|
||||||
}
|
}
|
||||||
|
|
||||||
focusable!(TransportToolbar);
|
|
||||||
|
|
||||||
focus!(TransportToolbar (focus) : 5 => [
|
|
||||||
playing, bpm, quant, sync, clock
|
|
||||||
]);
|
|
||||||
process!(TransportToolbar |self, _client, scope| {
|
|
||||||
self.update(&scope);
|
|
||||||
Control::Continue
|
|
||||||
});
|
|
||||||
|
|
||||||
pub struct TransportPlayPauseButton {
|
|
||||||
pub value: Option<TransportState>,
|
|
||||||
pub focused: bool
|
|
||||||
}
|
|
||||||
focusable!(TransportPlayPauseButton);
|
|
||||||
|
|
||||||
pub struct TransportBPM {
|
|
||||||
pub value: f64,
|
|
||||||
pub focused: bool
|
|
||||||
}
|
|
||||||
focusable!(TransportBPM);
|
|
||||||
|
|
||||||
pub struct TransportQuantize {
|
|
||||||
pub value: usize,
|
|
||||||
pub focused: bool
|
|
||||||
}
|
|
||||||
focusable!(TransportQuantize);
|
|
||||||
|
|
||||||
pub struct TransportSync {
|
|
||||||
pub value: usize,
|
|
||||||
pub focused: bool
|
|
||||||
}
|
|
||||||
focusable!(TransportSync);
|
|
||||||
|
|
||||||
pub struct TransportClock {
|
|
||||||
pub frame: usize,
|
|
||||||
pub pulse: usize,
|
|
||||||
pub ppq: usize,
|
|
||||||
pub usecs: usize,
|
|
||||||
pub focused: bool,
|
|
||||||
}
|
|
||||||
focusable!(TransportClock);
|
|
||||||
|
|
||||||
impl TransportToolbar {
|
impl TransportToolbar {
|
||||||
|
|
||||||
pub fn standalone () -> Usually<Arc<RwLock<Self>>> {
|
pub fn standalone () -> Usually<Arc<RwLock<Self>>> {
|
||||||
let mut transport = Self::new(None);
|
let mut transport = Self::new(None);
|
||||||
transport.focused = true;
|
transport.focused = true;
|
||||||
|
|
@ -86,7 +41,6 @@ impl TransportToolbar {
|
||||||
);
|
);
|
||||||
Ok(transport)
|
Ok(transport)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new (transport: Option<Transport>) -> Self {
|
pub fn new (transport: Option<Transport>) -> Self {
|
||||||
let timebase = Arc::new(Timebase::default());
|
let timebase = Arc::new(Timebase::default());
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -124,7 +78,6 @@ impl TransportToolbar {
|
||||||
jack: None,
|
jack: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn toggle_play (&mut self) -> Usually<()> {
|
pub fn toggle_play (&mut self) -> Usually<()> {
|
||||||
let transport = self.transport.as_ref().unwrap();
|
let transport = self.transport.as_ref().unwrap();
|
||||||
self.playing.value = match self.playing.value.expect("1st frame has not been processed yet") {
|
self.playing.value = match self.playing.value.expect("1st frame has not been processed yet") {
|
||||||
|
|
@ -140,7 +93,6 @@ impl TransportToolbar {
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update (&mut self, scope: &ProcessScope) -> (bool, usize, usize, usize, usize, f64) {
|
pub fn update (&mut self, scope: &ProcessScope) -> (bool, usize, usize, usize, usize, f64) {
|
||||||
let CycleTimes {
|
let CycleTimes {
|
||||||
current_frames,
|
current_frames,
|
||||||
|
|
@ -177,21 +129,122 @@ impl TransportToolbar {
|
||||||
period_usecs as f64
|
period_usecs as f64
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bpm (&self) -> usize {
|
pub fn bpm (&self) -> usize {
|
||||||
self.timebase.bpm() as usize
|
self.timebase.bpm() as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ppq (&self) -> usize {
|
pub fn ppq (&self) -> usize {
|
||||||
self.timebase.ppq() as usize
|
self.timebase.ppq() as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pulse (&self) -> usize {
|
pub fn pulse (&self) -> usize {
|
||||||
self.timebase.frame_to_pulse(self.clock.frame as f64) as usize
|
self.timebase.frame_to_pulse(self.clock.frame as f64) as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn usecs (&self) -> usize {
|
pub fn usecs (&self) -> usize {
|
||||||
self.timebase.frame_to_usec(self.clock.frame as f64) as usize
|
self.timebase.frame_to_usec(self.clock.frame as f64) as usize
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
impl<'a> Focus<5, TuiOutput<'a>, Rect> for TransportToolbar {
|
||||||
|
fn focus (&self) -> usize {
|
||||||
|
self.focus
|
||||||
|
}
|
||||||
|
fn focus_mut (&mut self) -> &mut usize {
|
||||||
|
&mut self.focus
|
||||||
|
}
|
||||||
|
fn focusable (&self) -> [&dyn Focusable<TuiOutput<'a>, Rect>;5] {
|
||||||
|
[
|
||||||
|
&self.playing as &dyn Focusable<TuiOutput<'a>, Rect>,
|
||||||
|
&self.bpm as &dyn Focusable<TuiOutput<'a>, Rect>,
|
||||||
|
&self.quant as &dyn Focusable<TuiOutput<'a>, Rect>,
|
||||||
|
&self.sync as &dyn Focusable<TuiOutput<'a>, Rect>,
|
||||||
|
&self.clock as &dyn Focusable<TuiOutput<'a>, Rect>,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
fn focusable_mut (&mut self) -> [&mut dyn Focusable<TuiOutput<'a>, Rect>;5] {
|
||||||
|
[
|
||||||
|
&mut self.playing as &mut dyn Focusable<TuiOutput<'a>, Rect>,
|
||||||
|
&mut self.bpm as &mut dyn Focusable<TuiOutput<'a>, Rect>,
|
||||||
|
&mut self.quant as &mut dyn Focusable<TuiOutput<'a>, Rect>,
|
||||||
|
&mut self.sync as &mut dyn Focusable<TuiOutput<'a>, Rect>,
|
||||||
|
&mut self.clock as &mut dyn Focusable<TuiOutput<'a>, Rect>,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a> Focusable<TuiOutput<'a>, Rect> for TransportToolbar {
|
||||||
|
fn is_focused (&self) -> bool {
|
||||||
|
self.focused
|
||||||
|
}
|
||||||
|
fn set_focused (&mut self, focused: bool) {
|
||||||
|
self.focused = focused
|
||||||
|
}
|
||||||
|
}
|
||||||
|
process!(TransportToolbar |self, _client, scope| {
|
||||||
|
self.update(&scope);
|
||||||
|
Control::Continue
|
||||||
|
});
|
||||||
|
|
||||||
|
pub struct TransportPlayPauseButton {
|
||||||
|
pub value: Option<TransportState>,
|
||||||
|
pub focused: bool
|
||||||
|
}
|
||||||
|
impl<'a> Focusable<TuiOutput<'a>, Rect> for TransportPlayPauseButton {
|
||||||
|
fn is_focused (&self) -> bool {
|
||||||
|
self.focused
|
||||||
|
}
|
||||||
|
fn set_focused (&mut self, focused: bool) {
|
||||||
|
self.focused = focused
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TransportBPM {
|
||||||
|
pub value: f64,
|
||||||
|
pub focused: bool
|
||||||
|
}
|
||||||
|
impl<'a> Focusable<TuiOutput<'a>, Rect> for TransportBPM {
|
||||||
|
fn is_focused (&self) -> bool {
|
||||||
|
self.focused
|
||||||
|
}
|
||||||
|
fn set_focused (&mut self, focused: bool) {
|
||||||
|
self.focused = focused
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TransportQuantize {
|
||||||
|
pub value: usize,
|
||||||
|
pub focused: bool
|
||||||
|
}
|
||||||
|
impl<'a> Focusable<TuiOutput<'a>, Rect> for TransportQuantize {
|
||||||
|
fn is_focused (&self) -> bool {
|
||||||
|
self.focused
|
||||||
|
}
|
||||||
|
fn set_focused (&mut self, focused: bool) {
|
||||||
|
self.focused = focused
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TransportSync {
|
||||||
|
pub value: usize,
|
||||||
|
pub focused: bool
|
||||||
|
}
|
||||||
|
impl<'a> Focusable<TuiOutput<'a>, Rect> for TransportSync {
|
||||||
|
fn is_focused (&self) -> bool {
|
||||||
|
self.focused
|
||||||
|
}
|
||||||
|
fn set_focused (&mut self, focused: bool) {
|
||||||
|
self.focused = focused
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TransportClock {
|
||||||
|
pub frame: usize,
|
||||||
|
pub pulse: usize,
|
||||||
|
pub ppq: usize,
|
||||||
|
pub usecs: usize,
|
||||||
|
pub focused: bool,
|
||||||
|
}
|
||||||
|
impl<'a> Focusable<TuiOutput<'a>, Rect> for TransportClock {
|
||||||
|
fn is_focused (&self) -> bool {
|
||||||
|
self.focused
|
||||||
|
}
|
||||||
|
fn set_focused (&mut self, focused: bool) {
|
||||||
|
self.focused = focused
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,8 @@ impl<'a> Render<TuiOutput<'a>, Rect> for TransportToolbar {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Render<TuiOutput<'a>, Rect> for TransportPlayPauseButton {
|
impl<'a> Render<TuiOutput<'a>, Rect> for TransportPlayPauseButton {
|
||||||
fn render (&self, target: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
let Rect { x, y, .. } = target.area;
|
let Rect { x, y, .. } = to.area;
|
||||||
let Self { value, focused } = &self;
|
let Self { value, focused } = &self;
|
||||||
let style = Some(match value {
|
let style = Some(match value {
|
||||||
Some(TransportState::Stopped) => GRAY_DIM.bold(),
|
Some(TransportState::Stopped) => GRAY_DIM.bold(),
|
||||||
|
|
@ -37,85 +37,86 @@ impl<'a> Render<TuiOutput<'a>, Rect> for TransportPlayPauseButton {
|
||||||
Some(TransportState::Stopped) => "⏹ STOPPED",
|
Some(TransportState::Stopped) => "⏹ STOPPED",
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
let mut area = label.blit(target.buffer, x + 1, y, style)?.unwrap();
|
let mut area = label.blit(to.buffer, x + 1, y, style)?.unwrap();
|
||||||
area.width = area.width + 1;
|
area.width = area.width + 1;
|
||||||
area.height = area.height + 1;
|
area.height = area.height + 1;
|
||||||
if *focused {
|
if *focused {
|
||||||
let area = Rect { x: area.x - 1, width: area.width - 1, ..area };
|
let area = Rect { x: area.x - 1, width: area.width - 1, ..area };
|
||||||
CORNERS.draw(target)?;
|
CORNERS.draw(to.buffer, to.area)?;
|
||||||
fill_bg(target.buffer, target.area, COLOR_BG1);
|
fill_bg(to.buffer, to.area, COLOR_BG1);
|
||||||
}
|
}
|
||||||
Ok(area)
|
Ok(Some(area))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Render<TuiOutput<'a>, Rect> for TransportBPM {
|
impl<'a> Render<TuiOutput<'a>, Rect> for TransportBPM {
|
||||||
fn render (&self, target: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
let Rect { x, y, .. } = area;
|
let Rect { x, y, .. } = to.area;
|
||||||
let Self { value, focused } = self;
|
let Self { value, focused } = self;
|
||||||
"BPM".blit(buf, x, y, Some(NOT_DIM))?;
|
"BPM".blit(to.buffer, x, y, Some(NOT_DIM))?;
|
||||||
let width = format!("{}.{:03}", value, (value * 1000.0) % 1000.0).blit(buf, x, y + 1, Some(NOT_DIM_BOLD))?.width;
|
let width = format!("{}.{:03}", value, (value * 1000.0) % 1000.0)
|
||||||
|
.blit(to.buffer, x, y + 1, Some(NOT_DIM_BOLD))?.width;
|
||||||
let area = Rect { x, y, width: (width + 2).max(10), height: 2 };
|
let area = Rect { x, y, width: (width + 2).max(10), height: 2 };
|
||||||
if *focused {
|
if *focused {
|
||||||
let area = Rect { x: area.x - 1, width: area.width - 1, ..area };
|
let area = Rect { x: area.x - 1, width: area.width - 1, ..area };
|
||||||
CORNERS.draw(buf, area)?;
|
CORNERS.draw(to.buffer, area)?;
|
||||||
fill_bg(buf, area, COLOR_BG1);
|
fill_bg(to.buffer, area, COLOR_BG1);
|
||||||
}
|
}
|
||||||
Ok(area)
|
Ok(Some(area))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Render<TuiOutput<'a>, Rect> for TransportQuantize {
|
impl<'a> Render<TuiOutput<'a>, Rect> for TransportQuantize {
|
||||||
fn render (&self, target: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
let Rect { x, y, .. } = area;
|
let Rect { x, y, .. } = to.area;
|
||||||
let Self { value, focused } = self;
|
let Self { value, focused } = self;
|
||||||
"QUANT".blit(buf, x, y, Some(NOT_DIM))?;
|
"QUANT".blit(to.buffer, x, y, Some(NOT_DIM))?;
|
||||||
let width = ppq_to_name(*value as usize).blit(buf, x, y + 1, Some(NOT_DIM_BOLD))?.width;
|
let width = ppq_to_name(*value as usize).blit(to.buffer, x, y + 1, Some(NOT_DIM_BOLD))?.width;
|
||||||
let area = Rect { x, y, width: (width + 2).max(10), height: 2 };
|
let area = Rect { x, y, width: (width + 2).max(10), height: 2 };
|
||||||
if *focused {
|
if *focused {
|
||||||
let area = Rect { x: area.x - 1, width: area.width - 1, ..area };
|
let area = Rect { x: area.x - 1, width: area.width - 1, ..area };
|
||||||
CORNERS.draw(buf, area)?;
|
CORNERS.draw(to.buffer, area)?;
|
||||||
fill_bg(buf, area, COLOR_BG1);
|
fill_bg(to.buffer, area, COLOR_BG1);
|
||||||
}
|
}
|
||||||
Ok(area)
|
Ok(Some(area))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Render<TuiOutput<'a>, Rect> for TransportSync {
|
impl<'a> Render<TuiOutput<'a>, Rect> for TransportSync {
|
||||||
fn render (&self, target: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
let Rect { x, y, .. } = area;
|
let Rect { x, y, .. } = to.area;
|
||||||
let Self { value, focused } = self;
|
let Self { value, focused } = self;
|
||||||
"SYNC".blit(buf, x, y, Some(NOT_DIM))?;
|
"SYNC".blit(to.buffer, x, y, Some(NOT_DIM))?;
|
||||||
let width = ppq_to_name(*value as usize).blit(buf, x, y + 1, Some(NOT_DIM_BOLD))?.width;
|
let width = ppq_to_name(*value as usize).blit(to.buffer, x, y + 1, Some(NOT_DIM_BOLD))?.width;
|
||||||
let area = Rect { x, y, width: (width + 2).max(10), height: 2 };
|
let area = Rect { x, y, width: (width + 2).max(10), height: 2 };
|
||||||
if *focused {
|
if *focused {
|
||||||
let area = Rect { x: area.x - 1, width: area.width - 1, ..area };
|
let area = Rect { x: area.x - 1, width: area.width - 1, ..area };
|
||||||
CORNERS.draw(buf, area)?;
|
CORNERS.draw(to.buffer, area)?;
|
||||||
fill_bg(buf, area, COLOR_BG1);
|
fill_bg(to.buffer, area, COLOR_BG1);
|
||||||
}
|
}
|
||||||
Ok(area)
|
Ok(Some(area))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Render<TuiOutput<'a>, Rect> for TransportClock {
|
impl<'a> Render<TuiOutput<'a>, Rect> for TransportClock {
|
||||||
fn render (&self, target: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
fn render (&self, to: &mut TuiOutput<'a>) -> Perhaps<Rect> {
|
||||||
let Rect { x, y, width, .. } = area;
|
let Rect { x, y, width, .. } = to.area;
|
||||||
let Self { frame, pulse, ppq, usecs, focused } = self;
|
let Self { frame, pulse, ppq, usecs, focused } = self;
|
||||||
let (beats, pulses) = if *ppq > 0 { (pulse / ppq, pulse % ppq) } else { (0, 0) };
|
let (beats, pulses) = if *ppq > 0 { (pulse / ppq, pulse % ppq) } else { (0, 0) };
|
||||||
let (bars, beats) = ((beats / 4) + 1, (beats % 4) + 1);
|
let (bars, beats) = ((beats / 4) + 1, (beats % 4) + 1);
|
||||||
let (seconds, msecs) = (usecs / 1000000, usecs / 1000 % 1000);
|
let (seconds, msecs) = (usecs / 1000000, usecs / 1000 % 1000);
|
||||||
let (minutes, seconds) = (seconds / 60, seconds % 60);
|
let (minutes, seconds) = (seconds / 60, seconds % 60);
|
||||||
let timer = format!("{bars}.{beats}.{pulses:02}");
|
let timer = format!("{bars}.{beats}.{pulses:02}");
|
||||||
timer.blit(buf, x + width - timer.len() as u16 - 1, y + 0, Some(NOT_DIM))?;
|
timer.blit(to.buffer, x + width - timer.len() as u16 - 1, y + 0, Some(NOT_DIM))?;
|
||||||
let timer = format!("{minutes}:{seconds:02}:{msecs:03}");
|
let timer = format!("{minutes}:{seconds:02}:{msecs:03}");
|
||||||
timer.blit(buf, x + width - timer.len() as u16 - 1, y + 1, Some(NOT_DIM))?;
|
timer.blit(to.buffer, x + width - timer.len() as u16 - 1, y + 1, Some(NOT_DIM))?;
|
||||||
let mut area = area;
|
let mut area = to.area;
|
||||||
area.width = area.width + 1;
|
area.width = area.width + 1;
|
||||||
if *focused {
|
if *focused {
|
||||||
let area = Rect { x: area.x - 1, ..area };
|
let area = Rect { x: area.x - 1, ..area };
|
||||||
CORNERS.draw(buf, area)?;
|
CORNERS.draw(to.buffer, area)?;
|
||||||
fill_bg(buf, area, COLOR_BG1);
|
fill_bg(to.buffer, area, COLOR_BG1);
|
||||||
}
|
}
|
||||||
Ok(area)
|
Ok(Some(area))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue