Thread overview
Sinking/bubbling event dispatching. Part I.
Feb 09, 2006
Andrew Fedoniouk
Feb 09, 2006
Tom S
Feb 10, 2006
Bruno Medeiros
Feb 10, 2006
Sean Kelly
Feb 10, 2006
Andrew Fedoniouk
February 09, 2006
Sinking/bubbling event dispatching.

"How it works?"

I will use this screenshot for illustration purposes: http://www.terrainformatica.com/harmonia/screenshots/harmonia.png and source at %harmonia%/samples/testbed/testbed.d

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 specific>

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 traverseEvent( EventKey e, Widget target /*focus widget*/ );

</harmonia specific>

What does traverseEvent?

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

SINKING:

a) set flag SINKING to the dispatching event type field.
b) 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)

<details>
signature of 'on' method of the widget is:
bool on(EventKey evt) { return false; } // return true to stop traversing
</details>

BUBBLING:
c) clear flag SINKING in the dispatching event type field.
b) 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
a) some particular widget inside is about to process
given keyboard event (sinking) and
b) will know that 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 that guy who
would like to "listen" what kind of keys its child handles
and it will receive them anyway.

All other types of events
(see: %harmonia%/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/slot with additional preprocessor (cool, but...).

"Hey, what's the deal? Why it is *so* good?"

(answer will be in next message)

(let me know if something is unclear here)

Andrew Fedoniouk.
http://terrainformatica.com



February 09, 2006
Andrew Fedoniouk wrote:
> (let me know if something is unclear here)

So far, so good. I'm waiting for the second part :)



-- 
-----BEGIN GEEK CODE BLOCK-----
Version: 3.1
GCS/M d-pu s+: a-->----- C+++$>++++ UL P+ L+ E--- W++ N++ o? K? w++ !O !M V? PS- PE- Y PGP t 5 X? R tv-- b DI- D+ G e>+++ h>++ !r !y
------END GEEK CODE BLOCK------

Tomasz Stachowiak  /+ a.k.a. h3r3tic +/
February 10, 2006
Andrew Fedoniouk wrote:
> 
> 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.

For those of us who arent't that GUI-knowledgeable (I am only experienced with .NET Forms), what are those mechanisms listeners and SIGNAL/SLOT?

-- 
Bruno Medeiros - CS/E student
"Certain aspects of D are a pathway to many abilities some consider to be... unnatural."
February 10, 2006
Bruno Medeiros wrote:
> Andrew Fedoniouk wrote:
>>
>> 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.
> 
> For those of us who arent't that GUI-knowledgeable (I am only experienced with .NET Forms), what are those mechanisms listeners and SIGNAL/SLOT?

That's a design pattern popularized by Qt:

http://en.wikipedia.org/wiki/Signals_and_slots

It's basically a generic event passing mechanism.


Sean
February 10, 2006
>>
>> For those of us who arent't that GUI-knowledgeable (I am only experienced with .NET Forms), what are those mechanisms listeners and SIGNAL/SLOT?
>
> That's a design pattern popularized by Qt:
>
> http://en.wikipedia.org/wiki/Signals_and_slots
>
> It's basically a generic event passing mechanism.
>

Yep. And more:

SIGNAL/SLOT in QT as Listeners in Java are implementations of the http://en.wikipedia.org/wiki/Observer_pattern

in D equivalent implementation is:
-------------------------------------------
class Observable
{
    void delegate(Observable o) slot;
    void doSignal() {  if( slot isnot null ) slot(this); }

    void someUICode()
    {
        .....
        if(  somethingChanged ) doSignal();
    }
}

Observable somethingInteresting = .....;

class Observer
{
    this() {   somethingInteresting.slot = &this.signal; }
    void signal(Observable o) {  msgbox( " Got signal from
somethingInteresting" );   }
}

EOF ----------------------------------------

In real life slot is a list of delegates to allow multiple Observers to be
connected to
the same slot (event)

Drawback here is simple: each instance of Observable shall have slots
for all types of events up front. Even if no one will be interested in this
particular
observerable it will take some memory - just in case.

As I said before - in real GUI life some container (owner) of the widget
will most
probably be an observer of the widget (observable).

Another task here: container pretty frequently wants to be also a controller
of
observable - it will want to manage execution of events by its child.
E.g. dialog box may want to process Tab key press before it
will be processed by any child and do not dispatch it to children at all.

Key point:
sinking/bubbling implements both patterns and practically free of charge.


Andrew Fedoniouk.
http://terrainformatica.com