As all veteran gadget developers are likely to tell you, you will most certainly be faced with different and sometimes challenging UI needs in each of your gadgets. Many gadgets require input from the user to set configuration options, answer questions, or input additional data. There are basically three ways you can do this:
div
in the main gadget that is normally hiddenplugin.ShowOptionsDialog()
Each of the above mentioned possibilities has pros and cons, but in this article, we will focus on the options dialog. There are several things you gain when using an options dialog: a standard OS window with "OK" and "Cancel" buttons, consistent and discoverable access to it via the gadget menu, and all the API's UI elements you've grown to love.
Both the options view and details view may seem to pose a communication problem, as scripts and variables in your main gadget code are inaccessible from either view. You can reduce code redundancy by putting all shared code in a separate ".js" file and referencing it from all involved XML files. However, communicating values from the options dialog to the main code at runtime might not be so apparent at first. This article will show you it's easy enough once you understand it.
You might be familiar with the options
object, which persists data for a gadget.
The same object can be used as a bridge to communicate data between the main code and the options dialog.
Below is the source code from a sample gadget I created for this article.
Inside main.js
, we set default values for our three options keys and then display the current values.
/** * Here we will read all stored options values * and initialize the labels with them. */ function initializeStoredOptions() { // first we assign some default options to avoid // errors caused by nonexistent values stored. options.putDefaultValue('storedOption1', 'first run - initial value 1'); options.putDefaultValue('storedOption2', 'first run - initial value 2'); options.putDefaultValue('storedOption3', 'first run - initial value 3'); // and then we initialize the labels option1.innerText = options.getValue('storedOption1'); option2.innerText = options.getValue('storedOption2'); option3.innerText = options.getValue('storedOption3'); }
As mentioned earlier, we can access the same options
object from within the options dialog.
In the code below, we read in the values and initialize text boxes.
When "OK" is pressed, we update the options
object with the new values.
Note that we don't actually modify the options
until the user selects "OK".
Remember that the user has a "Cancel" option, so it's best to hold off making any changes
until "OK" is pressed.
/** * Here we will read all stored options values * and initialize the text boxes with them. */ function initializeStoredOptions() { option1.value = options.getValue('storedOption1'); option2.value = options.getValue('storedOption2'); option3.value = options.getValue('storedOption3'); } /** * When the Ok button is pressed we transmit all options to the * main view by changing their values in the options object. */ function okPressed() { options.putValue('storedOption1', option1.value); options.putValue('storedOption2', option2.value); options.putValue('storedOption3', option3.value); } /** * Here is where we would cancel some options or do other cleanup * but we don't need to for this example so we'll leave it empty. */ function cancelPressed() { }
You've seen how the options
object provides a simple means to share data between the main code and options dialog.
The question now is: how does the main code know which options have been changed in order to react to the changes?
Here is where the onoptionchanged
event of the main view element comes into play.
All you have to do is set an event handler that will be notified when the options
object changes.
/** * Whenever an options is changed, this function gets called to deal with it. */ function optionChangedInOptionsDialog() { if (event.propertyName == 'storedOption1') { option1.innerText = options.getValue('storedOption1'); } else if (event.propertyName == 'storedOption2') { option2.innerText = options.getValue('storedOption2'); } else if (event.propertyName == 'storedOption3') { option3.innerText = options.getValue('storedOption3'); } }
The name of the option that has changed can be found from event.propertyName
.
Here, we update the label to reflect the new value.
You can check out the rest of the example below, or look at the many gadgets that implement the options dialog. The Day/Night World Clock has a particularly great looking implementation of the options dialog.
Here is how to put it all together:
<view height="80" width="250" onopen="initializeStoredOptions()" onoptionchanged="optionChangedInOptionsDialog()"> <div background="#FFFFFF" width="100%" height="100%"> <label x="0" y="0" width="75" height="20">Option 1 :</label> <label name="option1" x="75" y="0" width="175" height="20" /> <label x="0" y="20" width="75" height="20">Option 2 :</label> <label name="option2" x="75" y="20" width="175" height="20" /> <label x="0" y="40" width="75" height="20">Option 3 :</label> <label name="option3" x="75" y="40" width="175" height="20" /> <a x="0" y="60" height="20" onclick="plugin.showOptionsDialog()"> Open options dialog programmatically... </a> </div> <script src="main.js"/> </view>
<view height="60" width="250" onopen="initializeStoredOptions()" onok="okPressed()" oncancel="cancelPressed()"> <label x="0" y="0" width="75" height="20">Option 1 :</label> <edit name="option1" x="75" y="0" width="175" height="20" /> <label x="0" y="20" width="75" height="20">Option 2 :</label> <edit name="option2" x="75" y="20" width="175" height="20" /> <label x="0" y="40" width="75" height="20">Option 3 :</label> <edit name="option3" x="75" y="40" width="175" height="20" /> <script src="options.js"/> </view>
You can also download the above code as a gadget file from here.
Now that you know the basics, you can go ahead and explore further possibilities. As a bonus, I'd like to show a somewhat advanced options dialog that has different UI modes. This way, we can display completely different options dialogs to the user at runtime.
The main idea is to create a UI interface inside a container div for every purpose you have in mind. It's easy to just show the appropriate UI and hide the other divs.
You might want to make a default options dialog. To do this, set that div as visible in the options.xml and all others as hidden. The reason for this is that while you might be able to programmatically call the options dialog at any time, after any user interaction, the user also has the possibility to choose options from the gadget menu. You should also set the view's width and height to match the default case you want.
Now, when you want to show another options dialog, let's say an add dialog (Figure 2),
all you have to do is set an option to indicate you want to do this (e.g. options.putValue('optionsDialogType', 'ADD');
).
Check for this option in the onopen event handler of the options view. Here you can hide all other divs and show the add dialog one.
Also, the onopen event handler allows you the opportunity to change the view's width and height to match the design of the particular dialog you are showing.
At this point, you should clear out the dialog type option so the default menu shows when no type is specified.
Note that once the actual options dialog is rendered, you can still change the view's dimensions, but not the dimensions of the window that contains it.
Figures 1 and 2 show the two available options dialogs in the timezone gadget. This gadget is a great resource for learning this and other advanced techniques.
function onOpen() { if (options.getValue('optionsDialogType') == 'ADD') { // The add option dialog's UI manipulation code goes here. // Here is an example of what you might write: divNormal.visible = false; divEdit.visible = false; divAdd.visible = true; view.width = divAdd.width; view.height = divAdd.height; // ... } else if (options.getValue('optionsDialogType') == 'EDIT') { // The edit option dialog's UI manipulation code would go here. // ... } else { // The default option dialog 's UI manipulation code goes here // If y?u don't have anything special to do to initialize the default // options, this might even remain empty, because everything should // be set from the xml file. } }
Using all the basics in this article, you should now be able to get the most out of options dialogs and build great interactive interfaces that will make your gadgets stand out. Happy programming!
I'm currently a student at the "Politehnica" University of Timisoara. I've been programming since before I can remember and love to do it. Not too long ago, I came across the GD API, and just a few gadgets later, I managed to get listed in Google Desktop's Developer Hall of Fame. This later led to my joining the Google Desktop team as an intern in the summer of 2007 which has proved to be the greatest experience in my life so far.