Check Check Roger Roger
Last updated at 1:04 pm UTC on 16 January 2006
When we last left our hero, Johnny, he was struggling through some pretty tough stuff. Backgrounds here, fancy buttons there, workspaces, menus all sorts of wild looking stuff. All eye candy, no substance.
As there is no reason to start doing something useful yet, we're going to talk a little bit about fancy controls. First up, the check box. Here's the look we're shooting for:
Here's the check box changeset: checkboxmorph.st
The actual code is here: Check Check Roger Roger
Now Squeak has a perfectly serviceable check box implementation. It is based on the ThreePhaseButtonMorph. It looks something like this:
Let's talk about how the standard implementations works. When you send ThreePhaseButtonMorph the #checkBox message, you get back an instance of ThreePhaseButtonMorph that holds three images. The three images are an empty check box, a 'checked' checkbox, and a third image that is displayed when the mouse button is down with the cursor within the checkbox area.
It's up to you to add a label string to the image morph. You'll see this in the image as a code sequence such as: Create a horizontal AlignmentMorph, add a ThreePhaseButtonMorph with #checkBox, add a spacer, and add a StringMorph for the label. One way to customize this particular implementation is to replace the images that are used for the check box with other images, to fancy it up a bit. Pretty standard stuff, that's the way the Linux guys fancy up their check boxes, and we already have a good handle on how to do this ourselves.
But you know us, never do something easy when you could just as easily obfuscate a nice implementation. Rather than just do the simple and obvious, we're going to make some engineering decisions and have a little bit of fun.
Most operating systems are pretty strict with how their controls look. Usually there is some standard look for the check box, along with standard fonts for the control labels. That provides for consistency across different applications.
Of course, we just ignore all that and come up with our own look, under the slogan of "write once, run anywhere". I've found it is much easier to come up with a catchy slogan than to actually write code, and a good slogan can get you out of doing a lot of busy work, believe me. It's much easier that way, and you don't have to listen to all those silly people who have nothing better to do all day than sit around making sure that your product complies with ISO something something or other.
If one of those people tries bothering you about product compliance, just give them a pained look and say, "This product conformed to that standard several months ago, the current product is in a transitional state. I was told the product really needed to conform to ISO something something or other 3.2675 2001 by the end of August, or it was going to be my job!" This will send them scurrying back to their cubicle to sign up for the next ISO conference (usually held in some nice place like Switzerland in the late spring) so that they can get up to speed on the new standard.
Remember what I said about law makers and programmers? Well standards guys are the same way too. By the time that someone would normally figure out that you were actually bluffing, by golly, another standard has been written! Count on it. If you're smart, you can use variations of this scheme so that you may never have to have your product comply!
Your competitors will help you with this. They have the same type of compliance police working at their place, and they have to compete against you in the marketplace. So they implement a solution which very nearly conforms to the ISO something something or other PLUS they add their own feature set. Wouldn't be much competition if all of the different packages worked the same, would there?
If you don't believe me, think about Microsoft Java. This same ruse was used by Ford on the Model T. "You can have any color Model T that you want, as long as it is black". Microsoft uses the same trick 75 years later, "Microsoft Java is a write once, run anywhere programming environment (as long as you are writing and running on a Microsoft operating system)". You've got to like that. Java, lots of standards, lots of companies, lots of trips to Switzerland.
You should take advantage of this by telling your folks, "It's not good enough that we conform to the ISO standard, we must have something better. Look at our competitors." Then you can disregard the ISO standards all together and go your own way. It only takes one maverick to break the standards, why shouldn't it be you?
I want to remind you at this point that I don't actually encourage you to lie. I encourage you to lie AND exaggerate to such an extent that things seem so unbelievable that they must be true. This will give you much more time to write Morphic code.
Now let's talk about the look we're shooting for.
We are going to write algorithm drawn controls. Instead of pasting different images on our control boxes, we are going to draw them ourselves.
First, we are going to use an 'X' in our check box. Every implementation of an actual check mark in a check box that I've seen has looked peculiar to me, perhaps because you can never quite center a check mark in a square correctly. I'm not the tidiest person in the world, but it just doesn't look right to me for some reason.
Second, we want this to look a little bit 3D. We do this in a couple of ways. We make the rectangle that the check box sits in look 3D by putting raised borders around it. Another thing that we do is make the 'X' in the check box look embossed. A trick is commonly used to make text look bold. We modify this trick a little to make the 'X' look as if it is embedded in the check box.
Third, we are going to set our label to a bold faced large size font so that it will advertise itself as a control.
So, we extend SimpleButtonMorph yet again to create CheckButtonMorph. Yes, I know that there are other Morphs, and maybe even one day I'll use one of them. But not today my friend.
When we look at the #initialize method:
| rect x |
submorphs _ EmptyArray.
bounds _ 0@0 corner: 10@10.
self borderWidth: 0.
target _ nil.
actionSelector _ #check:.
arguments _ Array new: 1.
actWhen _ #buttonUp.
" Prepare the X box ; set position at upper left hand corner "
rect _ RectangleMorph new extent: 16 @ 16.
rect position: 1 @ 1 .
rect borderWidth: 2.
rect borderColor: #raised.
self addMorph: rect.
" Add the background X "
x _ StringMorph contents: 'X' font: Preferences standardButtonFont.
x position: 5 @ 1.
x color: Color white.
rect addMorph: x.
" Add the foreground X "
x _ StringMorph contents: 'X' font: Preferences standardButtonFont.
x position: 4 @ 0.
rect addMorph: x.
selected _ false.
" The color of the checkbox is a light gray "
self color: Color veryLightGray lighter.
we notice several things. First, there is a lot of initialization going on. The main reason for this is that we don't want to use the initialization code for a SimpleButtonMorph. Instead, I went back and grabbed some of the initialization code from Morph to get the basic underpinnings working.
Remember that we are creating a composite Morph here. It consists of several things. First, there is the RectangleMorph that represents the check box. Second, there is the actual check itself, which we depict with a character 'X'. Third, there is a StringMorph which represents the check box label.
Here's one of the tricks that I was telling you about. One of the ways to boldface a piece of text is to display the text, and then display the text again starting at an offset of one pixel from where the text was first displayed. In our case, we want an 'embossed' look for the 'X' in the check box. We achieve this by displaying a black colored 'X' and then a white colored 'X' underneath offset by 1@1. So we have two StringMorphs, both with the string 'X' to initialize. We hide and show these 'X' based on the current state of the check box.
Another interesting statement is:
This has the effect of making the Morph 'invisible' when you try to deconstruct a Morph. In other words, you won't be able to select the 'X' StringMorphs by the command click method. We do this because we don't want folks mucking about with the 'X's when they're playing with the Morph.
In the #initialize method we also set the color to a light gray. Remember that a control has to look good at all display depths, so if you change the color of a check box, make sure it looks OK in 8 and 16 bit color at least. Also, note that you can set the color of the check box to be any color that you want from the CheckBoxMorphs menu. This is different from most operating system check boxes.
Also in the #initialize method, we set the default label to be 'Check Box' using the #label: method. This method also lays out where the label string is positioned. When I wrote this, I hard wired where the StringMorph ends up, 6 pixels past the edge of the check box. I also used a specific bold faced font for the controls label. Note that we should probably factor this into some global preference, or used the Squeak default, but hey, I need to leave some work for you to do!
When I first implemented the CheckBoxMorph I bungled badly. Fortunately Morphic king Bert Freudenberg set me straight. Read about it here: Jim Bungles Check Box
There are a couple of auxillary methods to hide and show the 'X' in the check box, and the usual mouse event handlers to check and uncheck the box. Pretty straightforward when you look thru the code.
Off we go to Radio Buttons