Adds a framework-free AlignmentCalculator (6 alignment edges, center-spaced
distribution along 2 axes) operating on world-space bounding boxes, plus a
new toolbar row wired to it for the current multi-selection. Completes
Sprint 10's committed scope (13/13 points).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds an additive multi-selection layer alongside the existing single-select
state for both standalone elements and Address Controls: rubber-band drag,
Ctrl/Shift-click toggle, group drag preserving relative offsets, per-item
highlight, and a disabled "N items selected" properties panel. Collapses
back to ordinary single-select whenever 0-1 items remain selected so every
existing code path is unchanged.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Clicking an already-selected element to start a drag fired
SelectionChanged unconditionally, triggering TemplateDesignerForm's
un-gated full RefreshPropertiesPanel() every click (~40-60ms, measured).
Now only raises the event when the selection identity actually changes.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sprint 10 backlog refinement/planning: re-confirmed the epic-4 "rotated
wrap/indicator" story after Sprint 9 shipped rotation composition as a
byproduct; renamed and re-sized it down to the indicator alone (3 pts).
Committed it alongside epic 6's long-deferred multi-select/align pair.
Batch 1 shipped: a live wrap/clip indicator (dashed-orange outline) on the
canvas and preview panel, backed by a new framework-free WrapClipDetector.
Mid-batch, live-build feedback surfaced two real gaps, fixed together:
- No way to delete a placed element or Address Control - added
CanvasElementEditor.RemoveSelected()/TemplateCanvasControl.
RemoveSelectedElement(), wired to Delete/Backspace and a toolbar button.
- The resize handle should control only box width, with font size set
elsewhere - removed the Sprint 8 uniform-font-scale handle behavior
entirely. HasBox now means "Width is set" alone; Height is independent
and optional (new HasHeightClip flag). DebenuPdfRenderer gained a
"Width alone, auto-height, no clip" render path using Debenu's
DrawWrappedText/GetWrappedText+DrawRotatedMultiLineText.
Verified via a real licensed end-to-end CLI render (unrotated and rotated
auto-height wrap, no clipping) and a reflection-driven built-form harness
(real mouse-driven resize drag, real Delete key, real canvas screenshot).
Test suite grew from 476/476 to 488/488.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>