Skip to content
CyanideX edited this page Aug 5, 2026 · 2 revisions

Toggle Panels

Standalone collapsible panels with animated slide in/out, expand mode for automatic window resizing, and persistence.

Quick Start

local wu = GetMod("WindowUtils")
local split = wu.Splitter

-- Left toolbar that collapses
local isOpen = split.toggle("tools", {
    { content = drawToolbar },
    { content = drawCanvas },
}, { side = "left", size = 180 })

-- Bottom status bar
split.toggle("status", {
    { content = drawStatusBar },
    { content = drawMainArea },
}, { side = "bottom", size = 30, defaultOpen = true })

toggle(id, panels, opts?)

Standalone collapsible panel with animated slide in/out. panels[1] is the fixed/toggleable panel, panels[2] is the flex panel that fills remaining space.

Parameter Type Default Description Read
id string - Unique toggle ID -
panels table - Two-element array: [1] = fixed, [2] = flex. Both entries are required every frame
opts.side string "left" "left", "right", "top", or "bottom" every frame
opts.size number|string 200 Expanded size (pixels or percentage) every frame
opts.edgeFlush boolean false Bar sits flush against window edge when collapsed (right/bottom only) every frame
opts.toggleOnClick boolean false Expand mode only: single click toggles instead of double click every frame
opts.elementId string|nil nil Tutorial elementId for the toggle bar every frame
opts.expand boolean false Enable expand mode (auto window resize) every frame
opts.sizeMode string "fixed" "fixed", "flex", or "auto" (expand only) every frame
opts.defaultOpen boolean true Initial open state first call
opts.speed number 6.0 Animation speed multiplier first call
opts.animate boolean true Enable open/close animation first call
opts.barWidth number ItemSpacing.x Toggle bar thickness first call
opts.barBg table|nil transparent Idle bar background {r,g,b,a} first call
opts.windowName string|nil nil ImGui window name. Required when expand = true, and required by explicit persist first call
opts.normalConstraintPct number|nil nil Normal max-size constraint as display % (expand only) first call
opts.persist string|boolean|nil nil Explicit persistence mode: "auto" (restore on load), "manual" (save but don't auto-restore), true (same as "auto"), or nil/false (use automatic layout cache). Requires windowName. first call

Option Lifetime

The Read column above is not cosmetic. Fields marked "first call" are copied into the toggle's state the first time that id is seen and are silently ignored on every later frame, even though you keep passing them. That covers defaultOpen, speed, animate, barWidth, barBg, windowName, persist, and normalConstraintPct.

Fields marked "every frame" are re-read on each call, so side, size, sizeMode, expand, edgeFlush, toggleOnClick, and elementId can all change at runtime.

Use setToggleAnimate(id, enabled) to change animation after creation, and splitter.destroy(id) if you need the first-call fields to be picked up again.

windowName is required with expand = true. Expand mode indexes its panels by window name; passing expand = true without windowName logs expand.init: '<id>' requires opts.windowName and returns early, so the panel is never registered and expand mode silently does nothing. There is no fallback.

opts.expandDuration and opts.expandEasing used to configure a separate constraint animation. They are still forwarded internally but nothing reads them, so setting them does nothing. The smooth transition comes from getExpandSizePx following the toggle's own animation instead.

Automatic persistence: When no persist option is set and the consumer mod calls Update(windowName), toggle state automatically persists across sessions via the layout cache. No extra code needed. The explicit persist/windowName system takes priority when present. Setting persist without windowName disables both paths, so nothing is stored or restored.

Returns: boolean - current open state

Alias: split.t(...)

Panel Fields

Field Type Description
content function Renders the panel. panels[1].content is skipped while the panel is narrower than the bar
minWidth number|string|function|nil On panels[2] only: minimum flex size used by getMinSize. Defaults to one icon button plus padding
minHeight number|string|function|nil Same, for "top"/"bottom" sides

Interaction

  • Normal mode (no expand): single click on the bar toggles.
  • Expand mode: double click toggles, drag resizes. Set toggleOnClick = true for single-click toggling with drag still available.
-- Left toolbar that collapses
local isOpen = split.toggle("tools", {
    { content = drawToolbar },
    { content = drawCanvas },
}, { side = "left", size = 180 })

-- Bottom status bar
split.toggle("status", {
    { content = drawStatusBar },
    { content = drawMainArea },
}, { side = "bottom", size = 30, defaultOpen = true })

-- Expand mode: window resizes when panel opens/closes
split.toggle("sidebar", {
    { content = drawSidebar },
    { content = drawMain },
}, {
    side = "right",
    size = 250,
    expand = true,
    windowName = "MyWindow",
    sizeMode = "fixed",
    normalConstraintPct = 25,
})

Expand mode enables automatic window resizing. See expand for full documentation of the three size modes, constraint sizing, and position anchoring.

State Functions

setToggle(id, open)

Programmatically set toggle open/closed state. Silently does nothing if the toggle has not rendered at least once, since there is no state to write to yet.

Parameter Type Description
id string Toggle identifier
open boolean Desired open state

Persistence differs from a user click. A click on the bar writes to both the explicit window cache (when persist + windowName are set) and the automatic layout cache. setToggle only writes to the explicit window cache. If you rely on automatic persistence, a state set programmatically is not saved and the previous cached value is restored next session.

getToggle(id)

Query current toggle state. If the toggle has not rendered yet, falls back to the value stored in the automatic layout cache for that id, so it is safe to call before the first frame.

Returns: boolean|nil - open state, or nil if the id is unknown to both the live state and the cache

setToggleAnimate(id, enabled)

Enable or disable toggle animation at runtime. No-op if the toggle has not rendered yet.

getToggleAnimate(id)

Query whether animation is enabled.

Returns: boolean|nil - true or false for a known id, nil only when the toggle has not rendered yet.

getMinSize(id)

Get the cached minimum size (in pixels) for a splitter's primary direction. Returns the minimum content-region size needed so all panels fit at their minimums. Use this value (plus window padding) for SetConstraints.

Works with both multi() and toggle() splitters.

Returns: number|nil - minimum pixels, or nil if splitter hasn't rendered yet

getSavedToggle(windowName, panelId)

Query a panel's last saved open state from the window cache. Works with any persist mode ("auto" or "manual"). Returns the state that was saved when the game last closed, regardless of current state.

Parameter Type Description
windowName string The window name
panelId string The panel/toggle identifier

Returns: two values, boolean|nil saved open state and number|nil saved flex drag size. Both are nil when nothing is cached for that window and panel.

local wasOpen, dragSize = split.getSavedToggle("MyMod", "sidebar")

Careful when passing this straight into another call: the second return value comes along for the ride, so wrap it in parentheses ((split.getSavedToggle(...))) if the callee takes more arguments.

getExpandConstraint(id)

Get the base max constraint percentage for an expand-mode toggle. Returns normalConstraintPct as configured. Use with getExpandSizePx to compute pixel constraints. Returns nil if the toggle hasn't rendered yet, isn't in expand mode, or was created without normalConstraintPct. Dragging does not affect it.

Returns: number|nil - base constraint percentage

getExpandSizePx(id)

Get the current panel size contribution in pixels for constraint adjustment. Returns the grid-aligned panel size when open and settled, the live panel size while animating or dragging, or 0 when closed. Use this to adjust both min and max constraints when an expand panel is present.

Parameter Type Description
id string Toggle identifier (same id passed to splitter.toggle with expand = true)

Returns: number - pixels to add to min/max constraints. Always a number, never nil.

Returns 0 when the toggle exists but is not in expand mode. Before the toggle has rendered even once there is no state to read, so the value comes from the window cache instead, and only if you called registerToggle(id, windowName) first. Without that registration it returns 0 on frame 1, which shows up as a one-frame constraint pop.

registerToggle(id, windowName)

Map a toggle id to its window so getExpandSizePx can fall back to the cached window size on the first frame, before the toggle has rendered. Call it once during init or before Begin(). Idempotent, and it stores nothing else: it does not create toggle state or configure expand mode.

Parameter Type Description
id string Toggle identifier
windowName string Window name the toggle lives on
split.registerToggle("sidebar", "MyMod")

destroy(id)

Remove all internal state for a splitter ID. Call when dynamically created splitters are no longer needed to prevent unbounded state growth. Cleans up two-panel, multi-panel, and toggle state (including derived edge-toggle keys).

Parameter Type Description
id string Splitter identifier to clean up

Examples

Toggle Toolbar + Status Bar

split.toggle("toolbar", {
    { content = drawToolbar },
    { content = function()
        -- Main content with bottom status bar
        split.toggle("status", {
            { content = drawStatusBar },
            { content = drawMainContent },
        }, { side = "bottom", size = 28 })
    end },
}, { side = "left", size = 200 })

Expand Mode - Auto-Resizing Window

local wu = GetMod("WindowUtils")
local split = wu.Splitter
local expand = wu.Expand

local basePct = split.getExpandConstraint("sidebar")
local panelPx = split.getExpandSizePx("sidebar")
if basePct then
    local dw, dh = GetDisplayResolution()
    local maxW = dw * basePct / 100 + panelPx
    wu.SetConstraints(200, 200, maxW, dh * 0.8, "MyMod")
end

if ImGui.Begin("MyMod") then
    split.toggle("sidebar", {
        { content = drawSidebar },
        { content = drawMain },
    }, {
        side = "right",
        size = 280,
        expand = true,
        windowName = "MyMod",
        sizeMode = "fixed",
        normalConstraintPct = 25,
    })
    expand.applyWindowSize("MyMod")
end
ImGui.End()

Clone this wiki locally