My favorites | English | Sign in

Watch Google I/O keynotes live on May 19 and 20!

Google Desktop APIs (Labs)

Listboxes and Scrollbars in Google Desktop Gadgets

This article was written and submitted by an external developer. The Google Desktop Team thanks Yannick Stucki for his time and expertise.


Yannick Stucki, Gadget Developer
July 2007

Listboxes, listitems, scrollbars...how do I put these things together?

Introduction

Lists are key objects of many gadgets. Whether your gadget displays the latest RSS news feeds, or results from your local computer, you'll need UI objects that will display contents of lists.

In earlier versions of Google Desktop, you could only use contentarea, which did its job but wasn't very flexible. Since March 2007, however, you had the option of using the more powerful listbox, which can be filled with listitems. There are many ways to use the listbox that go beyond the scope of this introduction; however, I want to show you at least one way to take advantage of this very useful object.

Getting Started

As I've mentioned, the listbox offers a lot of functionality. I want to focus on a simple listbox (as used in the Movies Gadget) that can display information from either an RSS feed or a desktop query. This example will not demonstrate item deletion, selection (items will be immediately deselected), or multiple selection.


Movies Gadget

To get started, create a listbox in main.xml. It's best to wrap it in a div as we'll see later.

<div name="listDiv" height="165" width="132" x="6" y="21">
     <listbox height="100%" name="list" width="100%" x="0" y="0" itemWidth="100%" 
      itemHeight="33" itemSelectedColor="#404040" itemOverColor="#404040" onchange="listOnChange()"/>
</div>

Note: To make your list look nice, make sure that it doesn't crop the bottommost item. Notice that the height of the listbox is a multiple of itemHeight. In this case, the total height is 165, and each item has a height of 33 (165/33 = 5).

If you set listbox.itemSeperator to true, don't forget to account for the space occupied by the seperator(s).

Filling the list on the fly

In most situations, you need to be able to fill the list on the fly, since you don't know what your RSS feed is going to contain. This is an example of how you can add items to your list. Basically you wrap an XML definition in a string. The outer item is a listitem, inside of which you can put whatever you want:

list.appendElement("<listitem name=\"item\" tooltip=\""+item.name+"\"><label color=\"#FFFFFF\" tooltip=\""+item.name+"\">"+item.name+"</label><label color=\"#999999\" y=\"14\" tooltip=\""+item.name+"\">"+item.desc +"</label></listitem>");
For this line of code, I assumed I had a JavaScript object called item with name and desc properties. You can create such an object like this:
var item=new Object();
item.name="my Item";
item.desc="I am an Item";

I've also set a color and added a tooltip (be sure to add it on both the listitem and the label). You could also add more labels, pictures, and so on—just make sure you align those objects properly. If the listitem is too small for the objects, you can change the itemHeight of the listbox (not the listitem).

You might have noticed that I didn't give the listitem a distinct name, they are all named item. In fact, I could have left the name property empty altogether if I had used another powerful method to access each element: a hash table.

Accessing the items via a hash table

Hash table? Sounds difficult? It isn't at all.

A hash table is similar to an array, but instead of indexing its members with numbers (0, 1, 2, ...), you'd use strings ("item1," "breaking news," ...). Luckily, a JavaScript object can natively act as a hash table.

To begin, let's create a global variable:

var table=new Object();
Next, we have to add references to the listitems in the hash table as we create them:
function append(item){
  table[item.name]=item;
  list.appendElement("<listitem name=\"item\" tooltip=\""+item.name+"\"><label color=\"#FFFFFF\" tooltip=\""+item.name+"\">"+item.name+"</label><labelcolor=\"#999999\" y=\"14\" tooltip=\""+item.name+"\">"+item.desc "</label></listitem>");  
}

Now let's assume you've loaded an RSS feed with the content stored in "title," "description," and "link." All you have to do is create an item and call append:

var item=new Object();
item.name=title;
item.desc=description;
item.link=link;
append(item);
Do this for every element, but start from the back to the beginning so appended items will be inserted at the top of the list. This ensures that you have the same sorting as in the feed.

Because we have stored the whole item in the hash table, we only need to know the name property to access it again.

function listOnChange(){
var item= new Object();
item=table[list.selectedItem.children(0).innerText]
list.selectedIndex=-1;
//show a page in the browser via item.link
//or open a details view which shows more of item.description e.g. //showDetailsByName(item.name);
//or item.whatever additional information you gave it before
}
Notice that list.selectedItem.children(0).innerText is the same as item.name because we stored the name in the innerText of the first label; the label with the name inside is the first child: children(0).

