=== Top of the Swiki === Attachments ===
Eye Candy III
Here's the look that we're going to work on next:
fancywork.gif A 200K GIF file which is a picture of the project. (I seperated this out of the text for slow bandwidth users).
Different colors, fancy buttons, the whole nine yards. And I'm going tell you some extra stuff, like Morphic menus. For this example, your display should be set to a bit depth of 16.
Here's the changeset: EyeCandy.cs
And here's a page with the changeset: Eye Candy Code
There's a couple of things I'd like to talk about at this point. I'm changing the format a bit as our code base grows. First, as you have probably noticed, there is quite a bit of code in the changeset. Rather than go through each method by tedious method, I'm just going to point you to several interesting methods, and leave the rest for you to explore. Second, I'm going to talk about how I designed the 'Fancy Workspace' window. Also, throughout the text I will intersperse some things you need to think about when you are putting together your own user interfaces.
Let's look at the GradientButtonMorph first. If you remember back a couple of pages, when we created a button the initial color was gray at the top and black on the bottom, with black text. Obviously, this was way bad. Way bad for a couple of reasons. First, there is not enough contrast to indicate to the user that this is actually a control of some sort. Rule number 1: If you put a control on the screen, let people know that it is a control and not just some fancy decoration. You're taking up valuable screen real estate, advertise! Rule number 2: There's that whole people thing. As you know, a lot of folks (especially males) have varying degrees of color blindness, so you need to have light/dark contrast in addition to color contrast in your design. Also, you can get away with light to dark, but usually not dark to darker. So those gothic themes that you see that are variations of dark gray to black are just about invisible to a lot of folks. When you first saw the ButtonGradientMorph you thought, "This really sucks". You were right.
So the first fix is in GradientMorphButton #initialize where we assign the topColor to be lightGray and the bottomColor to be black. So try:
GradientButtonMorph new openInWorld
and you'll see a much shinier and brighter GradientButtonMorph smiling back at you. You may actually be able to read the text on the button now too.
Let's talk a little bit about sizing a button. Throughout the Morphic system you'll see things like: #extent: , #position: and #layoutChanged. These are usually located in the category "geometry". #position: places the Morph at a particular location in the World. #extent: sets the extent, or the size, of the Morphs bounding rectangle. #layoutChanged is sent to the Morph when someone has changed the geometry of the Morph, such as changed the bounding box. As an example, when we receive a #layoutChanged message in GradientButtonMorph, we recenter the label within the bounding box of the button.
The idea for the 'Fancy Workspace' is simple. Add a row of frequently used tool buttons at the top of a Workspace window. This idea is lifted from Stephen Travis Popes STP 12 tools. This implementation shows how to do a generic toolbar. You can just as easily substitue iconic buttons for the GradientButtonMorphs or draw a bitmap on top of the GradientButtonMorphs for an iconic toolbar if you want to.
Our old friend the AlignmentMorph is the container that holds the buttons, and is responsible for lining our ducks up in a row. We simply create an AlignmentMorph, with the orientation set to #horizontal, and add our buttons. This is done in WorkspacePane #addButtons. Also in #addButtons are several lines of the form:
button setBalloonText: 'Open up a System Browser window'
#setBalloonText: is a message which sets a Morphs balloon text. Ballon text is displayed if you allow the cursor to stay over a Morph for a little while. An example of this is the menu icon in a SystemWindow label. Place your cursor over the menu icon for a couple of seconds and a balloon that says "window menu" pops up.
Now things get a little harder because we have to make a design decision. There are several choices, but I decided I would just create a new class from scratch to act as a WorkspacePane. I could have subclassed class Workspace, and added the changes, but I wanted to show you how to create a class like this from scratch. We reuse several of the methods of Workspace to help us get started.
The major component of a workspace is a PluggableTextMorph. A PluggableTextMorph is a composite Morph which is both a kind of scrolling pane and TextMorph at the same time. We are going to use that baby, so that our intialization looks like this:
initialize
| tm |
super initialize.
self color: (Preferences windowColorFor: #StringHolder).
"Initialize the state of the receiver with its default contents."
contents _ self defaultContents.
self addButtons.
tm _ PluggableTextMorph
on: self
text: #contents
accept: #acceptContents:
readSelection: nil
menu: #codePaneMenu:shifted:.
tm position: 0 @ GradientButtonMorph buttonHeight.
self addMorph: tm.
self extent: self initialExtent.
We do a couple of things in the #initialize method. We set the color of the pane to be the selected color of Workspaces ( #StringHolder ) from the Preferences class. The instance variable 'contents' holds the text of the workspace.
A Workspace has a special menu. We tell the system to send us the message #codePaneMenu:shifted: when it wants that menu. That message will be sent to us if we place this pane within a SystemWindow as an example.
We position the PluggableTextMorph at a fixed position beneath our toolbar buttons by saying:
tm position: 0 @ GradientButtonMorph buttonHeight.
So far so good, let's try:
WorkspacePane new openInWorld.
Up pops a WorkspacePane! Wowee. It's a regular Morph like any other. Play with it a little. Put the cursor over one of the buttons for a second or two, and up pops the help balloon. The buttons are alive, and ready to roll. Try that out, see what happens when you click on one. You can bring the halos up and move the pane around on the screen.
The workspace pane is alive, you can type code in and execute it. This is where Morphic is different from most other systems that you have experienced. You can create these objects and instantiate them, and they are full live objects in the system. You can explore them, edit them, deconstruct them, send messages to them, all sorts of fun stuff.
Most of our system tools we keep in windows to give them a little more structure. On the class side of WorkspacePane there is a method called openInWindow which does just that. Of special interest is the line:
window _ (SystemWindow labelled: 'Fancy Workspace') model: wp.
This creates a SystemWindow Morph and sets the model to be the WorkspacePane that we just created. Basically, a model is responsible for handling the underlying housekeeping and data storage for a group of Morphs.
Remember back to our WorkspacePane menu. When you pull up a menu when the WorkspacePane is in a SystemWindow, the SystemWindow tells its model to put up the menu appropriate to the model. In our case, that filtered down to #codePaneMenu:Shifted which provides the standard text editing menus. In addition, a SystemWindow allows the model to add lines to the title bar menu which we respond to in the message #addModelItemsToWindowMenu: . This allows us to add menu line items like "save contents to file..." which lets us interact with the models data.
The methods #content and #bindingOf: provide underlying data to the WorkspacePane model. Some models are very complicated. Most of the time though they just provide some glue to keep things together, and a place to share common data between Morphs. In this example, it lets the SystemWindow talk to the WorkspacePane.
Another interesting line is:
window addMorph: wp frame: (0@0 corner: 1@1).
A SystemWindow has an attribute which we call a layout. The layout is reponsible for keeping each pane in an assigned location when the window is resized. The line above says, "Add the WorkspacePane Morph to the window and have it take up the entire window". If we say something like:
window addMorph: wp frame: (0@0 corner: 1@0.5).
then the pane only takes the top 1/2 of the window. The points represent where you want the Morph placed inside the window on a relative scale of 0.0 to 1.0 . That's how the Browsers and other multi-panel windows keep their layouts straight, they have a little map which guides their placement in the window.
Now we'll talk about setting the background of a WorkspacePane.
WorkspacePane background
Jim Benson
jb@speed.net