BlockEvents
Introduction
BlockEvents are persistent Items (with some non-persistent arguments) that are dispatched to one or more Blocks in response to a user action and cause a handler method on the Block(s) to be called.
Consider the case of a MenuItem, for instance the Cut menu Item: when the Cut menu item is chosen, Chandler locates the BlockEvent referenced by the menu item's MenuItem Block. This BlockEvent has a number of attributes that determine how the BlockEvent is dispatched, i.e. how to find the Block(s) to handle the event and which methods to call on the Block(s).
In the discussion below, we'll explain the details of this process.
Dispatching Events to a Block by Reference or Name
BlockEvents have a dispatchEnum attribute that controls how the event is dispatched.
One of the simplest ways to dispatch the event is by setting it to
SendToBlockByReference, which sends the event to the block pointed to by the
destinationBlockReference attribute.
Often, you don't have a reference to the block that you need to dispatch it to. In this case you can use the dispatchEnum
SendToBlockByName and specify the name of the block in the
dispatchToBlockName attribute. Only named events associated with rendered blocks are considered in the name lookup.
Blocks have an optional
eventsForNamedLookup attribute which points to a list of events which are added to the list of named events when the block is rendered. When the block is unrendered these events are removed from the list of named events.
Dispatching FocusBubbleUp
It's often handy to send an event to whatever widget (or its block) that has the focus. For example the Cut, Copy, and Paste BlockEvents work this way.
The dispatchEnum
FocusBubbleUp accomplishes this: if the widget doesn't have a handler, the block is checked for a handler. If the block doesn't have a handler then the block's parent is checked for a handler. This process causes the event to bubble all the way up the tree, eventually reaching the MainView block, which has default handlers for most common events. If no handler is found, the event will not be handled and no error is generated.
By adding a handler for a FocusBubbleUp event to a block in your parcel, you can override the default behavior of the event.
Handler Method Names
If you don't specify a value for the
methodName attribute of the BlockEvent it will default to a value based on the event's
blockName attribute, adding the prefix "on" and the suffix "Event" to the
blockName string. For example, if the event's
blockName attribute is "Cut" the default methodName is "onCutEvent".
The default works well as long as you have a single event. When you have multiple events that need to call the same handler, you should explicitly set the
methodName attribute.
Event Boundaries and Broadcast Dispatch
The
eventBoundary attribute on a block divides the entire tree of blocks into subtrees.
Typically, a parcel contains a subtree of user interface where the root of the tree has the
eventBoundary attribute set. BranchPoint blocks that substitute new subtrees also have the
eventBoundary attribute set.
If you choose a block in the subtree and recursively follow all parents and children, without passing through blocks with their eventBoundary set, you would enumerate all the blocks in the subtree.
The dispatchEnum
BroadcastInsideMyEventBoundary dispatches the event to the block that posted it and all the blocks contained in its subtree bounded by event boundaries. All blocks that have a handler will be called.
The dispatchEnum
BroadcastEverywhere dispatches the event to every rendered block.
These, along with the other broadcast dispatch methods, are slow and should be avoided whenever possible.
The Active View
One particular subtree is designated as "Active" and the block at the root of the tree is the "ActiveView". Each time a block with an event boundary is rendered, it's pushed onto a list of views in Globals.views, and when it's unrendered it's popped from the list of views.
Currently, the ActiveView is the second view in this list.
This mechanism has several problems and will be redesigned for 0.7. In the future there will probably be a tree of ActiveViews, instead of a single ActiveView.
The dispatchEnum
BroadcastInsideActiveViewEventBoundary broadcasts to all the blocks in the ActiveView's tree in the same way that BroadcastInsideMyEventBoundary works.
Likewise the dispatchEnum
ActiveViewBubbleUp works just like FocusBubbleUp except that it starts with the ActiveView.
Arguments
When the event's handler method is called it's passed the BlockEvent. The BlockEvent contains a nonpersistent attribute named
arguments which is a dictionary of values passed to the
Post method of block.
Post is the method used to dispatch BlockEvents.
Post adds to the
arguments the
sender block that sent the event.
For events that are dispatched BubbleUp there is a
continueBubbleUp attribute which defaults to False. If your handler changes it to True, the dispatching won't stop at your handler but will continue to bubble up to the next block that has a handler.
One of the arguments is
results which can be set by the handler and is returned by
Post.
Finally, the dispatchEnum
SendToSender dispatches the event to the block that sent it. This is used by scripting.
Commiting Changes
Since menus and Toolbar buttons implement commands by dispatching BlockEvents and you often want to commit changes after a command, you can do this automatically by just setting the
commitAfterDispatch attribute of your BlockEvent to True.
UpdateUI Events
Menus and Toolbars often need to be enabled/disabled, display a special string, or be checked based on the state of the application.
It is error-prone to write an application that has to update the menus and Toolbars whenever the state of the application changes. A technique that many UI frameworks use to simplify this task is to dispatch a variant of the command attached to the menu or Toolbar. It returns the correct enable, string or check state just before the menu or Toolbar is displayed.
This variant handler has the same method name as the command's handler with "UpdateUI" appended. For example, the Cut event handler's method name is onCutEventUpdateUI. Locating the "UpdateUI" handler next to the command's handler makes the code easier to understand because the command and code that sets the user interface are nearby.
Toolbars present a special problem since, unlike menus, they are always displayed so the UpdateUI handlers always need to be called.
Normally this is done in the application's
OnIdle routine, which is called after all the pending events are processed. If there are lots of Toolbar buttons or visible menus, there are going to be lots of UpdateUI handlers getting called all the time. Combine this with lots of commands that are broadcast and you've got a slow application.
For this reason, Chandler only dispatches UpdateUI events when the focused widget changes or the application's
needsUpdateUI attribute is set to True. If you write a piece of code that changes the state of the Toolbar and doesn't change the focused widget, you'll need to remember to set
needsUpdateUI to True.
The UpdateUI handler returns information about how to set the Menu or Toolbar using the
Check,
Enable and
Text attributes of the event's arguments.