| Edit | Rename | Changes | History | Upload | Download | Back to Top |
Trippy can be used just like the traditional inspector, with very little or no readjustment. It can also do many things the classic inspector couldn't. This walkthrough is a step-by-step guide that makes sure you don't miss anything just because you didn't expect to find that in an inspector. It is best to read it in a Smalltalk workspace so you can easily evaluate code that opens examples. You can download a .ws file to open in your VW image.
Let's start by inspecting a few simple objects, say a Point and an OrderedCollection:
(3 @ 4) inspect. (1 to: 10) asOrderedCollection inspectThe two windows that appear have the familiar inspector layout, even though with a few bells and whistles. There is a list of parts on the left hand side. Parts of the Point object are the point itself (let's not go into the philosophy of that) and its 'x' and 'y' instance variables. Parts of the OrderedCollection are its elements, from first to tenth.
Selecting a part shows the printString of the part in the right hand side text view. Selecting multiple parts is possible (hold down Ctrl or Shift while clicking). Because selecting all parts at once is very handy to get a quick overview of an object, there is a toolbar button (second from the right) and a keyboard shortcut Ctrl+A to do that. (Try it).
Right above the familiar "inspector-style" list box and a text view is a row of tabs. These tabs are views of the object. Any object has the "Basic" view that shows all instance variables. The OrderedCollection we look at also has the "Elements" view, initially selected. It is a higher-level "logical" view showing all elements of the collection but ignoring such implementation details as the 'firstIndex' and 'lastIndex' instance variables and unused indexed variables. Switch between it and the "Basic" view of the same collection to see the difference.
In terms of the "old" inspector, Elements view works like the OrderedCollectionInspector one would get when inspecting an OrderedCollection, while the Basic view is the same as doing the "basic inspect" on the OrderedCollectionInspector.
Any object also has a Methods view. Try it out to see what it is. Note the "Inheritance" menu that appears on the menu bar. If you think this is an overkill, or a "creeping feature", read on.
For some objects, the "Basic" view may include extra parts which are not its instance variables. In fact, "-self" which is a part that is always there is not an instance variable.
For a less obvious example, have a look at a compiled method:
(Object compiledMethodAt: #printString) inspect
The "Basic" view includes "-bytecode" and "-source". They are not parts of the receiver in the "physical" sence, but they are included in the basic view as "virtual" attributes.
Another interesting example is an Integer:
1234 inspect
or a Character:
Character space inspect
As usual, you can type a Smalltalk expression into the text view pane and evaluate it there in the context of the receiver. The annoying part of doing it this way is that whatever we type is lost as soon as we switch to another field.
Here is the alternative. Click on the last button on the toolbar ("Toggle Evaluation Pane"). This adds an extra text area to the window. This area stays unchanged regardless of what is going on in the rest of the inspector. It can keep a set of expressions to be used over and over again. As an added bonus, the contents of the text pane can be shared with other inspectors by accepting it. This saves the current text in a shared variable within the image. Because that variable is used as the text model by all inspectors, the accepted text will appear in all open inspectors, as well as in those that will be open in future.
It is possible to change the value of any instance variable of an object we are inspecting: select a variable--for example, 'x' in the Point object--enter a Smalltalk expression in the right-hand view, and accept. The result of evaluating the expression is saved in the variable.
The same happens if you try it on an element of the OrderedCollection. The old element at the selected index is replaced with a new one. Repace the fifth element with 0.
Since the inspector supports multiple selection, values of many variables or collection slots can be changed at once. Select all elements of the OrderedCollection we are inspecting, type 555 into the right-hand text view (erasing everything displayed in that pane) and accept.
Now imagine we changed our mind about this group assignment. Open the "Edit" menu and select "Undo".
Undo is multiple-level. We have just undone the previous step of storing 555 in all the collection slots. Because before that we replaced the fifth element with 0, "Undo" is still enabled and selecting it once again will restore the fifth element to be 5.
To explore more object editing possibilities, open another inspector on an OrderedCollection:
(OrderedCollection with: 1 with: 2 with: 3 with: 4) inspect
We can use drag-and-drop to change the order of elements in this collection, or to replace elements. Select two first elements of the collection, drag them and drop after the end of the list of fields. Select all elements to see the new order of elements. [Undo is not yet implemented for element reordering].
Now select one element, drag and drop in ONTO another element. Select all elements again to see what happened. Dropping on an element replaces the element with the dropped value.
Now select several elements in one OrderedCollection, drag and drop them into another collection. Because the target collection is in a different inspector, the elements are inserted, making the collection bigger.
The same kind of editing is possible with Arrays. Even though strictly speaking, Arrays are not resizeable, Trippy makes them appear resizeable. It is possible to insert more elements into an Array, or remove elements and make the array smaller. (See "Add" and "Remove" in the "Edit" menu).
Drag-and-drop is supported as widely as possible--between collections, between regular objects, between collections and regular objects.
Drag and drop is also extended to workspaces. You can add any object to a workspace as a local variable by dropping it onto the workspace. Or, you can switch to the workspace's "pool variables" view (the same button that used to spawn an inspector on the pool of locals now shows the pool variables in the workspace window). This view is actually an inboard inspector on the pool of local variables. You can use it as any other inspector to add new local variables, replace values of existing variables, or copy objects between workspaces.
Finally, there are "Copy" and "Paste" actions on the "Edit" menu. They give another way to copy objects between instance variables or collection slots, without having to keep both the source and the destination visible on the screen at the same time. Copied references (like drag-and-drop, cut and paste copies references rather than objects) are placed on the "object clipboard". The clipboard holds onto its contents weakly, so they may "fall off" the clipboard if the source inspector is closed.
Now let's see how the inspector helps discover the world outside a single object. Now we want to have a look at something more complex than a simple collection:
ScheduledControllers inspect
First of all, a side note. All instance variables of the ControlManager we end up looking at are marked with a hash sign. Trippy allows any class to declare some or all of its instance variables as "protected", and a hash sign (as the UML people suggest, think of it as bars on a window) indicates a protected variable. Protection is there for variables used all the time by the runtime machinery. There is nothing like accidentally dropping something on a window sensor because not even the emergency evaluator will come up. Protected variables can be changed, but only after answering yes to a confirmation dialog.
Now on to the navigation. The first thing we might want to do is follow a reference to another object. Just like with the original inspector, double-clicking a variable or element "dives" into that element. Double-click "scheduledControllers".
The inspector now shows the OrderedCollection of scheduled controllers. The title bar now shows, in addition to the class of the object, its role--the name of the instance variable it was stored in or an index of an element ("scheduledControllers: a ControlManager").
One of the first two toolbar buttons now became available. These buttons work like a web browser, moving the inspector "focus" along the trail of visited objects. Click the first one ("<-") to go back to the original ControlManager. The "->" button is now available, because the just visited collection of scheduled controllers is now "ahead" on the visit trail. Click "->" to return to scheduled controllers.
Now double-click the second element of the collection to dive into it. The current object should now be "an ApplicationStandardSystemController", explained in the title bar as "2: an ApplicationStandardSystemController". Most of its instance variables are protected as well. (It was a voice of experience when I said it wasn't a good thing to accidentally drop stuff on something like a sensor).
Open the "History" menu to look at it. It shows the trail of visited objects, with explanations of the objects role and class membership just like in the title bar. You can use the History menu to quickly jump to other places on the visit trail.
Diving and travelling along the visit trail are important navigation aids but they don't help much with exploring the wider network of relationships between objects. For example, if we wanted to find the window of the Launcher, we would have to repeatedly dive into elements of the scheduledControllers collection one after another, looking at their models until we find one with a VisualLauncher as the model.
What we really need here is the ability to "explore" the world outside any given object. In our case, we want to be able to easily inspect all controllers in the previously visited collection.
Assuming we are still looking at the second ApplicationStandardSystemController (return to it if you have left it), select "Explore>Siblings" menu item. The inspector is transformed so that an extra list box appears on the left, showing parts of the "previous" object--the collection we were inspecting before diving into the current controller. The list box lists the elements of that collection. Selecting an element in the list refocuses the inspector on the right so that it shows details of the newly selected element. Now, to find the launcher, all we have to do is select 'model' in the inspector and go through elements of the collection until we see the one with a VisualLauncher as the model.
When the controller for the VisualLauncher is found, we probably want to "focus" the inspector on that controller to get rid of the now unnecessary view of the parent collection. Select "Explore>Focus" to do that.
There is a variation of the "two-level" inspecting we have just done. For example, right now we are looking at an ApplicationStandardSystemController for the VisualLauncher. Suppose we want to study all the objects it refers to--the model, the view, and so on--in detail. In other words, we want to have a two-level inspector similar to the one we just used to find the VisualLauncher, but this time instead of going through the siblings of the original object, we want to go through its parts. This is what "Explore>Parts" does. (Try it).
Now double-click the "view" variable to focus on the view (which is a window). It appears in a "Preview" view showing what the window looks like. As a side note, while we are looking at a window, note that the "Object" menu (and the part list popup menu when "self" is selected) includes two extra items "Raise" and "Close". Any object can publish actions to be added to the inspector menus.
The Preview view is available for all visual components and images. For example, try
Image parcPlaceLogo inspect
Now come back to the inspector with the VisualLauncher window. Because the window is a member (the root, in fact) of a hierarchy of visual components, there is an extra item on the "Explore" menu: "Component Hierarchy". Select it.
The inspector now shows the component tree of the window. Expand some of the branches and see how selecting components refocuses the inspector. Don't miss this interesting twist: switch the inspector to the "Methods" view and you end up with a browser on the group of classes of the window components.
Understanding of the component hierarchy is not hard-coded into the inspector. The hierarchy is reported by the object itself. Some other objects that form hierarchies are classes, exceptions (with exception classes being members of two hierarchies at the same time), parse tree nodes, UI specs. All those hierarchies can be explored the same way.
One final way of navigating objects is using menu items at the bottom of the "Go" menu. Any object has a "Go>To Class" menu item that visits the class of the current object. In addition to that, an object can tell the inspector about other important objects somehow related to it. For example, revisit an ApplicationWindow we inspected at one of the prior steps (use the "History" menu to find it). The "Go" menu now includes items that can take you to all the other important objects related to that window: the model, the controller, the main visual component, and the application. All of these objects are in fact instance variables of the window, though is much easier to use the menu than to find them in a list of more than variables.
All the groups of objects we explored so far were formed using "hard grouping"--in other words, mechanically, based on a clearly defined criterion such as all parts of an object, or all objects bound with a parent-child relationship. There is one final object group of interest we might want to see, and that one is a "soft group". It is not selected for us by the inspector, we discover it ourselves as we travel through the object graph in the inspector. All the objects we visit are somehow important to our task at hand. As we dive into objects, then go back and dive into other objects, these visits form a tree. The final navigational feature of Trippy is remembering that tree of visits and opening it for exploring like any other hierarchy. Select "Explore>Visited" in the inspector we originally opened on ScheduledControllers and see all the objects we've visited during this demonstration.
Note that, since Trippy is a new and still highly experimental tool, the interfaces described in this section may change in future releases.
By default, an object will have two views in the inspector: "Basic" and "Methods", with "Basic" view showing "self" and all named and indexed variables of the instance. The traditional inspector customization of a custom #printOn: will, of course, work, but there are more powerful and flexible ways of customizing the presentation of your objects.
One is adding virtual attributes to the basic view of an object. This is done by defining an instance-side method #inspectorExtraAttributes. The method should return a sequence of instances of either DerivedAttribute or TextAttribute (both classes are in the Tools.Trippy namespace). The difference between the two is that a DerivedAttribute has an object value -- for example, it can be dragged and dropped on a variable to store its value in that variable. An example of a DerivedAttribute is the "asInteger" attribute of a Character. "self" that shows up in any basic view is also a DerivedAttribute. A TextAttribute is an attribute without an object value, just with a text shown in the text view of an inspector. An example are the various radix print strings of an Integer. See implementors of #inspectorExtraAttributes for examples.
Adding easily selectable object actions to the "Object" and the part list menu is done in a similar way: define an instance-side method #inspectorActions answering a sequence of instances of Action (also in Tools.Trippy namespace). See implementors of #inspectorAction for examples.
To define objects to jump to using the "Go" menu, define a method #inspectorCollaborators answering a sequence of instances of Collaborator.
To tell the inspector that an object is a part of a hierarchy (or several hierarchies), define a method #inspectorHierarchies answering a sequence of instances of Hierarchy.
If you object supports one of the standard collection protocols, such as Set (can meaninfully respond to #size, #includes:, #remove:, and the #-ect: messages) and you want to have an "Elements" tab in the inspector, define the #inspectorClasses method that returns a collection of *classes* of inspectors that can meaningfully display an object.
To define your own view of an object, create a subclass of Inspector (that's Tools.Trippy.Inspector, not the classic Tools.Inspector) and include it into the list of classes returned by #inspectorClasses of your object.
And finally, as the last demo of this walkthrough, here is how to make an instance variable of a class protected so it cannot be accidentally changed by Trippy--using Trippy itself. Open an inspector on a Point:
(3 @ 4) inspect
Suppose we want to make the 'y' variable protected. Like all other customizations, to do this we have to add a method to Point. Switch to the Methods view, switch to the class side, add a protocol called 'user interface', and add a new method:
protectedInstVarNames
^#('y')
Accept the method and switch back to the Basic view. 'y' is now protected.
Note that unlike all other customizations, intance variables are declared as protected on the class side, since it is the class that keeps track of instance variables. Another important difference is that #protectedInstVarNames methods defined throughout the superclass chain of a class are used to determine the variables of a given instance that need protection. Because of that, a method in a subclass can only add, but not remove, protection defined in a superclass.
To share comments and suggestions, send a message to the VisualWorks NC mailing list or directly to Vassili Bykov.
Object presentation:
| Edit | Rename | Changes | History | Upload | Download | Back to Top |