Widget Facade

A Tcl/Tk Idiom

Problem

Context

canvas

Each Tk widget has two associated commands, a "class command" and a "widget command". The class command is used to create new widgets of the class; for example, the button command creates new button widgets. A different widget command is used to control each widget; it is created dynamically at the same time as the widget and given the same name as the widget it controls. For example, creating a button named .btn also creates a command named .btn through which the button can be controlled.

Forces

  1. It is easy to implement a procedural interface to a composite widget.
  2. A procedural interface exposes implementation details of the composite widget. It is obvious that the widget is composed of primitive widgets. For example, querying the configuration options of a composite widget returns the configuration options of the root primitive widget from which it is composed.
  3. The Tk library provides support for implementing object-based commands for widgets, so the programming interface would change if one recoded a composite widget in C/C++.
  4. Tk documents a number of standard methods for widgets with which programmers are familiar. These cannot be supported by a procedural programming interface.

Solution

  1. Create a data structure to hold information for the new widget. This might be an array in some namespace or an [Incr Tcl] object. The only constraint is that the data structure exists for the lifetime of the widget being created.
  2. Create the root widget of the composite interface component and give it the name requested by the caller. This will also create a widget command for the new widget which has the same name as the widget command that you want to use for the composite widget. Therefore...
  3. Rename the widget command of the root widget to some unique name and store that name in the data structure of the composite widget.
  4. Define the widget command for the composite widget to have the same name as that requested by the user. If your widget command needs to manipulate the root widget of the composite, it calls the command that was renamed -- the command name is stored in thw widget's data structure and so is easily determined.
  5. Bind Glue Code to the Destroy event to remove the renamed command, thereby avoiding memory leaks.

Consequences

configure cget

You can reimplement your widget in C and give it the same programming interface, yet take advantage of the support for object-based commands and configuration options that is provided by the Tk library.

Implementation Issues

Known Uses

[Incr Tk] Jeff Hobb MegaWidget library

In the following exchange on comp.lang.tcl, Bryan Oakley gave an excellent description of the Widget Facade pattern in action.

Mikhail Teterin wrote:
>
> Hello! We use Tk8.0 here, and are writing a little specialized editor
> based around the text widget.
> 
> Almost everything works, except for "Undo" and "Would you like to
> save changes":
> 
>         * undo: short of saving the whole buffer over and over,
>           any tricks known?

Piece of cake. Relatively speaking. Took me perhaps one full day to
figure out and do. The basics are this: After creating the widget,
rename the widget command to something else (ie: rename .textwidget
.textwidget_original). Then, write your own widget command (ie:
proc .textwidget {args} ...) that, initially, just passes all the
commands back to the original. Make sure that part works.

Then, simply catch all text widget commands that modify the buffer
(notably, insert and delete, though there are others...). Each time an
insert happens, record the position and text that was inserted. For each
deletion, record the start position and the text that was deleted. Keep
a bit list. Or a small list if instead of unlimited undoes you just have
a single undo.

Then, when they press the undo key, just take the top item from the list
and reverse it.

Its not that difficult. I was able to implement a fully functioning
unlimited undo feature using this technique. One of these days, if I can
find the time, I'll reimplement it (the original code belongs to my old
employer...) and make it public. Its not rocket science, though. Any
decent tcl programmer can do it.

>
>         * save prompt: any ideas on how to figure out if the buffer
>           was changed? There are too many ways the buffer can be altered
>           to try to rebind all the bindings and still have a nice code :)

Using the technique above for writing your own widget proc, anytime you
add something to the undo buffer (ie: any time there is an insertion or
deletion) you can set a flag internally. Then you can implement your own
"changed" command to query this flag. Works like a charm. With the added
benefit that you can turn the flag back off if they undo all the way
back to the beginning of the undo buffer.

Brian Oakley has released a Tcl package implementing this example.