A step-by-step tutorial on how to create a widget in minutes. Originally available as a
sample widget.
* Introduction & prerequisites
So, you don't know anything about widgets but you want to create one right away: you came to the right place. Start the chronometer - once that you have the right tools installed
we'll show how you can write a widget from scratch in minutes.
OK... so what is a widget??
Term "widget" is often used for any cool-cute-easy-to-write "thing" that can be placed and dragged on your desktop or in your browser's window. Note that there are also "gadgets" - a term promoted mostly by Microsoft - which are pretty much similar to desktop widgets and which run on Microsoft Windows Vista.
Across this tutorial, we'll be referring to the desktop widgets and in particular to Yahoo! Widgets for Windows or Mac.
Tools that you need
1. A computer with Microsoft Windows XP or Mac. Obvious reasons why you need this one
2. Yahoo! Widgets engine. This is required to run Yahoo! widgets (in fact, it's the application that actually "interprets" the code from your widgets and make them show and behave on the desktop). You can
download it from here.
3. A graphics editor capable of saving images in PNG format (with
.png extension). You'll see that the widget's "face" or "skin" is basically such a PNG file, so you'll need the editor to create that "skin". Most popular such editors are probably Adobe Photoshop and Gimp (this one is free and you can
download it here).
4. Any text editor - you need it to specify the structure and behavior of your widget via XML and JavaScript (see below if you are not familiar with these).
5. A small program that you'll use to "pack" the files of your widget so that Yahoo! Widgets engine "understands" it. In fact, that program is itself a widget and you can
find it here.
Things that you need to know in advance
1. To create simple widgets:
nothing - isn't this why are you here in the first place?
2. To create advanced widgets: you'll have to learn a bit of JavaScript language and use the Yahoo! Widget Engine reference. You can find a good JavaScript documentation
here. Also, the Yahoo! Widget Engine reference is
available here.
And now... let's make a widget!
* Step 1: Creating the widget's folders
This is not quite a "real" step, but getting used to it will help you keep your widgets organized.
A widget is composed of
resources (e.g. images) and
code files. To better organize these files, it's advisable to create somewhere (for instance under the My Widgets folder) a directory for each new widget, with the following hierarchy of subdirectories:
....My Widget........
....|................
....+-- Contents.....
........|............
........+-- Resources
To anticipate a bit what is described below in details, you will normally place the
graphic files of a widget in its
Resources folder and the
code files directly in its
Contents folder.
* Step 2: Creating the widget's "skin"
The widget's skin is the most important
graphic component of any widget and is nothing else but an image in PNG format (e.g. "skin.png").
First thing to know about a widget and its PNG "skin" is that
transparent areas of the image will be invisible in the widget. This means that you can easily create a non-rectangular widget: you simply create a rectangular image with transparent background and draw something non-rectangular on it. That "something" will be the visible part of your widget:
In the image above, the zones with gray squares are transparent. All of them (including the circular cut in the bottom left) will be invisible in the widget, as in the screenshot below:
The gray window is the Yahoo! Widgets Engine's debug window. The red oval shape is how your widget will look. You may also notice the yellow area with the "Hello" text - it's simply painted on the image, but we'll make that area to be clickable like a button (see below how).
Once that you have created such a skin, save it as a .png file to the <b>Resources</b> folder above.
That's pretty much everything you need to know for a widget's skin.
Quick recap:
* PNG format;
* transparent areas will be invisible;
* to create non-rectangular widgets, simply draw widget's shape inside a rectangular image with transparent background;
* Step 3: "Describing" how the widget will look
If you try to describe in advance in plain English how your widget will look like, you will probably say something like
"will be an oval red shape with a hole in it, like the one depicted in this file: Resources\skin.png".
Well, in a similar way you must "describe" to Yahoo! Widgets Engine how do you want your widget to look (and this applies to other desktop widgets too). The "language" for that will obviously be a a bit different than plain English - but the information that you are passing is pretty intuitive. Here is an example:
<?xml version="1.0" encoding="UTF-8"?>
<widget>
<window>
..<image>
....<src>Resources/skin.png</src>
..</image>
</window>
</widget>
Note: if you are familiar with the XML format you may skip the next paragraphs.
First line<?xml version="1.0" encoding="UTF-8"?>
declares that this file is in
XML format, complies with version 1.0 of the XML specification and uses UTF-8 characters encoding. Actually, you
don't need to understand these to create a widget, even a complex one. Just remember to
always start your widget's description with such a line.
The second line contains the
<widget>
word enclosed between "
<" and "
>" signs. Note that the
last line contains the same word enclosed in the same signs but prefixed by a
slash</widget>
Similar happens for the
window and the
image words. The words enclosed between these signs are called
tags or
XML elements. The ones without slashes are called
opening tags, while the ones with slashes are
closing tags. What's between an
opening tag and a
closing tag is a
block.
I had to explain all these because of a very simple reason:
if a tag represents a visual element in your widget, what's inside a block will "belong" to that visual element. The simple definitions above allow for a very efficient way to express
relationships or
parenthoods between elements of your widget.
So what the file above sais is just
"create a window and show inside that window an image". Not too hard, right?
As you may see, "inside" that image is a
"src" tag. Does this mean that we tell engine to create a "src" inside the image? No - we're just specifying the
"src" property of the image. Depending on what tags are we using, the engine knows to interpret the enclosing relationship either that an element must be created inside another or that an enclosed tag is a property of its "parent".
That was all with the XML. Now we just have to save the text above as a "
.kon" file in the "
Contents" folder created above (e.g.
widget.kon).
Quick recap:
* widget structure is described in XML format and stored in a .kon file;
* widget elements are represented through XML tags;
* enclosing means either that the inner element logically belongs to the outer one or that the inner element is a property of the outer one;
* Step 4: Running the widget for the first time
If you are anxious to see your widget running for the first time, we have reached there!
Simply double click the
.kon file that you created at Step 3 above. You should normally see this dialog box that pops up:
Click the
Use Widget button and you will see your widget on the desktop as below:
You can play around with the widget - drag it on the desktop, right-click to open the contextual menu, etc. But don't get too excited

