wip: focus refactor, e13

This commit is contained in:
🪞👃🪞 2024-11-22 02:25:41 +01:00
parent 364769a2e0
commit 6127aa4b30
9 changed files with 110 additions and 65 deletions

View file

@ -34,11 +34,8 @@ pub enum SequencerCommand {
Editor(PhraseCommand), Editor(PhraseCommand),
} }
impl<T> Command<T> for SequencerCommand impl Command<SequencerTui> for SequencerCommand {
where fn execute (self, state: &mut SequencerTui) -> Perhaps<Self> {
T: ClockApi + PhrasesControl + PhraseEditorControl + FocusGrid<Item = SequencerFocus>
{
fn execute (self, state: &mut T) -> Perhaps<Self> {
use SequencerCommand::*; use SequencerCommand::*;
Ok(match self { Ok(match self {
Focus(cmd) => cmd.execute(state)?.map(Focus), Focus(cmd) => cmd.execute(state)?.map(Focus),
@ -258,7 +255,7 @@ pub enum PhraseCommand {
TimeZoomSet(usize), TimeZoomSet(usize),
} }
impl<T: PhraseEditorControl> Command<T> for PhraseCommand { impl<T: PhraseEditorControl + HasEnter> Command<T> for PhraseCommand {
fn execute (self, state: &mut T) -> Perhaps<Self> { fn execute (self, state: &mut T) -> Perhaps<Self> {
use PhraseCommand::*; use PhraseCommand::*;
Ok(match self { Ok(match self {

View file

@ -1,24 +1,36 @@
use crate::*; use crate::*;
pub trait TransportControl: ClockApi { pub trait TransportControl: ClockApi {
fn transport_focused (&self) -> TransportFocus; fn transport_focused (&self) -> Option<TransportFocus>;
} }
impl TransportControl for TransportTui { impl TransportControl for TransportTui {
fn transport_focused (&self) -> TransportFocus { fn transport_focused (&self) -> Option<TransportFocus> {
self.state.focus.inner() if let AppFocus::Content(focus) = self.focus.inner() {
Some(focus)
} else {
None
}
} }
} }
impl TransportControl for SequencerTui { impl TransportControl for SequencerTui {
fn transport_focused (&self) -> TransportFocus { fn transport_focused (&self) -> Option<TransportFocus> {
self.transport.focus.inner() if let AppFocus::Content(SequencerFocus::Transport(focus)) = self.focus.inner() {
Some(focus)
} else {
None
}
} }
} }
impl TransportControl for ArrangerTui { impl TransportControl for ArrangerTui {
fn transport_focused (&self) -> TransportFocus { fn transport_focused (&self) -> Option<TransportFocus> {
self.transport.focus.inner() if let AppFocus::Content(ArrangerFocus::Transport(focus)) = self.focus.inner() {
Some(focus)
} else {
None
}
} }
} }

View file

@ -21,7 +21,6 @@ impl Debug for TransportTui {
fn fmt (&self, f: &mut Formatter<'_>) -> DebugResult { 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("size", &self.size) .field("size", &self.size)
.field("cursor", &self.cursor) .field("cursor", &self.cursor)
.finish() .finish()

View file

@ -118,16 +118,53 @@ impl_focus!(TransportTui TransportFocus [
]); ]);
impl_focus!(SequencerTui SequencerFocus [ impl_focus!(SequencerTui SequencerFocus [
&[Menu, Menu ], &[
&[Content(Transport), Content(Transport) ], Menu,
&[Content(Phrases), Content(PhraseEditor)], Menu,
Menu,
Menu,
Menu,
], &[
Content(Transport(TransportFocus::Bpm)),
Content(Transport(TransportFocus::Sync)),
Content(Transport(TransportFocus::PlayPause)),
Content(Transport(TransportFocus::Clock)),
Content(Transport(TransportFocus::Quant))
], &[
Content(Phrases),
Content(Phrases),
Content(PhraseEditor),
Content(PhraseEditor),
Content(PhraseEditor),
],
]); ]);
impl_focus!(ArrangerTui ArrangerFocus [ impl_focus!(ArrangerTui ArrangerFocus [
&[Menu, Menu ], &[
&[Content(Transport), Content(Transport) ], Menu,
&[Content(Arranger), Content(Arranger) ], Menu,
&[Content(Phrases), Content(PhraseEditor)], Menu,
Menu,
Menu,
], &[
Content(Transport(TransportFocus::Bpm)),
Content(Transport(TransportFocus::Sync)),
Content(Transport(TransportFocus::PlayPause)),
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),
],
]); ]);
/// Focused field of `PhraseLength` /// Focused field of `PhraseLength`

View file

@ -175,9 +175,9 @@ 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::clock); impl_clock_api!(TransportTui::clock);
impl_clock_api!(SequencerTui::transport::clock); impl_clock_api!(SequencerTui::clock);
impl_clock_api!(ArrangerTui::transport::clock); impl_clock_api!(ArrangerTui::clock);
impl_clock_api!(PhrasePlayerModel::clock); impl_clock_api!(PhrasePlayerModel::clock);
impl_clock_api!(ArrangerTrack::player::clock); impl_clock_api!(ArrangerTrack::player::clock);
@ -192,5 +192,9 @@ 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 [
impl_phrase_editor_control!(ArrangerTui [ArrangerFocus::PhraseEditor]); AppFocus::Content(SequencerFocus::PhraseEditor)
]);
impl_phrase_editor_control!(ArrangerTui [
AppFocus::Content(ArrangerFocus::PhraseEditor)
]);

View file

@ -6,7 +6,7 @@ impl<T: TransportControl> InputToCommand<Tui, T> for TransportCommand {
use ClockCommand::{SetBpm, SetQuant, SetSync}; use ClockCommand::{SetBpm, SetQuant, SetSync};
use TransportFocus as Focused; use TransportFocus as Focused;
use TransportCommand::{Focus, Clock}; use TransportCommand::{Focus, Clock};
let focused = state.transport_focused(); let focused = state.transport_focused().unwrap();
Some(match input.event() { Some(match input.event() {
key!(Left) => Focus(FocusCommand::Prev), key!(Left) => Focus(FocusCommand::Prev),
key!(Right) => Focus(FocusCommand::Next), key!(Right) => Focus(FocusCommand::Next),
@ -47,39 +47,35 @@ impl<T: TransportControl> InputToCommand<Tui, T> for TransportCommand {
} }
} }
impl<T> InputToCommand<Tui, T> for SequencerCommand impl InputToCommand<Tui, SequencerTui> for SequencerCommand {
where fn input_to_command (state: &SequencerTui, input: &TuiInput) -> Option<Self> {
T: SequencerControl + TransportControl + PhrasesControl + PhraseEditorControl
+ HasFocus<Item = SequencerFocus>
+ FocusGrid<Item = SequencerFocus>
{
fn input_to_command (state: &T, input: &TuiInput) -> Option<Self> {
use FocusCommand::*; use FocusCommand::*;
use SequencerCommand::*; use SequencerCommand::*;
match input.event() { Some(match input.event() {
key!(KeyCode::Tab) => Some(Self::Focus(Next)), key!(KeyCode::Tab) => Self::Focus(Next),
key!(Shift-KeyCode::Tab) => Some(Self::Focus(Prev)), key!(Shift-KeyCode::Tab) => Self::Focus(Prev),
key!(KeyCode::BackTab) => Some(Self::Focus(Prev)), key!(KeyCode::BackTab) => Self::Focus(Prev),
key!(Shift-KeyCode::BackTab) => Some(Self::Focus(Prev)), key!(Shift-KeyCode::BackTab) => Self::Focus(Prev),
key!(KeyCode::Up) => Some(Self::Focus(Up)), key!(KeyCode::Up) => Self::Focus(Up),
key!(KeyCode::Down) => Some(Self::Focus(Down)), key!(KeyCode::Down) => Self::Focus(Down),
key!(KeyCode::Left) => Some(Self::Focus(Left)), key!(KeyCode::Left) => Self::Focus(Left),
key!(KeyCode::Right) => Some(Self::Focus(Right)), key!(KeyCode::Right) => Self::Focus(Right),
_ => Some(match state.focused() { _ => match state.focused() {
SequencerFocus::Transport => { AppFocus::Menu => todo!(),
AppFocus::Content(SequencerFocus::Transport(_)) => {
use TransportCommand::{Clock, Focus}; use TransportCommand::{Clock, Focus};
match TransportCommand::input_to_command(state, input)? { match TransportCommand::input_to_command(state, input)? {
Clock(command) => { todo!() }, Clock(command) => { todo!() },
Focus(command) => { todo!() }, Focus(command) => { todo!() },
} }
}, },
SequencerFocus::Phrases => AppFocus::Content(SequencerFocus::Phrases) =>
Phrases(PhrasesCommand::input_to_command(state, input)?), Phrases(PhrasesCommand::input_to_command(state, input)?),
SequencerFocus::PhraseEditor => AppFocus::Content(SequencerFocus::PhraseEditor) =>
Editor(PhraseCommand::input_to_command(state, input)?), Editor(PhraseCommand::input_to_command(state, input)?),
_ => return None, _ => return None,
})
} }
})
} }
} }
@ -100,22 +96,22 @@ impl InputToCommand<Tui, ArrangerTui> for ArrangerCommand {
key!(KeyCode::Esc) => Self::Focus(Exit), key!(KeyCode::Esc) => Self::Focus(Exit),
key!(KeyCode::Char(' ')) => Self::Clock(ClockCommand::Play(None)), key!(KeyCode::Char(' ')) => Self::Clock(ClockCommand::Play(None)),
_ => match state.focused() { _ => match state.focused() {
ArrangerFocus::Menu => { todo!() }, AppFocus::Menu => todo!(),
ArrangerFocus::Transport => { AppFocus::Content(ArrangerFocus::Transport(_)) => {
use TransportCommand::{Clock, Focus}; use TransportCommand::{Clock, Focus};
match TransportCommand::input_to_command(state, input)? { match TransportCommand::input_to_command(state, input)? {
Clock(command) => { todo!() }, Clock(command) => { todo!() },
Focus(command) => { todo!() } Focus(command) => { todo!() }
} }
}, },
ArrangerFocus::PhraseEditor => Editor( AppFocus::Content(ArrangerFocus::PhraseEditor) => Editor(
PhraseCommand::input_to_command(state, input)? PhraseCommand::input_to_command(state, input)?
), ),
ArrangerFocus::Phrases => match input.event() { AppFocus::Content(ArrangerFocus::Phrases) => match input.event() {
key!(KeyCode::Char('e')) => EditPhrase(state.phrase_editing().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 => { AppFocus::Content(ArrangerFocus::Arranger) => {
use ArrangerSelection as Select; use ArrangerSelection as Select;
use ArrangerTrackCommand as Track; use ArrangerTrackCommand as Track;
use ArrangerClipCommand as Clip; use ArrangerClipCommand as Clip;
@ -348,7 +344,7 @@ impl<T: PhrasesControl> InputToCommand<Tui, T> for PhraseRenameCommand {
} }
} }
impl<T: PhraseEditorControl> InputToCommand<Tui, T> for PhraseCommand { impl<T: PhraseEditorControl + HasEnter> InputToCommand<Tui, T> for PhraseCommand {
fn input_to_command (state: &T, from: &TuiInput) -> Option<Self> { fn input_to_command (state: &T, from: &TuiInput) -> Option<Self> {
use PhraseCommand::*; use PhraseCommand::*;
Some(match from.event() { Some(match from.event() {

View file

@ -223,7 +223,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::from(&self.transport.clock), player: PhrasePlayerModel::from(&self.clock),
}; };
self.tracks_mut().push(track); self.tracks_mut().push(track);
let index = self.tracks().len() - 1; let index = self.tracks().len() - 1;

View file

@ -94,8 +94,8 @@ impl StatusBar for ArrangerStatus {
} }
fn update (&mut self, (focused, selected, entered): &Self::State) { fn update (&mut self, (focused, selected, entered): &Self::State) {
*self = match focused { *self = match focused {
ArrangerFocus::Menu => { todo!() }, //ArrangerFocus::Menu => { todo!() },
ArrangerFocus::Transport => ArrangerStatus::Transport, ArrangerFocus::Transport(_) => ArrangerStatus::Transport,
ArrangerFocus::Arranger => match selected { ArrangerFocus::Arranger => match selected {
ArrangerSelection::Mix => ArrangerStatus::ArrangerMix, ArrangerSelection::Mix => ArrangerStatus::ArrangerMix,
ArrangerSelection::Track(_) => ArrangerStatus::ArrangerTrack, ArrangerSelection::Track(_) => ArrangerStatus::ArrangerTrack,

View file

@ -26,7 +26,7 @@ macro_rules! impl_transport_view_state {
($Struct:ident :: $field:ident) => { ($Struct:ident :: $field:ident) => {
impl TransportViewState for $Struct { impl TransportViewState for $Struct {
fn transport_selected (&self) -> TransportFocus { fn transport_selected (&self) -> TransportFocus {
self.$field.focus.inner() self.focused().inner()
} }
fn transport_focused (&self) -> bool { fn transport_focused (&self) -> bool {
true true
@ -34,16 +34,16 @@ macro_rules! impl_transport_view_state {
} }
} }
} }
impl_transport_view_state!(TransportTui::state); impl_transport_view_state!(TransportTui::clock);
impl_transport_view_state!(SequencerTui::transport); impl_transport_view_state!(SequencerTui::clock);
impl_transport_view_state!(ArrangerTui::transport); impl_transport_view_state!(ArrangerTui::clock);
pub trait ArrangerViewState { pub trait ArrangerViewState {
fn arranger_focused (&self) -> bool; fn arranger_focused (&self) -> bool;
} }
impl ArrangerViewState for ArrangerTui { impl ArrangerViewState for ArrangerTui {
fn arranger_focused (&self) -> bool { fn arranger_focused (&self) -> bool {
self.focused() == ArrangerFocus::Arranger self.focused() == AppFocus::Content(ArrangerFocus::Arranger)
} }
} }