Back to the     section ...

Managing events in DynaWorks

How events worked the old way...

Event handling is a very basic requirement for all GUI applications that process user interaction. Until now the event handling in DynaWorks had two different roots that didn't fit together too well: Splotlet-oriented event callbacks and its own ActionEvent handling in a Page class.

Application objects were derived from Spotlet, so the user initiated events (pen and key events) are send to the callback methods in the Application object. The Application object dispatches the event callbacks to the currently active Page; the corresponding Page itself dispatches the event further to one of its UserWidgets where the event is processed and an ActionEvent object is returned as a result to the calling (or better dispatching) Page.

The Page then passes the returned ActionEvent to the handleEvent() method, that is normally overridden in classes derived from Page. (handleEvent() is the method where most of the controller logic is implemented...).

After this processing the thread returns to the Spotlet instance. The following diagram shows a "compound" event flow between the involved classes:

Without knowing the internal details (I just hadn't found time to look into the KVM source) I think that the PalmOS event loop and the user written event handler code are all running in the same thread - something not very desirable because you are blocking the message loop for quite a long period of time.

This originates in the Spotlet design, not to receive an event message but to provide callback routines for event handling. While this behaviour might still be O.K. for KVM applications, it is a real problem for DynaWorks applications: Unlike KVM apps they do have an application thread that is running in the perform() method of the Page class. Any influence (posting events or peeking for events) on the event processing from this application thread is impossible.

So the event system was one of the points I worked on, and with the release 2.0 of DynaWorks the event handling has changed dramatically (for better, I hope) and is now much easier to use and to understand:

Back to top of page ...

The new style...

The most important class in the new event system is EventQueue. An EventQueue is a first-in, first-out (FIFO) stack of events. Any other class can push events onto the stack or pop events from the stack - from any thread running in the same VM! (EventQueue is completely thread safe)

This allows a complete new design of event handling in DynaWorks and the basic flow of things looks like this:

  1. The Application objects creates its own EventQueue object and registers this EventQueue with the Environment. The Environment runs a PalmOS message loop in its own thread; any messages received from the PalmOS interface are transformed to Event objects and pushed onto the registered EventQueue.

  2. The Application passes its EventQueue object to the Page as an argument to the perform() method. So the running Page object can retreive events from the queue and pass Events to the UserWidgets for processing. The resulting ActionEvents are passed to the handleEvent() method.

  3. ActionEvents from the EventQueue are send directly to the controller logic via the handleEvent() method (ActionEvent is derived from Event can can therefore be pushed to and popped from an EventQueue).

This design is quite flexible. The mediating class EventQueue is not bound in any ways to EventProducers or EventConsumers; it is a stand-alone object that can be used by these two other classes (roles). And of cource there can be more than one EventQueue in an application.

Back to top of page ...

Creative usage...

... implementing a modal UserWidget

The EventQueue concept allows some interesting things on the GUI side that where hard to achive in the old design. As an example for such a feature let's have a look on how to implement a modal UserWidget:

Most UserWidgets receive an 'event' and respond with an ActionEvent in one step: The whole 'transaction' is completed with one user interaction.

A modal UserWidget on the other side can require more than one user interaction to complete a 'transaction' - think of a ComboBox: You expand the selection list with one pen event, but you need another pen event to select an entry in the list. Between the two events the ComboBox needs the event focus to make sure it receives the next event first. This is not necessarily the case if the next event is preprocessed by the Page! So a ComboBox is derived from ModalWidget (a class, not an interface). If the penDown() method detects that the expand button is pressed, it runs a modal loop that uses its own EventQueue to catch the events:

ComboBox.penDown() {
  :
  if (hit (EXPAND_ICON, x, y) {
    // take over control.
    return runModal (MODE_EXPANDED);
  }
  :
}

ComboBox.setMode(int mode) {
  :
  // draw the control corresponding
  // to mode and set inner state.
  :
}
ModalWidget.runModal (int mode) {
  :
  setMode (mode);

  // establish our own MessageQueue.
  queue = new MessageQueue();
  MessageQueue lastq = Environment.register (queue);
  for (;;) {
    Thread.yield ();

    // get next event in the queue.
    Event evt = queue.pop (false);
            
    // if we have an input event to process, ...
    if (evt != null) {
      // ... dispatch it
      result = dispatchEvent (evt);
      // break on ActionEvent result.
      if (result != null)
        break;
  }
  Environment.register (lastq);

  return result;
}

By using a new EventQueue object a ModalWidget can "catch" all events from the user and can provide the needed modal behaviour.

Back to top of page ...


Copyright © 2000, Bernd R. Fix. All Rights Reserved.