Sinking/bubbling event dispatching

"How it works?"

Screenshot for illustration purposes:

Window with editbox

Let's speak about that editbox with “Hello world” text in it.

This editbox is placed in following containers:

  • Frame (Frame!(Splitter) is a Window (HWND in Win32)
    • Splitter (Frame's view)
      • Tabs (right splitter panel)
        • HtmlView? (current tab on the Tabs)
          • EditBox? (our edit box)

As you may see this EditBox? is pretty deep in parent/child hierarchy of widgets.

Now imagine that this editbox has input focus - so it will receive keyboard events (UP, DOWN, CHAR). Harmonia receives all OS events (WM_*** messages) in HarmoniaWindowProc (ui/native/win32window.d) which directly calls platform independent

handleKeyEvent(Window w, uint type, uint key, uint modifiers)

See: ui/window.d

HandleKeyEvent prepares EventKey objects and does call of

traverseEvent( EventKey e, Widget target /*focus widget*/ );

traverseEvent

traverseEvent does dispatching of the event in two phases - sinking and bubbling:

SINKING:

  1. set flag SINKING to the dispatching event type field.
  2. call each widget in parent-child chain of the target in

direction from parent to child down to the target.

So in our case sequence of calls in sinking phase will look like:

Application.on(EventKey evt) // "Ground"
Frame.on(EventKey evt)
Splitter.on(EventKey evt)
Tabs.on(EventKey evt)
HtmlView.on(EventKey evt)
EditBox.on(EventKey evt)

signature of 'on' method of the widget is:

bool on(EventKey evt) { return false; } 
// return true to stop traversing

BUBBLING:

  1. clear flag SINKING in the dispatching event type field.
  2. call each widget in parent-child chain of the target in

direction from child to parent starting from the target.

EditBox.on(EventKey evt)
HtmlView.on(EventKey evt)
Tabs.on(EventKey evt)
Splitter.on(EventKey evt)
Frame.on(EventKey evt)
Application.on(EventKey evt) // "Ground" 

Any widget in the chain and Application can stop event propagation by returning “true” (handled!) from correspondent call of “on” handler of the widget.

Thus all containers of the widget will know that

  • some particular widget inside is about to process given keyboard event (sinking) and
  • and no one widget inside has consumed the event (bubbling).

As we may see on example of EventKey handling we don't need any special mechanisms like listeners or SIGNAL/SLOT to be able to handle events. In 99.99% of GUI cases container is the object most interested in listening what kind of events its children are handling and how.

All other types of events (see: /ui/events.d) EventPointer, EventKey, EventWidget and EventCommand are handled in the same way.

EventPointer has little bit different handling as it also does translation of coordinates of the event and synthesizes ENTER/LEAVE events.

So complex event dispatching happens without any additional heavyweight structures like [multi]listetners or signal/slots.


Personal Tools