remove AppFocus

This commit is contained in:
🪞👃🪞 2024-11-26 01:40:30 +01:00
parent 3273c85630
commit e4027619e8
14 changed files with 127 additions and 140 deletions

View file

@ -238,14 +238,6 @@ impl Debug for PhrasePlayerModel {
} }
} }
#[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> { pub trait FocusWrap<T> {
fn wrap <'a, W: Widget<Engine = Tui>> (self, focus: T, content: &'a W) fn wrap <'a, W: Widget<Engine = Tui>> (self, focus: T, content: &'a W)
-> impl Widget<Engine = Tui> + 'a; -> impl Widget<Engine = Tui> + 'a;
@ -254,7 +246,7 @@ pub trait FocusWrap<T> {
#[macro_export] macro_rules! impl_focus { #[macro_export] macro_rules! impl_focus {
($Struct:ident $Focus:ident $Grid:expr) => { ($Struct:ident $Focus:ident $Grid:expr) => {
impl HasFocus for $Struct { impl HasFocus for $Struct {
type Item = AppFocus<$Focus>; type Item = $Focus;
/// Get the currently focused item. /// Get the currently focused item.
fn focused (&self) -> Self::Item { fn focused (&self) -> Self::Item {
self.focus.inner() self.focus.inner()
@ -285,8 +277,7 @@ pub trait FocusWrap<T> {
fn focus_cursor_mut (&mut self) -> &mut (usize, usize) { fn focus_cursor_mut (&mut self) -> &mut (usize, usize) {
&mut self.cursor &mut self.cursor
} }
fn focus_layout (&self) -> &[&[AppFocus<$Focus>]] { fn focus_layout (&self) -> &[&[$Focus]] {
use AppFocus::*;
use $Focus::*; use $Focus::*;
&$Grid &$Grid
} }

View file

@ -21,7 +21,7 @@ pub struct ArrangerTui {
pub note_buf: Vec<u8>, pub note_buf: Vec<u8>,
pub midi_buf: Vec<Vec<Vec<u8>>>, pub midi_buf: Vec<Vec<Vec<u8>>>,
pub editor: PhraseEditorModel, pub editor: PhraseEditorModel,
pub focus: FocusState<AppFocus<ArrangerFocus>>, pub focus: FocusState<ArrangerFocus>,
pub perf: PerfModel, pub perf: PerfModel,
} }
@ -49,9 +49,7 @@ impl TryFrom<&Arc<RwLock<JackClient>>> for ArrangerTui {
midi_buf: vec![vec![];65536], midi_buf: vec![vec![];65536],
note_buf: vec![], note_buf: vec![],
perf: PerfModel::default(), perf: PerfModel::default(),
focus: FocusState::Entered( focus: FocusState::Entered(ArrangerFocus::Transport(TransportFocus::PlayPause)),
AppFocus::Content(ArrangerFocus::Transport(TransportFocus::PlayPause))
),
}) })
} }
} }
@ -78,23 +76,23 @@ impl_focus!(ArrangerTui ArrangerFocus [
//Menu, //Menu,
//], //],
&[ &[
Content(Transport(TransportFocus::PlayPause)), Transport(TransportFocus::PlayPause),
Content(Transport(TransportFocus::Bpm)), Transport(TransportFocus::Bpm),
Content(Transport(TransportFocus::Sync)), Transport(TransportFocus::Sync),
Content(Transport(TransportFocus::Quant)), Transport(TransportFocus::Quant),
Content(Transport(TransportFocus::Clock)), Transport(TransportFocus::Clock),
], &[ ], &[
Content(Arranger), Arranger,
Content(Arranger), Arranger,
Content(Arranger), Arranger,
Content(Arranger), Arranger,
Content(Arranger), Arranger,
], &[ ], &[
Content(Phrases), Phrases,
Content(Phrases), Phrases,
Content(PhraseEditor), PhraseEditor,
Content(PhraseEditor), PhraseEditor,
Content(PhraseEditor), PhraseEditor,
], ],
]); ]);

View file

@ -13,7 +13,7 @@ pub struct SequencerTui {
pub entered: bool, pub entered: bool,
pub note_buf: Vec<u8>, pub note_buf: Vec<u8>,
pub midi_buf: Vec<Vec<Vec<u8>>>, pub midi_buf: Vec<Vec<Vec<u8>>>,
pub focus: FocusState<AppFocus<SequencerFocus>>, pub focus: FocusState<SequencerFocus>,
pub perf: PerfModel, pub perf: PerfModel,
} }
@ -22,21 +22,19 @@ impl TryFrom<&Arc<RwLock<JackClient>>> for SequencerTui {
fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> { fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
let clock = ClockModel::from(&Arc::new(jack.read().unwrap().transport())); let clock = ClockModel::from(&Arc::new(jack.read().unwrap().transport()));
Ok(Self { Ok(Self {
jack: jack.clone(), jack: jack.clone(),
phrases: PhrasesModel::default(), phrases: PhrasesModel::default(),
player: PhrasePlayerModel::from(&clock), player: PhrasePlayerModel::from(&clock),
editor: PhraseEditorModel::default(), editor: PhraseEditorModel::default(),
size: Measure::new(), size: Measure::new(),
cursor: (0, 0), cursor: (0, 0),
entered: false, entered: false,
split: 20, split: 20,
midi_buf: vec![vec![];65536], midi_buf: vec![vec![];65536],
note_buf: vec![], note_buf: vec![],
clock, clock,
perf: PerfModel::default(), perf: PerfModel::default(),
focus: FocusState::Entered( focus: FocusState::Focused(SequencerFocus::Transport(TransportFocus::PlayPause))
AppFocus::Content(SequencerFocus::Transport(TransportFocus::PlayPause))
),
}) })
} }
} }
@ -47,9 +45,12 @@ pub enum SequencerFocus {
/// The transport (toolbar) is focused /// The transport (toolbar) is focused
Transport(TransportFocus), Transport(TransportFocus),
/// The phrase list (pool) is focused /// The phrase list (pool) is focused
Phrases, PhraseList,
/// The phrase editor (sequencer) is focused /// The phrase editor (sequencer) is focused
PhraseEditor, PhraseEditor,
PhrasePlay,
PhraseNext,
} }
impl_focus!(SequencerTui SequencerFocus [ impl_focus!(SequencerTui SequencerFocus [
@ -61,18 +62,32 @@ impl_focus!(SequencerTui SequencerFocus [
//Menu, //Menu,
//], //],
&[ &[
Content(Transport(TransportFocus::PlayPause)), Transport(TransportFocus::PlayPause),
Content(Transport(TransportFocus::Bpm)), Transport(TransportFocus::Bpm),
Content(Transport(TransportFocus::Sync)), Transport(TransportFocus::Sync),
Content(Transport(TransportFocus::Quant)), Transport(TransportFocus::Quant),
Content(Transport(TransportFocus::Clock)), Transport(TransportFocus::Clock),
], ],
&[ &[
Content(Phrases), PhrasePlay,
Content(Phrases), PhrasePlay,
Content(PhraseEditor), PhraseEditor,
Content(PhraseEditor), PhraseEditor,
Content(PhraseEditor), PhraseEditor,
],
&[
PhraseNext,
PhraseNext,
PhraseEditor,
PhraseEditor,
PhraseEditor,
],
&[
PhraseList,
PhraseList,
PhraseEditor,
PhraseEditor,
PhraseEditor,
], ],
]); ]);

View file

@ -6,7 +6,7 @@ pub struct TransportTui {
pub clock: ClockModel, pub clock: ClockModel,
pub size: Measure<Tui>, pub size: Measure<Tui>,
pub cursor: (usize, usize), pub cursor: (usize, usize),
pub focus: FocusState<AppFocus<TransportFocus>>, pub focus: FocusState<TransportFocus>,
} }
/// Create app state from JACK handle. /// Create app state from JACK handle.
@ -18,9 +18,7 @@ impl TryFrom<&Arc<RwLock<JackClient>>> for TransportTui {
clock: ClockModel::from(&Arc::new(jack.read().unwrap().transport())), clock: ClockModel::from(&Arc::new(jack.read().unwrap().transport())),
size: Measure::new(), size: Measure::new(),
cursor: (0, 0), cursor: (0, 0),
focus: FocusState::Entered( focus: FocusState::Entered(TransportFocus::PlayPause)
AppFocus::Content(TransportFocus::PlayPause)
)
}) })
} }
} }
@ -60,11 +58,11 @@ impl FocusWrap<TransportFocus> for Option<TransportFocus> {
impl_focus!(TransportTui TransportFocus [ impl_focus!(TransportTui TransportFocus [
//&[Menu], //&[Menu],
&[ &[
Content(PlayPause), PlayPause,
Content(Bpm), Bpm,
Content(Quant), Quant,
Content(Sync), Sync,
Content(Clock), Clock,
], ],
]); ]);

View file

@ -142,37 +142,34 @@ fn to_arranger_command (state: &ArrangerTui, input: &TuiInput) -> Option<Arrange
Some(match input.event() { Some(match input.event() {
key!(Char('e')) => Cmd::Editor(PhraseCommand::Show(state.phrase_to_edit().clone())), key!(Char('e')) => Cmd::Editor(PhraseCommand::Show(state.phrase_to_edit().clone())),
_ => match state.focused() { _ => match state.focused() {
AppFocus::Menu => { todo!() }, ArrangerFocus::Transport(_) => {
AppFocus::Content(focused) => match focused { match TransportCommand::input_to_command(state, input)? {
ArrangerFocus::Transport(_) => { TransportCommand::Clock(command) => Cmd::Clock(command),
match TransportCommand::input_to_command(state, input)? { _ => return None,
TransportCommand::Clock(command) => Cmd::Clock(command), }
_ => return None, },
} ArrangerFocus::PhraseEditor => {
}, Cmd::Editor(PhraseCommand::input_to_command(state, input)?)
ArrangerFocus::PhraseEditor => { },
Cmd::Editor(PhraseCommand::input_to_command(state, input)?) ArrangerFocus::Phrases => {
}, Cmd::Phrases(PhrasesCommand::input_to_command(state, input)?)
ArrangerFocus::Phrases => { },
Cmd::Phrases(PhrasesCommand::input_to_command(state, input)?) ArrangerFocus::Arranger => {
}, use ArrangerSelection::*;
ArrangerFocus::Arranger => { match input.event() {
use ArrangerSelection::*; key!(Char('l')) => Cmd::Clip(ArrangerClipCommand::SetLoop(false)),
match input.event() { key!(Char('+')) => Cmd::Zoom(0), // TODO
key!(Char('l')) => Cmd::Clip(ArrangerClipCommand::SetLoop(false)), key!(Char('=')) => Cmd::Zoom(0), // TODO
key!(Char('+')) => Cmd::Zoom(0), // TODO key!(Char('_')) => Cmd::Zoom(0), // TODO
key!(Char('=')) => Cmd::Zoom(0), // TODO key!(Char('-')) => Cmd::Zoom(0), // TODO
key!(Char('_')) => Cmd::Zoom(0), // TODO key!(Char('`')) => { todo!("toggle state mode") },
key!(Char('-')) => Cmd::Zoom(0), // TODO key!(Ctrl-Char('a')) => Cmd::Scene(ArrangerSceneCommand::Add),
key!(Char('`')) => { todo!("toggle state mode") }, key!(Ctrl-Char('t')) => Cmd::Track(ArrangerTrackCommand::Add),
key!(Ctrl-Char('a')) => Cmd::Scene(ArrangerSceneCommand::Add), _ => match state.selected() {
key!(Ctrl-Char('t')) => Cmd::Track(ArrangerTrackCommand::Add), Mix => to_arranger_mix_command(input)?,
_ => match state.selected() { Track(t) => to_arranger_track_command(input, t)?,
Mix => to_arranger_mix_command(input)?, Scene(s) => to_arranger_scene_command(input, s)?,
Track(t) => to_arranger_track_command(input, t)?, Clip(t, s) => to_arranger_clip_command(input, t, s)?,
Scene(s) => to_arranger_scene_command(input, s)?,
Clip(t, s) => to_arranger_clip_command(input, t, s)?,
}
} }
} }
} }

View file

@ -172,12 +172,12 @@ macro_rules! impl_phrase_editor_control {
} }
} }
impl_phrase_editor_control!(SequencerTui impl_phrase_editor_control!(SequencerTui
[AppFocus::Content(SequencerFocus::PhraseEditor)] [SequencerFocus::PhraseEditor]
[self: Some(self.phrases.phrases[self.phrases.phrase.load(Ordering::Relaxed)].clone())] [self: Some(self.phrases.phrases[self.phrases.phrase.load(Ordering::Relaxed)].clone())]
[self, phrase: self.editor.show(phrase)] [self, phrase: self.editor.show(phrase)]
); );
impl_phrase_editor_control!(ArrangerTui impl_phrase_editor_control!(ArrangerTui
[AppFocus::Content(ArrangerFocus::PhraseEditor)] [ArrangerFocus::PhraseEditor]
[self: todo!()] [self: todo!()]
[self, phrase: todo!()] [self, phrase: todo!()]
); );

View file

@ -64,21 +64,19 @@ pub fn to_sequencer_command (state: &SequencerTui, input: &TuiInput) -> Option<S
ClockCommand::Pause(Some(0)) ClockCommand::Pause(Some(0))
}), }),
_ => match state.focused() { _ => match state.focused() {
AppFocus::Menu => { todo!() }, SequencerFocus::Transport(_) => {
AppFocus::Content(focused) => match focused { match TransportCommand::input_to_command(state, input)? {
SequencerFocus::Transport(_) => { TransportCommand::Clock(command) => Clock(command),
match TransportCommand::input_to_command(state, input)? { _ => return None,
TransportCommand::Clock(command) => Clock(command), }
_ => return None, },
} SequencerFocus::PhraseList => Phrases(
}, PhrasesCommand::input_to_command(state, input)?
SequencerFocus::Phrases => Phrases( ),
PhrasesCommand::input_to_command(state, input)? SequencerFocus::PhraseEditor => Editor(
), PhraseCommand::input_to_command(state, input)?
SequencerFocus::PhraseEditor => Editor( ),
PhraseCommand::input_to_command(state, input)? _ => todo!()
),
}
} }
}) })
} }

