dcsimg

Widget Wonderland for WebOS

Make your WebOS application look like the built-in applications with Widgets

Know that your application belongs

How do you know that your application belongs?

We’ve all seen them — applications that just don’t quite fit in with the rest of the applications on your mobile device.

You can easily pick out the applications written by the pros and the applications written by the hobbyists. To be fair, if you have written code for any period of time, you will most certainly have spent some time in both camps.

After all, not every project has the budget necessary for that rock-solid look and feel — and we don’t often get paid to go back and rewrite older applications just because a new widget came along or because down the road we are more knowledgable on how to best code an application.

That said, it does help when you have a feel for how to do something the “right” way the first time out of the gate. WebOS is new to everyone — even the folks at Palm — so let’s explore WebOS by adding some new widgets to an application we started a couple of weeks back which is described in this column.

In case you didn’t see the earlier project, we have a simple WebOS application which displays a basketball team roster in a List widget. We will expand the project by adding a number of features, some of which we’ll tackle in this article and then leave the others for next time. Here are the expanded features of our application:

  • Add CommandMenu buttons
  • Adding a new player via a second Scene
  • Using a TextField Widget
  • Using a ListSelector Widget
  • Using a IntegerPicker Widget
  • Sorting the List Widget
  • Persisting the List data to a Depot

In addition to working with these Palm WebOS widgets, we’ll also get a feel for some WebOS design patterns such as pushing scenes and using callback functions. Note, the source code to the application is hosted at the Linux Magazine Mobile code hosting project.

Command Menu

If you have seen the built-in WebOS applications, you may have noticed some sharp-looking buttons across the bottom of the screen as shown in the image below, taken from the Google Maps application.

commandmenu.png

While virtually all of the user interface elements of a WebOS application are defined in HTML and “skinned” with CSS, the CommandMenu is actually created entirely in Javascript code. In our application, we are adding two CommandMenu buttons: ‘Add Player’ and ‘Reset Roster’. The Add Player button causes the application to display a new scene for capturing the details for a new player. The Reset Roster button causes the list of players to be set back to the “starting 5″ after the user has perhaps added players or maybe even deleted some with the “swipe” gesture. Here is a screen shot of the application showing the two new Command Menu buttons at the bottom.

listfuncommandmenu.png

Here is the code for defining these two buttons.

this.cmdMenuModel = {
   items: [
      {label:'Add Player', command:'newplayer'},
      {label:'Reset Roster', command:'reset'}
   ]};

this.controller.setupWidget(Mojo.Menu.commandMenu, {}, this.cmdMenuModel);

The CommandMenu is setup with an array of items, one entry for each button. Each item needs to have a command property and either a label or icon. There are a handful of pre-packaged icons for commonly used commands like “Add”, which is a big plus sign. Because we had a label containing “Reset Roster” that didn’t have a clear iconic representation, we went with text labels for both of the buttons. It is a good practice to keep all of the buttons consistent and not mix-n-match icons and labels.

The application needs to have a means to process the commands when a button or menu is selected. To do this, we implement a handleCommand routine, as shown here.

HomeAssistant.prototype.handleCommand = function (event) {
	if (event.type == Mojo.Event.command) {
		if (event.command == "reset") {
                        /*implement reset command here */
			this.sortedby.value = 'name';
			this.controller.modelChanged(this.sortedby,this);
			this.listModel.items = this.originalRoster.items.slice(0);
			this.listModel.items.sort(this.mySortFunc);
			this.controller.modelChanged(this.listModel, this);
			return;
		}
		if (event.command == "newplayer") {
                        /* implement new player code here */
			var p = new Player();
			this.controller.stageController.pushScene('new-player',p,this.handleNewPlayer.bind(this));

			return;
		}
	}
}

The function receives an event object. If the event is a command, it is evaluated and dispatched appropriately.

Handling commands and making a scene

Let’s start by looking at the code to handle the “Add Player” command. When we add a new player, we need to display a new scene to capture the required information. But even prior to doing that we need to know what data elements make up a “Player”. To accomplish this, we’ve added a new file called Player.js and added it to the “model” folder in our project as shown below. The term “model” comes from the Model-View-Controller world. While this pattern does not match 100% with Palm WebOS, it does make sense to put “object definitions” in a central area of an application. For us, that means the model folder. You are free to put the file wherever in the project it makes sense for you to do so.

