mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
wip(p65,e2)
This commit is contained in:
parent
fffd830e15
commit
e768790658
9 changed files with 226 additions and 299 deletions
|
|
@ -1,5 +1,47 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub enum FocusState<T: Copy + Debug + PartialEq> {
|
||||||
|
Exited(T),
|
||||||
|
Focused(T),
|
||||||
|
Entered(T),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Copy + Debug + PartialEq> FocusState<T> {
|
||||||
|
pub fn inner (&self) -> T {
|
||||||
|
match self {
|
||||||
|
Self::Exited(inner) => *inner,
|
||||||
|
Self::Focused(inner) => *inner,
|
||||||
|
Self::Entered(inner) => *inner,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn set_inner (&mut self, inner: T) {
|
||||||
|
*self = match self {
|
||||||
|
Self::Exited(_) => Self::Exited(inner),
|
||||||
|
Self::Focused(_) => Self::Focused(inner),
|
||||||
|
Self::Entered(_) => Self::Entered(inner),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn is_exited (&self) -> bool {
|
||||||
|
if let Self::Exited(_) = self { true } else { false }
|
||||||
|
}
|
||||||
|
pub fn is_focused (&self) -> bool {
|
||||||
|
if let Self::Focused(_) = self { true } else { false }
|
||||||
|
}
|
||||||
|
pub fn is_entered (&self) -> bool {
|
||||||
|
if let Self::Entered(_) = self { true } else { false }
|
||||||
|
}
|
||||||
|
pub fn to_exited (&mut self) {
|
||||||
|
*self = Self::Exited(self.inner())
|
||||||
|
}
|
||||||
|
pub fn to_focused (&mut self) {
|
||||||
|
*self = Self::Focused(self.inner())
|
||||||
|
}
|
||||||
|
pub fn to_entered (&mut self) {
|
||||||
|
*self = Self::Entered(self.inner())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||||
pub enum FocusCommand {
|
pub enum FocusCommand {
|
||||||
Next,
|
Next,
|
||||||
|
|
|
||||||
|
|
@ -170,9 +170,9 @@ impl<'a, T: PhrasesViewState> Content for PhrasesView<'a, T> {
|
||||||
impl<'a, T: PhraseViewState> Content for PhraseView<'a, T> {
|
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();
|
let phrase = self.0.phrase_editing();
|
||||||
let size = self.0.size();
|
let size = self.0.size();
|
||||||
let focused = self.0.phrase_focused();
|
let focused = self.0.phrase_editor_focused();
|
||||||
let entered = self.0.phrase_editor_entered();
|
let entered = self.0.phrase_editor_entered();
|
||||||
let keys = self.0.keys();
|
let keys = self.0.keys();
|
||||||
let buffer = self.0.buffer();
|
let buffer = self.0.buffer();
|
||||||
|
|
|
||||||
|
|
@ -6,19 +6,19 @@ pub trait TransportControl: ClockApi {
|
||||||
|
|
||||||
impl TransportControl for TransportTui {
|
impl TransportControl for TransportTui {
|
||||||
fn transport_focused (&self) -> TransportFocus {
|
fn transport_focused (&self) -> TransportFocus {
|
||||||
self.state.focus
|
self.state.focus.inner()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransportControl for SequencerTui {
|
impl TransportControl for SequencerTui {
|
||||||
fn transport_focused (&self) -> TransportFocus {
|
fn transport_focused (&self) -> TransportFocus {
|
||||||
self.transport.focus
|
self.transport.focus.inner()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransportControl for ArrangerTui {
|
impl TransportControl for ArrangerTui {
|
||||||
fn transport_focused (&self) -> TransportFocus {
|
fn transport_focused (&self) -> TransportFocus {
|
||||||
self.transport.focus
|
self.transport.focus.inner()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,13 @@
|
||||||
|
// Not all fields are included here. Add as needed.
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
impl std::fmt::Debug for TransportModel {
|
use std::fmt::{Debug, Formatter, Error};
|
||||||
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
type DebugResult = std::result::Result<(), Error>;
|
||||||
f.debug_struct("TransportModel")
|
|
||||||
|
impl Debug for ClockModel {
|
||||||
|
fn fmt (&self, f: &mut Formatter<'_>) -> DebugResult {
|
||||||
|
f.debug_struct("editor")
|
||||||
.field("playing", &self.playing)
|
.field("playing", &self.playing)
|
||||||
.field("started", &self.started)
|
.field("started", &self.started)
|
||||||
.field("current", &self.current)
|
.field("current", &self.current)
|
||||||
|
|
@ -12,8 +17,17 @@ impl std::fmt::Debug for TransportModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for TransportTui {
|
impl Debug for TransportModel {
|
||||||
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
fn fmt (&self, f: &mut Formatter<'_>) -> DebugResult {
|
||||||
|
f.debug_struct("TransportModel")
|
||||||
|
.field("clock", &self.clock)
|
||||||
|
.field("focus", &self.focus)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for TransportTui {
|
||||||
|
fn fmt (&self, f: &mut Formatter<'_>) -> DebugResult {
|
||||||
f.debug_struct("Measure")
|
f.debug_struct("Measure")
|
||||||
.field("jack", &self.jack)
|
.field("jack", &self.jack)
|
||||||
.field("state", &self.state)
|
.field("state", &self.state)
|
||||||
|
|
@ -23,11 +37,21 @@ impl std::fmt::Debug for TransportTui {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for PhraseEditorModel {
|
impl Debug for PhraseEditorModel {
|
||||||
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
fn fmt (&self, f: &mut Formatter<'_>) -> DebugResult {
|
||||||
f.debug_struct("editor")
|
f.debug_struct("editor")
|
||||||
.field("note_axis", &self.time_axis)
|
.field("note_axis", &self.time_axis)
|
||||||
.field("time_axis", &self.note_axis)
|
.field("time_axis", &self.note_axis)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Debug for PhrasePlayerModel {
|
||||||
|
fn fmt (&self, f: &mut Formatter<'_>) -> DebugResult {
|
||||||
|
f.debug_struct("editor")
|
||||||
|
.field("clock", &self.clock)
|
||||||
|
.field("play_phrase", &self.play_phrase)
|
||||||
|
.field("next_phrase", &self.next_phrase)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -174,18 +174,23 @@ macro_rules! impl_phrase_editor_control {
|
||||||
impl_jack_api!(TransportTui::jack);
|
impl_jack_api!(TransportTui::jack);
|
||||||
impl_jack_api!(SequencerTui::jack);
|
impl_jack_api!(SequencerTui::jack);
|
||||||
impl_jack_api!(ArrangerTui::jack);
|
impl_jack_api!(ArrangerTui::jack);
|
||||||
impl_clock_api!(TransportTui::state);
|
|
||||||
impl_clock_api!(SequencerTui::transport);
|
impl_clock_api!(TransportTui::state::clock);
|
||||||
impl_clock_api!(ArrangerTui::transport);
|
impl_clock_api!(SequencerTui::transport::clock);
|
||||||
impl_clock_api!(PhrasePlayerModel);
|
impl_clock_api!(ArrangerTui::transport::clock);
|
||||||
impl_clock_api!(ArrangerTrack::player);
|
impl_clock_api!(PhrasePlayerModel::clock);
|
||||||
|
impl_clock_api!(ArrangerTrack::player::clock);
|
||||||
|
|
||||||
impl_has_phrases!(PhrasesModel);
|
impl_has_phrases!(PhrasesModel);
|
||||||
impl_has_phrases!(SequencerTui::phrases);
|
impl_has_phrases!(SequencerTui::phrases);
|
||||||
impl_has_phrases!(ArrangerTui::phrases);
|
impl_has_phrases!(ArrangerTui::phrases);
|
||||||
|
|
||||||
impl_midi_player!(SequencerTui::player);
|
impl_midi_player!(SequencerTui::player);
|
||||||
impl_midi_player!(ArrangerTrack::player);
|
impl_midi_player!(ArrangerTrack::player);
|
||||||
impl_midi_player!(PhrasePlayerModel);
|
impl_midi_player!(PhrasePlayerModel);
|
||||||
|
|
||||||
impl_phrases_control!(SequencerTui);
|
impl_phrases_control!(SequencerTui);
|
||||||
impl_phrases_control!(ArrangerTui);
|
impl_phrases_control!(ArrangerTui);
|
||||||
|
|
||||||
impl_phrase_editor_control!(SequencerTui [SequencerFocus::PhraseEditor]);
|
impl_phrase_editor_control!(SequencerTui [SequencerFocus::PhraseEditor]);
|
||||||
impl_phrase_editor_control!(ArrangerTui [ArrangerFocus::PhraseEditor]);
|
impl_phrase_editor_control!(ArrangerTui [ArrangerFocus::PhraseEditor]);
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ impl TryFrom<&Arc<RwLock<JackClient>>> for TransportTui {
|
||||||
fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
|
fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
jack: jack.clone(),
|
jack: jack.clone(),
|
||||||
state: TransportModel::from(jack.read().unwrap().transport()),
|
state: TransportModel::from(&Arc::new(jack.read().unwrap().transport())),
|
||||||
size: Measure::new(),
|
size: Measure::new(),
|
||||||
cursor: (0, 0),
|
cursor: (0, 0),
|
||||||
})
|
})
|
||||||
|
|
@ -16,11 +16,11 @@ impl TryFrom<&Arc<RwLock<JackClient>>> for TransportTui {
|
||||||
impl TryFrom<&Arc<RwLock<JackClient>>> for SequencerTui {
|
impl TryFrom<&Arc<RwLock<JackClient>>> for SequencerTui {
|
||||||
type Error = Box<dyn std::error::Error>;
|
type Error = Box<dyn std::error::Error>;
|
||||||
fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
|
fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
|
||||||
|
let transport = TransportModel::from(&Arc::new(jack.read().unwrap().transport()));
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
jack: jack.clone(),
|
jack: jack.clone(),
|
||||||
transport: TransportModel::from(jack.read().unwrap().transport()),
|
|
||||||
phrases: PhrasesModel::default(),
|
phrases: PhrasesModel::default(),
|
||||||
player: PhrasePlayerModel::default(),
|
player: PhrasePlayerModel::from(&transport.clock),
|
||||||
editor: PhraseEditorModel::default(),
|
editor: PhraseEditorModel::default(),
|
||||||
size: Measure::new(),
|
size: Measure::new(),
|
||||||
cursor: (0, 0),
|
cursor: (0, 0),
|
||||||
|
|
@ -28,6 +28,7 @@ impl TryFrom<&Arc<RwLock<JackClient>>> for SequencerTui {
|
||||||
split: 20,
|
split: 20,
|
||||||
midi_buf: vec![],
|
midi_buf: vec![],
|
||||||
note_buf: vec![],
|
note_buf: vec![],
|
||||||
|
transport,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -37,7 +38,7 @@ impl TryFrom<&Arc<RwLock<JackClient>>> for ArrangerTui {
|
||||||
fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
|
fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
jack: jack.clone(),
|
jack: jack.clone(),
|
||||||
transport: TransportModel::from(jack.read().unwrap().transport()),
|
transport: TransportModel::from(&Arc::new(jack.read().unwrap().transport())),
|
||||||
phrases: PhrasesModel::default(),
|
phrases: PhrasesModel::default(),
|
||||||
editor: PhraseEditorModel::default(),
|
editor: PhraseEditorModel::default(),
|
||||||
selected: ArrangerSelection::Clip(0, 0),
|
selected: ArrangerSelection::Clip(0, 0),
|
||||||
|
|
@ -58,4 +59,3 @@ impl TryFrom<&Arc<RwLock<JackClient>>> for ArrangerTui {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -112,7 +112,7 @@ impl InputToCommand<Tui, ArrangerTui> for ArrangerCommand {
|
||||||
PhraseCommand::input_to_command(state, input)?
|
PhraseCommand::input_to_command(state, input)?
|
||||||
),
|
),
|
||||||
ArrangerFocus::Phrases => match input.event() {
|
ArrangerFocus::Phrases => match input.event() {
|
||||||
key!(KeyCode::Char('e')) => EditPhrase(state.phrase().clone()),
|
key!(KeyCode::Char('e')) => EditPhrase(state.phrase_editing().clone()),
|
||||||
_ => Phrases(PhrasesCommand::input_to_command(state, input)?)
|
_ => Phrases(PhrasesCommand::input_to_command(state, input)?)
|
||||||
},
|
},
|
||||||
ArrangerFocus::Arranger => {
|
ArrangerFocus::Arranger => {
|
||||||
|
|
@ -121,7 +121,7 @@ impl InputToCommand<Tui, ArrangerTui> for ArrangerCommand {
|
||||||
use ArrangerClipCommand as Clip;
|
use ArrangerClipCommand as Clip;
|
||||||
use ArrangerSceneCommand as Scene;
|
use ArrangerSceneCommand as Scene;
|
||||||
match input.event() {
|
match input.event() {
|
||||||
key!(KeyCode::Char('e')) => EditPhrase(state.phrase().clone()),
|
key!(KeyCode::Char('e')) => EditPhrase(state.phrase_editing().clone()),
|
||||||
_ => match input.event() {
|
_ => match input.event() {
|
||||||
// FIXME: boundary conditions
|
// FIXME: boundary conditions
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,56 +1,57 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub struct TransportModel {
|
#[derive(Clone)]
|
||||||
|
pub struct ClockModel {
|
||||||
/// JACK transport handle.
|
/// JACK transport handle.
|
||||||
pub(crate) transport: Arc<Transport>,
|
pub(crate) transport: Arc<Transport>,
|
||||||
/// Playback state
|
/// Playback state
|
||||||
pub(crate) playing: Arc<RwLock<Option<TransportState>>>,
|
pub(crate) playing: Arc<RwLock<Option<TransportState>>>,
|
||||||
/// Global sample and usec at which playback started
|
/// Global sample and usec at which playback started
|
||||||
pub(crate) started: Arc<RwLock<Option<(usize, usize)>>>,
|
pub(crate) started: Arc<RwLock<Option<(usize, usize)>>>,
|
||||||
/// Current moment in time
|
/// Current moment in time
|
||||||
pub(crate) current: Arc<Instant>,
|
pub(crate) current: Arc<Instant>,
|
||||||
/// Note quantization factor
|
/// Note quantization factor
|
||||||
pub(crate) quant: Arc<Quantize>,
|
pub(crate) quant: Arc<Quantize>,
|
||||||
/// Launch quantization factor
|
/// Launch quantization factor
|
||||||
pub(crate) sync: Arc<LaunchSync>,
|
pub(crate) sync: Arc<LaunchSync>,
|
||||||
/// Enable metronome?
|
/// TODO: Enable metronome?
|
||||||
pub(crate) metronome: bool,
|
pub(crate) metronome: bool,
|
||||||
/// Selected transport component
|
|
||||||
pub(crate) focus: TransportFocus,
|
|
||||||
/// Whether the transport is focused
|
|
||||||
pub(crate) is_focused: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<jack::Transport> for TransportModel {
|
impl From<&Arc<Transport>> for ClockModel {
|
||||||
fn from (transport: Transport) -> Self {
|
fn from (transport: &Arc<Transport>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
current: Instant::default().into(),
|
current: Instant::default().into(),
|
||||||
focus: TransportFocus::PlayPause,
|
playing: RwLock::new(None).into(),
|
||||||
is_focused: true,
|
quant: Quantize::default().into(),
|
||||||
metronome: false,
|
started: RwLock::new(None).into(),
|
||||||
playing: RwLock::new(None).into(),
|
sync: LaunchSync::default().into(),
|
||||||
quant: Quantize::default().into(),
|
transport: transport.clone(),
|
||||||
started: RwLock::new(None).into(),
|
metronome: false,
|
||||||
sync: LaunchSync::default().into(),
|
}
|
||||||
transport: transport.into(),
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TransportModel {
|
||||||
|
/// State of clock and playhead
|
||||||
|
pub(crate) clock: ClockModel,
|
||||||
|
/// Whether the transport is focused and which part of it is selected
|
||||||
|
pub(crate) focus: FocusState<TransportFocus>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Arc<Transport>> for TransportModel {
|
||||||
|
fn from (transport: &Arc<Transport>) -> Self {
|
||||||
|
Self {
|
||||||
|
clock: ClockModel::from(transport),
|
||||||
|
focus: FocusState::Exited(TransportFocus::PlayPause),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Contains state for playing a phrase
|
/// Contains state for playing a phrase
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct PhrasePlayerModel {
|
pub struct PhrasePlayerModel {
|
||||||
/// Playback state
|
/// State of clock and playhead
|
||||||
pub(crate) playing: Arc<RwLock<Option<TransportState>>>,
|
pub(crate) clock: ClockModel,
|
||||||
/// Global sample and usec at which playback started
|
|
||||||
pub(crate) started: Arc<RwLock<Option<(usize, usize)>>>,
|
|
||||||
/// Current moment in time
|
|
||||||
pub(crate) current: Arc<Instant>,
|
|
||||||
/// Note quantization factor
|
|
||||||
pub(crate) quant: Arc<Quantize>,
|
|
||||||
/// Launch quantization factor
|
|
||||||
pub(crate) sync: Arc<LaunchSync>,
|
|
||||||
|
|
||||||
/// Start time and phrase being played
|
/// Start time and phrase being played
|
||||||
pub(crate) play_phrase: Option<(Instant, Option<Arc<RwLock<Phrase>>>)>,
|
pub(crate) play_phrase: Option<(Instant, Option<Arc<RwLock<Phrase>>>)>,
|
||||||
/// Start time and next phrase
|
/// Start time and next phrase
|
||||||
|
|
@ -73,14 +74,10 @@ pub struct PhrasePlayerModel {
|
||||||
pub(crate) notes_out: Arc<RwLock<[bool; 128]>>,
|
pub(crate) notes_out: Arc<RwLock<[bool; 128]>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PhrasePlayerModel {
|
impl From<&ClockModel> for PhrasePlayerModel {
|
||||||
fn default () -> Self {
|
fn from (clock: &ClockModel) -> Self {
|
||||||
Self {
|
Self {
|
||||||
playing: RwLock::new(None).into(),
|
clock: clock.clone(),
|
||||||
started: RwLock::new(None).into(),
|
|
||||||
current: Instant::default().into(),
|
|
||||||
quant: Quantize::default().into(),
|
|
||||||
sync: LaunchSync::default().into(),
|
|
||||||
midi_ins: vec![],
|
midi_ins: vec![],
|
||||||
midi_outs: vec![],
|
midi_outs: vec![],
|
||||||
reset: true,
|
reset: true,
|
||||||
|
|
@ -95,19 +92,6 @@ impl Default for PhrasePlayerModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&TransportModel> for PhrasePlayerModel {
|
|
||||||
fn from (transport: &TransportModel) -> Self {
|
|
||||||
Self {
|
|
||||||
playing: transport.playing.clone(),
|
|
||||||
started: transport.started.clone(),
|
|
||||||
current: transport.current.clone(),
|
|
||||||
quant: transport.quant.clone(),
|
|
||||||
sync: transport.sync.clone(),
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Contains state for viewing and editing a phrase
|
/// Contains state for viewing and editing a phrase
|
||||||
pub struct PhraseEditorModel {
|
pub struct PhraseEditorModel {
|
||||||
/// Phrase being played
|
/// Phrase being played
|
||||||
|
|
@ -123,9 +107,7 @@ pub struct PhraseEditorModel {
|
||||||
/// Cursor/scroll/zoom in time axis
|
/// Cursor/scroll/zoom in time axis
|
||||||
pub(crate) time_axis: RwLock<ScaledAxis<usize>>,
|
pub(crate) time_axis: RwLock<ScaledAxis<usize>>,
|
||||||
/// Whether this widget is focused
|
/// Whether this widget is focused
|
||||||
pub(crate) focused: bool,
|
pub(crate) focus: FocusState<()>,
|
||||||
/// Whether note enter mode is enabled
|
|
||||||
pub(crate) entered: bool,
|
|
||||||
/// Display mode
|
/// Display mode
|
||||||
pub(crate) mode: bool,
|
pub(crate) mode: bool,
|
||||||
/// Notes currently held at input
|
/// Notes currently held at input
|
||||||
|
|
@ -145,8 +127,7 @@ impl Default for PhraseEditorModel {
|
||||||
note_len: 24,
|
note_len: 24,
|
||||||
keys: keys_vert(),
|
keys: keys_vert(),
|
||||||
buffer: Default::default(),
|
buffer: Default::default(),
|
||||||
focused: false,
|
focus: FocusState::Exited(()),
|
||||||
entered: false,
|
|
||||||
mode: false,
|
mode: false,
|
||||||
notes_in: RwLock::new([false;128]).into(),
|
notes_in: RwLock::new([false;128]).into(),
|
||||||
notes_out: RwLock::new([false;128]).into(),
|
notes_out: RwLock::new([false;128]).into(),
|
||||||
|
|
@ -178,9 +159,7 @@ pub struct PhrasesModel {
|
||||||
/// Mode switch
|
/// Mode switch
|
||||||
pub(crate) mode: Option<PhrasesMode>,
|
pub(crate) mode: Option<PhrasesMode>,
|
||||||
/// Whether this widget is focused
|
/// Whether this widget is focused
|
||||||
pub(crate) focused: bool,
|
pub(crate) focus: FocusState<()>,
|
||||||
/// Whether this widget is entered
|
|
||||||
pub(crate) entered: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PhrasesModel {
|
impl Default for PhrasesModel {
|
||||||
|
|
@ -190,8 +169,7 @@ impl Default for PhrasesModel {
|
||||||
phrase: 0.into(),
|
phrase: 0.into(),
|
||||||
scroll: 0,
|
scroll: 0,
|
||||||
mode: None,
|
mode: None,
|
||||||
focused: false,
|
focus: FocusState::Exited(()),
|
||||||
entered: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -264,7 +242,7 @@ impl ArrangerTracksApi<ArrangerTrack> for ArrangerTui {
|
||||||
width: name.len() + 2,
|
width: name.len() + 2,
|
||||||
name: Arc::new(name.into()),
|
name: Arc::new(name.into()),
|
||||||
color: color.unwrap_or_else(||ItemColor::random()),
|
color: color.unwrap_or_else(||ItemColor::random()),
|
||||||
player: PhrasePlayerModel::default(),
|
player: PhrasePlayerModel::from(&self.transport.clock),
|
||||||
};
|
};
|
||||||
self.tracks_mut().push(track);
|
self.tracks_mut().push(track);
|
||||||
let index = self.tracks().len() - 1;
|
let index = self.tracks().len() - 1;
|
||||||
|
|
@ -353,16 +331,3 @@ impl PhraseLength {
|
||||||
format!("{:>02}", self.ticks())
|
format!("{:>02}", self.ticks())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PhrasesModel {
|
|
||||||
pub fn new (phrases: Vec<Arc<RwLock<Phrase>>>) -> Self {
|
|
||||||
Self {
|
|
||||||
scroll: 0,
|
|
||||||
phrase: 0.into(),
|
|
||||||
mode: None,
|
|
||||||
focused: false,
|
|
||||||
entered: false,
|
|
||||||
phrases,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,30 @@ pub trait TransportViewState: ClockApi + Send + Sync {
|
||||||
self.current().usec.format_msu()
|
self.current().usec.format_msu()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
macro_rules! impl_transport_view_state {
|
||||||
|
($Struct:ident :: $field:ident) => {
|
||||||
|
impl TransportViewState for $Struct {
|
||||||
|
fn transport_selected (&self) -> TransportFocus {
|
||||||
|
self.$field.focus.inner()
|
||||||
|
}
|
||||||
|
fn transport_focused (&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl_transport_view_state!(TransportTui::state);
|
||||||
|
impl_transport_view_state!(SequencerTui::transport);
|
||||||
|
impl_transport_view_state!(ArrangerTui::transport);
|
||||||
|
|
||||||
pub trait ArrangerViewState {
|
pub trait ArrangerViewState {
|
||||||
fn arranger_focused (&self) -> bool;
|
fn arranger_focused (&self) -> bool;
|
||||||
}
|
}
|
||||||
|
impl ArrangerViewState for ArrangerTui {
|
||||||
|
fn arranger_focused (&self) -> bool {
|
||||||
|
self.focused() == ArrangerFocus::Arranger
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait PhrasesViewState: Send + Sync {
|
pub trait PhrasesViewState: Send + Sync {
|
||||||
fn phrases_focused (&self) -> bool;
|
fn phrases_focused (&self) -> bool;
|
||||||
|
|
@ -34,10 +54,34 @@ pub trait PhrasesViewState: Send + Sync {
|
||||||
fn phrase_index (&self) -> usize;
|
fn phrase_index (&self) -> usize;
|
||||||
fn phrase_mode (&self) -> &Option<PhrasesMode>;
|
fn phrase_mode (&self) -> &Option<PhrasesMode>;
|
||||||
}
|
}
|
||||||
|
macro_rules! impl_phrases_view_state {
|
||||||
|
($Struct:ident $(:: $field:ident)*) => {
|
||||||
|
impl PhrasesViewState for $Struct {
|
||||||
|
fn phrases_focused (&self) -> bool {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn phrases_entered (&self) -> bool {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn phrases (&self) -> Vec<Arc<RwLock<Phrase>>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn phrase_index (&self) -> usize {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn phrase_mode (&self) -> &Option<PhrasesMode> {
|
||||||
|
&self$(.$field)*.mode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl_phrases_view_state!(PhrasesModel);
|
||||||
|
impl_phrases_view_state!(SequencerTui::phrases);
|
||||||
|
impl_phrases_view_state!(ArrangerTui::phrases);
|
||||||
|
|
||||||
pub trait PhraseViewState: Send + Sync {
|
pub trait PhraseViewState: Send + Sync {
|
||||||
fn phrase (&self) -> &Option<Arc<RwLock<Phrase>>>;
|
fn phrase_editing (&self) -> &Option<Arc<RwLock<Phrase>>>;
|
||||||
fn phrase_focused (&self) -> bool;
|
fn phrase_editor_focused (&self) -> bool;
|
||||||
fn phrase_editor_size (&self) -> &Measure<Tui>;
|
fn phrase_editor_size (&self) -> &Measure<Tui>;
|
||||||
fn phrase_editor_entered (&self) -> bool;
|
fn phrase_editor_entered (&self) -> bool;
|
||||||
fn keys (&self) -> &Buffer;
|
fn keys (&self) -> &Buffer;
|
||||||
|
|
@ -48,201 +92,48 @@ pub trait PhraseViewState: Send + Sync {
|
||||||
fn now (&self) -> &Arc<Pulse>;
|
fn now (&self) -> &Arc<Pulse>;
|
||||||
fn size (&self) -> &Measure<Tui>;
|
fn size (&self) -> &Measure<Tui>;
|
||||||
}
|
}
|
||||||
|
macro_rules! impl_phrase_view_state {
|
||||||
impl TransportViewState for TransportTui {
|
($Struct:ident $(:: $field:ident)*) => {
|
||||||
fn transport_selected (&self) -> TransportFocus {
|
impl PhraseViewState for $Struct {
|
||||||
self.state.focus
|
fn phrase_editing (&self) -> &Option<Arc<RwLock<Phrase>>> {
|
||||||
}
|
&self$(.$field)*.phrase
|
||||||
fn transport_focused (&self) -> bool {
|
}
|
||||||
true
|
fn phrase_editor_focused (&self) -> bool {
|
||||||
}
|
self$(.$field)*.focus.is_focused()
|
||||||
}
|
}
|
||||||
|
fn phrase_editor_size (&self) -> &Measure<Tui> {
|
||||||
impl TransportViewState for SequencerTui {
|
todo!()
|
||||||
fn transport_selected (&self) -> TransportFocus {
|
}
|
||||||
self.transport.focus
|
fn phrase_editor_entered (&self) -> bool {
|
||||||
}
|
self$(.$field)*.focus.is_entered()
|
||||||
fn transport_focused (&self) -> bool {
|
}
|
||||||
self.focused() == SequencerFocus::Transport
|
fn keys (&self) -> &Buffer {
|
||||||
}
|
&self$(.$field)*.keys
|
||||||
}
|
}
|
||||||
|
fn buffer (&self) -> &BigBuffer {
|
||||||
impl TransportViewState for ArrangerTui {
|
&self$(.$field)*.buffer
|
||||||
fn transport_selected (&self) -> TransportFocus {
|
}
|
||||||
self.transport.focus
|
fn note_len (&self) -> usize {
|
||||||
}
|
self$(.$field)*.note_len
|
||||||
fn transport_focused (&self) -> bool {
|
}
|
||||||
self.focused() == ArrangerFocus::Transport
|
fn note_axis (&self) -> &RwLock<FixedAxis<usize>> {
|
||||||
}
|
&self$(.$field)*.note_axis
|
||||||
}
|
}
|
||||||
|
fn time_axis (&self) -> &RwLock<ScaledAxis<usize>> {
|
||||||
impl ArrangerViewState for ArrangerTui {
|
&self$(.$field)*.time_axis
|
||||||
fn arranger_focused (&self) -> bool {
|
}
|
||||||
self.focused() == ArrangerFocus::Arranger
|
fn now (&self) -> &Arc<Pulse> {
|
||||||
}
|
&self$(.$field)*.now
|
||||||
}
|
}
|
||||||
|
fn size (&self) -> &Measure<Tui> {
|
||||||
impl PhrasesViewState for PhrasesModel {
|
&self$(.$field)*.size
|
||||||
fn phrases_focused (&self) -> bool {
|
}
|
||||||
todo!()
|
}
|
||||||
}
|
|
||||||
fn phrases_entered (&self) -> bool {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn phrases (&self) -> Vec<Arc<RwLock<Phrase>>> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn phrase_index (&self) -> usize {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn phrase_mode (&self) -> &Option<PhrasesMode> {
|
|
||||||
&self.mode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PhrasesViewState for SequencerTui {
|
|
||||||
fn phrases_focused (&self) -> bool {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn phrases_entered (&self) -> bool {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn phrases (&self) -> Vec<Arc<RwLock<Phrase>>> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn phrase_index (&self) -> usize {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn phrase_mode (&self) -> &Option<PhrasesMode> {
|
|
||||||
&self.phrases.mode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PhrasesViewState for ArrangerTui {
|
|
||||||
fn phrases_focused (&self) -> bool {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn phrases_entered (&self) -> bool {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn phrases (&self) -> Vec<Arc<RwLock<Phrase>>> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn phrase_index (&self) -> usize {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn phrase_mode (&self) -> &Option<PhrasesMode> {
|
|
||||||
&self.phrases.mode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PhraseViewState for PhraseEditorModel {
|
|
||||||
fn phrase (&self) -> &Option<Arc<RwLock<Phrase>>> {
|
|
||||||
&self.phrase
|
|
||||||
}
|
|
||||||
fn phrase_focused (&self) -> bool {
|
|
||||||
self.focused
|
|
||||||
}
|
|
||||||
fn phrase_editor_size (&self) -> &Measure<Tui> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn phrase_editor_entered (&self) -> bool {
|
|
||||||
self.entered
|
|
||||||
}
|
|
||||||
fn keys (&self) -> &Buffer {
|
|
||||||
&self.keys
|
|
||||||
}
|
|
||||||
fn buffer (&self) -> &BigBuffer {
|
|
||||||
&self.buffer
|
|
||||||
}
|
|
||||||
fn note_len (&self) -> usize {
|
|
||||||
self.note_len
|
|
||||||
}
|
|
||||||
fn note_axis (&self) -> &RwLock<FixedAxis<usize>> {
|
|
||||||
&self.note_axis
|
|
||||||
}
|
|
||||||
fn time_axis (&self) -> &RwLock<ScaledAxis<usize>> {
|
|
||||||
&self.time_axis
|
|
||||||
}
|
|
||||||
fn now (&self) -> &Arc<Pulse> {
|
|
||||||
&self.now
|
|
||||||
}
|
|
||||||
fn size (&self) -> &Measure<Tui> {
|
|
||||||
&self.size
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PhraseViewState for SequencerTui {
|
|
||||||
fn phrase (&self) -> &Option<Arc<RwLock<Phrase>>> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn phrase_focused (&self) -> bool {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn phrase_editor_size (&self) -> &Measure<Tui> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn phrase_editor_entered (&self) -> bool {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn keys (&self) -> &Buffer {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn buffer (&self) -> &BigBuffer {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn note_len (&self) -> usize {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn note_axis (&self) -> &RwLock<FixedAxis<usize>> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn time_axis (&self) -> &RwLock<ScaledAxis<usize>> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn now (&self) -> &Arc<Pulse> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn size (&self) -> &Measure<Tui> {
|
|
||||||
&self.size
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PhraseViewState for ArrangerTui {
|
|
||||||
fn phrase (&self) -> &Option<Arc<RwLock<Phrase>>> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn phrase_focused (&self) -> bool {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn phrase_editor_size (&self) -> &Measure<Tui> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn phrase_editor_entered (&self) -> bool {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn keys (&self) -> &Buffer {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn buffer (&self) -> &BigBuffer {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn note_len (&self) -> usize {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn note_axis (&self) -> &RwLock<FixedAxis<usize>> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn time_axis (&self) -> &RwLock<ScaledAxis<usize>> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn now (&self) -> &Arc<Pulse> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn size (&self) -> &Measure<Tui> {
|
|
||||||
&self.size
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl_phrase_view_state!(PhraseEditorModel);
|
||||||
|
impl_phrase_view_state!(SequencerTui::editor);
|
||||||
|
impl_phrase_view_state!(ArrangerTui::editor);
|
||||||
|
|
||||||
fn track_widths (tracks: &[ArrangerTrack]) -> Vec<(usize, usize)> {
|
fn track_widths (tracks: &[ArrangerTrack]) -> Vec<(usize, usize)> {
|
||||||
let mut widths = vec![];
|
let mut widths = vec![];
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue