Toolbox PostFilters
Rik Griffin (98) 264 posts |
The problem: Toolbox gadgets that uses nested window objects (TextArea, ScrollList, ScrollBar, Tabs, TreeView) don’t return the correct fields in the IDBlock on a Wimp event. You might say that such a gadget should handle all Wimp events itself, but it’s not unreasonable, for example, for an application to want to see KeyPressed events in a TextArea. Details: The Toolbox installs a Wimp PostFilter to handle Wimp events on Toolbox objects. When an event is intercepted, it’s offered to Toolbox modules that have registered an interest. Modules can register an interest for events on a certain object class (eg Window) or events on all object classes. The toolbox docs say that interest for Wimp events should be registered with for all object classes (ie with “0” in the class field in the list of events passed to Toolbox_RegisterPostFilter). Say you have a TextArea in a Window. The TextArea itself is a window, remember. The caret is in the TextArea and the user presses a key. Both the TextArea and Window modules have registered an interest in this event. The toolbox goes through its list of PostFilter claimants looking for one interested in Wimp event 8 on object class 0 (all objects). Because of the way the list is stored internally, the TextArea gets the event first. It processes the event (ie adds a character to its text buffer), and updates the IDBlock’s self_id and self_component fields to the ID of its parent window and its own component ID respectively. (Note – this last stage doesn’t currently happen in TextArea but it will have to if the applcation is to receive an event with the correct self_id and self_component fields in the IDBlock). TextArea sets r0 to 1 on exit from its PostFilter routine, indicating that it’s updated the IDBlock. The toolbox then passes the event on to the Window module, which overwrites the IDBlock with what it regards as the correct values – ie self_id being the ID if the TextArea’s window, and self_component being -1. The Toolbox then inspects self_id and sees that it belongs to a Window object, so it offers the event to any modules who registered an interest in events specifically on Window objects. But in this example there are none. Then the event is passed to the client application. |
Rik Griffin (98) 264 posts |
This is turning into a long post. The upshot of this is that the client application sees an event on an object that it has no knowledge of (the TextArea’s internal Window object). There are two solutions that I have come up with. The first is the quick and possibly nasty one. By changing the order that the Toolbox stores its list of event claimants, we can cause the Window module to get the event first, followed by TextArea, which will set IDBlock to contain the “correct” values. This is what I implemented in RISC OS Ltd’s Toolbox about 10 years ago, when I first discovered this problem! What makes me uncomfortable about this is that the order of registration of event interests isn’t documented anywhere as being significant, and some potential future change to the underlying list implementation might break everything again. My second idea: This is slightly more complicated but (I believe) more robust. 1 – Gadgets that use nested windows register their PostFilter interest only for events on Window objects (rather than all object classes as is currently the case). 2 – Introduce a new r0 return code for a gadget’s PostFilter handler, whose meaning is “I’ve updated the IDBlock but don’t offer this event to any other toolbox modules”. Let’s call this return code “2” :) The result of this will be (continuing the above example): User presses a key, Toolbox intercepts event. Toolbox offers event to “all classes” claimants, which now is only the Window module. Window module updates the IDBlock and returns 1 in r0. Toolbox looks up self_id and see that it’s a Window object. Therefore, Toolbox offers the event to “window class” claimants, which is now the TextArea module. TextArea updates the IDBlock and returns 2 in r0. Toolbox passes event to the client applcation. The new return code is needed because otherwise the Toolbox would offer the event again to all “window class” claimants, resulting in the TextArea getting the event twice (it would be an infinite loop only the Toolbox stops if the IDBlock doesn’t change). If you followed all that, comments are welcomed! |