# Sprint Backlog **Sprint:** 10 **Dates:** 2026-10-28 - 2026-11-01 **Sprint Goal:** Let an operator select multiple canvas elements at once (rubber-band or modifier-click, mixing standalone elements and Address Controls) and align/distribute them together, while also finishing the epic-4 wrap/clip chain's live visual indicator — the first new selection model this designer has ever had, plus a small, low-risk finish to already-shipped work. ## Committed Items | Story | Size | Status | Tasks | |---|---|---|---| | Add a live wrap/clip indicator for text elements | 3 points | Done | - [x] Determine, per selected/previewed element with a box, whether the resolved text's natural (unwrapped) GDI+-measured size exceeds the box width (wrapping) and/or the wrapped height exceeds the box height (clipping)
- [x] Draw a small visual indicator (dashed-orange-style, consistent with the existing unmapped-column warning) on `TemplateCanvasControl` and `TemplatePreviewControl` when either condition is true for the current sample record, drawn inside the existing per-element rotation transform so it rotates for free
- [x] Unit tests for the wrap/clip detection logic (Desktop.Core, framework-free)
- [x] Live built-form smoke: a narrow (120pt) boxed element's real canvas screenshot shows both genuine multi-line wrap and the dashed-orange indicator appearing correctly | | Select multiple elements at once on the canvas | 5 points | Not Started | - [ ] Add a multi-select data structure alongside the existing single-selection state (`CanvasElementEditor` for standalone elements, `TemplateCanvasControl` for Address Controls) rather than replacing it, so every existing single-select code path (properties panel, rotate/resize handles, line drill-in) keeps working unchanged when only one item is selected
- [ ] Rubber-band selection: a click-drag starting on empty canvas space (not on any element/handle) draws a selection rectangle and selects every element/control whose bounding box intersects it on release
- [ ] Modifier-click (Ctrl/Shift): adds or removes a single element/control from the current multi-selection without disturbing the rest
- [ ] Visible selection indication for every selected item, not just the primary one
- [ ] Dragging any selected item moves the entire multi-selection together, preserving each item's relative offset (delta-based movement, not absolute repositioning)
- [ ] Properties panel shows a clear "N items selected" state and disables per-item property editing while a multi-selection is active (Development Team's chosen simplest option from the story's own notes)
- [ ] Unit tests: rubber-band hit-testing, modifier-click toggle, multi-drag delta math (Desktop.Core)
- [ ] Live built-form smoke (touches the properties panel's multi-item behavior) | | Align and distribute multiple elements | 5 points | Not Started | - [ ] Alignment operations (left/right/top/bottom edges, horizontal/vertical centers) over the current multi-selection, using each item's existing bounding box (Address Control's `Width`/computed height, standalone elements' measured or box width/height)
- [ ] Distribution operations (equal horizontal/vertical spacing) over the current multi-selection
- [ ] A toolbar affordance to trigger each operation, enabled only when 2+ items are selected, clearly disabled (not silently a no-op) otherwise
- [ ] Unit tests for the alignment/distribution math (Desktop.Core, framework-free)
- [ ] Live full built-form smoke with a **fresh** screenshot (this story adds a new toolbar control to `TemplateDesignerForm`, per the Sprint 7 retrospective's carry-in) | ## Notes - **Capacity signal:** completed totals across Sprints 1-9 are **20, 19, 18, 18, 15, 18, 18, 13, 13** — nine data points. The 18-20 range remains this team's proven, repeatable velocity; 15 and both 13s are documented, deliberate under-commits (a large/risky story, then two fresh-priority stories), not capacity misses. - **Why epic 6's pair is finally committed this sprint, explicit reasoning:** Ready and waiting since Sprint 7, deferred twice for fresher, more urgent human requests (Sprint 8's Address Control rotation reversal; Sprint 9's wrap/clip request). Neither reason applies a third time — continuing to defer a Ready, sized, 3-sprint-old pair without a stated reason would itself become the anti-pattern this team's practice exists to avoid. - **Why pairing it with the small indicator story doesn't trip the "two large/novel stories" rule:** that caution is about stacking *two* large, first-of-its-kind, architecturally risky stories in one sprint (e.g., Sprint 8's rejected rotation-drag-handle + multi-select combination). Only multi-select carries that character here; the indicator story is small, additive, and touches no shared foundational state. - Impediments: template asset path strategy (absolute vs. relative) and UNC timeout/retry behavior remain open in `logs/impediment_log.md`. Neither blocks this sprint's committed items. - Carried over from previous sprint: none (Sprint 9 completed its one committed story, 13/13 points). - Not committed: "Add an adjustable width and height with text wrapping to Address Control lines" (8 pts, epic 4 — depends on this sprint's indicator story landing and its design settling first, deferred to Sprint 11 rather than built under time pressure in the same sprint); "Undo and redo layout changes" (13 pts, epic 6 — a reasonable alternative pairing with the indicator, not chosen given the multi-select/align pair's own 3-sprint-old deferral making it the more overdue commitment); "Complete the first text-only operator workflow" (5 pts, epic 1 — stale label, needs re-verification before ever being pulled). ## Execution Order Sequenced by dependency and risk: the small, independent indicator story first (quick, de-risks nothing else but clears easily), then the hard-dependent multi-select/align pair in order. | Batch | Story | Why it's gated here | |---|---|---| | 1 | Add a live wrap/clip indicator for text elements | No dependency on anything else this sprint; small and independent. | | 2 | Select multiple elements at once on the canvas | No dependency on Batch 1, but sequenced next as the foundational half of the epic-6 pair. | | 3 | Align and distribute multiple elements | Strictly depends on Batch 2's multi-selection existing — there is nothing to align/distribute without it. | ## Daily Scrum Log | Day | Date | Completed | Planned | Blocked/At risk | |---|---|---|---|---| | 1 | 2026-10-27 | **Batch 1** ("Add a live wrap/clip indicator for text elements", 3 points) done, all ACs met. New framework-free `WrapClipDetector` (Desktop.Core) compares an element's natural (unwrapped) GDI+-measured width against its box width, and its GDI+-wrapped height against an explicit clip ceiling when one is set (never flags clipping otherwise, since there is no ceiling to exceed). Wired into `TemplateCanvasControl.DrawElement` and `TemplatePreviewControl.DrawItem`'s existing box-mode branches, drawing a dashed-orange outline (the unmapped-column warning's visual language) inside the same rotation transform every other per-element visual already uses, so it orbits for free when rotated. **Mid-batch, the human product owner raised two live-build issues, addressed together before continuing to Batch 2** (full detail in `epics/02_template_designer_gui_foundation.md`'s and `epics/04_live_preview_and_record_navigation.md`'s post-Sprint-9 correction notes): (1) no way to delete a placed element or Address Control — added `CanvasElementEditor.RemoveSelected()`/`TemplateCanvasControl.RemoveSelectedElement()`, wired to the Delete/Backspace key and a new toolbar button; (2) the resize handle should control only box width, font size set elsewhere — removed the Sprint 8 uniform-font-scale handle behavior entirely, `ResizeDragTo` now only ever sets `Width`, and `TextElementLayout.HasBox`/`TemplateElement.HasBox` were decoupled from `Height` (a new independent `HasHeightClip` flag) as a direct consequence, with `DebenuPdfRenderer.AddPage` gaining a "Width alone" native-wrap-no-clip render path (`DrawWrappedText` unrotated, `GetWrappedText`+`DrawRotatedMultiLineText` rotated). Tests: net +12 (8 obsolete font-scale tests removed, 20 new/updated across `CanvasElementEditorTests`, `TextElementPropertiesEditorTests`, `TemplateLayoutXmlSerializerTests`, `TemplateXmlParserTests`, `DebenuPdfRendererWrapTests`, plus the new `WrapClipDetectorTests`). Full suite 476/476 -> 488/488. Live-verified: a real, licensed end-to-end CLI render of a width-only (no height) template showed correct unrotated and rotated auto-height wrap with no clipping; a reflection-driven built-form harness confirmed a real mouse-driven resize drag sets only `Width` (Height/FontSize unchanged), the real Delete key removes the selected element, and a real canvas screenshot shows both genuine wrap and the new indicator on a narrow boxed element. One disclosed evidence-depth caveat: the toolbar Delete button's own `PerformClick()` didn't reliably fire in the synthetic harness (a known WinForms quirk absent a real shown window) — the underlying method was proven correct via a direct call, and the button's wiring matches every other proven-working toolbar button. | Batch 2, "Select multiple elements at once on the canvas" (5 points, epic 6). | None. |