One of the main reasons why Hello World is a good example to start with is because it is a minimal widget and shows the minimal set of steps that you would go through to create a widget that uses the WRTKit for its user interface. We will create just two files, an HTML file called HelloWorld.html and a JavaScript file called HelloWorld.js and both will be very short.
Before we'll start writing code let's talk about what exactly it is that we want to build. A typical Hello World application simply displays the text Hello World with the minimal amount of code. But because using the WRTKit is so simple, we'll go one step further and make it a bit fancier.
WRTKit user interfaces are composed of views and controls. A view is one logical group of stuff that you can see on the screen. A view can be longer than what fits on the screen at once in which case the user has to scroll, but it's still one view. For example in a web browser application you might have one view for the bookmarks, another for settings and a third view for viewing actual web pages. Controls are user interface elements like buttons, text fields, checkboxes, etc. that either let the user perform some kind of interactive action or simply shows some information. When you create a user interface with the WRTKit you create one or more controls, one or more views, and place the controls on the views. After this, all you have to do is ask one of the views to be displayed and the rest happens automatically.
Since this is a very simple widget we will just have one view and we'll call it the "main view". Instead of just saying "Hello World" we will let the user input their name and then click a button to popup a dialog that says hello to the user by name. E.g. if the user enters "John" we will popup "Hello John!". But we will also create some error handling so that if the user doesn't input a name we will popup a warning dialog that says "Please enter your name!" instead.
As mentioned earlier, you create user interfaces using JavaScript rather than HTML when you use the WRTKit. Because of this it's perhaps not so surprising that the HTML file is extremely short. What might come as a surprise however is that it's extremely short and nearly identical no matter what kind of widget you are building if you are using the WRTKit. In fact it typically only has about 10 lines of code, including the DOCTYPE declaration! Here's what it looks like:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <script type="text/javascript" src="WRTKit/WRTKit.js"></script> <script type="text/javascript" src="HelloWorld.js"></script> </head> <body onload="init()"> </body> </html>
We're using XHTML 1.0 so the DOCTYPE declaration is for XHTML 1.0 Strict. And because this is XHTML we also have the normal XML declaration (<?xml...). After that the content should be familiar even if you've used previously only used HTML rather than XHTML. We have a normal html root level tag with a head and body. Note that in XHTML tags are case sensitive and should be in lowercase as in the example above. The xmlns attribute is a name space declaration that states that all the tags used here are part of the XHTML standard.
Because we're creating a widget and not a web page we don't need to have a title. If you want you can define one here but you won't see it anywhere. Next comes an important bit: <script type="text/javascript" src="WRTKit/WRTKit.js"></script>. That piece of code instructs the Web Runtime to load a JavaScript file called WRTKit.js from a directory called WRTKit. The WRTKit.js file takes care of loading the WRTKit and including all the files that are needed by it. Those files are all inside the WRTKit directory and you don't need to concern yourself with them at this point. In fact all you need to do to use the WRTKit in a widget is to copy the WRTKit directory into your own widget's root directory and then include the XHTML script tag presented above that loads the WRTKit/WRTKit.js JavaScript file. If you are creating a widget of your own you can find the WRTKit directory in the Library directory in the WRTKit SDK. But for your convenience it has already been copied to the Hello World example.
Notice that there's another script tag too, this one loading a file called HelloWorld.js. This is where we'll put all the JavaScript code that implements our Hello World widget. We could have just written the JavaScript code inline between a <script> and </script> tag in the HelloWorld.html file but the HTML file is less cluttered if we move all JavaScript to a separate file.
Finally let's look at the body tag. There are two things of interest here. First of all there's no content between <body> and </body>. That's because all of the content will be created by the WRTKit using JavaScript. And that leads us to the other point of interest in the body: the onload="init()" attribute of the body tag. This sets up an event handler that gets called after the widget has loaded all of its content. The event handler calls a JavaScript function called init(). This is the point where we jump from the HTML to JavaScript. From this point on everything will be JavaScript and the place where it all starts is the init() function that gets called thanks to this little onload event handler for the body tag.
The HTML file that we created above set things up so that there is a function call to a function named "init()" when the widget is done loading. No such function exists a this point so we have to create it. The init() function is the entry point to our JavaScript and this is also where we will start the implementation.
The S60 Web Runtime supports two kinds of user interface interaction: tab and pointer. The WRTKit supports but methods but usually the tab interaction method (also known as "navigation mode") is preferred. Unfortunately the pointer based navigation mode is the default so we'll have to disable it and switch to the tab navigation mode. Also by default the softkey bar is hidden, which we don't want because we want to give the user a clue about how to exit the widget. The good news is that these two tweaks are easy to do and only requires two simple method calls. The bad news is that those method calls are done through objects that only exist in the Web Runtime but not in a PC web browser. Due to this, we'll create the init() function as follows:
// Called from the onload event handler to initialize the widget. function init() { // set tab-navigation mode and show softkeys // (only if we are in the WRT environment) if (window.widget) { widget.setNavigationEnabled(false); menu.showSoftkeys(); } }
We wrapped the calls to disable the pointer navigation and show the softkeys in an if-clause that checks if there's such an object available as window.widget. This will evaluate to false on a PC web browser but true in the S60 Web Runtime.
Now that we've tuned the Web Runtime to our liking we can create the actual user interface. We'll create four things: the so-called "user interface manager" that is in charge of managing views and other user interface resources, the main view for the widget, a text field where users can enter their name, and finally a button that they can click on to popup a notification dialog that says "Hello" Notice that we don't have to create the notification dialog because this is done for us by the user interface manager.
Before we create those four objects we will declare four global variables so that we can access those objects elsewhere in the widget. So let's add the following to the top of the file (outside the init() function):
// References to the WRTKit user interface manager and main view. var uiManager; var mainView; // References to controls in the main view. var helloButton; var nameField;
Now that we have some variables that can track the objects we are about to create we can actually craete those objects. We'll create the objects inside the init() function so that the user interface gets created right after the widget has loaded.
The first thing we create is the user interface manager. This is quite simple and requires only a single line of code:
// create UI manager uiManager = new UIManager();
Next we'll create the main view. The WRTKit allows all kinds of views to be created if one has special needs for how user interface controls should be laid out. However in the vast majority of cases the ListView class will be sufficient and that's what we'll create this time too:
// create main view mainView = new ListView(null, "Hello World");
The first argument to the ListView constructor is a unique identifier for the view. All user interface elements in the WRTKit can have a unique identifier. The identifier is helpful if you want to specifically target a view or control with some CSS style rule or if you want to identify the source of an event in an event listener callback, etc. However we don't need it here so we don't bother giving our main view a unique id and just pass a null identifier value instead. The second argument is a caption for the view. We'll just call our view "Hello World". The caption will be displayed at the very top of the screen when we display the main view. But before we do that, let's create the rest of the user interface.
The widget should have a text field to let users enter their name and a button to trigger the greeting popup. Both the text field and the button are WRTKit controls and will be created and added to the main view that we just created. There are two kinds of buttons in the WRTKit though, form buttons and navigation buttons. Form buttons are meant to trigger actions whereas navigation buttons are meant for situations where clicking it would result in moving from one view to another. Clearly therefore, we want a form button in this case.
Creating the textfield and form button is done as follows:
// add a text field to the view nameField = new TextField(null, "Enter your name"); mainView.addControl(nameField); // add a button to the view helloButton = new FormButton(null, "Say Hello!"); mainView.addControl(helloButton);
The first argument for both the textfield and button is the same as for the view: an optional unique identifier. We won't need it here either so rather than scratching our heads trying to come up with a unique identifier that we'll end up ignoring we'll just define it as null. The second argument for the textfield constructor is a caption. All controls except buttons have captions and this is the second argument in all constructors for controls with captions. The caption is displayed above the control and tells the user what the control does. In our case we want to have a textfield where the user should enter the name so we'll use "Enter your name" as the control caption. The second argument for the button constructor is the text for the button. The button text for form buttons should be a verb or other descriptive text that lets the user know what happens if the button is pressed. We'll put "Say Hello!" on our button.
After we have created a control we can add it to a view. Custom views can have their own ways to do so but in the case of the default ListView that you'll use in most cases you add a control by calling the addControl() method.
The user interface for the main view is now ready and we can show it. Showing a view is done by calling setView() in the user interface manager. So we'll add one more line of code to the end of the init() function:
// display the main view uiManager.setView(mainView);
If you zipped up the widget directory, renamed it HelloWorld.wgz and installed the widget on an emulator or handset, or just simply ran it in a PC web browser, you'd notice that the user interface seems to work. There's just one problem. Clicking on the button doesn't do anything! But don't worry, we're about to fix that.
The thing that is missing is that we have no way of knowing when the button was pressed. And without knowing when it's pressed we can't react to the press and show a greeting. A user interface action such as pressing a button is called an "event". In the WRTKit events are reported to "listener" functions that developers can register to controls. The way this is done is by creating a function that you want to get called when an event occurrs, and then calling addEventListener() on the control whose events you are interested in, passing the function to that method. However there are many types of events and you are almost surely not interested in all of them. Because of this you can specify the type of event that you want to get notified about by giving the event type name to the addEventListener() method. The event types and their names are described in the WRTKit API Reference for each control. Note that because controls inherit from other classes they also inherit event types.
The event type that we're interested in here is "ActionPerformed". That event is fired by form buttons whenever a user clicks on them. In order to get notified of this event we'll need two things: create a function that will get called when the event occurs and register that function as an event listener to our button. Let's create the function first but leave the implementation empty for now:
// Called when the hello-button is clicked. function helloButtonClicked(event) {}
Now that we have the event handler callback function we can add the event listener registration code. Let's put that right after where we create the button in the init() function:
helloButton.addEventListener("ActionPerformed", helloButtonClicked);
Now our helloButtonClicked() function will get called whenever the button is clicked. Notice that the function has an argument called event. This argument will receive an event object that describes the event that occurred. This can be useful if you have an event handler function that handles many different events. The event object contains three properties that you can examine to decide on what to do: a property called "source" that points back to the control or view from where the event was fired, a property called "type" that contains the event type name for this event, and a property called "value" that has helpful additional info that depends on the event type. You'll find more detailed information about this in the API Reference.
All that remains now is to write the code that shows the greeting popup dialog when the button is pressed. This code will naturally go in the helloButtonClicked() function.
Using notification popup dialogs is very easy with the WRTKit. The user interface manager has two methods: showNotification() and hideNotification() that are used to show and hide notification popup dialogs. The showNotification() method takes four arguments: displayTime, type, text and progress. The displayTime argument is used to supply a time (in milliseconds) for how long the notification popup should be shown for. We want our greeting to be visible for 3 seconds so we'll pass 3000 to this argument. The type argument is a string that tells the method what kind of popup to show, which determines the visual style of the popup. We'll be using two types: "info" and "warning". The "info" type for when we show the greeting and "warning" if the user didn't enter a name and we want to show a notification dialog that complains about this. The text argument is simply the text to show in the notification dialog. And finally the progess argument is a decimal number between 0.0 and 1.0 that is used in progress dialogs to display how far along a process is.
A progress of 0.0 means "0% progress" and 1.0 means "100% progress". So for example if some process is 25% completed, you'd pass 0.25 to this argument. If you don't know how long a process will take you can pass a negative number. This will result in an animated progress bar that has a visual style that indicates that the progress is unknown. Typically you'd use the "wait" notification type is you want to show a progress dialog. If you don't want to show any progress information in the dialog, such as in our case, you can omit the progress argument or pass a null to it.
Before we can write our code we need one more thing. We need to know what the user wrote in the name textfield. This value can be retrieved by calling the getText() method for the textfield. We're now ready to write the code for the helloButtonClicked() function:
var name = nameField.getText(); if (name.length == "") { uiManager.showNotification(3000, "warning", "Please enter your name!"); } else { uiManager.showNotification(3000, "info", "Hello " + name + "!"); }
The Hello World widget is now done and you can zip it up, rename it to HelloWorld.wgz and deploy it on a handset or emulator. But before that, let's try it in a normal PC browser. Testing in a PC browser is useful because it allows you to use deubugging tools (like the Firebug JavaScript debugger for Firefox) in case something isn't working. Using a PC browser to test is also very fast because you don't have to install the widget to the emulator or handset. Just hit reload in the web browser when you make a change and you can see the results immediately. Of course there are features that will be missing in a web browser, such as that you won't have access to the Options menu or any other advanced widget functionality. But for the purpose of rapidly testing user interfaces it can be very helpful.
There are a couple of points that are good to note now that the widget is ready. First of all, your widget now works with both the pointer and tab navigation modes. If you comment out the widget.setNavigationEnabled(false) call in the init() function you can try this out. Second, your widget works resolution independently. You can try this either by resizing the PC browser window or by trying the widget out with different resolutions in the S60 emulator. Third, the widget correctly handles screen rotations and other resizes, e.g. if the softkey bar is hidden / shown. Fourth, the widget looks can be customized without needing to touch any code - simply by changing the UI.css and image files that you can find in the WRTKit/Resources directory.
But perhaps more important than any of the above is the fact that the WRTKit simplifies the separation of user interface code from data and logic code. That's not something that is apparent in a simple widget like this one, but it becomes very important as you create something more complex and especially if the user interface contains elements that are created dynamically for example based on data that has been fetched from the Internet using AJAX.