From 25b128e5385af20f1b7f34ab6f68cf4eebf40d54 Mon Sep 17 00:00:00 2001 From: Trevor Brown Date: Sun, 1 Nov 2026 00:14:22 -0400 Subject: [PATCH] Pencil colour: document the dark threshold; tests for a frame without a fill, a see-through fill and the threshold Review follow-up on #1775: the dark rule is the one text uses (on_dark_background); a frame with no fill, or a fill under 50% alpha, is looked through to what is behind it, and the threshold is Color::is_dark (#757575 is dark, #767676 is light). Co-Authored-By: Claude Fable 5.1 --- crates/engine/src/brush_tests.rs | 10 ++++++++++ crates/engine/src/canvas/tools/handles.rs | 4 +++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/crates/engine/src/brush_tests.rs b/crates/engine/src/brush_tests.rs index 984eaaad5..35dba6a7f 100644 --- a/crates/engine/src/brush_tests.rs +++ b/crates/engine/src/brush_tests.rs @@ -138,4 +138,14 @@ fn the_pencil_draws_white_on_a_dark_background() { s.execute("page.setBackground", &json!({"color": "#1E1E1E"})).unwrap(); s.execute("node.create", &json!({"type": "frame", "x": 600, "y": 300, "width": 200, "height": 200, "props": {"fills": ["#FFFFFF"]}})).unwrap(); assert_eq!(stroke(&mut s, 620.0, 320.0), (Color::BLACK, Some(Color::BLACK)), "inside a light frame on a dark page"); + // A frame without a fill, or with a see-through one, is looked through to what is behind it. + s.execute("node.create", &json!({"type": "frame", "x": 900, "y": 300, "width": 200, "height": 200, "props": {"fills": []}})).unwrap(); + assert_eq!(stroke(&mut s, 920.0, 320.0), (Color::WHITE, Some(Color::WHITE)), "a frame without a fill on a dark page"); + s.execute("node.create", &json!({"type": "frame", "x": 1200, "y": 300, "width": 200, "height": 200, "props": {"fills": ["#FFFFFF40"]}})).unwrap(); + assert_eq!(stroke(&mut s, 1220.0, 320.0), (Color::WHITE, Some(Color::WHITE)), "a mostly transparent light frame on a dark page"); + // The threshold is Color::is_dark: white reads better than black from #757575 down. + for (bg, want) in [("#767676", Color::BLACK), ("#757575", Color::WHITE)] { + s.execute("page.setBackground", &json!({"color": bg})).unwrap(); + assert_eq!(stroke(&mut s, 0.0, 600.0).0, want, "on {bg}"); + } } diff --git a/crates/engine/src/canvas/tools/handles.rs b/crates/engine/src/canvas/tools/handles.rs index 9bd1c5973..9fa04d9bb 100644 --- a/crates/engine/src/canvas/tools/handles.rs +++ b/crates/engine/src/canvas/tools/handles.rs @@ -37,7 +37,9 @@ pub fn anchor_name(a: (u8, u8)) -> &'static str { } /// Whether what is drawn in `parent` sits on a dark background: the nearest layer from `parent` -/// out whose top visible fill is a mostly opaque solid colour, else the page (#362). +/// out whose top visible fill is a mostly opaque (alpha 0.5 or more) solid colour, else the page +/// (#362); a frame without such a fill is looked through to what is behind it. Dark means white +/// reads better on it than black (`Color::is_dark`: `#757575` is dark, `#767676` is light). pub(crate) fn on_dark_background(doc: &Document, parent: NodeId) -> bool { std::iter::once(parent) .chain(doc.ancestors(parent))