UI Placeable Sequence & List Components
Status: Experimental (subject to API refinements)
This document describes the new UI components that replace the basic ItemList
-driven placeable selection with a richer, extensible system supporting variant cycling via left/right arrows.
- Provide a flexible per-entry layout (icon, name, variant index, navigation arrows)
- Support variant sequences (e.g. different visual styles or upgrade tiers) under a single selectable slot
- Clean keyboard & mouse navigation
- Theming consistent with the cyan/magenta dark UI palette
Components Overview
Section titled “Components Overview”Groups an ordered list of placeable-like resources (each expected to expose display_name
and optional icon
).
Fields:
display_name: String
– Label for the whole sequence (used when variant elements lack a name)placeables: Array[Resource]
– Ordered variantsicon: Texture2D (optional)
– Representative icon if you want a static image (falls back to first variant’s icon)
Utility methods:
count() -> int
get_variant(index: int) -> Resource
variant_display_name(index: int) -> String
Single UI row representing a PlaceableSequence.
Features:
- Displays active variant icon & name
- Variant index label (e.g.
2/3
) when more than one - Left/Right arrow buttons (hidden if
count() <= 1
) - Keyboard: Left / Right to cycle variants; Enter / Space or mouse click selects
- Emits:
selected(entry)
variant_changed(entry, variant_index)
Scrollable container managing multiple PlaceableListEntry instances.
Features:
- Vertical navigation with Up / Down keys
- Maintains selection state & highlight
- Emits
selection_changed(entry)
when selection or active variant (of selected) changes - API:
add_sequence(sequence: PlaceableSequence)
clear()
get_selected_entry()
Demo Scene
Section titled “Demo Scene”res://templates/grid_building_templates/ui/placement_selection/placeable_list_demo.tscn
populates several sequences (some with a single variant, others with three) to demonstrate behavior.
Usage Example
Section titled “Usage Example”var seq := PlaceableSequence.new()seq.display_name = "Wall Segments"for i in 4: var variant := load("res://placeables/wall_variant_%d.tres" % i) seq.placeables.append(variant)placeable_list.add_sequence(seq)
React to selection:
placeable_list.selection_changed.connect(func(entry): var active_variant = entry._active_object() # internal helper; expose wrapper if needed # Use active_variant to drive preview/build)
Design Notes
Section titled “Design Notes”Why not extend Placeable? Keeping PlaceableSequence separate avoids changing existing placement validation paths (each variant is still a normal placeable resource). The sequence groups them only for UI selection convenience.
Why not ItemList
? We need per-row controls (arrows) and custom layout + theming per entry which ItemList
does not support without hacks.
Keyboard & Accessibility
Section titled “Keyboard & Accessibility”- Initial selection auto-focuses first entry
- Up/Down traverse entries
- Left/Right only act on currently focused entry to change its variant
- Entry
tooltip_text
reflects variant status:Name (Variant 2/3)
Extensibility Ideas
Section titled “Extensibility Ideas”Future enhancements (not implemented yet):
- Drag & drop reordering within a sequence
- Favorite pinning & filtering
- Persist last chosen variant per sequence in player profile
- Search box above the list
\n## Theming
Each entry uses dynamic StyleBoxFlat
overrides for normal/selected states. Integrate with a global theme by replacing these overrides or injecting a custom style via a factory method later.
Integration Steps
Section titled “Integration Steps”- Replace existing ItemList usage in PlaceableSelectionUI with PlaceableList node.
- Wrap all single placeables into sequences of length 1 (helper factory recommended).
- On selection change, resolve active variant to feed existing placement preview flow (no rule changes required).
\n## Helper Factory (Suggested)
(Not yet implemented) A small utility that takes an Array of Placeable or Array[PlaceableSequence|Placeable]
and returns only sequences (wrapping singles). This reduces UI wiring code.
static func normalize_sequences(items: Array) -> Array[PlaceableSequence]: var out: Array[PlaceableSequence] for it in items: if it is PlaceableSequence: out.append(it) elif it is Placeable: var seq := PlaceableSequence.new() seq.display_name = it.display_name seq.placeables.append(it) out.append(seq) return out
Validation & Edge Cases
Section titled “Validation & Edge Cases”- Empty sequence: arrows hidden; entry still selectable (might represent a placeholder). Consider preventing empty sequences in production builds.
- Null icons: entry reserves icon space for alignment consistency.
- Rapid variant cycling: stateless; no debounce needed unless variant load is heavy.
Migration Checklist
Section titled “Migration Checklist”- Add sequences wrapping for all existing placeables
- Replace old selection signal binding with new
selection_changed
- Update any tests referencing the UI list to accept sequences
- Add factory utility if repetition appears
Last Updated: 2025-08-09