Compare commits

...

3 commits

Author SHA1 Message Date
stop screaming
2d7ca155e0 fix(device): last 8 left to full compile?
Some checks are pending
/ build (push) Waiting to run
2026-02-20 00:55:20 +02:00
stop screaming
82ff49b386 wip: fix: 15 errors left here 2026-02-20 00:50:15 +02:00
stop screaming
bba1f41ed5 fix: bump to fixed tengri 2026-02-20 00:38:41 +02:00
13 changed files with 698 additions and 556 deletions

1106
Cargo.lock generated

File diff suppressed because it is too large Load diff

2
deps/dizzle vendored

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

2
deps/tengri vendored

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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