Uli's Web Site
Using Cocoa in SuperCompiler
After my Introduction to SuperCompiler, let's go and do something slightly more advanced. Let's write our first useful XCMD that does something you wouldn't be able to do in straight SuperTalk.
In fact, what we will be doing is write some Cocoa code that will let us find out what "control tint color" the user has selected, Aqua or Graphite. This can be very handy when drawing your own user interface elements, because you'll want them to be blue or grey, depending on what tint the user chose. So, fire up SuperCard and open the SuperCompiler project and we can get going...
Finding your way around Apple's documentation
Then open a web browser and open Apple's Developer web site. Somewhere there you will find a link to the Reference documentation. At the time of writing, it's at the left under the heading "Reference Library" and is a link called Reference. There, click on Cocoa to get to Apple's Cocoa reference documentation, i.e. their equivalent to SuperCard's SuperTalk Language Guide.
We want to do something related to drawing and graphics, so we click on Graphics and Imaging. In the following list, you'll find two mentions of colors, CIColor and NSColor. Everything that is "normal" in Cocoa generally starts with "NS" ("NextStep"). Stuff that starts with other letters is for more complex things. "CI" for example stands for "CoreImage" and is part of Apple's fast, new drawing stuff for effects, so let's look at the NSColor documentation.
We could probably have found this page much more quickly by just using Google's advanced search and telling it to only look for stuff on developer.apple.com, but since you'll not always know that you are looking for control tints, it's better we took the scenic route this time around.
Basics of using Cocoa in SuperCompiler
If you scroll around a little, you will find a Cocoa command that is called "currentControlTint", and in its entry, you'll find a "typewritten" line which looks like this:
+ (NSControlTint)currentControlTintThis is called a prototype, and is pretty much what you need to know to use a command. You have to know that in Cocoa every command is associated with an object. Like in SuperCard, where each handler is in some object's script. We have the documentation for NSColor here, so these commands all belong in NSColor's script.
This command is actually a function. The "NSControlTint" in brackets is a description of the return value you can expect from this function. Click on it on Apple's web site to see a description of it. To use this command in SuperCompiler, you use it the following way, making sure you write the uppercase/lowercase characters exactly as shown in Apple's documentation:
put [NSColor currentControlTint] into myTintThis is intentionally similar to how you would write it in Objective C to make it easier to work with Apple's documentation. If you were to write this in SuperTalk somehow, it would be equivalent to:
put currentControlTint() via NSColor into myTintNow, if you look at the description for NSControlTint, you'll find that it is basically a list of numbers. They're constants. Just like SuperTalk has "pi" to mean 3.14..., we have NSBlueControlTint to mean 1, NSGraphiteControlTint to mean 6 etc.
So, if we were to give up easily, we would write the following XFcn:
function currentControlTint put [NSColor currentControlTint] into theTint if theTint = 1 then return "aqua" else if theTint = 6 then return "graphite" end if end currentControlTintBut really, wouldn't it be much nicer to have the actual color for our tint? So, let's look some more through the documentation. Maybe we can find a way to get from an NSControlTint to a color?
We can improve him!
Yes, there is "colorForControlTint:" and in its entry, you'll find its prototype, which looks like this:
+ (NSColor *)colorForControlTint:(NSControlTint)controlTint
Now, you need to know that NSColor is a class. A class is an object that is a factory for creating other objects. There is only one factory, but it can be used to create any number of objects, whose "ID" you can then put in a variable. Then you can call functions from the script of that object. The plus sign at the start of the prototype of this function and currentControlTint above means that this command is in the factory's script. Commands that have a minus sign at the start are ones that are in the scripts of objects you create using the factory.
This time, the return value is described as "NSColor *". The asterisk means that it is some sort of "object ID". Now, since we can easily get at NSColor just by writing "NSColor", an ID can only mean this will be an actual color created by our NSColor factory. That sounds like we're on the right track!
The "(NSControlTint)controlTint" part after the colon is the description of the one parameter this function takes. It's roughly equivalent to
function colorForControlTint controlTintThe parts in brackets are simply descriptions of what to put in controlTint or what the function will give back. With that knowledge, let's extend our XFcn:
function currentControlTint put [NSColor currentControlTint] into theTint put [NSColor colorForControlTint: theTint] into theColor -- what now ... ? end currentControlTintOkay, so now we have an NSColor object in theColor. How do we get at its color info? Well, scroll around a little. This time, ignore all the nice methods with "+" signs. You'll come across three nice functions named
-(float) redComponent -(float) greenComponent -(float) blueComponentThat sounds good. Now, if you read SuperCompiler's Help, in particular the chapter on calling system functions, you'll know that "float" is a floating point number. That's because in Cocoa, color values go from 0 to 1, not from 0 to 255 like in SuperCard... but that's nothing a little multiplication won't cure.
Okay, so we call the redComponent handler on theColor in our XFcn:
function currentControlTint put [NSColor currentControlTint] into theTint put [NSColor colorForControlTint: theTint] into theColor return [theColor redComponent] end currentControlTint
A slight bump in the road...
Now, if you try this out by typing put currentControlTint() in the message box, you'll find that this doesn't work. Your XFcn will give back empty, and if you look at the console, you'd find the following error message:
2006-12-03 23:41:35.215 SuperCard 4.6 Objective C error: *** -redComponent not defined for the NSColor NSNamedColorSpace System blueControlTintColor; need to first convert colorspace.This complains that our color doesn't understand the redComponent function. What? Yes, well, he reason for this is that Cocoa's colors aren't just RGB triplets. They can be everything from named patterns to CMYK or HSB colors. So, what you need to do is convert this color into an RGB color. To do that, we'll search some more through the docs, what looks good is:
- (NSColor *)colorUsingColorSpaceName:(NSString *)colorSpaceNow NSString is just a string of text. And while this function returns "NSColor*", it has a "-" at the start, so it's not a function in the factory, but rather a function that other colors have. Trouble is, what is a color space name? Well, I guess we'll have to google for that, because there's no nice clickable link to a description available. Eventually, you'll find Apple's documentation for color spaces, and Table 1 on that page holds a list of color space names.
NSDeviceRGBColorSpace looks good. So we'll use that name:
put [theColor colorUsingColorSpaceName: "NSDeviceRGBColorSpace"] into rgbColorAnd now we have a color that understands our redComponent function!
The final polish, or hungarian, or ...
Now, let's finish our XFcn; we want all three color components, and since they only go from 0 to 1, but we need 0 to 255, we'll also multiply each of the components by 255. Since this may give a fractional number, we'll also have to use trunc() to truncate the number to an integer. The result is the following XFcn:
function currentControlTint put [NSColor currentControlTint] into theTint put [NSColor colorForControlTint: theTint] into theColor put [theColor colorUsingColorSpaceName: "NSDeviceRGBColorSpace"] into rgbColor put trunc([rgbColor redComponent] *255) into theRed put trunc([rgbColor greenComponent] *255) into theGreen put trunc([rgbColor blueComponent] *255) into theBlue return theRed & "," & theGreen & "," & theBlue end currentControlTintThat's it. Click the "Create XCMD" button and when it's finished type "put currentControlTint()" into the message box, and see the current control tint color!
That concludes our first trip into writing our first SuperCompiler XCMD. I admit this doesn't look a lot like SuperTalk anymore, but the advantage is that you can do stuff like this when needed, and use regular SuperTalk whenever that is sufficient. You can conquer the world from the comfort of SuperTalk, so to say.
Questions? Comments? Suggestions for future SuperCompiler tutorials? And remember, I hang out on the SuperCardXCmd mailing list, so if you want to try this, ask questions there and I'll help you sort out any issues you may have.
Created: 2006-12-03 @064 Last change: 2006-12-04 @119 | Home | Admin | Edit|
© Copyright 2003-2014 by M. Uli Kusterer, all rights reserved.