- there is more in the next steps!
* Step 5: About the predefined elements
Let's make a step back and ask ourselves a legitimate question:
if window and image XML tags represent objects on screen, what other objects are there (and which XML tags specify them)?
A quick answer would be:
there are many predefined objects that implement most of the common Windows controls: static texts, text inputs, scroll bars, popup menus, slide controls. But beyond these visual elements, there are also some predefined objects that only encapsulate a functionality. They are called timer, filesystem, screen, system, animator, XMLHttpRequest and others.
Your widget can use any of these - Yahoo! Widgets Engine will know how to deal with each of them. If you are - for instance - declaring an
image in your widget as in Step 3 above, that image will be
rendered in your widget. If you are declaring a
screen object, that has no visual correspondent but its properties and
methods will allow you to find out data about the desktop area, such as its
width or
resolution. So, predefined objects (or XML elements) encapsulate either visual components, or functionality, or both.
The complete list and documentation for these elements can be found in
Yahoo! Widgets Engine Reference Manual. In this step, we'll try to use and explain some of these elements.
First, let's try to add a text input so that our users can type something. We'll do this with the following declarations in our
.kon file:
<window>
..<image>
....<src>Resources/skin.png</src>
..</image>
..<textarea>
....<data>hello world!</data>
..</textarea>
</window>
Close the widget from the right-click menu and then restart it by double clicking the .kon file. You will see the warning message displayed in Step 4 above (that message appears each time when the .kon file was modified) and after you click the 'Use Widget' button, your widget will show on the desktop as follows:
Probably not quite what you expected: the text input appears to be
outside the widget! In fact, it is
inside the widget's window at top left - but
outside its visible area. Remember how we drew the skin image? The image rectangle was larger than the oval shape, but the area outside the oval was invisible. And the widget's window is
the entire image rectangle - even though the visible area is smaller.
Let's fix this: taking a look at the
textarea element in the Yahoo! Widgets Engine Reference shows this property:
hOffset
the horizontal offset of a textarea object
Description
The hOffset attribute of the text block defines the horizontal (left to
right) offset for the text based on 0,0 being the upper left hand corner
of the object's parent view (superview). The greater the value assigned,
the farther to the right the text will be drawn.
This means that setting a hOffset greater than 0 will move the text to the right. Similarly, setting vOffset greater than 0 will move the text lower. Let's add these to our
.kon file. At the same time, we'll change the color of the text so that is clearly visible. With these changes, the
.kon file will look as follows:
<?xml version="1.0" encoding="UTF-8"?>
<widget>
<window>
..<image>
....<src>Resources/skin.png</src>
..</image>
..<textarea>
....<data>hello.world!</data>
....<hOffset>100</hOffset>
....<vOffset>50</vOffset>
....<color>#FFFFFF</color>
..</textarea>
</window>
</widget>
and the widget will look like this:
To make it clearly visible, we'll set also its background and opacity (which is default 0, i.e. fully transparent!):
....<bgColor>#F0D0E0</bgColor>
....<bgOpacity>100</bgOpacity>
and obtain this look:
We'll leave it to you further discovering of another elements and how to use them.
Quick recap:
* Yahoo! Widgets Engine Reference describes a lot of elements and their properties;
* elements encapsulate visual elements, functionality or both;
* you can control visual elements throuth their properties;
* functionality is exposed through elements' methods and properties;
* Step 6: Handling user actions
Our widget looks great - but what if we add some user interaction? Let's try these two things:
* save user's edits from the text area when user clicks the yellow zone;
* restore them when widget starts;
Across this step we'll focus on the first task. We have to do "something" when user clicks the yellow zone - which means that we first have to make the yellow zone clickable.
A quick look through the Yahoo! Widgets Engine Reference for 'mouse' will show this potentially useful entry:
onMouseDown
the script called when the mouse button is down inside the object
Description
The onMouseDown attribute of a text block is a wrapper for JavaScript code
that will execute when the user presses the mouse button down within the
object. This is useful for triggering a visual change of the object based
on a pressed state.
JavaScript
myObjectName.onMouseDown
Example
<text data="Example Text">
..<name>myLabel</name>
..<onMouseDown>
....myLabel.color = "#FF0000";
..</onMouseDown>
</text>
Looks good, excepting that this
handler is called when mouse button is pressed over an entire object. In our case, we want it called only when mouse button is pressed over the yellow area of our skin - which is just
a part of our
window object.
To workaround this, we have two options:
* Redraw the widget's skin so that the yellow area is a distinct image, then modify our
.kon file to place that image over the red oval, as much as possible in the same position.
* Stick to the original skin but try to get the screen coordinates of the mouse click and try to see if those coordinates lay inside the yellow area.
We may say that the first approach is more designer-style, while the second is a more programmer-style. Let's see both of them at work.
Designer style approach
Redraw the skin and save the yellow area as a separate image as below:
Then add the second image to our widget:
<?xml version="1.0" encoding="UTF-8"?>
<widget>
<window>
..<image>
....<src>Resources/skin.png</src>
..</image>
..<image>
....<src>Resources/button.png</src>
..</image>
..<textarea>
....<data>hello.world!</data>
....<hOffset>100</hOffset>
....<vOffset>50</vOffset>
....<color>#FFFFFF</color>
..</textarea>
</window>
</widget>
Now add the mouse click handler so that you'll be able to execute some code when user clicks the yellow image. That will be a JavaScript function that we'll call
onButtonClicked. Following the instructions in the Yahoo! Widgets Engine Reference, we'll add the line below to the
.kon file:
<?xml version="1.0" encoding="UTF-8"?>
<widget>
<window>
..<image>
....<src>Resources/skin.png</src>
..</image>
..<image>
....<src>Resources/button.png</src>
....<onMouseDown>onButtonClicked();</onMouseDown>
..</image>
..<textarea>
....<data>hello.world!</data>
....<hOffset>100</hOffset>
....<vOffset>50</vOffset>
....<color>#FFFFFF</color>
..</textarea>
</window>
</widget>
Attempting to restart the widget in this exact form will result in an error, as we did not yet implement the
onButtonClicked function. To see the exact error, you should enable debug window by adding this line at the beginning of your
.kon file:
<?xml version="1.0" encoding="UTF-8"?>
<widget>
<debug>on</debug>
...
We'll see how to implement the handler in the next Step.
Programmer style approach
As opposed to the designer style approach, we'll prefer not to create a separate image for the yellow area (aka "button"), but rather find the coordinates of its bounding rectangle relative to the entire image (or to widget's window). Then, we'll just add a mouse click handler to the
entire window and test the mouse coordinates against the rectangle above.
The
.kon file with the handler will look as below:
<?xml version="1.0" encoding="UTF-8"?>
<widget>
<window>
..<image>
....<src>Resources/skin.png</src>
..</image>
..<textarea>
....<data>hello.world!</data>
....<hOffset>100</hOffset>
....<vOffset>50</vOffset>
....<color>#FFFFFF</color>
..</textarea>
..<onMouseDown>onButtonClicked();</onMouseDown>
</window>
</widget>
The tests for mouse coordinates are illustrated in the image below:
To anticipate a bit what we'll discuss in the next chapter, a possible implementation for the
onButtonClicked function based on the scheme above may look as follows:
function onButtonClicked()
{
..// Read the last mouse event coordinates (see Yahoo! Widgets Engine Reference)
..// and test whether they lay inside the yellow area's approximate bounding rectangle.
..if (system.event.hOffset > 164 && system.event.hOffset < 270 &&
......system.event.vOffset > 95 && system.event.vOffset < 132)
..{
....// Do something here
....TODO
..}
}
We'll discuss the rest of implemntation in the next Step.
Note that adding a mouse click event on an object replaces the default behavior, which is to have that object dragged. You will have to implement dragging manually if you still need it (and you will probably want to move the window on screen with mouse...)
This was an example to illustrate that there can be more approaches to the same problem. Choosing one approach or another depends mostly on your preference and skills. In certain cases it's probably better to choose one in favor of the other, in other cases it's the reverse. With the time, you'll be able to pick the approach that suits better both your needs and your skills.
Quick recap:
* Each user input generates various events (also called "triggers");
* Yahoo! Widgets Engine predefines a set of possible events for both mouse and keyboard;
* To handle user input we must define handlers for events (also called "actions");
* Handlers are JavaScript functions that are automatically executed by the framework when an event of that class is triggered (e.g. on mouse down);
* A handler for a specific class of events can be defined for the entire widget or for visual elements that are part of the widget;
* Events can have various properties, such as coordinates relative to the widget's window, or to the screen, key that was pressed, etc. We'll probably need to test some of those in handlers.
* Step 7: Storing settings in preferences
We are now at the point where we can implement the handler in Step 6 above. We'll want user's input in the text box to be saved when the yellow area (or "button") is clicked and then restored when widget starts.
Let's focus on the
first task. We saw in the previous step how to define a handler for the yellow area - whether by using a separate image for it (actually, the
better approach for this particular case, as it doesn't disable the default dragging behavior) or by using a handler for the entire window and test if the mouse was clicked inside the region of interest. In any case, here is what else we'll need:
* retrieve the text modified by user in the text box;
* find a way to save this text permanently;
* load the previously saved text when widget is started;
* copy the saved text to the text box (after we loaded it);
Looking for the "
textarea" object in the Yahoo! Widgets Engine Reference will show this entry that solves the first task in the list:
data
the text that the textarea object contains
Description
The text to be edited. This is optional. If omitted, the user will be presented
with an empty text entry field.
For the second task, you may remember that widgets have by default
preferences that are saved, so you may assume (correctly) that we'll be able to save the text in the same way. Let's take a look at the preferences in the manual:
<preference>
block defining a preference setting and associated properties
Attributes
defaultValue
description
directory
extension
group
hidden
kind
maxLength
minLength
name
notSaved
option
optionValue
secure
style
ticks
tickLabel
title
type
value
Description
The preference block defines a block of information that is to be stored by the Widget
between open/closed sessions, as well as user entered data.
To store user text, we'll try to declare a preference. We won't want that editable in widget's preferences dialog - so we'll set its
hidden attribute. Its
value attribute will represent the data to be saved. Finally, we'll need to set the
name attribute so that we can reference this preference and the
name attribute of the
text area so that we can reference that too.
After declaring the preference, your
.kon file should look like this:
<?xml version="1.0" encoding="UTF-8"?>
<widget>
<window>
..<image>
....<src>Resources/skin.png</src>
..</image>
..<image>
....<src>Resources/button.png</src>
....<onMouseDown>onButtonClicked();</onMouseDown>
..</image>
..<textarea>
....<data>hello.world!</data>
....<hOffset>100</hOffset>
....<vOffset>50</vOffset>
....<color>#FFFFFF</color>
....<name>myTextArea</name>
..</textarea>
</window>
<preference>
..<name>textareaPref</name>
..<hidden>true</hidden>
</preference>
</widget>
A widget's preferences are saved automatically by Yahoo! Widgets Engine when widget closes, so to save the text it's probably enough to copy it to the preference when user clicks the yellow button. It's therefore enough to add a line like the one below in the handler that we've previously defined for mouse down event, replacing the call to the fictuous function onButtonClicked:
..preferences.textareaPref.value = myTextArea.data;
With this, the
.kon file would look like this:
...
..<image>
....<src>Resources/button.png</src>
....<onMouseDown>preferences.textareaPref.value = myTextArea.data;</onMouseDown>
..</image>
...
Finally, we'll want the text restored when widget starts. Since we already saw in the previous step that interaction with user is based on
events (aka triggers), we may assume there might exist such an event that occurs when widget has just started.
A search for "load" or "action" or "trigger" in the Yahoo! Widgets Engine Reference will show these entries:
<action>
code block not associated with an object
Attributes
file
interval
trigger
Description
The action XML block defines when and how a Widget will execute code that is
triggered automatically rather than by a user.
...
trigger
the event that triggers the enclosed code
Values
onGainFocus
onIdle
onKeyDown
onKeyUp
onKonsposeActivated
onKonsposeDeactivated
onLoad
onLoseFocus
onMouseDown
onMouseEnter
onMouseExit
onMouseUp
onPreferencesChanged
onRunCommandInBgComplete
onScreenChanged
onTellWidget
onTimer
onUnload
onWakeFromSleep
onWillChangePreferences
onYahooLoginChanged
Description
The trigger attribute for the action block defines what will trigger the contained block
of code.
Let's simply declare an
action block for the
onLoad trigger and try to restore data there:
<?xml version="1.0" encoding="UTF-8"?>
<widget>
<window>
..<image>
....<src>Resources/skin.png</src>
..</image>
..<image>
....<src>Resources/button.png</src>
....<onMouseDown>preferences.textareaPref.value = myTextArea.data;</onMouseDown>
..</image>
..<textarea>
....<data>hello world!</data>
....<hOffset>100</hOffset>
....<vOffset>50</vOffset>
....<color>#FFFFFF</color>
....<name>myTextArea</name>
..</textarea>
</window>
<preference>
..<name>textareaPref</name>
..<hidden>true</hidden>
</preference>
<action>
..<trigger>onLoad</trigger>
..myTextArea.data = preferences.textareaPref.value;
</action>
</widget>
Note: the discussion at this step applied for the "designer's approach". For the "programmer's approach" you just have to replace the "TODO" line in the onMouseDown handler with the line written above in red.
Quick recap:
* Each widget has its set of preferences that is automatically saved and loaded;
* Even though the engine saves and loaded the preferences, it's the programmer who sets the value of a particular preference to be saved, as well as uses the saved value to restore a property of the widget;
* Together with events generated by user inputs, there are also events triggered when widget starts, terminates or at certain moments of time (timers);
* Usually, saving the widget's state will happen on the onUnload trigger while restoring it will happen on the onLoad trigger
* Step 8: Final word...
That was all about this tutorial. It was probably longer than you expected, but hopefully useful. You should now start experimenting by your own, using also other widgets as model. Keep in mind that writing a widget is not rocket science -
if you can describe what you want in simple terms in plain English, then with a little practice you'll be able to translate it in a widget.
Make sure you also check the
libraries provided by our users and by Widgipedia team - they are a great way to develop complex widgets.
Thank you!
Adrian Citu