Rendering HTML Using the Brew uiOne Toolkit

Rendering HTML Using the Brew uiOne Toolkit

any applications require?or are enhanced by?the presentation of aptly named rich text, text in different formats interspersed with images and hyperlinks to other content. Whether the application is a pure content play, such as a news distribution application, or simply needs a bit of pizzazz in its on-line help, HTML is the answer. Easy to write and maintain (ether by hand or with a specialized editor), HTML gives you a great deal of flexibility in how you present text and graphic content. Brew has long had support for HTML using the IHTMLViewer, an IControl-based user interface; recently, this same capability was added to the Brew uiOne Toolkit, the user interface components accessible to C and C++ developers as part of the uiOne SDK. The htmlwidget, an optional widget bundled with the SDK and used by the uiOne trigplayer, can be used to display HTML in applications using the Brew uiOne Toolkit (BUIT) just as any other widget might. This provides unprecedented flexibility for developers with access to the toolkit (such as handset manufacturers and those writing pre-loaded applications for manufacturers) who need to present rich text from within their applications.

In this article, I’ll show you how to use the htmlwidget, summarizing the samplehtmlwidget example that ships with the Brew uiOne SDK. I present only the most relevant code, assuming you’re familiar both with the basics of BREW and using widgets and forms from the BUIT itself.

What the HtmlWidget Widget Provides
The htmlwidget provides a slimmed-down HTML viewer suitable for displaying the kinds of content users would expect within a mobile device. A viewer partially compliant with HTML 3.2, its presentation features include:

  • Hypertext links
  • Bold and large text
  • Text coloration
  • Text alignment on the horizontal axis (left, center, and right)
  • Inline images
  • Simple form input support for text input, lists, check boxes and radio buttons, and submit buttons.

Unsurprisingly, it does not support the following:

  • Scripting with Javascript or applets using Java
  • Frames
  • Cascading Style Sheets (CSS)
  • Background coloration
  • Most malformed HTML, such as HTML with mismatched or missing end tags.

Firefox it isn’t, but then again, that’s not the point; the point is to provide a lightweight widget that can render formatted text efficiently on a wide variety of devices.

The widget itself consists of three classes: the htmlwidget itself, the htmlviewmodel, and the docmodel. The purpose of the htmlwidget is obvious: it’s the widget that encapsulates drawing HTML content to the screen. But what about the other two? The htmlviewmodel is an IModel subclass that offers HTML-specific information about the textual contents of an htmlwidget, such as the state of a rendering operation or when the user selects a link to jump to a new page. The docmodel is an IModel subclass that encapsulates document-specific behaviors of a model, such as the ability to load content from an ISource, or track when the widget to which the model belongs is loading a new document.

Preparing to Use an Htmlwidget
As with all interfaces Brew, before you can use something, you need to create it:

  // html widget creation process  nErr = ISHELL_CreateInstance(me->pIShell, AEECLSID_HTMLWIDGET,                                (void**)&(me->piwHtml));  if (SUCCESS != nErr) {    return FALSE;  }  IWIDGET_SetBGColor(me->piwHtml, MAKE_RGBA(255,255,255,255));  we.width = (me->rcRoot).dx;  we.height = (me->rcRoot).dy;  IWIDGET_SetExtent(me->piwHtml,&we);  IWIDGET_SetIWeb(me->piwHtml, me->piWeb);  nErr = IWIDGET_GetModel(me->piwHtml, AEEIID_DOCMODEL, (IModel**)&(me->piDocModel));  if (SUCCESS != nErr) {    return FALSE;  }  nErr = IWIDGET_GetViewModel(me->piwHtml, (IModel**)&piModel);  if (SUCCESS != nErr) {    IMODEL_Release(piModel);    return FALSE;  }  nErr = IMODEL_QueryInterface(piModel, AEEIID_HTMLVIEWMODEL,                                (void**)&me->piViewModel);  if (SUCCESS != nErr) {    IMODEL_Release(piModel);    return FALSE;  }

After creating an instance of the HTML widget, the code sets its background color to white, and its bounds to the bounds of the application itself. Next, using IWIDGET_SetIWeb, it links the htmlwidget with a pre-allocated IWeb instance. The htmlwidget will use this IWeb instance to load any embedded content it requires, such as inline images. (By decoupling the htmlwidget from the IWeb instance, you’re better able to configure the IWeb instance on your own, setting options such as custom headers or other HTTP options.) Next, the code gets two models: the docmodel and the htmlviewmodel. The application uses the former to load text into the widget, while the application uses the latter to handle things such as user navigation within the HTML pages that the application presents. To do this, the code must also attach listeners to each of these models:

	LISTENER_Init(&(me->mlHtmlView), samplehtmlwidgetapp_htmlviewlistener, me);	LISTENER_Init(&(me->mlHtmlDoc), samplehtmlwidgetapp_htmldoclistener, me);	IDOCMODEL_AddListener(me->piDocModel, &(me->mlHtmlDoc));	IHTMLVIEWMODEL_AddListener(me->piViewModel, &(me->mlHtmlView));

(Of course, all of this will only work if you’ve included the htmlwidget module in your development environment; be sure to have the DLL and MIF file installed in your simulation environment, and if necessary, pre-load the MOD and MIF files to your handset under test before you begin. If you find your code fails to instantiate an htmlwidget, check your environment to be sure you have the appropriate MOD or DLL installed correctly!)

