Compare commits

...

5 commits

Author SHA1 Message Date
baad8254a2 add buttons
Some checks are pending
/ build (push) Waiting to run
2025-05-18 00:23:00 +03:00
958e602577 use stack in view_inputs 2025-05-17 23:05:35 +03:00
50c263b4d3 fix disappearing input rows 2025-05-17 22:04:25 +03:00
01db41b75d arranger: trying to fix conditional layers 2025-05-17 21:56:46 +03:00
9fcb5a08c6 style editor stats as commands 2025-05-17 21:50:14 +03:00
9 changed files with 168 additions and 112 deletions

View file

@ -64,7 +64,7 @@ arranger-ext:
arranger-release:
{{release}} {{name}} {{bpm}} arranger
arranger-release-ext:
{{release}} {{name}} {{bpm}} {{midi-in}} {{midi-out}} arranger
{{release}} {{name}} {{bpm}} {{midi-in}} {{firefox-in}} {{midi-out}} arranger
groovebox:
{{debug}} {{name}} {{bpm}} groovebox

View file

@ -22,10 +22,10 @@
(bsp/w :view-meters-output
(bsp/e :view-meters-input
(bsp/n (fixed/y 2 :view-status-h2)
(bsp/n (fill/x (align/w :view-tracks-inputs))
(bsp/s (fill/x (align/w :view-tracks-devices))
(bsp/s (fill/x (align/w :view-tracks-outputs))
(bsp/s (fill/x (align/w :view-tracks-names))
(bsp/n :view-tracks-inputs
(bsp/s :view-tracks-devices
(bsp/s :view-tracks-outputs
(bsp/s :view-tracks-names
(fill/xy (either :is-editing
(bsp/e (fixed/x 20 :view-scenes-names) :view-editor)
(bsp/e :view-scenes-names :view-editor)
:view-scenes)))))))))))

View file

@ -435,7 +435,8 @@ impl Configuration {
return Err(format!("(e4) unexpected non-symbol {next:?}").into())
};
if let Some(Token { value: Value::Str(path), .. }) = exp.peek() {
let token = exp.peek();
if let Some(Token { value: Value::Str(path), .. }) = token {
let path = base.as_ref().parent().unwrap().join(unquote(path));
if !std::fs::exists(&path)? {
return Err(format!("(e5) not found: {path:?}").into())
@ -448,7 +449,11 @@ impl Configuration {
map.add_layer_if(
Box::new(move |state|{
let mut exp = exp.clone();
Context::get(state, &mut exp).unwrap_or(false)
let value = Context::get(state, &mut format!(":{cond}").as_str().into()).unwrap_or(false);
if value {
panic!("layer-if cond={cond:?} exp={exp:?} value={value:?}");
}
value
}),
keys
);

View file

@ -123,13 +123,14 @@ impl App {
self.project.view_scenes_clips()
}
pub fn view_tracks_inputs <'a> (&'a self) -> impl Content<TuiOut> + use<'a> {
Fixed::y(1 + self.project.midi_ins.len() as u16, self.project.view_inputs(self.color))
Fixed::y(1 + self.project.midi_ins.len() as u16,
self.project.view_inputs(self.color))
}
pub fn view_tracks_outputs <'a> (&'a self) -> impl Content<TuiOut> + use<'a> {
Fixed::y(1 + self.project.midi_outs.len() as u16, self.project.view_outputs(self.color))
self.project.view_outputs(self.color)
}
pub fn view_tracks_devices <'a> (&'a self) -> impl Content<TuiOut> + use<'a> {
Fixed::y(3, self.project.view_track_devices(self.color))
Fixed::y(4, self.project.view_track_devices(self.color))
}
pub fn view_tracks_names <'a> (&'a self) -> impl Content<TuiOut> + use<'a> {
Fixed::y(2, self.project.view_track_names(self.color))

View file

@ -39,6 +39,14 @@ impl ArrangementCommand {
}
}
};
if let Some(editor) = arranger.editor.as_mut() {
if let Some(clip) = editor.clip() {
let length = clip.read().unwrap().length.max(1);
let width = arranger.inner_size.w().saturating_sub(20).max(1);
editor.set_time_zoom(length / width);
editor.redraw();
}
}
Ok(None)
}
/// Set the selection

View file

