•
|
Demo Launcher - Used to launch the example applications. The launcher (actually, the class GFDemoLauncher) uses GFButtonGO’s to launch the applications.
|
•
|
About Dialog - The GFAboutDialog displays the propaganda information about the demo as well as the rotating tensegrity picture.
|
•
|
Drawing Editor - A kind of “catch all” application that lets you try out new GO’s you might create. You can access all the basic GO’s supplied with GF/ST from the floating palette. The Drawing Editor (actually the class GFDrawingView) shows the default behavior of all base GF/ST GO’s.
|
•
|
GFThreeDEditor - A good example of coupling underlying domain models (of 3-D figures in this case) with GO’s.
|
•
|
PsiVisualInspector - A new tool for your toolbox. We expect you’ll start using this inspector instead of the standard ones in Smalltalk, especially since you have them built into it, too.
|
The concept of a tool is central to GF/ST, as is the concept of
handle. For an overview of the concepts, refer to “
Basic Concepts and Classes“. For details of how you will be using tools and handles in your own application, you can start with the chapter on “
GF/ST Application Design & Decisionmaking“. Right now, we’re trying to concentrate only on how the Drawing Editor behaves without getting hung up in too much detail.
Each graphic object can have its own specialized set of handles. To be technically accurate, graphic objects do not know what handles they have - they simply trigger the event
#generateHandles, and any object can return the appropriate handles. The Drawing Editor demonstrates the
default handles which are provided when the event is not handled.
The undo operation in GF/ST is very powerful. We use a “memento“ approach, and it is generalized for use in any of your applications, with or without GF/ST. Give it a test by undoing up to fifteen things you’ve done. We chose to limit the size of the bounded stack tracking undo-able actions to fifteen in the Drawing Editor, but you can set it to be unlimited or to any size you want. See the comments in GFBoundedStack for more information on the undo stack itself.
Undo is difficult to achieve in Smalltalk without violating encapsulation - how do you undo an operation on some object or an operation done by an object on itself, without having access to protocol or state you should not know about? The approach in GF/ST uses the “memento” pattern discussed in “Mementos“ in “
Miscellaneous Topics“ of “
Visual SmalltalkWarning: If you do not use finalization under Visual Smalltalk 3.0.x, you must explicitly release your GO’s that use bitmaps (GFImageGO, GFGroupGO, GFButtonGO, and any of the Visual Inspector object GO’s). In addition, you should either use an unbounded stack for undo’s (the default is 15) or turn the option off via noUndo. The reason is that the GFBoundedStack holds onto instances of GFMemento in order to undo deletion operations you might perform. If undo is enabled, you should not release GO’s when you delete them, because an undo might cause it to come back. And, since you can’t release it when you delete one, if the stack is bounded, it is possible that the GO will be garbage collected when the stack grows, without your having released it. If you use finalization, this problem goes away. If you do not use finalization for some reason, you may want to consider making an unbounded stack or “no undo” be the default.Mostly GFGraphicObject-Related How To’s
Warning: If you do not use finalization under Visual Smalltalk 3.0.x, you must explicitly release your GO’s that use bitmaps (GFImageGO, GFGroupGO, GFButtonGO, and any of the Visual Inspector object GO’s). In addition, you should either use an unbounded stack for undo’s (the default is 15) or turn the option off via noUndo. The reason is that the GFBoundedStack holds onto instances of GFMemento in order to undo deletion operations you might perform. If undo is enabled, you should not release GO’s when you delete them, because an undo might cause it to come back. And, since you can’t release it when you delete one, if the stack is bounded, it is possible that the GO will be garbage collected when the stack grows, without your having released it. If you use finalization, this problem goes away. If you do not use finalization for some reason, you may want to consider making an unbounded stack or “no undo” be the default.
Under Visual Smalltalk and VisualAge, the GF/ST host components are real Windows components. Under VisualWorks, they are true VisualWorks widgets. In the Drawing Editor, we’ve elected to make them active as soon as you unselect them, or select something else on the screen. In other words, if you draw a button on the screen, click on something else, and then click back on the button, it behaves just like a real button, since it is a real button. The consequence of this choice is that you need to use rubber-banding to re-select a host component once you’ve drawn it. If you were developing an application for building user interfaces, you would probably want to be able to switch between a building mode and a test mode to avoid confusion.
Notice major any degradation of performance as you add tons of graphic objects? We use a GFQuadTreeElement approach to be able to handle large numbers of objects. Most drawing programs use outlines as you manipulate objects on the screen. The problem with outlines is that you cannot really see how your object will look when you get done modifying it. In addition, the performance of many drawing packages will degrade rapidly as complexity increases. The reason for this degredation is apparent when you consider what goes on as you drag an object across a complicated display of other objects, maintaining knowledge of z-ordering. Even if your application ignores z-ordering, you still need to determine what objects on the screen are affected by your changes to another object. Conceptually, it is not hard to do, but in practice, simple approaches make dragging, selecting, and scrolling become your biggest performance bottleneck. GF/ST’s quad-tree approach manages the spatial complexity to maximize performance. See the discussion in “Quad Trees“ in “
Miscellaneous Topics“ of “
Visual SmalltalkWarning: If you do not use finalization under Visual Smalltalk 3.0.x, you must explicitly release your GO’s that use bitmaps (GFImageGO, GFGroupGO, GFButtonGO, and any of the Visual Inspector object GO’s). In addition, you should either use an unbounded stack for undo’s (the default is 15) or turn the option off via noUndo. The reason is that the GFBoundedStack holds onto instances of GFMemento in order to undo deletion operations you might perform. If undo is enabled, you should not release GO’s when you delete them, because an undo might cause it to come back. And, since you can’t release it when you delete one, if the stack is bounded, it is possible that the GO will be garbage collected when the stack grows, without your having released it. If you use finalization, this problem goes away. If you do not use finalization for some reason, you may want to consider making an unbounded stack or “no undo” be the default.Mostly GFGraphicObject-Related How To’s
Warning: If you do not use finalization under Visual Smalltalk 3.0.x, you must explicitly release your GO’s that use bitmaps (GFImageGO, GFGroupGO, GFButtonGO, and any of the Visual Inspector object GO’s). In addition, you should either use an unbounded stack for undo’s (the default is 15) or turn the option off via noUndo. The reason is that the GFBoundedStack holds onto instances of GFMemento in order to undo deletion operations you might perform. If undo is enabled, you should not release GO’s when you delete them, because an undo might cause it to come back. And, since you can’t release it when you delete one, if the stack is bounded, it is possible that the GO will be garbage collected when the stack grows, without your having released it. If you use finalization, this problem goes away. If you do not use finalization for some reason, you may want to consider making an unbounded stack or “no undo” be the default.
Add and select a cube in the 3-D Figures Demo. The displayed cube actually has four handles. One is the black square in the center that looks like the standard handles generated for any GO. The other three handles are the letters X, Y, and Z. When you use any handle in this demo, the handle sends messages to a separate instance of an interface class, which then manipulates an instance of a cube. In this scenario, the cube is considered to be the domain model - a cube knows its dimensions and what its orientation is. The graphical display is a reflection of the state of the cube - which way it is facing, how tall it is, and so on. The instance of an interface class could be ViewManager in VisualSmalltalk, ApplicationModel in VisualWorks, and in VisualAge, it is a subclass of WbApplication. It could simply be a subclass of Object, as long as it can fulfill its role of coordinating the flow of information from the handles to the cube itself.
When you grab the “Z” handle and move it, the handle sends the message changeZ: delta: to the GFThreeDEditor. The first argument is the index of the GO tracked by the GFThreeDEditor,
i, and the second argument is a point that is the change in mouse position. In its
changeZ:delta: method, the GFThreeDEditor then tells the underlying domain model to change its Z dimension.
From the GFThreeDEditor>>addPsiSolidAt: method, the dependent lines are created as follows:
The lines variable is a collection of the GFDependentLineGO’s that is created at the beginning of the method and added to the GFCompositeGO at the end. The instance variable
gos is a collection of collections, one for each of the GFCompositeGO’s in the drawing. For efficiency of display and manipulation of 3-D objects in a 2-D world, each of the secondary collections tracks the individual GO’s in the composite as well as the composite itself. (It’s just a caching trick to eke some more speed out of the demo, since determining face order in a rotating 3-D object is computationally intensive).
Notice that the pop-up menu for a cube is different than the menu for a tensegrity. Actually, the Faces submenu is different, because we want the faces of the tensegrity to remain transparent. You can specialize menus very easily because we use the event system to get the menu for an individual graphic object. The application itself can define the menu to be used for each graphic object if the default menu is not wanted. The application is also able to define what, if any, menu should be used for the overall drawing, when you pop-up a menu outside of a figure.
To flesh out the hierarchy, we initially created three subclasses of PsiAbstractObjectGO - PsiObjectGO, PsiVariableObjectGO (a subclass of PsiObjectGO), and PsiVariableByteObjectGO. These classes were needed to provide different behavior for the three flavors of objects available in Smalltalk. To deal with primitive objects, such as nil,
Integer,
true,
false, and
Symbol, we also created a subclass of GFTextGO, PsiPrimitiveObjectGO. To round it all off, we added two more classes under PsiObjectGO - PsiOrderedCollectionGO, and PsiDictionaryGO. Since the base image development environment provides these specialized inspectors, we decided their usefulness would extend into the Visual Inspector as well.