Skip to content

UI Routines

UI Routines (STL.UI_ROUTINES) are named update functions that synchronize the visible state of the UI with the current engine state. They answer a single question: given the current parameter values, what should be shown, hidden, or labelled differently?

They are not callbacks — they do not write to parameters. They are pure read-and-display logic, always callable, always safe to re-run.

Why UI Routines Exist

Without a centralized refresh mechanism, display logic is scattered across init code, callback handlers, and reload logic. When a preset loads, the display does not update. When a parameter changes programmatically, the widgets lag. UI Routines solve this by providing a single call uir.update() that runs all display logic from scratch, regardless of what triggered the need for a refresh.

Key Concepts

Registration and Auto-Linkage

Every routine is a plain KSP function registered into the STL.UI_ROUTINES group at compile time. Calling call uir.update() runs all registered routines in order.

define STL.UI_ROUTINES += plc_global_navigation

function uir.plc_global_navigation()
    plc__ui -> hide := HIDE_WHOLE_CONTROL
    select plc.ui.section_id
        case plc.ui.section.POLYCORE
            plc__ui -> hide := HIDE_PART_NOTHING
        case plc.ui.section.CONSOLE
            console.ui -> hide := HIDE_PART_NOTHING
    end select
end function

The naming convention is uir.<name>() where <name> matches the string in the STL.UI_ROUTINES registration. IVLS auto-links the two at compile time; a typo causes a build error.

Multiple routines are registered independently. Each is called once per uir.update(), in registration order.

Sub-Groups

A sub-group is a named subset that can be invoked in isolation with uir.RunGroup(GroupName), avoiding the full update chain for events that only affect a narrow part of the UI:

define UIR.Polycore.ARP_TABLE_DISPLAY += UIR.Polycore.ARP_TABLE_CONTENT
define STL.UI_ROUTINES += UIR.Polycore.ARP_DISPLAY

function uir.plc_arp_dynamic_display()
    uir.RunGroup(Polycore.ARP_TABLE_DISPLAY)
end function

The parent routine acts as a proxy — the full uir.update() run reaches the sub-group via this proxy, but the sub-group can also be called directly when only the ARP table needs refreshing.

Auto-Linkage via Box Parameters

An Engine Box parameter can carry a ui_routine property. When the parameter changes, that routine is called automatically via process_ui_routine(par):

tokyo.mono_poly.ui_routine := uir.tss.legato_latency

This is implemented in the SDK's unnamed-pars.ksp — the par setter calls process_ui_routine(par) after writing the value, which invokes the assigned routine if one is set.

Use this when a parameter change has a narrow, well-defined UI consequence. Prefer the full call uir.update() when uncertain.

The Standard cb Reload Pattern

The canonical way to restore UI state after a DAW reload or preset change is to call uir.update() from cb Reload:

node Polycore.GUILogic:
    cb Reload:
        call uir.update()

The instpers variables (section_id, page_id, etc.) survive the reload. Without uir.update(), the widgets would remain in their default visual state even though those variables are set correctly.

Connections to Other Parts of IVLS

UI Routines are typically called from:

  1. par_cb.response in engine-box nodes, for parameters whose change affects widget visibility
  2. cb Reload (or cb PostReload) to restore display state after preset load
  3. Manual on ui_control() handlers in ui-callbacks that write navigation state and then need a display refresh

Patterns and Caveats

Do not write raw set_control_par() or widget -> hide := outside of UI Routine functions or on ui_control() handlers. If display logic is scattered across init code or callback code without a routine, it will fail to fire on DAW reload and cause visual state corruption.

Not every parameter change needs call uir.update(). A volume change never affects widget visibility. Only call it when the change might affect what is shown or hidden.

Routines are read-only. They observe state and update display. They never write to parameters. That direction goes through ui-callbacks.

The typical flow for a navigation button:

  1. User clicks button → on ui_control() fires in cb Functions
  2. Handler writes new navigation state to an instpers variable
  3. Handler calls call uir.update()
  4. Routines read the variable and update panel visibility
  • engine-box — par_cb.response calls uir.update() after relevant parameter changes
  • ui-callbacks — widget binding and the on ui_control() pattern