mirror of
https://github.com/storytold/soundcraft.git
synced 2026-10-26 19:46:47 -06:00
The Edit window painted the bar and grid lines on the empty lane and then the opaque clip bodies on top, so the grid vanished wherever an audio or MIDI clip sat. Pro Tools draws grid lines over the clips. - While grid lines are shown, the lines are drawn after the clips (audio, MIDI, video and their overlays) and before automation and the selection. - The lines are a translucent white (Dark) or black (Light), worked out from the palette's line and lane colours, so a line on an empty lane looks as before and waveforms and notes stay readable under it. Bar lines now also stand out where a grid line lands on them; the opaque grid line used to cover them. - With grid lines hidden, the bar lines stay behind the clips as before. Test: tests/grid_lines.rs renders the demo Edit window in both themes and checks that the lines over the Kick clip are painted after it only while grid lines are shown, and that each one looks like the theme's line colour on an empty lane.
71 lines
3.1 KiB
Rust
71 lines
3.1 KiB
Rust
use egui::{Color32, FullOutput, Pos2, RawInput, Rect, Shape, pos2, vec2};
|
|
use serde_json::json;
|
|
use soundcraft_ui_egui::theme::Tokens;
|
|
use soundcraft_ui_egui::{Services, SoundApp, edit_window};
|
|
|
|
fn frame(ctx: &egui::Context, app: &mut SoundApp) -> FullOutput {
|
|
let raw = RawInput { screen_rect: Some(Rect::from_min_size(Pos2::ZERO, vec2(1600.0, 1000.0))), ..Default::default() };
|
|
let mut out = ctx.run_ui(raw, |ui| {
|
|
app.logic(ui.ctx());
|
|
app.ui(ui);
|
|
});
|
|
out.textures_delta.clear();
|
|
out
|
|
}
|
|
|
|
fn over(top: Color32, bg: Color32) -> [i32; 3] {
|
|
let keep = 255 - i32::from(top.a());
|
|
[(top.r(), bg.r()), (top.g(), bg.g()), (top.b(), bg.b())].map(|(t, b)| i32::from(t) + i32::from(b) * keep / 255)
|
|
}
|
|
|
|
fn near(a: [i32; 3], b: Color32) -> bool {
|
|
a.iter().zip([b.r(), b.g(), b.b()]).all(|(x, y)| (x - i32::from(y)).abs() <= 6)
|
|
}
|
|
|
|
#[test]
|
|
fn grid_lines_are_drawn_over_clips_while_shown() {
|
|
let mut app = SoundApp::new(soundcraft_engine::demo::demo_engine(), None, Services::default());
|
|
let ctx = egui::Context::default();
|
|
for theme in ["dark", "light"] {
|
|
for shown in [true, false] {
|
|
app.run("ui.theme", json!({"mode": theme})).unwrap();
|
|
app.run("view.grid_lines", json!({"value": shown})).unwrap();
|
|
frame(&ctx, &mut app);
|
|
let out = frame(&ctx, &mut app);
|
|
let t = Tokens::current();
|
|
let s = app.engine.session().clone();
|
|
let kick = s.track_by_name("Kick").unwrap();
|
|
let clip = kick.clips()[0].clone();
|
|
let [x0, y0, x1, y1] = app.edit_layout.timeline;
|
|
let tl = Rect::from_min_max(pos2(x0, y0), pos2(x1, y1));
|
|
let row = app.edit_layout.rows.iter().find(|(id, _)| *id == kick.id.0).unwrap().1;
|
|
let lane = Rect::from_min_max(pos2(tl.min.x, row[1]), pos2(tl.max.x, row[1] + kick.height.points()));
|
|
let (cx0, cx1) = (edit_window::x_of(&s, tl, clip.start), edit_window::x_of(&s, tl, clip.end()));
|
|
let body = out
|
|
.shapes
|
|
.iter()
|
|
.position(|c| matches!(&c.shape, Shape::Rect(r) if r.rect.min.x == cx0 && r.rect.max.x == cx1 && r.rect.min.y == lane.min.y + 1.0))
|
|
.unwrap();
|
|
let lines: Vec<(usize, Color32)> = out
|
|
.shapes
|
|
.iter()
|
|
.enumerate()
|
|
.filter_map(|(i, c)| match &c.shape {
|
|
Shape::LineSegment { points: [a, b], stroke }
|
|
if a.x == b.x && a.x > cx0 + 1.0 && a.x < cx1 - 1.0 && a.y == lane.min.y && b.y == lane.max.y =>
|
|
{
|
|
Some((i, stroke.color))
|
|
}
|
|
_ => None,
|
|
})
|
|
.collect();
|
|
assert!(lines.len() > 4, "{theme} {shown}: {} lines", lines.len());
|
|
assert!(lines.iter().all(|(i, _)| (*i > body) == shown), "{theme} {shown}");
|
|
for (_, col) in &lines {
|
|
let look = over(*col, t.playlist_bg);
|
|
assert!(near(look, t.grid_line) || near(look, t.bar_line), "{theme} {shown}: {look:?}");
|
|
}
|
|
}
|
|
}
|
|
}
|