View file

@ -34,30 +34,24 @@ pub trait TransportControl: ClockApi + FocusGrid + HasEnter {
impl TransportControl for TransportTui { impl TransportControl for TransportTui {
fn transport_focused (&self) -> Option<TransportFocus> { fn transport_focused (&self) -> Option<TransportFocus> {
if let AppFocus::Content(focus) = self.focus.inner() { Some(self.focus.inner())
Some(focus)
} else {
None
}
} }
} }
impl TransportControl for SequencerTui { impl TransportControl for SequencerTui {
fn transport_focused (&self) -> Option<TransportFocus> { fn transport_focused (&self) -> Option<TransportFocus> {
if let AppFocus::Content(SequencerFocus::Transport(focus)) = self.focus.inner() { match self.focus.inner() {
Some(focus) SequencerFocus::Transport(focus) => Some(focus),
} else { _ => None
None
} }
} }
} }
impl TransportControl for ArrangerTui { impl TransportControl for ArrangerTui {
fn transport_focused (&self) -> Option<TransportFocus> { fn transport_focused (&self) -> Option<TransportFocus> {
if let AppFocus::Content(ArrangerFocus::Transport(focus)) = self.focus.inner() { match self.focus.inner() {
Some(focus) ArrangerFocus::Transport(focus) => Some(focus),
} else { _ => None
None
} }
} }
} }

View file

@ -64,7 +64,7 @@ impl HasEditor for SequencerTui {
&self.editor &self.editor
} }
fn editor_focused (&self) -> bool { fn editor_focused (&self) -> bool {
self.focused() == AppFocus::Content(SequencerFocus::PhraseEditor) self.focused() == SequencerFocus::PhraseEditor
} }
fn editor_entered (&self) -> bool { fn editor_entered (&self) -> bool {
self.entered() && self.editor_focused() self.entered() && self.editor_focused()
@ -76,7 +76,7 @@ impl HasEditor for ArrangerTui {
&self.editor &self.editor
} }
fn editor_focused (&self) -> bool { fn editor_focused (&self) -> bool {
self.focused() == AppFocus::Content(ArrangerFocus::PhraseEditor) self.focused() == ArrangerFocus::PhraseEditor
} }
fn editor_entered (&self) -> bool { fn editor_entered (&self) -> bool {
self.entered() && self.editor_focused() self.entered() && self.editor_focused()

View file

@ -45,7 +45,7 @@ pub trait HasPhraseList: HasPhrases {
impl HasPhraseList for SequencerTui { impl HasPhraseList for SequencerTui {
fn phrases_focused (&self) -> bool { fn phrases_focused (&self) -> bool {
self.focused() == AppFocus::Content(SequencerFocus::Phrases) self.focused() == SequencerFocus::PhraseList
} }
fn phrases_entered (&self) -> bool { fn phrases_entered (&self) -> bool {
self.entered() && self.phrases_focused() self.entered() && self.phrases_focused()
@ -60,7 +60,7 @@ impl HasPhraseList for SequencerTui {
impl HasPhraseList for ArrangerTui { impl HasPhraseList for ArrangerTui {
fn phrases_focused (&self) -> bool { fn phrases_focused (&self) -> bool {
self.focused() == AppFocus::Content(ArrangerFocus::Phrases) self.focused() == ArrangerFocus::Phrases
} }
fn phrases_entered (&self) -> bool { fn phrases_entered (&self) -> bool {
self.entered() && self.phrases_focused() self.entered() && self.phrases_focused()

View file

@ -71,7 +71,7 @@ pub trait ArrangerViewState {
} }
impl ArrangerViewState for ArrangerTui { impl ArrangerViewState for ArrangerTui {
fn arranger_focused (&self) -> bool { fn arranger_focused (&self) -> bool {
self.focused() == AppFocus::Content(ArrangerFocus::Arranger) self.focused() == ArrangerFocus::Arranger
} }
} }

View file

@ -55,7 +55,7 @@ impl<'a> Content for PhraseListView<'a> {
}; };
let row2 = TuiStyle::bold(row2, true); let row2 = TuiStyle::bold(row2, true);
add(&col!(row1, row2).fill_x().bg(color.base.rgb))?; add(&col!(row1, row2).fill_x().bg(color.base.rgb))?;
if *focused && i == *index { if *entered && i == *index {
add(&CORNERS)?; add(&CORNERS)?;
} }
Ok(()) Ok(())

View file

@ -47,10 +47,9 @@ impl<'a> Content for PhraseSelector<'a> {
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 content = content.fill_xy().bg(Color::Rgb(28, 35, 25)).border(border); let content = content.fill_xy().bg(Color::Rgb(28, 35, 25)).border(border);
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 upper_left = format!("[{}] {title}", if *entered {""} else {" "});
lay!( lay!(
content, content,
TuiStyle::fg(upper_left.to_string(), title_color).push_x(1).align_nw().fill_xy(), TuiStyle::fg(*title, title_color).push_x(1).align_nw().fill_xy(),
) )
} }
} }

View file

@ -76,17 +76,14 @@ where
impl From<&TransportTui> for Option<TransportFocus> { impl From<&TransportTui> for Option<TransportFocus> {
fn from (state: &TransportTui) -> Self { fn from (state: &TransportTui) -> Self {
match state.focus.inner() { Some(state.focus.inner())
AppFocus::Content(focus) => Some(focus),
_ => None
}
} }
} }
impl From<&SequencerTui> for Option<TransportFocus> { impl From<&SequencerTui> for Option<TransportFocus> {
fn from (state: &SequencerTui) -> Self { fn from (state: &SequencerTui) -> Self {
match state.focus.inner() { match state.focus.inner() {
AppFocus::Content(SequencerFocus::Transport(focus)) => Some(focus), SequencerFocus::Transport(focus) => Some(focus),
_ => None _ => None
} }
} }
@ -95,7 +92,7 @@ impl From<&SequencerTui> for Option<TransportFocus> {
impl From<&ArrangerTui> for Option<TransportFocus> { impl From<&ArrangerTui> for Option<TransportFocus> {
fn from (state: &ArrangerTui) -> Self { fn from (state: &ArrangerTui) -> Self {
match state.focus.inner() { match state.focus.inner() {
AppFocus::Content(ArrangerFocus::Transport(focus)) => Some(focus), ArrangerFocus::Transport(focus) => Some(focus),
_ => None _ => None
} }
} }