One of the joys of life is a perfect fit. Whether shoes, clothes, or chairs, the right size makes all the difference, and Desktop gadgets are no exception.
Most Desktop gadgets available today are not resizable—their UI dimensions are fixed unless the developer specifies otherwise. Luckily, the platform does its best to ensure the gadget is usable. For example, you may have a large gadget that is wider than the current Sidebar width. The Sidebar automatically scales such gadgets down so that they are displayed in their entirety. Although the majority of gadgets aren't resizable, many could benefit from taking advantage of this capability.
Two steps are needed to make your gadget resizable. The first is enabling resizing so the user can resize the gadget. When a gadget is resizable, a resize frame appears when the mouse is within the area of the gadget. The second part is writing code that reacts to size changes and updates the gadget's UI accordingly.
There are several reasons to make your gadget resizable. First and foremost, your users will appreciate having control over how much space the gadget takes up, especially if it contains a lot of text content.
Also, when docked in the Sidebar, a gadget may scale down to the point of being unreadable (Figure 1). On the other hand, the available Sidebar area may be wider than the gadget, leaving unused space (Figure 2). Ideally, the gadget should attempt to be readable at all times and use all available space.
Figure 1: Text is unreadable
Figure 2: Wasted space
The Twitter gadget is a good example of a resizable gadget. It fits nicely in the Sidebar and can be resized when outside of it.
Desktop gadgets have three different resizing modes, which are specified using the view
object's resizable
property.
This property expects one of the following strings:
The default behavior is 'zoom'. The gadget is not resizable, but it may be zoomed by the Sidebar when docked.
Of course, you can also set the value at runtime from script.
if (isDocked) { view.resizable = 'zoom'; } else { view.resizable = 'true'; }
One thing to keep in mind is that the gadget could also be resized by the Sidebar. If the gadget is resizable, the Sidebar updates the width of the gadget to match the current Sidebar width.
Once the gadget is set to be resizable, two handlers are notified of size changes:
view.onsizing
and
view.onsize
.
onsizing
fires while the gadget is being resized.
event.width
and event.height
contain the new width and height.
Within this handler, you can cancel the resizing or override the new dimensions by setting event.width
and event.height
yourself.
Cancelling a size change
function onSizing() { // Cancels the change in size. event.returnValue = false; }
Overriding a size change
function onSizing() { // Override the change in size. event.width = 200; event.height = 200; }
The onsize
event is also provided which is triggered after the resizing is complete—view.width
and view.height
contain the new dimensions.
This event is preferred If you don't need to cancel or override the resizing.
Here's an example of a basic framed gadget. It displays what seems like nonsense.
Figure 3: You're a vegetable
main.xml
is defined as follows:
<view width="200" height="140" resizable="false">
<script src="main.js" />
<div name="frame" background="frame.png"
x="0" y="0"
width="200" height="140" />
<div name="frameMiddle" background="#DDDDFF"
x="21" y="22"
width="156" height="95">
<label name="content" width="100%" height="100%" wordWrap="true"> You're a vegetable, you're a vegetable Still they hate you, you're a vegetable You're just a buffet, you're a vegetable They eat off of you, you're a vegetable
</label>
</div>
</view>
The problem here is the monolithic frame. For the gadget to be resizable, the frame image must be broken up into four corners and four edges (highlighted in Figure 4 below):
Figure 4: Cutting up the frame
The edges are merely slices that serve as a repeating background in a div
.
Now let's revise main.xml using the cut-up image set, setting its resizable
property to 'true'
, and listening for the onsize
event.
<view width="200" height="140" resizable="true" onsize="onSize();">
<script src="main.js" />
<div name="frameTopLeft" background="top_left.png"
x="0" y="0"
width="21" height="22" />
<div name="frameTop" background="top.png"
x="21" y="0"
width="156" height="22" />
<div name="frameTopRight" background="top_right.png"
x="177" y="0"
width="23" height="22" />
<div name="frameLeft" background="left.png"
x="0" y="22"
width="21" height="95" />
<div name="frameMiddle" background="#DDDDFF"
x="21" y="22"
width="156" height="95">
<label name="content" width="100%" height="100%" wordWrap="true"> You're a vegetable, you're a vegetable Still they hate you, you're a vegetable You're just a buffet, you're a vegetable They eat off of you, you're a vegetable
</label>
</div>
<div name="frameRight" background="right.png"
x="177" y="22"
width="23" height="95" />
<div name="frameBottomLeft" background="bottom_left.png"
x="0" y="117"
width="21" height="23" />
<div name="frameBottom" background="bottom.png"
x="21" y="117"
width="156" height="23" />
<div name="frameBottomRight" background="bottom_right.png"
x="177" y="117"
width="23" height="23" />
</view>
Most of the actual work is done in the resize handler.
The handler is notified of size changes, and then resizes and positions all of the controls and UI elements appropriately.
For example, there may be a label that should be right aligned. When the gadget's size changes, the handler repositions that label to keep it flush to the right.
I recommend expressing sizes in percentages rather than pixels whenever possible. Take the example of a listbox
: if all of its inner items are set to 100% width, they are resized automatically when the listbox's size changes.
Continuing the example above, the resize handler does the following:
As you see below, the calculations are fairly simple:
function onSize() { // Determine the gadget's new width and height. var width = view.width; var height = view.height; // No need to reposition the top left corner. // Top right corner. frameTopRight.x = width - frameTopRight.width; // Bottom left corner. frameBottomLeft.y = height - frameBottomLeft.height; // Bottom right corner. frameBottomRight.x = width - frameBottomRight.width; frameBottomRight.y = height - frameBottomRight.height; // Position and resize the edges. frameTop.width = width - frameTopLeft.width - frameTopRight.width; frameBottom.width = width - frameBottomLeft.width - frameBottomRight.width; frameBottom.y = height - frameBottom.height; frameLeft.height = height - frameTopLeft.height - frameBottomLeft.height; frameRight.height = height - frameTopRight.height - frameBottomRight.height; frameRight.x = width - frameRight.width; // Resize the center. frameMiddle.width = width - frameLeft.width - frameRight.width; frameMiddle.height = height - frameTop.height - frameBottom.height; }
That's all there is to it! This amazing gadget is also available for download.
I hope you see that implementing resizable gadgets isn't too much of a "stretch". Do you think you can "handle" it?
James likes riding buses, trains, and planes. He's too lazy for bikes. Walking is okay, but not first thing in the morning.