Async Zero-Latency Buttons

Async Buttons allows you to create custom UI buttons that trigger with zero delay on a mouse-click, making them feel much more responsive to the user.

Applications
  • Instant feedback sound: Custom UI has a reputation for being laggy and feeling unresponsive. However, the latency of a custom UI button click is the same as that of a regular ability button. The only difference is that for an ability button, the click sound plays instantly, while for a custom UI button, the sound, for whatever reason, plays only once the button click has been synced. By giving the player instant audio feedback, the button will instantly feel much more responsive, even if the synced effect trigger has the same delay.
  • Ultra-responsive buttons for single-player maps: All buttons have an inherent delay, even in singleplayer. By using Async Buttons, you can make the controls of your singleplayer map feel much more crisp.
  • Buttons that can be dragged or right-clicked.

How it works

Instead of creating a trigger with BlzTriggerRegisterFrameEvent (or on top of creating that trigger), we register the button with:
Lua:
RegisterAsyncButton(whichButton, buttonData)
The buttonData table must contain the field controlPushIndex and highlightIndex. The controlPushIndex field specifies which of the button's children is the backdrop that becomes visible as long as the mouse is holding it down. For a standard ScriptDialogButton, that index is 1. If you're using your custom buttons, you should be able to determine the index easily enough. The highlightIndex field must be provided similarly. The index for a ScriptDialogButton is 5.

onLeftDownTriggers when the button is pushed down using the left mouse-button.
onLeftUpThe default callback for a button. Triggers when the left mouse-button is released after pushing down the button while still hovering it.
onLeftDragTriggers when the mouse-cursor leaves the button while that button is pushed down using the left mouse-button.
onLeftFizzleTriggers when the left mouse-button is released while no longer hovering the button after the button has been pushed down by it.
onRightDownTriggers when the right mouse-button is pressed while the mouse is hovering over the button.
onRightUpTriggers when the right mouse-button is released after pushing down the button while still hovering it.
onRightFizzleTriggers when the right mouse-button is released while no longer hovering the button after the button has been pushed down by it.


Left-click callbacks have virtually zero latency. Right-click callbacks are delayed by one sync-cycle, but are still async.

The button is then added to the list and the system repeatedly checks if the pushed backdrop is visible and invokes the callback function as it stops being visible. Only buttons that are currently visible will be checked, so your map won't be slowed down if you have hundreds of buttons across different UI systems.

Installation

Copy the AsyncButtons script file into your map. Also install TotalInitialization and Hook , if you haven't already.

If you want to create an instant button click sound, you have to overwrite "Sound\Interface\BigButtonClick.flac" with an empty sound file to prevent it from playing. Then, import the original sound under a different path.
Contents

AsyncButtons (Map)

AsyncButtons (Binary)

Reviews
Wrda
if REGISTER_BUTTON_RIGHT_CLICKS then local mouseDownTrig = CreateTrigger() local mouseUpTrig = CreateTrigger() local player for i = 0, 23 do player = Player(i) if...
nice idea, speeds up the clicking action even in single player by upto 0.2s.

But I dislike when one clicks, holds mouse, moves out of button and then releases mouse button the action is called, which is not like normal control click.
You could fix this with an optional check for the Mouse HoverLight child to know that the button still is hovered.
 
nice idea, speeds up the clicking action even in single player by upto 0.2s.

But I dislike when one clicks, holds mouse, moves out of button and then releases mouse button the action is called, which is not like normal control click.
You could fix this with an optional check for the Mouse HoverLight child to know that the button still is hovered.
Good idea!
 
Not a yuuuge fan of the dependency hell. I suppose I will have to rip this apart like I do with your other scripts
You just have to replace a few lines to get rid of the requirements. Just overwrite the BlzFrameSetEnable and BlzFrameSetVisible functions manually.

I'm actually gonna probably remove a large chunk of that stuff. I tried to import it into my map and I can't get it to not crash. Even using BlzGetOriginFrame can crash the game apparently.

Maybe that's something we could get the new team to fix. Would not take a lot of time to just put a few safety guards in front of these natives, such as BlzFrameGetParent returning nil if used on an originframe.
 
Level 2
Joined
Dec 27, 2024
Messages
2
Async Buttons enable the user to interface with custom UI elements smoothly and eloquently. A must have for any project.
 

Wrda

Spell Reviewer
Level 28
Joined
Nov 18, 2012
Messages
2,010
Lua:
if REGISTER_BUTTON_RIGHT_CLICKS then
            local mouseDownTrig = CreateTrigger()
            local mouseUpTrig = CreateTrigger()
            local player
            for i = 0, 23 do
                player = Player(i)
                if GetPlayerSlotState(player) == PLAYER_SLOT_STATE_PLAYING and GetPlayerController(player) == MAP_CONTROL_USER then
                    TriggerRegisterPlayerEvent(mouseDownTrig, player, EVENT_PLAYER_MOUSE_DOWN)
                    TriggerRegisterPlayerEvent(mouseUpTrig, player, EVENT_PLAYER_MOUSE_UP)
                end
            end
            TriggerAddAction(mouseDownTrig, OnMouseRightDown)
            TriggerAddAction(mouseUpTrig, OnMouseRightUp)
        end
Engulf this if condition scope into an OnInit.final to prevent any risks of desync due to player slot state.

A very neat system that breaches borders to an imaginary world of frames of possibilities.

Approved
 
Top