iGloo: Delayed Creation

One of the particular problems that’s been bugging me is the issue of creating megawidgets in iGloo. A megawidget, of course, is simply an object that acts and looks like a widget; and for an object to act like a widget, it has to have a real Tk widget at its core. The process goes something like this:

First, you decide what to call your widget, remembering that there are definite constraints on what a widget can be called. For the sake of argument we’ll create a widget called .text.

Next, you create a real Tk widget with that name, to serve as the hull of your megawidget:

text .text

Next, you rename the real widget something else:

rename .text _.text

Next, you create your own command called .text. This command should pass most of its subcommands to the hull (_.text), and define any new or modified subcommands it chooses.

proc .text {method args} {
    .
    .
    .
    _.text $method {expand} $args
    .
    .
    .
}

You’ve now got a megawidget, more or less. (As always, the devil is in the details.) I call this process of replacing the widget’s command with my own command hijacking the widget command.

The question is, how can one write a TIP 257 object that does this? It’s a little bit more involved, because you already have a command name when you enter the constructor. Consider the following code snippet, which is part of some code to create a read-only text (rotext) widget:

    constructor {args} {
        my variable hull

        # FIRST, rename [self] to something else, temporarily
        set self [self]
        rename [self] ::_temp$self

        # NEXT, create the Tk widget (remove the ::)
        text [namespace tail $self] {expand}$args

        # NEXT, rename the Tk widget
        set hull ::_$self
        rename $self $hull

        # NEXT, put self back
        rename ::_temp$self $self
    }

As you can see, you need to move your chosen window name out of the way, then create the Tk widget, then move it out of the way, and then put your own command back again. This code works, but it has two problems: first, it doesn’t allow you to hijack a previously existing widget, which is sometimes handy. And second, look what happens when you create your widget:

% rotext .text
::.text
%

Whoops! When you create a TIP 257 object, you get a fully-qualified command name! But when you create a Tk widget, you get back the window name. It’s true that .text and ::.text name the same command; but the first is a window name and the second isn’t.

The problem here is that by the time we enter the constructor the object has already been created. What we’d like is the chance to do a little work before the object is created. And the way you do that is by redefining the rotext class’s create method:

    self.method create {win args} {
        # FIRST, create a text widget with the desired name.
        # (We could also hijack a widget that had already been
        # created.)
        text $win

        # NEXT, rename it to something else.
        set hull ::_$win
        rename $win $hull

        # NEXT, create a new object with the original name, 
        # passing the new name to the constructor.  Note that
        # we add the "::" to make sure the command is created in
        # the global scope.
        next ::$win $hull {expand}$args

        # NEXT, return the bare window name
        return $win
    }

    constructor {theHull args} {
        my variable hull

        # FIRST, save the hull
        set hull $theHull

        # NEXT, pass the options to the hull
        $hull configure {expand}$args
    }

First, we use self.method to define a new create method for our class. We create a text widget, and rename it. Then we use the next command to invoke the superclass’s create method; this will create the object and call its constructor. We insert the Tk widget’s new name into the argument list. The constructor then grabs the Tk widget’s new name and saves it in the instance variable hull, and passes the widget’s options to it.

It remains to redefine the insert and delete methods, and forward the other methods to the real text widget. Here’s the complete example: rotext.tcl.

1 thought on “iGloo: Delayed Creation

  1. Pingback: The View From The Foothills » Blog Archive » iGloo: A Problem with Delayed Creation

Comments are closed.