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.
| 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) |
logs/impediment_log.md. Neither blocks this sprint's committed items.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. |
| 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. |
| 2 | 2026-10-27 | Ad-hoc regression fix, before starting Batch 2: user reported clicking on and moving a dynamic placeholder had become “not smooth any more.” Full detail in epics/02_template_designer_gui_foundation.md's new post-Sprint-10-Batch-1 note. Root cause, found by direct Stopwatch instrumentation of TemplateCanvasControl.OnMouseDown rather than guessing: SelectionChanged fired unconditionally on every left-click, including a click on an already-selected element (the normal way to start a drag), and TemplateDesignerForm's handler runs the full un-gated RefreshPropertiesPanel() every time that fires — a cost that only became perceptible (~40-60ms/click, measured) once this sprint's added property-panel rows made the refresh heavier. Fixed by only raising SelectionChanged when the pre-click and post-click selection identity actually differ. Live-verified with the same instrumented harness: an already-selected element's second click dropped from ~50ms to 0.295ms; a genuine selection change still pays the warranted refresh cost unchanged. Full suite re-run: Desktop.Core 364/364, CLI 124/124 (fix is isolated to the WinForms-only TemplateCanvasControl, which has no automated test project). |
Batch 2, “Select multiple elements at once on the canvas” (5 points, epic 6). | None. |
Powered by TurnKey Linux.