@ -2,91 +2,98 @@ use crate::*;
impl Arrangement {
pub fn view_inputs <'a> (&'a self, theme: ItemTheme) -> impl Content<TuiOut> + 'a {
let mut h = 0;
let mut h = 1u16;
for track in self.tracks().iter() {
h = h.max(self.midi_ins.len() as u16);
}
let h = h + 1;
self.view_track_row_section(
theme,
Bsp::s(
Fixed::y(1, Fill::x(Align::w(button_3("i", "nput ", format!("{}", self.midi_ins.len()), false)))),
Fixed::y(h - 1, Fill::x(Align::nw(Stack::south(move|add: &mut dyn FnMut(&dyn Render<TuiOut>)|{
for (index, port) in self.midi_ins.iter().enumerate() {
add(&Fill::x(Align::w(format!("·i{index:02} {}", port.name()))));
}
}))))),
button_2("I", "+", false),
Tui::bg(theme.darker.rgb, Align::w(Fill::x(
Stack::east(move|add: &mut dyn FnMut(&dyn Render<TuiOut>)|{
for (index, track, x1, x2) in self.tracks_with_sizes() {
add(&Fixed::x(self.track_width(index, track),
Stack::south(move|add: &mut dyn FnMut(&dyn Render<TuiOut>)|{
let index = 0;
add(&Fixed::y(1, track.sequencer.midi_ins.get(0).map(|port|
Tui::fg_bg(Rgb(255, 255, 255), track.color.base.rgb,
Fill::x(Align::w(format!("·i{index:02} {}", port.name())))))));
for (index, port) in self.midi_ins().iter().enumerate() {
add(&Fixed::y(1, Align::w(row!(
Either(track.sequencer.monitoring, Tui::fg(Green, "●mon "), "·mon "),
Either(track.sequencer.recording, Tui::fg(Red, "●rec "), "·rec "),
Either(track.sequencer.overdub, Tui::fg(Yellow, "●dub "), "·dub "),
))));
}
})))}})))))
Stack::south(move|add: &mut dyn FnMut(&dyn Render<TuiOut>)|{
add(&Fixed::y(1,
Bsp::e(Fixed::x(20, Align::w(button_3("i", "nput ", format!("{}", self.midi_ins.len()), false))),
Bsp::w(Fixed::x(4, button_2("I", "+", false)),
Stack::east(move|add: &mut dyn FnMut(&dyn Render<TuiOut>)|{
for (index, track, x1, x2) in self.tracks_with_sizes() {
add(&Tui::bg(track.color.dark.rgb, Align::w(Fixed::x(track.width as u16, row!(
Either(track.sequencer.monitoring, Tui::fg(Green, "mon "), "mon "),
Either(track.sequencer.recording, Tui::fg(Red, "rec "), "rec "),
Either(track.sequencer.overdub, Tui::fg(Yellow, "dub "), "dub "),
)))))
}
})))));
for (index, port) in self.midi_ins().iter().enumerate() {
add(&Fixed::y(1, Bsp::e(
Fixed::x(20, Align::w(Bsp::e("", Tui::bold(true, Tui::fg(Rgb(255,255,255),port.name()))))),
Bsp::w(Fixed::x(4, ()),
Stack::east(move|add: &mut dyn FnMut(&dyn Render<TuiOut>)|{
for (index, track, x1, x2) in self.tracks_with_sizes() {
add(&Tui::bg(track.color.darker.rgb, Align::w(Fixed::x(track.width as u16, row!(
Either(track.sequencer.monitoring, Tui::fg(Green, ""), " · "),
Either(track.sequencer.recording, Tui::fg(Red, ""), " · "),
Either(track.sequencer.overdub, Tui::fg(Yellow, ""), " · "),
)))))
}
})))));
}
})
}
pub fn view_outputs <'a> (&'a self, theme: ItemTheme) -> impl Content<TuiOut> + 'a {
let mut h = 1u16;
for track in self.tracks().iter() {
h = h.max(track.sequencer.midi_outs.len() as u16);
let mut h = 1;
for output in self.midi_outs().iter() {
h += 1 + output.conn().len();
}
let h = h + 1;
self.view_track_row_section(
theme,
Bsp::s(
Fixed::y(1, Fill::x(Align::w(button_3("o", "utput", format!("{}", self.midi_outs.len()), false)))),
Fixed::y(h - 1, Fill::xy(Align::nw(Stack::south(|add: &mut dyn FnMut(&dyn Render<TuiOut>)|{
for (index, port) in self.midi_outs().iter().enumerate() {
add(&Fill::x(Align::w(format!("·o{index:02} {}", port.name()))));
let h = h as u16;
let list = Bsp::s(
Fixed::y(1, Fill::x(Align::w(button_3("o", "utput", format!("{}", self.midi_outs.len()), false)))),
Fixed::y(h - 1, Fill::xy(Align::nw(Stack::south(|add: &mut dyn FnMut(&dyn Render<TuiOut>)|{
for (index, port) in self.midi_outs().iter().enumerate() {
add(&Fixed::y(1,Fill::x(Bsp::e(
Align::w(Bsp::e("", Tui::fg(Rgb(255,255,255),Tui::bold(true, port.name())))),
Fill::x(Align::e(format!("{}/{} ",
port.port().get_connections().len(),
port.conn().len())))))));
for (index, conn) in port.conn().iter().enumerate() {
add(&Fixed::y(1, Fill::x(Align::w(format!(" c{index:02}{}", conn.info())))));
}
}))))),
button_2("O", "+", false),
}
})))));
Fixed::y(h, self.view_track_row_section(theme, list, button_2("O", "+", false),
Tui::bg(theme.darker.rgb, Align::w(Fill::x(
Stack::east(move|add: &mut dyn FnMut(&dyn Render<TuiOut>)|{
for (index, track, x1, x2) in self.tracks_with_sizes() {
add(&Fixed::x(self.track_width(index, track),
Stack::south(move|add: &mut dyn FnMut(&dyn Render<TuiOut>)|{
let index = 0;
add(&Fixed::y(1, track.sequencer.midi_outs.get(0).map(|port|
Tui::fg_bg(Rgb(255, 255, 255), track.color.base.rgb,
Fill::x(Align::w(format!("·o{index:02} {}", port.name())))))));
add(&Fixed::y(1, Align::w(Bsp::e(
Either(true, Tui::fg(Green, "play "), "play "),
Either(false, Tui::fg(Yellow, "solo "), "solo "),
))));
for (index, port) in self.midi_outs().iter().enumerate() {
add(&Fixed::y(1, Align::w(Bsp::e(
Either(true, Tui::fg(Green, "●play "), "·play "),
Either(false, Tui::fg(Yellow, "●solo "), "·solo "),
Either(true, Tui::fg(Green, ""), " · "),
Either(false, Tui::fg(Yellow, ""), " · "),
))));
}
})));
}
})))))
for (index, conn) in port.conn().iter().enumerate() {
add(&Fixed::y(1, Fill::x("")));
}
}})))}}))))))
}
pub fn view_track_devices <'a> (&'a self, theme: ItemTheme) -> impl Content<TuiOut> + 'a {
let mut h = 2u16;
for track in self.tracks().iter() {
h = h.max(track.devices.len() as u16);
h = h.max(track.devices.len() as u16 * 2);
}
self.view_track_row_section(
theme,
button_3("d", "evice", format!("{}", self.track().map(|t|t.devices.len()).unwrap_or(0)), false),
button_3("d", "evice", format!("{}", self.track().map(|t|t.devices.len()).unwrap_or(0)), false),
button_2("D", "+", false),
Fixed::y(h, Tui::bg(theme.darker.rgb, Align::w(Fill::x(Stack::east(
move|add: &mut dyn FnMut(&dyn Render<TuiOut>)|{
for (index, track, x1, x2) in self.tracks_with_sizes() {
add(&Fixed::xy(self.track_width(index, track), h + 1,
Tui::bg(track.color.dark.rgb, Align::nw(Map::south(1, move||0..h,
|_, index|format!("·d{index:02} {}", "--------"))))));
}
}))))))
Stack::east(move|add: &mut dyn FnMut(&dyn Render<TuiOut>)|{
for (index, track, x1, x2) in self.tracks_with_sizes() {
add(&Fixed::xy(self.track_width(index, track), h + 1,
Tui::bg(track.color.dark.rgb, Align::nw(Map::south(2, move||0..h,
|_, index|Fixed::xy(track.width as u16, 2, Tui::bg(ItemTheme::G[32].base.rgb,
Align::nw(format!(" · {}", "--")))))))));
}
}))
}
}
@ -135,15 +142,10 @@ pub trait TracksView:
theme: ItemTheme,
button: impl Content<TuiOut>,
button_add: impl Content<TuiOut>,
content: impl Content<TuiOut>
content: impl Content<TuiOut>
) -> impl Content<TuiOut> {
Bsp::w(
Fill::y(Fixed::x(4, Align::nw(button_add))),
Bsp::e(
Fixed::x(20, Fill::y(Align::nw(button))),
Fill::xy(Align::c(content))
)
)
Bsp::w(Fill::y(Fixed::x(4, Align::nw(button_add))),
Bsp::e(Fixed::x(20, Fill::y(Align::nw(button))), Fill::xy(Align::c(content))))
}
fn view_track_header <'a, T: Content<TuiOut>> (
&'a self, theme: ItemTheme, content: T
@ -212,7 +214,7 @@ pub trait TracksView:
}
self.view_track_row_section(
theme,
button_2("i", "nput", false),
button_2("i", "nput", false),
button_2("I", "+", false),
Tui::bg(theme.darker.rgb, Align::w(Fill::x(
Stack::east(move|add: &mut dyn FnMut(&dyn Render<TuiOut>)|{
@ -231,13 +233,7 @@ pub trait TracksView:
}})))))
}
fn track_width (&self, index: usize, track: &Track) -> u16 {
(if self.selection().track() == Some(index)
&& let Some(editor) = self.editor()
{
editor.width().max(24).max(track.width)
} else {
track.width
}) as u16
track.width as u16
}
}
@ -247,7 +243,7 @@ pub trait ScenesView:
HasSceneScroll +
HasClipsSize +
Send +
Sync
Sync
{
/// Default scene height.
const H_SCENE: usize = 2;
@ -257,15 +253,13 @@ pub trait ScenesView:
fn w_side (&self) -> u16;
fn w_mid (&self) -> u16;
fn scenes_with_sizes (&self) -> impl ScenesSizes<'_> {
let editing = self.editor().is_some();
let height = Self::H_SCENE;
let larger = 8;//FIXME//self.editor().map(|e|e.height()).unwrap_or(Self::H_SCENE);
let selected_track = self.selection().track();
let selected_scene = self.selection().scene();
let mut y = 0;
self.scenes().iter().enumerate().skip(self.scene_scroll()).map_while(move|(s, scene)|{
let active = editing && selected_track.is_some() && selected_scene == Some(s);
let height = if active { larger } else { height };
let height = if self.selection().scene() == Some(s) && self.editor().is_some() {
8
} else {
Self::H_SCENE
};
if y + height <= self.clips_size().h() {
let data = (s, scene, y, y + height);
y += height;
@ -276,15 +270,15 @@ pub trait ScenesView:
})
}
fn view_scenes_names (&self) -> impl Content<TuiOut> {
Stack::south(move|add: &mut dyn FnMut(&dyn Render<TuiOut>)|{
Fixed::x(20, Stack::south(move|add: &mut dyn FnMut(&dyn Render<TuiOut>)|{
for (index, scene, ..) in self.scenes_with_sizes() {
add(&self.view_scene_name(index, scene));
}
})
}))
}
fn view_scene_name (&self, index: usize, scene: &Scene) -> impl Content<TuiOut> {
let h = if self.selection().scene() == Some(index) && let Some(editor) = self.editor() {
(editor.height() as u16).max(12)
7
} else {
Self::H_SCENE as u16
};
@ -321,13 +315,7 @@ pub trait ClipsView:
for (track_index, track, _, _) in self.tracks_with_sizes() {
//column(&Fixed::x(5, Fill::xy(Tui::bg(Green, "kyp"))));
column(&Fixed::x(
if self.selection().track() == Some(track_index)
&& let Some(editor) = self.editor()
{
editor.width().max(24).max(track.width)
} else {
track.width
} as u16,
track.width as u16,
Fill::y(self.view_track_clips(track_index, track))
))
}

View file

@ -13,9 +13,15 @@ impl MidiEditor {
(clip.color, clip.name.clone(), clip.length, clip.looped)
} else { (ItemTheme::G[64], String::new().into(), 0, false) };
Fixed::x(20, col!(
Fill::x(Align::w(Bsp::e(" Clip ", format!("{name}")))),
Fill::x(Align::w(Bsp::e(" Length ", format!("{length}")))),
Fill::x(Align::w(Bsp::e(" Loop ", looped.to_string()))),
Fill::x(Align::w(Bsp::e(
button_2("f2", "name ", false),
Fill::x(Align::e(Tui::fg(Rgb(255, 255, 255), format!("{name} "))))))),
Fill::x(Align::w(Bsp::e(
button_2("l", "ength ", false),
Fill::x(Align::e(Tui::fg(Rgb(255, 255, 255), format!("{length} "))))))),
Fill::x(Align::w(Bsp::e(
button_2("r", "epeat ", false),
Fill::x(Align::e(Tui::fg(Rgb(255, 255, 255), format!("{looped} "))))))),
))
}
@ -31,9 +37,18 @@ impl MidiEditor {
let note_pos = format!("{:>3}", note_pos);
let note_len = format!("{:>4}", self.get_note_len());
Fixed::x(20, col!(
Fill::x(Align::w(Bsp::e(" Time ", format!("{length}/{time_zoom}+{time_pos}")))),
Fill::x(Align::w(Bsp::e(" Lock ", format!("{time_lock}")))),
Fill::x(Align::w(Bsp::e(" Note ", format!("{note_name} {note_pos} {note_len}")))),
Fill::x(Align::w(Bsp::e(
button_2("t", "ime ", false),
Fill::x(Align::e(Tui::fg(Rgb(255, 255, 255),
format!("{length} /{time_zoom} +{time_pos} "))))))),
Fill::x(Align::w(Bsp::e(
button_2("z", "lock ", false),
Fill::x(Align::e(Tui::fg(Rgb(255, 255, 255),
format!("{time_lock}"))))))),
Fill::x(Align::w(Bsp::e(
button_2("x", "note ", false),
Fill::x(Align::e(Tui::fg(Rgb(255, 255, 255),
format!("{note_name} {note_pos} {note_len}"))))))),
))
}

View file

@ -38,3 +38,42 @@ mod dialog; pub use self::dialog::*;
#[cfg(feature = "vst2")] mod vst2; #[cfg(feature = "vst2")] pub use self::vst2::*;
#[cfg(feature = "vst3")] mod vst3; #[cfg(feature = "vst3")] pub use self::vst3::*;
#[cfg(feature = "clap")] mod clap; #[cfg(feature = "clap")] pub use self::clap::*;
pub fn button_2 <'a> (
key: impl Content<TuiOut> + 'a, label: impl Content<TuiOut> + 'a, editing: bool,
) -> impl Content<TuiOut> + 'a {
let key = Tui::fg_bg(Tui::orange(), Tui::g(0), Bsp::e(
Tui::fg(Tui::g(0), ""),
Bsp::e(key, Tui::fg(Tui::g(96), ""))
));
let label = When::new(!editing, Tui::fg_bg(Tui::g(255), Tui::g(96), label));
Tui::bold(true, Bsp::e(key, label))
}
pub fn button_3 <'a, K, L, V> (
key: K,
label: L,
value: V,
editing: bool,
) -> impl Content<TuiOut> + 'a where
K: Content<TuiOut> + 'a,
L: Content<TuiOut> + 'a,
V: Content<TuiOut> + 'a,
{
let key = Tui::fg_bg(Tui::orange(), Tui::g(0),
Bsp::e(Tui::fg(Tui::g(0), ""), Bsp::e(key, Tui::fg(if editing {
Tui::g(128)
} else {
Tui::g(96)
}, ""))));
let label = Bsp::e(
When::new(!editing, Bsp::e(
Tui::fg_bg(Tui::g(255), Tui::g(96), label),
Tui::fg_bg(Tui::g(128), Tui::g(96), ""),
)),
Bsp::e(
Tui::fg_bg(Tui::g(224), Tui::g(128), value),
Tui::fg_bg(Tui::g(128), Reset, ""),
));
Tui::bold(true, Bsp::e(key, label))
}

2
deps/tengri vendored

@ -1 +1 @@
Subproject commit f21781e81664e1991e3985e2377becca9c1d58cf
Subproject commit 921378b6dbb38d4f301f688abd1cfef9bdc0f941