Marker names are drawn in a small outlined box, in theme colours

A named marker drew its name as bare text beside the flag. After Effects puts
it in a small outlined box. The box uses the theme's field fill, field outline
and text tokens, so it follows Dark, Darker and Light instead of a fixed grey.

It applies to every marker with a name, composition markers included; the dimmed
names of nested-comp markers on a precomp layer's bar are unchanged.

Split out of the WAV markers change (#1423), where the review asked for it to be
its own decision. Test: ui_marker_names renders the timeline in Dark and in
Light, samples the box's fill and outline pixels, and checks they equal that
theme's tokens and differ between the themes.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Kristian Myram
2026-10-18 17:38:28 +02:00
co-authored by Claude Sonnet 5.5
parent f3f24ef189
commit 4124ec6de7
2 changed files with 57 additions and 1 deletions
+6 -1
View File
@@ -298,7 +298,12 @@ fn marker(app: &mut EffectcraftApp, ui: &mut egui::Ui, p: &egui::Painter, comp:
}
p.add(egui::Shape::convex_polygon(marker_shape(x, y0, h), col, Stroke::NONE));
if !m.comment.is_empty() {
p.text(pos2(x + 7.0, y0 + h / 2.0), Align2::LEFT_CENTER, m.comment.lines().next().unwrap_or_default(), Tokens::ui(10.0), t.text);
// The name sits in a small outlined box beside the flag, as in After Effects. It is drawn in the theme's field
// colours, so it follows Dark, Darker and Light.
let galley = p.layout_no_wrap(m.comment.lines().next().unwrap_or_default().to_string(), Tokens::ui(10.0), t.text);
let box_rect = Rect::from_min_size(pos2(x + 6.0, y0), vec2(galley.size().x + 8.0, h.max(galley.size().y + 2.0)));
p.rect(box_rect, t.radius_sm, t.field_bg, Stroke::new(1.0, t.field_border), egui::StrokeKind::Inside);
p.galley(pos2(box_rect.min.x + 4.0, box_rect.center().y - galley.size().y / 2.0), galley, t.text);
}
let hit = Rect::from_min_max(pos2(x - 5.0, y0 - 1.0), pos2(x + 5.0, y1));
let id = egui::Id::new(("marker", r.layer, r.index));
+51
View File
@@ -0,0 +1,51 @@
//! A marker's name is drawn in a small outlined box beside its flag, in the theme's own field colours (egui_kittest, a real
//! render): the fill and the outline follow Dark and Light, and the two themes differ. `MARKER_NAMES_SNAPSHOT=/abs/dir`
//! also saves the rendered panel for each theme.
use effectcraft_engine::Session;
use effectcraft_ui_egui::EffectcraftApp;
use effectcraft_ui_egui::theme::ThemeKind;
use egui_kittest::Harness;
use serde_json::json;
fn close(got: [u8; 4], want: egui::Color32) -> bool {
got[..3].iter().zip(want.to_array()).all(|(a, b)| a.abs_diff(b) <= 3)
}
#[test]
fn the_name_box_follows_the_theme() {
let mut s = Session::default();
s.execute("comp.new", json!({"name": "Song", "width": 640, "height": 360, "duration": 4})).unwrap();
s.execute("layer.newSolid", json!({"name": "Bed", "color": "#204060", "width": 640, "height": 360})).unwrap();
s.execute("markers.set", json!({"new": true, "time": 1.0, "comment": "Verse"})).unwrap();
let mut h = Harness::builder().with_size(egui::vec2(1600.0, 1000.0)).build_eframe(move |_| EffectcraftApp::new(s));
h.run_steps(4);
let ctx = h.ctx.clone();
let mut seen = vec![];
for (kind, name) in [(ThemeKind::Dark, "dark"), (ThemeKind::Light, "light")] {
h.state_mut().set_theme(&ctx, kind);
h.run_steps(4);
let tokens = h.state().tokens;
assert_eq!(tokens.kind, kind);
// the flag's hit area is 10 wide, centred on the marker; the box starts 6 points right of the centre
let hit = h.state().auto.find("timeline.marker.0").unwrap_or_else(|| panic!("the marker is on screen ({name})")).rect;
let (x, y0) = (hit[0] + hit[2] / 2.0, hit[1] + 1.0);
let h_flag = (hit[3] - 1.0).min(10.0);
let image = h.render().expect("the panel renders");
if let Some(dir) = std::env::var_os("MARKER_NAMES_SNAPSHOT") {
image.save(std::path::Path::new(&dir).join(format!("marker-name-{name}.png"))).unwrap();
}
let scale = image.width() as f32 / 1600.0;
let at = |px: f32, py: f32| image.get_pixel((px * scale) as u32, (py * scale) as u32).0;
let mid = y0 + h_flag / 2.0;
// inside the box, left of the text: the field fill; on its left edge: the field outline
let fill = at(x + 6.0 + 2.5, mid);
let edge = at(x + 6.0 + 0.5, mid);
assert!(close(fill, tokens.field_bg), "{name}: box fill {fill:?}, the theme's field_bg is {:?}", tokens.field_bg);
assert!(close(edge, tokens.field_border), "{name}: box outline {edge:?}, the theme's field_border is {:?}", tokens.field_border);
seen.push((fill, edge));
}
assert_ne!(seen[0], seen[1], "the box looks different in Dark and in Light, so it is not a fixed grey");
}