Prefab Version 1.0.1

Executive Summary:

Prefab is a set of widgets for the Squeak Smalltalk system built on top of the Morphic windowing system meant to provide graphical layout of windows and easy support for conventional "boring" GUI development.

License:

Prefab is released under the same license as Squeak itself.

(The previous version was released under the MIT license but this has changed.)

Installation:

This version of Prefab was tested on Squeak 2.7. It may work on some earlier versions but I know for sure that it doesn't work with Squeak 2.4.

To install:

  1. Unpack the files, making sure that the .xpm images are created in the "images" subdirectory. Start up squeak in the directory above "images". (Otherwise, it will have trouble finding the images.)
  2. You may wish to set FileStream class>>concreteStream to return CrLfFileStream. This is optional (unless I forget to convert convert the sources and images to Mac EOL conventions) but makes it easier to load xpm's created on non-Mac platforms.
  3. FileIn "xpm.cs".
  4. FileIn "Prefab.cs".

For the Impatient:

If you're the sort who doesn't like reading lots of documentation, then frankly, you deserve what you get. But since I'm trying to get people to actually use my software in the hope of becoming an open-source IPO millionaire, I've compiled a few simple instructions to get you started. Just read these--it'll only take you a couple of minutes.

To create a Prefab GUI:

  1. Go to a Morphic world
  2. From the "New Morph...">"Prefab-Construction" menu, create a PrefabConstruction. Resize it as desired. You may want to turn on gridding to get a neater arrangement.
  3. Create whichever control you want from the Prefab menu. (Avoid "Prefab-Support" and "Prefab-Examples").
  4. If the control isn't purely decorative, use the "Set Tag" menu item to give it a unique identifier. This gets turned into a Symbol.
  5. Resize, recolor, or otherwise configure the control as you see fit using the various main menu options. (Using non-menu items probably won't make it to the generated code.)
  6. Drop the control onto the PrefabConstruction. (You need to make sure that the pointer is inside the PrefabConstruction when you drop the control or else it won't get properly dropped.)
  7. Repeat the last 4 steps until done or bored.
  8. From the PrefabConstruction's menu, select "Display Construction Source". This will open up a workspace containing the source code that recreates the GUI layout.
  9. Paste the source code wherever convenient. This may be a subclass of PrefabManager but doesn't have to be. However, the argument passed to the method must be a PrefabManager.
  10. To add semantic actions or otherwise programmatically tweak individual controls, use PrefabManager>>clientWithTag: with the tag as argument. The tag must be a symbol. This is a bit slow so you might want to store reference to often-referenced controls in an instance variable.

Other hints:

You should only need to use those methods in the API categories to modify Prefab controls.

To edit an existing dialog, enter the following:

    Calculator new buildOn: (PrefabConstruction new openInWorld)    

(Substitute your creation for "Calculator").

To give a PrefabManager a title bar, use #openInSystemWindow or #openInSystemWindowNamed:.

(E.g. "Calculator new openInSystemWindowNamed: 'My Calculator'.")

Introduction And Rationale

One of the most frustrating things I've found with Squeak is creating user interfaces. Since Squeak is purely GUI-based, it becomes necessary during the development of any application to stop what you're doing and write yet another GUI window. After the first couple, this ceases to be fun.

Morphic provides an extremely elegant and flexible framework for GUI building. It's easy to write a completely bizarre, non-traditional GUI with relatively little work compared to what it would take in, say, Win32. Unfortunately, it takes about as much effort to write a standard, traditional GUI in Morphic. There isn't much in the way of traditional widgets in Morphic, and where they do exist, they tend to require a lot of fairly complex interaction to get them working correctly. The upshot is that I have to spend a lot of time poking through the source to figure out how to use them, and since I only write GUIs when I need to, I often have to go back and relearn it for the next design.

This is a lot more work than I want to do. Prefab is my attempt to solve this problem once and for all.

Philosophy

Prefab widgets are designed to make it easy for a programmer to write a simple conventional GUI without much work. To do this, I basically cloned a subset of Tk for Squeak (and added graphical drag+drop-based dialog layout). What this means is that I've adopted the following guidelines:

Note that these are just guidelines, not rules. I sometimes break them, but only when there's a good reason.

Using Prefab

A Prefab interface consists of three entities: the clients, the manager and the resizer. The clients are the individual controls that make up the UI. The manager is the window that contains and owns the controls. (This relationship maps to the Tk slave/master relationship. However, I used different terminology. Call it a Dilbert influence.) The third part, the resizer, is stored in an instance variable of the manager. Its job is to resize clients appropriately when the manager is resized.

Managers:

The manager is either an instance of PrefabManager or of one of its subclasses. In the latter case (as can be seen in PrefabCalculator and the other examples), the subclassing is done to reduce the code to one class and to cause the component to appear in the "New Morph" menu.

(When I first started this project, it seemed like a good idea. Now, I'm not too sure. Subclassing breaks the interface/application separation guideline and makes it difficult to switch to another kind of manager, should somebody add one. Consider this practice semi-deprecated.)

Managers can be embedded in system windows using a special subclass of SystemWindow, PrefabManagerWrapper. You can put a manager in a frame with the #openInSystemWindow message. For example:

    PrefabManager new openInSystemWindow

or,

    PrefabManager newInSystemWindow

also works. You can set the window title with the message #title:.

A special subclass of PrefabManager--PrefabConstruction--is used to do graphical GUI building. To use it, simply create one, adjust to the appropriate dimensions and then drag and drop clients into it. Once you've got a layout you like, select the "Display Construction Source" menu item and a workspace will pop up containing the source for a method named "buildOn:". Paste this code where appropriate.

When you want to create the window, just send "buildOn:" to the appropriate object with the manager on which the clients will be placed as an argument.

This is also useful if you want to graphically edit an existing layout. Just create and open a PrefabConstruction, then call the appropriate #buildOn: on it. Once you've finished editing the layout, simply generate a new buildOn: method from it and paste it over the original.

For example, to edit the layout of the GUI for class "Foo", you'd enter:

    Foo new buildOn: (PrefabConstruction new openInWorld).

Clients:

Clients are those Morphs that appear in the "Prefab" submenu of the "New Morph" menu. It's pretty easy to figure out what they're supposed to do from their names and class comments, so I'll refer you to the source code and the class comments. You should only need to look a the class comments and initial comments of any API messages (i.e. messages in categories with "API-" in the name.)

(Yes, I know, saying "The source code is the documentation" is the epitome of luserdom. However, this is the standard practice with Squeak and I've commented all of the importants stuff.)

Resizers:

There are three classes of Resizers:

PrefabResizer
This is the default. It's really, really stupid. It does nothing when a window is resized, which is OK for windows that are normally not resized but tends to not be satisfactory most of the time.
RubberSheetResizer
This is slightly smarter. It stretches the clients relative to the manager's dimensions. Note that some clients don't stretch in some dimensions. Also, clients can't safely be resized down to zero. You will probably want to set a reasonable minimum size for the manager.
FixedDistanceResizer
This one is the best of them.

It works by letting you anchor one or more edges of a client to a parallel edge. By default, all client edges are anchored to the top and left of their manager. However, via a number of messages of the form: fix<side>Of:, you can change that.

For example, if "cl" is a client of manager "mgr", you can make it so that cl stretches as "mgr" is resized by doing this:

	mgr resizer
		fixBottomOf: cl;
		fixRightOf: cl.

What this is doing is specifying that the bottom and right edges of "cl" will always stay the same distance from the manager's bottom and right edges respectively.

The EditorFrame example uses a FixedDistanceResizer. You might want to take a look at it to see what I mean.

If you wish to use a different resizer than the default, you can do it via the manager's #resizer: method:

    self resizer: FixedDistanceResizer new.
This must be done before any clients are put on the Manager and may not be changed after that.

(Yes, I know this violates my guidelines, so consider this a minor bug. I don't think being able to change resizers on the fly is a useful capability and leaving it out saves me some work. If you need this functionality, the source code is right there. Add it yourself and then send me a change set.)

Miscellaneous Hints and Comments

To Do