Write a Widget in 8 Steps

by Georgia Hutchings on April 30, 2015 15 comments

Ever thought that your LiveCode project could be improved by a custom control? Perhaps you want to add a rounded rectangle push button to your stack, where the border color is different to the text color? Maybe you’re thinking of building an app with 10 cards, and each card needs a header bar, and each header bar needs a label, a button, a background graphic, a line graphic… Getting tired just thinking about it? Enter widgets.

Widgets are controls that can be dragged from the tools palette and dropped onto your stack. In LiveCode 8, users now have the ability to write their own custom controls, or widgets, in the new LiveCode Builder language. The purpose of this article is to guide you through the widget writing process in LiveCode Builder, from the very start to using your end product in a LiveCode stack.

We are going to follow 8 steps to write a widget in LiveCode Builder:

  1. Create a .lcb File
  2. Declarations
  3. Saving and Loading the Widget’s Properties
  4. Handlers to Set the Widget’s Properties
  5. Creating and Drawing the Widget
  6. Defining Custom Handlers
  7. Testing, Packaging and Installing the Widget
  8. Use the Widget in a LiveCode Stack

The widget we will be writing is the speech bubble, as featured in the first app on the CreateIt with LiveCode course (check out this video for more information on the course).

Disclaimer: The following is intended towards the more advanced user, although all are welcome to try writing widgets, and have a go using LiveCode Builder in general!

1. Create a .lcb File

First we need to select a text editor to write the code in. At the moment, LiveCode does not have a built in editor for .lcb files, but since they are plain text files they can be written in any text editor. I suggest using TextWrangler, which is available to download for free online. There is a custom LiveCode Builder color scheme which can be used in TextWrangler, which you can get here. Once you have downloaded the color scheme file, you need to put it in

~/Library/Application Support/BBEdit/Language Modules

or equivalent folder.

Next, create a folder “speechBubble” in which to save the .lcb file. When you build and package extensions, additional files that are associated with the extension are created. By saving the .lcb file in a folder, all additional files that are created will be saved in the same folder. Open TextWrangler, select the LiveCode Builder color scheme and save the file as “speechBubble.lcb” in the “speechBubble” folder. Now we can begin writing the widget!

screenshot 1

2. Declarations

The first thing we need to do is declare the type of extension we are writing. There are two types of extension in LiveCode Builder: widgets and libraries. Widgets are controls and libraries add new commands and functions to the engine. We are writing a widget and so we need to declare the extension as such.


-- declaring extension as widget, followed by identifier
widget community.livecode.gohutchings.speechBubble

The widget declaration is followed by an identifier. An extension identifier should be in the form

community.livecode..

where the username should be the username you use to log into the LiveCode extension store. Don’t have a username? Create one here.

In this widget, we have two dependencies: the com.livecode.canvas library, which specifies the syntax definitions and bindings for canvas drawing and operations in LiveCode Builder, and the com.livecode.widget library, which consists of the operations on widgets provided by LiveCode Builder.


use com.livecode.canvas
use com.livecode.widget

This widget has two properties: the text that will be displayed in the speech bubble and the color of the speech bubble. For these two properties, we only need to declare “set” handlers. The properties’ values will be stored in private instance variables, which we will need to declare later.


property bubbleText 	get mText 		set setText
property bubbleColor 	get mBubbleColor 	set setBubbleColor

In order to Package any extension in LiveCode Builder we need to add metadata which is required by the LiveCode store: the extension’s title, the author and a version number. If the required metadata is not added, then a number of warnings may be printed into the log field when building and packaging the extension.


metadata title is "Speech Bubble"
metadata author is "Georgia Hutchings"
metadata version is "1.0.0"

We also need to add some metadata for the properties declared above. For each property we need to set: the editor for the property in the property inspector of the widget; the default value that is displayed in the editor if the property has not been set yet.

For the property bubbleText, we want the property editor to be a string editor and the default value to be “Hello World”. For the property bubbleColor, we want to restrict the user to two options: blue and grey. To do this, we need to set the property editor to an enum editor. We also need to set the options available, as well as the default value.


metadata bubbleText.editor	is "com.livecode.pi.string"
metadata bubbleText.default	is "Hello World"

metadata bubbleColor.editor	is "com.livecode.pi.enum"
metadata bubbleColor.options	is "blue,grey"
metadata bubbleColor.default	is "blue"

Finally, we need to declare any private instance variables and constants that we are going to use. We have four variables in this widget: two that store the property values and two that store paints. There is also one constant that will be used when drawing the widget.


private variable mText as String
private variable mBubbleColor as String

