From: greg@afs.com (Gregory H. Anderson)
Subject: Re: Alternative GUI class hierarchies?
Reply-To: greg@afs.com
Date: Mon, 28 Dec 1992 17:08:47 GMT

In article <1992Dec27.063724.15059@u.washington.edu>  
wiml@milton.u.washington.edu (William Lewis) writes:
> 
>    Recently I've been pondering the construction of an object-oriented
> GUI toolkit (particularly, using gcc-objc over X, but that detail is 
> irrelevant), trying to combine the best attributes of the X intrinsics
> and NeXTstep (the two OO GUI toolkits I'm familiar with). One thing that
> strikes me as inelegant about both toolkits is the View/Cell
> distinction (in X, the Widget/Gadget distinction). Although I understand
> the utility of a "lightweight view" for menu selections and so forth,
> I've never really liked it. I've thought of two possible solutions.
> 
>    The first one is really just a workaround. Instead of having
> separate hierarchies for "heavyweight" and "lightweight" objects, each
> object could select at instantiation whether it should be lightweight
> or not. "Views" ("widgets") who are "lightweight" would have to 
> communicate with their closest "heavyweight" ancestor to ask for
> events, drawing requests, to determine the (x,y) offset at which
> to draw (since in X at least, drawing coordinates are relative to the
> destination drawable --- transformation matrices could be very useful
> here) and so forth. This would require a little more work on the part
> of object authors, however, to handle both cases; and it still wouldn't
> simplify the client's structure much (only the display server's). 

I think NeXT's architecture solves this problem nicely. TextFields, for  
example, already act primaily as Enclosures. Most of their methods are  
simply "covers" for the underlying Cell methods. The fact that a distinct  
Cell is actually doing the drawing is hidden in the implementation. (So  
well hidden, in fact, that we subclassed TextFields wrong the first time,  
by putting most of the cool new stuff in the View, not the Cell. Duh.)  
Cells draw relative to their bounding (not frame) rectangle, which in fact  
is relative to the destination drawable. So what's the problem?

>    The other idea is to divide the object hierarchy even more strictly,
> so that any given object can *only* be in one or another subhierarchy.
> Basically, I'm thinking of dividing all graphical objects into two
> types: those which enclose an area, and those which draw things in
> an area. Only area-enclosing objects would receive events from the
> display server and mouse. Area-enclosing objects would be allowed to do 
> a little drawing, say a frame and a background, but nothing complex 
> like text.  Under X, for instance, "enclosing" objects would (possibly) 
> have their own window, but "drawing" objects would never have a window.

This is *exactly* the distinction between NeXT Views and Cells. But the  
strict hierarachy is bad, for a different reason: A View can give the  
handle of its Cell to another View, which can then share the draw code. In  
this way, the exact same contents can be drawn on multiple Windows, in  
differently sized (and attributed) Views. We use this. It's nice.

>    Immediately, the question is "but what about objects that both
> enclose area, *and* draw things in that area? Like buttons, sliders,
> text-boxes, menus ... pretty much everything in fact." For these,
> I would have a *third* object which "owns" and manages the enclosing
> and the drawing objects. For example, a Button object might own
> a SqareEnclosure object and a Text (or SmallImage) object. Only
> the enclosure and the text objects would have a visible manifestation. 
> The application programmer would interact only with the Button object,
> but the Button object would use the other two objects to actually
> do its work. This strikes me as having many advantages. Because each
> UI element is more modular, it's easy to swap the subparts around:
> the same Button class could be used for a button with a text label,
> a button with an iconic label, or a button with some application-defined
> image, without changing the Button class at all. Similarly, square
> buttons, rounded buttons, and hexagonal buttons can be created by
> writing a new "enclosure" class and causing the Button to use that
> class instead of the "SquareEnclosure". I think this advantage is
> applicable to other common UI objects as well.

Again, for the most part NeXTSTEP works this way. A Form, for example,  
owns a list of FormCells. The FormCells, in turn, each own a TextField  
cell for the purpose of containing the drawing their titles. All of this  
is hidden in the implementation by using "cover" methods.

On of the great things about the distinction between Views and Cells is  
that you can take a simple View like a TextField, suck out its Cell as the  
prototype for a Matrix, and voila! you have multiple Cells all acting and  
validating like the original. Instant spreadsheet.

>    The main *disadvantage* is that, in one sense, I've worsened the
> original problem: there are now *more* objects floating around in the
> application program than there are in the usual scenario! Three objects 
> just for a button; more for anything more complex. However, I think this
> might not be so bad, because
>      - The individual objects are simpler. There are probably also
> fewer distinct classes in use at any given time, which could reduce the
> RSS of the application.
>      - If written well, the Enclosure class could offer much greater
> control over server-side resource use. The idea of having each object
> select whether it;s lightweight or heavyweight could be used here as  
well. 
>      - Changing the "look" of an application -- say, from Motif-y to
> Next-ish to Mac-like --- might involve changing only the Enclosures
> and some drawers (like sliders), leaving many application-defined
> objects alone. This would have to be designed in, though.

In theory, yes. In practice, difficult. NeXT's implementation of the  
Responder chain within responder objects, to give one gotcha example, does  
not port well to X.

>      - In Objective-C, all objects are individually dynamically
> allocated. But with some tweaking, it should be possible to allocate 
> space for a Button and both of its slave objects in one *alloc() call.
> The space would be freed when the Button was freed; the Button would
> have the responsibility of calling its component objects' -free methods
> before it deallocates itself. ([Object free] would have to be rewritten
> to check for this sort of situation, of course.) 

This already happens. When you init a TextField, it allocs its  
TextFieldCell at the same time.

>    I think that combining objects in this way would make the increase
> in the number of objects much less harmful, since the number of
> malloc() blocks would be the same, or even reduced. It would also
> improve the locality of reference when responding to events.

If you use allocFromZone, which you should, this problem is solved.

--
Gregory H. Anderson          | Thus spake the master programmer: 
Master Programmer / Manager  | "Let the programmers be many and  
Anderson Financial Systems   | the managers few; then all will be 
greg@afs.com  (Nextmail OK)  | productive." -- Tao of Programming