Compare commits

..

No commits in common. "2d7ca155e02f7beaf91eac5cf28fc63dc9982391" and "86869a1110d4af5edcd01c2fe58201482456dc60" have entirely different histories.

13 changed files with 552 additions and 694 deletions

1098
Cargo.lock generated

File diff suppressed because it is too large Load diff

2
deps/dizzle vendored

@ -1 +1 @@
Subproject commit 5af7c8b09b1469f95073092fb3dc23e636018e64
Subproject commit 361874ff72b19d2386f3cb6e8604e673c02203c9

2
deps/tengri vendored

@ -1 +1 @@
Subproject commit 090546b2d6fba3beb69b8ff65b30e5492d8a7b66
Subproject commit d5976cae8e858f717ff051602f7d8570d6eab30d

View file

@ -44,35 +44,30 @@ impl HasJack<'static> for Arrangement {
&self.jack
}
}
has!(Jack<'static>: |self: Arrangement|self.jack);
has!(Measure<TuiOut>: |self: Arrangement|self.size);
has!(Jack<'static>: |self: Arrangement|self.jack);
has!(Measure<TuiOut>: |self: Arrangement|self.size);
#[cfg(feature = "editor")] has!(Option<MidiEditor>: |self: Arrangement|self.editor);
#[cfg(feature = "port")] has!(Vec<MidiInput>: |self: Arrangement|self.midi_ins);
#[cfg(feature = "port")] has!(Vec<MidiOutput>: |self: Arrangement|self.midi_outs);
#[cfg(feature = "clock")] has!(Clock: |self: Arrangement|self.clock);
#[cfg(feature = "select")] has!(Selection: |self: Arrangement|self.selection);
#[cfg(feature = "port")] has!(Vec<MidiInput>: |self: Arrangement|self.midi_ins);
#[cfg(feature = "port")] has!(Vec<MidiOutput>: |self: Arrangement|self.midi_outs);
#[cfg(feature = "clock")] has!(Clock: |self: Arrangement|self.clock);
#[cfg(feature = "select")] has!(Selection: |self: Arrangement|self.selection);
#[cfg(all(feature = "select", feature = "track"))] has!(Vec<Track>: |self: Arrangement|self.tracks);
#[cfg(all(feature = "select", feature = "track"))] maybe_has!(Track: |self: Arrangement|
{ Has::<Selection>::get(self).track().map(|index|Has::<Vec<Track>>::get(self).get(index)).flatten() };
{ Has::<Selection>::get(self).track().map(|index|Has::<Vec<Track>>::get_mut(self).get_mut(index)).flatten() });
#[cfg(all(feature = "select", feature = "scene"))] has!(Vec<Scene>: |self: Arrangement|self.scenes);
#[cfg(all(feature = "select", feature = "scene"))] maybe_has!(Scene: |self: Arrangement|
{ Has::<Selection>::get(self).track().map(|index|Has::<Vec<Scene>>::get(self).get(index)).flatten() };
{ Has::<Selection>::get(self).track().map(|index|Has::<Vec<Scene>>::get_mut(self).get_mut(index)).flatten() });
#[cfg(feature = "select")] impl Arrangement {
#[cfg(feature = "clip")] fn selected_clip (&self) -> Option<MidiClip> { todo!() }
#[cfg(feature = "scene")] fn selected_scene (&self) -> Option<Scene> { todo!() }
#[cfg(feature = "track")] fn selected_track (&self) -> Option<Track> { todo!() }
#[cfg(feature = "port")] fn selected_midi_in (&self) -> Option<MidiInput> { todo!() }
#[cfg(feature = "port")] fn selected_midi_out (&self) -> Option<MidiOutput> { todo!() }
fn selected_device (&self) -> Option<Device> {
todo!()
}
#[cfg(feature = "select")]
impl Arrangement {
#[cfg(feature = "clip")] fn selected_clip (&self) -> Option<MidiClip> { todo!() }
#[cfg(feature = "scene")] fn selected_scene (&self) -> Option<Scene> { todo!() }
#[cfg(feature = "track")] fn selected_track (&self) -> Option<Track> { todo!() }
#[cfg(feature = "port")] fn selected_midi_in (&self) -> Option<MidiInput> { todo!() }
#[cfg(feature = "port")] fn selected_midi_out (&self) -> Option<MidiOutput> { todo!() }
fn selected_device (&self) -> Option<Device> { todo!() }
fn unselect (&self) -> Selection {
Selection::Nothing
}
@ -314,13 +309,13 @@ impl Arrangement {
#[cfg(feature = "scene")]
impl ScenesView for Arrangement {
fn h_scenes (&self) -> u16 {
(self.measure_height() as u16).saturating_sub(20)
(self.height() as u16).saturating_sub(20)
}
fn w_side (&self) -> u16 {
(self.measure_width() as u16 * 2 / 10).max(20)
(self.width() as u16 * 2 / 10).max(20)
}
fn w_mid (&self) -> u16 {
(self.measure_width() as u16).saturating_sub(2 * self.w_side()).max(40)
(self.width() as u16).saturating_sub(2 * self.w_side()).max(40)
}
}
@ -371,8 +366,8 @@ impl Arrangement {
}
pub(crate) fn wrap (bg: Color, fg: Color, content: impl Content<TuiOut>) -> impl Content<TuiOut> {
let left = Tui::fg_bg(bg, Reset, Fixed::X(1, Repeat::Y("")));
let right = Tui::fg_bg(bg, Reset, Fixed::X(1, Repeat::Y("")));
let left = Tui::fg_bg(bg, Reset, Fixed::X(1, RepeatV("")));
let right = Tui::fg_bg(bg, Reset, Fixed::X(1, RepeatV("")));
Bsp::e(left, Bsp::w(right, Tui::fg_bg(fg, bg, content)))
}

View file

@ -53,7 +53,15 @@ impl Browse {
files.push((name, format!("📄 {decoded}")));
}
}
Ok(Self { cwd, dirs, files, ..Default::default() })
Ok(Self {
cwd,
dirs,
files,
filter: "".to_string(),
index: 0,
scroll: 0,
size: Measure::new(),
})
}
pub fn len (&self) -> usize {

View file

@ -186,16 +186,16 @@ pub trait ClipsView:
let w = if self.selection().track() == Some(track_index)
&& let Some(editor) = self.editor ()
{
(editor.measure_width() as usize).max(24).max(track.width)
editor.width().max(24).max(track.width)
} else {
track.width
} as u16;
let y = if self.selection().scene() == Some(scene_index)
&& let Some(editor) = self.editor ()
{
(editor.measure_height() as usize).max(12)
editor.height().max(12)
} else {
Self::H_SCENE as usize
Self::H_SCENE
} as u16;
to.place(&Fixed::XY(w, y, Bsp::b(

View file

@ -1,23 +1,5 @@
use crate::*;
impl<T: Has<Option<MidiEditor>>> HasEditor for T {}
pub trait HasEditor: Has<Option<MidiEditor>> {
fn editor (&self) -> Option<&MidiEditor> {
self.get().as_ref()
}
fn editor_mut (&mut self) -> Option<&mut MidiEditor> {
self.get_mut().as_mut()
}
fn is_editing (&self) -> bool {
self.editor().is_some()
}
fn editor_w (&self) -> usize {
self.editor().map(|e|e.size.w()).unwrap_or(0) as usize
}
fn editor_h (&self) -> usize {
self.editor().map(|e|e.size.h()).unwrap_or(0) as usize
}
}
#[macro_export] macro_rules! has_editor {
(|$self:ident: $Struct:ident|{
editor = $e0:expr;
@ -34,6 +16,14 @@ pub trait HasEditor: Has<Option<MidiEditor>> {
}
};
}
impl<T: Has<Option<MidiEditor>>> HasEditor for T {}
pub trait HasEditor: Has<Option<MidiEditor>> {
fn editor (&self) -> Option<&MidiEditor> { self.get().as_ref() }
fn editor_mut (&mut self) -> Option<&mut MidiEditor> { self.get_mut().as_mut() }
fn is_editing (&self) -> bool { self.editor().is_some() }
fn editor_w (&self) -> usize { self.editor().map(|e|e.size.w()).unwrap_or(0) }
fn editor_h (&self) -> usize { self.editor().map(|e|e.size.h()).unwrap_or(0) }
}
/// Contains state for viewing and editing a clip
pub struct MidiEditor {
@ -44,14 +34,7 @@ pub struct MidiEditor {
}
has!(Measure<TuiOut>: |self: MidiEditor|self.size);
impl Default for MidiEditor {
fn default () -> Self {
Self {
size: Measure::new(0, 0),
mode: PianoHorizontal::new(None),
}
}
}
impl Default for MidiEditor { fn default () -> Self { Self { size: Measure::new(), mode: PianoHorizontal::new(None), } } }
impl std::fmt::Debug for MidiEditor {
fn fmt (&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
f.debug_struct("MidiEditor").field("mode", &self.mode).finish()
@ -187,12 +170,8 @@ impl MidiEditor {
}
}
impl Draw<TuiOut> for MidiEditor {
fn draw (&self, to: &mut TuiOut) { self.content().draw(to) }
}
impl Layout<TuiOut> for MidiEditor {
fn layout (&self, to: XYWH<u16>) -> XYWH<u16> { self.content().layout(to) }
}
impl Draw<TuiOut> for MidiEditor { fn draw (&self, to: &mut TuiOut) { self.content().draw(to) } }
impl Layout<TuiOut> for MidiEditor { fn layout (&self, to: [u16;4]) -> [u16;4] { self.content().layout(to) } }
impl HasContent<TuiOut> for MidiEditor {
fn content (&self) -> impl Content<TuiOut> { self.autoscroll(); /*self.autozoom();*/ self.size.of(&self.mode) }
}
@ -264,7 +243,7 @@ has!(Measure<TuiOut>:|self:PianoHorizontal|self.size);
impl PianoHorizontal {
pub fn new (clip: Option<&Arc<RwLock<MidiClip>>>) -> Self {
let size = Measure::new(0, 0);
let size = Measure::new();
let mut range = MidiRangeModel::from((12, true));
range.time_axis = size.x.clone();
range.note_axis = size.y.clone();
@ -288,12 +267,8 @@ pub(crate) fn note_y_iter (note_lo: usize, note_hi: usize, y0: u16)
(note_lo..=note_hi).rev().enumerate().map(move|(y, n)|(y, y0 + y as u16, n))
}
impl Draw<TuiOut> for PianoHorizontal {
fn draw (&self, to: &mut TuiOut) { self.content().draw(to) }
}
impl Layout<TuiOut> for PianoHorizontal {
fn layout (&self, to: XYWH<u16>) -> XYWH<u16> { self.content().layout(to) }
}
impl Draw<TuiOut> for PianoHorizontal { fn draw (&self, to: &mut TuiOut) { self.content().draw(to) } }
impl Layout<TuiOut> for PianoHorizontal { fn layout (&self, to: [u16;4]) -> [u16;4] { self.content().layout(to) } }
impl HasContent<TuiOut> for PianoHorizontal {
fn content (&self) -> impl Content<TuiOut> {
Bsp::s(
@ -382,7 +357,7 @@ impl PianoHorizontal {
let buffer = self.buffer.clone();
Thunk::new(move|to: &mut TuiOut|{
let source = buffer.read().unwrap();
let XYWH(x0, y0, w, _h) = to.area();
let [x0, y0, w, _h] = to.area().xywh();
//if h as usize != note_axis {
//panic!("area height mismatch: {h} <> {note_axis}");
//}
@ -416,7 +391,7 @@ impl PianoHorizontal {
let time_zoom = self.get_time_zoom();
let style = Some(Style::default().fg(self.color.lightest.rgb));
Thunk::new(move|to: &mut TuiOut|{
let XYWH(x0, y0, w, _) = to.area();
let [x0, y0, w, _] = to.area().xywh();
for (_area_y, screen_y, note) in note_y_iter(note_lo, note_hi, y0) {
if note == note_pos {
for x in 0..w {
@ -447,7 +422,7 @@ impl PianoHorizontal {
let off_style = Some(Style::default().fg(Tui::g(255)));
let on_style = Some(Style::default().fg(Rgb(255,0,0)).bg(color.base.rgb).bold());
Fill::Y(Fixed::X(self.keys_width, Thunk::new(move|to: &mut TuiOut|{
let XYWH(x, y0, _w, _h) = to.area();
let [x, y0, _w, _h] = to.area().xywh();
for (_area_y, screen_y, note) in note_y_iter(note_lo, note_hi, y0) {
to.blit(&to_key(note), x, screen_y, key_style);
if note > 127 {
@ -463,7 +438,7 @@ impl PianoHorizontal {
}
fn timeline (&self) -> impl Content<TuiOut> + '_ {
Fill::X(Fixed::Y(1, Thunk::new(move|to: &mut TuiOut|{
let XYWH(x, y, w, _h) = to.area();
let [x, y, w, _h] = to.area();
let style = Some(Style::default().dim());
let length = self.clip.as_ref().map(|p|p.read().unwrap().length).unwrap_or(1);
for (area_x, screen_x) in (0..w).map(|d|(d, d+x)) {

View file

@ -135,7 +135,7 @@ audio!(|self: Lv2, _client, scope|{
impl Draw<TuiOut> for Lv2 {
fn draw (&self, to: &mut TuiOut) {
let area = to.area();
let XYWH(x, y, _, height) = area;
let [x, y, _, height] = area;
let mut width = 20u16;
let start = self.selected.saturating_sub((height as usize / 2).saturating_sub(1));
let end = start + height as usize - 2;

View file

@ -12,7 +12,7 @@ pub struct Log10Meter(pub f32);
impl Layout<TuiOut> for Log10Meter {}
impl Draw<TuiOut> for Log10Meter {
fn draw (&self, to: &mut TuiOut) {
let XYWH(x, y, w, h) = to.area();
let [x, y, w, h] = to.area();
let signal = 100.0 - f32::max(0.0, f32::min(100.0, self.0.abs()));
let v = (signal * h as f32 / 100.0).ceil() as u16;
let y2 = y + h;
@ -36,7 +36,7 @@ pub struct RmsMeter(pub f32);
impl Layout<TuiOut> for RmsMeter {}
impl Draw<TuiOut> for RmsMeter {
fn draw (&self, to: &mut TuiOut) {
let XYWH(x, y, w, h) = to.area();
let [x, y, w, h] = to.area();
let signal = f32::max(0.0, f32::min(100.0, self.0.abs()));
let v = (signal * h as f32).ceil() as u16;
let y2 = y + h;

View file

@ -760,9 +760,9 @@ impl Sampler {
let lo_fg = Color::Rgb(64, 64, 64);
let lo_bg = if y == 7 { Color::Reset } else { tx_bg };
Fixed::XY(w, h, Bsp::s(
Fixed::Y(1, Tui::fg_bg(hi_fg, hi_bg, Repeat::X(Phat::<()>::LO))),
Fixed::Y(1, Tui::fg_bg(hi_fg, hi_bg, RepeatH(Phat::<()>::LO))),
Bsp::n(
Fixed::Y(1, Tui::fg_bg(lo_fg, lo_bg, Repeat::X(Phat::<()>::HI))),
Fixed::Y(1, Tui::fg_bg(lo_fg, lo_bg, RepeatH(Phat::<()>::HI))),
Fill::X(Fixed::Y(1, Tui::fg_bg(tx_fg, tx_bg, name))),
),
))
@ -883,7 +883,7 @@ fn draw_list_item (sample: &Option<Arc<RwLock<Sample>>>) -> String {
fn draw_viewer (sample: Option<&Arc<RwLock<Sample>>>) -> impl Content<TuiOut> + use<'_> {
let min_db = -64.0;
Thunk::new(move|to: &mut TuiOut|{
let XYWH(x, y, width, height) = to.area();
let [x, y, width, height] = to.area();
let area = Rect { x, y, width, height };
if let Some(sample) = &sample {
let sample = sample.read().unwrap();

View file

@ -108,7 +108,7 @@ pub trait ScenesView:
} else {
Self::H_SCENE
};
if y + height <= self.clips_size().h() as usize {
if y + height <= self.clips_size().h() {
let data = (s, scene, y, y + height);
y += height;
Some(data)

View file

@ -388,7 +388,7 @@ pub trait MidiRecord: MidiMonitor + HasClock + HasPlayClip {
}
}
pub trait MidiViewer: Measured<TuiOut> + MidiRange + MidiPoint + Debug + Send + Sync {
pub trait MidiViewer: HasSize<TuiOut> + MidiRange + MidiPoint + Debug + Send + Sync {
fn buffer_size (&self, clip: &MidiClip) -> (usize, usize);
fn redraw (&self);
fn clip (&self) -> &Option<Arc<RwLock<MidiClip>>>;

View file

@ -188,23 +188,23 @@ pub trait TracksView:
ScenesView +
HasMidiIns +
HasMidiOuts +
Measured<TuiOut> +
HasSize<TuiOut> +
HasTrackScroll +
HasSelection +
HasEditor +
HasClipsSize
{
fn tracks_width_available (&self) -> u16 {
(self.measure_width() as u16).saturating_sub(40)
(self.width() as u16).saturating_sub(40)
}
/// Iterate over tracks with their corresponding sizes.
fn tracks_with_sizes (&self) -> impl TracksSizes<'_> {
let _editor_width = self.editor().map(|e|e.measure_width());
let _editor_width = self.editor().map(|e|e.width());
let _active_track = self.selection().track();
let mut x = 0;
self.tracks().iter().enumerate().map_while(move |(index, track)|{
let width = track.width.max(8);
if x + width < self.clips_size().w() as usize {
if x + width < self.clips_size().w() {
let data = (index, track, x, x + width);
x += width + Self::TRACK_SPACING;
Some(data)