Once this is done, the htmlwidget can be inserted into a container or form just like any other widget.

Loading HTML into the Widget
The htmlwidget is pretty boring unless it has something to display. While with a regular text widget you can simply invoke IWIDGET_SetText and you’re good to go, the htmlwidget is more flexible: it can load its content directly from an ISource. This is important because both the file and Web interfaces can provide sources to content. (Brew’s file interface actually implements the IAStream interface, but you can convert from a stream to a source using the ISourceUtil interface, as you soon see here.) Loading content is handled through the docmodel, which may seem weird if you’re used to simpler widgets and how they load their content directly through the widget interface, until you consider that it’s really the model’s responsibility to store and manage the information used by the widget anyway! In fact, under the hood, methods like IWIDGET_SetText manipulate a widget’s model anyway; they’re just more convenient for simple widgets.

So, with the docmodel in hand, here’s how to load an HTML document from a file:

  piFile = IFILEMGR_OpenFile(me->piFileM, pszFilename,_OFM_READ);  nErr = IFILEMGR_GetLastError(me->piFileM);  nErr = ISOURCEUTIL_SourceFromAStream(me->piSourceUtil,                                        (IAStream*)piFile, &piSource);  if (NULL != piFile)  {    nErr = IDOCMODEL_LoadSource(me->piDocModel, piSource);  }  // release handles  RELEASEIF(piFile);  RELEASEIF(piSource);

Pretty simple, isn’t it? After opening the destination file, the code converts the stream interface provided by the file to a source interface, and then just hands the source off to the docmodel. In turn, the docmodel now owns the source (which owns the file), so to prevent memory leaks, the code releases both the file and the source.

Handling Callbacks from the HTMLWidget
A lot of the interactions with HTML are actually asynchronous?not just document loading and rendering, but user actions as well. Consequently, the two models provided by the htmlwidget invoke your listeners for a variety of reasons, including:

  1. When the document is reset (the docmodel sends an EVT_HDM_RESET event).
  2. When the document loading has stopped (the docmodel sends an EVT_HDM_STOP event).
  3. The first visible region has been rendered, including any embedded objects (the htmlviewmodel sends an EVT_HVM_PAGEDONE event).
  4. Rendering is complete (the htmlviewmodel sends an EVT_HVM_DONE event).
  5. The document, including embedded objects, is fully loaded (the htmlviewmodel sends an EVT_HVM_CONTENTDONE event).
  6. The user has selected a hyperlink (the htmlviewmodel sends an EVT_HVM_JUMP event).
  7. The user clicked the submit button on a form (the htmlviewmodel sends an EVT_HVM_SUBMIT event).
  8. Focus has changed between one item and another within the view (the htmlviewmodel sends an EVT_HVM_FOCUS event).
  9. The listeners, not surprisingly, consist of a series of if statements against these events. The docmodel‘s listener is the simplest; you could use this to update a UI to indicate when a network action is taking place, for example:

    void samplehtmlwidgetapp_htmldoclistener(samplehtmlwidgetapp* me, ModelEvent *pEv){  if (pEv->evCode == EVT_HDM_RESET) {  }  if (pEv->evCode == EVT_HDM_STOP) {  }  return;}

    The htmlviewmodel is more complex; but that’s because it must handle more events, although the sample code doesn’t handle many events, either:

    void samplehtmlwidgetapp_htmlviewlistener(samplehtmlwidgetapp* me, ModelEvent *pEv){    if (pEv->evCode == EVT_HVM_DONE) {  }  if (pEv->evCode == EVT_HVM_PAGEDONE) {  }  if (pEv->evCode == EVT_HVM_CONTENTDONE) {  }  if (pEv->evCode == EVT_HVM_JUMP) {    samplehtmlwidgetapp_loadhtmlfile(me, ((HtmlViewEvent *)pEv)->u.jump.pszURL);  }  if (pEv->evCode == EVT_HVM_SUBMIT) {  }  if (pEv->evCode == EVT_HVM_FOCUS) {  }  return;}

    The sample code only intercepts the EVT_HVM_JUMP event, and uses it to trigger the loading of a new file’s worth of HTML using the code you saw in the previous section. If you’re loading content from the network, it might be a good idea to use the EVT_HVM_CONTENTDONE method to deactivate a busy indicator, giving the user some indication of when loading starts and stops for a specific page. You can also handle form submissions using EVT_HVM_SUBMIT; the form data is encoded within the submit field of the event union, and has the following fields:

  • posElem is the position of the link that triggered the submit
  • pszURL is the URL to which the data should be sent
  • pszMethod is the HTTP method to be used when sending the data (e.g., “POST“)
  • pszData is the data to be sent if the pszMethod isn’t “GET“.

Using HTML and intercepting form input, you can build a fairly robust test application by intercepting form data for user input and processing it within your application.

Surf’s Up!
Well, not quite?as both the documentation for the htmlwidget and I point out in this article, the widget is designed for well-formed application-native content, not general surfing. Nonetheless, if you have a need for rich text display in your BUIT application, look no further than Brew’s htmlwidget.


Share the Post: