diff --git a/ROADMAP.md b/ROADMAP.md index 49ed8c987..76e983013 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -455,6 +455,7 @@ Newest first. - 2026-10-15: Ruler menus as InDesign's: right-clicking where the rulers meet sets the units of both; each ruler's menu lists InDesign's units and Hide Rulers, the horizontal one Delete All Guides on Spread. - 2026-10-15: Properties › Edit Page works as InDesign's: the Page tool on the page in view, and Properties shows that page alone (Page Size: page, preset, orientation, W/H; Margins and Columns; Quick Actions) with Back. It opened Document Setup, which changes every page. - 2026-10-15: Deleting pages that contain objects asks first, as InDesign does ("The affected page contains objects. Delete the page anyway?", with Don't show again), from the Pages panel, Layout › Pages › Delete Page and Properties. +- 2026-10-15: A page scrolled or jumped to shows its paper while its render is on its way, as in InDesign, instead of the black of its shadow. - **2026-10-14** · The Type tool draws a new text frame over a locked empty frame, as InDesign does, instead of turning the locked frame into a text frame. No readiness percentage change. - **2026-10-14** · 100% black shows as pure black on screen by default, as InDesign's Appearance of Black does out of the box (Display All Blacks as Rich Black); Preferences › Appearance of Black › Display All Blacks Accurately still shows it as the dark grey it prints, and a saved choice is kept. No readiness percentage change. - **2026-10-14** · Pathfinder keeps the frontmost object, as InDesign does: a photo placed over shapes and intersected with them shows through the shapes (it was dropped, and the shapes kept their black fill); Add, Exclude Overlap and Minus Back take the front object's fill too, Subtract still keeps the back one. No readiness percentage change. diff --git a/crates/ui-egui/src/canvas.rs b/crates/ui-egui/src/canvas.rs index 32e3e4f17..6bdceeba2 100644 --- a/crates/ui-egui/src/canvas.rs +++ b/crates/ui-egui/src/canvas.rs @@ -437,6 +437,15 @@ pub fn show(app: &mut DesignApp, ui: &mut egui::Ui) { let r = xf.rect(slot.bounds); painter.rect_filled(r.translate(vec2(1.5, 1.5)), 0.0, Color32::BLACK); } + // Paper under the rendered content: a page scrolled or jumped to shows its paper colour, + // not the shadow's black, until its render arrives. + let [r, g, b, _] = doc.resolve_color(designcraft_color::swatch::PAPER, 1.0).unwrap_or(designcraft_color::Color::WHITE).to_rgba8(1.0); + let paper = Color32::from_rgb(r, g, b); + for slot in &layout.slots { + for page in doc.spread(slot.spread).map(|sp| sp.pages.as_slice()).unwrap_or_default() { + painter.rect_filled(xf.rect(slot.xf.transform_rect_bbox(page.bounds())), 0.0, paper); + } + } // Rendered content. render_texture(app, ui.ctx(), rect, &xf, &layout, preview); if let (Some(tex), Some(sh)) = (&app.canvas.texture, app.canvas.shown) { @@ -2833,6 +2842,39 @@ mod tests { assert_eq!(press(Some(up), 0.0, 1.0), 1); } + /// While a page's render is on its way the page shows paper, not the black of its shadow. + #[test] + fn pages_show_paper_under_their_render() { + let mut app = DesignApp::new(designcraft_engine::Session::new(), crate::Services::default()); + app.run("file.new", serde_json::json!({"pages": 3, "facingPages": true})).unwrap(); + let ctx = egui::Context::default(); + let mut shapes = Vec::new(); + for _ in 0..2 { + let raw = egui::RawInput { screen_rect: Some(Rect::from_min_size(Pos2::ZERO, vec2(1440.0, 900.0))), ..Default::default() }; + let mut out = ctx.run_ui(raw, |ui| show(&mut app, ui)); + out.textures_delta.clear(); + shapes = out.shapes; + } + let st = app.session.active().unwrap(); + let layout = CanvasLayout::new(&st.doc, false); + let xf = Xf::new(app.canvas_rect.unwrap(), app.view().unwrap()); + // The paper swatch on screen (white, as the colour settings show it). + let [r, g, b, _] = st.doc.resolve_color(designcraft_color::swatch::PAPER, 1.0).unwrap().to_rgba8(1.0); + let paper: Vec = shapes + .iter() + .filter_map(|c| match &c.shape { + egui::Shape::Rect(s) if s.fill == Color32::from_rgb(r, g, b) => Some(s.rect), + _ => None, + }) + .collect(); + // Each page on its own (a spread's pages may differ in size). + assert_eq!(paper.len(), st.doc.page_count()); + for abs in 0..st.doc.page_count() { + let want = xf.rect(layout.page_rect(&st.doc, abs).unwrap()); + assert!(paper.iter().any(|r| (r.min - want.min).length() < 0.01 && (r.max - want.max).length() < 0.01), "page {abs}"); + } + } + fn harness(app: DesignApp) -> Harness<'static, DesignApp> { let mut h = Harness::builder().with_size(vec2(1440.0, 900.0)).build_ui_state( |ui, app: &mut DesignApp| {