APL Commands
Commands are messages that change the visual or audio presentation of the content on the screen. You send commands to the device in response to a spoken utterance with the Alexa.Presentation.APL.ExecuteCommands
directive. You use event handlers in your APL document to trigger commands directly, such as the response to a button press.
For specific commands, see APL Standard Commands and APL Media Commands.
Commands and screen actions
Commands support the following types of actions on the scene:
- Navigate within a scene
- Scrolling a
ScrollView
orSequence
- Scrolling to bring a component into view
- Change the page displayed in a
Pager
- Scrolling a
- Change a component within an existing scene
- Update an input control to reflect a new state
- Change the visibility on an existing component
- Play or pause a video clip within the existing scene
- Speech
- Read the audio content of a single component
- Read the audio content from more than one components
- Send commands to an APL extension
Command execution summary
This section summarizes the algorithm used to execute a command started by either an event handler or an externally-generated command.
Assume an array of commands to execute in a series in either normal mode or fast mode.
Normal mode evaluation
A command that executes in normal mode must have a named sequencer. Commands run by default on the "MAIN" sequencer, unless they are assigned to a different sequencer or are a subcommand of a Sequential or Parallel is assigned to a different sequencer.
For each command in the array in turn, do the following:
-
Evaluate the
when
property of the command. If it evaluates tofalse
, skip the command and go to the next command in the array. -
Evaluate the
delay
property of the command. If it is greater than zero, pause for the specified duration in milliseconds. -
Check the
sequencer
property of the command. When it has a value not equal to the current sequencer, hand this command off to the appropriate sequencer and go to the next command in the array. -
Evaluate the
type
property of the command. Whentype
is unrecognized, go to the next command in the array.Valid
type
values include the following:- A built-in command such as SendEvent. Execute the command when all required command properties have values. Some commands return immediately. Other commands pause the sequencer until the command finishes executing.
- A user-defined command. User-defined commands inflate into an array of commands to execute, so run those commands following the rules of this section and continue when all of those commands complete.
- An extension command. Execute the command. Some extension commands return immediately. Other commands pause the sequencer until the command finishes executing.
Fast mode evaluation
A command executing in fast mode
doesn't have a named sequencer
. For each command in the array in turn, do the following:
-
Evaluate the
when
property of the command. If it evaluates tofalse
, skip the command and go to the next command in the array. -
Check the
sequencer
property of the command. When it has a value, hand this command off to the appropriate sequencer and go to the next command in the array. The handed-off command now executes in normal mode instead of fast mode. -
Evaluate the
type
property of the command. Whentype
is unrecognized, go to the next command in the array.Valid
type
values include the following:- A built-in command that supports fast mode, such as SetValue. Execute the command if all required command properties have been set. For a list of commands that support fast mode, see Fast mode commands
- A built-in command that doesn't support fast mode, such as SendEvent. Skip the command.
- A user-defined command. User-defined commands inflate into an array of commands to execute, so run those commands following the rules of this section and continue when all of those commands complete.
- An extension command that supports fast mode. Execute the command.
- An extension command that doesn't support fast mode. Skip the command.
Command evaluation
The individual properties of a command support data-binding. The data-binding context for a command includes the context in which the command is defined, extended by an event
property containing information about the circumstances that triggered the command and the component that was the target of the command.
Event context
Commands evaluate in their source data-binding context.
A command sent to the device by the ExecuteCommands
directive evaluates in the top-level data-binding context. The data-binding context contains the viewport
and environment
properties along with any named resources.
A command issued in response to an APL event (such as a screen touch) evaluates in a local data-binding context where the command is defined.
For example, consider this TouchWrapper
sample.
{
"type": "TouchWrapper",
"bind": [
"name": "myRandomData",
"value": 24.3
],
"onPress": {
"type": "SendEvent",
"arguments": [ "The value is ${myRandomData}" ]
}
}
Pressing the TouchWrapper sets the first argument to "The value is 24.3".
Event property
When a command evaluates, the source data-binding context is extended with event data. You access this event data within the event
property.
The event property has two main sub-properties: event.source
and event.target
.
- The
event.source
property contains system-provided information about the component that caused the event - The
event.target
property contains system-provided information about the component that is receiving the event (if applicable).
Not all commands have an event.target
property. For example, the SendEvent
command doesn't have an event.target
property. All commands do have a event.source
property. The details with event.source
can vary and might not represent a component. For example, the source might come from an extension rather than a component.
The event
property added to the data-binding context has the following form.
"event": {
"source": { // Always exists
"type": "COMPONENT_TYPE", // The type of the component or "Document"
"handler": "EVENT_HANDLER", // The name of the event handler (see command notes)
"id": "SOURCE_ID", // The ID of the source component
"uid": "SOURCE_UID", // The UID of the source component
... // Additional source component properties
},
"target": { // Only exists when the command has a component target
"type": "COMPONENT_TYPE", // The type of the target component
"id": "TARGET_ID", // The ID of the target component
"uid": "TARGET_UID", // The UID of the target component
... // Additional target component properties
},
... // Command-specific properties
}
The event.source
property has a handler
property, and the event.target
property doesn't.
The following table lists the standard event.source
and event.target
properties that different components report. For complete documentation on the properties for a given component, see the documentation for that component.
Property | Type | Description | Reported By |
---|---|---|---|
bind |
Map | Data-binding context | Component |
checked |
Boolean | Checked state | Component |
color |
Color | Current color | Text |
currentTime |
Integer | Current playback position | Video |
disabled |
Boolean | Disabled state | Component |
duration |
Integer | Duration of the current video (ms) | Video |
ended |
Boolean | True if the video is ended | Video |
focused |
Boolean | Focused state | Component |
height |
Number | Height in dp | Component |
id |
String | Component id | Component |
opacity |
Number | Local opacity (not cumulative) | Component |
page |
Integer | Current displayed page | Pager |
paused |
Boolean | True if the video is paused | Video |
position |
Number | Percentage of scrolled distance | Sequence , ScrollView |
pressed |
Boolean | Pressed state | Component |
text |
String | Displayed text | Text |
trackCount |
Integer | Number of tracks | Video |
trackIndex |
Integer | Index of the current track | Video |
type |
String | Component type (e.g., "Frame") | Component |
uid |
String | Runtime-generated unique id | Component |
url |
String | Source URL | Image , VectorGraphic , Video |
width |
Number | Width in dp | Component |
The bind
property provides access to the data-binding context of the component. The following example illustrates how you access a bound value. The component with the ID MyText
is the target of the SetValue
command. Therefore, the event.target.bind
property contains the data bound to the MyText
component and the command can use that value.
[
{
"type": "TouchWrapper",
"onPress": {
"type": "SetValue",
"componentId": "MyText",
"property": "text",
"value": "The word of the day is ${event.target.bind.WordOfTheDay}"
},
"items": [
{
"type": "Text",
"text": "Click me!"
}
]
},
{
"type": "Text",
"bind": [
{
"name": "WordOfTheDay",
"value": "Bear"
}
],
"id": "MyText"
}
]
event.source
The event.source
property of the event
contains meta-information about the circumstances that triggered the event. The APL runtime generates event.source
. You can use this information in your document. The event.source
property contains all of the standard properties along with the following:
Property | Type | Description |
---|---|---|
handler | String | The name of the event handler that initiated this message. For example, "Press", "Checked". |
value | Any | The value of the component that initiated this message. |
The event.source.value
property depends on the component. For example, for a TouchWrapper
, event.source.value
contains the checked state of the component. For a ScrollView
, the property contains the the scroll position. Refer to the individual component definitions for the specific event.source.value
property provided.
event.source.value
property is deprecated and should not be used. Use the direct properties documented in for each component instead. For example, for the checked state of a TouchWrapper
, use event.source.checked
instead of event.source.value
.For backwards-compatibility with older versions of APL, event.source
property also contains a source
sub-property equal to the type
property. Therefore, ${event.source.source == event.source.type}
. Avoid using event.source.source
because it is deprecated.
event.target
The event.target
property provides state information about the component receiving the event. The values in the target
depend on the specific component. Refer to the individual component definitions for the specific target
properties you can expect.
For backwards-compatibility with older versions of APL the target property of the event also reports an source
sub-property equal to the url property for the Image
, VectorGraphic
, and Video
components. Therefore, ${event.target.source == event.target.url}
. Avoid using event.target.source
</span>` property because it is deprecated.
Evaluation notes
- Most commands take a componentId as a target. You can omit
componentId
for a command that targets itself. - When you use the
ExecuteCommands
directive to send a command to the device, you must always specify componentId. - When the command includes the
componentId
property, the value is used to search through all the components starting at the root of the tree hierarchy in a depth-first search manner and return the first component that matches the value. To guarantee that the command targets the correct component, do one of the following:- Assign a unique value to the target component with the
id
property and setcomponentId
for the command to that value. - Use the system-generated
uid
property value provided in the visual context.
- Assign a unique value to the target component with the
- Command data-binding expressions are evaluated when the command executes, not when it is defined. For example, an ExecuteCommands directive can contain data-bound expressions that refer to the global data-binding context. These expressions are evaluated when the command is executed on the device, not when the command is constructed in the cloud.
- Event handlers and the ExecuteCommands directive take an array of commands to execute. This array is treated exactly like a
Sequential
command command with a repeatCount of 0.
Command sequencing
The APL runtime environment is responsible for executing commands. User and environmental actions trigger commands to run. The Parallel
and Sequential
commands also trigger a set of commands to run. The command sequencing rules determine the behavior when multiple commands run at the same time. This is necessary because otherwise, commands might conflict with one another. You can control some of this behavior based on how you define your commands.
Note the following terms used in this section:
- Resource – Something that is intrinsically used by a command that can't be shared with another command. Commands that speak text use the "speech" resource. A command that acts on a specific component over time (such as AnimateItem) considers that component a resource.
- Normal mode – The standard method of executing commands. Most commands run only in normal mode. Normal mode commands may take time to execute.
- Fast mode – An alternative method of executing commands, used when it would be inappropriate to run commands that take time to execute. All delays and most commands are ignored in fast mode. Fast mode is used in event handlers such as onScroll, where the event handler could be fired many times per second.
- Sequencer – A named entity that can execute a single command at a time. Each
normal mode
executing command is associated with a named sequencer. Thecommand_sequencer_property
property is used to specify the sequencer. - Subcommand – A command contained within another command (
parallel_command
orsequential_command
). A hierarchy of subcommands is sometimes called a command tree.
Command sequencing rules
The APL runtime uses the following rules to sequence commands.
- Every normal mode command executes on a single sequencer. If that sequencer is already executing a command, the preexisting command is terminated and the new command is executed.
- Commands running in fast mode don't use a sequencer.
- Any command that explicitly specifies a sequencer executes in normal mode.
- A subcommand of a command runs on the same sequencer as its parent unless the subcommand explicitly specifies a sequencer. When the subcommand does specify a sequencer, the runtime hands off the command to the sequencer and marks the command as complete in the parent.
- The default sequencer is named "MAIN". When a normal mode command that isn't a subcommand doesn't explicitly name a sequencer, the command executes on the "MAIN" sequencer. Normal mode subcommands follow rule 4.
- Any user interaction with the device such as touching the screen or typing on a keyboard terminates any command running on the "MAIN"
sequencer
. - When a command starts executing, any currently running command using a
resource
required by the new command is terminated.
For example, the user touches a button the screen. This interaction starts a series of commands on the "MAIN" sequencer to do the following:
- Animate a change on the screen.
- Scroll down a list to a specific location.
- Speak an item.
The user can interrupt this series of commands at any time by touching the screen again. When the user touches the screen, the "MAIN" sequencer terminates the currently running command.
To override the default command sequencing behavior, you can specify a sequencer directly. Use this technique to run commands in parallel. Some scenarios in which you would change the sequencing behavior:
- You want an event handler that runs in fast mode by default to execute a normal mode command. For example, you want to execute a
SendEvent
command in aScrollView
component'sonScroll
event handler. Normally, theonScroll
handler ignores normal mode commands. Providing a named sequencer onSendEvent
in this case forces the handler to use normal mode, so theSendEvent
executes. - You want to override normal user interaction behavior and continue to run a command even when the user touches the screen. For example, you use an
AnimateItem
command to animate an on-screen component in a children's game where touching the screen should not stop the animation. - When multiple components animations are triggered to show new components appearing or old component disappearing.
Resources
Each command might use one or more system resources when executing. As defined in the sequencer rules, only a single command using a given resource may execute at a time. When a new command that requires that resource starts executing, the old command terminates.
The following is a list of resources and which commands use those resources:
- Foreground audio playback
SpeakItem
SpeakList
PlayMedia
when using the foreground audio track.ControlMedia
executing theplay
command on a foreground audio track.
- Background audio playback
PlayMedia
when using the background audio track.ControlMedia
executing theplay
command on a background audio track.
- Scroll position (per the scrolling component; either a
ScrollView
orSequence
) - Pager position (per the
Pager
component) - Component animation (per the component property being animated)
The SpeakItem
and SpeakList
can use multiple resources.
Normal mode
The APL runtime executes a command in normal mode in the following scenarios:
- When the document initially loads (Document
onMount
and ComponentonMount
). - The user touches or selects a
TouchWrapper
(onPress
). - The user swipes on a
Pager
and changes the displayed page (onPageChanged
) - A Video component ends playback of the video or changes the track (
onEnd
,onPause
,onPlay
,onTrackUpdate
). - A new set of commands arrives from an external source such as the ExecuteCommand directive or an extension event handler.
- The user presses or releases a key on the keyboard (
handleKeyDown
andhandleKeyUp
).
By default all normal mode commands use the "MAIN" sequencer.
In the following example the onPress event handler has an array of commands. This array is treated as a sequential array to be execute. Therefore, SpeakItem
runs first. After the speech finishes, the Scroll
command executes. When Scroll
finishes, the SendEvent
command executes and sends a UserEvent
to your skill. All of these commands run on the "MAIN" sequencer.
{
"type": "TouchWrapper",
"items": {
"type": "Text",
"id": "myText",
"speech": "VALUE TO SPEAK"
},
"onPress": [
{
"type": "SpeakItem",
"componentId": "myText"
},
{
"type": "Scroll",
"componentId": "myScrollRegion",
"distance": 2
},
{
"type": "SendEvent",
"arguments": [
"The button was pushed and spoken have I"
]
}
]
}
In the above example, the component executing the Scroll
command generates scrolling events which might invoke commands defined in the component's onScroll event handler. These commands run in fast mode and therefore don't affect the currently running sequence of commands.
If the user taps the TouchWrapper
multiple times, the command sequence ends and restarts with each tap. Although you could set the onPress
handler to also disable the TouchWrapper
, this doesn't solve the problem. Any user tap on the screen terminates the commands on the "MAIN" sequencer.
Instead, to guarantee that the sequence of commands completes, assign a different sequencer to the commands. The following example both disables the TouchWrapper
so that the user can't restart the sequence until it finishes and uses a different sequencer so that touching the screen does not cancel the commands.
{
"type": "TouchWrapper",
"items": {
"type": "Text",
"id": "myText",
"speech": "VALUE TO SPEAK"
},
"onPress": [
{
"type": "Sequential",
"sequencer": "MySequencer",
"commands": [
{
"type": "SetState",
"state": "disabled",
"value": true
},
{
"type": "SpeakItem",
"componentId": "myText"
},
{
"type": "Scroll",
"componentId": "myScrollRegion",
"distance": 2
},
{
"type": "SendEvent",
"arguments": [
"The button was pushed and spoken have I"
]
}
],
"finally": {
"type": "SetState",
"state": "disabled",
"value": false
}
}
]
}
Because the custom "MySequencer" is not the "MAIN" sequencer, another touch on the screen does not cancel the commands. Note that this example executes the final SetState
command in the finally
block. If the Sequential
does terminate for some reason, the commands specified in finally
still run to re-enable the TouchWrapper
.
Note that the previous example works for a series of commands because all the commands are subcommands of the Sequential
. According to the sequencer rules, subcommands run on the same sequencer as the parent unless they specify their own.
In contrast, the following array of commands assigned to onPress
doesn't work. When the user taps this TouchWrapper
, nothing happens:
{
"type": "TouchWrapper",
"items": {
"type": "Text",
"id": "myText",
"speech": "VALUE TO SPEAK"
},
"onPress": [
{
"type": "SetState",
"state": "disabled",
"value": true,
"sequencer": "BadIdea"
},
{
"type": "SpeakItem",
"componentId": "myText",
"sequencer": "BadIdea"
},
{
"type": "Scroll",
"componentId": "myScrollRegion",
"distance": 2,
"sequencer": "BadIdea"
},
{
"type": "SendEvent",
"arguments": [
"The button was pushed and spoken have I"
],
"sequencer": "BadIdea"
},
{
"type": "SetState",
"state": "disabled",
"value": false,
"sequencer": "BadIdea"
}
]
}
This example fails because the onPress
command runs on the MAIN sequencer by default. Specifying a different sequencer causes the handler to hand off the command to the other sequencer and then execute the next command in the array. When a sequencer receives a new command, it terminates any existing command and replaces the existing command with the new one.
The sequence of events for the failed example would be the following.
- The
onPress
handler hands offSetState
to theBadIdea
sequencer. - The
onPress
handler moves on toSpeakItem
and immediately hands offSpeakItem
toBadIdea
. - The
BadIdea
sequencer cancelsSetState
to startSpeakItem
. - The
onPress
handler hands offSendEvent
toBadIdea
. - The
BadIdea
sequencer cancelsSpeakItem
to startSendEvent
. - The
onPress
handler hands offSetState
toBadIdea
. - The
BadIdea
sequencer cancelsSendEvent
to startSetState
. - The
SetState
command does successfully run, but has no effect because theTouchWrapper
is already enabled.
Sequential
command and assign the new sequencer to the Sequential
command.You can also use a sequencer to toggle a command between running and not running. In the following example, the first TouchWrapper
executes an AnimateItem
command to animate moving the ball. The second TouchWrapper
forces the animation to stop by sending a new command to the same sequencer to cancel the AnimateItem
command.
{
"type": "Container",
"direction": "row",
"items": [
{
"type": "Frame",
"id": "Ball",
"width": 100,
"height": 100,
"borderRadius": 50,
"backgroundColor": "red"
},
{
"type": "TouchWrapper",
"items": {
"type": "Text",
"text": "Start Moving"
},
"onPress": [
{
"type": "AnimateItem",
"easing": "ease-in-out",
"duration": 1000,
"repeatCount": 10000000,
"repeateMode": "reverse",
"componentId": "Ball",
"sequencer": "BallSequencer",
"value": {
"property": "transform",
"from": {
"translateY": 0
},
"to": {
"translateY": 300
}
}
}
]
},
{
"type": "TouchWrapper",
"items": {
"type": "Text",
"text": "Stop Moving"
},
"onPress": [
{
"type": "Idle",
"sequencer": "BallSequencer"
}
]
}
]
}
The onPress
handler for the second TouchWrapper
executes an Idle
command on the BallSequencer
to terminate AnimateItem
. The Idle
command doesn't do anything, but it does terminates any command currently running on the sequencer. Any other command sent to BallSequencer
would have the same result.
Fast mode
Some event handlers can trigger many times per second. For example, the onScroll
handler on a ScrollView
fires every time the scroll position moves. At the same time, some commands might take a measurable amount of time to execute. For example, scrolling a list on the screen or speaking text.
To avoid issues with long-running commands executing from frequently-fired event handlers, those handlers execute their commands in fast mode.
Any set of commands triggered from an event handler that executes at frame rate uses fast mode. All other command sequences execute in normal mode. In fast mode, the handler ignores all delay settings in the commands and skips commands that take measurable time to execute. The event handlers have the following behavior:
Event Handler | Behavior |
---|---|
Actionable onBlur |
Fast mode |
Actionable onFocus |
Fast mode |
Actionable handleKeyDown |
Normal mode |
Actionable handleKeyUP |
Normal mode |
Component onCursorEnter |
Fast mode |
Component onCursorExit |
Fast mode |
Component onMount |
Normal mode |
Document onMount |
Normal mode |
Pager onPageChanged |
Normal or fast mode |
ScrollView onScroll |
Fast mode |
Sequence onScroll |
Fast mode |
Touchable onDown |
Fast mode |
Touchable onMove |
Fast mode |
Touchable onUp |
Fast mode |
Touchable onPress |
Normal mode |
Video onEnd |
Normal or fast mode |
Video onPause |
Normal or fast mode |
Video onPlay |
Normal or fast mode |
Video onTimeUpdate |
Fast mode |
Video onTrackUpdate |
Normal or fast mode |
A one-time event that occurs due to user interaction, such as tapping a TouchWrapper
, always executes in normal mode. Events such as scrolling or time updates execute in fast mode. Events that can execute in either mode can be triggered by a normal action (such as a video track ending) or by a command that was ultimately triggered by fast mode (such as a video pause from a scroll event). External commands and extension commands execute in normal mode.
Each command documents its behavior in fast mode. The following table summarizes fast-mode behavior:
Command | Fast mode behavior |
---|---|
Idle |
Ignored |
Sequential |
Executed |
Parallel |
Executed |
SendEvent |
Ignored |
SetValue |
Executed |
SetState |
Executed |
SetFocus |
Executed |
ClearFocus |
Executed |
SpeakItem |
Ignored |
SpeakList |
Ignored |
Scroll |
Ignored |
ScrollToIndex |
Ignored |
ScrollToComponent |
Ignored |
SetPage |
Ignored |
AutoPage |
Ignored |
PlayMedia |
Ignored |
ControlMedia |
Ignored for command="play" , executed otherwise. |
OpenUrl |
Ignored |
AnimateItem |
Jumps to end state. |
Finsih |
Executed |
Select |
Executed |
Any command with a value in the sequencer
property executed in fast mode is handed off to the specified sequencer to be run in normal mode. Use this feature to execute normal mode commands from fast mode event handlers.
The following example checks the position of a scroll view whenever it moves and sends an event when the scroll position is at the top. It applies a rate limit by recording the time when it sends an event is sent, so at least a second passes between events. Without the sequencer
property, the SendEvent
would never execute.
{
"type": "ScrollView",
"bind": [
{
"name": "LastEventSentTime",
"value": 0
}
],
"onScroll": [
{
"type": "Sequential",
"when": "${event.source.value == 0 && utcTime > LastEventSentTime + 1000}",
"commands": [
{
"type": "SetValue",
"property": "LastEventSentTime",
"value": "${utcTime}"
},
{
"type": "SendEvent",
"arguments": [
"top of scroll view reached"
],
"sequencer": "ScrollSender"
}
]
}
]
}
Command Trees
A command tree is the complete set of commands that executes. Command trees occur because commands may be nested or because a command may cause a new event handler to fire. Various primitive commands have nested commands:
Parallel
Sequential
OpenURL
User-defined commands also support nested commands.
Primitive commands can also trigger event handlers. For example, the Scroll
ScrollToComponent
, and SpeakList
commands can all invoke the onScroll
event handler.
Command trees can run to completion or terminate before completion. A terminated command tree stops execution of all commands immediately. A source triggers the execution of a command tree. The command tree then runs to completion unless it terminates early.
The following example illustrates command tree for an ExecuteCommands
directive that includes the Scroll
, SpeakItem
, and PlayVideo
commands.
ExecuteCommand
+ Scroll (distance=-10000) // Scroll to top
+ onScroll // Fires multiple times as the view scrolls to the top
+ SetValue (name="opacity", value=event.source.value * 10) // Change opacity
+ SpeakItem (id) // Scroll item into view and run karaoke
+ onScroll // Fires multiple times as the view scrolls
+ SetValue (name="opacity"....)
+ PlayVideo (synchronously)
+ onStart // Fires once
+ onTrackUpdate // Fires each time a new track is displayed
+ SetValue (name="progress"...) // Update a progress bar display
+ onStop // Fires once
This series of commands scrolls to the top of the screen, speaks one of the items, and plays a video. If the user touches on the screen during playback, any running speech, scrolling, or video playback will be halted.
An individual command inside of a series of commands might specify a different sequencer. When command is reached, it is handed off to the appropriate sequencer and then the next command in the sequence runs immediately. Commands that don't have an explicit sequencer property run on the current sequencer. Commands only run on the next sequencer after command delay has been processed. For example, consider the following series of commands running on the main sequencer:
+ Sequential
+ AnimateItem A (delay=100, duration=1000)
+ AnimateItem B (delay=200, sequencer="other", duration=2000)
+ Parallel (delay=200)
+ AnimateItem C (duration=1000)
+ AnimateItem D (sequencer="other", duration=2000)
+ AnimateItem E (delay=100, duration=1000)
The following table summarizes the time line actions caused by this command tree.
Time | Action |
---|---|
0 | Sequential command starts |
100 | AnimateItem A starts |
1100 | AnimateItem A finishes |
1300 | AnimateItem B starts on sequencer "other" |
1500 | Parallel command starts |
AnimateItem C starts | |
AnimateItem D starts on sequencer "other". AnimateItem B is terminated. | |
2500 | AnimateItem C finishes |
Parallel command finishes | |
2600 | AnimateItem E starts |
3500 | AnimateItem D finishes on sequencer "other" |
3600 | AnimateItem E finishes |
Sequential command finishes |
When a command tree is terminated, it needs to go to a consistent state. APL makes a number of assumptions:
- Scrolling should stop
- Page turns should be canceled and return to either the original page or the next page (whichever is closer)
- Speaking should immediately stop
- Scene changes and structural changes to the layout should “jump” to their final position.