3. #LiveCodeGlobal
LiveCode Builder Extensions
In this lesson we will create a minimal LiveCode Builder library
which extends the set of commands and functions available in
LiveCode.
We will
● Install and configure an appropriate text editor for developing in
LiveCode Builder.
● Create a Hello World library which extends LiveCode with a
single function, which returns the string “Hello World!”.
● Compile the library.
● Install the library into the LiveCode IDE.
● Include the library in a standalone.
4. #LiveCodeGlobal
Libraries and Widgets
There are two types of extensions that can be created with LiveCode Builder:
● Libraries: A library can be used to extend the set of commands and
functions available in LiveCode. The public handlers in a library are
added at the bottom of the Message Path. LCB libraries make LCB
features available to LiveCode apps directly. They can also be used as
support code for widgets.
● Widgets: A widget is a custom control that is treated as an engine level
element. Widgets appear in the Tools Palette and can be added as
controls to any LiveCode stack.
A widget and a library are identical, except that a widget draws to a canvas.
As a result, the authoring process is much the same for both extension types.
5. #LiveCodeGlobal
When to use LiveCode Builder
Write widgets when:
● You need unique capabilities that can't be provided by
"traditional" controls.
● You require encapsulation - widgets are 'black boxes' whose
behavior can only be changed from LiveCode apps via
predefined API.
● You want access to drawing / low-level / native APIs.
● You want improved performance, you control what
recalculations are made when a control is redrawn.
6. #LiveCodeGlobal
When to use LiveCode Builder
Write libraries when:
● You want to write functions that are available to both LiveCode
Builder(LBC) and LiveCode Script(LCS).
● You want to wrap platform-specific functionality and make it
usable from LiveCode Script.
● You want to use LiveCode Builder's stricter rules to implement
algorithms that can't be expressed reliably in LiveCode Script.
7. #LiveCodeGlobal
The Message Path
LiveCode Engine
LiveCode Builder Libraries
Stack
Card
Group
Grouped Control Control
Input Events
(mouseUp, keyDown, etc.)
Other Events
(openCard, closeStack, etc.)
Handlers
implemented in
installed libraries are
placed at the end of
the Message Path.
For more on the
Message Path see
the “Message Path”
Chapter of the
LiveCode User
Guide.
8. #LiveCodeGlobal
Installed widgets are shown in the widgets section of the Tools Palette.
Widgets are added to stacks by dragging and dropping from the Tools
Palette.
Tools Palette showing installed widgets
Rotated Text Widget
Pie Chart Widget
9. #LiveCodeGlobal
LiveCode Builder API
The API for LiveCode Builder is
available in the Dictionary
stack. You may find it useful to
have this open while you are
writing your extension.
1. Open the Dictionary from
the Menubar.
2. Ensure you are on the API
pane.
3. Select LiveCode Builder
from the drop down menu.
10. #LiveCodeGlobal
LiveCode Builder extensions are written in a text editor rather than
the LiveCode IDE.
We recommend using the Atom text editor. A LiveCode package is
available which provides some colorization as well as indentation.
If you are on Mac and prefer to use TextWrangler, there is a
colorizing script here. It should be placed in /Application
Support/TextWrangler/Language Modules/.
Installing a Text Editor
11. #LiveCodeGlobal
Installing the Atom LiveCode Language Pack
To install the LiveCode
Language Pack in Atom
● Select File -> Preferences
to open the Atom Settings
● Go to the Install tab of the
Settings pane
● Search for the
“language-livecode”
package in the Search field
● Click Install
12. #LiveCodeGlobal
Creating a LiveCode Builder Library
Start by creating a new directory to
develop your extension in.
When you package an extension a
number of files are created. The
Extension Builder currently relies on
there being only one .lcb file in a
given directory. Because of this it is
simplest to create a new directory
for each extension you create.
The extension files that are created
include an API file, manifest and
compiled module file.
13. #LiveCodeGlobal
Creating a LiveCode Builder Library
Create a new plain text file in the
directory and save it to disk with the
extension ".lcb".
It’s important to use the “.lcb”
extension so your chosen text editor
applies LiveCode Builder formatting
to your code, if available.
I am naming my file
"helloWorldLibrary.lcb" but you can
name your file anything suitable.
14. #LiveCodeGlobal
LCB Library Definition
The first thing we need to do is declare
the type of extension we are writing.
We are writing a library and so we need
to declare the extension as such.
The library declaration is followed by an
identifier. An extension identifier should
be in the form
community.livecode.<user name>.<library
name>
In my case my username is "elanorb" and
I have chosen to use "helloworld" as the
name part of the identifier.
library community.livecode.elanorb.helloworld
end library
15. #LiveCodeGlobal
LCB module naming
The module name uses reverse DNS notation. For example, a module created by the
Example Organisation would use module names beginning with ‘org.example.’.
You must only use module names corresponding to domain names that you control or
are allowed to use.
If you don't have a domain name of your own, you may use module names beginning
with ‘community.livecode.’. For example, if your username is "sophie", then you can
create a module named ‘community.livecode.sophie.mymodule’.
Always write module names in lower case.
For more detailed information check the LiveCode Builder Style Guide. The guide is
available under the ‘Guide’ tab in the Dictionary or as a PDF under the ‘Resources’ tab
for this lesson.
16. #LiveCodeGlobal
LCB public handler definitions
Handler definitions are used to define functions which can be called from LCB code,
invoked as a result of events triggering in a widget module, or called from LCS if public
and inside a library module.
There is no distinction between handlers which return a value and ones which do not,
apart from the return type.
Definitions can be either public or private (the default is private). Private handlers can
only used within the module. Public handlers are available when the module is used by
another module, for example public handlers can be called from LCS.
Handler definitions have the form
<HandlerType> handler <HandlerName>(<ParameterList>) [ returns <ReturnType> ]
end handler
17. #LiveCodeGlobal
Naming Handlers
It is best practice to give handlers TitleCase names.
In general, please use verbs to name your handlers. For example,
handler RotateShape(inout xShape, in pAngleInDegrees)
...
end handler
For more on this see the LiveCode Builder Language Reference
Guide in the Dictionary or Additional Course Resources.
18. #LiveCodeGlobal
The Hello World Library has one handler which
returns the string "Hello World!".
Add the definition for the sayHello handler to the
library.
Let's break down the definitions
● HandlerType - public: we want to be able
to call this handler from LCS so it needs
to be public
● HandlerName - SayHello: the name of the
handler, used to call the handler from LCS
● ParameterList - empty, no parameters are
passed in.
The SayHello handler
<HandlerType> handler <HandlerName>
(<ParameterList>) [ returns <ReturnType> ]
library community.livecode.elanorb.helloworld
public handler SayHello()
end handler
end library
19. #LiveCodeGlobal
To return a string value we can
return the string directly.
Add a return statement to the
sayHello handler.
Returning a string
library community.livecode.elanorb.helloworld
public handler SayHello()
return "Hello World!"
end handler
end library
20. #LiveCodeGlobal
Testing the library
Now we have a complete library
we want to compile and test it.
To do this we use the
LiveCode Extension Builder.
1. Open LiveCode.
2. Open the Extension
Builder from the Tools
Menu.
21. #LiveCodeGlobal
The Extension Builder
1. Open button - allows you to select and
load the extension you wish to build.
2. Data that the builder was able to parse
from the directory such as icons,
resources, API's the user guides.
3. Log: Shows status, error and log
messages.
4. Test Button: compiles the extension and
loads it into LiveCode
5. Script Button: Opens the lcb script in an
external default editor.
6. Install Button: Installs the extension into
the IDE
7. Uninstall Button: Uninstalls the extension
from the IDE
8. Package Button: Creates a .lce package
which can be shared
22. #LiveCodeGlobal
Compiling the Hello World Library
Initially we will use the Test button
to compile the library and test it
without installing.
1. Click the Open button and
select the
“HelloWorldLibrary.lcb” file.
2. The Icons, Resources, Default
Script, API and User Guide
sections are automatically
populated.
3. Click the Test button to
compile and load the library in
LiveCode.
23. #LiveCodeGlobal
Compiling the Hello World Library
The log field will give you
show the compilation progress
and display any warnings.
The Extension Builder will
create a test stack.
24. #LiveCodeGlobal
LiveCode Builder Extension Files
When the Extension Builder is used to compile a LiveCode Builder file a number of files
are created.
● api.lcdoc - LiveCode doc file for the extension. This file is generated by extracting
all of the documentation information from the LCB source code, and is used to
display the API documentation for the library in the Dictionary window in the IDE
● mylibraryname.lci - LiveCode Interface file. This file provides information about
the "public" interface for the library. It's used by the compiler when compiling LCB
source code that uses the library.
● Manifest.xml - This file describes the library and its contents in a structured format.
It is used by the IDE and by other tools to discover what the library contains.
● Module.lcm - The compiled module file. This is the compiled library bytecode,
which is the output of compiling the library source code. It is the low-level code
that's run by the LCB virtual machine when the library is in use.
Each module you create will have files with the same names, for example api.lcdoc,
which is why we create each extension in a separate directory.
25. #LiveCodeGlobal
Now the library is loaded we can
test it in a LiveCode stack.
● Add a button to the test stack.
● Set the code of the button to
● Click the button. You should
see an answer dialog
displaying the “Hello World!”
message returned by the
library.
Testing the library in a LiveCode stack
on mouseUp
answer sayHello()
end mouseUp
26. #LiveCodeGlobal
In order to package any extension in LiveCode Builder we need to add some
required metadata. The metadata provides information about the extension.
The required metadata is
● title: a human-readable name for the module
● author : the name of the author of the module
● version a string in the form X.Y.Z (with X, Y and Z integers) describing
the module version
You can also include an optional description
● description: a simple description of the module's purpose
If the required metadata is not added a number of warnings will be printed
into the log field when building and packaging the extension.
LiveCode Builder extension metadata
27. #LiveCodeGlobal
Version numbers
We suggest using Semantic Versioning when assigning the version number of
the extension.
The uses a version format of X.Y.Z (Major.Minor.Patch).
Given this format you would increment
1. The Major version when you make incompatible API changes.
2. The Minor version when you add functionality in a backwards-compatible
manner.
3. The Patch version when you make backwards-compatible bug fixes.
Under this scheme version numbers, and the way they change, provide clear
and valuable information about the underlying code and the changes between
versions.
For a more detailed explanation of Semantic Versioning see http://semver.org/.
28. #LiveCodeGlobal
Library metadata
library community.livecode.elanorb.helloworld
metadata title is "Hello World Library"
metadata author is "Elanor Buchanan"
metadata version is "1.0.0"
… handler definition
end library
Add the metadata after the
library definition in the LCB file
● title: a human-readable
name for the module, I
have used “Hello World
Library” but you can
choose any suitable name
● author: add your name as
the author of your library
● version: this is the first
version of the library so the
version is “1.0.0”
29. #LiveCodeGlobal
The finished library
library community.livecode.elanorb.helloworld
metadata title is "Hello World Library"
metadata author is "Elanor Buchanan"
metadata version is "1.0.0"
public handler sayHello()
return "Hello World!"
end handler
end library
The library is now complete,
including metadata, ready to be
installed into LiveCode
30. #LiveCodeGlobal
Installing the library into the LiveCode IDE
The Extension Builder is also used to
install libraries. Installed libraries are
loaded each time LiveCode is started up.
Extensions are installed into your My
LiveCode folder.
1. Open the Extension Builder from
the Tools menu.
2. Click the Open button and load the
“HelloWorldLibrary.lcb” file.
3. Click the Install button to install the
library.
4. You will be asked to select an icon
and a high resolution icon, click
‘Cancel’ on these dialogs as no
icon is needed for a library.
31. #LiveCodeGlobal
The installed library
Go to your My LiveCode folder.
In the Extensions folder you will see
a folder with the same name as
your library identifier.
This folder contains the files that
are make up the packaged
extension.
Any extensions installed to My
LiveCode are automatically loaded
when LiveCode is started up.
32. #LiveCodeGlobal
To test your library is correctly installed
execute execute
put the loadedExtensions
in the Message Box. Your library will
appear in the list of extensions.
To test the library handler execute
put sayHello()
in the Message Box
Testing the installed library
33. #LiveCodeGlobal
Packaging a library
In order to share your library you create a
package. This package can be shared
with other LiveCode users.
1. Open the Extension Builder from
the Tools menu.
2. Click the Open button and load the
“HelloWorldLibrary.lcb” file.
3. Click the Package button.
This will create a packaged library file
with the extension .lce in the same folder
as your .lcb file. The name of the file is
the library identifier followed by the
version number.
34. #LiveCodeGlobal
Loading a Packaged Extension
To load a packaged extension you use
the Extension Manager.
1. Open the Extension Manager
from the Tools menu.
2. Click Open(+).
3. Select the .lce file you want to
install.
The loaded extension appears in the list.
You can also use the Extension
Manager to uninstall extensions.
35. #LiveCodeGlobal
Including the library in a standalone
If you have created a LiveCode app
that uses a library and want to build
it into a standalone you must
ensure the library is included in the
standalone file.
Choose File -> Standalone
Application Settings.
On the General Pane you can
choose to let LiveCode search for
inclusions (libraries, widgets and
externals) or select any inclusions
manually.
36. #LiveCodeGlobal
Selecting inclusions
If you choose to select the
extensions to include the
Inclusions pane will be enabled.
Go to the Inclusions pane and
select the extensions you want to
include in the standalone.
37. #LiveCodeGlobal
Testing the standalone
Once your Standalone Settings are
complete choose File -> Save as
Standalone Application.
Start up the standalone and check
that the library is included and
returning the message.
41. #LiveCodeGlobal
Extending the Hello World library
In this lesson we will extend the minimal LiveCode Builder library we
created in Lesson 1.
We will
● Learn about LiveCode Builder types.
● Work with LiveCode Builder lists.
● Learn about type conversion between LCB and LCS.
● Pass a parameter to the SayHello handler.
● Document the library using documentation comments.
● Browse the library documentation in the Dictionary stack.
42. #LiveCodeGlobal
LiveCode Builder Typing
LiveCode Builder is a strongly, dynamically typed language,
although typing is completely optional in most places.
If a type is not specified it is taken to be the most general type
optional any (meaning any value, including nothing).
43. #LiveCodeGlobal
If a language is defined as typed it means that the types of all
variables are known or inferred at compile time.
Strongly typed
A strongly typed language does not allow you to use one type as
another. For example you cannot add a string and a number
together.
Type conversions have to be performed explicitly when required.
Weakly typed
A weakly typed language allows types to be mixed in the same
expression, by making implicit type conversions.
What is a strongly typed language?
44. #LiveCodeGlobal
What is a dynamically typed language?
Dynamically typed
Dynamically typed programming languages do type checking at
run-time as opposed to compile-time.
Statically typed
Statically typed programming languages do type checking (the
process of verifying and enforcing the constraints of types) at
compile-time as opposed to run-time.
LiveCode Builder is a dynamically typed language. Any typing errors
are found at runtime, not at compile time.
45. #LiveCodeGlobal
LiveCode Builder Types
The range of core types is relatively small, comprising the following:
● Boolean: one of true or false
● Integer: any integral numeric value
● Real: any numeric value
● Number: any integer or real value
● String: a sequence of UTF-16 code units
● Data: a sequence of bytes
● List: a sequence of any values
● Array: a mapping from strings to values
● any: a value of any type
Additionally, all types can be annotated with optional. An optional annotation
means the value may be the original type or nothing.
There is one additional type, nothing, meaning no value.
46. #LiveCodeGlobal
To demonstrate typing in LiveCode
Builder we will create a very simple
addition library.
Example 1: Adding 2 numbers together
In this example we create 2 number
variables and add them together,
resulting in another number.
● The return type of the additionTest
handler is defined as Number.
● The 2 variables tLeft and tRight
are defined as Number.
● Adding two numbers together
results in a number value which can
be successfully returned.
Addition library library community.livecode.elanorb.addition
metadata version is "1.0.0"
metadata author is "Elanor Buchanan"
metadata title is "Addition Library"
public handler AdditionTest() returns Number
variable tLeft as Number
variable tRight as Number
put 4 into tLeft
put 5 into tRight
return tLeft + tRight
end handler
end library
47. #LiveCodeGlobal
Test the library using the Extension
Builder.
1. Open the Extension Builder from
the Tools menu.
2. Load the addition.lcb file.
3. Click the Test button
Execute
put additionTest()
in the Message Box .
The correct value “9” is displayed in the
Message Box.
Testing the Addition library
48. #LiveCodeGlobal
Example 2: Adding a string and a
number
In this example we try to add a String and
a Number and return a number.
● The return type of the AdditionTest
handler is defined as Number.
● Variable tLeft is defined as a
String.
● Variable tRight is defined as a
Number.
● Attempting to add the 2 values
together will return an error.
Addition library 2 library community.livecode.elanorb.addition
metadata version is "1.0.0"
metadata author is "Elanor Buchanan"
metadata title is "Addition Library"
public handler AdditionTest() returns Number
variable tLeft as String
variable tRight as Number
put 4 into tLeft
put 5 into tRight
return tLeft + tRight
end handler
end library
49. #LiveCodeGlobal
Testing the Addition library
Test the library using the Extension Builder
● Open the Extension Builder from the
Tools menu.
● Load the addition.lcb file.
● Click the Test button
No errors are returned at compile time but if you
execute
put AdditionTest()
In the Message Box you will see an error.
This is because LBC is strongly typed (you can’t
add an string to a number) and dynamically
typed (the error occurs at runtime).
50. #LiveCodeGlobal
Addition library 3
Example 3: Adding a string and a
number using type conversion
In this example we create a String and a
Number variable, convert the String to a
Number and return a Number.
● The return type of the AdditionTest
handler is defined as Number
● Variable tLeft is defined as a
String
● Variable tRight is defined as a
Number
● The value in tLeft is converted to a
number by parsing the value as a
number
● The value of the two numbers
added together is returned
library community.livecode.elanorb.addition
metadata version is "1.0.0"
metadata author is "Elanor Buchanan"
metadata title is "Addition Library"
public handler AdditionTest() returns Number
variable tLeft as String
variable tRight as Number
put 4 into tLeft
put 5 into tRight
return tLeft parsed as number + tRight
end handler
end library
51. #LiveCodeGlobal
Test the library using the Extension Builder.
● Open the Extension Builder from the
Tools menu.
● Load the addition.lcb file.
● Click the Test button.
Execute
put AdditionTest()
In the Message Box .
The correct value “9” is displayed in the
Message Box. The value in the string variable
was converted to a number before the
calculation was performed, allowing a number
to be calculated and returned.
Testing the Addition library
52. #LiveCodeGlobal
LiveCode Builder Lists and Arrays
Two important types in LCB, which deserve some more attention
are lists and arrays.
● list - a list is a sequence of values, each element of the
sequence is assigned a numerical index, starting with 1 and
proceeding sequentially
● array - an array is a mapping from a string to any value (i.e. an
associative array, just like in LiveCode Script)
53. #LiveCodeGlobal
Creating Lists in LCB library community.livecode.elanorb.example
metadata title is "Example Library"
metadata author is "Elanor Buchanan"
metadata version is "1.0.0"
public handler CreateList() returns nothing
variable tLetterList as List
variable tNumberList as List
variable tMixedList as List
put ["a","b","c"] into tLetterList
put [1,2,3] into tNumberList
put ["one","two","three",4,5,6] into
tMixedList
end handler
end library
Creating a list in LiveCode
Builder is very simple, you put a
comma-separated list of values
between square brackets as
shown.
Each element in a list can hold
a different type of value.
54. #LiveCodeGlobal
Creating Lists in LCB
public handler CreateListFromString() returns
String
variable tVar as String
variable tSplit as List
put "first,second,third,fourth,fifth" into
tVar
split tVar by "," into tSplit
end handler
You can also split a string into a
list using the split keyword.
You specify the delimiter you
want to use and the string is
split into a list of strings using
the delimiter.
55. #LiveCodeGlobal
List elements
You can access the individual elements
of a list using the element keyword.
You can
● Get the value of an element
● Set the value of an element
● Delete an element
To add an element to a list you push a
value onto the list, you can push a value
onto the front or end of a list.
Note: LCB lists are 1 based.
Note: An error is returned if the index is
out of range.
public handler ListElement() returns any
variable tMixedList as List
put ["one","two","three",4,5,6] into
tMixedList
delete element 2 of tMixedList
-- tMixedList = ["one","three",4,5,6]
put "five" into element 4 of tMixedList
-- tMixedList = ["one","three",4,"five",6]
push "zero" onto front of tMixedList
--tMixedList =
["zero","one","three",4,"five",6]
end handler
56. #LiveCodeGlobal
Creating Arrays in LCB
Arrays in LCB are created in
the same way as in LiveCode
Script, so you are likely familiar
with the syntax.
You specify an element of an
array variable by using the
variable name along with the
element's key. You enclose the
key in square brackets. In LCB
array keys are always strings.
public handler CreateArray() returns nothing
variable tCapitals as Array
put "Kabul" into tCapitals["Afghanistan"]
put "Tirana" into tCapitals["Albania"]
put "Algiers" into tCapitals["Algeria"]
put "Andorra la Vella" into
tCapitals["Andorra"]
put "Luanda" into tCapitals["Angola"]
end handler
57. #LiveCodeGlobal
Creating Arrays in LCB
You can also create LiveCode
Builder arrays using {} notation
with the syntax:
{Key 1:Value 1,Key 2:Value
2,...,Key n:Value n}
public handler CreateBracketedArray() returns
nothing
variable tCapitals as Array
put {"Afghanistan":"Kabul",
"Albania":"Tirana", "Algeria":"Algiers",
"Andorra":"Andorra la Vella",
"Angola":"Luanda"} into tCapitals
end handler
58. #LiveCodeGlobal
Array elements
You can access the individual
elements on array using
square bracket notation.
You can
● Get the value of an
element
● Set the value of an element
● Delete an element
public handler CreateArray() returns nothing
… previous array code
-- Add an element to the array
put "St. John" into tCapitals["Antigua and
Barbuda"]
-- Update an existing element of the array
put "St. John's" into tCapitals["Antigua
and Barbuda"]
-- Delete the specified element of the
array
delete tCapitals["Antigua and Barbuda"]
end handler
59. #LiveCodeGlobal
Nested elements
Both lists and arrays can be nested. This
means an element in a list can contain another
list, or an element in an array can contain
another array.
Elements of a nested list can be accessed by
using multiple element values. For example:
return element 2 of element 2 of
tListOfLetters
-- returns “B”
Elements of a nested array can be accessed by
using multiple sets of square brackets
containing the keys. For example:
return tCapitals["Spain"]["name"]
-- returns “Madrid”
public handler CreateNested() returns nothing
-- A nested list (list of lists)
variable tListOfLetters as List
put ["a", ["b","B"]] into tListOfLetters
-- A nested array
variable tCapitals as Array
variable tSpain as Array
put "Madrid" into tSpain["name"]
put "3,165,000" into tSpain["population"]
put tSpain into tCapitals["Spain"]
end handler
60. #LiveCodeGlobal
You can also create nested arrays directly using the syntax
{Key 1:Value 1,Key 2:Value 2,...,Key n:Value n}
Where each value can also be an array expression
{Key 1:{Key a:Value a,Key b:{Key α:Value α,Key β:Value β}},Key 2:Value 2,...,Key n:Value n}
Example Returns
public handler CreateNested() returns nothing
variable tCapitals as Array
put {"Spain": {"Name":"Madrid",
"Population":"3,165,000"},
"UK":{"Name":"London",
"Population":"8,539,000"}} into tCapitals
end handler
Nested elements 2
61. #LiveCodeGlobal
Type Conversion between LCB and LCS
When a value is returned to LiveCode from a LiveCode Builder library what
type will it be?
Because LCB is strongly typed and LiveCode Script is weakly typed for most
LCB types we don’t need to worry about it.
● nothing
● Boolean
● Integer
● Real
● Number
● String
● Data
All these types can be used directly in expressions or put into a LiveCode
Script variable.
62. #LiveCodeGlobal
Type Conversion between LCB and LCS
The two exceptions are LiveCode Builder lists and arrays.
● list: when a LiveCode Builder handler returns a list it is converted to a numerically keyed
LiveCode array, with continuous numerical keys.
● array: when a LiveCode Builder handler returns an array it is converted to a LiveCode array
LiveCode Builder LiveCode Script
variable tLetterList
put ["a","b","c"] into tLetterList
return tLetterList
LiveCode Builder LiveCode Script
variable tSpain
put "Madrid" into tSpain["name"]
put "3,165,000" into tSpain["population"]
return tSpain
63. #LiveCodeGlobal
Adding explicit types to the library
In the first version of the Hello World Library we didn’t use any
explicit types. In this lesson we will update the library with:
● An explicit return type
● A typed variable that will hold the message to be returned
64. #LiveCodeGlobal
Adding a return type
The first change we will make is to
define the return type of the
handler.
In the initial version of the library the
return type was not specified so
was taken to be the general type
optional any, meaning any value,
including nothing, could be
returned.
We want to ensure the SayHello
handler always returns a string so
change the handler definition to
specify String as the return type.
public handler sayHello() returns String
… handler code
end handler
65. #LiveCodeGlobal
Adding a typed variable
To ensure we return a String from the
SayHello handler we will also update the
handler code to store the message in a
String variable and return the value of
the variable.
Update the SayHello handler with:
1. String variable declaration.
2. Command to update the value of
the variable with the “Hello World!”
message.
3. Return statement returning the
value of the String variable.
public handler sayHello() returns String
variable tMessage as String
put "Hello World!" into tMessage
return tMessage
end handler
66. #LiveCodeGlobal
Test that your library compiles and
behaves correctly.
1. Open the Extension Builder.
2. Load the updated LiveCode Builder
file.
3. Click the Uninstall button to
uninstall the previous version.
4. Click the Test button.
5. Execute
put sayHello()
in the Message Box.
The string returned by the library is
displayed in the Message Box.
Testing the Extended Library
67. #LiveCodeGlobal
Passing parameters to a library handler
In many cases you will want to use parameters in your library handlers.
Handler definitions have the form
<HandlerType> handler <HandlerName>(<ParameterList>) [ returns <ReturnType> ]
end handlerName
The parameter list is a comma separated list of parameters, each of which
has the form
( 'in' | 'out' | 'inout' ) <ParameterName> [ 'as' <ParameterType>]
The type of parameter is optional, if no type is specified it is taken to be
optional any meaning it can be of any type.
68. #LiveCodeGlobal
Passing parameters to a library handler
The parameter list describes the parameters which can be passed to the
handler. Handlers must be called with the correct number of parameters.
An in parameter means that the value from the caller is copied to the
parameter variable in the callee handler.
An out parameter means that no value is copied from the caller, and the
value on exit of the callee handler is copied back to the caller on return.
An inout parameter means that the value from the caller is copied to the
parameter variable in the callee handler on entry, and copied back out again
on exit.
The type of parameter is optional, if no type is specified it is taken to be
optional any meaning it can be of any type.
69. #LiveCodeGlobal
Adding a parameter to the sayHello handler
We want to update the SayHello handler to take a pName
parameter, passed in when SayHello is called from LiveCode Script.
The pName parameter will be a String value, which we will use to
construct and return a custom “Hello” string.
70. #LiveCodeGlobal
Adding a parameter
We will
● Update the handler definition with
an in String parameter, pName.
● Construct the string to be returned
using the pName parameter.
● Return the constructed string.
Constructing a string in LCB uses the
same syntax as LCS
● & - concatenates 2 strings
● && - concatenates 2 strings with a
space in between
public handler SayHello(in pName as String)
returns String
variable tMessage as String
put "Hello" && pName & "!" into tMessage
return tMessage
end handler
71. #LiveCodeGlobal
Test that your library compiles and
behaves correctly.
1. Open the Extension Builder.
2. Load the updated LCB file.
3. Click the Test button.
4. Execute
put SayHello(“Elanor”)
in the Message Box.
The returned String, containing the
passed String, is displayed in the
Message Box.
Testing the parameter passing
72. #LiveCodeGlobal
Using lists in the library
It will often be useful to return more that a single piece of data from
a library handler.
Next we will update the SayHello handler to take a string containing
a comma separated list of names, and return a LCB list with an
element containing a “Hello” message for each name passed in.
73. #LiveCodeGlobal
Updating the return type
The first step is to update the
SayHello handler definition.
We will be returning a List rather
than a String so update the return
type.
We also want to be able to pass
more than one name in so update
the parameter name to pNames.
public handler sayHello(in pNames as String)
returns List
… widget code
end handler
74. #LiveCodeGlobal
Defining variables
We will be using 3 variables in the
handler
● tNameList - a list variable created
from the pNames parameter
● tMessage - the list variable that will
be returned, with an element
holding a message for each name
passed in in pNames
● tElement - a string variable used to
iterate over the elements of
tNameList
Add variable definitions for these 3
variables.
public handler sayHello(in pNames as String)
returns List
variable tNameList as List
variable tMessage as List
variable tElement as String
end handler
75. #LiveCodeGlobal
Creating a List from a String
We want to convert the String
parameter into a List. We can do
this using the split statement, which
splits the string into a list of strings,
using the specified delimiter.
We know the pNames parameter is
a command separated list of names
so we split pNames by comma to
create a List variable.
public handler sayHello(in pNames as String)
returns List
variable tNameList as List
variable tMessage as List
variable tElement as String
split pNames by "," into tNameList
end handler
76. #LiveCodeGlobal
Building a List
Next we want to create a list of
messages, with an element for each
name.
● Loop over each element in
tNameList
● Construct a “Hello” message using
the current element
● Append it to the end of the list
using the push statement.
● Return the list of messages
Note: When a value is pushed onto a list
the pushed value becomes the tail of the
list, by default. Use the 'front of' variant to
push onto the front of a list instead.
public handler sayHello(in pNames as String)
returns List
variable tNameList as List
variable tMessage as List
variable tElement as String
split pNames by "," into tNameList
repeat for each element tElement in
tNameList
push ("Hello" && tElement & "!") onto
tMessage
end repeat
return tMessage
end handler
77. #LiveCodeGlobal
Compile the library using the Extension Builder.
● Open the Extension Builder from the
Tools menu.
● Load the helloWorldLibary.lcb file
● Click Test.
● This will load the library and create a test
stack.
● Add a button to the test stack.
● Set the code of the button to
on mouseUp
put sayHello("Adam,Brenda,Craig") into
tArray
end mouseUp
● Add a breakpoint to line 3 line so you can
view the array in the Variable Watcher
Testing the library
Note: Remember that LCB lists are converted to numerically keyed arrays in LiveCode Script.
78. #LiveCodeGlobal
Documenting the Library
Extensions can provide an API (Dictionary) and User Guide as part
of the installed package. They are installed and viewable through
the LiveCode Dictionary stack.
Any extension can include an API. To do so, either add a file called
api.lcdoc to your widget folder alongside the other widget files or
markup your source code inline. The api.lcdoc file must be in the
lcdoc format. For a full description of the lcdoc format see the
Contributing to LiveCode Documentation guide.
79. #LiveCodeGlobal
Marking up your code
In this example we will document the
library using inline code comments.
Marking up your scripts is simple and
follows a similar model to other
documentation formats.
Consider the following handler:
public handler myHandler(in pString as
String, in pNumber as Number)
end handler
To add an entry to the API for this
handler, place a formatted comment
above the handler definition:
/**
summary: Use this handler to do an action
pString: This parameter does x
pNumber: This parameter does y
description:
# Markdown Title
Here is a full description in markdown for how
this function works. Once again, any GitHub
flavoured markdown is accepted.
**/
public handler MyHandler(in pString as String,
in pNumber as Number)
end handler
80. #LiveCodeGlobal
Documenting SayHello
We want to markup the source code of
the Hello World library by adding inline
documentation to the SayHello handler.
● The documentation is enclosed
within /** and **/
● Add a summary
● Add a description of the pNames
parameter
● Add a description of the return
value
● Add a full description of how the
handler works.
library community.livecode.elanorb.helloWorld
metadata version is "1.0.0"
metadata author is "Elanor Buchanan"
metadata title is "Hello World Library"
/**
Summary: Constructs and returns a list of Hello
messages
pNames: String containing a comma separated list
of names
Returns: List of Hello strings
Description:
Takes a comma separated String of names, converts
the String to a List, constructs a Hello message
for each name and returns the List of Hello
messages.
**/
public handler SayHello(in pNames as String)
returns List
… handler code
end handler
end library
81. #LiveCodeGlobal
Browse the Documentation in the Dictionary
Now we have added documentation
to the library we can view it in the
Dictionary Stack.
Extension documentation only
shows once the extension is fully
installed.
● Open the Extension Builder.
● Load the Hello World Library.
● Click Install.
82. #LiveCodeGlobal
Browse the Documentation in the Dictionary
Now open the Dictionary Stack and
go to the API tab.
Open the Drop Down list, you
should see “Hello World Library”, or
the title you gave your library, in the
list.
Select the library and you will see
the documentation you added to the
source file displayed.
83. #LiveCodeGlobal
Congratulations
You have completed this lesson, you can now
● Use explicit typing
● Perform type conversion
● Use LCB lists and arrays
● Pass parameters to library handlers
● Add documentation to libraries
86. #LiveCodeGlobal
The Rotated Text Widget
The second type of extension that that be created is a widget. A widget is a custom
control that is treated as an engine level element. Widgets appear in the Tools Palette
and can be added as controls to any LiveCode stack.
The Rotated Text Widget displays text and allows the user to set the rotation property
of the widget.
We will
● Define the widget.
● Define properties for the widget.
● Draw the widget using LCB canvas operations.
● Compile and test the widget.
● Install the widget into LiveCode.
● Add the widget to a stack.
● Include the widget in a standalone.
87. #LiveCodeGlobal
What is a Canvas?
A canvas is a container that holds various drawing elements (lines, shapes, text, frames
containing other elements, etc.). It takes its name from the canvas used in visual arts.
The main difference between a library and an extension is that a widget draws itself to
the canvas, providing a UI element for the extension.
Advantages
Widgets have a number of advantages over traditional custom controls, created using
LiveCode groups
● Once built the widget it is an atomic control.
● Widgets are more efficient.
● Widgets are not affected by engine messages.
● Widgets can be easily updated by updating the extension without having to
replace the controls on the stacks.
88. #LiveCodeGlobal
Creating a LiveCode Builder Widget
Just like libraries widgets are written
in a text editor.
Start by creating a plain text file in a
new directory and save it to disk
with the extension ".lcb".
I am naming my file
"rotatedText.lcb" but you can name
your file anything suitable.
Note: The extension builder
currently relies on there being only
one .lcb file in a given directory, this
is why we create a separate folder
for each new extension.
89. #LiveCodeGlobal
The first thing we need to do is declare the type
of extension we are writing.
We are writing a widget so we need to declare
the extension as such.
The widget declaration is followed by an
identifier. An extension identifier should be in
the form
community.livecode.<user name>.<widget name>
In my case my username is "elanorb" and I
have chosen to use "rotatedtext" as the name
part of the identifier.
For more on module naming see the Naming
section of the LiveCode Builder Style Guide,
accessible from the Dictionary stack or the
Additional Resources section of this course.
LCB widget definition
widget community.livecode.elanorb.rotatedtext
end widget
90. #LiveCodeGlobal
As with an LCB library, widgets also
require metadata.
The required metadata for widgets
is the same as for libraries
● title
● author
● version
You can also include an optional
description.
Add the widget metadata to the
definition.
Widget metadata
widget community.livecode.elanorb.rotatedtext
metadata title is "Rotated Text"
metadata author is "Elanor Buchanan"
metadata version is "1.0.0"
end widget
91. #LiveCodeGlobal
The LiveCode builder syntax is broken down into modules. Each module contains the syntax for a
particular part of LiveCode Builder.
You include the modules that provide the syntax for the features your extension requires.
There are 3 classes of module:
Type Description
Default These modules are part of LiveCode builder and are included by default. Their
syntax is always available to you as a LiveCode developer.
Optional These modules are created and distributed by LiveCode Ltd and must be imported
by the extension developer in order to make use of their syntax.
Custom These modules are created and distributed through the online portal and must be
imported by the extension developer in order to make use of their syntax.
For a full list of modules see the Importing Libraries section of the Extending LiveCode Guide and the
API entries for the individual modules.
The guide can be found under the Guides tab of the Dictionary or in the Additional Resources
section of the course.
LCB modules
92. #LiveCodeGlobal
As a general rule we recommend
importing all three optional modules
and the Widget Utilities library
whenever developing widgets.
Include the 3 modules and 1 library
using the use keyword, followed by
the module or library extension.
Including modules
widget community.livecode.elanorb.rotatedtext
use com.livecode.canvas
use com.livecode.widget
use com.livecode.engine
use com.livecode.library.widgetutils
metadata title is "Rotated Text"
metadata author is "Elanor Buchanan"
metadata version is "1.0.0"
end widget
93. #LiveCodeGlobal
Module level variables
A variable definition defines a module-scope variable. In a widget module, such variables are
per-widget (i.e. instance variables). In a library module, there is only a single instance (i.e. a
private global variable). Module-scope variables in LCB are similar to script local variables in
LiveCode Script.
The syntax for declaring variables is
variable <variableName> [as <type>]
The type specification for the variable is optional, if it is not specified the type of the variable is
optional any meaning that it can hold any value, including being nothing.
Variables whose type has a default value are initialized to that value at the point of definition.
For a list of default values see the Variables section of the LiveCode Builder Language
Reference Guide. The guide can be found under the Guides tab of the Dictionary or in the
Additional Resources section of the course.
Variables whose type do not have a default value will remain unassigned and it is a checked
runtime error to fetch from such variables until they are assigned a value.
94. #LiveCodeGlobal
Variable declarations
In this widget we will use one module
level variable to store the value of the
rotation property of the widget.
The value of the rotation property must
be a number so we define the type as
such.
The variable will be a module level
instance variable, so we prepend the
variable name with a lower case ‘m’ to
make it easily identifiable as such.
Add the variable definition to the widget
code.
widget community.livecode.elanorb.rotatedtext
use com.livecode.canvas
use com.livecode.widget
use com.livecode.engine
use com.livecode.library.widgetutils
metadata title is "Rotated Text"
metadata author is "Elanor Buchanan"
metadata version is "1.0.0"
variable mRotation as Number
end widget
95. #LiveCodeGlobal
Defining widget properties
Property definitions can only appear in widget modules. They define a property which can
be accessed from LiveCode Script in the usual way (e.g. the myProperty of widget 1).
The syntax for declaring properties is
property <propertyName> get <getterIdentifier> [ set <setterIdentifier> ]
The getterIdentifier and setterIdentifier can use either a variable or handler identifier. If a
variable identifier is used, then the property value is fetched (and stored) from that variable.
If a handler identifier is used then a handler is called instead.
A getter handler must take no arguments and return a value. A setter handler must take a
single argument and return no value.
The set clause is optional. If it is not present then the property is read-only.
Note: The Extension Builder will report an error if a setter or getter handler that is used in a
property definition is not defined in the module file.
96. #LiveCodeGlobal
The rotation property
widget community.livecode.elanorb.rotatedtext
use com.livecode.canvas
use com.livecode.widget
use com.livecode.engine
use com.livecode.library.widgetutils
metadata title is "Rotated Text"
metadata author is "Elanor Buchanan"
metadata version is "1.0.0"
private variable mRotation as Number
property "rotation" get mRotation set
setRotation
end widget
The widget will only have one property,
rotation. The value of the rotation property will
be stored in the mRotation variable.
The getter
The getter for the rotation property will return
the value of mRotation.
The setter
The setter for the rotation property will call a
handler, setRotation. Implementing the "setter"
ourselves provides us with a little more flexibility
and allows us to take multiple actions when the
property is set.
Add the property definition to the widget code.
97. #LiveCodeGlobal
widget community.livecode.elanorb.rotatedtext
… previous code
public handler setRotation(in pRotation as
Number) returns nothing
put pRotation into mRotation
redraw all
end handler
end widget
The setRotation handler allows us to take
multiple actions when the property is updated
● The in parameter is a number.
● The handler does not return a value.
1. Update mRotation with the new value.
2. Redraw the widget to reflect the property
change. We do this by calling "redraw
all".
Add the setRotation handler to the widget
code.
The setRotation handler
98. #LiveCodeGlobal
The Property Inspector
Any properties that are defined in the widget are automatically
shown in the Basic pane of the Property Inspector.
We will be looking at integrating properties into the Property
Inspector in more detail in the next lesson.
99. #LiveCodeGlobal
Widget Handlers
There are five core handlers that any widget developer should implement:
Handler Description
OnPaint Sent to your widget whenever LiveCode requires it to redraw.
The performance of your widget is tied primarily to this handler
and should be kept as efficient as possible.
OnCreate Sent to your widget when it is first created by LiveCode. This can
be used to initialise default data and where applicable, reduce
the burden for calculating constants etc in the OnPaint handler.
OnGeometryChanged Sent when the control is changed in size.
OnSave Sent when your widget is about to be destroyed and enables the
widget to save data set on the widget.
OnLoad Sent when your widget is created and enables the widget to
retrieve data saved on the widget.
100. #LiveCodeGlobal
The OnPaint handler
This widget is very simple so only
the OnPaint handler is required to
draw the widget.
The OnPaint handler will be a
public handler which takes no
parameters and does not return a
value.
Add the OnPaint definition to the
widget code.
public handler OnPaint()
end handler
101. #LiveCodeGlobal
The OnPaint handler
width
heightLiveCode
There are a number of steps to take
in the OnPaint handler. The first
step is to draw the unrotated text in
the widget.
1. Work out the size of the widget
by getting its width and height
2. Work out the rectangle of the
area we have to draw into
3. Draw the text at the center of
the available space
102. #LiveCodeGlobal
public handler OnPaint()
variable tText as String
variable tHeight as Number
variable tWidth as Number
put my width into tWidth
put my height into tHeight
put "LiveCode" into tText
variable tRectangle as Rectangle
put rectangle [0,0,tWidth,tHeight] into
tRectangle
fill text tText at center of tRectangle
on this canvas
end handler
Drawing the text
For the basic widget we will use
“LiveCode” as the text.
1. Declare variables for the text
to be displayed and the height
and width of the canvas.
2. Update the variables with the
relevant values.
3. Declare a rectangle variable.
4. Put the rectangle describing
the area of the canvas into the
rectangle variable.
5. Draw the text in the center of
the canvas.
103. #LiveCodeGlobal
my width: Returns the width of the widget.
my height:Returns the height of the widget.
rectangle: Specifies an area, consists of a list of 4 integers left, top, right, bottom
Example: put rectangle [0,0,tWidth,tHeight] into tRectangle
fill text at: Renders text on a canvas
Syntax: fill text mText at mAlignment of mRect on mCanvas
Example: fill text "Widget Label" at top left of rectangle [50,
100, 250, 200] on this canvas
For more on each of these keywords see the API entries in the LiveCode Dictionary.
Search for “com.livecode.canvas” in the LiveCode Builder API to see all the associated
API entries.
Drawing the text
104. #LiveCodeGlobal
Rotating text
To draw rotated text we
1. Rotate the canvas
2. Get the rectangle of the rotated canvas as a path
3. Rotate the path back
4. Get the rectangle of the un-rotated path
5. Draw the text within the un-rotated rectangle.
This draws the text horizontally, but relative to the position of
the canvas. Because the canvas is rotated the text also
appears rotated.
105. #LiveCodeGlobal
Key Concept: Rectangle Path
Syntax: rectangle path of mRect
Summary: Creates a new path.
Parameters: mRect: An expression which evaluates to a rectangle.
Example: // Create a rectangle path
variable tPath as Path
put rectangle path of rectangle [10,10,210,60]
into tPath
106. #LiveCodeGlobal
Key Concept: Bounding Box
Syntax: the bounding box of mPath
Summary: The bounding box of a path which is the smallest
rectangle that completely encloses mPath.
Parameters: mPath: An expression which evaluates to a path.
Example: // Create a circle path
variable tPath as Path
put circle path centered at point [100,100] with
radius 50 into tPath
// Get the bounds of the path
variable tBounds as Rectangle
put the bounding box of tPath into tBounds
107. #LiveCodeGlobal public handler OnPaint()
variable tText as String
variable tHeight as Real
variable tWidth as Real
put my width into tWidth
put my height into tHeight
put "LiveCode" into tText
rotate this canvas by mRotation
variable tRectangle as Rectangle
variable tPath as Path
put rectangle path of rectangle
[0,0,tWidth,tHeight] into tPath
rotate tPath by (mRotation * -1)
put the bounding box of tPath into
tRectangle
fill text tText at center of tRectangle
on this canvas
end handler
Drawing rotated text
1. Rotate the canvas by the value stored in
the mRotation variable.
2. Declare a path variable tPath.
3. Put the rectangle path of the canvas into
tPath.
4. Rotate the path by -mRotation, setting it
to a horizontal rectangle.
5. Update the line that sets tRectangle to
get the bounding box of the un-rotated
path and put it into the tRectangle
variable. This gives the rectangle that
completely encloses the un-rotated the
path in tPath.
6. Draw the text at the center of the
rectangle given by tRectangle.
The text in drawn in a horizontal rectangle, but
relative to the canvas, so appears drawn on an
angle.
108. #LiveCodeGlobal
Testing the widget
Now we have completed the
OnPaint handler we are ready
to test the widget.
This is the full code of the
widget, so far.
widget community.livecode.elanorb.rotatedtext
use com.livecode.canvas
use com.livecode.widget
use com.livecode.engine
use com.livecode.library.widgetutils
metadata title is "Rotated Text"
metadata author is "Elanor Buchanan"
metadata version is "1.0.0"
private variable mRotation as Number
property "rotation" get mRotation set
setRotation
public handler setRotation(in pRotation as
Number) returns nothing
put pRotation into mRotation
redraw all
end handler
… continued on next slide
109. #LiveCodeGlobal
Testing the widget
Now we have completed the
OnPaint handler we are ready
to test the widget.
This is the full code of the
widget, so far.
public handler OnPaint()
variable tText as String
variable tHeight as Real
variable tWidth as Real
put my width into tWidth
put my height into tHeight
put "LiveCode" into tText
rotate this canvas by mRotation
variable tRectangle as Rectangle
variable tPath as Path
put rectangle path of rectangle
[0,0,tWidth,tHeight] into tPath
rotate tPath by (mRotation * -1)
put rectangle [0,0,tWidth,tHeight] into
tRectangle
put the bounding box of tPath into
tRectangle
fill text tText at center of tRectangle
on this canvas
end handler
end widget
110. #LiveCodeGlobal
Testing the widget
Just like a library we compile,
test, package and install
widgets using the Extension
Builder.
1. Open the Extension Builder
from the Tools menu
2. Click the Open button
3. Load the ‘rotatedtext.lcb’
file
111. #LiveCodeGlobal
Testing the widget
To test the widget click the Test
button.
This will create a stack in the
IDE with a Rotated Test widget
on it.
1. Go into Edit mode.
2. Select the widget.
3. Open the Property
Inspector.
4. Set the Rotation property.
112. #LiveCodeGlobal
Widget icons
When a widget is installed it shows in the
Tools Palette.
To provide an icon for the widget you add
image files to the folder containing the
widget definition .lcb file.
Open the folder containing the LCB file
● Create a “support” folder
● Add 2 image files
○ Icon.png (20 x 20)
○ icon@extrahigh.png (40x40)
The icon files can be found under the
Resources tab of this lesson.
113. #LiveCodeGlobal
Installing a widget
To install a widget we use the Extension
Builder.
1. Open the Extension Builder from
the Tools Palette.
2. Select the Open button.
3. Load the ‘rotatedtext.lcb’ file.
4. Click Install.
The widget will be installed and will
appear in the Tools Palette.
114. #LiveCodeGlobal
Creating a widget
You can now add the widget to
a stack by dragging it out from
the Tools Palette.
1. Create a new stack.
2. Drag on a Rotated Text
Widget.
3. Go into Edit mode.
4. Select the widget.
5. Open the Property
Inspector for the widget.
6. Set the Rotation property
on the widget.
115. #LiveCodeGlobal
Including the Widget in a Standalone
When building a standalone that
includes a widget you need to
ensure that the widget is included
when the standalone is built.
Open the Standalone Application
Settings from the File menu.
On the General Pane you can
choose to let LiveCode search for
inclusions (libraries and widgets) or
select any inclusions manually.
116. #LiveCodeGlobal
Selecting inclusions
If you choose to select the
extensions to include yourself
the Inclusions pane will be
enabled.
Go to the Inclusions pane and
check the extensions you want
to include in the standalone.
In this case we want to include
the Rotated Text widget so
check it in the list.
117. #LiveCodeGlobal
Saving the standalone
Choose File -> Save as
Standalone Application to
save the stack as a standalone.
Start up the standalone and
check that the widget is
included and the rotation can
be set.
121. #LiveCodeGlobal
The Advanced Rotated Text Widget
In this lesson we will take the Rotated Text Widget to the next level.
We will
● Fully integrate widget properties into the Property Inspector
● Add color and font properties by shadowing standard LiveCode
properties
● Handle mouse events to make the widget clickable
● Add handlers to load and save widgets
● Add read only properties to a widget
● Document the widget
122. #LiveCodeGlobal
Widget properties
In Lesson 3 we defined the rotation
property of the widget
property "rotation" get
mRotation set setRotation
● Properties are shown in the
Basic pane of the Property
Inspector
● The name of the property is
shown as the label in the
Property Inspector
● A standard editor is shown for
the property
123. #LiveCodeGlobal
You can more fully define widget properties in the LCB source file using metadata. All
property metadata is optional. Property metadata definitions have the format
metadata <property name>.<metadata name> is <value>
You can define
● Property label
● Editor type
● Editor specific metadata
● Default value
● Property Inspector Section
● Property group
● User visible
● Read only
For more on property metadata see Property Definitions Read Me.
Widget properties
124. #LiveCodeGlobal
Property metadata
Add the metadata for the rotation property to
the widget source file.
● label: the label to be shown in the
Property Inspector
● editor: the type of editor to be used in
the Property Inspector
● default: the default value of the property
Editor specific metadata
● step: the step value for number
properties
● min: the minimum value for a number
property
● max: the maximum value for a number
property
We want this property to appear in the Basic
section so do not specify a section.
widget community.livecode.elanorb.rotatedtext
… widget inclusions and metadata
private variable mRotation as Number
property "rotation" get mRotation set
setRotation
metadata rotation.label is "Rotation"
metadata rotation.editor is
"com.livecode.pi.number"
metadata rotation.default is "0"
metadata rotation.step is "1"
metadata rotation.min is "0"
metadata rotation.max is "359"
… widget handlers
end widget
125. #LiveCodeGlobal
Test in the Property Inspector
To see the property integrated into the
Property Inspector, using the metadata,
rebuild the widget.
● Open the Extension Builder from
the Tools menu
● Load the widget source file
● Click the Test button
● Select the widget on the test stack
● Open the Property Inspector
You should see the rotation property,
with a number property editor that will not
allow values outside the range 0-360.
126. #LiveCodeGlobal
The Content property
We will add a second property,
content, which allows the user to
change the text displayed in the
widget.
● Define a variable to store the
property value
● Define the property, getter and
setter
● Add metadata for the property
○ editor
○ default
○ label
widget community.livecode.elanorb.rotatedtext
… widget inclusions and metatdata
… rotation property definition
private variable mContent as String
property "content" get mContent set setContent
metadata content.editor is "com.livecode.pi.string"
metadata content.default is "Default text"
metadata content.label is "Content"
… widget handlers
end widget
127. #LiveCodeGlobal
Add the content property setter
handler.
● Takes a string parameter
● Has no return value
● Update the mContent variable
with the parameter value
● Redraw the widget with the
new text
The setContent handler widget community.livecode.elanorb.rotatedtext
… widget inclusions and metatdata
… property definitions
… OnPaint handler
… SetRotation handler
public handler SetContent(in pText as String)
returns nothing
put pText into mContent
redraw all
end handler
end widget
128. #LiveCodeGlobal
Update OnPaint
The OnPaint() handler needs
updated to use the value of the
content property.
● Delete the declaration for the
tText variable
● Delete the line where
“LiveCode” is put into the tText
variable
● Update the “fill text” line to use
the mContent variable
public handler OnPaint()
variable tText as String
variable tHeight as Real
variable tWidth as Real
put my width into tWidth
put my height into tHeight
put "LiveCode" into tText
rotate this canvas by mRotation
variable tRectangle as Rectangle
variable tPath as Path
put rectangle path of rectangle
[0,0,tWidth,tHeight] into tPath
rotate tPath by (mRotation * -1)
put the bounding box of tPath into tRectangle
fill text tText at center of tRectangle on this
canvas
fill text mContent at center of tRectangle on
this canvas
end handler
129. #LiveCodeGlobal
When a widget is created the default
values for its properties should be used.
The OnCreate handler is called when the
widget is first created.
To ensure the values of the rotation and
content properties are set we add the
OnCreate handler which will call the
property setters with the default values.
● Set the default value of the
content property to “Default text”
● Set the default value of the
rotation property to 0
The OnCreate handler widget community.livecode.elanorb.rotatedtext
… widget inclusions and metadata
… property definitions
… OnPaint handler
public handler OnCreate()
setContent("Default text")
setRotation(0)
end handler
… Property setters
end widget
130. #LiveCodeGlobal
Test the widget
Now build and test your widget
using the Extension Builder.
You should see the Content
property in the Property
Inspector and be able to
change the text displayed in the
widget.
131. #LiveCodeGlobal
In addition to adding widget specific properties you can also link your widget to standard LiveCode
control properties, such as font, background color etc.
All controls in LiveCode, both widgets and classic controls, have a set of basic properties which are
always present and do not need to be implemented when writing a widget. Any widget you create will
always have these properties in its Property Inspector. These properties are
You can also choose to allow your widget to implement any other LiveCode control properties which
are not in the basic set.
Basic name, kind, tooltip, visible, disabled
Custom properties custom properties and custom property sets
Colors ink, blendLevel
Position lockLoc, width, height, location, left, top, right, bottom, layer
Text textFont, textSize
Advanced layerMode, behavior, traversalOn, number
Shadowing LiveCode control properties
132. #LiveCodeGlobal
Accessing standard properties
Although all controls, including
widgets, have all the basic
properties some work
automatically and you need to
take some into account when
you draw the widget.
For example the width property
works automatically but the text
size requires some extra
handling.
133. #LiveCodeGlobal
Text properties
The two basic text properties all controls
have are
● Text font
● Text size
We want to use the values of these
properties when drawing the widget. We
get the effective text properties of the
widget using my font and apply these
settings when drawing the widget.
Update the OnPaint handler to apply
these settings to the canvas before
rotating the canvas.
public handler OnPaint()
… variable declarations
… tWidth and tHeight values
set the font of this canvas to my font
rotate this canvas by mRotation
… remainder of code
end handler
134. #LiveCodeGlobal
Test the text properties
To test the text properties in the Property
Inspector re-compile the widget using the
Extension Builder.
● Select the widget on the test stack
● Open the Property Inspector
● Go to the Text section
● Set the Font and Text size
The text in the widget should change to
reflect the property values.
135. #LiveCodeGlobal
Linking to other LiveCode properties
You can also link to other standard LiveCode properties beyond the
basic, built in set.
For example we might want to set the Text fill of the Rotated Text
Widget.
Text fill, or foregroundColor to use the property name, is a
standard LiveCode property but it not part of the basic set.
To access it we have to specify that the widget has the property, but
we do not have to define the property itself.
136. #LiveCodeGlobal
Linking to other LiveCode properties
Below is a list of the LiveCode properties you can link to as described.
abbrevId, abbrevName, abbrevOwner, altId, backColor, backPattern,
backPixel, blendLevel, borderColor, borderPattern, borderPixel, bottom,
bottomColor, bottomLeft, bottomPattern, bottomPixel, bottomRight,
brushColor, cantSelect, colors, customKeys, customProperties,
customPropertySet, customPropertySets, disabled, enabled, focusColor,
focusPattern, focusPixel, foreColor, forePattern, forePixel, height, hiliteColor,
hilitePattern, hilitePixel, ink, invisible, kind, layer, layerMode, left, location,
lockLocation, longId, longName, longOwner, name, number, owner,
parentScript, patterns, penColor, properties, rectangle, right, script, selected,
shadowColor, shadowPattern, shadowPixel, shortId, shortName,
shortOwner, textFont, textSize, textStyle, themeControlType, toolTip, top,
topColor, topLeft, topPattern, topPixel, topRight, traversalOn,
unicodeToolTip, visible, width
137. #LiveCodeGlobal
Metadata for LiveCode
properties
To define a LiveCode property in a widget you
provide metadata, but do not need to define the
property within the widget.
We want to the Rotated Text Widget to have the
foregroundColor property, and for it to be
settable in the Property Inspector.
Add the metadata for the property.
● Use a color editor in the Property
Inspector
● Setting the default to empty means the
value will be inherited
● Show in the Colors section of the
Property Inspector
● Set the label to “Text Fill”
widget community.livecode.elanorb.rotatedtext
… widget inclusions and metadata
… property definitions for rotation and
content
metadata foregroundColor.editor is
"com.livecode.pi.color"
metadata foregroundColor.default is ""
metadata foregroundColor.section is "Colors"
metadata foregroundColor.label is "Text fill"
… widget handlers
end widget
138. #LiveCodeGlobal
Update OnPaint
We now need to update the OnPaint
handler to use the selected
foregroundColor when drawing the text
to the canvas.
● my foreground paint - returns the
effective foreground color of the
widget. This value is either
inherited, set in code or set in the
Property Inspector
● set the paint of this canvas - sets
the color to be used when drawing
to the canvas
public handler OnPaint()
… variable declarations
set the font of this canvas to my font
set the paint of this canvas to my
foreground paint
… code continues
end handler
139. #LiveCodeGlobal
Test the Text Fill property
Compile and test the widget using the
Extension Builder.
● Go into Edit mode
● Select the widget in the test stack
● Open the Property Inspector
● Go to the Colors pane
● Change the Text fill
140. #LiveCodeGlobal
Handling mouse events
You will often want to create widgets that can respond to user actions.
LCB provides a range of messages, similar to LiveCode event messages, some
examples are
● OnMouseDown
● OnMouseUp
● OnDoubleClick
● OnKeyPress
You can choose which of these messages your widget will handle and how to handle
them. Note that the event names are all one word, e.g. OnMouseUp, unlike LCS.
You can can also post messages to the widget object in LiveCode, these can be
standard messages such as mouseUp or customised messages, such as
segmentClicked in a Pie Chart widget.
As with widget handlers we use TitleCase for event names.
141. #LiveCodeGlobal
Handling mouseUp
When the widget is clicked we want it to
get a mouseUp message, just like a
button or graphic control.
To do this we handle the LCB
OnMouseUp message and post a
mouseUp message to the widget object.
Add the OnMouseUp handler to the
widget code.
Note: Properties and events are, by their
nature, always public as they define
things which are external to the widget.
widget community.livecode.elanorb.rotatedtext
… widget inclusions and metadata
… property definitions
… OnPaint and OnCreate handlers
… Property Setters
public handler OnMouseUp()
post "mouseUp"
end handler
end widget
142. #LiveCodeGlobal
Compile and test the widget using the
Extension Builder.
● Go into Edit mode
● Select the widget on the test stack
● Open the Code editor for the
widget
on mouseUp
answer "mouseUp received"
end mouseUp
● Go into Run mode
● Click the widget
● The widget will respond to the
mouse click
Testing mouseUp
143. #LiveCodeGlobal
You can post any message to the widget
object, not just LiveCode messages.
For example if you want the Rotated Text
Widget to receive a rotatedTextClicked
message instead of a mouseUp
message when it is clicked you would
post “rotatedTextClicked”.
public handler OnMouseUp()
post "rotatedTextClicked"
end handler
Custom messages
144. #LiveCodeGlobal
Creating, saving and loading widgets
In Lesson 3 we looked at the core handlers but so far we have only
implemented the OnPaint and OnCreate handlers. In this lesson we
will implement the OnSave and OnLoad handlers.
Handler Description
OnSave Sent when your widget is about to be destroyed
and enables the widget to save data set on the
widget.
OnLoad Sent when your widget is created and enables
the widget to retrieve data saved on the widget.
145. #LiveCodeGlobal
We want to be able to save and load the widget’s properties.
If the OnSave and OnLoad handlers are not included then every time you
open a saved stack that includes a widget, the widget will lose its property
data and the properties will be set to the default values.
When your widget is saved you are sent an OnSave message. It returns an
array which you can fill with whatever widget data you have. LiveCode saves
this data along with instances of the widget in the stack file.
This same array will be returned to you as a parameter to the the OnLoad
message, which is sent when the widget is next opened and can be used to
recreate the widget with its saved properties.
Saving and loading the widget state
146. #LiveCodeGlobal
The OnSave handler
First we write the OnSave handler, which saves
the widget’s properties when a stack containing
a widget is saved.
Add the OnSave handler to the widget code.
The rProperties array is an out parameter
meaning the value of the array is copied back
to the caller when the handler completes.
Add an element to the rProperties array for
each property
● key: property name
● value: property value
Note: LiveCode properties are automatically
saved so don’t need to be explicitly stored in the
array.
widget community.livecode.elanorb.rotatedtext
… widget inclusions and metadata
… property definitions
… OnPaint and OnCreate handlers
… Property Setters
… Event handlers
public handler OnSave(out rProperties as Array)
put mContent into rProperties["content"]
put mRotation into rProperties["rotation"]
end handler
end widget
147. #LiveCodeGlobal
The OnLoad handler
Next we write the OnLoad handler, which loads
the widget’s properties when a stack containing
a widget is opened:
Add the OnLoad handler to the widget code.
The array of saved properties is passed in as a
parameter, and can be used to set the values of
the private instance variables to the stored
property values.
● Set the value of the content property to
the value of the content element in the
properties array
● Set the value of the rotation property to
the value of the rotation element in the
properties array
widget community.livecode.elanorb.rotatedtext
… widget inclusions and metadata
… property definitions
… OnPaint and OnCreate handlers
… Property Setters
… Event handlers
… OnSave handler
public handler OnLoad(in pProperties as Array)
put pProperties["content"] into mContent
put pProperties["rotation"] into mRotation
end handler
end widget
148. #LiveCodeGlobal
Testing saving and loading a widget
In order to test saving and loading a widget we have
to install the widget.
● Open the Extension Builder from the Tools
Palette
● Load the lcb file
● Uninstall the previous widget, if necessary
● Click Install
● The widget will appear in the Tools Palette
● Create a new stack
● Drag on a Rotated Text Widget
● Set some properties
● Save the stack
● Close LiveCode
● Reopen the stack
The widget should appear with its properties set to
the saved state.
149. #LiveCodeGlobal
Read only properties
So far we have only looked at properties that can be set by the user.
In this lesson we will implement some read-only properties that depend
entirely on the widget state. These properties will be familiar to anyone
experienced with LiveCode Script.
● formattedWidth - Reports the width needed by an object to display its
full contents without scrolling. This property is read-only and cannot be
set.
● formattedHeight - Reports the height needed by an object to display its
full contents without scrolling. This property is read-only and cannot be
set.
We will implement these two properties for the Rotated Text widget by
calculating the space the text displayed in the widget requires and returning
these values.
150. #LiveCodeGlobal
Declare the properties
Firstly declare the properties in the
widget source code.
Add two instance variables to hold the
property values and add definitions for
the formattedWidth and
formattedHeight properties.
Because the properties are read only
they do not require setter identifiers. The
getter simply returns the value of the
variable.
widget community.livecode.elanorb.rotatedtext
… widget inclusions and metadata
… existing property definitions
private variable mFormattedWidth as Real
private variable mFormattedHeight as Real
property "formattedWidth" get mFormattedWidth
property "formattedHeight" get mFormattedHeight
… OnPaint and OnCreate handlers
… Property Setters
… Event handlers
.... OnSave and OnLoad handlers
end widget
151. #LiveCodeGlobal
We want to update the values of the
formattedWidth and formattedHeight
properties whenever the state of the widget
changes. This ensures the correct value will
always be returned by the property.
Update the OnPaint handler to calculate and
store the formattedWidth and
formattedHeight of the widget whenever it is
redrawn.
● Declare a rectangle variable
● Get the bounds of the text on the
canvas. This returns the bounding box of
the text when drawn at point 0,0 as a
rectangle, taking into account the font,
style and text size of the canvas
● Update the variables holding the property
values with the width and height of the
bounding box of the text
Storing the values
public handler OnPaint()
… previous code
variable tBounds as Rectangle
put the bounds of text mContent on this
canvas into tBounds
put the width of tBounds into mFormattedWidth
put the height of tBounds into
mFormattedHeight
end handler
152. #LiveCodeGlobal
Testing read only properties
Compile and test the widget using the
Extension Builder.
● Go into Edit mode
● Select the widget in the test stack
● Open the Property Inspector
The formattedWidth and
formattedHeight properties will be
shown on the Basic pane, they can’t be
edited because the are read only.
You can check the width and height
properties in the Position pane and see
they are different values.
153. #LiveCodeGlobal
Hiding properties
There may be cases where your widget
has properties that you do not want to
show in the Property Inspector. This may
be because they are read only, internal
or undocumented.
You can set property metadata to
prevent the properties being visible to the
user.
Add user_visible metadata to the
formattedWidth and formattedHeight
properties
widget community.livecode.elanorb.rotatedtext
… widget inclusions and metadata
… existing property definitions
private variable mFormattedWidth as Real
private variable mFormattedHeight as Real
property "formattedWidth" get mFormattedWidth
metadata formattedWidth.user_visible is "false"
property "formattedHeight" get mFormattedHeight
metadata formattedHeight.user_visible is "false"
… OnPaint and OnCreate handlers
… Property Setters
… Event handlers
.... OnSave and OnLoad handlers
end widget
154. #LiveCodeGlobal
Testing non user-visible properties
Compile and test the widget using the
Extension Builder.
● Go into Edit mode
● Select the widget on the test stack
● Open the Property Inspector
The formattedWidth and
formattedHeight properties will not be
shown in the Property Inspector.
However you can get the property value
from code. Execute the following code in
the Message Box to test the property.
put the formattedHeight of widget 1
155. #LiveCodeGlobal
Documenting the widget
Extensions can provide an API (Dictionary) entry and User Guide as
part of the installed package. They are installed and viewable
through the LiveCode Dictionary stack.
In Lesson 2 we documented a library extension, which involved
documenting the handlers provided by the library.
To document the widget we will document the properties and
messages associated with the widget. As with the Hello World
library, we will use in-line documentation comments in the LCB file
to document the widget.
156. #LiveCodeGlobal
Documenting the widget
A description of the widget can be
included in a comment block above
the widget declaration in the LCB
file.
This description will appear in the
Dictionary stack under the API entry
for the widget identifier.
Add a comment block with a
description of the widget at the start
of the LCB file.
/**
A rotated text control. The control displays
text rotated to a given number of degrees, 0
degrees meaning unrotated.
**/
widget community.livecode.elanorb.rotatedText
… widget code
end widget
157. #LiveCodeGlobal
Documenting messages
Widget messages are not declared
explicitly in the LCB file, so all messages
should be documented within the
comment block that includes the widget
description at the start of the LCB file.
Each message has
● Name
● Type
● Syntax
● Description
Add the documentation for the mouseUp
message to LCB file.
You can add documentation for multiple
messages in this comment block.
/**
A rotated text control. The control displays
text rotated to a given number of degrees, 0
degrees meaning unrotated.
Name: mouseUp
Type: message
Syntax: on mouseUp
Description:
The <mouseUp> message is sent when the widget
is clicked.
**/
widget community.livecode.elanorb.rotatedText
… widget code
end widget
158. #LiveCodeGlobal
Documenting properties
When properties in LCB, the documentation is
included above the property definition within a
/** **/ comment block.
Include
● Syntax: the syntax for using the property.
This usually included a set and get
example
● Summary: a brief description of the
property
● Description: a more extensive description
of the property, its effects and how to use
it
Add in-line documentation for the declared
Rotated Text widget properties: rotation,
content, formattedWidth and
formattedHeight.
/**
Syntax: set the rotation of <widget> to
<pDegrees>
Syntax: get the rotation of <widget>
Summary: The number of degrees the text
displayed in the widget is rotated by.
Description:
Use the <rotation> property to set the amount
the text displayed in the widget is rotated
by. The default is 0 degrees which is
unrotated, horizontal text. The text is
rotated in a clockwise direction.
**/
property "rotation" get mRotation set
setRotation
159. #LiveCodeGlobal
Documenting Properties
The foregroundColor property shadows a
LiveCode property and is not declared explicitly
in the widget source code. Therefore the
documentation must be in the top-level
comment beside the message documentation.
Include
● Name: the property name
● Type: “property”
● Syntax: the syntax for using the property.
This usually included a set and get
example
● Summary: a brief description of the
property
● Description: a more extensive description
of the property, its effects and how to use
it
/**
...widget and message documentation
Name: foregroundColor
Type: property
Syntax: set the foregroundColor of <widget> to
<pColor>
Syntax: get the foregroundColor of <widget>
Summary: The text color of the widget
Description:
Use the <foregroundColor> property to control
the text color of the widget.
**/
widget community.livecode.elanorb.rotatedText
… widget code
end widget
160. #LiveCodeGlobal
Browsing the documentation
Use the Extension Builder to install the
widget. You can find the widget icons in
the Lesson 4 Resources.
You will see the Rotated Text widget in
the Tools Palette.
Open the Dictionary stack and select
Rotated Text from the drop down menu.
The documentation for the widget,
message and properties will be shown in
the Dictionary stack.
161. #LiveCodeGlobal
Congratulations
You have completed this lesson, you can now
● Fully integrate widget properties into the Property Inspector
● Shadow standard LiveCode properties
● Handle mouse events to make widgets clickable
● Save and load widget properties
● Add read only properties to a widget
● Document the widget
164. #LiveCodeGlobal
The Pie Chart Widget
In this lesson we will plan and implement a Pie Chart widget.
We will
● Decide how the Pie Chart should look
● Decide what properties the Pie Chart should have
● Decide what messages the Pie Chart should send
● Implement the Pie Chart widget in steps
○ Define the Properties
○ Implement OnCreate
○ Implement OnPaint
○ Implement OnGeometryChanged
○ Implement OnSave
○ Implement OnLoad
○ Respond to mouse events
● Document the widget
● Package and install the widget
165. #LiveCodeGlobal
Pie Chart Examples
Everyone is familiar with pie charts, but before we start
implementing the pie chart widget we’ll look at a couple of examples.
Google Docs Libre Office
166. #LiveCodeGlobal
The Pie Chart information
There are 2 main pieces of information
required to draw the pie chart
● List of values
● Labels associated with each value
In the examples we looked at, the values
and labels were:
Label Value
January 1
February 2
March 3
April 5
May 8
June 13
167. #LiveCodeGlobal
The Pie Chart layout
The usual layout for pie charts is to have
● Chart on the left
● List of labels on the right
Because the user can resize the widget there
are some considerations for us
● Ensure the labels are shown
● Make the pie chart the maximum size
while showing all the labels
● If the widget is taller than it is wide
display the labels below the chart
● Allow the user to choose whether to
show the labels or not, to provide
flexibility within apps
Aaa
Bbb
Ccc
Aaa
Bbb
Ccc
168. #LiveCodeGlobal
Pie Chart properties and messages
Through planning our widget we have identified 3 properties the widget needs
● List of values
● List of labels
● Boolean value specifying whether the labels should be shown or not
We also want the widgets to respond to user actions. We could simply provide a
mouseUp message but for a pie chart this is not particularly informative. Instead a
useful message would tell the user that the chart had been clicked, and which sector
was clicked on. So we will send a message with a parameter
● sectorClicked pSectorNumber
The colors used for sectors will be assigned by the widget. A possible extension of the
widget would be to make these colors settable by the user.
169. #LiveCodeGlobal
Creating the LiveCode Builder file
As with the Hello World Library and
Rotated Text Widget we will start a new
LiveCode Builder source file.
● Create a new directory.
● Create a new plain text file in the
directory and save it to disk with
the extension "lcb".
I am naming my file "pieChart.lcb" but
you can name your file anything suitable.
Note: The extension builder currently
relies on there being only one .lcb file in
a given directory, this is why we create a
separate folder for each new extension.
170. #LiveCodeGlobal
Widget definition
Begin by defining the pie chart widget in
the LCB file.
Remember
● The widget declaration is followed
by an identifier.
● An extension identifier should be in
the form
community.livecode.<user
name>.<widget name>
For more on module naming see the
Naming section of the LiveCode Builder
Style Guide, accessible from the
Dictionary stack or the Additional
Resources section of the course.
widget community.livecode.elanorb.piechart
end widget
171. #LiveCodeGlobal
Include the recommended modules and
libraries in the LCB file.
● com.livecode.canvas
● com.livecode.widget
● com.livecode.engine
● Com.livecode.library.widgetutils
For a full list of modules see the
Importing Libraries section of the
Extending LiveCode Guide and the API
entries for the individual modules.
Including modules
widget community.livecode.elanorb.piechart
use com.livecode.canvas
use com.livecode.widget
use com.livecode.engine
use com.livecode.library.widgetutils
end widget
172. #LiveCodeGlobal
Widget metadata
Add the required widget metadata to the
definition.
● Title
● Author
● Version number
You can also include an optional
metadata. We want to ensure the Pie
Chart widget is created at a reasonable
size so add
● Preferred size
widget community.livecode.elanorb.piechart
use com.livecode.canvas
use com.livecode.widget
use com.livecode.engine
use com.livecode.library.widgetutils
metadata title is "Pie Chart"
metadata author is "Elanor Buchanan"
metadata version is "1.0.0"
metadata preferredSize is "200,150"
end widget
173. #LiveCodeGlobal
Variable declarations
In Step 1 we identified 3 properties of the Pie
Chart widget: values, labels and whether labels
should be shown.
In addition we want an a list variable to store the
list of colors that will be used for the sectors.
As before we will use module level variables
to store the values of these properties.
Declare the 4 variables and their types.
Also add a constant, kPadding, which will be
used to layout the elements of the widget.
Note: See Lesson 3 - Step 3: Module level
variables if you need a refresher.
widget community.livecode.elanorb.piechart
… module imports
… metadata
private variable mValues as List
private variable mLabels as List
private variable mColors as List
private variable mShowLabels as Boolean
constant kPadding is 10
end widget
174. #LiveCodeGlobal
The values property will have both a
getter and a setter handler.
Add the definition for the property,
including:
● Property name
● Getter handler name
● Setter handler name
● Default value
● Property label
The values property
widget community.livecode.elanorb.piechart
… module imports
… metadata
… instance variable declarations
property "sectorValues" get getValues set
setValues
metadata sectorValues.default is
"1,2,3,5,8,13"
metadata sectorValues.label is "Values"
end widget
175. #LiveCodeGlobal
The segmentValues property has both a
getter and a setter property.
The Property Inspector allows the user to
set the segmentValues to a comma
separated list of values.
The setValues handler converts the
comma separated string to a LCB list.
The getValues handler converts the LCB
list to a string, allowing it to be displayed
in the Property Inspector.
Add the handlers to the source code.
The values property
widget community.livecode.elanorb.piechart
...previous code
public handler setValues(in pValues as String)
returns nothing
split pValues by "," into mValues
redraw all
end handler
private handler getValues() returns String
variable tValues
combine mValues with "," into tValues
return tValues
end handler
end widget
176. #LiveCodeGlobal
The labels property will also have both a
getter and a setter handler.
Add the definition for the property,
including:
● Property name
● Getter handler name
● Setter handler name
● Default value
● Property label
The labels property widget community.livecode.elanorb.piechart
… module imports
… metadata
… instance variable declarations
… previous property definitions
property "sectorLabels" get getLabels set
setLabels
metadata sectorLabels.default is
"Jan,Feb,Mar,Apr,May,Jun"
metadata sectorLabels.label is "Labels"
… code continues
end widget
177. #LiveCodeGlobal
Like the segmentValues property the
segmentLabels property has both a
getter and a setter property.
The Property Inspector allows the user to
set the segmentLabels to a comma
separated list of values.
The setLabels handler converts the
comma separated string to a LCB list.
The labels property widget community.livecode.elanorb.piechart
… previous code
public handler setLabels(in pLabels as String)
returns nothing
split pLabels by "," into mLabels
redraw all
end handler
private handler getLabels() returns String
variable tLabels
combine mLabels with "," into tLabels
return tLabels
end handler
end widget
178. #LiveCodeGlobal
The Show labels property is a boolean
value so only has a setter handler.
The value of the mShowLabels variable
is returned when the property value is
requested.
Add the definition for the property,
including:
● Property name
● Variable name for the get method
● Setter handler name
● Default value
● Property label
The showLabels property
widget community.livecode.elanorb.piechart
… module imports
… metadata
… instance variable declarations
… previous property definitions
property "showLabels" get mShowLabels set
setShowLabels
metadata showLabels.default is "true"
metadata showLabels.label is "Show labels"
… code continues
end widget
179. #LiveCodeGlobal
The setShowLabels handler sets the
value of the mShowLabels variable to the
value passed in.
Add the handler to the source code.
The showLabels property
widget community.livecode.elanorb.piechart
… previous code
public handler setShowLabels(in pShow as
Boolean) returns nothing
put pShow into mShowLabels
redraw all
end handler
end widget
180. #LiveCodeGlobal
The OnCreate handler
The OnCreate handler is sent to a widget when it is
first created by LiveCode.
This handler can be used to initialise default data
and, where applicable, reduce the burden for
calculating constants etc in the OnPaint handler.
Add the OnCreate handler to the LCB file
● Define a variable, tSectors, to hold the
number of sectors
● Call setValues to set the default values of
the mValues variable.
● Call setLabels to set the default values of
the mLabels variable
● Set the value of mShowLabels to true
● Update tSectors with the number of elements
in the list of values
● Call colorList to intitialise the mColors
variable
Setting the property values in the OnCreate handler
ensures the values of the properties are set when
we test from the Extension Builder.
widget community.livecode.elanorb.piechart
...previous code
public handler OnCreate()
variable tSectors
setValues("1,2,3,5,8,13")
setLabels("Jan,Feb,Mar,Apr,May,Jun")
put true into mShowLabels
put the number of elements in mValues into
tSectors
put colorList(tSectors) into mColors
end handler
end widget
181. #LiveCodeGlobal
LiveCode Builder Colors
LiveCode Builder colors are expressions which evaluates to a list of
3 or 4 numbers, the red, green, blue, and (optional) alpha
components of the color.
The component value denotes the intensity of that component,
expressed as a real number between 0 and 1. The alpha component
represents the opacity of the color. If the alpha component is not
specified then it is assumed to be 1 (fully opaque).
variable tColor
-- Set tColor to opaque red
put color [1.0, 0.0, 0.0] into tColor
-- Set tColor to partially transparent cyan
put color [0.0, 1.0, 1.0, 0.75] into tColor
182. #LiveCodeGlobal
The colorList handler Red: 1,0,0,1
Green: 0,1,0,1
Blue: 0,0,1,1
Yellow: 1,1,0,1
Purple: 1,0,1,1
Cyan: 0,1,1,1
Red 2: 0.5,0,0,1
Green 2: 0,0.5,0,1
Blue 2: 0,0,0.5,1
Yellow 2: 0.5,0.5,0,1
Purple 2: 0.5,0,0.5,1
Cyan 2: 0,0.5,0.5,1
The colorList handler returns a list of unique
colors for use when coloring the sectors of the
chart.
The number of colors to be returned is given by
the pNumber parameter.
The handler loops calculating RGB values for
colors.
● Highest intensity primary colors
● Highest intensity secondary colors
● Reduce the color intensity by half
Each iteration adds 6 colors to the list.
1. R,0,0,1
2. 0,G,0,1
3. 0,0,B,1
4. R,G,0,1
5. R,0,B,1
6. 0,G,B,1
183. #LiveCodeGlobal
The colorList handler
Add the colorList handler to the source
file.
● Declare the variables that will be
used.
● Assign a value to tColorLevel: the
color intensity to be used.
● Calculate the number of repeats
○ Divide pNumber by 6, 6
colors are add to the color list
on each iteration of the loop
○ Round the result
○ Add 1 to the result to ensure
we always calculate enough
colors
public handler colorList(in pNumber) returns
List
variable tColors as List
variable tColorLevel as Number
variable tRepeats as Number
put 1 into tColorLevel
put pNumber / 6 into tRepeats
round tRepeats
add 1 to tRepeats
… continues on next slide
end handler