Updated on January 7, 2009 to reflect changes in F-Script 2.0 alpha 7.
The following program illustrates subclassing Cocoa classes with F-Script 2.0. It displays a circle that can generate psychedelic effects by displaying flashes of colors. Here is a screenshot of the application:
The main component of the program is PsychedelicCircleView a subclass of NSView. This graphical component takes care of displaying the animated circle and handles mouse events, allowing the user to start and stop the psychedelic process. To that end, it implements the standards drawRect: and mouseDown: methods.
Below is the code for our NSView's subclass. To execute it, all you have to do is to copy/paste it in the F-Script console. Then copy/paste the code in charge of putting the component on screen (provided further below).
"Define the PsychedelicCircleView class, a subclass of Cocoa's NSView"
PsychedelicCircleView : NSView
{
"Instance variables"
timer "A NSTimer object used to animate the view"
message "The message to display"
attributes "A dictionary holding drawing attributes"
"Define the designated initializer"
- initWithFrame:(NSRect)frame
{
self := super initWithFrame:frame.
self ~~ nil ifTrue:
[
|font| "A local variable to hold the font object"
"Determine the font to use.
We use Synchro LET if available, the default user font otherwise"
font := NSFont fontWithName:'Synchro LET' size:40.
font == nil ifTrue:[ font := NSFont userFontOfSize:40 ].
"Initialize the attributes dictionary used to draw the message"
attributes := NSDictionary dictionaryWithObjects:{font}
forKeys:{NSFontAttributeName}.
"Initialize the message to display"
message := ' Click to start/stop\nexpansion of consciousness'.
].
^ self
}
"Define the method invoked by Cocoa to draw the view"
- (void) drawRect:(NSRect)aRect
{
"Define local variables"
|red green blue size x y|
"Generate random values for color components"
red := 10 random / 9.
green := 10 random / 9.
blue := 10 random / 9.
"Set the color and draw the circle"
(NSColor colorWithCalibratedRed:red green:green blue:blue alpha:1) set.
(NSBezierPath bezierPathWithOvalInRect:self bounds) fill.
"If the psychedelic mode is not active, draw a message"
timer == nil ifTrue:
[
"Compute coordinates of the message in order to have it centered"
size := message sizeWithAttributes:attributes.
x := self bounds extent x / 2 - (size width / 2).
y := self bounds extent y / 2 - (size height / 2).
"Draw the message"
message drawAtPoint:x<>y withAttributes:attributes.
].
}
"Define the method invoked by Cocoa when the view is clicked"
- (void) mouseDown:(NSEvent *)theEvent
{
timer == nil ifTrue:
[
"Create a NSTimer object to put the psychedelic process in motion"
timer := NSTimer scheduledTimerWithTimeInterval:0.01
target:[self setNeedsDisplay:YES]
selector:#value
userInfo:nil
repeats:YES
]
ifFalse:
[
"Stop the psychedelic process"
timer invalidate.
timer := nil.
self setNeedsDisplay:YES.
]
}
}.
The definition of our custom view class illustrates several elements of the class syntax: subclassing an existing Cocoa class, defining instance variables and methods, using self and super, etc. You’ll find further information in these posts:
Now that our view class is defined, we can put it in a window and play with it:
"Instantiate and configure a window"
window := NSWindow alloc initWithContentRect:(0<>0 extent:700<>700)
styleMask:NSTitledWindowMask + NSClosableWindowMask + NSMiniaturizableWindowMask + NSResizableWindowMask
backing:NSBackingStoreBuffered
defer:NO.
window setBackgroundColor:NSColor blackColor;
setReleasedWhenClosed:NO;
setTitle:'Psychedelic Circle';
center.
"Instantiate and configure a psychedelic circle"
circle := PsychedelicCircleView alloc initWithFrame:window contentView bounds.
circle setAutoresizingMask:NSViewWidthSizable + NSViewHeightSizable.
"Put the circle view in the window"
window contentView addSubview:circle.
"Put the window onscreen"
window makeKeyAndOrderFront:nil.
The code above should open a window and display the psychedelic circle.
This is a good opportunity to experiment with live class redefinition. While the psychedelic circle is flashing, you can modify the program and see your modifications take effect immediately. For example, in the drawRect: method, replace the call to bezierPathWithOvalInRect: by a call to bezierPathWithRect:. Now, if you look at the flashing circle, you'll notice that it has turned into a flashing rectangle.
Note that, at the time of this writing, all the users of this program (i.e. me and my cat) are not experiencing an expansion of their consciousness by looking at the psychedelic circle. In fact, 50% of the users declare that the only thing this program expands is their headaches. The other 50% try compulsively to catch flies while producing strange sounds, just as usual.
Read Full Post »