Cocoa Programming for Mac OS X - Aaron Hillegass [87]
NSResponder (from which NSView inherits) has a method called interpretKeyEvents:. For most key events, it just tells the view to insert the text. For events that might do something else (such as Tab or Shift-Tab), it calls methods on itself.
In keyDown:, you simply call interpretKeyEvents:
- (void)keyDown:(NSEvent *)event
{
[self interpretKeyEvents:[NSArray arrayWithObject:event]];
}
Then you need to override the methods that interpretKeyEvents: will call:
- (void)insertText:(NSString *)input
{
// Set string to be what the user typed
[self setString:input];
}
- (void)insertTab:(id)sender
{
[[self window] selectKeyViewFollowingView:self];
}
// Be careful with capitalization here, "backtab" is considered
// one word.
- (void)insertBacktab:(id)sender
{
[[self window] selectKeyViewPrecedingView:self];
}
- (void)deleteBackward:(id)sender
{
[self setString:@" "];
}
@end
Build and run your program. You should see that your view becomes the first responder. While it is first responder, it should take keyboard events and log them to the terminal. Also, note that you can Tab and Shift-Tab between the views (Figure 19.11).
Figure 19.11. Completed Application
(Yes, acceptsFirstResponder gets called more times than you might expect (each time the view is selected)).
For the More Curious: Rollovers
Three mouse events were not discussed in Chapter 18: mouseMoved:, mouseEntered:, and mouseExited:.
- (void)mouseMoved:(NSEvent *)event
To receive mouseMoved:, the view’s window needs to accept “mouse-moved” events. If it does, the mouseMoved: message is sent to the window’s first responder. To set the window to get mouse-moved events, you send it the message setAcceptsMouseMovedEvents:
[[self window] setAcceptsMouseMovedEvents:YES];
At this point, the view will be sent the message every time the mouse moves. This is a lot of events. When people ask us about mouse-moved events, we ask them why they want it. They usually say, “Uh, rollovers.”
Rollovers are very popular in Web browsers. As you roll over a region, its appearance changes to make it clear that if you clicked now, that region would accept the click. Hyperlinks in Safari, for example, become highlighted when you roll over them.
To do rollovers, you don’t typically use mouseMoved:. Instead, you set up a tracking area and override mouseEntered: and mouseExited:.
When a view is put on a window, viewDidMoveToWindow gets called. This is a pretty good place to create tracking areas. By passing the NSTrackingInVisibleRect, the tracking area will automatically match the visible rect of the owner.
- (void)viewDidMoveToWindow
{
int options = NSTrackingMouseEnteredAndExited |
NSTrackingActiveAlways |
NSTrackingInVisibleRect;
NSTrackingArea *ta;
ta = [[NSTrackingArea alloc] initWithRect:NSZeroRect
options:options
owner:self
userInfo:nil];
[self addTrackingArea:ta];
}
Then, you change the appearance when mouseEntered: and mouseExited: are called. Assuming that you have a variable called isHighlighted of type BOOL, here is the code:
- (void)mouseEntered:(NSEvent *)theEvent
{
isHighlighted = YES;
[self setNeedsDisplay:YES];
}
- (void)mouseExited:(NSEvent *)theEvent
{
isHighlighted = NO;
[self setNeedsDisplay:YES];
}
You would then check isHighlighted in your drawRect: method and draw the view appropriately.
The Fuzzy Blue Box
Your BigLetterView gets a blue box around its edge when it is firstResponder. Note, however, that the box isn’t nice and fuzzy like the box around text fields. You want the fuzzy blue box? It takes a little work.
See where you draw the blue box in drawRect: in BigLetterView.m? Change it to look like this:
if (([[self window] firstResponder] == self) &&
[NSGraphicsContext currentContextDrawingToScreen]) {
[NSGraphicsContext saveGraphicsState];
NSSetFocusRingStyle(NSFocusRingOnly);
[NSBezierPath fillRect:bounds];
[NSGraphicsContext restoreGraphicsState];