When the WikiText has been transformed into a parse-tree the next step is to transform this parse-tree into a widget-tree.
We talked about widgets as parts of the WikiText markup but in fact each node of the parse-tree is transformed to a widget object.
The core plug-in provides a basic widget object which gets the parse node it should represent and a DOM node. The widget then must create the DOM structure which represents the parse node and add it to the provided DOM node.
A LinkWidget for example would create the DOM node for a <a>...</a>
tag and put it in the provided DOM node.
When a widget gets a parse node with child nodes it must create the corresponding child widgets.
All this functionality is basically provided with the base widget. But when creating the widget for a parse node it loads additional modules with module-type: widget
. These modules can export multiple widgets which extend the base widget and can override it's methods like the rendering and refresh functions.
As described in Parser each parse node contains a "type" property. This type directly determines which widget module is used.
{type: "text", text: <string>} - a text node
Would be transformed to a TextWidget. (Note that the TextWidget module exports a property "text".)
So in fact when talking about widgets in WikiText, they are not a feature added to WikiText but the ability to directly use a specific widget instead of the parser choosing a widget type.
In the beginning we talked about widgets and how they enable dynamic behaviour. But up until now we only described how widgets create DOM nodes from parse nodes. Widgets add dynamic behaviour in two ways.
Messages
Messages are events that are triggered by the user. They are generated by widgets for example when the user clicks on a ButtonWidget. Each message has a type property like "tm-delete-tiddler" and a parameter.
{type: "tm-delete-tiddler", param: "MyOldTiddler"}
When such a message is created by a widget it sends the message to it's parent widget which sends it to it's own parent widget and so on.
On this way each widget can try to dispatch the message.
This concept is realised in the base widget object.
It provides a function dispatchEvent(message)
which is called by the children to bubble the message up the widget tree.
Another function addEventListener(type,listener)
can be used to bind a function to a specific message type.
If the listener returns false, the message is send to the parent widget.
The TiddlyWiki core plug-in handles a lot of messages in the NavigatorWidget.
Selective Update
With Messages a widget is able to put some kind of events into the TiddlyWiki application which is one part of the dynamic behaviour of widgets.
The other part is selective updating.
Widgets are often dependant on tiddler states.
The ListWidget for example can be configured to list all tiddlers which are tagged with "important".
Now, when such a tiddler is changed and it's "important" tag is removed, this change should reflect in the ListWidget.
To allow widgets to react on such changes, each widget can provide a function refresh(changedTiddlers)
.
The RootWidget is registered to the wiki store, using the Event Mechanism. When an change event occurs it starts to call the refresh function of its children with a list of the changed tiddlers.
Each widget can then decide if it has to change or re-render its DOM representation and call the refresh function of its own children.
This way every time a tiddler or the wiki store itself changes, each widget can instantly react on these changes or ignore them.
Another way of updating are text reference attributes (text references explained in Transclusion and TextReference):
{type: "indirect", textReference: <textReference>}
When a widget got a attribute which is a text reference and the refresh function is called, it can check if the text reference references a changed tiddler and update accordingly.
Nearly every state of the UI is stored in tiddlers. A search mechanism for example would use a EditTextWidget which shows an text input field for the search string and a ListWidget to show the results. The EditTextWidget is bound to a tiddler $:/temp/search. Meaning when editing the text in the input field the tiddlers content is changed to this text. On the other hand when the tiddler changes the RootWidget is notified. It then starts calling its childrens refresh methods. Eventually the refresh method of the EditTextWidget is called and it can re-render itself if necessary. This way TiddlyWiki can re-use an already existing data structure which is not only convenient because we don't need to introduce an additional structure but tiddlers managed in the wiki store are already a pretty powerful data structure, supporting an Event Mechanism (so we don't need an additional observer pattern), Caching, Metadata by additional tiddler fields and a way to obtain specific tiddlers.