private variable mBackgroundPaint as Paint
private variable mTextPaint as Paint

constant kBuffer is 10

Now that we have taken care of declarations, we can get on with writing the handlers!

3. Saving and Loading the Widget’s Properties

We want to be able to save and load the widget’s properties. If the following handlers are not included then every time you open a saved stack that includes a widget, the widget will lose it’s property data and the properties will be set to the default values.

First we write the OnSave() handler, which saves the widget’s properties when a stack containing a widget is saved:


public handler OnSave(out rProperties as Array)
	put the empty array into rProperties
	
	put mText into rProperties["bubbleText"]
	put mBubbleColor into rProperties["bubbleColor"]
	
end handler

Next we write the OnLoad() handler, which loads the widget’s properties when a stack containing a widget is opened:


public handler OnLoad(in pProperties as Array)
	
	put pProperties["bubbleText"] into mText
	put pProperties["bubbleColor"] into mBubbleColor
	
end handler

Next we will take care of the handler that set the widget’s properties.

4. Handlers to Set the Widget’s Properties

In the property declarations, we defined “set” handlers for each of the properties. These are called when the user sets a property in the property inspector. We will define the “set” handlers below. The set handlers are private to the widget and don’t return anything. For this widget, both set handlers take a string as a parameter (the string entered by the user into the property editor in the property inspector).

First we want to be able to set the bubbleText property. This handler is very straightforward as all we need to do is set the mText variable to the string entered by the user.


private handler setText(in pText as String)
	put pText into mText
end handler

Next we want to set the bubbleColor property. The background color and text color depend on this property, so we will also set the paint variables that we declared in step two when the bubbleColor property is changed. The reason for doing this is so we can speed up the drawing process, by taking as much calculation out of it as possible.


private handler setBubbleColor(in pColor as String)

	if pColor is "blue" then
		put solid paint with color [0,128/255,1] into mBackgroundPaint
		put solid paint with color [1,1,1] into mTextPaint
	else if pColor is "grey" then
		put solid paint with color [224/255,224/255,224/255] into mBackgroundPaint
		put solid paint with color [0,0,0] into mTextPaint
	end if

	put pColor into mBubbleColor
end handler

5. Creating and Drawing the Widget

Next we write the OnCreate() handler. This is called when the widget is first created, and here is where we initialise the private instance variables that we declared in step two. We will set the variables that store the property values to the default values. We will use the setBubbleColor() handler to set the mBubbleColor, mBackgroundPaint and mTextPaint variables in one.


public handler OnCreate()
	
	put "Hello World" into mText
	setBubbleColor(“blue”)
	
end handler

The OnPaint() handler is called whenever LiveCode needs to redraw the widget, and it is here that we write the code that draws the widget. First, we draw the speech bubble. The speech bubble is made up of two shapes: a rounded rectangle that forms the main body of the bubble and a crescent shape in the bottom corner. We start by setting the paint of the canvas, then we fill the paths of the two shapes. Next we draw the text. We need to change the paint of the canvas and set the font, then we can draw the text within a text box.


public handler OnPaint()
	
	-- draw the speech bubble
	set the paint of this canvas to fetchPaint("bubble")
	fill fetchPath("rounded rectangle") on this canvas
	fill fetchPath("crescent") on this canvas
	
	-- fill in the text
	set the paint of this canvas to fetchPaint("text")
	set the font of this canvas to font "Helvetica" at size 12
	fill text mText at top left of fetchTextBox() on this canvas
	
end handler

You will notice that the paint, the paths and the text box are all set from custom handlers. We will write these handlers in the next step.

6. Defining Custom Handlers

The fetchPaint() handler is private, it takes a string as a parameter and it returns a paint. Since the paints are stored in variables, all we need to do in this handler is return the correct variable based on the object.


private handler fetchPaint(in pObject as String) returns Paint
	
	if pObject is "bubble" then
		return mBackgroundPaint
	else if pObject is "text" then
		return mTextPaint
	end if
	
end handler

The fetchPath() handler is private, takes a string as a parameter and returns a Path. This handler draws the path for the main body of the speech bubble, a rounded rectangle, and a crescent shape that forms the flick at the bottom.

Typically, the blue speech bubble looks as though the speaker is to the right of the screen and vice versa for the grey speech bubble. We use the buffer constant as we do not want the rounded rectangle path to go all the way to the edge of the widget’s bounds on all sides. This is so that we have space for the crescent in either the bottom right (blue speech bubble) or bottom left (grey speech bubble) in the bounds of the widgets.

In order to draw the crescent shape we need to draw two curved lines. We do this by first creating a new empty path, then move to a point to begin a new subpath. Next we continue the path with a curve through a specified point to another specified point. Finally, we curve through another specified point to end up at the start point.


private handler fetchPath(in pObject as String) returns Path
	if pObject is "rounded rectangle" then
	
		variable tRect as Rectangle
	
		if mBubbleColor is "blue" then
			put rectangle [0,0,my width-kBuffer,my height-kBuffer] into tRect
		else if mBubbleColor is "grey" then
			put rectangle [kBuffer,0,my width,my height-kBuffer] into tRect
		end if
			
		return rounded rectangle path of tRect with radius 10

else if pObject is "crescent" then
		variable tCrescentPath as Path
		put the empty path into tCrescentPath
		
		if mBubbleColor is "blue" then
			move to point [my width-kBuffer,my height-2*kBuffer] on tCrescentPath
			curve through point [my width-kBuffer/2,my height-kBuffer] to point [my width,my height-kBuffer] on tCrescentPath
			curve through point [my width-kBuffer,my height-kBuffer/2] to point [my width-2*kBuffer,my height-2*kBuffer] on tCrescentPath
		else if mBubbleColor is "grey" then
			move to point [kBuffer,2*kBuffer] on tCrescentPath
			curve through point [kBuffer/2,my height-kBuffer] to point [0,my height-kBuffer] on tCrescentPath
			curve through point [kBuffer,my height-kBuffer/2] to point [2*kBuffer,my height-2*kBuffer] on tCrescentPath
		end if
		
		return tCrescentPath
	
	end if
end handler

The fetchTextBox() handler is private, takes no parameters and returns a Rectangle. Again, the position of the text box depends on the color of the speech bubble. We want the text box to fit nicely inside the main body of the speech bubble, with a ten pixel buffer between the edges of the text box and the edge of the rounded rectangle part of the speech bubble.


private handler fetchTextBox() returns Rectangle
	variable tRect as Rectangle
	
	if mBubbleColor is "blue" then
		put rectangle [kBuffer,kBuffer,my width-2*kBuffer,my height-2*kBuffer] into tRect
	else if mBubbleColor is "grey" then
		put rectangle [2*kBuffer,kBuffer,my width-kBuffer,my height-2*kBuffer] into tRect
	end if
	
	return tRect
end handler

That concludes the custom handlers. The final thing we need to do is add the following line to the end of our code:


end widget

That’s it for the code! So let’s test, package and install the widget.

7. Test, Package, Install

Open LiveCode 8 (download for latest release is available here), and open the extensionBuilder plugin.

screenshot 2

You will then need to select the folder icon in the top right hand corner and choose your speechBubble.lcb file.

screenshot 3

You should be able to see five buttons on the bottom right hand side of the plugin: Package; Uninstall; Install; Script and Test.

First we want to hit Test. This will compile and load the widget, and a new stack should pop up containing the widget. Any compile errors will be displayed in the log field in the plugin. Providing the widget has compiled successfully we can Package and Install the widget.

To package the widget, you will need to have an icon. Pressing the Package button will ask you to select an icon, and once the icon has been selected an extension package will be produced. You will find this in the “speechBubble” folder. The file name will be the widget identifier that you set in step two after declaring the extension as widget, and the file extension will be .lci.

Finally, pressing Install will install the widget in the “Extensions” directory of your “My LiveCode” folder. It will now be available to you whenever you run the LiveCode IDE.

8. Use the Widget in a LiveCode Stack

Having successfully installed your widget, you can now use it in a LiveCode stack. In the IDE, open a new mainstack and then all you need to do is drag out your widget from the tools palette. In the style of Blue Peter, here is one I made earlier:

screenshot 4

Georgia HutchingsWrite a Widget in 8 Steps

Related Posts

Take a look at these posts

15 comments

Join the conversation
  • Richmond - April 30, 2015 reply

    This article is really good.

    However, I wouldn’t want to ruin my reputation by not having a “however”: I note you recommend TextWrangler – which is a super bit of software IFF you are working on a Mac: but very many people aren’t, so let me recommend jEdit: http://jedit.org/

    Georgia - April 30, 2015 reply

    Hi Richmond,

    Thanks! My main reason for recommending TextWrangler is because we have a LiveCode Builder colour scheme that can be used in TextWrangler, but of course users are free to use whatever text editor they feel most comfortable with.

    Georgia

    Peter - May 4, 2015 reply

    For cross-platform work, I have also recently published a LiveCode Builder editing mode for the Atom text editor: https://atom.io/

    To add LiveCode Builder syntax highlighting and indenting, just install the “language-livecode” package from Atom’s settings dialog.

    Richmond - May 4, 2015 reply

    That’s really marvellous: installing Atom as I write. Thank you so much!

    Ben M - May 4, 2015 reply

    That’s great! Do you know if it’s compatible with Microsoft”s new Visual Studio Code, which is based on Atom? (If not sure I’ll check tomorrow)

  • Josep - May 8, 2015 reply

    Great article! How create widget for use NFC from Android? or how connect to Android SDK?

  • lhasbhopa - May 25, 2015 reply

    Hi! Geogia,
    Thank you very much for the post.I learned a lot from it.However my question is not related to this post.I am new to livecode and I have one problem in the current app I am working on. In my app I want to make two fields overlapped and when the card opens the second filed will remain hidden but when user clicks on the first one it will show the content in the field two.I manage to solve the problem when I put two fields in a different position.But when i overlapped them I am not getting what I want.Can you please help me to solve this issue.I would be so grateful to you if you can help me with this.Following are the script I wrote.Its working but I want them to be overlap so that when the second filed is hidden.It won’t take a long gap between the second filed and the upcoming fields after it.
    on Opencard
    hide fld 2
    end Opencard

    on mouseUp
    show fld 2
    end mouseUp

    Much Regards
    Tsering

    Georgia Hutchings - May 26, 2015 reply

    Hi Tsering,

    If I have understood correctly:

    You have two fields, field 1 and field 2. Field 2 is behind field 1, and it is hidden. When you click on field 1 you want field 2 to be revealed.

    I suggest for the script:

    on openCard
    hide field 2
    show field 1
    end openCard

    on mouseUp
    hide field 1
    show field 2
    end mouseUp

    For the size and position of the fields, if you want field 2 to be directly behind field 1 then you need to set the size and position of the fields to be the same, and then lock size and position (in the Size and Position pane in the property inspector) so that they remain in the same place.

    I hope I have understood your problem correctly and have helped. I suggest using the LiveCode forums, as you will be able to upload your stack so that it is easier for others to understand the problem. Please let me know if I can help you further.

    Kind Regards,

    Georgia

    lhasbhopa - May 27, 2015 reply

    Thank you very much Georgia for your kind gesture.Yes it helped me.But,again I need to trouble you a bit. is it possible not to hide the field 1, when field 2 shows.Instead let field 2 to drop down under field 1 and field to remain at its position, And On mouseDown field 2 will go back to its first position and remain hidden at the back of field 1 given that field 2 is hidden right at the same position of field 1.
    Much Regards
    Tsering

    PS: And thank you very much for suggesting about the forum.Actually I lost my password for both livecode forum and my personal email,therefore can’t login to both accounts hahaha! .But i will definitely registered with different account now.

    Georgia Hutchings - May 27, 2015 reply

    Hi Tsering,
    I think you want something like:
    command showField
    show field 2
    set the location of field 2 to x,y
    end showField
    command hideField
    hide field 2
    set the location of field 2 to (the location of field 1)
    end hideField
    That should solve the problem for you.
    Hope to see you back on the forums soon!
    Kind Regards,
    Georgia

  • Mark Smith - July 26, 2015 reply

    Thanks for an excellent intro Georgia. One bit has me confused. When you say “where the username should be the username you use to log into the LiveCode extension store. Don’t have a username? Create one here.” I am a bit puzzled because I log into the store with my full email address (which you probably have access to). I am presuming that is NOT what I should be putting into this declaration, correct? Then what? Thanks

    Mark

    Georgia Hutchings - September 2, 2015 reply

    Hi Mark,
    Make sure you are logged in to your LiveCode account, then try this link: http://livecode.com/account/developer/register. You can create your developer id here. If you look back at step 2, then you will see my developer id is gohutchings.
    Hope that clears things up for you!
    Thanks,
    Georgia

  • Mikey - August 30, 2015 reply

    Waaaaay back in step 1, when you suggest ‘Open TextWrangler, select the LiveCode Builder color scheme and save the file as “speechBubble.lcb” in the “speechBubble” folder. Now we can begin writing the widget!’

    That implies, to me, that you should open the color scheme and save it as the source for the widget. I don’t think that’s what you meant.

    Georgia Hutchings - September 2, 2015 reply

    Hi Mikey,
    It just means to open a new text document in text wrangler and save it as .lcb file so that before you start writing the widget you have the file saved in the right place with the right extension and with the right colour scheme in TextWrangler.
    Hope that makes sense,
    Georgia

  • Christopher Armstrong - February 11, 2016 reply

    Great documentation. Followed it and worked like a charm. Gave me a much better understanding how widgets and libraries work.

Join the conversation

*