toggle edit mode with editor

This commit is contained in:
i do not exist 2026-09-24 11:24:36 +03:00
parent ea74aba7b7
commit 767f917302
5 changed files with 156 additions and 144 deletions

View file

@ -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;

View file

@ -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)))

View file

@ -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<Arc<[Binding<Arc<str>>]>> {
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<Arc<str>>]>) -> Usually<()> {
for binding in bindings.iter() {
for src in binding.commands.iter() {
if let Some(command) = Namespace::<AppCmd>::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.
///
/// ```

View file

@ -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<Arc<[Binding<Arc<str>>]>> {
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<Arc<str>>]>) -> Usually<()> {
for binding in bindings.iter() {
for src in binding.commands.iter() {
if let Some(command) = Namespace::<AppCmd>::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<str>) -> Perhaps<AppCmd> {
#[command(Enter = "enter")] pub fn enter_mode (&mut self, mode: Arc<str>) -> Perhaps<AppCmd> {
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<AppCmd> {
#[command(Leave = "leave")] fn leave_mode (&mut self) -> Perhaps<AppCmd> {
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<AppCmd> {
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<AppCmd> {
@ -512,7 +570,8 @@ impl<T: HasScene
}
}
impl<T: HasClip + HasEditor
impl<T: HasClip
+ HasEditor
+ Namespace<u32>
+ Namespace<f64>
+ Namespace<bool>
@ -521,7 +580,8 @@ impl<T: HasClip + HasEditor
+ Namespace<ClipSlot>
> MidiEditControl for T {}
#[tek_proc::commands(MidiEditCmd)] pub trait MidiEditControl: HasClip + HasEditor
#[tek_proc::commands(MidiEditCmd)] pub trait MidiEditControl: HasClip
+ HasEditor
+ Namespace<u32>
+ Namespace<f64>
+ Namespace<bool>
@ -533,29 +593,12 @@ impl<T: HasClip + HasEditor
if self.is_editing() {
*self.editor_mut() = None;
} else if let Some(clip) = self.clip() {
let editor = MidiEditor {
mode: PianoHorizontal {
keys_width: 4,
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(16usize.into()),
Arc::new(6usize.into()),
),
..Default::default()
},
size: Sizer(
Arc::new(20usize.into()),
Arc::new(6usize.into()),
),
};
let editor = MidiEditor::new();
*editor.mode.clip.0.write() = clip.clone();
editor.redraw();
*self.editor_mut() = Some(editor);
} else {
return Ok(None)
}
Ok(Some(MidiEditCmd::Toggle))
}

View file

@ -906,61 +906,11 @@ fn view_modes <'a, S: HasModeStack> (state: &'a S) -> impl Draw<Tui> + 'a {
})
}
/// Draw clips per scene
fn view_scenes_clips <S: TracksView + ScenesView> (state: &S) -> impl Draw<Tui> {
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<str>, 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<usize> {
@ -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 <S: TracksView + ScenesView> (state: &S) -> impl Draw<Tui> {
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<str>, 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<Tui> + '_ {
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('·');