org.symbian.tools.wrttools.doc.WRTKit/html/WRTKit_Travel_Companion_user_interface-GUID-989d17cc-f019-46bb-b6f5-49166e258aca.html
changeset 230 7848c135d915
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.doc.WRTKit/html/WRTKit_Travel_Companion_user_interface-GUID-989d17cc-f019-46bb-b6f5-49166e258aca.html	Fri Mar 05 19:31:41 2010 -0800
@@ -0,0 +1,698 @@
+<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html lang="en" xml:lang="en">
+<head>
+<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
+<meta name="copyright" content="(C) Copyright 2005" />
+<meta name="DC.rights.owner" content="(C) Copyright 2005" />
+<meta content="concept" name="DC.Type" />
+<meta name="DC.Title" content="Travel Companion user interface" />
+<meta scheme="URI" name="DC.Relation" content="WRTKit_Travel_Companion_Tutorial-GUID-be79ba64-fa03-4968-964e-d7dcc42d7053.html" />
+<meta content="XHTML" name="DC.Format" />
+<meta content="GUID-989D17CC-F019-46BB-B6F5-49166E258ACA" name="DC.Identifier" />
+<meta content="en" name="DC.Language" />
+<link href="commonltr.css" type="text/css" rel="stylesheet" />
+<title>
+Travel Companion user interface</title>
+</head>
+<body id="GUID-989D17CC-F019-46BB-B6F5-49166E258ACA"><a name="GUID-989D17CC-F019-46BB-B6F5-49166E258ACA"><!-- --></a>
+
+
+
+    <h1 class="topictitle1">
+Travel Companion user interface</h1>
+
+    <div>
+
+        <div class="section"><h2 class="sectiontitle">
+Handling multiple views</h2>
+
+            
+            <p>
+
+                The Travel Companion widget will follow many of the same patterns that the Hello World
+                and RSS Reader widgets have followed. The entry point to the widget is the init() function
+                that we call from the onload event handler in the main HTML file TravelCompanion.html.
+                We will implement the init() function along with the rest of the widget and user interface
+                in the TravelCompanion.js file. Let's start with the init() function so that we get the
+                widget up and running.
+            </p>
+
+            <p>
+
+                From previous widgets we already know a lot of things about the WRTKit and about what
+                we will need. For example we know that we will have a user interface manager and that
+                we'll need a reference to each of the views that our widget uses. And since we have
+                designed the Travel Companion widget we know what views we will have. Before we write the
+                init() function we can go ahead and declare the variables that we will use to refer to
+                the views and the user interface manager. And while we're at it we can also declare the
+                variable that we'll use to refer to the Travel Companion engine. All of these variables
+                are globals and thus go outside the init() function:
+            </p>
+
+<pre>
+
+// Reference to travel companion engine that implements the business logic.
+var engine;
+
+// Reference to the WRTKit user interface manager.
+var uiManager;
+
+// References to views.
+var mainView;
+var infoView;
+var converterView;
+var weatherView;
+var settingsView;
+</pre>
+
+            <p>
+
+                Previously we've written the init() function so that it creates the entire user interface
+                but because the Travel Companion widget has a much larger user interface than anything
+                we've done before, it's probably a good idea to split things up into smaller functions
+                so we don't end up with a huge init() function that will be hard to read and maintain.
+                Let's create a function for the creation of each view, and then one function that will
+                call these view creation functions and setup all other things that we need for the user
+                interface, such as the Options menu. We'll just create stub functions for now but we
+                will soon get to implement the functions:
+            </p>
+
+<pre>
+
+// Creates the user interface.
+function createUI() {
+}
+
+// Creates the main view.
+function createMainView() {
+}
+
+// Creates the info view.
+function createInfoView() {
+}
+
+// Creates the converter view.
+function createConverterView() {
+}
+
+// Creates the weather view.
+function createWeatherView() {
+}
+
+// Creates the settings view.
+function createSettingsView() {
+}
+</pre>
+
+            <p>
+
+                In the RSS Reader widget we used functions called showMainView() and showSettings() to
+                move between views. That allowed us to implement all the logic that was required when
+                moving between views in one place regardless of what action triggered the change of
+                views. We'll do the same thing in the Travel Companion widget but this time we'll need
+                five functions since we have five views. Let's create stubs for these too at this stage
+                so that the functions exist if we need to refer to them.
+            </p>
+
+<pre>
+
+// Displays the main view.
+function showMainView() {
+}
+
+// Displays the info view.
+function showInfoView() {
+}
+
+// Displays the converter view.
+function showConverterView() {
+}
+
+// Displays the weather view.
+function showWeatherView() {
+}
+
+// Displays the settings view.
+function showSettingsView() {
+}
+</pre>
+
+            <p>
+
+                Since we will create each user interface view in its own function our init() function
+                will be much simpler than in previous widgets. We will create the business logic engine,
+                create the user interface by calling createUI() and then display the main view.
+            </p>
+
+<pre>
+
+// Called from the onload event handler to initialize the widget.
+function init() {
+    // initialize the business logic engine
+    engine = new Engine();
+    
+    // create the user interface
+    createUI();
+    
+    // display the main view
+    showMainView();
+}
+</pre>
+
+            <p>
+
+                Let's get started with creating the user interface by returning to the createUI()
+                function. We'll want to widget to show the softkey bar and we'll want it to be in
+                tab navigation mode instead of the default pointer navigation mode. We then need
+                to create the user interface manager and the five views that this widget has. Of 
+                course those five views are actually created by the five create-functions that we
+                just wrote stubs for so we just need to call those functions from createUI().
+            </p>
+
+<pre>
+
+// Creates the user interface.
+function createUI() {
+    if (window.widget) {
+        // set tab-navigation mode and show softkeys
+        widget.setNavigationEnabled(false);
+        menu.showSoftkeys();
+    }
+    
+    // create UI manager
+    uiManager = new UIManager();
+    
+    // create views
+    createMainView();
+    createInfoView();
+    createConverterView();
+    createWeatherView();
+    createSettingsView();
+}
+</pre>
+
+            <p>
+
+                Because we created the stub functions for moving between views, we can also create
+                the Options menu now. We'll only add one item to the menu in addition to the default
+                "Exit" menu item. The item we'll add is one that will take the user to the settings
+                view. We'll first need to create a global menu item identifier:
+            </p>
+
+<pre>
+
+// Constants for menu item identifiers.
+var MENU_ITEM_SETTINGS = 0;
+</pre>
+
+            <p>
+
+                Now that we have the menu item identifier we can actually create the menu item. Because
+                the Options menu functionality is only available in the S60 Web Runtime and not in PC
+                browsers we'll add that code in the same if-clause where we set the navigation mode and
+                show the softkey bar.
+            </p>
+
+<pre>
+
+// create menu
+var settingsMenuItem = new MenuItem("Settings", MENU_ITEM_SETTINGS);
+settingsMenuItem.onSelect = menuItemSelected;
+menu.append(settingsMenuItem);
+</pre>
+
+            <p>
+
+                When the menu item is selected it calls a function called menuItemSelected(). Let's
+                create that function and handle the case when the settings menu item is selected.
+                When it is, we want to call showSettingsView() so that the user ends up in the settings
+                view as expected:
+            </p>
+
+<pre>
+
+// Callback for when menu items are selected.
+function menuItemSelected(id) {
+    switch (id) {
+        case MENU_ITEM_SETTINGS:
+            showSettingsView();
+            break;
+    }
+}
+</pre>
+
+            <p>
+
+                The framework that we'll need when implementing the actual views is now nearly complete.
+                But before we move on let's talk about how we'll be using softkeys in the Travel
+                Companion widget.
+            </p>
+
+            <p>
+
+                In the main view we want the right softkey to be the default "Exit". But in the
+                four functional views we don't want it to exit the widget but rather to return to
+                the main view. We'll thus use "Back" as the title in the functional views, except
+                in the settings view where we'll use "Cancel".
+            </p>
+
+            <p>
+
+                Let's create three functions that will handle setting the widget right softkey to
+                these configurations. The functions will check if we're actually in the S60 Web
+                Runtime before attempting to change the right softkey in order to make the widget
+                also work in a PC browser for testing purposes.
+            </p>
+
+<pre>
+
+// Sets the softkeys for the main view.
+function setMainViewSoftkeys() {
+    if (window.widget) {
+        // set right softkey to "exit"
+        menu.setRightSoftkeyLabel("", null);
+    }
+}
+
+// Sets the softkeys for sub views.
+function setSubViewSoftkeys() {
+    if (window.widget) {
+        // set right softkey to "back" (to main view)
+        menu.setRightSoftkeyLabel("Back", showMainView);
+    }
+}
+
+// Sets the softkeys for settings view.
+function setSettingsViewSoftkeys() {
+    if (window.widget) {
+        // set right softkey to "cancel" (returns to main view)
+        menu.setRightSoftkeyLabel("Cancel", showMainView);
+    }
+}
+</pre>
+
+        </div>
+
+        <div class="section"><h2 class="sectiontitle">
+Main view</h2>
+
+            
+            <p>
+
+                Let's create our main view now so that we get something visible. The main view is
+                of course created in the function createMainView() for which we already created a
+                stub function. The main view will have four WRTKit NavigationButton controls in it,
+                one for each of the views (not counting the main view itself) in the widget. Using
+                the NavigationButton control is just like using a FormButton, with the difference
+                that it looks different and can have an icon image in addition to just text. Earlier
+                when we looked at the files that were in the Examples/TravelCompanion directory we
+                noticed that there were four icons there. Now it's time to use them as icons in the
+                navigation buttons.
+            </p>
+
+            <p>
+
+                We will add event listerners to the buttons so that when they are clicked (when they
+                emit an ActionPerformed event) they will call the functions we have created for
+                moving between views. At this point the functions are just stubs, but we will of
+                course fix that later.
+            </p>
+
+            <p>
+
+                We've had captions for the views that we've created so far in the Hello World and
+                RSS Reader widgets and the Travel Companion will also have view captions. However
+                for the Travel Companion we'll use an image as our view caption. We will set the
+                view caption to an empty string because if it's undefined or null then the view
+                caption area will be hidden. Then we will create a CSS rule for the ListViewCaptionText
+                class that is used by the area where the caption text goes in a list view. Because
+                our CSS rule is defined after the default WRTKit CSS rule for ListViewCaptionText,
+                our rule will be override the default one. Let's open up TravelCompanion.css and
+                create this rule:
+            </p>
+
+<pre>
+
+/* Place logo in list view caption */
+.ListViewCaptionText {
+    background: url("ListViewCaptionLogo.png") no-repeat;
+    height: 35px;
+}
+</pre>
+
+            <p>
+
+                We specified a height in addition to the background image. The height is needed
+                so that the caption text area won't collapse to zero height since it won't contain
+                any text. We're now ready to implement the createMainView() function:
+            </p>
+
+<pre>
+
+// Creates the main view.
+function createMainView() {
+    // empty caption text to display the caption bar - custom background using CSS
+    mainView = new ListView(null, "");
+    
+    // info
+    var navToInfoButton = new NavigationButton(null, "NavInfoIcon.png", "Information");
+    navToInfoButton.addEventListener("ActionPerformed", showInfoView);
+    mainView.addControl(navToInfoButton);
+    
+    // converter
+    var navToConverterButton = new NavigationButton(null, "NavConverterIcon.png", "Currency Converter");
+    navToConverterButton.addEventListener("ActionPerformed", showConverterView);
+    mainView.addControl(navToConverterButton);
+    
+    // weather
+    var navToWeatherButton = new NavigationButton(null, "NavWeatherIcon.png", "Weather Forecast");
+    navToWeatherButton.addEventListener("ActionPerformed", showWeatherView);
+    mainView.addControl(navToWeatherButton);
+    
+    // settings
+    var navToSettingsButton = new NavigationButton(null, "NavSettingsIcon.png", "Settings");
+    navToSettingsButton.addEventListener("ActionPerformed", showSettingsView);
+    mainView.addControl(navToSettingsButton);
+}
+</pre>
+
+            <p>
+
+                We now have a main view and our widget even calls showMainView() when it starts
+                but nothing will actually show up yet because we haven't implemented the showMainView()
+                function yet. Let's do that now:
+            </p>
+
+<pre>
+
+// Displays the main view.
+function showMainView() {
+    setMainViewSoftkeys();
+    uiManager.setView(mainView);
+}
+</pre>
+
+            <p>
+
+                The function calls the setMainViewSoftkeys() function to put the right softkey
+                in the proper state and then asks the user interface manager to show the main
+                view. Remember that we're already calling the showMainView() function from the
+                init() function so we can go ahead and test this now in a PC browser, emulator
+                or handset. Notice the custom list view caption image and the navigation button
+                icons. Clicking the navigation buttons doesn't do anything yet because we don't
+                have any other views yet and the functions to show other views except the main
+                view aren't implemented. But then again, because we wrote stub functions we are
+                also not getting any error messages.
+            </p>
+
+            <div class="fignone" id="GUID-989D17CC-F019-46BB-B6F5-49166E258ACA__GUID-B006B086-0D8B-4EA4-8A71-179507B7595A"><a name="GUID-989D17CC-F019-46BB-B6F5-49166E258ACA__GUID-B006B086-0D8B-4EA4-8A71-179507B7595A"><!-- --></a><span class="figcap">Figure 1. 
+Travel Companion main view</span>
+
+                
+                <br /><img src="Travel_Companion_Main_Screenshot_1.png" /><br />
+            </div>
+
+        </div>
+
+        <div class="section"><h2 class="sectiontitle">
+Settings view</h2>
+
+            
+            <p>
+
+                It's a good idea to implement the settings view as early as possible because
+                that allows you to test the rest of the widget more easily. In order to get
+                the settings view up and running we'll need to do three things. First of all
+                we'll need to implement the createSettingsView() function so that the widget
+                actually has a settings view. Second, we'll need to implement the actions
+                that can be performed in that view. There are two actions: "Save" and "Cancel".
+                Of these, save is the tricker one and we'll implement it in a function that
+                we will call saveSettingsClicked(). The cancel action will simply take us back
+                to the main view and will be handled by the showMainView() function that we
+                have already implemented. And third, we need to implement the showSettingsView()
+                function so that clicking on the settings navigation button in the main view
+                takes us to the settings view and sets it up correctly so that the view
+                reflects the current preferences.
+            </p>
+
+            <p>
+
+                The settings view will have four sections that we will separate using a WRTKit
+                Separator control. The first section lets the user select the home city and the
+                whether the home city is in daylight saving time. Here we'll use a SelectionMenu
+                control for the city selection and a SelectionList control with a single option
+                for the daylight saving time option. We could have used a SelectionList with
+                two options "Daylight saving time" and "Normal time". Since the selection would
+                have been a single selection this would have resulted in two radio buttons. But
+                partly for the sake of usability and partly to show how to do it, we'll implement
+                it so that it's a single checkbox instead. Thus, we'll use a SelectionList with
+                a single option but we'll get the checkboxes instead of radio buttons by putting
+                the SelectionList in multiple selection mode. Because there's only one option
+                there will only be one checkbox.
+            </p>
+
+            <p>
+
+                The second section is just like the first one but for the local city. The third
+                section is for the temperature unit selection (Celsius or Fahrenheit) and we'll
+                implement it as a single selection SelectionList control. And finally the fourth
+                section is for the Save and Cancel FormButton controls.
+            </p>
+
+            <p>
+
+                The options for the selection controls will be created just before we create
+                the controls themselves. The daylight saving time and temperature unit options
+                are just static JSON defined option lists since we know what the options are.
+                But for the city selection we'll create it dynamically by asking the business
+                logic engine for a list of all the supported cities. We'll then create one option
+                for each city.
+            </p>
+
+            <p>
+
+                Because we'll need to access the controls in the settings view from outside the
+                creation function we'll need to create global references to them:
+            </p>
+
+<pre>
+
+// Settings view controls.
+var homeCitySelection;
+var homeCityDSTSelection;
+var localCitySelection;
+var localCityDSTSelection;
+var temperatureUnitSelection;
+</pre>
+
+            <p>
+
+                We can now implement the settings view creation function:
+            </p>
+
+<pre>
+
+// Creates the settings view.
+function createSettingsView() {
+    // empty caption text to display the caption bar - custom background using CSS
+    settingsView = new ListView(null, "");
+    
+    // create city options from cities array
+    var cityOptions = [];
+    var cities = engine.getCities();
+    for (var i = 0; i &lt; cities.length; i++) {
+        cityOptions.push({ value: cities[i], text: cities[i].name });
+    }
+    
+    // create DST option
+    dstOptions = [ { value: true, text: "DST (+1h)" } ];
+    
+    // home city
+    homeCitySelection = new SelectionMenu(null, "Home City", cityOptions);
+    settingsView.addControl(homeCitySelection);
+    
+    // home city DST (using a multiple selection but only one option to get a single checkbox)
+    homeCityDSTSelection = new SelectionList(null, "Daylight Saving Time (home)", dstOptions, true);
+    settingsView.addControl(homeCityDSTSelection);
+    
+    // separator
+    settingsView.addControl(new Separator());
+    
+    // local city
+    localCitySelection = new SelectionMenu(null, "Local City", cityOptions);
+    settingsView.addControl(localCitySelection);
+    
+    // local city DST (using a multiple selection but only one option to get a single checkbox)
+    localCityDSTSelection = new SelectionList(null, "Daylight Saving Time (local)", dstOptions, true);
+    settingsView.addControl(localCityDSTSelection);
+    
+    // separator
+    settingsView.addControl(new Separator());
+    
+    // temperature unit
+    var temperatureUnitOptions = [ { value: "c", text: "Celsius" }, { value: "f", text: "Fahrenheit" } ];
+    temperatureUnitSelection = new SelectionList(null, "Temperature Unit", temperatureUnitOptions);
+    settingsView.addControl(temperatureUnitSelection);
+    
+    // separator
+    settingsView.addControl(new Separator());
+    
+    // save button
+    var saveSettingsButton = new FormButton(null, "Save");
+    saveSettingsButton.addEventListener("ActionPerformed", saveSettingsClicked);
+    settingsView.addControl(saveSettingsButton);
+    
+    // cancel button
+    var cancelSettingsButton = new FormButton(null, "Cancel");
+    cancelSettingsButton.addEventListener("ActionPerformed", showMainView);
+    settingsView.addControl(cancelSettingsButton);
+}
+</pre>
+
+            <p>
+
+                Note that we used the actual city as the value for the city options. You can
+                use anything you want for the value property for a selection control option
+                and in this case we used the actual city object because it will come in handy
+                later when we want to set the home and local cities that the user has selected
+                to the engine.
+            </p>
+
+            <p>
+
+                Our save button calls the saveSettingsClicked() function we talked about earlier
+                but we haven't created that function yet. We'll continue implementing the user
+                interface by writing this function.
+            </p>
+
+            <p>
+
+                You'll recall that the business logic engine actually stores all the preferences
+                so all we really need to do is read what values have been selected in the settings
+                view, telling these values to the engine, calling the savePreferences() method in
+                the engine to persist the settings, and then return to the main view.
+            </p>
+
+<pre>
+
+// Called when the user clicks on the "save" button in the settings view.
+function saveSettingsClicked() {
+    // update the selected home city
+    var selectedHomeCityOption = homeCitySelection.getSelected();
+    if (selectedHomeCityOption != null) {
+        engine.setHomeCity(selectedHomeCityOption.value);
+    }
+    
+    // update the selected local city
+    var selectedLocalCityOption = localCitySelection.getSelected();
+    if (selectedLocalCityOption != null) {
+        engine.setLocalCity(selectedLocalCityOption.value);
+    }
+    
+    // update home and local city DST
+    // there's only one checkbox but we are using a multiple-selection menu.
+    // if the selected array has one element then the checkbox is checked.
+    engine.setHomeCityDST(homeCityDSTSelection.getSelected().length == 1);
+    engine.setLocalCityDST(localCityDSTSelection.getSelected().length == 1);
+    
+    // update temperature unit
+    var selectedTemperatureUnitOption = temperatureUnitSelection.getSelected();
+    if (selectedTemperatureUnitOption != null) {
+        engine.setTemperatureUnit(selectedTemperatureUnitOption.value);
+    }
+    
+    // save settings
+    engine.savePreferences();
+    
+    // go back to the main view
+    showMainView();
+}
+</pre>
+
+            <p>
+
+                We're writing defensive code here and making sure that the selection controls
+                actually have anything selected at all. This should never be possible with
+                single selection control if they have one option already selected, but we'll
+                use the principle of "better safe than sorry".
+            </p>
+
+            <p>
+
+                Notice that the daylight saving time selection is checked by asking for the
+                selected options array (we're using a multiple selection control) and checking
+                if there is exactly one selected option. We don't care about the actual selected
+                option because we know that there's only one. Either it's selected or it's not.
+            </p>
+
+            <p>
+
+                We have now implemented two of the three steps we said were necessary to get the
+                settings view fully functional. Let's implement the final step: showing the view.
+                Showing the settings view is done by first updating the settings view controls
+                to reflect the current configuration, setting the right softkey to the state that
+                it should be in for the settings view and then asking the user interface manager
+                to show the settings view.
+            </p>
+
+<pre>
+
+// Displays the settings view.
+function showSettingsView() {
+    // update settings view controls to match current configuration
+    // the DST selected sets are either the options array with its single option or an empty array
+    
+    // home city and DST setting
+    homeCitySelection.setSelected(homeCitySelection.getOptionForValue(engine.getHomeCity()));
+    homeCityDSTSelection.setSelected(engine.getHomeCityDST() ? homeCityDSTSelection.getOptions() : []);
+    
+    // local city and DST setting
+    localCitySelection.setSelected(localCitySelection.getOptionForValue(engine.getLocalCity()));
+    localCityDSTSelection.setSelected(engine.getLocalCityDST() ? localCityDSTSelection.getOptions() : []);
+    
+    // temperature unit
+    temperatureUnitSelection.setSelected(temperatureUnitSelection.getOptionForValue(engine.getTemperatureUnit()));
+    
+    setSettingsViewSoftkeys();
+    uiManager.setView(settingsView);
+}
+</pre>
+
+            <p>
+
+                We're using the convenient getOptionForValue() function that all selection controls
+                have to retrieve the right option to select for each of the selection controls. The
+                way we handle the daylight saving time selection controls may seem a little strange
+                at first but remember that we only have a single option and thus if that option should
+                be selected then the selected options array is identical with the options array - both
+                are arrays containing the one and same option. Selection controls always copy the options
+                and selected options arrays when the setOptions() and setSelected() methods are called
+                so there is no problem in passing the options array as the selected options array.
+            </p>
+
+            <p>
+
+                The settings view is now completed. You can try it out and play with it, change the
+                settings, open it up again, change some more settings, cancel the changes, etc. to
+                verify that it's working as it should.
+            </p>
+
+            <div class="fignone" id="GUID-989D17CC-F019-46BB-B6F5-49166E258ACA__GUID-66154A40-DD96-45B3-BF53-01594426BA71"><a name="GUID-989D17CC-F019-46BB-B6F5-49166E258ACA__GUID-66154A40-DD96-45B3-BF53-01594426BA71"><!-- --></a><span class="figcap">Figure 2. 
+Travel Companion settings view</span>
+
+                
+                <br /><img src="Travel_Companion_Settings_Screenshot_1.png" /><br />
+            </div>
+
+        </div>
+
+    </div>
+
+<div>
+<div class="familylinks">
+<div class="parentlink"><strong>Parent topic:</strong> <a href="WRTKit_Travel_Companion_Tutorial-GUID-be79ba64-fa03-4968-964e-d7dcc42d7053.html">Travel Companion</a></div>
+</div>
+</div>
+
+</body>
+</html>
\ No newline at end of file