mirror of
https://github.com/storytold/designcraft.git
synced 2026-11-05 18:19:57 +00:00
Parent pages: show running-header placeholders, keep page overlays off them
On a parent page a running-header variable has no page to read, so it composed blank; it now shows <variable name>, as the header's placeholder. While parents were edited, document frames weren't laid out and fell back to the identity canvas transform, so their hidden characters (a flood of ¶), spelling squiggles and other overlays were drawn over the right parent page. Items on spreads that aren't shown now have no canvas transform. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CgGsJkVe5GYG9qK6VFdECa
This commit is contained in:
4 files changed
+45
-6
No files matched your search
@@ -348,6 +348,7 @@ list) · [roadmap.md](docs/roadmap.md) (milestones, plan) · [parity-checklist.m
|
||||
|
||||
Newest first.
|
||||
|
||||
- **2026-10-17** · Parent pages: a running-header variable shows its name in angle brackets on the parent (it was blank), and while parents are edited the document pages' hidden characters, spelling squiggles and other overlays no longer pile onto the right parent page. No readiness percentage change.
|
||||
- **2026-10-16** · Threading: the loaded text cursor threads into a text frame inside a group (it hit the group and showed it could not thread); a plain click still selects the group (#1075). Gap 27 closed. No readiness percentage change.
|
||||
- **2026-10-16** · Greek text hyphenates. The patterns were never compiled in, so Greek set unhyphenated however narrow the column; `"Greek"`, `"Greek: Greece"`, `el` and `el-GR` all resolve now and ανθρωπος breaks αν-θρω-πος. They are the monotonic patterns (hyph-el-monoton, Dimitrios Filippou, under MIT), which is what polytonic text hyphenates by too. Five of InDesign's ~40 hyphenation languages. No readiness percentage change.
|
||||
- **2026-10-16** · A swatch applied to a placed greyscale or bitmap image colours it, as in InDesign: IDML's `<Image FillColor>` is read and written, greyscale pixels become a tint of the swatch on opaque white and a bitmap's white lets the frame show through, on screen and in PDF (spot swatches print on their plate). Colour images keep their colours. Applying the swatch from the Swatches panel comes next. No readiness percentage change.
|
||||
|
||||
@@ -139,11 +139,12 @@ pub fn values(doc: &Document, page: Option<usize>, running: Option<&RunningIndex
|
||||
.enumerate()
|
||||
.map(|(i, v)| match &v.kind {
|
||||
VarKind::RunningHeader { style, use_, character } => {
|
||||
let body = match (running, page) {
|
||||
(Some(r), Some(p)) => r.value(style, *character, *use_, p),
|
||||
_ => String::new(),
|
||||
};
|
||||
format!("{}{}{}", v.before, body, v.after)
|
||||
match (running, page) {
|
||||
(Some(r), Some(p)) => format!("{}{}{}", v.before, r.value(style, *character, *use_, p), v.after),
|
||||
(None, Some(_)) => format!("{}{}", v.before, v.after),
|
||||
// A parent page has no entries of its own: show which variable sits there.
|
||||
(_, None) => format!("<{}>", v.name),
|
||||
}
|
||||
}
|
||||
_ => doc.variable_value(i, page).unwrap_or_default(),
|
||||
})
|
||||
|
||||
@@ -176,6 +176,11 @@ fn define(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn shown_on_parent(s: &Session, d: &designcraft_doc::Document, sid: designcraft_doc::StoryId) -> usize {
|
||||
let cs = s.cache.get(d, sid, None);
|
||||
cs.frames.iter().flat_map(|f| f.lines.iter()).map(|l| l.glyphs.iter().filter(|g| g.visible && g.adv > 0.0).count()).sum()
|
||||
}
|
||||
|
||||
/// A parent-page page-number marker follows Numbering & Section Options: changing a section's
|
||||
/// start number renames its pages without changing their index or the number of sections, and
|
||||
/// the marker composed for a page shows the new name.
|
||||
@@ -352,6 +357,11 @@ mod tests {
|
||||
assert_eq!(idx.value("Heading", false, Default::default(), 2), "Epsilon");
|
||||
let list = s.execute("variables.list", &json!({})).unwrap();
|
||||
assert!(list.as_array().unwrap().iter().any(|v| v["name"] == "Last Page Number" && v["value"] == "3"));
|
||||
// On the parent page itself the header shows which variable it holds, not a blank.
|
||||
let head = d.variable("Chapter Head").unwrap();
|
||||
assert_eq!(crate::compose::vars::values(&d, None, Some(&idx))[head], "<Chapter Head>");
|
||||
assert_eq!(crate::compose::vars::values(&d, Some(2), Some(&idx))[head], "Epsilon");
|
||||
assert!(shown_on_parent(&s, &d, sid) > p1, "the parent shows <Chapter Head>, longer than Alpha");
|
||||
// Deleting a variable flattens its instances to text.
|
||||
s.execute("variables.delete", &json!({"name": "Last Page Number"})).unwrap();
|
||||
assert!(s.doc().unwrap().doc.story(sid).unwrap().text.ends_with(" · 3"));
|
||||
|
||||
@@ -1014,7 +1014,10 @@ fn draw_guides(app: &DesignApp, painter: &egui::Painter, xf: &Xf, doc: &Document
|
||||
/// Spread-space → canvas transform for an item at `loc`.
|
||||
fn item_canvas_xf(doc: &Document, layout: &CanvasLayout, id: designcraft_doc::ItemId) -> Option<(Affine, &'static str)> {
|
||||
let loc = doc.find(id)?;
|
||||
Some((layout.xf(loc.spread) * doc.parent_xf(&loc), ""))
|
||||
// Items on spreads that aren't laid out (document pages while parents are being edited, or
|
||||
// the reverse) have no place on the canvas.
|
||||
let slot = layout.slot(loc.spread)?;
|
||||
Some((slot.xf * doc.parent_xf(&loc), ""))
|
||||
}
|
||||
|
||||
fn path_screen(it: &Item, xf: &Xf, a: Affine) -> Vec<Vec<Pos2>> {
|
||||
@@ -3678,6 +3681,30 @@ mod tests {
|
||||
/// The always-on overset mark sits at the frame's out port, a step along its own right edge
|
||||
/// (so a rotated frame gets it inside its edge, not straight up from the corner), and a
|
||||
/// selected overset frame gets only the mark its ports draw.
|
||||
#[test]
|
||||
fn document_pages_have_no_place_on_the_parent_canvas() {
|
||||
// Regression: while parents were edited, document frames fell back to the identity
|
||||
// transform and their ¶ marks (and other overlays) piled onto the right parent page.
|
||||
let mut app = DesignApp::new(designcraft_engine::Session::new(), crate::Services::default());
|
||||
app.run("file.new", json!({"pages": 2})).unwrap();
|
||||
let s = &mut app.session;
|
||||
let body =
|
||||
s.execute("frame.create", &json!({"rect": [36, 36, 300, 300], "content": "text", "text": "one\ntwo"})).unwrap()["id"].as_u64().unwrap();
|
||||
let head = s
|
||||
.execute("frame.create", &json!({"spread": {"kind": "parent", "index": 0}, "rect": [36, 10, 300, 30], "content": "text", "text": "head"}))
|
||||
.unwrap()["id"]
|
||||
.as_u64()
|
||||
.unwrap();
|
||||
let doc = app.session.doc().unwrap().doc.clone();
|
||||
let (body, head) = (designcraft_doc::ItemId(body), designcraft_doc::ItemId(head));
|
||||
let pages = CanvasLayout::new(&doc, false);
|
||||
assert!(item_canvas_xf(&doc, &pages, body).is_some());
|
||||
assert!(item_canvas_xf(&doc, &pages, head).is_none());
|
||||
let parents = CanvasLayout::new(&doc, true);
|
||||
assert!(item_canvas_xf(&doc, &parents, body).is_none(), "a page frame is not drawn over the parent");
|
||||
assert!(item_canvas_xf(&doc, &parents, head).is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overset_mark_sits_on_the_out_port_once() {
|
||||
let mut app = DesignApp::new(designcraft_engine::Session::new(), crate::Services::default());
|
||||
|
||||
Reference in new issue
Block a user