Lua Scripting

Lua Node#

A Lua node's script will run any time one or more of it's input or output values change. This includes impulses and triggers firing, and values backflowing on an output.

Execution Order#

The Lua node handles io with the following execution order:

  1. Read node inputs and outputs, placing their values into identically named variables.

  2. Runs your Lua script.

  3. Reads output variables for changes that your script made, and writes them to the node outputs.

  4. Reads input variables for changes that your script made, and backflows their values to the node system.

Special Variables#

  • last_step_dt
  • run_next_frame

Impulses#

Impulse types work differently when inputs versus outputs. Impulse inputs will always have the value 'false' when your script runs. Setting their value to true will cause the impulse to be fired after your script finishes.

If the impulse output has event data, the variable's value will be an array of all instances of the impulse being fired since the previous execution of your script. If the impulse was fired, but there was no event data, it's value will be 'true'. If the impulse was never fired, the value will instead be nil. Here is an example reading an output on a Lua node connected to an onDrag property:

-- pos is a dimension typed output. It could be connected to a translateX property.
if my_output then
  for i=1,#my_output do
    ev = my_output[i]
    if ev.action == INK_EVENT_CLICK_DOWN then
      -- drag started
    else if ev.action == INK_EVENT_CLICK_UP then
      -- drag ended
    else if ev.action == INK_EVENT_MOTION then
      pos = pos + ev.dx
    end
  end
end

Events#

As seen in the previous section's example, an impulse output may be an array of events. The event object has several members:

action:
  INK_EVENT_CANCEL
  INK_EVENT_CLICK_DOWN
  INK_EVENT_CLICK_UP
  INK_EVENT_ENTER
  INK_EVENT_EXIT
  INK_EVENT_MOTION
  INK_EVENT_ARBITER_QUERY_ACCEPT
  INK_EVENT_ARBITER_ENTER
  INK_EVENT_ARBITER_EXIT
  INK_EVENT_ARBITER_DRAG
  INK_EVENT_ARBITER_DROP
  INK_EVENT_SCROLL_START
  INK_EVENT_SCROLL
  INK_EVENT_SCROLL_ENDED
  INK_EVENT_SCROLL_WHEEL_ENDED

button:
  INK_MOUSE_LEFT
  INK_MOUSE_MIDDLE
  INK_MOUSE_RIGHT
  INK_MOUSE_X1
  INK_MOUSE_X2
  INK_TOUCH
  INK_STYLUS
  INK_STYLUS_B1
  INK_STYLUS_B2
  INK_STYLUS_B3

x, y:
  Position of the pointer
  relative to the view
  generating the impulse.

global_x, global_y:
  Position of the pointer
  relative to the window.

w, h:
  Size of view generating
  the impulse.

dx, dy:
  Relative pointer movement.

text:
  If this was a key event, a utf-8 string. Otherwise nil.

key_state:
  INK_KEY_DOWN
  INK_KEY_REPEAT
  INK_KEY_UP
  INK_TEXT_INPUT
  or nil if this was not a text event.

Drag Arbiters#

Drag and Drop arbiter example using the lua bindings:

-- corrisponds to the "Drag Id" property
local DRAGGING_TYPE = "item"

-- corrisponds to the "Drag Target" property
local DRAGGING_TARGET = "slot"

ink.event_register_drag_arbiter(DRAGGING_TYPE, DRAGGING_TARGET)
ink.before_step(function(dt)
    repeat
        local ev = ink.event_next_drag_arbiter()
        if ev ~= nil then
            local r = false
            if ev.drag_type_id == DRAGGING_TYPE and ev.target_type_id == DRAGGING_TARGET then
                -- ev.dragging_id and ev.target_id corrispond to the "Id"
                -- properties of the two views involved
                if ev.info.action == INK_EVENT_ARBITER_QUERY_ACCEPT then
                    r = true
                elseif ev.info.action == INK_EVENT_ARBITER_ENTER then
                    r = true
                elseif ev.info.action == INK_EVENT_ARBITER_EXIT then
                    r = true
                elseif ev.info.action == INK_EVENT_ARBITER_DRAG then
                    -- ev.info.x
                    -- ev.info.y

                    -- r value is ignored. event_report_drag_arbiter still
                    -- needs to be called.
                elseif ev.info.action == INK_EVENT_ARBITER_DROP then
                    r = true
                end
            end
            ink.event_report_drag_arbiter(r)
        end
    until ev == nil
end)

