25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

15KB

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 Done - [x] 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
- [x] 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
- [x] Modifier-click (Ctrl/Shift): adds or removes a single element/control from the current multi-selection without disturbing the rest
- [x] Visible selection indication for every selected item, not just the primary one
- [x] Dragging any selected item moves the entire multi-selection together, preserving each item's relative offset (delta-based movement, not absolute repositioning)
- [x] 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)
- [x] Unit tests: rubber-band hit-testing, modifier-click toggle, multi-drag delta math (Desktop.Core)
- [x] Live built-form smoke (touches the properties panel's multi-item behavior)
Align and distribute multiple elements 5 points Done - [x] 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)
- [x] Distribution operations (equal horizontal/vertical spacing) over the current multi-selection
- [x] A toolbar affordance to trigger each operation, enabled only when 2+ items are selected, clearly disabled (not silently a no-op) otherwise
- [x] Unit tests for the alignment/distribution math (Desktop.Core, framework-free)
- [x] 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.
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.
3 2026-10-27 Batch 2 (“Select multiple elements at once on the canvas”, 5 points) done, all ACs met. Built as a strictly additive layer per the story's own conversation notes: CanvasElementEditor.MultiSelected (a HashSet<TextElementLayout>) alongside the existing Selected field, and a parallel TemplateCanvasControl._multiSelectedAddressControls for Address Controls, with a CollapseMultiSelectionIfSingular rule ensuring every existing single-select code path keeps working unchanged whenever 0 or 1 items end up selected. Rubber-band selection (CanvasElementEditor.ElementsInRect, rotation-aware via rotated-corner AABB, plus a parallel TemplateCanvasControl.AddressControlsInRect) and modifier-click toggle (ToggleMultiSelect, seeding from the current single selection) both replace or extend the multi-selection on release/click; group drag (BeginMultiDrag/MultiDragTo, snapping the shared delta once rather than each item's position independently) moves the whole set together preserving relative offsets; DrawAddressControl was split into separate isPrimarySelected (handles) and isMultiSelected (highlight-only) parameters so every selected item is visibly marked without ambiguous multi-item handles; the properties panel shows “N items selected” and disables per-item editing (Delete disabled too, rather than extended to a bulk operation — out of this story's scope). Tests: +12 (Desktop.Core, including a rotated-element ElementsInRect case and a snap-to-grid MultiDragTo case). Full suite 364/364 -> 376/376 (Desktop.Core; CLI unaffected at 124/124). Live-verified with a reflection-driven harness against the real built form: rubber-band selected exactly the 2 of 3 elements it should; the panel showed the disabled “2 items selected.” state; toggling a third item in and the first back out produced the exact expected membership; a group drag moved two elements by an identical measured delta with their relative offset exactly unchanged and the unselected third element untouched; clicking empty space cleared the selection; a real canvas screenshot confirmed every multi-selected item shows the highlight border. One disclosed evidence-depth caveat: Control.ModifierKeys has no public setter and queries live keyboard state, so modifier-click was verified by calling the same private toggle method the click handler delegates to, directly, rather than through a real simulated key-press. One pre-existing, unrelated cosmetic gap found (not fixed) during verification: a plain element's selection highlight border visibly wraps only part of a multi-word display string — confirmed present identically for ordinary single-selection, logged to logs/technical_debt_log.md (2026-10-27) rather than fixed in-scope. Batch 3, “Align and distribute multiple elements” (5 points, epic 6, depends on Batch 2's multi-selection now existing). None.
4 2026-10-27 Batch 3 (“Align and distribute multiple elements”, 5 points) done, all ACs met — Sprint 10 committed scope now 13/13 points complete. New framework-free AlignmentCalculator (Desktop.Core) computes pure position deltas from a list of world-space bounding boxes: ComputeAlignmentDeltas (6 edges, relative to the selection's combined bounding box) and ComputeDistributionDeltas (equal center-to-center spacing, a documented MVP simplification vs. edge-to-edge gap spacing). TemplateCanvasControl feeds it a mixed bounds list built from a newly-public CanvasElementEditor.GetWorldBounds (standalone elements, reusing Batch 2's rotation-aware AABB) and a new GetAddressControlWorldBounds (Address Controls, extracted from Batch 2's AddressControlsInRect to remove a duplicate copy of the corner-rotation math), applying each returned delta as a plain X += Dx; Y += Dy. A new toolbar row (8 buttons: 6 alignment edges + 2 distribution axes) was appended to the existing element toolbar — it wraps to a second line on its own, no new form layout row needed — enabled only while 2+ items are multi-selected, refreshed from inside the existing RefreshSelectionLabel so it can never drift out of sync. Tests: +12 (AlignmentCalculatorTests, Desktop.Core, framework-free, covering all 6 edges, 3- and 4-item distribution including an out-of-order case, and the sub-3/empty no-op cases). Full suite 376/376 -> 388/388. Live-verified with a reflection-driven harness against the real built form: the toolbar correctly disabled at 0 and 1 selected, enabled at 2+; Align Left/Top moved a 3-element selection to the exact expected shared edge; Distribute Horizontal left both extreme elements’ centers unchanged and placed the middle element's center at the exact arithmetic midpoint; a fresh full-form screenshot (not reused from Batch 2, per this story's own conversation notes) confirmed the new toolbar row, its enabled state, and every selected item's highlight all render correctly together. Sprint Review and Retrospective (sprint's committed scope is now fully done). None.

Powered by TurnKey Linux.