mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 04:06:45 +01:00
remove PhraseViewState
This commit is contained in:
parent
9319315595
commit
3569019b86
16 changed files with 432 additions and 473 deletions
|
|
@ -10,10 +10,6 @@ pub(crate) use std::ffi::OsString;
|
||||||
pub(crate) use std::fs::read_dir;
|
pub(crate) use std::fs::read_dir;
|
||||||
|
|
||||||
submod! {
|
submod! {
|
||||||
tui_focus
|
|
||||||
tui_menu
|
|
||||||
tui_status
|
|
||||||
|
|
||||||
tui_app_arranger
|
tui_app_arranger
|
||||||
tui_app_sequencer
|
tui_app_sequencer
|
||||||
tui_app_transport
|
tui_app_transport
|
||||||
|
|
@ -240,3 +236,117 @@ impl Debug for PhrasePlayerModel {
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub enum AppFocus<T: Copy + Debug + PartialEq> {
|
||||||
|
/// The menu bar is focused
|
||||||
|
Menu,
|
||||||
|
/// The app content is focused
|
||||||
|
Content(T)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait FocusWrap<T> {
|
||||||
|
fn wrap <'a, W: Widget<Engine = Tui>> (self, focus: T, content: &'a W)
|
||||||
|
-> impl Widget<Engine = Tui> + 'a;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export] macro_rules! impl_focus {
|
||||||
|
($Struct:ident $Focus:ident $Grid:expr) => {
|
||||||
|
impl HasFocus for $Struct {
|
||||||
|
type Item = AppFocus<$Focus>;
|
||||||
|
/// Get the currently focused item.
|
||||||
|
fn focused (&self) -> Self::Item {
|
||||||
|
self.focus.inner()
|
||||||
|
}
|
||||||
|
/// Get the currently focused item.
|
||||||
|
fn set_focused (&mut self, to: Self::Item) {
|
||||||
|
self.focus.set_inner(to)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl HasEnter for $Struct {
|
||||||
|
/// Get the currently focused item.
|
||||||
|
fn entered (&self) -> bool {
|
||||||
|
self.focus.is_entered()
|
||||||
|
}
|
||||||
|
/// Get the currently focused item.
|
||||||
|
fn set_entered (&mut self, entered: bool) {
|
||||||
|
if entered {
|
||||||
|
self.focus.to_entered()
|
||||||
|
} else {
|
||||||
|
self.focus.to_focused()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl FocusGrid for $Struct {
|
||||||
|
fn focus_cursor (&self) -> (usize, usize) {
|
||||||
|
self.cursor
|
||||||
|
}
|
||||||
|
fn focus_cursor_mut (&mut self) -> &mut (usize, usize) {
|
||||||
|
&mut self.cursor
|
||||||
|
}
|
||||||
|
fn focus_layout (&self) -> &[&[AppFocus<$Focus>]] {
|
||||||
|
use AppFocus::*;
|
||||||
|
use $Focus::*;
|
||||||
|
&$Grid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait StatusBar: Copy + Widget<Engine = Tui> {
|
||||||
|
type State;
|
||||||
|
fn hotkey_fg () -> Color where Self: Sized;
|
||||||
|
fn update (&mut self, state: &Self::State) where Self: Sized;
|
||||||
|
fn command (commands: &[[impl Widget<Engine = Tui>;3]])
|
||||||
|
-> impl Widget<Engine = Tui> + '_
|
||||||
|
where
|
||||||
|
Self: Sized
|
||||||
|
{
|
||||||
|
let hotkey_fg = Self::hotkey_fg();
|
||||||
|
Stack::right(move |add|{
|
||||||
|
Ok(for [a, b, c] in commands.iter() {
|
||||||
|
add(&row!(
|
||||||
|
" ",
|
||||||
|
widget(a),
|
||||||
|
widget(b).bold(true).fg(hotkey_fg),
|
||||||
|
widget(c),
|
||||||
|
))?;
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn content_with_menu_and_status <'a, A, S, C> (
|
||||||
|
content: &'a A,
|
||||||
|
menu_bar: &'a Option<MenuBar<Tui, S, C>>,
|
||||||
|
status_bar: &'a Option<impl StatusBar>
|
||||||
|
) -> impl Widget<Engine = Tui> + 'a
|
||||||
|
where
|
||||||
|
A: Widget<Engine = Tui>,
|
||||||
|
S: Send + Sync + 'a,
|
||||||
|
C: Command<S>
|
||||||
|
{
|
||||||
|
let menus = menu_bar.as_ref().map_or_else(
|
||||||
|
||&[] as &[Menu<_, _, _>],
|
||||||
|
|m|m.menus.as_slice()
|
||||||
|
);
|
||||||
|
Either(
|
||||||
|
menu_bar.is_none(),
|
||||||
|
Either(
|
||||||
|
status_bar.is_none(),
|
||||||
|
widget(content),
|
||||||
|
Split::up(
|
||||||
|
1,
|
||||||
|
widget(status_bar.as_ref().unwrap()),
|
||||||
|
widget(content)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Split::down(
|
||||||
|
1,
|
||||||
|
row!(menu in menus.iter() => {
|
||||||
|
row!(" ", menu.title.as_str(), " ")
|
||||||
|
}),
|
||||||
|
widget(content)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,3 +66,152 @@ pub enum ArrangerFocus {
|
||||||
/// The phrase editor (sequencer) is focused
|
/// The phrase editor (sequencer) is focused
|
||||||
PhraseEditor,
|
PhraseEditor,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_focus!(ArrangerTui ArrangerFocus [
|
||||||
|
//&[
|
||||||
|
//Menu,
|
||||||
|
//Menu,
|
||||||
|
//Menu,
|
||||||
|
//Menu,
|
||||||
|
//Menu,
|
||||||
|
//],
|
||||||
|
&[
|
||||||
|
Content(Transport(TransportFocus::PlayPause)),
|
||||||
|
Content(Transport(TransportFocus::Bpm)),
|
||||||
|
Content(Transport(TransportFocus::Sync)),
|
||||||
|
Content(Transport(TransportFocus::Clock)),
|
||||||
|
Content(Transport(TransportFocus::Quant))
|
||||||
|
], &[
|
||||||
|
Content(Arranger),
|
||||||
|
Content(Arranger),
|
||||||
|
Content(Arranger),
|
||||||
|
Content(Arranger),
|
||||||
|
Content(Arranger),
|
||||||
|
], &[
|
||||||
|
Content(Phrases),
|
||||||
|
Content(Phrases),
|
||||||
|
Content(PhraseEditor),
|
||||||
|
Content(PhraseEditor),
|
||||||
|
Content(PhraseEditor),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
/// Status bar for arranger app
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum ArrangerStatus {
|
||||||
|
Transport,
|
||||||
|
ArrangerMix,
|
||||||
|
ArrangerTrack,
|
||||||
|
ArrangerScene,
|
||||||
|
ArrangerClip,
|
||||||
|
PhrasePool,
|
||||||
|
PhraseView,
|
||||||
|
PhraseEdit,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StatusBar for ArrangerStatus {
|
||||||
|
type State = (ArrangerFocus, ArrangerSelection, bool);
|
||||||
|
fn hotkey_fg () -> Color where Self: Sized {
|
||||||
|
TuiTheme::hotkey_fg()
|
||||||
|
}
|
||||||
|
fn update (&mut self, (focused, selected, entered): &Self::State) {
|
||||||
|
*self = match focused {
|
||||||
|
//ArrangerFocus::Menu => { todo!() },
|
||||||
|
ArrangerFocus::Transport(_) => ArrangerStatus::Transport,
|
||||||
|
ArrangerFocus::Arranger => match selected {
|
||||||
|
ArrangerSelection::Mix => ArrangerStatus::ArrangerMix,
|
||||||
|
ArrangerSelection::Track(_) => ArrangerStatus::ArrangerTrack,
|
||||||
|
ArrangerSelection::Scene(_) => ArrangerStatus::ArrangerScene,
|
||||||
|
ArrangerSelection::Clip(_, _) => ArrangerStatus::ArrangerClip,
|
||||||
|
},
|
||||||
|
ArrangerFocus::Phrases => ArrangerStatus::PhrasePool,
|
||||||
|
ArrangerFocus::PhraseEditor => match entered {
|
||||||
|
true => ArrangerStatus::PhraseEdit,
|
||||||
|
false => ArrangerStatus::PhraseView,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Content for ArrangerStatus {
|
||||||
|
type Engine = Tui;
|
||||||
|
fn content (&self) -> impl Widget<Engine = Tui> {
|
||||||
|
let label = match self {
|
||||||
|
Self::Transport => "TRANSPORT",
|
||||||
|
Self::ArrangerMix => "PROJECT",
|
||||||
|
Self::ArrangerTrack => "TRACK",
|
||||||
|
Self::ArrangerScene => "SCENE",
|
||||||
|
Self::ArrangerClip => "CLIP",
|
||||||
|
Self::PhrasePool => "SEQ LIST",
|
||||||
|
Self::PhraseView => "VIEW SEQ",
|
||||||
|
Self::PhraseEdit => "EDIT SEQ",
|
||||||
|
};
|
||||||
|
let status_bar_bg = TuiTheme::status_bar_bg();
|
||||||
|
let mode_bg = TuiTheme::mode_bg();
|
||||||
|
let mode_fg = TuiTheme::mode_fg();
|
||||||
|
let mode = TuiStyle::bold(format!(" {label} "), true).bg(mode_bg).fg(mode_fg);
|
||||||
|
let commands = match self {
|
||||||
|
Self::ArrangerMix => Self::command(&[
|
||||||
|
["", "c", "olor"],
|
||||||
|
["", "<>", "resize"],
|
||||||
|
["", "+-", "zoom"],
|
||||||
|
["", "n", "ame/number"],
|
||||||
|
["", "Enter", " stop all"],
|
||||||
|
]),
|
||||||
|
Self::ArrangerClip => Self::command(&[
|
||||||
|
["", "g", "et"],
|
||||||
|
["", "s", "et"],
|
||||||
|
["", "a", "dd"],
|
||||||
|
["", "i", "ns"],
|
||||||
|
["", "d", "up"],
|
||||||
|
["", "e", "dit"],
|
||||||
|
["", "c", "olor"],
|
||||||
|
["re", "n", "ame"],
|
||||||
|
["", ",.", "select"],
|
||||||
|
["", "Enter", " launch"],
|
||||||
|
]),
|
||||||
|
Self::ArrangerTrack => Self::command(&[
|
||||||
|
["re", "n", "ame"],
|
||||||
|
["", ",.", "resize"],
|
||||||
|
["", "<>", "move"],
|
||||||
|
["", "i", "nput"],
|
||||||
|
["", "o", "utput"],
|
||||||
|
["", "m", "ute"],
|
||||||
|
["", "s", "olo"],
|
||||||
|
["", "Del", "ete"],
|
||||||
|
["", "Enter", " stop"],
|
||||||
|
]),
|
||||||
|
Self::ArrangerScene => Self::command(&[
|
||||||
|
["re", "n", "ame"],
|
||||||
|
["", "Del", "ete"],
|
||||||
|
["", "Enter", " launch"],
|
||||||
|
]),
|
||||||
|
Self::PhrasePool => Self::command(&[
|
||||||
|
["", "a", "ppend"],
|
||||||
|
["", "i", "nsert"],
|
||||||
|
["", "d", "uplicate"],
|
||||||
|
["", "Del", "ete"],
|
||||||
|
["", "c", "olor"],
|
||||||
|
["re", "n", "ame"],
|
||||||
|
["leng", "t", "h"],
|
||||||
|
["", ",.", "move"],
|
||||||
|
["", "+-", "resize view"],
|
||||||
|
]),
|
||||||
|
Self::PhraseView => Self::command(&[
|
||||||
|
["", "enter", " edit"],
|
||||||
|
["", "arrows/pgup/pgdn", " scroll"],
|
||||||
|
["", "+=", "zoom"],
|
||||||
|
]),
|
||||||
|
Self::PhraseEdit => Self::command(&[
|
||||||
|
["", "esc", " exit"],
|
||||||
|
["", "a", "ppend"],
|
||||||
|
["", "s", "et"],
|
||||||
|
["", "][", "length"],
|
||||||
|
["", "+-", "zoom"],
|
||||||
|
]),
|
||||||
|
_ => Self::command(&[])
|
||||||
|
};
|
||||||
|
//let commands = commands.iter().reduce(String::new(), |s, (a, b, c)| format!("{s} {a}{b}{c}"));
|
||||||
|
row!(mode, commands).fill_x().bg(status_bar_bg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,3 +49,53 @@ pub enum SequencerFocus {
|
||||||
/// The phrase editor (sequencer) is focused
|
/// The phrase editor (sequencer) is focused
|
||||||
PhraseEditor,
|
PhraseEditor,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_focus!(SequencerTui SequencerFocus [
|
||||||
|
//&[
|
||||||
|
//Menu,
|
||||||
|
//Menu,
|
||||||
|
//Menu,
|
||||||
|
//Menu,
|
||||||
|
//Menu,
|
||||||
|
//],
|
||||||
|
&[
|
||||||
|
Content(Transport(TransportFocus::PlayPause)),
|
||||||
|
Content(Transport(TransportFocus::Bpm)),
|
||||||
|
Content(Transport(TransportFocus::Sync)),
|
||||||
|
Content(Transport(TransportFocus::Clock)),
|
||||||
|
Content(Transport(TransportFocus::Quant))
|
||||||
|
],
|
||||||
|
&[
|
||||||
|
Content(Phrases),
|
||||||
|
Content(Phrases),
|
||||||
|
Content(PhraseEditor),
|
||||||
|
Content(PhraseEditor),
|
||||||
|
Content(PhraseEditor),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
/// Status bar for sequencer app
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum SequencerStatusBar {
|
||||||
|
Transport,
|
||||||
|
PhrasePool,
|
||||||
|
PhraseEditor,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StatusBar for SequencerStatusBar {
|
||||||
|
type State = ();
|
||||||
|
fn hotkey_fg () -> Color {
|
||||||
|
TuiTheme::hotkey_fg()
|
||||||
|
}
|
||||||
|
fn update (&mut self, state: &()) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Content for SequencerStatusBar {
|
||||||
|
type Engine = Tui;
|
||||||
|
fn content (&self) -> impl Widget<Engine = Tui> {
|
||||||
|
todo!();
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,3 +54,35 @@ impl FocusWrap<TransportFocus> for Option<TransportFocus> {
|
||||||
lay!(corners, highlight, *content)
|
lay!(corners, highlight, *content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_focus!(TransportTui TransportFocus [
|
||||||
|
//&[Menu],
|
||||||
|
&[
|
||||||
|
Content(PlayPause),
|
||||||
|
Content(Bpm),
|
||||||
|
Content(Sync),
|
||||||
|
Content(Clock),
|
||||||
|
Content(Quant)
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct TransportStatusBar;
|
||||||
|
|
||||||
|
impl StatusBar for TransportStatusBar {
|
||||||
|
type State = ();
|
||||||
|
fn hotkey_fg () -> Color {
|
||||||
|
TuiTheme::hotkey_fg()
|
||||||
|
}
|
||||||
|
fn update (&mut self, state: &()) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Content for TransportStatusBar {
|
||||||
|
type Engine = Tui;
|
||||||
|
fn content (&self) -> impl Widget<Engine = Tui> {
|
||||||
|
todo!();
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,121 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
||||||
pub enum AppFocus<T: Copy + Debug + PartialEq> {
|
|
||||||
/// The menu bar is focused
|
|
||||||
Menu,
|
|
||||||
/// The app content is focused
|
|
||||||
Content(T)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait FocusWrap<T> {
|
|
||||||
fn wrap <'a, W: Widget<Engine = Tui>> (self, focus: T, content: &'a W)
|
|
||||||
-> impl Widget<Engine = Tui> + 'a;
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! impl_focus {
|
|
||||||
($Struct:ident $Focus:ident $Grid:expr) => {
|
|
||||||
impl HasFocus for $Struct {
|
|
||||||
type Item = AppFocus<$Focus>;
|
|
||||||
/// Get the currently focused item.
|
|
||||||
fn focused (&self) -> Self::Item {
|
|
||||||
self.focus.inner()
|
|
||||||
}
|
|
||||||
/// Get the currently focused item.
|
|
||||||
fn set_focused (&mut self, to: Self::Item) {
|
|
||||||
self.focus.set_inner(to)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl HasEnter for $Struct {
|
|
||||||
/// Get the currently focused item.
|
|
||||||
fn entered (&self) -> bool {
|
|
||||||
self.focus.is_entered()
|
|
||||||
}
|
|
||||||
/// Get the currently focused item.
|
|
||||||
fn set_entered (&mut self, entered: bool) {
|
|
||||||
if entered {
|
|
||||||
self.focus.to_entered()
|
|
||||||
} else {
|
|
||||||
self.focus.to_focused()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl FocusGrid for $Struct {
|
|
||||||
fn focus_cursor (&self) -> (usize, usize) {
|
|
||||||
self.cursor
|
|
||||||
}
|
|
||||||
fn focus_cursor_mut (&mut self) -> &mut (usize, usize) {
|
|
||||||
&mut self.cursor
|
|
||||||
}
|
|
||||||
fn focus_layout (&self) -> &[&[AppFocus<$Focus>]] {
|
|
||||||
use AppFocus::*;
|
|
||||||
use $Focus::*;
|
|
||||||
&$Grid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_focus!(TransportTui TransportFocus [
|
|
||||||
//&[Menu],
|
|
||||||
&[
|
|
||||||
Content(PlayPause),
|
|
||||||
Content(Bpm),
|
|
||||||
Content(Sync),
|
|
||||||
Content(Clock),
|
|
||||||
Content(Quant)
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
|
|
||||||
impl_focus!(SequencerTui SequencerFocus [
|
|
||||||
//&[
|
|
||||||
//Menu,
|
|
||||||
//Menu,
|
|
||||||
//Menu,
|
|
||||||
//Menu,
|
|
||||||
//Menu,
|
|
||||||
//],
|
|
||||||
&[
|
|
||||||
Content(Transport(TransportFocus::PlayPause)),
|
|
||||||
Content(Transport(TransportFocus::Bpm)),
|
|
||||||
Content(Transport(TransportFocus::Sync)),
|
|
||||||
Content(Transport(TransportFocus::Clock)),
|
|
||||||
Content(Transport(TransportFocus::Quant))
|
|
||||||
],
|
|
||||||
&[
|
|
||||||
Content(Phrases),
|
|
||||||
Content(Phrases),
|
|
||||||
Content(PhraseEditor),
|
|
||||||
Content(PhraseEditor),
|
|
||||||
Content(PhraseEditor),
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
|
|
||||||
impl_focus!(ArrangerTui ArrangerFocus [
|
|
||||||
//&[
|
|
||||||
//Menu,
|
|
||||||
//Menu,
|
|
||||||
//Menu,
|
|
||||||
//Menu,
|
|
||||||
//Menu,
|
|
||||||
//],
|
|
||||||
&[
|
|
||||||
Content(Transport(TransportFocus::PlayPause)),
|
|
||||||
Content(Transport(TransportFocus::Bpm)),
|
|
||||||
Content(Transport(TransportFocus::Sync)),
|
|
||||||
Content(Transport(TransportFocus::Clock)),
|
|
||||||
Content(Transport(TransportFocus::Quant))
|
|
||||||
], &[
|
|
||||||
Content(Arranger),
|
|
||||||
Content(Arranger),
|
|
||||||
Content(Arranger),
|
|
||||||
Content(Arranger),
|
|
||||||
Content(Arranger),
|
|
||||||
], &[
|
|
||||||
Content(Phrases),
|
|
||||||
Content(Phrases),
|
|
||||||
Content(PhraseEditor),
|
|
||||||
Content(PhraseEditor),
|
|
||||||
Content(PhraseEditor),
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
|
|
@ -27,23 +27,23 @@ impl Audio for ArrangerTui {
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: one of these per playing track
|
// FIXME: one of these per playing track
|
||||||
self.now().set(0.);
|
//self.now.set(0.);
|
||||||
if let ArrangerSelection::Clip(t, s) = self.selected {
|
//if let ArrangerSelection::Clip(t, s) = self.selected {
|
||||||
let phrase = self.scenes().get(s).map(|scene|scene.clips.get(t));
|
//let phrase = self.scenes().get(s).map(|scene|scene.clips.get(t));
|
||||||
if let Some(Some(Some(phrase))) = phrase {
|
//if let Some(Some(Some(phrase))) = phrase {
|
||||||
if let Some(track) = self.tracks().get(t) {
|
//if let Some(track) = self.tracks().get(t) {
|
||||||
if let Some((ref started_at, Some(ref playing))) = track.player.play_phrase {
|
//if let Some((ref started_at, Some(ref playing))) = track.player.play_phrase {
|
||||||
let phrase = phrase.read().unwrap();
|
//let phrase = phrase.read().unwrap();
|
||||||
if *playing.read().unwrap() == *phrase {
|
//if *playing.read().unwrap() == *phrase {
|
||||||
let pulse = self.current().pulse.get();
|
//let pulse = self.current().pulse.get();
|
||||||
let start = started_at.pulse.get();
|
//let start = started_at.pulse.get();
|
||||||
let now = (pulse - start) % phrase.length as f64;
|
//let now = (pulse - start) % phrase.length as f64;
|
||||||
self.now().set(now);
|
//self.now.set(now);
|
||||||
}
|
//}
|
||||||
}
|
//}
|
||||||
}
|
//}
|
||||||
}
|
//}
|
||||||
}
|
//}
|
||||||
|
|
||||||
// End profiling cycle
|
// End profiling cycle
|
||||||
self.perf.update(t0, scope);
|
self.perf.update(t0, scope);
|
||||||
|
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
|
|
||||||
fn content_with_menu_and_status <'a, A, S, C> (
|
|
||||||
content: &'a A,
|
|
||||||
menu_bar: &'a Option<MenuBar<Tui, S, C>>,
|
|
||||||
status_bar: &'a Option<impl StatusBar>
|
|
||||||
) -> impl Widget<Engine = Tui> + 'a
|
|
||||||
where
|
|
||||||
A: Widget<Engine = Tui>,
|
|
||||||
S: Send + Sync + 'a,
|
|
||||||
C: Command<S>
|
|
||||||
{
|
|
||||||
let menus = menu_bar.as_ref().map_or_else(
|
|
||||||
||&[] as &[Menu<_, _, _>],
|
|
||||||
|m|m.menus.as_slice()
|
|
||||||
);
|
|
||||||
Either(
|
|
||||||
menu_bar.is_none(),
|
|
||||||
Either(
|
|
||||||
status_bar.is_none(),
|
|
||||||
widget(content),
|
|
||||||
Split::up(
|
|
||||||
1,
|
|
||||||
widget(status_bar.as_ref().unwrap()),
|
|
||||||
widget(content)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Split::down(
|
|
||||||
1,
|
|
||||||
row!(menu in menus.iter() => {
|
|
||||||
row!(" ", menu.title.as_str(), " ")
|
|
||||||
}),
|
|
||||||
widget(content)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
|
|
@ -52,3 +52,33 @@ impl Default for PhraseEditorModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait HasEditor {
|
||||||
|
fn editor (&self) -> &PhraseEditorModel;
|
||||||
|
fn editor_focused (&self) -> bool;
|
||||||
|
fn editor_entered (&self) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasEditor for SequencerTui {
|
||||||
|
fn editor (&self) -> &PhraseEditorModel {
|
||||||
|
&self.editor
|
||||||
|
}
|
||||||
|
fn editor_focused (&self) -> bool {
|
||||||
|
self.focused() == AppFocus::Content(SequencerFocus::PhraseEditor)
|
||||||
|
}
|
||||||
|
fn editor_entered (&self) -> bool {
|
||||||
|
self.entered() && self.editor_focused()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasEditor for ArrangerTui {
|
||||||
|
fn editor (&self) -> &PhraseEditorModel {
|
||||||
|
&self.editor
|
||||||
|
}
|
||||||
|
fn editor_focused (&self) -> bool {
|
||||||
|
self.focused() == AppFocus::Content(ArrangerFocus::PhraseEditor)
|
||||||
|
}
|
||||||
|
fn editor_entered (&self) -> bool {
|
||||||
|
self.entered() && self.editor_focused()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,195 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
|
|
||||||
pub trait StatusBar: Copy + Widget<Engine = Tui> {
|
|
||||||
|
|
||||||
type State;
|
|
||||||
|
|
||||||
fn hotkey_fg () -> Color where Self: Sized;
|
|
||||||
|
|
||||||
fn update (&mut self, state: &Self::State) where Self: Sized;
|
|
||||||
|
|
||||||
fn command (commands: &[[impl Widget<Engine = Tui>;3]])
|
|
||||||
-> impl Widget<Engine = Tui> + '_
|
|
||||||
where
|
|
||||||
Self: Sized
|
|
||||||
{
|
|
||||||
let hotkey_fg = Self::hotkey_fg();
|
|
||||||
Stack::right(move |add|{
|
|
||||||
Ok(for [a, b, c] in commands.iter() {
|
|
||||||
add(&row!(
|
|
||||||
" ",
|
|
||||||
widget(a),
|
|
||||||
widget(b).bold(true).fg(hotkey_fg),
|
|
||||||
widget(c),
|
|
||||||
))?;
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct TransportStatusBar;
|
|
||||||
|
|
||||||
impl StatusBar for TransportStatusBar {
|
|
||||||
type State = ();
|
|
||||||
fn hotkey_fg () -> Color {
|
|
||||||
TuiTheme::hotkey_fg()
|
|
||||||
}
|
|
||||||
fn update (&mut self, state: &()) {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Content for TransportStatusBar {
|
|
||||||
type Engine = Tui;
|
|
||||||
fn content (&self) -> impl Widget<Engine = Tui> {
|
|
||||||
todo!();
|
|
||||||
""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Status bar for sequencer app
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub enum SequencerStatusBar {
|
|
||||||
Transport,
|
|
||||||
PhrasePool,
|
|
||||||
PhraseEditor,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StatusBar for SequencerStatusBar {
|
|
||||||
type State = ();
|
|
||||||
fn hotkey_fg () -> Color {
|
|
||||||
TuiTheme::hotkey_fg()
|
|
||||||
}
|
|
||||||
fn update (&mut self, state: &()) {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Content for SequencerStatusBar {
|
|
||||||
type Engine = Tui;
|
|
||||||
fn content (&self) -> impl Widget<Engine = Tui> {
|
|
||||||
todo!();
|
|
||||||
""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Status bar for arranger app
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
pub enum ArrangerStatus {
|
|
||||||
Transport,
|
|
||||||
ArrangerMix,
|
|
||||||
ArrangerTrack,
|
|
||||||
ArrangerScene,
|
|
||||||
ArrangerClip,
|
|
||||||
PhrasePool,
|
|
||||||
PhraseView,
|
|
||||||
PhraseEdit,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StatusBar for ArrangerStatus {
|
|
||||||
type State = (ArrangerFocus, ArrangerSelection, bool);
|
|
||||||
fn hotkey_fg () -> Color where Self: Sized {
|
|
||||||
TuiTheme::hotkey_fg()
|
|
||||||
}
|
|
||||||
fn update (&mut self, (focused, selected, entered): &Self::State) {
|
|
||||||
*self = match focused {
|
|
||||||
//ArrangerFocus::Menu => { todo!() },
|
|
||||||
ArrangerFocus::Transport(_) => ArrangerStatus::Transport,
|
|
||||||
ArrangerFocus::Arranger => match selected {
|
|
||||||
ArrangerSelection::Mix => ArrangerStatus::ArrangerMix,
|
|
||||||
ArrangerSelection::Track(_) => ArrangerStatus::ArrangerTrack,
|
|
||||||
ArrangerSelection::Scene(_) => ArrangerStatus::ArrangerScene,
|
|
||||||
ArrangerSelection::Clip(_, _) => ArrangerStatus::ArrangerClip,
|
|
||||||
},
|
|
||||||
ArrangerFocus::Phrases => ArrangerStatus::PhrasePool,
|
|
||||||
ArrangerFocus::PhraseEditor => match entered {
|
|
||||||
true => ArrangerStatus::PhraseEdit,
|
|
||||||
false => ArrangerStatus::PhraseView,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Content for ArrangerStatus {
|
|
||||||
type Engine = Tui;
|
|
||||||
fn content (&self) -> impl Widget<Engine = Tui> {
|
|
||||||
let label = match self {
|
|
||||||
Self::Transport => "TRANSPORT",
|
|
||||||
Self::ArrangerMix => "PROJECT",
|
|
||||||
Self::ArrangerTrack => "TRACK",
|
|
||||||
Self::ArrangerScene => "SCENE",
|
|
||||||
Self::ArrangerClip => "CLIP",
|
|
||||||
Self::PhrasePool => "SEQ LIST",
|
|
||||||
Self::PhraseView => "VIEW SEQ",
|
|
||||||
Self::PhraseEdit => "EDIT SEQ",
|
|
||||||
};
|
|
||||||
let status_bar_bg = TuiTheme::status_bar_bg();
|
|
||||||
let mode_bg = TuiTheme::mode_bg();
|
|
||||||
let mode_fg = TuiTheme::mode_fg();
|
|
||||||
let mode = TuiStyle::bold(format!(" {label} "), true).bg(mode_bg).fg(mode_fg);
|
|
||||||
let commands = match self {
|
|
||||||
Self::ArrangerMix => Self::command(&[
|
|
||||||
["", "c", "olor"],
|
|
||||||
["", "<>", "resize"],
|
|
||||||
["", "+-", "zoom"],
|
|
||||||
["", "n", "ame/number"],
|
|
||||||
["", "Enter", " stop all"],
|
|
||||||
]),
|
|
||||||
Self::ArrangerClip => Self::command(&[
|
|
||||||
["", "g", "et"],
|
|
||||||
["", "s", "et"],
|
|
||||||
["", "a", "dd"],
|
|
||||||
["", "i", "ns"],
|
|
||||||
["", "d", "up"],
|
|
||||||
["", "e", "dit"],
|
|
||||||
["", "c", "olor"],
|
|
||||||
["re", "n", "ame"],
|
|
||||||
["", ",.", "select"],
|
|
||||||
["", "Enter", " launch"],
|
|
||||||
]),
|
|
||||||
Self::ArrangerTrack => Self::command(&[
|
|
||||||
["re", "n", "ame"],
|
|
||||||
["", ",.", "resize"],
|
|
||||||
["", "<>", "move"],
|
|
||||||
["", "i", "nput"],
|
|
||||||
["", "o", "utput"],
|
|
||||||
["", "m", "ute"],
|
|
||||||
["", "s", "olo"],
|
|
||||||
["", "Del", "ete"],
|
|
||||||
["", "Enter", " stop"],
|
|
||||||
]),
|
|
||||||
Self::ArrangerScene => Self::command(&[
|
|
||||||
["re", "n", "ame"],
|
|
||||||
["", "Del", "ete"],
|
|
||||||
["", "Enter", " launch"],
|
|
||||||
]),
|
|
||||||
Self::PhrasePool => Self::command(&[
|
|
||||||
["", "a", "ppend"],
|
|
||||||
["", "i", "nsert"],
|
|
||||||
["", "d", "uplicate"],
|
|
||||||
["", "Del", "ete"],
|
|
||||||
["", "c", "olor"],
|
|
||||||
["re", "n", "ame"],
|
|
||||||
["leng", "t", "h"],
|
|
||||||
["", ",.", "move"],
|
|
||||||
["", "+-", "resize view"],
|
|
||||||
]),
|
|
||||||
Self::PhraseView => Self::command(&[
|
|
||||||
["", "enter", " edit"],
|
|
||||||
["", "arrows/pgup/pgdn", " scroll"],
|
|
||||||
["", "+=", "zoom"],
|
|
||||||
]),
|
|
||||||
Self::PhraseEdit => Self::command(&[
|
|
||||||
["", "esc", " exit"],
|
|
||||||
["", "a", "ppend"],
|
|
||||||
["", "s", "et"],
|
|
||||||
["", "][", "length"],
|
|
||||||
["", "+-", "zoom"],
|
|
||||||
]),
|
|
||||||
_ => Self::command(&[])
|
|
||||||
};
|
|
||||||
//let commands = commands.iter().reduce(String::new(), |s, (a, b, c)| format!("{s} {a}{b}{c}"));
|
|
||||||
row!(mode, commands).fill_x().bg(status_bar_bg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
|
|
@ -36,7 +36,7 @@ impl Content for ArrangerTui {
|
||||||
Split::right(
|
Split::right(
|
||||||
self.splits[1],
|
self.splits[1],
|
||||||
PhrasesView(self),
|
PhrasesView(self),
|
||||||
PhraseView(self),
|
PhraseView2::from(self),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,41 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
impl Widget for PhraseEditorModel {
|
pub struct PhraseView2<'a> {
|
||||||
type Engine = Tui;
|
pub(crate) focused: bool,
|
||||||
fn layout (&self, to: [u16;2]) -> Perhaps<[u16;2]> {
|
pub(crate) entered: bool,
|
||||||
PhraseView(self).layout(to)
|
pub(crate) phrase: &'a Option<Arc<RwLock<Phrase>>>,
|
||||||
|
pub(crate) size: &'a Measure<Tui>,
|
||||||
|
pub(crate) keys: &'a Buffer,
|
||||||
|
pub(crate) buffer: &'a BigBuffer,
|
||||||
|
pub(crate) note_len: usize,
|
||||||
|
pub(crate) note_axis: &'a RwLock<FixedAxis<usize>>,
|
||||||
|
pub(crate) time_axis: &'a RwLock<ScaledAxis<usize>>,
|
||||||
|
pub(crate) now: &'a Arc<Pulse>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: HasEditor> From<&'a T> for PhraseView2<'a> {
|
||||||
|
fn from (state: &'a T) -> Self {
|
||||||
|
Self {
|
||||||
|
focused: state.editor_focused(),
|
||||||
|
entered: state.editor_entered(),
|
||||||
|
note_len: state.editor().note_len,
|
||||||
|
phrase: &state.editor().phrase,
|
||||||
|
size: &state.editor().size,
|
||||||
|
keys: &state.editor().keys,
|
||||||
|
buffer: &state.editor().buffer,
|
||||||
|
note_axis: &state.editor().note_axis,
|
||||||
|
time_axis: &state.editor().time_axis,
|
||||||
|
now: &state.editor().now
|
||||||
}
|
}
|
||||||
fn render (&self, to: &mut TuiOutput) -> Usually<()> {
|
|
||||||
PhraseView(self).render(to)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PhraseView<'a, T: PhraseViewState>(pub &'a T);
|
impl<'a> Content for PhraseView2<'a> {
|
||||||
|
|
||||||
impl<'a, T: PhraseViewState> Content for PhraseView<'a, T> {
|
|
||||||
type Engine = Tui;
|
type Engine = Tui;
|
||||||
fn content (&self) -> impl Widget<Engine = Tui> {
|
fn content (&self) -> impl Widget<Engine = Tui> {
|
||||||
let phrase = self.0.phrase_editing();
|
let Self {
|
||||||
let size = self.0.size();
|
focused, entered, phrase, size, keys, buffer, note_len, note_axis, time_axis, now
|
||||||
let focused = self.0.phrase_editor_focused();
|
} = self;
|
||||||
let entered = self.0.phrase_editor_entered();
|
|
||||||
let keys = self.0.keys();
|
|
||||||
let buffer = self.0.buffer();
|
|
||||||
let note_len = self.0.note_len();
|
|
||||||
let note_axis = self.0.note_axis();
|
|
||||||
let time_axis = self.0.time_axis();
|
|
||||||
let FixedAxis { start: note_start, point: note_point, clamp: note_clamp }
|
let FixedAxis { start: note_start, point: note_point, clamp: note_clamp }
|
||||||
= *note_axis.read().unwrap();
|
= *note_axis.read().unwrap();
|
||||||
let ScaledAxis { start: time_start, point: time_point, clamp: time_clamp, scale: time_scale }
|
let ScaledAxis { start: time_start, point: time_point, clamp: time_clamp, scale: time_scale }
|
||||||
|
|
@ -68,7 +80,7 @@ impl<'a, T: PhraseViewState> Content for PhraseView<'a, T> {
|
||||||
})
|
})
|
||||||
}).fill_x();
|
}).fill_x();
|
||||||
let cursor = CustomWidget::new(|to|Ok(Some(to)), move|to: &mut TuiOutput|{
|
let cursor = CustomWidget::new(|to|Ok(Some(to)), move|to: &mut TuiOutput|{
|
||||||
Ok(if focused && entered {
|
Ok(if *focused && *entered {
|
||||||
let area = to.area();
|
let area = to.area();
|
||||||
if let (Some(time), Some(note)) = (time_point, note_point) {
|
if let (Some(time), Some(note)) = (time_point, note_point) {
|
||||||
let x1 = area.x() + (time / time_scale) as u16;
|
let x1 = area.x() + (time / time_scale) as u16;
|
||||||
|
|
@ -87,7 +99,7 @@ impl<'a, T: PhraseViewState> Content for PhraseView<'a, T> {
|
||||||
|to:[u16;2]|Ok(Some(to.clip_h(1))),
|
|to:[u16;2]|Ok(Some(to.clip_h(1))),
|
||||||
move|to: &mut TuiOutput|{
|
move|to: &mut TuiOutput|{
|
||||||
if let Some(_) = phrase {
|
if let Some(_) = phrase {
|
||||||
let now = self.0.now().get() as usize; // TODO FIXME: self.now % phrase.read().unwrap().length;
|
let now = now.get() as usize; // TODO FIXME: self.now % phrase.read().unwrap().length;
|
||||||
let time_clamp = time_clamp
|
let time_clamp = time_clamp
|
||||||
.expect("time_axis of sequencer expected to be clamped");
|
.expect("time_axis of sequencer expected to be clamped");
|
||||||
for x in 0..(time_clamp/time_scale).saturating_sub(time_start) {
|
for x in 0..(time_clamp/time_scale).saturating_sub(time_start) {
|
||||||
|
|
@ -103,14 +115,14 @@ impl<'a, T: PhraseViewState> Content for PhraseView<'a, T> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
).push_x(6).align_sw();
|
).push_x(6).align_sw();
|
||||||
let border_color = if focused{Color::Rgb(100, 110, 40)}else{Color::Rgb(70, 80, 50)};
|
let border_color = if *focused{Color::Rgb(100, 110, 40)}else{Color::Rgb(70, 80, 50)};
|
||||||
let title_color = if focused{Color::Rgb(150, 160, 90)}else{Color::Rgb(120, 130, 100)};
|
let title_color = if *focused{Color::Rgb(150, 160, 90)}else{Color::Rgb(120, 130, 100)};
|
||||||
let border = Lozenge(Style::default().bg(Color::Rgb(40, 50, 30)).fg(border_color));
|
let border = Lozenge(Style::default().bg(Color::Rgb(40, 50, 30)).fg(border_color));
|
||||||
let note_area = lay!(notes, cursor).fill_x();
|
let note_area = lay!(notes, cursor).fill_x();
|
||||||
let piano_roll = row!(keys, note_area).fill_x();
|
let piano_roll = row!(keys, note_area).fill_x();
|
||||||
let content = piano_roll.bg(Color::Rgb(40, 50, 30)).border(border);
|
let content = piano_roll.bg(Color::Rgb(40, 50, 30)).border(border);
|
||||||
let content = lay!(content, playhead);
|
let content = lay!(content, playhead);
|
||||||
let mut upper_left = format!("[{}] Sequencer", if entered {"■"} else {" "});
|
let mut upper_left = format!("[{}] Sequencer", if *entered {"■"} else {" "});
|
||||||
if let Some(phrase) = phrase {
|
if let Some(phrase) = phrase {
|
||||||
upper_left = format!("{upper_left}: {}", phrase.read().unwrap().name);
|
upper_left = format!("{upper_left}: {}", phrase.read().unwrap().name);
|
||||||
}
|
}
|
||||||
|
|
@ -121,10 +133,10 @@ impl<'a, T: PhraseViewState> Content for PhraseView<'a, T> {
|
||||||
//time_start, time_point.unwrap_or(0),
|
//time_start, time_point.unwrap_or(0),
|
||||||
//time_scale, time_clamp.unwrap_or(0),
|
//time_scale, time_clamp.unwrap_or(0),
|
||||||
//);
|
//);
|
||||||
if focused && entered {
|
if *focused && *entered {
|
||||||
lower_right = format!("┤Note: {} {}├─{lower_right}",
|
lower_right = format!("┤Note: {} {}├─{lower_right}",
|
||||||
note_axis.read().unwrap().point.unwrap(),
|
note_axis.read().unwrap().point.unwrap(),
|
||||||
pulses_to_name(note_len));
|
pulses_to_name(*note_len));
|
||||||
//lower_right = format!("Note: {} (+{}:{}|{}) {upper_right}",
|
//lower_right = format!("Note: {} (+{}:{}|{}) {upper_right}",
|
||||||
//pulses_to_name(*note_len),
|
//pulses_to_name(*note_len),
|
||||||
//note_start,
|
//note_start,
|
||||||
|
|
@ -146,74 +158,6 @@ impl<'a, T: PhraseViewState> Content for PhraseView<'a, T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait PhraseViewState: Send + Sync {
|
|
||||||
fn phrase_editing (&self) -> &Option<Arc<RwLock<Phrase>>>;
|
|
||||||
fn phrase_editor_focused (&self) -> bool;
|
|
||||||
fn phrase_editor_size (&self) -> &Measure<Tui>;
|
|
||||||
fn phrase_editor_entered (&self) -> bool;
|
|
||||||
fn keys (&self) -> &Buffer;
|
|
||||||
fn buffer (&self) -> &BigBuffer;
|
|
||||||
fn note_len (&self) -> usize;
|
|
||||||
fn note_axis (&self) -> &RwLock<FixedAxis<usize>>;
|
|
||||||
fn time_axis (&self) -> &RwLock<ScaledAxis<usize>>;
|
|
||||||
fn now (&self) -> &Arc<Pulse>;
|
|
||||||
fn size (&self) -> &Measure<Tui>;
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! impl_phrase_view_state {
|
|
||||||
($Struct:ident $(:: $field:ident)* [$self1:ident : $focused:expr] [$self2:ident : $entered:expr]) => {
|
|
||||||
impl PhraseViewState for $Struct {
|
|
||||||
fn phrase_editing (&self) -> &Option<Arc<RwLock<Phrase>>> {
|
|
||||||
&self$(.$field)*.phrase
|
|
||||||
}
|
|
||||||
fn phrase_editor_focused (&$self1) -> bool {
|
|
||||||
$focused
|
|
||||||
//self$(.$field)*.focus.is_focused()
|
|
||||||
}
|
|
||||||
fn phrase_editor_entered (&$self2) -> bool {
|
|
||||||
$entered
|
|
||||||
//self$(.$field)*.focus.is_entered()
|
|
||||||
}
|
|
||||||
fn phrase_editor_size (&self) -> &Measure<Tui> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn keys (&self) -> &Buffer {
|
|
||||||
&self$(.$field)*.keys
|
|
||||||
}
|
|
||||||
fn buffer (&self) -> &BigBuffer {
|
|
||||||
&self$(.$field)*.buffer
|
|
||||||
}
|
|
||||||
fn note_len (&self) -> usize {
|
|
||||||
self$(.$field)*.note_len
|
|
||||||
}
|
|
||||||
fn note_axis (&self) -> &RwLock<FixedAxis<usize>> {
|
|
||||||
&self$(.$field)*.note_axis
|
|
||||||
}
|
|
||||||
fn time_axis (&self) -> &RwLock<ScaledAxis<usize>> {
|
|
||||||
&self$(.$field)*.time_axis
|
|
||||||
}
|
|
||||||
fn now (&self) -> &Arc<Pulse> {
|
|
||||||
&self$(.$field)*.now
|
|
||||||
}
|
|
||||||
fn size (&self) -> &Measure<Tui> {
|
|
||||||
&self$(.$field)*.size
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl_phrase_view_state!(PhraseEditorModel
|
|
||||||
[self: true]
|
|
||||||
[self: true]
|
|
||||||
);
|
|
||||||
impl_phrase_view_state!(SequencerTui::editor
|
|
||||||
[self: self.focused() == AppFocus::Content(SequencerFocus::PhraseEditor)]
|
|
||||||
[self: self.entered() && self.focused() == AppFocus::Content(SequencerFocus::PhraseEditor)]
|
|
||||||
);
|
|
||||||
impl_phrase_view_state!(ArrangerTui::editor
|
|
||||||
[self: self.focused() == AppFocus::Content(ArrangerFocus::PhraseEditor)]
|
|
||||||
[self: self.entered() && self.focused() == AppFocus::Content(ArrangerFocus::PhraseEditor)])
|
|
||||||
;
|
|
||||||
|
|
||||||
/// Colors of piano keys
|
/// Colors of piano keys
|
||||||
const KEY_COLORS: [(Color, Color);6] = [
|
const KEY_COLORS: [(Color, Color);6] = [
|
||||||
(Color::Rgb(255, 255, 255), Color::Rgb(255, 255, 255)),
|
(Color::Rgb(255, 255, 255), Color::Rgb(255, 255, 255)),
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ impl Content for SequencerTui {
|
||||||
TransportView::from(self),
|
TransportView::from(self),
|
||||||
Split::right(20,
|
Split::right(20,
|
||||||
widget(&PhrasesView(self)),
|
widget(&PhrasesView(self)),
|
||||||
widget(&PhraseView(self)),
|
widget(&PhraseView2::from(self)),
|
||||||
).min_y(20)
|
).min_y(20)
|
||||||
),
|
),
|
||||||
self.perf.percentage()
|
self.perf.percentage()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue