Adobe's "Set up a modern magazine layout" (Brian Wood, 2:12–4:23) locks a
page frame drawn round the spread (Cmd+L), then draws the text frames on
top of it with the Type tool. In DesignCraft the press landed in the
locked frame, which the Type tool turned into a text frame, putting the
caret in it; no new frame was drawn.
- The Type tool takes a hit only on text frames (locked or not, as
InDesign lets you edit their text) and unlocked empty frames; over a
locked empty frame, the press starts a new text frame.
- text.placeCaret refuses to convert a locked empty frame (or one on a
locked layer).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Chris Nielsen's "The Adobe InDesign Toolbox" (InDesign 2020) walks the
Tools panel in one and two columns. Compared with it:
- Gradient Swatch and Gradient Feather are separate tools (one flyout
here before).
- In two columns InDesign lays out Content Collector and Content Placer,
Apply Color / Apply Gradient / Apply None and Normal / Preview (with
Bleed, Slug, Presentation in its flyout) side by side
(SPLIT_IN_TWO_COLUMNS). One column keeps one well with a flyout, which
now names what is in front (Apply Color, Apply Gradient or Apply None).
- The utility button is View Options: Frame Edges, Rulers, Guides, Smart
Guides, Baseline Grid and Hidden Characters, checked when shown, with
their shortcuts (17:40). It was Text Frame Options (Object › Text Frame
Options…, ⌘B, still opens it).
- Apply Color (,) applies the colour applied last ([Black] at first) and
Apply Gradient (.) the gradient applied last, else the document's first,
else white to black (InDesign's default), to the fill or stroke in front.
The flyout had Apply Color as [Black] and no Apply Gradient.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tool cursors are glyphs drawn in code (black ink, white halo): the filled
arrow for Selection, the hollow arrow for Direct Selection, the pen nib
with an add, delete, close or convert badge, and a curved double arrow
for rotating. Linux and Windows show them as OS cursor bitmaps,
rasterized per pixels-per-point and cached; macOS paints them at the
pointer, since it sizes cursor bitmaps in points; the web build keeps
the system cursors. Text, hand, zoom and resize cursors stay system
cursors.
The Pen tool reports what a click does: over the first anchor of the
path being drawn it closes the path, and between paths a click on a
selected path's anchor deletes it and a click on its outline adds one.
The Add, Delete and Convert Direction Point tools show the matching pen
badges.
The glyph geometry and the rasterizer are adapted from VectorCraft's
code-drawn tool cursors (same organisation, original work).
Fixes#438
Chris Nielsen's "The Adobe InDesign Toolbox" (10:10): with the White
Arrow he clicks outside, then presses on a frame's corner and pulls it up,
turning the rectangle into a lopsided triangle. DesignCraft only looked for
anchors on objects already selected, so the press fell through to moving
the whole frame.
The Direct Selection tool now also looks at the anchors of the object
under the pointer: on one, it selects the object and drags that point, in
one undo step. Elsewhere on the object it still drags it whole; Shift-
click still toggles the selection.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add multi-click text selection, pending caret formatting and clipboard support, text-aware colour controls, guide/grid snapping, and frame threading with loaded-port interactions.
Include the layout, drop-cap and IDML updates carried from #83, with safeguards for generated and linked stories, external text references, local IDs, locked frames and atomic colour edits.
Fixes#23.
Co-authored-by: Sean Hammond <sean.hammond@und.edu>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Chris Cicchino's "Adobe InDesign – Page Tool and Liquid Layout" (6:32–
9:58) clicks an object with the Page tool on an object-based page and
gets InDesign's on-canvas liquid controls: a pin outside each edge
("Object is pinned relative to the page edge. Click to release.") and a
padlock on the width and height lines ("The object's width is locked.
Click to let the object change width as the page changes width."). The
pins and resize settings existed (liquid.object, the Liquid Layout
panel) but the canvas showed none of it.
- tools: on an object-based page a Page tool click selects the object
with its page (layout.selectPage takes `ids`); its pins (14 px outside
each edge's middle) and width/height locks (beside the centre) toggle
liquid.object on click, show InDesign's tooltips on hover and a hand
cursor.
- canvas: LiquidPins (orange dashed width/height lines with padlocks,
open when the object follows the page; hollow pins, or a line to the
page edge when pinned) and Tip overlays; tooltip translations.
Builds on the Page tool PR (#862).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Chris Cicchino's "Adobe InDesign – Page Tool and Liquid Layout" (1:02–
2:06, 13:47) sizes pages from the Control panel with the Page tool
selected: X/Y, W/H, the page-size preset ("US Business Card"),
orientation, Liquid Page Rule ("Controlled by Parent"), Objects Move with
Page and Show Parent Page Overlay. DesignCraft's Control panel showed the
(empty) object controls.
- ui: with the Page tool, the Control panel shows the selected page (or
the page in view): the reference point (W/H, preset and orientation
keep it in place via layout.pageSize's anchor), X/Y (read-only),
W/H, the preset dropdown, portrait/landscape, Liquid Page Rule, Objects
Move with Page (always on: pages don't move within spreads yet) and
Show Parent Page Overlay.
- doc: LiquidRule::ControlledByParent (the parent page's rule, Off
without one; resolved by liquid::effective_rule); Page.show_parent_overlay.
- engine: layout.pageOptions {pages, showParentOverlay}; liquid.pageRule
takes controlledByParent.
- canvas: the parent page's size over a page with the overlay on.
- Liquid Layout panel and Control panel share InDesign's rule list
(Off, Scale, Re-center, Object-based, Guide-based, Controlled by
Parent); "US Business Card" preset; translations.
Builds on the Page tool PR (#862) for the selected page.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Chris Cicchino's "Adobe InDesign – Page Tool and Liquid Layout" (0:42–
2:06) selects a page with the Page tool, gets its bounding box with
handles, drags a handle (the page springs back: a liquid-rule preview),
holds Option to really resize it, Shift for proportions, and reads the
size in a tooltip by the cursor. DesignCraft's Page tool only opened a
generic Page Size dialog on click.
- tools: the Page tool selects the page under a click (none on the
pasteboard); the selected page shows its outline, eight handles and
its liquid page rule ("Scale", "Object-Based"…) above it. Dragging a
handle previews layout.pageSize (the liquid rule re-flows the
objects) and springs back on release; Alt resizes, Shift keeps the
proportions; a W/H readout follows the pointer; resize cursors on the
handles. Double-click a page for the Page Size dialog.
- doc: Selection.page (the Page tool's page); engine: layout.selectPage
(no undo step).
- layout.pageSize `anchor` (0-8): the page grows away from the
reference point and its objects and guides keep their place relative
to it (a handle drag anchors the opposite edge); liquid rules work
from that.
- canvas: PageHandles and Tag overlays; translations for the tags and
the Resize Page undo label.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Callers from outside the app now get a parameter error for a key a
command's params description doesn't name, instead of the command
running with its defaults: `text.select {story, start, end}` and
`style.list {bogus: 1}` are refused with "unknown parameter `…`" and the
nearest or the accepted keys.
- engine: `cmd::params` (ported from VectorCraft #1442) reads the keys
from each params description; `check_params` / `check_keys`.
`Session::execute` itself stays lenient so old journals replay; in the
engine's own tests it checks too, as the audit of the descriptions.
- MCP: tools/call answers -32602 for an argument a tool doesn't take and
for params a command doesn't take (`execute`, every `batch` step before
the first runs); the headless backend checks as well.
- Control channel (`engine.execute`) and `designcraft-cli run --cmd`.
- Descriptions now name every key their commands read (table target
cells, anchored objects, gap.move, file.place, data merge, prefs.set,
…); type.char / type.para stay unchecked (their attributes may be the
params object itself; the setters refuse unknown attributes).
- Tests that passed keys the commands never read are fixed, and the pen
tool no longer sends the unread `notifyPen`.
Fixes#776
With a placed graphic selected (Direct Selection, or the content
grabber), the graphic's own brown bounding box has eight handles:
dragging one resizes the image inside its frame (Shift keeps its
proportions, Alt from its centre) and leaves the frame as it is.
- tools: a content-handle hit test on the selected graphic's bounds
(after the frame's anchors under Direct Selection), a ContentResize
drag previewing `transform.set {content, bounds}`, the handle cursors,
and the content ghost while dragging. The frame's handles don't resize
the frame while its graphic is selected.
- engine: `transform.set` takes `bounds` for content: the graphic's new
bounding box on the spread (normalized, never empty).
- canvas: the brown content box gets its handles; the frame's handles
are hidden while the graphic is selected.
(cherry picked from commit 8e0e6c2f2e)
As in InDesign, the Eyedropper applies a colour sampled from a placed image
to the fill or the stroke, whichever is in front in the toolbox: with the
Stroke box in front a selected line takes it on its stroke (it has no
fill to show it). The toolbox's state reaches tools through
ViewInfo/ToolContext::stroke_in_front.
Depends on #572.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cycle checks walk based_on with a seen-set over all parents, so a cycle
longer than the drawing depth cap is still found. Snapping lists parent
items through shown_parent_pages instead of recursing, and offers base
items on a parent spread. A base parent's text on a parent spread is
composed for that spread, so page-number markers show its prefix.
Cmd+Shift-click hits what is drawn: it honours Show Parent Items, hidden
and locked layers, the facing-page filter and layer order. IDML import
warns when a parent spread's pages name different bases or their own
spread.
Pages draw the base parent's items, then the parent's, then their own,
on the canvas, in PDF and image export, and on the parent spread itself.
Chains are followed base first with a depth bound. Overrides, parent hit
testing and the flattener's transparency check include base items.
layout.parents.options {parent, basedOn} sets or clears the base and
refuses cycles; document.inspect reports basedOn per parent. Opening a
.designcraft or IDML file clears a cyclic or dangling base with a warning.
IDML already carried the base as the parent pages' AppliedMaster; a test
covers the round trip.
As in InDesign, the Eyedropper applies a colour sampled from a placed image
to the fill or the stroke, whichever is in front in the toolbox: with the
Stroke box in front a selected line takes it on its stroke (it has no
fill to show it). The toolbox's state reaches tools through
ViewInfo/ToolContext::stroke_in_front.
Depends on #572.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With the Type tool, Option/Alt+Left/Right with a caret changes the manual kerning of the pair at the caret by the Kerning/Tracking increment (a new document preference, 20/1000 em by default, in Preferences > Units & Increments); with a selection it changes the tracking of the selected text. Cmd (Ctrl on Windows and Linux) multiplies the step by five. A pair without manual kerning starts from 0. The new type.kernStep command does the work, one undo step per press.
Moving by words is Cmd+Left/Right on macOS and Ctrl+Left/Right on Windows and Linux; Option/Alt+Shift+Left/Right still extends the selection by words. Fixes#621.
Selected type on a path shows its start and end brackets across the path,
as in InDesign, and the Selection and Direct Selection tools drag them
along it (Move Bracket, one undo step): the text starts and ends where
they are, and text past the end bracket is overset.
- doc: PathType.end (None: the path's end)
- geom: PathWarp::nearest, the arc length of the nearest point
- engine: type.pathOptions {end}; brackets stay on the path and can't cross
- compose: the line runs from the start to the end bracket
- idml: EndBracket read and written
- tools: Cursor::PathBracket
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
As in InDesign, the Pen over an end point of an open path (selected, or
under the pointer) shows a continue cursor; clicking it carries on drawing
that path, new anchors going on its subpath, and clicking its other end
closes it. Dragging from the end point first reshapes its direction
lines. From the first anchor the subpath is reversed first.
- engine: path.reverse (Object > Paths > Reverse Path), path.setAnchor;
path.appendAnchor and path.close take a subpath, and appendAnchor
refuses a closed subpath
- tools: Cursor::PenContinue
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflict: crates/engine/src/cmd/object.rs: object.fit's doc keeps next's text and adds the `axis` clause; test modules from both sides
Conflict: crates/ui-egui/src/canvas.rs: kept next's input code (it sends the second press as Down with clicks: 2) and dropped the PR's release-time DoubleClick; the PR's UI test stays
Conflict: crates/tools/src/select.rs: the handle double-click joins next's second-press preamble, after the port checks and before editing text and the frame/content toggle (Down or DoubleClick with click_count 2 on a handle runs object.fit along fitting_axis); the DoubleClick arm stays empty; module doc from both
Conflict: crates/tools/src/tests.rs, ROADMAP.md: both sides
color.sample takes `matchFrom`: with no opaque image pixel under the point (an empty part of an image frame, a transparent pixel), it applies that object's attributes as object.matchAttributes does, in the same command; the tool passes the object under the pointer. sample_item and the tool walk shown_children(), so only a multi-state object's active state is sampled, and sample_item skips points outside the graphic's clipping path. object.matchAttributes's body is a function both commands share.
Conflict: crates/engine/src/cmd/style.rs: object.color keeps next's apply_color, spec and enabled check (#84); takes the PR's color.sample (with next's enabled check), image_color_at and sample_item; drops the PR's color_swatch, color_items and has_text_or_selection; explicit `id`/`ids` win over a text selection in apply_color; the PR's tests follow next's caret behaviour (a caret takes the sampled colour for the next typing)
Conflict: crates/tools/src/tests.rs: both sides; the eyedropper test's Asset and Graphic literals gain next's pdf_crop, pdf_box, clip and wrap fields
Conflict: crates/ui-egui/src/i18n.rs, i18n/{ar,it,ja,pt_br,uk}.rs, ROADMAP.md: both sides
Conflict: crates/engine/src/tooling.rs: every with_ctx caller (IME preedit, commit, end, caret) takes the PR's `|s, cx| s.tool.…` form; pointer keeps next's end_composition before dispatch and tool_key its composing() early return
Conflict: crates/ui-egui/src/canvas.rs: the cursor block takes `space` from navigation_space, then the zoom branch, then `(space && !cmd) || alt_text`; the spring-zoom pointer events carry next's `clicks`; the PR's tests name their button helper `press` (next's tests have a `primary`) and compare cursors with what cursors::show sets for the engine cursor (next paints or rasterizes cursors per platform)
Conflict: ROADMAP.md, docs/target-app-parity.md, docs/ui-parity.md: status lines list both sides; progress and change-log rows keep both
A placed PDF, EPS or SVG under the Eyedropper matches attributes again,
as before, instead of failing with "no placed image under the point"
(Asset::is_raster, shared by the tool and color.sample). color.sample
hands its colour to object.color, which now colours selected text, the
text of selected frames ({text: true}), or the typing colour of an empty
story; a caret in existing text is refused before a swatch or undo step.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>