Skip to content
CyanideX edited this page Aug 5, 2026 · 1 revision

Hint

Pulsing outline highlights for individual UI elements. Draws attention to a control without needing a full tutorial flow. Hints dismiss on click or after a timeout.

The module is exposed as wu.Hint.

Quick Start

local wu = GetMod("WindowUtils")
local hint = wu.Hint
local controls = wu.Controls

-- Give your control an elementId
controls.Button("Settings", "inactive", 0, 0, { elementId = "settings_btn" })

-- Show a hint on it
hint.show("settings_btn")

Basics

Show a hint (click to dismiss)

hint.show("my_control_id")

The hint pulses until the user left-clicks the target element. Only left clicks count, and the element must be reporting bounds that frame, which it does automatically while any hint is active.

Show a hint with timeout

hint.show("my_control_id", { dismiss = "timeout", duration = 5.0 })

The hint disappears after 5 seconds regardless of user interaction.

An empty or non-string elementId is logged and ignored.

Hide a hint manually

hint.hide("my_control_id")

Immediately removes the hint. No-op if no hint is active for that ID.

Replace an active hint

Calling show() on an ID that already has an active hint replaces it (resets dismiss timer/mode).

hint.show("my_btn", { dismiss = "timeout", duration = 3.0 })
-- Later, switch to click-dismiss:
hint.show("my_btn", { dismiss = "click" })

Advanced Usage

Conditional hints

-- Show hint only on first use
if not hasSeenFeature then
    hint.show("new_feature_btn")
end

Multiple simultaneous hints

hint.show("step_1_btn")
hint.show("step_2_slider")
hint.show("step_3_combo")
-- Each dismisses independently

Combining with tutorials

Hints work alongside tutorials. Both systems share the same bounds tracking infrastructure, so a control with an elementId can be targeted by either system. However, hints are fully independent of tutorial state: they render even when no tutorial is active.

API Reference

Function Parameters Description
hint.show(elementId, opts?) elementId: string, opts: table Show a pulsing hint on the target element
hint.hide(elementId) elementId: string Remove a hint immediately
hint.shouldReport() - Returns true if any hints are active (internal)
hint.update() - Process dismissals for the frame (internal, called by WindowUtils)
hint.render() - Draw the pulsing strokes (internal, called by WindowUtils)

There is no "clear all hints" call. Track your own ids and call hide for each one.

opts

Field Type Default Description
dismiss "click" or "timeout" "click" How the hint is dismissed
duration number nil Seconds before timeout dismissal (required when dismiss = "timeout")

Dismiss modes

"click" and "timeout" are the only values the update loop handles:

dismiss value Behaviour
"click" (default) Cleared when the target is left-clicked
"timeout" with a numeric duration Cleared once duration seconds have elapsed since show()
"timeout" without duration Never cleared automatically
anything else, e.g. "manual" or "hover" Never cleared automatically

Unsupported values are not validated or logged, so a typo leaves the hint pulsing indefinitely. In that case only hint.hide(elementId) removes it. Hints also survive overlay close and reopen: nothing clears them on their own.

Visual Appearance

Hints share stroke settings with the tutorial system (configure via Tutorial.setSetting()):

  • strokeMin / strokeMax — outline thickness range
  • pulseSpeed — animation speed
  • pulseEasing — pulse curve: linear, easeIn, easeOut, easeInOut, bounce
  • spotlightRounding sets the corner rounding of the outline (negative values match the UI frame rounding)

The stroke color is fixed (teal, with opacity riding the pulse) and the outline is padded 4 pixels around the element rect. Neither is configurable.

Requirements

The target element needs an elementId in its control opts. The bounds tracker only captures positions when hint.shouldReport() or tutorial.shouldReport() returns true — init.lua handles this automatically.

Clone this wiki locally