--- /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 < 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