diff --git a/src/midi_edit.rs b/src/midi_edit.rs index dbae5b88..49e19cce 100644 --- a/src/midi_edit.rs +++ b/src/midi_edit.rs @@ -46,6 +46,33 @@ impl_default!(MidiEditor: Self { }); impl MidiEditor { + pub fn new () -> Self { + Self { + mode: PianoHorizontal { + keys_width: 4, + point: MidiCursor { + note_len: Arc::new(1.into()), + ..Default::default() + }, + range: MidiSelection { + time_len: Arc::new(96usize.into()), + time_zoom: Arc::new(1usize.into()), + note_axis: Arc::new(12usize.into()), + ..Default::default() + }, + size: Sizer( + Arc::new(48usize.into()), + Arc::new(12usize.into()), + ), + ..Default::default() + }, + size: Sizer( + Arc::new(52usize.into()), + Arc::new(12usize.into()), + ), + } + } + /// Put note at current position pub fn put_note (&mut self, advance: bool) { let mut redraw = false; diff --git a/src/tek.edn b/src/tek.edn index b36befbc..ed05b994 100644 --- a/src/tek.edn +++ b/src/tek.edn @@ -3,7 +3,7 @@ (mode Help (view (bg (g 32) (fg (g 64) (fill/xy - (bg (g 48) (fg (g 224) (align/c (expand/wh 2 2 (text Nobody Will Help! )))))))))) + (bg (g 48) (fg (g 224) (align/c (expand/wh 2 2 (text Nobody helps! )))))))))) (mode Quit (keys (@y Quit)) @@ -50,6 +50,9 @@ (bar/s 2 TracksNamesCells TracksInputsCells)))))) (bar/w 20 (text) (bar/e 16 ScenesNames ScenesClips)))))) +(mode Edit + (keys X Y)) + (mode Sequencer (info MIDI sequencer.) (keys Global Clock I IFine Y Page EditorView EditorAdd EditorDel) (view (bsp/s (exact/h 1 Transport) (bsp/n (exact/h 1 StatusBar) (fill/xy (bsp/a (fill/xy (align/e Pool)) Editor)))))) @@ -86,7 +89,7 @@ (@f10 (enter Quit)) (@f12 (enter Devtools))) -(keys Confirm (@enter confirm)) +(keys Confirm (@enter confirm)) (keys Page (@pgup page/up) (@pgdn page/down)) (keys Delete (@delete delete) (@backspace delete/back)) (keys History (@u (undo 1)) (@r (redo 1))) diff --git a/src/tek.rs b/src/tek.rs index 611f54f5..1e32d4b8 100644 --- a/src/tek.rs +++ b/src/tek.rs @@ -390,62 +390,6 @@ impl ScenesView for App { } } -tui_keys!(self: App, input { - #[cfg(feature = "prof2")] profiling::scope!("App::tui_keys!"); - Self::input_cmd(self, input) -}); - -impl App { - - fn input_cmd (state: &mut Self, input: &TuiEvent) -> Usually<()> { - if let Some(bindings) = Self::input_cmds_find(state, input) { - return Self::input_cmds_exec(state, bindings) - } - Ok(()) - } - - fn input_cmds_find (state: &mut Self, input: &TuiEvent) -> Option>]>> { - for name in state.modes.read().iter().rev() { - let mode = state.config.get_mode(name).as_ref().map(Arc::clone); - if let Some(mode) = mode { - let binds = state.config.binds.clone(); - for id in mode.keys.iter() { - if let Some(event_map) = binds.find(id) - && let Some(bindings) = event_map.query(input) { - return Some(bindings.into()) - } - } - } - } - None - } - - fn input_cmds_exec (state: &mut Self, bindings: Arc<[Binding>]>) -> Usually<()> { - for binding in bindings.iter() { - for src in binding.commands.iter() { - if let Some(command) = Namespace::::namespace(state, &src)? { - Self::cmd_dispatch(state, command)?; - } - } - } - Ok(()) - } - - fn cmd_dispatch (state: &mut Self, command: AppCmd) -> Usually<()> { - match command.clone().dispatch(state) { - Ok(undo) => { - state.history.push((command, undo)); - Ok(()) - }, - Err(err) => { - state.history.push((command, None)); - Err(err) - } - } - } - -} - /// A control axis. /// /// ``` diff --git a/src/tek_ctrl.rs b/src/tek_ctrl.rs index 19b21c13..9a527f85 100644 --- a/src/tek_ctrl.rs +++ b/src/tek_ctrl.rs @@ -1,5 +1,56 @@ use crate::*; +tui_keys!(self: App, input { + #[cfg(feature = "prof2")] profiling::scope!("App::tui_keys!"); + self.input_cmds(input) +}); + +impl App { + fn input_cmds (&mut self, input: &TuiEvent) -> Usually<()> { + if let Some(bindings) = Self::input_cmds_find(self, input) { + return Self::input_cmds_exec(self, bindings) + } + Ok(()) + } + fn input_cmds_find (state: &Self, input: &TuiEvent) -> Option>]>> { + for name in state.modes.read().iter().rev() { + let mode = state.config.get_mode(name).as_ref().map(Arc::clone); + if let Some(mode) = mode { + let binds = state.config.binds.clone(); + for id in mode.keys.iter() { + if let Some(event_map) = binds.find(id) + && let Some(bindings) = event_map.query(input) { + return Some(bindings.into()) + } + } + } + } + None + } + fn input_cmds_exec (state: &mut Self, bindings: Arc<[Binding>]>) -> Usually<()> { + for binding in bindings.iter() { + for src in binding.commands.iter() { + if let Some(command) = Namespace::::namespace(state, &src)? { + Self::cmd_dispatch(state, command)?; + } + } + } + Ok(()) + } + fn cmd_dispatch (state: &mut Self, command: AppCmd) -> Usually<()> { + match command.clone().dispatch(state) { + Ok(undo) => { + state.history.push((command, undo)); + Ok(()) + }, + Err(err) => { + state.history.push((command, None)); + Err(err) + } + } + } +} + impl_from!(AppCmd: |x: AxisCmd| AppCmd::Axis { command: x }); impl_from!(AppCmd: |x: BrowseCmd| AppCmd::Browse { command: x }); impl_from!(AppCmd: |x: MidiClipCmd| AppCmd::MidiClip { command: x }); @@ -22,7 +73,7 @@ impl_from!(AppCmd: |x: TracksCmd| AppCmd::Tracks { command: x }); Ok(None) } /// Enter a contextual mode. - #[command(Enter = "enter")] pub fn cmd_mode_enter (&mut self, mode: Arc) -> Perhaps { + #[command(Enter = "enter")] pub fn enter_mode (&mut self, mode: Arc) -> Perhaps { let mut modes = self.modes_mut(); Ok(if modes.last() != Some(&mode) { modes.push(mode); @@ -32,7 +83,7 @@ impl_from!(AppCmd: |x: TracksCmd| AppCmd::Tracks { command: x }); }) } /// Leave a contextual mode. - #[command(Leave = "leave")] fn cmd_mode_leave (&mut self) -> Perhaps { + #[command(Leave = "leave")] fn leave_mode (&mut self) -> Perhaps { let mut modes = self.modes_mut(); if modes.len() > 1 { let mode = modes.pop(); @@ -85,6 +136,13 @@ impl_from!(AppCmd: |x: TracksCmd| AppCmd::Tracks { command: x }); } /// Commands pertaining to the MIDI clip editor. #[command(MidiEdit = "edit")] pub fn cmd_midi_edit (&mut self, command: MidiEditCmd) -> Perhaps { + if matches!(command, MidiEditCmd::Toggle) { + if self.is_editing() { + self.leave_mode()?; + } else { + self.enter_mode("Edit".into())?; + } + } Ok(command.dispatch(self)?.map(Into::into)) } #[command(Scene = "scene")] pub fn cmd_scene (&mut self, command: SceneCmd) -> Perhaps { @@ -512,7 +570,8 @@ impl + Namespace + Namespace @@ -521,7 +580,8 @@ impl > MidiEditControl for T {} -#[tek_proc::commands(MidiEditCmd)] pub trait MidiEditControl: HasClip + HasEditor +#[tek_proc::commands(MidiEditCmd)] pub trait MidiEditControl: HasClip + + HasEditor + Namespace + Namespace + Namespace @@ -533,29 +593,12 @@ impl (state: &'a S) -> impl Draw + 'a { }) } -/// Draw clips per scene -fn view_scenes_clips (state: &S) -> impl Draw { - let select = state.selection(); - let editor = state.editor(); - let editing = state.is_editing(); - let sized = move|it|state.with_clips_size(true, it).align_c(); - let tracks = move|it|iter_east(move||state.tracks_with_sizes().map(it)); - let scenes = move|it|iter_south(move||state.scenes_with_sizes().map(it)); - sized(tracks(move|(track_index, track, _, _)|{ - let w = view_track_w(select, track_index, track, editor.as_ref()) as u16; - scenes(move|(scene_index, scene, _, _)|{ - let (name, theme): (Arc, ItemTheme) = view_scene_name_theme(scene, track_index); - let f = theme.lightest.term; - let (b, o) = view_scene_bg(theme, select, track_index, scene_index); - let is_sel = view_scene_sel(select, track_index, scene_index, editing); - below( - Outer(true, Style::default().fg(o)).full_wh(), - above( - below( - fg_bg(o, b, "".full_wh()), - fg_bg(f, b, bold(true, name)).align_nw().full_wh(), - ), - when(is_sel, editor).full_wh() - ).full_wh() - ).exact_wh(w, view_scene_y(select, scene_index, editor.as_ref()),) - }).exact_w(w) - })) -} - -pub fn view_scene_bg ( - theme: ItemTheme, select: &Selection, track_index: usize, scene_index: usize -) -> (Color, Color) { - let mut outline = theme.base.term; - (if select.track() == Some(track_index) && select.scene() == Some(scene_index) { - outline = theme.lighter.term; - theme.light.term - } else if select.track() == Some(track_index) || select.scene() == Some(scene_index) { - outline = theme.darkest.term; - theme.base.term - } else { - theme.dark.term - }, outline) -} - -pub fn view_scene_sel ( - select: &Selection, track_index: usize, scene_index: usize, editing: bool -) -> bool { - editing && select.track() == Some(track_index) && select.scene() == Some(scene_index) -} - pub fn view_track_w ( select: &Selection, track_index: usize, track: &Track, editor: Option<&MidiEditor> ) -> u16 { if select.track() == Some(track_index) && let Some(editor) = editor { - (editor.size.w() as usize).max(24).max(track.width) as u16 + (editor.size.w() as usize).max(20).max(track.width) as u16 } else { track.width as u16 } @@ -970,16 +920,6 @@ pub(crate) fn track_width (_index: usize, track: &Track) -> u16 { track.width as u16 } -pub fn view_scene_y ( - select: &Selection, scene_index: usize, editor: Option<&MidiEditor> -) -> u16 { - if select.scene() == Some(scene_index) && let Some(editor) = editor { - editor.size.h().max(12) - } else { - Scene::DEFAULT_HEIGHT as u16 - } -} - pub fn draw_sample ( to: &mut Tui, x: u16, y: u16, note: Option<&u7>, sample: &Sample, focus: bool ) -> Usually { @@ -1169,6 +1109,61 @@ pub fn view_scene_name <'a> ( bg(c, above(a, b.push_y(1)).align_nw().exact_h(h)) } +/// Draw clips per scene +fn view_scenes_clips (state: &S) -> impl Draw { + let select = state.selection(); + let editor = state.editor(); + let editing = state.is_editing(); + let sized = move|it|state.with_clips_size(true, it).align_c(); + let tracks = move|it|iter_east(move||state.tracks_with_sizes().map(it)); + let scenes = move|it|iter_south(move||state.scenes_with_sizes().map(it)); + sized(tracks(move|(track_index, track, _, _)|{ + let w = view_track_w(select, track_index, track, editor.as_ref()) as u16; + scenes(move|(scene_index, scene, _, _)|{ + let (name, theme): (Arc, ItemTheme) = view_scene_name_theme(scene, track_index); + let f = theme.lightest.term; + let (b, o) = view_scene_bg(theme, select, track_index, scene_index); + let is_sel = view_scene_sel(select, track_index, scene_index, editing); + let border = Outer(true, Style::default().fg(o)).full_wh(); + let title = below(fg_bg(o, b, "".full_wh()), + fg_bg(f, b, bold(true, name)).align_nw().full_wh()); + above(border, above(title, when(is_sel, editor).full_wh()).full_wh()) + .exact_wh(w, view_scene_y(select, scene_index, editor.as_ref()),) + }).exact_w(w) + })) +} + +pub fn view_scene_bg ( + theme: ItemTheme, select: &Selection, track_index: usize, scene_index: usize +) -> (Color, Color) { + let mut outline = theme.base.term; + (if select.track() == Some(track_index) && select.scene() == Some(scene_index) { + outline = theme.lighter.term; + theme.light.term + } else if select.track() == Some(track_index) || select.scene() == Some(scene_index) { + outline = theme.darkest.term; + theme.base.term + } else { + theme.dark.term + }, outline) +} + +pub fn view_scene_sel ( + select: &Selection, track_index: usize, scene_index: usize, editing: bool +) -> bool { + editing && select.track() == Some(track_index) && select.scene() == Some(scene_index) +} + +pub fn view_scene_y ( + select: &Selection, scene_index: usize, editor: Option<&MidiEditor> +) -> u16 { + if select.scene() == Some(scene_index) && let Some(editor) = editor { + editor.size.h().max(13) + } else { + Scene::DEFAULT_HEIGHT as u16 + } +} + pub fn view_clip_info (editor: &MidiEditor) -> impl Draw + '_ { let ( _color, name, length, looped @@ -1512,7 +1507,7 @@ pub fn draw_piano_bg ( cell.set_char('='); } else if (127 - note) % 6 == 0 { cell.set_fg(clip.color.darker.term); - cell.set_char('—'); + cell.set_char('-'); } else { cell.set_fg(clip.color.darker.term); cell.set_char('·');