Files
soundcraft/crates/ui-egui/tests/edit_io_readout.rs
grovebit d26d6fec77 Lay out Edit window tracks and plugin slots like Pro Tools
Measured from the user's Pro Tools screenshots:

- Track headers are 136 points wide (were 236). A 14-point colour strip
  holds an options button (a folder's open/close) and, at the bottom, a
  key that shows or hides volume automation. The name is bold beside a
  playlist menu and highlighted when the track is selected (the blue
  outline that marked a selected tinted header is gone). Then come
  16-point record, input, solo and mute keys (record stays dark red when
  off, and a mute implied by another track's solo shows dark orange);
  the track view; freeze, voice (dyn, or off to make the track
  inactive) and automation mode; timebase and the Elastic Audio
  algorithm (an instrument track's instrument); and a slim meter down
  the right edge. Rows drop away on shorter tracks.
- The vol/pan and inserts summaries and the AUX/INST labels are gone:
  the I/O and insert columns show them.
- The I/O column has input and output selectors, a fader key that shows
  the track in the Mix window, and a black vol/pan readout.
- Insert, send and instrument columns are 75 points wide on a neutral
  well, with 15-point cells every 18 points. An empty insert shows a
  dot, an empty send its letter. Tracks too short for three cells show
  a row of squares, with arrows for sends.
- A used slot is a rounded, raised grey button: its dot or letter, a
  groove, the name centred in 9.5-point bold, and for sends a small
  level box (the level in dB is in the tooltip; the box still opens the
  Send window or drags the level). A bypassed insert turns blue. The Mix
  window's slots share this drawing; filled ones don't brighten on
  hover, which would cost the name its contrast.
- New slot_well, slot_empty, slot_face, slot_bypassed and slot_mark
  colours replace insert_on, insert_bypass, send_on and slot_border.
  mix_instrument_contrast now reads the slot's painted gradient, and
  finds the empty instrument slot by its cell, which shows a dot
  rather than "no instrument".
- The header's record, input, solo and mute keys carry accessibility
  labels.

Part of #138.
2026-10-12 22:03:32 +01:00

62 lines
2.5 KiB
Rust

use egui::{Color32, Shape, vec2};
use egui_kittest::Harness;
use serde_json::json;
use soundcraft_ui_egui::{Services, SoundApp, theme::ThemeMode};
fn contrast(a: Color32, b: Color32) -> f64 {
let luminance = |color: Color32| {
let linear = |channel: u8| {
let s = f64::from(channel) / 255.0;
if s <= 0.04045 { s / 12.92 } else { ((s + 0.055) / 1.055).powf(2.4) }
};
0.2126 * linear(color.r()) + 0.7152 * linear(color.g()) + 0.0722 * linear(color.b())
};
let (a, b) = (luminance(a), luminance(b));
(a.max(b) + 0.05) / (a.min(b) + 0.05)
}
#[test]
fn the_io_columns_vol_and_pan_readout_is_readable_in_both_themes() {
for mode in [ThemeMode::Dark, ThemeMode::Light] {
let mut app = SoundApp::new(soundcraft_engine::demo::demo_engine(), None, Services::default());
app.run("view.edit_io", json!({})).unwrap();
app.run("track.height", json!({"tracks": ["Kick"], "height": "medium"})).unwrap();
app.run("ui.theme", json!({"mode": mode.id()})).unwrap();
let mut harness = Harness::builder().with_size(vec2(1600.0, 1000.0)).build_ui_state(
|ui, app: &mut SoundApp| {
app.logic(ui.ctx());
app.ui(ui);
},
app,
);
for _ in 0..4 {
harness.step();
}
let output = harness.output();
let labels: Vec<_> = output
.shapes
.iter()
.filter_map(|shape| match &shape.shape {
Shape::Text(text) if matches!(text.galley.job.text.as_str(), "vol" | "pan") => Some(text),
_ => None,
})
.collect();
assert!(!labels.is_empty(), "{mode:?}: no vol/pan readout painted");
for label in labels {
let center = label.visual_bounding_rect().center();
let well = output
.shapes
.iter()
.filter_map(|shape| match &shape.shape {
Shape::Rect(rect) if rect.rect.contains(center) && rect.fill.a() == 255 => Some(rect),
_ => None,
})
.min_by(|a, b| a.rect.area().total_cmp(&b.rect.area()))
.unwrap_or_else(|| panic!("{mode:?}: no readout behind {:?}", label.galley.job.text));
let ink = label.override_text_color.unwrap_or(label.galley.job.sections[0].format.color);
let ratio = contrast(ink, well.fill);
assert!(ratio >= 4.5, "{mode:?}: {:?} reads at {ratio:.2}:1", label.galley.job.text);
}
}
}