Posts Tagged ‘mojo’

webOS list modelChanged bug

I ran into a bug with the webOS mojo framework a while ago, which I nailed down thanks to this forum post. Simply put, calling this.controller.modelChanged on a list doesn’t work properly if the user has scrolled down the list. With the dotspott webOS app I notice a gray rectangle popping up, obscuring the last 2 items in the list.

From pacemkr,

Calling this.controller.modelChanged on a list that has been scrolled past renderLimit will blank out the screen and not show the changes. This happens because modelChanged handler in the List widget resets renderOffset to 0. In other words the list widget assumes that the user is at the top of the list when modelChanged event happens. This is a pretty nasty bug.

The solution is to use mojo.setLengthAndInvalidate() instead:

listElement.mojo.setLengthAndInvalidate(this.listModel.items.length);

While Mojo has been superseded by Enyo, this is still a pretty big deal, there’s still lots of Pixi and Pre models out there with webOS 1.4.5.

Mojo.Widget.ImageView

Using the ImageView widget is somewhat perplexing. The documentation states:

This widget is designed to view an image full screen with support for zooming and panning…

You would assume that you could simply create the ImageView widget and it would be full screen and handle orientation changes (something very essential and common when viewing photos) out of the box. Unfortunately, this is not the case and some additional code is necessary to get it to function as such.

dotspott photo view

I’ll present some code here, but much of what I did was based on the code at webOS 101.

First, a few additions to the assistant’s activate function:

  • We setup full-screen mode
  • Set “free” orientation (allows the system to rotate the display)
  • Manually size the ImageView widget (id=photoView)
ViewPhotoAssistant.prototype.activate = function(event) {

    
this.controller.enableFullScreenMode(true);
    
this.controller.stageController.setWindowOrientation('free');
$(
'photoView').mojo.manualSize(Mojo.Environment.DeviceInfo.screenWidth, Mojo.Environment.DeviceInfo.screenHeight);

    
/* set images here */
    
};

To actually respond to orientation changes we need to setup an event listener for the “resize” event. This is dealt with in the assistant’s setup function:

ViewPhotoAssistant.prototype.setup = function() {
    
    
var photoViewerAttributes = { noExtractFS: true };
var photoViewerModel = {};

    
this.controller.setupWidget('photoView', photoViewerAttributes, photoViewerModel);
    
    
// setup handler for resize event ...
    
this.handleWindowResizeHandler = this.handleWindowResize.bindAsEventListener(this);
    
this.controller.listen(this.controller.window, 'resize', this.handleWindowResizeHandler);

/* other setup code... */

};

Within the resize event handler, we manually resize the ImageView widget based on the new dimensions of the window:

ViewPhotoAssistant.prototype.handleWindowResize = function (event){
if ($('photoView') && $('photoView').mojo) {
        $(
'photoView').mojo.manualSize(this.controller.window.innerWidth, this.controller.window.innerHeight);
}
}

Finally, in the assistant’s cleanup function, we stop listening on the event:

ViewPhotoAssistant.prototype.cleanup = function(event) {

    
this.controller.stopListening(this.controller.window, 'resize', this.handleWindowResizeHandler);    

};

Changing the model of activity buttons on webOS

This little issue drove me insane: you can’t change the model on an activity button after it’s been activated.

So, first, a little background. webOS/Mojo has widgets and one type of widget is a button. Setting up a button requires attributes (to set type – default or activity button) and a model (to set the label, disable/enable, and associated CSS class). A type of Mojo.Widget.activityButton turns the button into an activity button, which adds a spinner (preloader) to the button when the Mojo.Widget.Button.activate() method is called. The Mojo framework also allows altering the model of any widget and calling the Mojo.Controller.SceneController.modelChanged() method to commit the updates.

Here’s some code to show how all of this pieces together. This code is correct and functions as expected.

function setupSignInButton() { var btnModel = { buttonClass: "affirmative", label : "Sign in", disabled: false } var btnAttributes = { type: Mojo.Widget.activityButton } ctrlr.setupWidget("signin-button", btnAttributes, btnModel); Mojo.Event.listen(ctrlr.get('signin-button'), Mojo.Event.tap, function() { btnModel.disabled = true; btnModel.label = "Signing in..."; ctrlr.modelChanged(btnModel); ctrlr.get('signin-button').activate(); }); }

Note, ctrlr is a reference to the scene controller (Mojo.Controller.SceneController).

When the button is tapped, the spinner is activated, the button is disabled, and the label for the button is changed to “Signing in…”, as shown below.

webOS activity button, signing in

The problem is, if you move the call to activate { ctrlr.get(‘signin-button’).activate(); } above the call to change the model { ctrlr.modelChanged(btnModel); }, the model change does not occur. So you have an activated button without the model change, as shown below.

webOS activity button

Note, deactivating the button and attempting the model change does not work.

Edit: After playing with the code a bit more, turns out there’s no need to call activate(), the activity button is activated automatically when the button is tapped. Also, the issue I mentioned above seems to all stem from the call to activate(). It seems no code after the activate() call is executed (I attempted to show an alert dialog) and very much looks like an issue where the activate() call is blocking.

Edit 2: Hmmm, turns out nothing was being blocked, but an uncaught exception was being thrown. If you call a method for a widget as I was doing above, something like the following is thrown:

TypeError: Object# has no method 'activate'

To call a method, you need to put ‘.mojo’ before the method name, so:

ctrlr.get('signin-button').mojo.activate();