How-To: Bind a Slot Widget

Goal: a complete slot widget - icon, keybind, cooldown sweep, count badge, gray-out, click, drag-out, drop-in - that reads only the snapshot and never touches the ability system or the inventory.

Prerequisites
Quick Start. A widget class per slot; a container widget per bar that spawns one slot widget per GetSlotCount(BarTag).

1. Subscribe and seed

Event Construct -> Find (Crimson Action Bar Component) -> store it. Bind Event to On Slot Changed, On Slot Live State Changed, On Slot Highlight Changed, On Slot Input Pressed. Then Get Slot State for your address and call your handlers once so the widget is right on the first frame.

Verify
The widget shows the current icon and keybind on the first frame after being added to the viewport.

2. Identity: On Slot Changed

Fires when the static half changes: bound / cleared / stale / locked / keybind text / display. Rebuild the icon, name, keybind label and lock glyph here. Compare Address to your own (the delegate is per component, not per slot).

Handler: Branch (Address == MyAddress) -> Set Brush from Soft Texture (State.Display.Icon), Set Text (State.KeybindText), gray if State.Stale, padlock if State.Locked.

3. Live: On Slot Live State Changed

Fires on edges only: enabled, cost, cooldown started / ended / restarted, charges, count, active. Never per frame. Drive the gray-out, the resource tint, the count badge and the start of a sweep here.

Handler: gray if not State.Live.Enabled; blue tint if not State.Live.CostMet; count text from State.Live.Count (hide when -1); charges from State.Live.Charges / MaxCharges; a current frame when State.Live.Active.

4. The sweep

Interpolate from the snapshot's end times on your own tick; do not poll the component per frame. A regular cooldown wins over the global one when it is longer.

Event Tick -> Get Slot State -> if State.CooldownEndWorldTime > 0: Remaining = EndTime - Get Game Time in Seconds, Percent = Remaining / State.Live.CooldownDuration -> Set Percent on the sweep material / progress bar. Same with GlobalCooldownEndWorldTime / Live.GlobalCooldownDuration when it is the longer of the two.

Verify
Fire an ability with a 6 s cooldown: the sweep drains smoothly for 6 s, then On Slot Live State Changed fires once with Enabled = true.

5. Click, drag out, drop in

On Clicked -> Press Slot then Release Slot (address). On Drag Detected: if not Are Bars Locked, create your drag operation carrying State.Binding and the address. On Drop: if the payload has an address -> Move Binding (from, to); if it carries a binding from a picker -> Can Assign Binding (show Reason on false) then Assign Binding.

Verify
Drag slot 1 onto slot 5: they swap on every client of that player and nowhere else. Drop onto a locked slot: Can Assign Binding returns This slot is locked and nothing changes on the server.
Interoperating with your other binding views
Nothing about the drop path is bar-specific. Give your drag operation one field - an FCrimsonSlotBinding (plus a display preview if you want one) - and the same slot widget takes drops from every view over the store: a picker, a spellbook, a radial wheel. Accept the operation, read its Binding, pre-flight with Can Assign Binding, assign.

6. Or start from CrimsonCore's slot widget

Everything above is the raw plugin surface. CrimsonCore's UCrimsonCoreActionBarSlotWidget (W_ActionBar_Slot) already does all of it in C++ - subscribes, seeds, sweeps, drags, drops - and owns its whole look as class defaults (SlotMaterial, MaterialBinding, Visuals; the layout asset holds no look). Subclass it when you want a different look without redoing the plumbing: override Apply Appearance or leave the material pass in place and react to the events below, which fire only when the state actually flips, so an animation started on true can be stopped on false.

EventFires when
On Slot Changed / On Live State Changed / On Highlight ChangedIdentity, live state or proc glow changed (the plugin's own signals, forwarded).
On Key Pressed / On Key Released / On Activation RefusedThe slot's key edge, or a press the plugin refused (empty, stale, disabled).
On Hover ChangedThe pointer entered / left the slot.
On Press ChangedPressed / released, by mouse or by the slot's key.
On Drag State ChangedThis slot's binding is being dragged out / the drag ended anywhere.
On Drop Feedback ChangedA drag moved over the slot: Accept, Refuse, or None when it left.
On Cooldown ChangedA cooldown (own or global) started sweeping (with remaining / total seconds) or every sweep finished.
On Active ChangedThe bound thing became active (channel / toggle on) or stopped.
On Stale ChangedThe binding stopped resolving (unlearned, sold) or resolves again.
On Edit Lock ChangedThe player may / may not change the slot: slot lock, server-only, or the bars locked in combat (not CommonUI's button lock).
On Availability ChangedEnabled (usable right now) and / or cost payable flipped.
Why the look is the widget's
The plugin ships no UI and its layout asset says only where slots are, how big they are and which widget presents them. A project with a completely different art direction implements this page's surface (or subclasses the Core widget) and never touches the plugin - and the layout editor's canvas stays honest by drawing neutral tiles at the real geometry rather than guessing at your material.

See also

  • Concept: Providers and Identity
  • API Reference -> Snapshots