project.png

This code is very simple — we set every player to default to being a Guard with a number of 5. Obviously this is just an arbitrary set of values. Many times classes will have more sophisticated intialization.

var Player = Class.create({

	initialize: function(){

		this.name = '';
		this.position = 'Guard';
		this.number = 5;
	}
});

Note that if you add a new file to a WebOS application, you will not get the benefit of it — also known as pulling your hair out when your application doesn’t work as expected — until you add the javascript file reference to the file named sources.json which is in the root of your project. sources.json is used when the application is packaged up. If the file is not referenced here, it does not make it to the device!

[
    {"source": "app\/model\/player.js"},
	{"source": "app\/assistants\/stage-assistant.js"},
    {
        "source": "app\/assistants\/home-assistant.js",
        "scenes": "home"
    },
    {
        "source": "app\/assistants\/new-player-assistant.js",
        "scenes": "new-player"
    }
]

A common Palm WebOS design pattern is to pass an object to a scene. This approach allows the scene to act on the object and/or pass the object back via a callback function. Passing the object in to begin with makes future enhancements like editing an existing player a bit easier to implement. Note that the code first creates a new Player object and then “pushes” the scene entitled “new-player”. When a scene is pushed, it is displayed on the top of the stack of screens. Some folks in Palm land call this the deck of cards. Whatever you call it, your scene is now visible.

In addition to the scene name, there are two additional parameters we pass to the scene:

The first paramter is the Player object we just created. The second parameter is a binding to a function within our code which is used as a “callback”. Again, another design pattern to get used to — Palm WebOS is callback crazy. Get comfortable with it! The reason we need to mess with the “bind” syntax is that we want the instance of this function to be called back that is part of this scene object, not a class-wide function. This is like calling a membe function, not a “static” or class-level function in other environments.

Let’s have a quick look at the “new-player” scene to better understand how this application works.

Adding a new player

The new-player scene takes three pieces of input:

  1. Player Name
  2. Position
  3. Jersey Number

To gather this data, we are going to use three different kinds of widgets:

  1. TextField for the player’s Name
  2. ListSelector for the player’s Position
  3. IntegerPicker for the player’s Jersey Number

Each of these widgets is declared in the new-player.html file which is located in the /app/views/new-player folder.

<div class="palm-group">
	<div class="palm-group-title" style="text-align:center">Player Details</div>
	<div x-mojo-element="TextField" id="playerName"></div><br />
	<div x-mojo-element="ListSelector" id="playerPosition"></div><br />
	<div x-mojo-element="IntegerPicker" id="playerNumber"></div>
</div>

And here is a screen shot of the scene in action:

newplayerscene.png

It take a bit of code to get these widgets setup — though not too much so don’t worry.

function NewPlayerAssistant(player,callback) {
	this.player = player;
	this.callback = callback;
	Mojo.Log.info("new player!" + this.player.Name);
}

NewPlayerAssistant.prototype.setup = function() {

	this.controller.setupWidget("playerName",
	{
		hintText:'enter player name',
		multiline: false,
		focus: true,
		modelProperty: 'name',
		label : 'Name'
	},
	this.player
	);

	this.controller.setupWidget("playerPosition",
			{
				choices: [{label:"Guard",value:"Guard"},
				          {label:"Forward",value:"Forward"},
						  {label:"Center",value:"Center"}
				          ],
				modelProperty: 'position',
				label:'Position',
				labelPlacement: Mojo.Widget.labelPlacementLeft
			},
			this.player
		);

	this.controller.setupWidget("playerNumber",
		{
			modelProperty: 'number',
			label:'Jersey Number',
			min:0,
			max:55
		},
		this.player
	);

}

NewPlayerAssistant.prototype.cleanup = function(event) {
	this.callback(this.player);
}

Breaking down the code

The parameters of player and callback are passed into the scene. We save these references off and store them in this.player and this.callback respectively. For fun, we also send a message to the Log. OK, it’s not for fun — it is a sanity check that things are wired up the way we hope them to be. Palm WebOS is currently pretty short on debugging tools, so using the Log is an important skill, no matter how pedestrian it may appear to be.

