Selected-ness rides down as a compared parameter — a marquee crossing repaints its faces, not the board
A selection change re-ran every CardFaceView on the board (180 bodies ≈ 85 ms on the 6×30 fixture, 515 ≈ 233 ms on a real 515-card board, debug): the face's body read store.selection in three places — isSelected, the drag replica's count, and the context menu's styleTarget — and Observation invalidates every reader of the property, past the equatable gate entirely. The band overlay stayed cheap, which is why the marquee tracked the cursor while the highlight lagged ~0.4 s behind. Now LaneView and TrashLaneView hoist one selection read per body and hand each face isSelected/selectedCount as compared parameters; StyleMenuItems takes its target as a deferred closure; TrashLaneRowView gains the same treatment plus the Equatable gate it never needed before. Select-one-card: 180 bodies → 1. A growing band costs the selection's own running size; the real board's crossing fell 233 → 112 ms — the remainder is lane bodies re-measuring their masonry, a separate lane-level finding recorded in RENDER-INSTRUMENTATION.md. Also: select() gains defaultsSoleMember — the marquee's explicit nils never avoided the sole-member default, so a one-card band acquired a selectionHead and could scroll the lane out from under its own drag. MarqueeRenderCostTests pins the shape: redundant samples cost zero bodies, a growing band pays per crossing, and selectionStillRepaints holds a ≤8 budget.
This commit is contained in:
@@ -4545,8 +4545,19 @@ public final class BoardStore: HealHost {
|
||||
/// for "the lane that most recently held selection or a creation", and a *card* selection is
|
||||
/// its lane holding selection just as much as the lane's own header click is — so both are
|
||||
/// noted here, and creation notes itself in `beginPlaceholder`.
|
||||
public func select(_ ids: Set<ItemID>, in container: ItemContainer, anchor: ItemID? = nil, head: ItemID? = nil) {
|
||||
transient.select(ids, in: container, anchor: anchor, head: head)
|
||||
///
|
||||
/// - Parameter defaultsSoleMember: forwarded verbatim — see `TransientBoardState.select`, whose
|
||||
/// sole-member default the rubber band opts out of.
|
||||
public func select(
|
||||
_ ids: Set<ItemID>,
|
||||
in container: ItemContainer,
|
||||
anchor: ItemID? = nil,
|
||||
head: ItemID? = nil,
|
||||
defaultsSoleMember: Bool = true
|
||||
) {
|
||||
transient.select(
|
||||
ids, in: container, anchor: anchor, head: head, defaultsSoleMember: defaultsSoleMember
|
||||
)
|
||||
transient.noteActiveLane(Self.lane(holding: ids, in: snapshot))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user