mirror of
https://github.com/storytold/soundcraft.git
synced 2026-11-01 22:46:49 -06:00
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.
62 lines
2.5 KiB
Rust
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);
|
|
}
|
|
}
|
|
}
|