What is of real interest to us is the setup function. Here we see each of the three widgets being configured with a call to this.controller.setupWidget. The general syntax for this function includes:

  • The object’s id as defined in the html file associated with this scene.
  • An object representing various attributes to specify the specific behavior of this widget. For example we indicate that we do not want the player name TextField to be multiline. Additionally, we set the hint text to provide some guidance for the user. For the ListSelector, we provide an array of choices to be displayed along with a data model for storing the current value. And for the IntegerPicker, we tell it what range of integers to accept, from 0 – 55.
  • Lastly, we include a data model. This is data that is automatically updated when the user interfaces with the data.

Once we have finished making our entries and hit the “back” gesture, we signal the calling scene that we’re done editing this new player. This is accomplished in the “cleanup” function by invoking the caller-provided callback function, passing in the reference to the player. The new-player scene is now “popped” off of the display and our home scene is again visible. Let’s jump back to the HomeAssistant.js file to see what happens when our callback is invoked with the new player!

Heading Home

Our new player has been created — AC Green, a tremendously talented forward who played for a number of NBA teams. That’s great, we added AC — but what do we do with him? We add him to the list of course!

HomeAssistant.prototype.handleNewPlayer = function(player) {
	Mojo.Log.info("handleNewPlayer: " + player.name + "," + player.position + "," + player.number);
	this.listModel.items.push(player);
	this.listModel.items.sort(this.mySortFunc);
	this.controller.modelChanged(this.listModel,this);
}

Let’s step through this function:

The function takes a single parameter: player. This is the same player object that was passed to the new-player scene previously — and the same player object that was sent via the callback function.

As a sanity check, I like to dump the new object’s info to the Log file. Over time these kinds of calls to the log typically get removed when we’re confident that everything is working to our satisfaction.

Next, this player is “pushed” into the list of items which holds our roster of players.

Next, the list is sorted — we cover that in the next column, so look for the next article to see how that works.

Lastly, we notify any widgets that are using the “this.listModel” data as the their model data. Once the List Widget is notified of the changed data, it re-draws itself as shown below.

acgreen.png

OK, so now we’ve added a new player to our team. Let’s say things didn’t work out with salary negotiations with AC, so we want to revert to our starting line-up. We can either swipe-to-delete AC’s entry in the list or we can hit the “Reset Roster” button at the bottom of the screen. When we hit the reset button we want the list to revert to our original roster. This is a very simple operation.

We need to get a copy of the original roster, which we can do with a call to the array function slice.

this.listModel.items = this.originalRoster.items.slice(0);

This makes a copy of the originalRoster.items array and stores it into the listModel.items array. If we were to simply do an assignment, such as listModel.items = originalRoster.items, we would wind up with two references to the same array. We need to use the “slice” function because we want a copy, not a reference. Yes, I learned that the hard way, thank you.

Of course, just updating the data alone does not make the List widget reflect the changes — we need to notify the List that the data has changed. We do this with the modelChanged() function:

this.controller.modelChanged(this.listModel, this);

Now any widgets keeping tabs on that data model will be notified of the change and refresh their view of the data.

Wrap Up

We have added a new scene to the application which allows us to include new players to the roster. Along the way we learned a bit about the TextField, ListSelector and IntegerPicker widgets. We also learned how data is passed between scenes. Of course, there are other design patterns to this environment and arguably one of the most frustrating (and powerful) aspects of the Palm WebOS is that there is so much flexibility with which to tackle a problem — at times you’re left wondering which way is the “right” way. Over time we will get a clearer picture of those patterns as the experience level of the developer community rises. Next time, we’ll finish up this application with by adding list sorting and saving data to a Depot.

Comments on "Widget Wonderland for WebOS"

I keep listening to the news update talk about getting free online grant applications so I have been looking around for the top site to get one. Could you advise me please, where could i get some?

Reply

Your article is great! Your viewpoints are valid and werenreplesl-ted. This is useful and interesting data that I can appreciate. You are obviously knowledgeable in this area. You kept this content smart and very engaging.

Reply

