191 lines
10 KiB
Markdown
191 lines
10 KiB
Markdown
# Card Row UX (target design)
|
|
|
|
This document describes the intended behavior for the next CardRow/CardControl UX iteration (presorted hand, external-drag box select, unified program row).
|
|
|
|
## Sources
|
|
- `DonkeysAndDroids.Godot/CardRow.cs`
|
|
- `DonkeysAndDroids.Godot/CardControl.cs`
|
|
- `DonkeysAndDroids.Godot/card_row.tscn`
|
|
- `DonkeysAndDroids.Godot/program.tscn`
|
|
- `DonkeysAndDroids.Godot/ProgramScreen.cs`
|
|
- `DonkeysAndDroids.Godot/improve.tscn`
|
|
- `DonkeysAndDroids.Godot/Improve.cs`
|
|
- `DonkeysAndDroids.Godot/draw_glitch.tscn`
|
|
- `DonkeysAndDroids.Godot/DrawGlitch.cs`
|
|
- `DonkeysAndDroids.Godot/gamble.tscn`
|
|
- `DonkeysAndDroids.Godot/Gamble.cs`
|
|
- `DonkeysAndDroids.Godot/buffer_overflow.tscn`
|
|
- `DonkeysAndDroids.Godot/BufferOverflow.cs`
|
|
|
|
## Component model
|
|
- CardRow is the container that owns CardControls, maintains OrderedCards, layout, and drag/drop reordering. It exposes signals `SelectionChanged` and `OrderChanged`.
|
|
- CardControl is the per-card UI. It handles hover/selection/drag visuals and emits `CardDroppedOn` and `CardSelectionChanged` signals.
|
|
|
|
## Card states and visuals
|
|
State triggers and visuals (applies when not Disabled unless noted):
|
|
- Idle: default; scale (1,1), pivot center, modulate (1,1,1), z-index 0.
|
|
- Hovered: mouse enters card while no GUI drag is active and (CanSelect or CanDrag) is true; scale (1.25,1.25), modulate (1.25,1.25,1.25), z-index 1.
|
|
- Selected: `Selected == true`; pivot Y moves to bottom edge, scale (1.25,1.25). Tooltip panel is updated via `Tooltip.Instance.Describe`.
|
|
- Drop target (before/after): during a valid drag over this card, `m_DropBefore` set based on cursor X; pivot X snaps to left or right edge, scale (1.25,1.25), alpha set to 0.5.
|
|
- Dragging (source card): after drag starts, card is hidden by setting scale X to 0; hover and drop indicators do not apply.
|
|
|
|
State precedence (in `UpdateState`):
|
|
1) Dragging overrides all other visuals.
|
|
2) Drop target overrides pivot X and alpha (pivot Y from Selected may remain).
|
|
3) Hover overrides scale/modulate.
|
|
4) Selected sets pivot Y and scale.
|
|
|
|
All pivot/scale/modulate changes are tweened with short spring transitions.
|
|
|
|
## Selection behavior
|
|
- Select toggle: left mouse press + release on a card toggles `Selected` if `CanSelect` and not Disabled.
|
|
- External-drag box select: if the left mouse button is pressed outside any CardRow (not on a card/row) and the mouse is dragged, any hovered card in the first CardRow entered is force-selected (no toggle off) until release. This is additive unless `SupportsMultiSelect` is false (in which case the most recently hovered card remains selected).
|
|
- Clicking a selected card again deselects it (subject to capacity rules when deselecting; see Program screen).
|
|
- SupportsMultiSelect:
|
|
- If false, selecting a card automatically deselects any other selected cards in that row.
|
|
- If true, multiple cards can be toggled independently.
|
|
- SelectionContext is global: selecting a card in any row clears selection in the previously active row. Multi-row selection is not possible.
|
|
- `SelectionChanged` emits an array of selected CardControls from the row. The array order is not guaranteed (dictionary order).
|
|
- Tooltip:
|
|
- Hover uses Godot `TooltipText` set to card name + description.
|
|
- Selection additionally calls `Tooltip.Instance.Describe(card)`.
|
|
|
|
## Drag and drop behavior
|
|
### Drag start
|
|
- Valid only when `CanDrag` is true and the card is not Disabled.
|
|
- Drag payload is the original CardControl; drag preview is a duplicated CardControl configured with the same card and row.
|
|
- The source card becomes visually hidden (scale X = 0) until drag ends.
|
|
|
|
### Multi-drag selection
|
|
- `SupportsMultiDrag` must be true.
|
|
- If multiple cards are selected and the dragged card is among them, all selected cards are dragged together.
|
|
- If multiple cards are selected but the dragged card is not selected, only the dragged card moves.
|
|
- If only one card is selected, only that card moves.
|
|
- Dragged card order is preserved as the order they appear in `OrderedCards`.
|
|
|
|
### Drop targets
|
|
- Row background (`CardRow._DropData`) inserts at an index computed from cursor X relative to existing card positions.
|
|
- Card target (`CardControl._DropData`) inserts before/after the target card based on cursor X < card width/2.
|
|
|
|
### Valid drop checks
|
|
A drop is valid only if all are true:
|
|
- Target row is not Disabled.
|
|
- Target row `CardsCanDrag == true`.
|
|
- Drag payload is a CardControl currently in a CardRow.
|
|
- If source row != target row, capacity check passes:
|
|
`targetRow.OccupiedSpace + sum(dragged.OccupiedSpace) <= targetRow.MaxCards`.
|
|
|
|
Additional invalid cases:
|
|
- Dropping a card onto itself (source == target) is ignored.
|
|
- Dropping onto a card that is part of the dragged set is ignored.
|
|
- If the dragged card cannot be resolved in its source row (no card id match), the drop is ignored.
|
|
|
|
### Move results
|
|
- Reorder within the same row:
|
|
- Cards are removed and reinserted at the adjusted index (accounts for removed cards before the insert point).
|
|
- Selection state is preserved because existing CardControls are reused.
|
|
- `OrderChanged` is emitted on that row.
|
|
- Move across rows:
|
|
- Cards are removed from the source list and inserted into the target list.
|
|
- New CardControls are created in the target row; selection state resets for moved cards.
|
|
- `OrderChanged` is emitted on the target row only.
|
|
- All moves animate via tweened layout. During drag-triggered moves, new cards start at their target positions (no center-entry animation).
|
|
|
|
## Row-to-row interaction rules (valid and invalid)
|
|
- Selecting a card in Row B always clears selection in Row A (global SelectionContext).
|
|
- External-drag box selection locks onto the first row entered; hovered cards in other rows are ignored until mouse release.
|
|
- Dragging from Row A to Row B is only possible if both rows have `CardsCanDrag = true` and are not Disabled.
|
|
- Dropping on a row with `CardsCanDrag = false` is invalid (no acceptance, no drop indicator).
|
|
- Dropping into a row that would exceed `MaxCards` is invalid (drop rejected).
|
|
- Dragging within a row is always allowed when the row is draggable, regardless of capacity.
|
|
- Dragging while any GUI drag is active disables hover visuals on other cards (no hover scaling).
|
|
|
|
## Layout and ordering
|
|
- Card positions are computed from `CardSize`, `CardSpacing`, and the row `Size`.
|
|
- Non-wrap rows center the line of cards; spacing shrinks (can be negative) when there is insufficient width, causing overlap.
|
|
- Wrap rows (`WrapCards = true`) break onto additional rows when the next card would overflow width and set `CustomMinimumSize` to fit.
|
|
- `GetSortedCards` sorts by:
|
|
1) Rarity
|
|
2) Card Id (ECard enum)
|
|
3) Modifier count
|
|
4) CardId (unique Guid)
|
|
|
|
## Use cases by screen
|
|
|
|
### Program row (Program screen, unified hand/tape)
|
|
Files: `DonkeysAndDroids.Godot/program.tscn`, `DonkeysAndDroids.Godot/ProgramScreen.cs`.
|
|
Row node: single CardRow replacing the split `%Hand`/`%Tape`.
|
|
Configuration:
|
|
- CardSize 80x100 (from `card_row.tscn`).
|
|
- CardSpacing 2.
|
|
- AnimationDuration 0.5.
|
|
- CardsCanDrag true, CardsCanSelect true.
|
|
- SupportsMultiSelect true, SupportsMultiDrag true.
|
|
- `MaxCards` is not used to enforce tape/hand capacity; capacity is enforced by selection rules.
|
|
|
|
Behavior:
|
|
- The row contains all cards from `CoreLoop.Hand` and `CoreLoop.Tape`.
|
|
- Initial order on entry: `CoreLoop.Tape` (in tape order) followed by `GetSortedCards(CoreLoop.Hand)` (hand pre-sorted).
|
|
- Initial selection: cards from `CoreLoop.Tape` are Selected; cards from `CoreLoop.Hand` are Deselected.
|
|
- Selection defines location:
|
|
- Selected cards count as Tape.
|
|
- Deselected cards count as Hand.
|
|
- Tape order is the order of selected cards in the row; hand order is the order of deselected cards in the row.
|
|
- Program row order is authoritative; results must not reorder existing cards. `HandResult`/`TapeResult` are treated as membership sets (selection + add/remove only).
|
|
- New cards not already in the row are appended after existing cards (tape cards in result order, then hand cards in result order).
|
|
- Dragging reorders the row only (no row-to-row drag). Multi-drag still reorders within the row.
|
|
- Selecting a card is only allowed if the resulting tape `OccupiedSpace` is `<= TapeLength`.
|
|
- Deselecting a card is only allowed if the resulting hand `OccupiedSpace` is `<= HandSize`.
|
|
- Discard operates on the selected set (selected cards can be discarded).
|
|
- Sort button re-sorts only the deselected (hand) cards and keeps selected (tape) cards fixed in place.
|
|
|
|
### Shop (Improve screen)
|
|
Files: `DonkeysAndDroids.Godot/improve.tscn`, `DonkeysAndDroids.Godot/Improve.cs`.
|
|
Row node: `%Shop` (CardRow instance).
|
|
Configuration:
|
|
- CardSize 90x112.
|
|
- CardSpacing 5.
|
|
- CardsCanDrag false.
|
|
- CardsCanSelect true.
|
|
- SupportsMultiSelect true.
|
|
- SupportsMultiDrag false (default).
|
|
|
|
Behavior:
|
|
- Multiple cards can be selected for purchase; Buy button cost is the sum of selected cards.
|
|
- Selection is the only interaction; drag and drop are disabled.
|
|
- Selection persists within the row until cleared by clicking again or selecting another row.
|
|
|
|
Invalid interactions:
|
|
- Dragging from or dropping onto the shop row is not possible (CardsCanDrag false).
|
|
- Selecting cards in the deck preview clears shop selection.
|
|
|
|
### Deck preview (Improve + DrawGlitch screens)
|
|
Files: `DonkeysAndDroids.Godot/improve.tscn`, `DonkeysAndDroids.Godot/draw_glitch.tscn`,
|
|
`DonkeysAndDroids.Godot/Improve.cs`, `DonkeysAndDroids.Godot/DrawGlitch.cs`.
|
|
Row nodes: `%Deck` in both scenes.
|
|
|
|
Configuration (Improve deck):
|
|
- CardsCanDrag false.
|
|
- WrapCards true.
|
|
- CardSpacing 15.
|
|
- CardsCanSelect true (default), SupportsMultiSelect false.
|
|
|
|
Configuration (DrawGlitch deck):
|
|
- CardsCanDrag false.
|
|
- WrapCards true.
|
|
- CardSpacing 10.
|
|
- CardsCanSelect true (default), SupportsMultiSelect false.
|
|
|
|
Behavior:
|
|
- Deck order is always the sorted order from `GetSortedCards`.
|
|
- Cards can be hovered and selected (single selection only), primarily for tooltip/inspect.
|
|
- No drag/drop or reordering.
|
|
|
|
Invalid interactions:
|
|
- Dragging from or dropping onto deck preview rows is not possible.
|
|
- Selecting a card here clears any selection in other rows.
|
|
|
|
### Variants using the same CardRow mechanics (for completeness)
|
|
- Gamble (`DonkeysAndDroids.Godot/gamble.tscn`): `%Patches` row uses CardsCanDrag false and single selection to pick exactly one card to add.
|
|
- BufferOverflow (`DonkeysAndDroids.Godot/buffer_overflow.tscn`): `%Hand` row uses CardsCanDrag false and single selection to pick exactly one card to remove.
|