LiveCode for C, C++ and Java Developers
This article is for new LiveCode developers who have experience developing applications in other programming languages (such as C, C++, or Java), and who require an overview of LiveCode’s development process and programming model.
Moving Into LiveCode
LiveCode is a development environment that integrates user-interface design with writing and testing program code. Instead of building your user interface in one environment and writing your code in another, then combining them, you create the user interface right in the development environment, using drag-and-drop tools to draw objects such as buttons and text fields right onto your application’s windows. You modify the appearance and behavior of these objects in the Properties palette.
Then you write code in the LiveCode language for each object, associating the code directly with the object. (For example, the code affecting a button is contained in the button, and goes with it when you copy and paste the button.) You can change an object’s properties directly from within a LiveCode program statement, as well as in the Properties palette in the development environment. This means your application can determine appearance and behavior of the user interface at runtime.
There is no compile/link/run cycle; the test runtime environment, user interface builder, and program editor are one and the same. You go directly from editing code to modifying the user interface to testing the application and back again, without any need to re-launch or switch environments. To test your application in a “clean” environment without any of LiveCode’s own menus, palettes, or other parts of the development environment, use the “Suspend LiveCode UI” item in the Development menu.
When you’ve completed testing, you compile your application into a double-clickable standalone using the Distribution Builder. You can build standalones, from the same file, for any or all of the platforms LiveCode supports. Your finished applications will have the native appearance and behavior for each platform you deploy on.
While LiveCode and the LiveCode language are full-featured and capable of complex operations, the learning curve is gentle. Experienced programmers will begin to be productive with LiveCode in just a few hours after doing the tutorials.
The LiveCode Development Environment
As an integrated development environment, LiveCode contains all the tools you need to create applications: interface builder, code editor and debugger, runtime environment, and application builder. The development environment—menus, palettes, dialog boxes, and all—is built entirely in LiveCode. This not only demonstrates the power and speed of the LiveCode engine, it means that as you advance, there is the possibility of exploring the LiveCode user interface and customizing it for your needs.
These are the basic tools you can expect to use most often when creating LiveCode applications. All these tools and their uses are described in the LiveCode Tutorials, but here is a quick overview:
The browse and pointer tools
Like a draw program, LiveCode’s interface builder has various modes you access by clicking tools in a tool palette. Choosing “Tools Palette” from the Tools menu shows the tool palette. The two tools at the top are the ones you will use most often.
The tool on the left, called the Browse tool, is shaped like a hand. You use the Browse tool to test your application, to click buttons, and to enter text into fields.
The tool on the right, called the Pointer tool, is shaped like an arrow. You use the Pointer tool to select objects in your application’s windows. To select an object such as a field or button, you click the object with the Pointer tool.
The Properties palette
A property is an attribute of a LiveCode object. Each type of object has built-in properties that determine the object’s appearance and behavior. You can also define custom properties for any object, and use them to store any kind of data.
You access an object’s properties by selecting the object, then choosing “Object Properties” from the Object menu. The Properties palette contains tabs across the top for:
basic properties (common to all objects)
the object’s script
the object’s custom properties
properties specific to the object’s type (some object types, such as buttons, have two tabs)
You can also set any property programmatically using Transcript’s set command.
The script editor
You use the script editor by clicking the Script tab in the object’s Properties palette. Each object has its own script, which consists of the LiveCode code that belongs to that object.
The LiveCode script editor accepts styled text, so you can use colors and fonts to emphasize portions of your code, mark unfinished code, and so on. To search a script, choose “Find and Replace” from the Script menu while in the script editor.
The message box
The message box is a small palette that serves as an instant command interpreter or mini-console, a calculator, and a quick command tester. You can enter any command into the message box for immediate execution, or enter any expression for evaluation.
To open the message box, choose “Message Box” from the Tools menu.
The Application Overview
The Application Overview gives you a hierarchical view of all the objects in every open LiveCode file. Here you can browse the object tree, access each object’s properties and scripts, preview multimedia objects, and import, move, and delete objects.
To open the Application Overview, choose “Application Overview” from the View menu.
LiveCode’s Object Model
The LiveCode object model is object-based. There are twelve classes of objects, arranged in a well-defined object hierarchy, and objects inherit the behavior of other objects higher in the hierarchy. Program execution is triggered by messages, which are dispatched by the LiveCode engine in response to user actions and other events, and which pass from object to object according to the object hierarchy. All but two of the object types have a visual representation.
Objects are related to each other in an “is-part-of” model, rather than an “is-a” model. Each object is part of another object type that’s at a higher level of the hierarchy. For example, buttons inherit from the windows they belong to, but a button is part of a window, rather than being a type of window.
A window in LiveCode is called a stack, and stacks are the highest objects in the object hierarchy: every other type of object is contained in a stack. Each window you see in LiveCode is a stack. Palettes, dialog boxes, and standard windows are all stacks.
Each LiveCode file contains one or more stacks. The first stack in the file is called the main stack, and is part of the object hierarchy for all other stacks in that file. Other stacks are called substacks of the main stack. To change which stack in a file is the file’s main stack, you use the mainStack property, or the Application Overview.
Each stack, in turn, contains one or more screens of information, called cards, which are organized in a doubly-linked list. The card is the entire content area inside the stack window; a stack window displays one card at a time. Each card can have a different set of objects, or all the cards in a stack can contain the same objects. A stack can be thought of as an ordered collection of one or more cards.
Each card can contain buttons, text fields, bitmapped images, vector graphics, scrollbars, video or sound players, and (on Unix systems) EPS objects. These objects are referred to as controls because the user interacts with them.
Any set of one or more controls can be incorporated into a group. When a control is included in a group, the group becomes a part of the object hierarchy for that control. Groups can also be nested, so a group can contain one or more groups. A group can appear on more than one card in a stack.
Finally, audio clip and video clip objects hold sound or movie data. Neither audio clips nor video clips appear in the stack window. Both audio clip and video clip objects inherit directly from the stack they are in.
Messages and the Message Path
The message path is the set of rules that determine which objects, in which order, have the opportunity to respond to a message.
Each message has an initial target, which is the object the message is originally sent to. For example, if the user clicks in a window, a mouseDown message is sent to the object that was clicked. If that object does not handle the message, it passes to the next object in the object hierarchy. If that object does not handle the message either, it passes to the next object in the hierarchy, and so forth, until the top of the hierarchy is reached.
The pass and send commands can be used to temporarily override the normal message path. You can also use the insert script and start using commands to insert objects into a specified point in the message path.
LiveCode sends messages in response to user actions such as typing, as well as system events. For a complete list of LiveCode messages, see the Transcript Dictionary section of the LiveCode documentation. You can also send your own custom messages to any object, and write custom message handlers to respond to these messages.
LiveCode is inherently event-driven. All LiveCode code is contained in message handlers, which are executed when the object whose script contains the handler receives the corresponding message.
There is no explicit event loop or main() function. LiveCode sends messages when a stack opens, when a card opens, and when the application starts up, and you can write handlers for these messages to initialize the application environment as needed.
Each object has a script, which contains all the Transcript code pertaining to that object. An object’s script is a property of the object, like other properties, and can be set programmatically using the set command. In other words, the code for each object is attached to the object and is stored in the same file as the object.
The LiveCode Language
LiveCode is a verbose language with an English-like syntax, allowing for easy readability and maintainability. Scripts are internally compiled to a byte-code-like representation, which combines the speeds characteristic of a compiled language with the flexibility of an interpreted language.
Statements must be separated by either returns or semicolons. LiveCode scripts are not otherwise whitespace sensitive, although the script editor can auto-indent scripts to display their structure. The language is case-insensitive.
Each object has a script consisting of one or more routines, called handlers. There are four handler types:
Message handlers are executed when a particular message is received by the object. Message handlers begin with the word on.
Function handlers are executed when a function call is made by a handler in the same object or lower in the object hierarchy, and typically return a value to the calling handler. Function handlers begin with the word function.
SetProp handlers are executed when a particular custom property is set for the object or for an object lower in the object hierarchy. SetProp handlers begin with the word setProp.
GetProp handlers are executed when a particular custom property’s value is requested. GetProp handlers begin with the word getProp.
The script of a particular object can contain any or all of these handler types, and can contain an effectively unlimited number of handlers of each type.
Parameters can be passed to any handler, either by value or by reference. They are named in the first line of a handler, separated by commas.
For example, the first line of a custom message handler might look like this:
on myMessage firstParam,secondParam,thirdParam
In this example, “myMessage” is the name of the handler (and therefore the name of the message that triggers it). “firstParam”, “secondParam”, and “thirdParam” are three parameters that may be passed to this handler. The handler can be called with a statement that looks like this:
myMessage "Some string", 2, 8+4
If a parameter value is not supplied by the calling statement, its value in the handler is the empty string.
You can also access undeclared parameters from within a handler by using the param, paramCount, and params functions. This lets you pass an indeterminate number of parameters to a handler.
Variables and sources of value
Transcript supports scalar and array variables. Arrays are associative, meaning you can use any value—not just integers—as the key for an array element.
Although arrays are supported, LiveCode is otherwise a typeless language. The compiler performs appropriate casting internally when performing numeric operations, but from the viewpoint of a LiveCode developer, all variables are treated as strings. It is not necessary to assign a type to a variable. You can test the data type of the contents of a variable (or any other source of value) using the is a and is not a operators.
Variables may contain text or binary data. You set a variable’s value using the put command:
put "something or other" into myVariable
Local variables need not be declared or initialized. If a variable does not already exist, putting something into it declares it as a local variable and initializes it to the specified value. (You can set the explicitVariables global property to true to force all variables to be explicitly declared.)
By default, the scope of a variable is the current handler. A variable can be made either local to an object, or global, by using the local or global command to declare the variable and optionally initialize it. A variable that is declared but not initialized has the empty string as its value.
In addition to variables, LiveCode can use functions, parameters, object properties, field text, and URLs as sources of value. In general, sources of value can be used interchangeably in any LiveCode expression, without needing to read the source of value into a variable. For example, this is a valid expression in LiveCode, consisting of the sum of field text, a variable, and a function result:
(field "Some Number") + someNumericVar + currentSum()
LiveCode supports the following standard control structures:
repeat: Use the repeat control structure to loop through a set of statements a specified number of times, while a specified condition is true, by iteration over a set of values, or until the loop is explicitly broken with an exit repeat statement.
conditional: Use the if control structure to conditionally execute a set of statements.
switch: Use the switch control structure to select from a number of cases.
try/catch/throw: Use the try control structure to execute a set of statements while handling errors internally.
Control structures can be nested to any depth.
LiveCode distinguishes between subroutines, which are implemented as message handlers, and functions, which are implemented as function handlers.
You call a message handler by using its name (and any parameters) as a LiveCode statement. The message handler can reside in the script of the same object as the calling handler, or in any other object. The message is sent through the normal message path, starting with the current object.
As with any message, you can override the message path by using the send command to trigger a message handler in any object.
You call a function handler by using its name followed by parentheses (which enclose any parameters) in an expression. The function handler follows the same message-path rules as message handlers. These are examples of function calls:
put someFunction(firstParam,secondParam) into myVariable
put myFunction() into field "Help"
You can override the message path for a function call by using the value function to specify the object that the function handler resides in.
Function handlers use the return control structure to return a value to the calling handler.
Libraries and Code Re-use
The message hierarchy allows a single handler to control multiple objects. For example, the controls on a card are lower in the hierarchy than the card object, so a mouseDown handler in the card’s script can respond to mouse clicks on any object that’s on the card. Similarly, a handler in a stack script is available to every object in that stack. Strategic placement of handlers in the message hierarchy helps avoid duplication of code between similar objects.
To filter a handler so that it only responds to certain objects, rather than every object below it in the hierarchy, use the target function to determine which object originally received the message being handled.
The insert script command adds an arbitrary object to the message hierarchy. To create a code library, place the handlers you want to re-use in any object that’s available in your a stack, then use the insert script command to add that object to the hierarchy. Handlers in that object’s script are now accessible to any other handler in LiveCode.
The Development Process
This is a quick overview of the basic process of developing an application in LiveCode. For more concrete, hands-on information, do the LiveCode Tutorials.
The first step in developing a LiveCode application is creating a new main stack. Typically, this stack will be your application’s main window.
Next, create any other windows, palettes, and dialog boxes your application needs, as substacks of the main stack. This allows your entire application to reside in a single file.
Next, create the controls your application needs in each window. You can create push buttons, popup menus, text fields, list fields, and more. Use the “New Control” submenu in the Object menu to get an idea of the kinds of control you can create.
Use the Menu Manager (in the Tools menu) to create a cross-platform menu bar for your application.
Finally, write the code needed for each object to implement its behavior, including library routines as needed.
This development process is “inside out” compared to the conventional development methodology. In LiveCode, it’s usually most efficient to first develop the user interface, then write the code for each interface element. (Of course, you can refine, change, and add to both user interface and code at any stage in the development process.)
Your LiveCode application will generally be cross-platform ready as designed, though a few tweaks may be needed for optimal function and display on all platforms. LiveCode language elements that vary between platforms are marked in the LiveCode Dictionary. You can preview the look and feel of your application on any platform using the “Look and Feel” submenu in the View menu.
When you’re done, choose “Build Distribution” from the File menu to create standalone applications for the platforms you want to deploy on. The Distribution Builder creates a native application for each platform you select.