even if the other guest didn’t. David disappointed me in the position he took also. People are getting the money from somewhere to buy for Christmas. From what I hear from people that I work with they have gone back to their credit cards. Most are hoping overtime in the new year will help pay for this. This will show up on the credit card usage reports in a couple of months. Stay on track because it does my heart good to see someone younger than I feel the same waekhTan.s,Kyvin Cunningham http://ulqukopxbmt.com [url=http://twhptzowa.com]twhptzowa[/url] [link=http://lpuhrfpfd.com]lpuhrfpfd[/link]

Reply

inevitably cause http://autoinsurancersr.top unless card accounts http://autoinsurancemaw.info mortgage after multiple quotes http://autoinsurancegl.net teachers accidents http://cheapcarinsurancefc.top larger package digital assistants http://carinsurancelit.top want beyond save much http://autoinsurancenir.top ask think twice http://autoinsurancebit.net needs

Reply

every http://autoinsurancebit.net extremely legal insurance http://carinsurancemr.net happy medical coverage http://carinsurancert.top low ever needed http://autoinsurancequotesem.us says until after http://safeinauto.com excess been paying http://carinsuranceast.us insurance policy companies http://carinsurancelit.top agent obtaining http://carinsuranceratescto.info sue happy

Reply

low risk http://cheapcarinsurancefc.top injury great lawyers http://carinsuranceratescto.info process receive http://autoinsurancegl.net agent even thousands http://autoinsurancersr.top time failing particular requirements http://cheapcarinsurancecr.top covers many only use http://autoinsurancequotesem.us good driving

Reply

companies offer http://autoinsurancend.info found yourself little problem http://cheapcarinsurancecr.top make yourself provide equal http://carinsurancelit.top vehicle law http://carinsuranceast.us company car crash http://cheapcarinsurancefc.top very important risk makes http://autoinsurancequotesem.us credit much money http://autoinsuranceweb.top would

Reply

accident happens http://carinsuranceratescto.info good driver early http://carinsurancequotessc.top could most efficient http://cheapcarinsurancefc.top depending inspire them http://cheapautoinsurancewcx.info types internet http://autoinsurancegl.net know about vehicle while http://safeinauto.com ticket big http://autoinsurancenir.top more companies insurance http://autoinsurancequotesro.info traffic violations

Reply

analyze carefully http://autoinsurancemaw.info owners policies insurance http://carinsurancemr.net automatically deposited want http://autoinsurancersr.top natasha richardson automobile insurance http://carinsurancequotessc.top involved son only http://carscoverageonline.com policies find insurance http://carinsurancerut.info wrong hands found http://safeinauto.com insurance rates carefully consider http://cheapcarinsurancecr.top nicey nicey

Reply

more unsafe http://cheapcarinsurancefc.top car pooling utilize http://carinsurancemr.net more thing insurance companies http://carinsurancelit.top pick-up vans old either http://cheapcarinsurancecr.top problems encourages natasha richardson http://autoinsurancersr.top generate compare auto http://autoinsurancend.info go

Reply

give http://cheapcarinsurancecr.top old either wonderful time http://carscoverageonline.com insurance specialists certain guidelines http://carinsurancemr.net particular company decision http://carinsuranceratescto.info insurance application relevant companies http://carinsurancert.top avenue marketing someone who http://autoinsurancequotesro.info shopping having information about http://autoinsurancemaw.info quotes

Reply

rates jump http://cheapautoinsurancewcx.info many positive health histories http://autoinsurancemaw.info give find http://safeinauto.com start looking coverage http://autoinsurancequotesem.us every just email http://autoinsuranceweb.top financially responsible

Reply

only http://autoinsuranceweb.top binding location automobile http://carscoverageonline.com using circumstances dictate http://carinsurancert.top expense projections budget plan http://carinsuranceast.us insurance

Reply

kph elsewhere http://cheapautoinsurancewcx.info right better according http://autoinsurancemaw.info good driver first call http://cheapcarinsurancecr.top today hectic insurance http://safeinauto.com bad decisions business owner http://autoinsurancersr.top insurance coverage http://carscoverageonline.com direct party under http://carinsuranceast.us insured other line numbers http://autoinsurancend.info insurer agrees

Reply

reduced care http://carscoverageonline.com insurance specialists chosen http://autoinsuranceweb.top inexpensive any http://carinsurancequotessc.top how price http://autoinsurancenir.top right insurance riskiest drivers http://autoinsurancemaw.info like die-hard brochure http://carinsuranceast.us because

Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>