Tick Event Handlers
Tick events occur as time passes. You can use a handleTick event handler to capture tick events and run commands periodically based on time.
For example, the following TouchWrapper
increments the RepeatCounter
variable about every 100 milliseconds as long as the component has the pressed
state. When the user releases the component and the pressed
state ends, the onPress
handler resets the counter to zero.
{
"type": "APL",
"version": "2023.1",
"theme": "dark",
"mainTemplate": {
"parameters": [
"payload"
],
"items": [
{
"type": "TouchWrapper",
"bind": [
{
"name": "RepeatCounter",
"value": 0
}
],
"handleTick": [
{
"when": "${event.source.pressed}",
"minimumDelay": 100,
"commands": [
{
"type": "SetValue",
"property": "RepeatCounter",
"value": "${RepeatCounter + 1}"
}
]
}
],
"onPress": [
{
"type": "SetValue",
"property": "RepeatCounter",
"value": 0
}
],
"items": {
"type": "Text",
"text": "You held this button for ${RepeatCounter} counts"
}
}
]
}
}
Tick event handlers
You can define tick handlers at the component level or at the document level.
All components have the handleTick
property. The APL document also has a top-level handleTick
property.
Property | Type | Default | Styled | Dynamic | Description |
---|---|---|---|---|---|
handleTick | Array of tick handlers | [] | No | No | Handlers to run when tick events occur. |
A single tick handler is an object with the following properties.
Property | Type | Default | Description |
---|---|---|---|
commands | Array of commands | REQUIRED | Commands to run when this handler invokes. |
description | String | "" | Optional description of this tick handler. |
minimumDelay | Number | 1000 | Minimum duration in milliseconds that must pass before this handler is invoked again. |
when | Boolean | true | When true, invoke this handler. |
The array of tick handlers runs in parallel. Each tick handler has a when
clause. A tick handler runs when both of the following conditions are met:
- The
when
clause for the handler evaluates to true. - At least
minimumDelay
milliseconds have passed after the last time this tick handler ran.
Individual APL running environments might check and run tick handlers at indeterminate intervals. An APL running environment should check tick handlers at least 60 times per second, but a resource-constrained environment might check less frequently.
For example, if you set a tick handler with a minimumDelay
of 30, APL invokes it at best every 30 milliseconds. It might be invoked at less frequent intervals. Don't use a tick counter to measure the passage of time. instead use the elapsedTime
or utcTime
properties.
commands
An array of commands to run when this tick handler is invoked.
The event generated has the following form:
"event": {
"source": {
"type": "COMPONENT_TYPE", // The component type or "Document"
"handler": "Tick",
"id": ID, // ID of the component or null for the Document
"uid": UID, // Runtime-generated unique ID of the component or null for the Document
"value": NUMBER // Value for the component or null for the document
}
}
Tick commands always run in fast mode.
minimumDelay
The minimum amount of time in milliseconds that must pass between invocations of this tick handler.
when
When true, this handler runs when the system detects a tick event.
Tick handler examples
Animation example
You can use tick handlers to create state machines that fire commands periodically. The following toggles the lights in a traffic light at random intervals (but approximately once per four seconds).
{
"type": "Container",
"bind": [
{
"name": "CurrentIndex",
"value": 0
}
],
"handleTick": [
{
"minimumDelay": 1000,
"description": "Cycle through the traffic light at random intervals. The cycle order is green-yellow-red-green-...",
"commands": [
{
"when": "${Math.random() < 0.25}",
"type": "SetValue",
"property": "CurrentIndex",
"value": "${(CurrentIndex + 2) % 3}"
}
]
},
{
"minimumDelay": 15000,
"description": "Every 15 seconds just go back to red to annoy folks",
"commands": [
{
"type": "SetValue",
"property": "CurrentIndex",
"value": 0
}
]
}
],
"items": {
"type": "Frame",
"width": 100,
"height": 100,
"borderRadius": 100,
"backgroundColor": "${index == CurrentIndex ? data : 'grey'}"
},
"data": [
"red",
"yellow",
"green"
]
}
Rate limiters
You can use tick handlers to create rate limiters. For example, assume you needed to periodically pass information the user scrolling the screen to your skill.
Without tick handlers, you could assign a SendEvent
command to the onScroll
handler of the ScrollView
. This approach causes problems, as the command floods the system with SendEvent
messages each time the scroll view moves.
{
"type": "ScrollView",
"onScroll": {
"type": "SendEvent",
"sequencer": "SendEventSequencer",
"arguments": [
"now at position ${event.source.position}"
]
}
}
To improve this solution, throttle the messages with a handleTick
handler. In this example, the tick handler limits the rate of sending updates to one time per second and sends the updated scroll position if the last reported position has changed.
{
"type": "ScrollView",
"bind": [
{
"name": "ScrollPosition",
"value": 0
},
{
"name": "LastScrollPosition",
"value": 0
}
],
"handleTick": {
"minimumDelay": 1000,
"when": "${ScrollPosition != LastScrollPosition}",
"commands": [
{
"type": "SetValue",
"property": "LastScrollPosition",
"value": "${ScrollPosition}"
},
{
"type": "SendEvent",
"sequencer": "SendEventSequencer",
"arguments": [
"now at position ${ScrollPosition}"
]
}
],
"onScroll": {
"type": "SetValue",
"property": "ScrollPosition",
"value": "{event.source.position}"
}
}
}
Last updated: Jun 09, 2022