Data Object Functions#

  • append(field [, variant [, index]])
  • clear(field)
  • move(index)
  • remove()
  • get_index()
  • call_impulse(field)
  • trigger(field)
  • set_bool(field, value)
  • set_image(field, value)
  • set_string(field, value)
  • set_ident(field, value)
  • set_color(field, value)
  • set_int(field, value)
  • set_number(field, value)
  • get_bool(field)
  • get_string(field)
  • get_ident(field)
  • get_color(field)
  • get_int(field)
  • get_number(field)
  • ptr()
  • root_id()

The ink variable#

Lua runner scripts have a global variable named ink, which is the currently active ink instance. It has the following members:

  • language(lang)

    Sets the language. lang should be a valid language code (i.e. 'en', 'es', 'ru', etc.)

  • before_step(cb)

    cb will be called before ink_step.

  • on_step(cb)

    cb will be called after ink_step.

  • data_event_next()

  • event_register_drag_arbiter(dragging_type, dragging_target)
  • event_next_drag_arbiter()
  • event_report_drag_arbiter(result)

Manager#

Lua runner scripts have a global manager table with the following functions to register callbacks:

  • on_step(callback)

    ink_step will call callback with the argment dt (seconds since last step).

  • on_source(name, callback)

    When a source node with name is instantiated, callback will be called passing the new data object.

  • on_change(data, field, callback)

    When the field on data changes value, callback is called.

  • on_impulse(data, field, callback)

    When the impulse typed field on data fires, callback is called passing an event object.

The manager uses ink.on_step. Calling ink.on_step will override the manager's operation. The manager can be restored by calling ink.on_step(manager.handle_events)

Note

The manager is only available in lua runner scripts. Lua Nodes do not have access to the manager.

Manager Source#

Source code of the manager table for reference. It is automatically loaded as a part of the lua runner context.

manager = {
    source_tags = {},
    change_tags = {},
    impulse_tags = {},
    on_step_cb = nil,

    handle_events = function(dt)
        repeat
            local ev = uiink.data_event_next()
            if ev.type == INK_DATA_EVENT_NEW_SOURCE then
                local cb = manager.source_tags[ev.source_name]
                if cb ~= nil then
                    cb(ev.source_data)
                end
            elseif ev.type == INK_DATA_EVENT_REMOVE_SOURCE then
                local root = ev.source_data:root_id()
                manager.change_tags[root] = nil
                manager.impulse_tags[root] = nil
            elseif ev.type == INK_DATA_EVENT_CHANGE then
                local root_t = manager.change_tags[ev.data_object:root_id()]
                if root_t ~= nil then
                    local tag = root_t[ev.data_object:ptr()]
                    if tag ~= nil then
                        local cb = tag[ev.field]
                        if cb ~= nil then
                            cb()
                        end
                    end
                end
            elseif ev.type == INK_DATA_EVENT_IMPULSE then
                local root_t = manager.impulse_tags[ev.data_object:root_id()]
                if root_t ~= nil then
                    local tag = root_t[ev.data_object:ptr()]
                    if tag ~= nil then
                        local cb = tag[ev.field]
                        if cb ~= nil then
                            cb(ev.event_info)
                        end
                    end
                end
            end
        until ev.type == INK_DATA_EVENT_NONE
        if manager.on_step_cb ~= nil then
            manager.on_step_cb(dt)
        end
    end,
    on_step = function(cb)
        manager.on_step_cb = cb
    end,
    on_source = function(name, cb)
        manager.source_tags[name] = cb
    end,
    on_change = function(data, field, cb)
        local root = data:root_id()
        local ptr = data:ptr()
        if manager.change_tags[root] == nil then
            manager.change_tags[root] = {}
        end
        if manager.change_tags[root][ptr] == nil then
            manager.change_tags[root][ptr] = {}
        end
        manager.change_tags[root][ptr][field] = cb
    end,
    on_impulse = function(data, field, cb)
        local root = data:root_id()
        local ptr = data:ptr()
        if manager.impulse_tags[root] == nil then
            manager.impulse_tags[root] = {}
        end
        if manager.impulse_tags[root][ptr] == nil then
            manager.impulse_tags[root][ptr] = {}
        end
        manager.impulse_tags[root][ptr][field] = cb
    end
}