Repository navigation
drags
Drag controls use ImGui.DragFloat/ImGui.DragInt instead of sliders. The value changes by dragging left/right with configurable speed. Hold Shift for precision mode (reduced drag speed).
local wu = GetMod("WindowUtils")
local c = wu.Controls
local styles = wu.Styles
-- Simple float drag
local val, changed = c.DragFloat(
IconGlyphs.Speedometer, "speed", settings.speed,
0.0, 10.0, { speed = 0.05, default = 1.0, tooltip = "Drag speed" }
)
-- Three colored float drags in a row
local values, changed = c.DragFloatRow(nil, "pos", {
{ value = pos.x, color = styles.dragColors.x, label = "X", min = -2000, max = 2000 },
{ value = pos.y, color = styles.dragColors.y, label = "Y", min = -2000, max = 2000 },
{ value = pos.z, color = styles.dragColors.z, label = "Z", min = -2000, max = 2000 },
}, { speed = 0.1 })Float drag with optional icon prefix, column width, right-click reset, tooltip, and Shift precision.
| Parameter | Type | Default | Description |
|---|---|---|---|
| icon | string|nil | - | IconGlyph (nil = no icon) |
| id | string | - | Unique drag ID |
| value | number | - | Current value |
| min | number | - | Minimum |
| max | number | - | Maximum |
| opts.speed | number | (max-min)/200 | Base drag speed |
| opts.format | string | "%.2f" | Display format |
| opts.cols | number|nil | nil | Grid columns (nil = fill remaining) |
| opts.default | number|nil | nil | Right-click reset value |
| opts.tooltip | string|nil | nil | Icon tooltip (attaches to the icon, so needs icon set) |
| opts.alwaysShowTooltip | boolean | true | Show the icon tooltip even when tooltips are disabled globally. Set false to respect the global setting |
| opts.elementId | string|nil | nil | Tutorial/bounds target ID |
| opts.precisionMultiplier | number | 0.1 | Shift precision factor (lower = slower) |
| opts.noPrecision | boolean | false | Disable Shift precision for this control |
Returns: number, boolean - new value, whether changed
Shift precision: ImGui natively multiplies drag speed by 10x when Shift is held. DragFloat counters that and applies precisionMultiplier, giving an effective speed of baseSpeed * 0.1 * precisionMultiplier when Shift is held.
local val, changed = c.DragFloat(
IconGlyphs.Speedometer, "myDrag", settings.speed,
0.0, 10.0, { speed = 0.05, default = 1.0, tooltip = "Drag speed" }
)Same as DragFloat but for integers. Default format: "%d", default speed: 0.5.
local val, changed = c.DragInt(
IconGlyphs.Counter, "quality", settings.quality,
1, 100, { tooltip = "Quality level" }
)Compound control that renders multiple float drag inputs (and optionally buttons) on a single horizontal line. Supports per-drag color theming, width weighting, label-to-value display, and delta mode.
| Parameter | Type | Description |
|---|---|---|
| icon | string|nil | IconGlyph prefix (nil = no icon) |
| id | string | Base ImGui ID (each drag appends its index) |
| drags | table | Array of Drag_Definitions and/or Button_Definitions |
| opts | table|nil | Row-level options |
opts fields:
| Field | Type | Default | Description |
|---|---|---|---|
| cols | number|nil | nil | Grid columns for row width |
| speed | number|nil | (max-min)/200 | Default base drag speed |
| min | number|nil | nil | Default min for all drags |
| max | number|nil | nil | Default max for all drags |
| precisionMultiplier | number|nil | 0.1 | Shift precision factor |
| noPrecision | boolean|nil | false | Disable Shift precision |
| tooltip | string|nil | nil | Icon tooltip. Always shown regardless of the global tooltip setting; row icons ignore alwaysShowTooltip
|
| spacing | number|nil | itemSpacingX | Gap between elements |
| suppressCtrlReveal | boolean|nil | false | Stop Ctrl from revealing values on every labelled element in the row |
| mode | string|nil | "absolute" |
"absolute" (value in/out) or "delta" (always 0 in, delta out) |
| onChange | function|nil | nil |
(index, newValue, key) callback when a drag changes |
| onReset | function|nil | nil |
(index, key) callback on right-click in delta mode |
| disabled | boolean|string|nil | nil |
true = fully faded, "dimmed" = faded with outlined on hover, "dimmedColor" = faded with color border on hover |
| state | table|nil | nil | Caller-owned state table for unbound rows (enables hover tracking and delta accumulation across frames) |
| id | string|nil | nil | Unique suffix for bound rows sharing the same first key on the same bind context |
Returns: table, boolean - values array (one per drag, buttons excluded), whether any drag changed
| Field | Type | Default | Description |
|---|---|---|---|
| type | string|nil | nil | Omit or "drag" for the row's native type, "int" to force ImGui.DragInt for this element |
| value | number | required | Current value (ignored in delta mode) |
| min | number|nil | opts.min | Minimum (falls back to -math.huge) |
| max | number|nil | opts.max | Maximum (falls back to math.huge) |
| color | table|nil | nil | Base {r,g,b,a} color theme |
| format | string|nil | "%.2f" | Display format. type = "int" elements default to "%d"
|
| label | string|nil | nil | Idle display text, switches to value on hover, active drag, or Ctrl |
| tooltip | string|nil | nil | Hover tooltip |
| default | number|nil | nil | Right-click reset value. In delta mode, right-click calls onReset instead |
| speed | number|nil | opts.speed | Per-drag speed override |
| key | any|nil | nil | Passed as the second argument to onChange/onReset. Bound rows set this to the data key |
| width | number|nil | nil | Fixed pixel width |
| widthPercent | number|nil | nil | Fixed width as percentage of display width (e.g., 5 = 5% of screen) |
| widthFrom | string|nil | nil | Use cached width from a button group (see getButtonGroupWidth) |
| fitLabel | boolean|nil | nil | Auto-size width to fit the label text with padding. Needs label set, otherwise ignored |
| weight | number|nil | 1 | Flex weight for remaining space (ignored when width/widthPercent/widthFrom/fitLabel is set) |
| disabled | boolean|string|nil | nil | Per-drag disabled style override (same values as opts.disabled) |
| onChange | function|nil | nil | Per-drag (newValue, key) callback. Takes precedence over opts.onChange
|
| groupId | string|nil | nil | Group ID for width caching (elements with same groupId have widths summed) |
| progressFrom | string|nil | nil | Replace with progress bar when the named hold button is active |
| progressStyle | string|nil | "danger" | Style for cross-element progress bar |
| mode | string|nil | nil | Per-element "delta" override |
| elementId | string|nil | nil | Tutorial/bounds target ID. Only reported while bounds reporting is active |
Width precedence is width, widthFrom, widthPercent, fitLabel, then flex weight. The first one present wins.
Consecutive elements sharing the same progressFrom are merged into a single progress bar spanning their combined width. A type = "button" element ends the run.
| Field | Type | Default | Description |
|---|---|---|---|
| type | string | required | Must be "button"
|
| icon | string|nil | nil | Icon name (resolved via utils.resolveIcon). Takes precedence over label
|
| label | string|nil | nil | Button text |
| hoverLabel | string|nil | nil | Text shown on hover or Ctrl (label-to-value pattern). Needs a state table |
| style | string | "inactive" | Button style. "transparent" also opts the button out of row-level disabled styling, so invisible spacers stay invisible |
| tooltip | string|nil | nil | Hover tooltip |
| onClick | function|nil | nil | Click callback (index). On a hold button, fires when the hold completes, not on click |
| holdDuration | number|nil | nil | Hold-to-confirm duration. Setting it turns the slot into a HoldButton |
| progressDisplay | string|nil | nil | Hold progress display mode passed to HoldButton (defaults to "overlay" there) |
| progressFrom | string|nil | nil | Show progress bar from another hold button |
| progressStyle | string|nil | "danger" | Progress bar style |
| width | number|nil | auto | Fixed pixel width. Default auto-sizes to the icon or label text |
| widthPercent | number|nil | nil | Fixed width as percentage of display width |
| widthFrom | string|nil | nil | Use cached width from a button group |
| fitLabel | boolean|nil | nil | Auto-size width to fit label with extra padding |
| weight | number|nil | nil | Flex weight (participates in remaining space distribution) |
| groupId | string|nil | nil | Group ID for width caching, read back via widthFrom or getButtonGroupWidth
|
| disabled | boolean|string|nil | nil | Disable the button. Falls back to opts.disabled
|
| id | string|nil | nil | Custom button ID (for hold buttons) |
| elementId | string|nil | nil | Tutorial/bounds target ID. Only reported while bounds reporting is active |
A disabled button is drawn as a raw ImGui.Button under the faded style so it still highlights on hover, but onClick is never wired up, so clicks do nothing.
| Field | Type | Default | Description |
|---|---|---|---|
| type | string | required | Must be "time"
|
| value | number | 0 | Seconds since midnight (0-86399) |
| default | number|nil | nil | Right-click reset value (seconds) |
| tooltip | string|nil | nil | Hover tooltip |
| onChange | function|nil | nil |
(newSeconds, key) callback. Takes precedence over opts.onChange
|
| onRelease | function|nil | nil |
(seconds) called on the frame the drag stops while the element was hovered last frame. Needs a state table |
| key | any|nil | nil | Passed as the second argument to onChange
|
| color | table|nil | nil | Base {r,g,b,a} color theme, same as a drag element |
| disabled | boolean|string|nil | nil | Disable the control. Falls back to opts.disabled
|
| weight | number|nil | 1 | Flex weight for width distribution |
| width | number|nil | nil | Fixed pixel width |
| widthPercent | number|nil | nil | Fixed width as percentage of display width |
| widthFrom | string|nil | nil | Use cached width from a button group |
| groupId | string|nil | nil | Group ID for width caching |
| elementId | string|nil | nil | Tutorial/bounds target ID. Only reported while bounds reporting is active |
Displays "h:MM AM/PM" format. Drag adjusts in minute increments and wraps at midnight. Double-click to type a time string ("1:45 PM", "13:45", "1345"). Time elements do not support format, label, min, max, speed, mode, fitLabel, progressFrom, or progressStyle.
Elements without type (or type = "drag") are treated as drags. Elements with type = "button" render as buttons. Elements with type = "int" render as integer drags. Elements with type = "time" render as time-of-day drags. All elements are laid out left-to-right with SameLine(). Fixed-width elements (width, widthFrom, widthPercent, fitLabel) and unweighted buttons are subtracted from available space first; remaining space is divided proportionally by weight among weighted elements.
values holds one entry per drag or time element, in order, with buttons skipped. Callers that pass opts.state (or use a bound row) get the same values table back on every frame, reused in place. Copy out what you need rather than holding the table across frames.
Rows called without a state table allocate a fresh values table each frame, but lose hover tracking, delta accumulation, cached ImGui IDs, and onRelease.
A single {r, g, b, a} base color derives four ImGui style colors with a shared faded blue FrameBg background and axis-colored hover, active, and border. Drags without a color use the default outlined style.
disabled = true renders a fully faded blue style. "dimmed" shows faded at rest but reveals the normal outlined style on hover. "dimmedColor" shows faded at rest but reveals the full color border on hover (requires color on each drag). Can be set per-drag or at the row level via opts.
When a drag has a label, it displays the label text when idle and switches to the numeric value on hover or active drag. Requires a persistent state table (automatic for bound rows, pass opts.state for unbound rows).
Holding Ctrl reveals values on every labelled element in the row at once, and on buttons that set hoverLabel. This is suppressed while any ImGui item is active, and can be turned off per row with opts.suppressCtrlReveal = true.
Fully disabled elements (disabled = true) always show their label and never reveal the value.
-- Three colored float drags.
-- posRowState lives outside onDraw so hover reveal and onRelease work.
local values, changed = c.DragFloatRow(nil, "pos", {
{ value = pos.x, color = styles.dragColors.x, label = "X", min = -2000, max = 2000 },
{ value = pos.y, color = styles.dragColors.y, label = "Y", min = -2000, max = 2000 },
{ value = pos.z, color = styles.dragColors.z, label = "Z", min = -2000, max = 2000 },
}, { speed = 0.1, state = posRowState })
-- Mixed buttons + drags
local values, changed = c.DragFloatRow(nil, "posRow", {
{ type = "button", icon = "Upload", tooltip = "Load", onClick = function() loadPos() end },
{ type = "button", icon = "Refresh", tooltip = "Update", holdDuration = 1.0, onClick = function() updatePos() end },
{ value = pos.x, color = styles.dragColors.x, label = "X", min = -2000, max = 2000 },
{ value = pos.y, color = styles.dragColors.y, label = "Y", min = -2000, max = 2000 },
{ value = pos.z, color = styles.dragColors.z, label = "Z", min = -2000, max = 2000 },
}, { speed = 0.1 })
-- Delta mode (drags always show 0, onChange receives deltas)
c.DragFloatRow(nil, "posRelative", {
{ value = 0, color = styles.dragColors.x, label = "X", min = -10000, max = 10000 },
{ value = 0, color = styles.dragColors.y, label = "Y", min = -10000, max = 10000 },
{ value = 0, color = styles.dragColors.z, label = "Z", min = -10000, max = 10000 },
}, {
mode = "delta",
speed = 0.1,
state = relRowState,
onChange = function(index, delta)
local axes = { right, forward, up }
local axis = axes[index]
pos.x = pos.x + delta * axis.x
pos.y = pos.y + delta * axis.y
pos.z = pos.z + delta * axis.z
end,
})
-- Width weighting (3 equal-weight + 1 fixed-width)
c.DragFloatRow(nil, "camera", {
{ value = cam.yaw, label = "Yaw", min = -180, max = 180 },
{ value = cam.tilt, label = "Tilt", min = -90, max = 90 },
{ value = cam.roll, label = "Roll", min = -360, max = 360 },
{ value = cam.fov, label = "FOV", width = 80, min = 20, max = 130 },
}, { speed = 0.5 })Identical to DragFloatRow but uses ImGui.DragInt. Default format: "%d", default speed: 0.5.
local values, changed = c.DragIntRow("TuneVariant", "stats", {
{ value = stats.quality, label = "Quality" },
{ value = stats.priority, label = "Priority", width = 60 },
{ value = stats.count, label = "Count", width = 60 },
}, { min = 0, max = 100 })Standalone time-of-day drag control. Value is seconds since midnight (0-86399). Displays as "h:MM AM/PM". Drag adjusts in minute increments with wrapping. Double-click to type a time string.
| Parameter | Type | Default | Description |
|---|---|---|---|
| icon | string|nil | - | IconGlyph (nil = no icon) |
| id | string | - | Unique drag ID |
| value | number | - | Seconds since midnight (0-86399) |
| opts.cols | number|nil | nil | Grid columns (nil = fill remaining) |
| opts.default | number|nil | nil | Right-click reset value (seconds) |
| opts.tooltip | string|nil | nil | Icon tooltip |
| opts.alwaysShowTooltip | boolean | true | Show the icon tooltip even when tooltips are disabled globally |
| opts.elementId | string|nil | nil | Tutorial/bounds target ID |
Returns: number, boolean - new value in seconds, whether changed
Text input accepts: "1:45 PM", "1:45pm", "13:45", "1345", "945", "9:45". Raw seconds are not accepted: bare digit strings of three or more characters are read as HHMM, so "43200" parses as hour 432 and is rejected. Minutes wrap at 0 and 1439.
TimeDrag and type = "time" row elements share one text-edit state table keyed by ImGui ID, so keep their IDs distinct. TimeSlider uses a separate table.
local tod, changed = c.TimeDrag(
IconGlyphs.ClockOutline, "gameTime", settings.timeOfDay,
{ default = 43200, tooltip = "Game time" }
)Convenience color presets for common XYZ axis theming. Located in modules/styles.lua.
styles.dragColors = {
x = { 0.8, 0.3, 0.3, 1.0 }, -- red
y = { 0.3, 0.7, 0.3, 1.0 }, -- green
z = { 0.3, 0.5, 0.9, 1.0 }, -- blue
}Custom RGBA tables are also accepted anywhere a color is expected.
Push/pop colored drag style with faded blue FrameBg and axis-colored hover, active, and border. Returns false if color is nil, not a table, or not exactly four elements, in which case nothing is pushed. A three-element {r,g,b} table is rejected. Located in modules/styles.lua.
Push/pop the faded blue disabled drag style. Renders faded blue backgrounds, no colored border, and muted white text. Located in modules/styles.lua.