Repository navigation
holdbuttons
Hold-to-confirm buttons that prevent accidental destructive actions, plus compound action buttons with primary click and secondary hold.
local wu = GetMod("WindowUtils")
local c = wu.Controls
-- Hold-to-confirm delete button
local held, clicked = c.HoldButton("delete_all", "Delete All", {
duration = 1.5, style = "danger",
onHold = function() deleteAll() end,
onClick = function() wu.Notify.info("Hold to confirm") end,
})
-- Action button: click to load, hold to delete
c.ActionButton("preset_1", "My Preset", {
onPrimary = function() loadPreset(1) end,
onSecondary = function() deletePreset(1) end,
isActive = currentPreset == 1,
})Button that requires holding to trigger. Prevents accidental destructive actions.
| Parameter | Type | Default | Description |
|---|---|---|---|
| id | string | - | Unique button ID |
| label | string | - | Display label |
| opts.duration | number | 2.0 | Hold duration in seconds |
| opts.style | string | "danger" | Button style name |
| opts.width | number | 0 | Button width (negative = fill) |
| opts.progressDisplay | string | "overlay" | "overlay", "replace", or "external" |
| opts.progressStyle | string | "danger" | Progress bar style (for replace mode) |
| opts.disabled | boolean | false | Disable the button |
| opts.tooltip | string|nil | nil | Tooltip text |
| opts.overlayColor | table|nil | nil | Custom overlay fill color {r,g,b,a}
|
| opts.onClick | function|nil | nil | Callback on early release (click) |
| opts.onHold | function|nil | nil | Callback on hold completion |
| opts.elementId | string|nil | nil | Tutorial/bounds target ID |
Returns: boolean, boolean - triggered (hold completed), clicked (released early)
Progress display modes:
- overlay - rect fills over the button as you hold
- replace - button is replaced by a progress bar while held
-
external - no visual on the button itself; use
ShowHoldProgress()elsewhere
opts.overlayColor only applies in overlay mode. opts.progressStyle only applies in replace mode.
opts.elementId is not reported while a progressDisplay = "replace" button is actively held, because that path returns before the bounds reporting step.
ButtonRow and ButtonBlock forward def.warningMessage into the HoldButton opts table. HoldButton never reads it, so it has no effect anywhere. Use opts.tooltip (or def.tooltip) if you want to warn the user before they commit.
local held, clicked = c.HoldButton("delete_all", "Delete All", {
duration = 1.5, style = "danger",
onHold = function() deleteAll() end,
onClick = function() wu.Notify.info("Hold to confirm") end,
})Compound button: primary click + secondary hold-to-confirm with cross-element progress.
| Parameter | Type | Default | Description |
|---|---|---|---|
| opts.onPrimary | function|nil | - | Primary click callback |
| opts.onSecondary | function|nil | - | Secondary hold callback |
| opts.secondaryIcon | string | trash icon | Secondary button icon |
| opts.secondaryDuration | number | 1.0 | Hold duration |
| opts.secondaryStyle | string | "danger" | Secondary button style |
| opts.progressStyle | string | "danger" | Progress bar style |
| opts.style | string | "inactive" | Primary button style |
| opts.isActive | boolean|nil | nil | Override to "active" style |
| opts.width | number|nil | full width | Total width |
| opts.elementId | string|nil | nil | Tutorial/bounds target ID |
The secondary button is only rendered when onSecondary is set. Without it the control is just a styled full-width button, and secondaryTriggered is always false. The secondary hold always runs in progressDisplay = "external" mode: the progress bar replaces the primary button rather than filling the icon.
The secondary hold button registers under the ID id .. "_secondary", which is the ID to pass to getHoldProgress or resetHoldState.
Returns: table - a fresh table { primaryClicked, secondaryTriggered } on every call
c.ActionButton("preset_1", "My Preset", {
onPrimary = function() loadPreset(1) end,
onSecondary = function() deletePreset(1) end,
isActive = currentPreset == 1,
})Non-interactive status label with an optional hold-to-confirm action icon. A named shorthand for the display-label-plus-destructive-action pattern.
When actionDef is provided, the label occupies the remaining width after the hold icon slot. When omitted, the label fills the full row width.
| Parameter | Type | Default | Description |
|---|---|---|---|
| id | string | - | Unique ID for the hold button (only used when actionDef is provided) |
| label | string | - | Display text for the label slot |
| labelStyle | string|nil | "label" | Style name for the label slot |
| actionDef | table|nil | nil | Optional hold action definition (see below) |
| opts.tooltip | string|nil | nil | Tooltip for the label slot |
actionDef fields:
| Field | Type | Default | Description |
|---|---|---|---|
| icon | string|nil | trash icon | Icon glyph for the hold button |
| style | string | "danger" | Hold button style |
| duration | number | 1.0 | Hold duration in seconds |
| onHold | function|nil | nil | Callback on hold completion |
The hold button registers under id .. "_action". Pass that ID to resetHoldState if you need to clear it manually.
-- Label only (no action)
c.StatusRow("preset_status", "My Preset", "label")
-- Label with hold-to-delete action
c.StatusRow("preset_status", "My Preset", nil, {
icon = IconGlyphs.TrashCanOutline,
duration = 1.5,
onHold = function() deletePreset() end,
})
-- Custom label style and tooltip
c.StatusRow("preset_status", "Read-only Preset", "statusbar", nil, {
tooltip = "This preset cannot be modified",
})Returns: number|nil - progress 0-1, or nil if not holding
Renders a progress bar showing another button's hold state. Use with progressDisplay = "external".
Returns: boolean - true if progress bar is showing
Returns the progress and source ID of the first actively-held button. Accepts a single ID string or an array of IDs.
| Parameter | Type | Description |
|---|---|---|
| ids | string | string[] | Single button ID or array of IDs to check |
Returns: number|nil, string|nil - progress fraction (0-1) and source button ID, or nil, nil if none are held
-- Single ID form
local progress, sourceId = c.getFirstActiveHoldProgress("my_hold_btn")
-- Array form
local progress, sourceId = c.getFirstActiveHoldProgress({ "hold_a", "hold_b", "hold_c" })
if progress then
print("Button " .. sourceId .. " at " .. string.format("%.0f%%", progress * 100))
endWhen multiple buttons are held simultaneously, the one whose ID appears first in the array wins.
Renders a progress bar for the first actively-held button in a list. No-op when no button is held.
| Parameter | Type | Default | Description |
|---|---|---|---|
| ids | string | string[] | - | Single button ID or array of IDs to check |
| width | number|nil | fill | Bar width in pixels (negative = fill) |
| progressStyle | string|table|nil | "danger" | Style name, or a table mapping source IDs to style names |
Returns: boolean - true if a progress bar was rendered
-- Single ID
c.ShowFirstActiveHoldProgress("hold_btn", -1, "danger")
-- Array with per-source styles
c.ShowFirstActiveHoldProgress(
{ "hold_a", "hold_b" }, -1,
{ hold_a = "danger", hold_b = "warning" }
)When progressStyle is a table and the active source ID is not present as a key, the style falls back to "danger".
Clear hold button state for cleanup.