-
Notifications
You must be signed in to change notification settings - Fork 1
Click Handling
VGui decodes client inventory packets into ClickContext, applies global vetoes, dispatches item and view handlers, and restores the authoritative screen when transactions are cancelled.
For a click targeting the current VGui window:
- The state id and raw mode, button, slot, and changed-slot indexes are validated.
- VGui reads the view's transaction cancellation policy.
- A configured per-player cooldown is checked for non-drag clicks.
- Every global
VGuiListener.onClickruns. - If all listeners return true, the clicked top-slot
ViewItemhandler runs. - The view-level
onClickhandler runs. - If cancellation is enabled and the same session remains open, VGui corrects the predicted slots and cursor.
A malformed or unknown click is cancelled before callbacks. A global listener returning false skips both the item and view handlers and corrects the client even for a pass-through view.
| Value | Meaning |
|---|---|
LEFT |
Normal left click |
RIGHT |
Normal right click |
MIDDLE |
Creative clone click |
SHIFT_LEFT |
Shift and left click |
SHIFT_RIGHT |
Shift and right click |
NUMBER_KEY |
Number key 1 through 9 |
OFFHAND_SWAP |
F key swap |
DROP |
Q drop-one action |
CTRL_DROP |
Control and Q drop-stack action |
LEFT_OUTSIDE |
Left click outside the window |
RIGHT_OUTSIDE |
Right click outside the window |
DOUBLE_CLICK |
Collect matching items |
DRAG_START |
Start click-drag |
DRAG_ADD |
Add a slot to click-drag |
DRAG_END |
Finish click-drag |
UNKNOWN |
Unrecognized mode and button combination |
Convenience predicates include isShift, isLeft, isRight, isDrag, and isOutside.
-
player()returns the Velocity player. -
slot()is the raw window slot, or-999for outside clicks. -
slotPos()returns row and column only for top-inventory slots. -
isGui()identifies the top inventory. -
isPlayerInventory()identifies the player-inventory section. -
clickType()returns the decoded classification. -
hotbarKey()returns 0 through 8 forNUMBER_KEY, otherwise-1. -
view(),contents(), andcontext()expose the current session. -
rawButton(),rawMode(), andstateId()expose packet-level values for advanced diagnostics.
Do not assume every non-GUI click is a normal player-inventory slot. Outside clicks have negative slots.
click.close();
click.open(detailsView);
click.openReplacing(statusView);
boolean returned = click.back();
click.playSound(Sounds.UI_BUTTON_CLICK);
click.playSound(sound, 0.7f, 1.2f);Opening or closing inside a handler changes the active session. VGui notices that change and does not resync the closed session afterward.
ViewItem toggle = ItemBuilder.of(ItemTypes.LEVER)
.name(Component.text("Toggle alerts"))
.onClick(click -> {
boolean enabled = Boolean.TRUE.equals(click.context().get(ALERTS));
click.context().set(ALERTS, !enabled);
click.contents().set(click.slot(), alertsItem(!enabled));
click.playSound(Sounds.UI_BUTTON_CLICK);
});Use onClick for behavior that is not tied to one item, such as diagnostics or clicks in the player inventory.
.onClick(click -> {
if (click.isPlayerInventory()) {
auditPlayerInventoryClick(click.player(), click.slot(), click.clickType());
}
})Cancellation defaults to true. The client may visually predict item movement before receiving the correction, so VGui restores the changed slots and clears the cursor after a processed click. Large or legacy multi-slot operations fall back to a complete window refresh.
Returning false from cancelClientTransactions() allows packets to continue toward the backend. The backend did not open the proxy window and may not understand those transactions. This option is only for an integration with a complete packet and inventory strategy.
The cooldown is disabled by default. If configured, a click arriving sooner does not invoke handlers and is still corrected. Drag start/add/end packets bypass the cooldown so quick-craft sequences remain coherent.
.clickCooldown(Duration.ofMillis(150))Use zero to disable. The builder rejects negative durations.
Click and listener callbacks run on the player's Netty thread. Never block it.
For external work:
- Copy the values needed from the click.
- Submit the slow work to an executor or Velocity scheduler.
- Before updating, confirm
contents.isOpen()and that the result still belongs to the current view. - Apply several slot changes in
batch.
See Performance and Threading for safe patterns.
- View Types and Builders
- Layouts and Slots
- Items and Skulls
- Click Handling
- Contents and Updates
- Context and State
- Navigation
- Pagination
- Anvil Input
- Lifecycle and Listeners
- API Reference