This commit is contained in:
Brandon Thomas
2026-10-14 12:39:59 -04:00
14 changed files with 430 additions and 8 deletions
+18 -3
View File
@@ -389,20 +389,35 @@ pub fn specs() -> Vec<CommandSpec> {
has_doc,
page_options
),
cmd!(noundo "layout.selectPage", "Select Page", [], None, "{page: 1-based | null} — the page the Page tool selects (its handles, the Control panel)", has_doc, |s, p| {
cmd!(
noundo "layout.selectPage",
"Select Page",
[],
None,
"{page: 1-based | null, ids?: [item ids] (objects selected with it: the Page tool on an object-based page)} — the page the Page tool selects (its handles, the Control panel)",
has_doc,
|s, p| {
let n = s.doc()?.doc.page_count();
let page = match p.get("page") {
None | Some(Value::Null) => None,
Some(v) => Some(v.as_u64().filter(|v| *v >= 1 && *v as usize <= n).ok_or_else(|| bad("layout.selectPage", format!("`page`: 1–{n}")))? as usize - 1),
};
let ids: Vec<designcraft_doc::ItemId> = {
let d = &s.doc()?.doc;
p.get("ids")
.and_then(Value::as_array)
.map(|a| a.iter().filter_map(Value::as_u64).map(designcraft_doc::ItemId).filter(|id| d.item(*id).is_some()).collect())
.unwrap_or_default()
};
let st = s.doc_mut()?;
if page.is_some() {
st.selection = Default::default();
st.selection = designcraft_doc::Selection::items(ids);
}
st.selection.page = page;
st.revision += 1;
Ok(json!({"page": page.map(|p| p + 1)}))
}),
}
),
cmd!(
"layout.section",
"Numbering & Section Options…",
+56
View File
@@ -668,6 +668,62 @@ fn controlled_by_parent_takes_the_parent_pages_rule() {
assert!(s.execute("layout.pageOptions", &json!({"pages": [2], "showParentOverlay": true})).is_err());
}
#[test]
fn page_tool_pins_and_locks_objects_on_object_based_pages() {
use designcraft_tools::{CanvasLayout, Mods, PointerEvent, PointerKind};
let mut s = Session::new();
s.execute("file.new", &json!({"width": 612, "height": 792, "facingPages": false})).unwrap();
let id = s.execute("frame.create", &json!({"rect": [256, 72, 356, 132]})).unwrap()["id"].as_u64().unwrap();
s.execute("liquid.pageRule", &json!({"rule": "objectBased"})).unwrap();
s.execute("edit.deselectAll", &json!({})).unwrap();
s.set_tool("page");
let v = ViewInfo::at_zoom(1.0);
let ev =
|kind, p: designcraft_geom::Point, alt: bool| PointerEvent { mods: Mods { alt, ..Default::default() }, ..PointerEvent::new(kind, p.x, p.y) };
let canvas = |s: &Session| {
let d = &s.doc().unwrap().doc;
let l = CanvasLayout::new(d, false);
(
l.page_rect(d, 0).unwrap(),
l.xf(designcraft_doc::SpreadRef::Doc(0)).transform_rect_bbox(d.item(designcraft_doc::ItemId(id)).unwrap().bounds()),
)
};
let click = |s: &mut Session, p: designcraft_geom::Point| {
s.pointer(&ev(PointerKind::Down, p, false), v).unwrap();
s.pointer(&ev(PointerKind::Up, p, false), v).unwrap();
};
let liquid = |s: &Session| s.doc().unwrap().doc.item(designcraft_doc::ItemId(id)).unwrap().liquid;
// A click on the object selects it with its page; its pins and locks show.
let (_, o) = canvas(&s);
click(&mut s, o.center());
let sel = s.doc().unwrap().selection.clone();
assert_eq!((sel.page, sel.items.clone()), (Some(0), vec![designcraft_doc::ItemId(id)]));
assert!(
s.overlays(v)
.iter()
.any(|o| matches!(o, designcraft_tools::Overlay::LiquidPins { pins: [false, false, false, false], resize: [true, true], .. }))
);
// The right pin (14 px outside the right edge) and the width lock (10 px right of the centre).
click(&mut s, designcraft_geom::Point::new(o.x1 + 14.0, o.center().y));
click(&mut s, designcraft_geom::Point::new(o.center().x + 10.0, o.center().y));
let l = liquid(&s).unwrap();
assert!(l.pin_right && !l.resize_width && l.resize_height && !l.pin_left, "{l:?}");
// Alt-drag the page 100 pt wider: the pinned, width-locked frame keeps its size by the right edge.
let (p, _) = canvas(&s);
let right = designcraft_geom::Point::new(p.x1, p.center().y);
s.pointer(&ev(PointerKind::Down, right, true), v).unwrap();
s.pointer(&ev(PointerKind::Drag, right + designcraft_geom::Vec2::new(100.0, 0.0), true), v).unwrap();
s.pointer(&ev(PointerKind::Up, right + designcraft_geom::Vec2::new(100.0, 0.0), true), v).unwrap();
let d = &s.doc().unwrap().doc;
let (pg, b) = (&d.spreads[0].pages[0], d.item(designcraft_doc::ItemId(id)).unwrap().bounds());
assert_eq!(pg.width, 712.0);
assert!((b.width() - 100.0).abs() < 1e-6 && (pg.x + pg.width - b.x1 - 256.0).abs() < 1e-6, "{b:?}");
// Hovering a pin shows its tooltip.
let (_, o) = canvas(&s);
s.pointer(&ev(PointerKind::Move, designcraft_geom::Point::new(o.x1 + 14.0, o.center().y), false), v).unwrap();
assert!(s.overlays(v).iter().any(|o| matches!(o, designcraft_tools::Overlay::Tip { text, .. } if text.starts_with("Object is pinned"))));
}
/// Load the text from a frame's out port with the Selection tool and click on the pasteboard:
/// a new frame is threaded there and the overset text flows into it.
#[test]