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

View file

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

View file

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

View file

@ -142,37 +142,34 @@ fn to_arranger_command (state: &ArrangerTui, input: &TuiInput) -> Option<Arrange
Some(match input.event() {
key!(Char('e')) => Cmd::Editor(PhraseCommand::Show(state.phrase_to_edit().clone())),
_ => match state.focused() {
AppFocus::Menu => { todo!() },
AppFocus::Content(focused) => match focused {
ArrangerFocus::Transport(_) => {
match TransportCommand::input_to_command(state, input)? {
TransportCommand::Clock(command) => Cmd::Clock(command),
_ => return None,
}
},
ArrangerFocus::PhraseEditor => {
Cmd::Editor(PhraseCommand::input_to_command(state, input)?)
},
ArrangerFocus::Phrases => {
Cmd::Phrases(PhrasesCommand::input_to_command(state, input)?)
},
ArrangerFocus::Arranger => {
use ArrangerSelection::*;
match input.event() {
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('`')) => { todo!("toggle state mode") },
key!(Ctrl-Char('a')) => Cmd::Scene(ArrangerSceneCommand::Add),
key!(Ctrl-Char('t')) => Cmd::Track(ArrangerTrackCommand::Add),
_ => match state.selected() {
Mix => to_arranger_mix_command(input)?,
Track(t) => to_arranger_track_command(input, t)?,
Scene(s) => to_arranger_scene_command(input, s)?,
Clip(t, s) => to_arranger_clip_command(input, t, s)?,
}
ArrangerFocus::Transport(_) => {
match TransportCommand::input_to_command(state, input)? {
TransportCommand::Clock(command) => Cmd::Clock(command),
_ => return None,
}
},
ArrangerFocus::PhraseEditor => {
Cmd::Editor(PhraseCommand::input_to_command(state, input)?)
},
ArrangerFocus::Phrases => {
Cmd::Phrases(PhrasesCommand::input_to_command(state, input)?)
},
ArrangerFocus::Arranger => {
use ArrangerSelection::*;
match input.event() {
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('`')) => { todo!("toggle state mode") },
key!(Ctrl-Char('a')) => Cmd::Scene(ArrangerSceneCommand::Add),
key!(Ctrl-Char('t')) => Cmd::Track(ArrangerTrackCommand::Add),
_ => match state.selected() {
Mix => to_arranger_mix_command(input)?,
Track(t) => to_arranger_track_command(input, t)?,
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
[AppFocus::Content(SequencerFocus::PhraseEditor)]
[SequencerFocus::PhraseEditor]
[self: Some(self.phrases.phrases[self.phrases.phrase.load(Ordering::Relaxed)].clone())]
[self, phrase: self.editor.show(phrase)]
);
impl_phrase_editor_control!(ArrangerTui
[AppFocus::Content(ArrangerFocus::PhraseEditor)]
[ArrangerFocus::PhraseEditor]
[self: 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))
}),
_ => match state.focused() {
AppFocus::Menu => { todo!() },
AppFocus::Content(focused) => match focused {
SequencerFocus::Transport(_) => {
match TransportCommand::input_to_command(state, input)? {
TransportCommand::Clock(command) => Clock(command),
_ => return None,
}
},
SequencerFocus::Phrases => Phrases(
PhrasesCommand::input_to_command(state, input)?
),
SequencerFocus::PhraseEditor => Editor(
PhraseCommand::input_to_command(state, input)?
),
}
SequencerFocus::Transport(_) => {
match TransportCommand::input_to_command(state, input)? {
TransportCommand::Clock(command) => Clock(command),
_ => return None,
}
},
SequencerFocus::PhraseList => Phrases(
PhrasesCommand::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 {
fn transport_focused (&self) -> Option<TransportFocus> {
if let AppFocus::Content(focus) = self.focus.inner() {
Some(focus)
} else {
None
}
Some(self.focus.inner())
}
}
impl TransportControl for SequencerTui {
fn transport_focused (&self) -> Option<TransportFocus> {
if let AppFocus::Content(SequencerFocus::Transport(focus)) = self.focus.inner() {
Some(focus)
} else {
None
match self.focus.inner() {
SequencerFocus::Transport(focus) => Some(focus),
_ => None
}
}
}
impl TransportControl for ArrangerTui {
fn transport_focused (&self) -> Option<TransportFocus> {
if let AppFocus::Content(ArrangerFocus::Transport(focus)) = self.focus.inner() {
Some(focus)
} else {
None
match self.focus.inner() {
ArrangerFocus::Transport(focus) => Some(focus),
_ => None
}
}
}

View file

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

View file

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

View file

@ -71,7 +71,7 @@ pub trait ArrangerViewState {
}
impl ArrangerViewState for ArrangerTui {
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);
add(&col!(row1, row2).fill_x().bg(color.base.rgb))?;
if *focused && i == *index {
if *entered && i == *index {
add(&CORNERS)?;
}
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 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 upper_left = format!("[{}] {title}", if *entered {""} else {" "});
lay!(
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> {
fn from (state: &TransportTui) -> Self {
match state.focus.inner() {
AppFocus::Content(focus) => Some(focus),
_ => None
}
Some(state.focus.inner())
}
}
impl From<&SequencerTui> for Option<TransportFocus> {
fn from (state: &SequencerTui) -> Self {
match state.focus.inner() {
AppFocus::Content(SequencerFocus::Transport(focus)) => Some(focus),
SequencerFocus::Transport(focus) => Some(focus),
_ => None
}
}
@ -95,7 +92,7 @@ impl From<&SequencerTui> for Option<TransportFocus> {
impl From<&ArrangerTui> for Option<TransportFocus> {
fn from (state: &ArrangerTui) -> Self {
match state.focus.inner() {
AppFocus::Content(ArrangerFocus::Transport(focus)) => Some(focus),
ArrangerFocus::Transport(focus) => Some(focus),
_ => None
}
}