Creating a script widget

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

How to create a script widget

Once you know what script widget you want to create, there are are three essential ingredients to creating it:

  1.  Figure out what controls are needed to build your widget's appearance and functionality
  2.  Decide what properties you want to expose to the users of your widget
  3.  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.

Creating the internal controls

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.

Laying out the internal controls

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.

Adding a property

To add a property, simply add a getProp or setProp handler to the implementation script. We are going to add a label property.

Test your widget

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"

Add some functionality

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.

Standard properties

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.

Metadata

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.

Using the Extension Builder

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.

8 Comments

Matthias Rebbe

The final stack as download would be great. ;)

Mark

OMG, my chin is on the floor!!! I had other plans for next week but LC just usurped those. I now know what I'll be working on. Do I need LC 10 to do this, or will it work in 9.6.8?

Matthias Rebbe

Mark, you'll need LC 10 DP5

Mark

Thank you Matthias.

Heather Laine

I've added a link to the sample script at the top of the article.

Stam

Great walkthrough - I created my first widget and in spite the slight learning curve this was done in a couple of days, while having to learn LCB and writing this would have taken a couple of months - at least.

My only comment would be that some further information regarding setting up property metadata (ie the various options that exist, or even what 'pi' is - I know know it's the property inspector and not the geometric constant!) would have been helpful - I managed to piece this together after *a lot* of searching online...

But thank you team, this made my day!

Stam

I've created a useful widget and it was fun.

However, there is a strange issue: If I save a stack with my widget, close it and re-open it, there is a 1-2 second delay until the widget actually appears on the card.

This does not happen if just used as a simple group based on the same code (Lock Screen is used judiciously already)

Is this normal/expected for script widgets or is my funky code to blame do you think?

Also: how can I generate events/messages that can be captured in the script of the widget so the developer using this can add script?

Panos Merakos

Hello Stam,

I think this question is now answered in the forums - here is the link for anyone interested:

https://forums.livecode.com/viewtopic.php?t=38569

Kind regards,
Panos
--

Add your comment

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.