We set selectedIndex to -1 which unselects the item we just clicked.

If you are using a details view and you click the same item twice in a row, it re-opens the details view instead of closing it. This isn't the default behaviour that a user might expect. Therefore, I used the following code in the Movies Gadget:

var lastDetails="";

function showDetailsByName(name){
  if (name==lastDetails){//the user clicked the same thing twice, close it instead of re-opening
  plugin.CloseDetailsView();
  lastDetails="";
  return;
}
//the name was different than the last details: open that details view
  lastDetails=name;
  //open details view
  //etc etc
}

How to implement a scrollbar

The easiest way to deploy a scrollbar is to set the autoscroll property of the listbox to true. This has two possible disadvantages:

  • No scrollbar is present if the gadget is docked in the sidebar. This might work in many cases, but I wanted a scrollbar to be present even when the Movies Gadget is docked.
  • You can't change the scrollbar's classic gray appearance.

It really isn't hard to implement an advanced scrollbar; just alter your main.xml code to this:

<div name="listDiv" height="165" width="132" x="6" y="21">
 <listbox
  height="200" name="List" width="119" itemWidth="100%"
  itemHeight="33" itemSelectedColor="#404040" itemOverColor="#404040"
  onchange="listOnChange()"/>
 <scrollbar enabled="true" height="184" hitTest="htclient" name="sb"
  width="10" x="124" y="-10" lineStep="14" max="200"
  orientation="vertical" background="0000000"
  thumbDownImage="scroll.png" thumbImage="scroll.png" thumbOverImage="scroll.png"
  onchange="sbOnChange()"/>
</div>

Make the height of the listbox slightly bigger than in the div. We also have to shorten the listbox's width a bit to make room for the scrollbar.

In the case of the Movies Gadget, I just set a transparent background for the scrollbar since the underlying background is black. The only image I added was the scroll button itself. If you don't set any images and the background is not fully transparent, the default gray scrollbar will be used.

I also didn't want the up/down arrows to appear, so I set the y value to a negative value and the height bigger than in the div. The scrollbar is purposely too long, and since it's wrapped in a div, it will automatically be cropped.

Now we need to "connect" the div and the scrollbar:

function sbOnChange(){
  list.height=NUMBER_OF_ITEMS*list.itemHeight;
  sb.max=NUMBER_OF_ITEMS*list.itemHeight-listDiv.height;
  if (sb.max<0)
  sb.max=0;
  list.y=-sb.value;
}

You will need to know the number of items that have been added to the list, so keep track of this in your application. The trick here is if the list grows too long, we negatively offset the list and only the parts of the list which are in the div area will be displayed.

Putting it all together

So far you have:

  • a main.xml entry
  • a function to append an element
  • a function to react to clicking on an element
  • a scrollbar
The final element is an update() function that can be called when a user clicks on a query button, or periodically by a setInverval():

function update(){
  list.removeAllElements(); //don't forget that
  table=null; //clear the table
  table=new Object();
  //fetch your content somewhere, e.g. via xmlHttpRequest
for (/*whatever*/){ //go through the results backwards; write what you need into a var item= new Object() append(item); } }

Conclusion

I hope you can use these examples to more effectively implement lists in your own gadgets. There are other features of listbox, such as listitem deletion and multi-selection, which we skipped. Nevertheless, for many gadgets, the methods shown in the above examples will be the most common uses of listbox.

Resources

If you want to see such a listbox in action, have a look at the Movies Gadget. The gadget is open sourced so feel free to browse the source code. All the functions shown here are taken (nearly 1:1) from there. You'll find mostly everything in the file called main.js, except for the update function which is in parsing.js.


Gadget API reference:

Author's Bio

Yannick Stucki

I am currently a student and started programming a few years ago. It's always tough to produce complete programs yourself (that are actually used by others) because as a hobbyist programmer, you just don't have the time and resources of a fully-staffed team. That's why I love gadgets programming: gadgets range from very simple to fairly complex, but most importantly, you can get them out there and have people actually using them.

Besides programming, I love playing Rugby. And in winter, I go snowboarding in the Swiss Alps.

You can read more about my gadgets on my homepage. There you'll also find contact information if you need more help or have suggestions.


Creative Commons License
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License.