digitalmars.D.dwt - Sinking/bubbling event dispatching. Part I.
- Andrew Fedoniouk (84/84) Feb 08 2006 Sinking/bubbling event dispatching.
- Tom S (9/10) Feb 08 2006 So far, so good. I'm waiting for the second part :)
- Bruno Medeiros (8/13) Feb 10 2006 For those of us who arent't that GUI-knowledgeable (I am only
- Sean Kelly (5/15) Feb 10 2006 That's a design pattern popularized by Qt:
- Andrew Fedoniouk (42/48) Feb 10 2006 Yep. And more:
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
Feb 08 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 +/
Feb 08 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."
Feb 10 2006
Bruno Medeiros wrote:Andrew Fedoniouk wrote:That's a design pattern popularized by Qt: http://en.wikipedia.org/wiki/Signals_and_slots It's basically a generic event passing mechanism. SeanAs 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?
Feb 10 2006
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.comFor 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.
Feb 10 2006