One of the earliest things to consider in handling mouse-down events is whether the receiving NSView object should become the first responder, which means that it will be the first candidate for subsequent key events and action messages. Views that handle graphic elements that the user can select—drawing shapes or text, for example—should typically accept first responder status on a mouse-down event by overriding the acceptsFirstResponder method to return YES, as discussed in “Preparing a Custom View for Receiving Events.”
By default, a mouse-down event in a window that isn’t the key window simply brings the window forward and makes it key; the event isn’t sent to the NSView object over which the mouse click occurs. The NSView can claim an initial mouse-down event, however, by overriding acceptsFirstMouse: to return YES. The argument of this method is the mouse-down event that occurred in the non-key window, which the view object can examine to determine whether it wants to receive the mouse event and potentially become first responder. You want the default behavior of this method in, for example, a control that affects the selected object in a window. However, in certain cases it’s appropriate to override this behavior, such as for controls that should receive mouseDown: messages even when the window is inactive. Examples of controls that support this click-through behavior are the title-bar buttons of a window.
In your implementation of an NSResponder mouse-event method, often the first thing you might do is examine the passed-in NSEvent object to decide if this is an event you want to handle. If it is an event that you handle, then you may need to extract information from the NSEvent object to help you handle it. Specifically, you can get the following information from the NSEvent object:
Get the location of the mouse event in base coordinates (locationInWindow) and then convert this location to the receiving view’s coordinate system; see “Getting the Location of an Event” for details.
See if any modifier keys were pressed when the mouse button was clicked (modifierFlags); this procedure is described in “Testing for Event Type and Modifier Flags.” An application may define modifier keys to change the significance of a mouse event.
Find out how many mouse clicks occurred in quick succession (clickCount); multiple mouse clicks are conceptually treated as a single mouse-down event within a narrow time threshold (although they arrive in a series of mouseDown: messages). As with modifier keys, a double- or triple-click can change the significance of a mouse event for an application. (See Listing 4-3 for an example.)
If the interval between mouse clicks is important, you can send timestamp to the NSEvent object and record the moment each event occurred.
If the change in position of the mouse between subsequent events is important, you can find out the delta values for the x-coordinate and y-coordinate using, respectively, deltaX and deltaY.
Many view objects in the Application Kit (such as controls and menu items) change their appearance in response to mouse-down events, sometimes only until the subsequent mouse-up event. Doing this provides visual confirmation to the user that their action is effective or that the clicked object is now selected. Listing 4-1 shows a simple example of this.
Listing 4-1 Simple handling of mouse click—changing view’s appearance
- (void)mouseDown:(NSEvent *)theEvent { |
[self setFrameColor:[NSColor redColor]]; |
[self setNeedsDisplay:YES]; |
} |
- (void)mouseUp:(NSEvent *)theEvent { |
[self setFrameColor:[NSColor greenColor]]; |
[self setNeedsDisplay:YES]; |
} |
- (void)drawRect:(NSRect)rect { |
[[self frameColor] set]; |
NSRectFill(rect); |
} |
But many view objects, particularly controls and cells, do more than simply change their appearance in response to mouse clicks. One common paradigm is for the clicked view to send an action message to a target object (where both action and target are settable properties of the view). As shown in Listing 4-2, the view typically sends the message on mouseUp: rather than mouseDown:, thus giving users an opportunity to change their minds mid-click.
Listing 4-2 Simple handling of mouse click—sending an action message
- (void)mouseDown:(NSEvent *)theEvent { |
[self setFrameColor:[NSColor redColor]]; |
[self setNeedsDisplay:YES]; |
} |
- (void)mouseUp:(NSEvent *)theEvent { |
[self setFrameColor:[NSColor greenColor]]; |
[self setNeedsDisplay:YES]; |
[NSApp sendAction:[self action] to:[self target] from:self]; |
} |
- (SEL)action {return action; } |
- (void)setAction:(SEL)newAction { |
action = newAction; |
} |
- (id)target { return target; } |
- (void)setTarget:(id)newTarget { |
target = newTarget; |
} |
Listing 4-3 gives a more complex, real-world example. (It’s from the example project for the Sketch application.) This implementation of mouseDown: determines if users double-clicked a graphical object and, if they did, enables the editing of that object. Otherwise, if a palette object is selected, it creates an instance of that object at the location of the mouse click.
Listing 4-3 Handling a mouse-down event—Sketch application
- (void)mouseDown:(NSEvent *)theEvent { |
Class theClass = [[SKTToolPaletteController sharedToolPaletteController] currentGraphicClass]; |
if ([self editingGraphic]) { |
[self endEditing]; |
} |
if ([theEvent clickCount] > 1) { |
NSPoint curPoint = [self convertPoint:[theEvent locationInWindow] fromView:nil]; |
SKTGraphic *graphic = [self graphicUnderPoint:curPoint]; |
if (graphic && [graphic isEditable]) { |
[self startEditingGraphic:graphic withEvent:theEvent]; |
return; |
} |
} |
if (theClass) { |
[self clearSelection]; |
[self createGraphicOfClass:theClass withEvent:theEvent]; |
} else { |
[self selectAndTrackMouseWithEvent:theEvent]; |
} |
} |
The classes of the Application Kit that implement controls manage this target-action behavior for you.
Last updated: 2007-03-16