The BREW uiOne Toolkit (BUIT) is a robust user interface toolkit, with all kinds of goodies for the application developer. Eventually, however, the time comes when you find there’s simply no widget that precisely meets your needs. Typically, you find that what you’re missing falls into one of three categories:
- A widget needs additional event-handling behavior beyond what’s available.
- A widget needs to draw additional content whenever it draws.
- A widget needs to show more than one kind of information, such as several bitmaps and text at the same time.
Of course, you can create your own widget from scratch—and, thanks to the fact that the source to the BUIT is part of the distribution, it’s not nearly as much work as it would otherwise be, because you can see how widgets are supposed to behave, and even base your implementation on the existing implementation of a widget, something you can’t easily do with other BREW interface implementations. However, given that the vast majority of the time you really only need to tweak the behavior of a BREW UI widget to meet your requirements, why reinvent the wheel?
The BUIT provides two obvious ways to customize widget behavior: hooking the event handler and draw decorators. In addition, a third mechanism is available, but not immediately obvious: composition. By populating a container with widgets that meet the visual appearance you need, and returning a widget instance to the container, you can often create the appearance of complex user interface widgets without needing to write new widgets at all.
Hooking the Event Handler
Brew provides the HandlerDesc, a structure that lets you chain widget event handlers. In conjunction with the IHandler interface, an interface implemented by all widgets, it lets you hook a widget’s event handler and process an event destined for a specific widget before the widget receives the event. Because HandlerDescs operate as a chain of event handlers, more than one event handler can be active for a specific widget, and each event handler reserves the right as to whether or not to pass a specific event on to the next handler in the chain. Using one is easy. Here’s how:
- Write your custom event handling code in a function that conforms to the event-handling interface, taking the component, event code, and event arguments.
- After creating an instance of the component whose event handler you want to hook, create a HandlerDesc structure on the heap and initialize it with your custom event handler.
- Finally, link your event handler to the instance of your component by using its SetHandler method, inherited from IHandler.
- Within your custom event handling code, be sure to invoke HANDLERDESC_Call to chain to the next event handler in the chain any time you want to let the underlying event handler process an event (typically whenever you’re done processing events).
For more about the mechanics of using a HandlerDesc, see my previous article for Developer.
Exactly when you chain to the next event handler in the chain is typically the tricky part when using HandlerDescs to enhance a widget’s behavior. If you want to prevent an event from being processed, or translate an event of some kind into another event (that is, replace one action with another), first process the incoming event and then invoke HANDLERDESC_Call. If, on the other hand, you want to do additional processing after the default event handling, invoke HANDLERDESC_Call first, and then ignore its return value (which will be TRUE if the event handler handled the event) and perform any additional event handling you require for specific events.
Using Draw Decorators
Somewhat confusingly, the BUIT uses the notion of a decorator in two different ways. The first way, with which you’re likely familiar, is to refer to the decorator design pattern, about which you can read more at Wikipedia. A common use of decorators within the BUIT is when managing scroll bars or lists; these decorate a widget such as a text widget or list item widget responsible for drawing the scrolled content or individual items within a list.
A second, less-frequently encountered term is the draw decorator. I like to think of draw decorators as HandlerDescs for a widget’s draw function, because it operates in the same way: Within an IDrawDecorator, you can hook the draw function, letting you take over the drawing behavior for a specific widget. By decorating a widget with an IDrawDecorator widget, you have the opportunity to do any drawing you like whenever a widget is redrawn. The basic process is only slightly more complicated than using a HandlerDesc:
- Create the custom drawing function, which must implement the signature PFNDRAWHANDLER, taking a private context, the canvas for the decorator, and the coordinates of the decorator within the canvas as integers offset from the top left of the canvas.
- In your custom drawing function, be sure to call DRAWHANDLERDESC_Call to invoke the next drawing function in the chain of draw decorators.
- Create an instance of IDrawDecorator.
- Create and populate a DrawHandlerDesc on the heap that references your draw function and any private data.
- Link the DrawHandlerDesc to the IDrawDecorator using IDRAWDECORATOR_SetDraw.
- Decorate the widget whose drawing you want to override using IDRAWDECORATOR_SetWidget, assigning the widget to the draw decorator.
Of course, you can use draw decorators with handlerdescs, too. This is a great way to do things such as provide custom selection borders around widgets; the custom event handler monitors focus change events for the specific widget, while the draw decorator draws the additional selection cues over or around the decorated widget.
Composing a New Widget
Sometimes, what you need isn’t one widget, but many widgets acting in concert. For example, a to-do list may need to show an icon indicating a task’s status (done or undone), an icon indicating whether the task is late or not, as well as a summary of the task. List item widgets are a common place where this is necessary, but not the only place; custom text entry widgets for things like date and time entry can be simplified by this approach, too.
An obvious approach is the brute force approach: writing an entirely new widget that provides precisely the behavior you need. As I mention in the introduction, however, that’s still a significant amount of work, even with the sources to the BUIT in hand. It may be your only recourse, but if what you seek is a composition of widgets, why not just just create a container to hold the desired widgets instead? To do this:
- Create a data structure that holds all the context information your collection of widgets actually needs. For simplicity, it shouldn’t refer to the widgets in the composition unless you actually need to mutate them during your composed widget’s lifetime, because your container will hold your widget references and release them when it’s released.
- Write a constructor method that creates a container widget and the child widgets you require, along with your context structure. It should return the widget interface to the container widget you created to hold your child widgets. Your constructor should also register a HandlerDesc whose destruction function will destroy the objects in your context structure and the context structure itself.
That’s it! For the example I gave previously—an element in a to-do list that contains two icons and a text item—the widget would consist of a static text widget and two image widgets (or perhaps an image static widget and a second image widget), embedded in a container such as a proportional or xy container to control the layout of the child widgets. In fact, to see this trick in action, you need look only as far as the image static widget itself, which is really a static widget and an image widget held together by a proportional container! Take a look at the ImageStaticWidget.c file in a distribution of the BUIT near you.
To paraphrase an old saying, why build when you can hook, decorate, or compose? These three tricks are central features of the BUIT, and make it easy for you to customize the behavior of existing widgets, obviating the need for many a custom widget. The next time you look at a user interface specification in dismay, stop and ask: Can you modify an existing widget or create a collection of widgets that does the same thing?
About the Author
Ray Rischpater is the chief architect at Rocket Mobile, Inc., specializing in the design and development of messaging and information access applications for today’s wireless devices. He is the author of several books on software development, including eBay Application Development and Software Development for the QUALCOMM BREW Platform, both available from Apress, and is an active Amateur Radio operator. Contact Ray at ^40HandlerDescs41firstname.lastname@example.org.