Creating a script widget
A script widget is a collection of (internal) controls and a behavior script which, from the point of view of the outside world, function as a single object. An instance of a script widget always appears as a single element when on a card, regardless of how many controls it contains, is always selected as a single control and does not allow internal controls to be directly referenced from outside. Script widgets are manipulated in script using the widget chunk, can define properties (using setProp and getProp handlers), and can send messages to and receive messages from other controls. Importantly, unlike custom property handlers defined on normal controls, attempting to set and get properties of script widgets will always call the handlers – regardless of whether messages are locked.
You can download the script widget sample script below
Once you know what script widget you want to create, there are are three essential ingredients to creating it:
- Figure out what controls are needed to build your widget's appearance and functionality
- Decide what properties you want to expose to the users of your widget
- Decide how the internal controls should be laid out when the object is resized
If you have an existing group that you want to turn into a script widget, it's fairly likely you will have these ingredients already.
For this example we are going to create an editable label widget. The first step is to create a new script-only stack and give it a name corresponding to the widget kind you want to define.
The internal controls of a script widget should be created in the stack's openControl handler. In general we recommend implementing a separate handler, eg createVisualControls which is called during openControl.
Our widget will need both a label field for displaying the label and an input field for editing it. We recommend storing the created controls' IDs in script local variables for future use, as well as having a script local that tracks whether openControl has been called yet.
The resizing code for a script widget should be executed in the stack's resizeControl handler. Again we recommend implementing a separate handler, eg layoutVisualControls which is called during resizeControl. For this example, the layout code is very simple - we always want the rect of the label field (and input field) to be the same as the rect of the external control.
Make sure you also call layoutVisualControls in openControl after creating your controls.
To add a property, simply add a getProp or setProp handler to the implementation script. We are going to add a label property.
At this point you should be able to create an instance of your widget and see what it looks like. Since script widgets are 'loaded' when the implementation stack is in memory, you don't have to use the extension builder at this point - simply create a stack and execute
create widget as "com.livecode.widget.editablelabel"
in the message box.
Try changing the label:
set the label of widget 1 to "New Label"
As it stands we have essentially re-made a version of the label field already available in the tools palette, albeit it has the advantage that its label property can still be fetched and set when messages are locked. It needs some additional functionality.
We are going to make it so that double-clicking on the widget turns it into an input field into which you can enter a new label.
In this case the event handling code is simplified by the fact that the label field takes up the whole rect of the widget - if a widget has multiple components and does different things depending on which component is clicked, it is necessary to disambiguate by comparing
the id of the target with the stored id of the corresponding control.
With this additional code, the widget can be double-clicked and turned into an input field, into which a new label can be entered.
Some standard control or object properties cannot be overridden as widget properties. In this case the widget implementation is notified of a property change by the parentPropertyChanged message. This can be used to ensure that property changes made by the user can be handled appropriately. For example, the textAlign property is not inherited from the parent, so if it is set on the widget, code is needed to ensure that the field controls within the script widget change alignment accordingly.
In order for a script widget to become a bona-fide integrated object, it needs to have a certain level of metadata. Firstly it needs some top-level metadata - a title, type, author and version. This is also where the widget's description can be added, which is displayed in LiveCode documentation when the widget is installed.
The widget also needs property metadata to function correctly in the IDE. This is specified using a special custom property handler, propertyMetadata, which is used at compile time to generate information about how the widget properties should be displayed in the property inspector, and also what properties the widget has by default when dragged out from the Tools palette.
The Extension Builder can now be used to test the widget as it would be if it were installed. To load the widget, ensure the stack is saved in a folder on its own and open the Extension Builder (Tools > Extension Builder). Click the folder icon in the top-right.
Ensure LiveCode Script files are selected in the file filter and choose the script-only stack file in which you have implemented the widget.
When the widget is opened in the Extension Builder, you should see the information defined in the top-level metadata. In our case that's just the title and icon currently:
Clicking the play button (in the footer of the Extension Builder) will cause the widget to be installed temporarily. You should see the icon appear in the Tools palette, and a stack will appear with the widget on it. If you select the object and click Inspector you will see your defined properties in the tabs as specified in your property metadata:
Right-clicking and selecting 'Show Documentation' will bring up the widget docs. If you have documented the properties, links to their docs will appear in a table underneath the description
The widget can be installed directly so that it is loaded next time the IDE is started using the plus icon in the Extension Builder footer. Alternatively, the package icon can be used to make a .lce extension package file which can be shared. To install an .lce package, open the Extension Manager (Tools > Extension Manager), click the plus in the top-right and select the package to be installed.