In the main event loop, the application object (NSApp) continuously gets the next (topmost) event in the event queue, converts it to an NSEvent object, and dispatches it toward its final destination. It performs this fetching of events by invoking the nextEventMatchingMask:untilDate:inMode:dequeue: method in a closed loop. When there are no events in the event queue, this method blocks, resuming only when there are more events to process.
After fetching and converting an event, NSApp performs the first stage of event dispatching in the sendEvent: method. In most cases NSApp merely forwards the event to the window in which the user action occurred by invoking the sendEvent: method of that NSWindow object. The window object then dispatches most events to the NSView object associated with the user action in an NSResponder message such as mouseDown: or keyDown:. An event message includes as its sole argument an NSEvent object describing the event.
The object receiving an event message differs slightly by type of event. For mouse and tablet events, the NSWindow object dispatches the event to the view over which the user pressed the mouse or stylus button. It dispatches most key events to the first responder of the key window. Figure 1-3 and Figure 1-4 illustrate these different general delivery paths. The destination view may decide not to handle the event, instead passing it up the responder chain (see “The Responder Chain”).
In the preceding paragraph you might have noticed the use of qualifiers such as “in most cases” and “usually.“ The delivery of an event (and especially a key event) in Cocoa can take many different paths depending on the particular kind of the event. Some events, many of which are defined by the Application Kit (type NSAppKitDefined), have to do with actions controlled by a window or the application object itself. Examples of these events are those related to activating, deactivating, hiding, and showing the application. NSApp filters out these events early in its dispatch routine and handles them itself.
The following sections describe the different paths of the events that can reach your view objects. For detailed information on these event types, read “Event Objects and Types.”
The Path of Mouse and Tablet Events
The Path of Key Events
Other Event Dispatching
As noted above, an NSWindow object in its sendEvent: method forwards mouse events to the view over which the user action involving the mouse occurred. It identifies the view to receive the event by invoking the NSView method hitTest:, which returns the lowest descendant that contains the cursor location of the event (this is usually the topmost view displayed). The window object forwards the mouse event to this view by sending it a mouse-related NSResponder message specific to its exact type, such as mouseDown:, mouseDragged:, or rightMouseUp:, On (left) mouse-down events, the window object also asks the receiving view whether it is willing to become first responder for subsequent key events and action messages.
A view object can receive mouse events of three general types: mouse clicks, mouse drags, and mouse movements. Mouse-click events are further categorized—as specific NSEventType constants and NSResponder methods—by mouse button (left, right, or other) and direction of click (up or down). Mouse-dragged and mouse-up events are typically sent to the same view that received the most recent mouse-down event. Mouse-moved events are sent to the first responder. Mouse-down, mouse-dragged, mouse-up, and mouse-moved events can occur only in certain situations relative to other mouse events:
Each mouse-up event must be preceded by a mouse-down event.
Mouse-dragged events occur only between a mouse-down event and a mouse-up event.
Mouse-moved events do not occur between a mouse-down and a mouse-up event.
Mouse-down events are sent when a user presses the mouse button while the cursor is over a view object. If the window containing the view is not the key window, the window becomes the key window and discards the mouse-down event. However, a view can circumvent this default behavior by overriding the acceptsFirstMouse: method of NSView to return YES.
Views automatically receive mouse-clicked and mouse-dragged events, but because mouse-moved events occur so often and can bog down the event queue, a view object must explicitly request its window to watch for them using the NSWindow method setAcceptsMouseMovedEvents:. Tracking rectangles, described in “Other Event Dispatching,” are a less expensive way of following the mouse’s location.
In its implementation of an NSResponder mouse-event method, a subclass of NSView can interpret a mouse event as a cue to perform a certain action, such as sending a target-action message, selecting a graphic element, redrawing itself at a different location, and so on. Each event method includes as its sole parameter an NSEvent object from which the view can obtain information about the event. For example, the view can use the locationInWindow to locate the mouse cursor’s hot spot in the coordinate system of the receiver’s window. To convert it to the view’s coordinate system, use convertPoint:fromView: with a nil view argument. From here, you can use mouse:inRect: to determine whether the click occurred in an interesting area.
Tablet events take a path to delivery to a view that is similar to that for mouse events. The NSWindow object representing the window in which the table event occurred forwards the event to the view under the cursor. However, there are two kinds of tablet events, proximity events and pointer events. The former are generally native tablet events (of type NSTabletProximity) generated when the stylus moves into and out of proximity to the tablet. Tablet pointer events occur between proximity-entering and proximity-leaving tablet events and indicate such things as stylus direction, pressure, and button click. Pointer events are generally subtypes of mouse events. Refer to “Handling Tablet Events” for more information.
For the paths taken by mouse-tracking and cursor-update events, see “Other Event Dispatching.”
Processing keyboard input is by far the most complex part of event dispatch. The Application Kit goes to great lengths to ease this process for you, and in fact handling the key events that get to your custom objects is fairly straightforward. However, a lot happens to those events on their way from the hardware to the responder chain. Of particular interest are the types of key events that arrive in a Cocoa application as NSEvent objects and the order and way these types of events are handled.
A Cocoa application evaluates each key event to determine what kind of key event it is and then handles in an appropriate way. The path a key event can take before it is handled can be quite long. Figure 1-5 shows these potential paths.
The following list describes in detail the possible paths for key events, in the order in which an application evaluates each key event;
Key equivalents. A key equivalent is a key or key combination (usually a key modified by the Command key) that is bound typically to some menu item or control object in the application. Pressing the key combination simulates the action of clicking the control or choosing the menu item.
The application object handles key equivalents by going down the view hierarchy in the key window, sending each object a performKeyEquivalent: message until an object returns YES. If the message isn’t handled by an object in the view hierarchy, NSApp then sends it to the menus in the menu bar. Some Cocoa classes, such as NSButton, NSMenu, NSMatrix, and NSSavePanel provide default implementations.
For more information, see “Handling Key Equivalents.”
Keyboard interface control. A keyboard interface control event manipulates the input focus among objects in a user interface. In the key window, NSWindow interprets certain keys as commands to move control to a different interface object, to simulate a mouse click on it, and so on. For example, pressing the Tab key moves input focus to the next object; Shift-Tab reverses the direction; pressing the space bar simulates a click on a button. The order of interface objects controlled through this mechanism is specified by a key view loop. You can set up the key view loop in Interface Builder and you can manipulate the key view loop programmatically through the setNextKeyView: and nextKeyView methods of NSView.
For more information, see “Keyboard Interface Control.”
Keyboard action. Unlike the action messages that controls send to their targets (see “Action Messages”), keyboard actions are commands (represented by methods defined by the NSResponder class) that are per-view functional interpretations of physical keystrokes (as identified by the constant returned by the characters method of NSEvent). In other words, keyboard actions are bound to physical keys through the key bindings mechanism described in “Text System Defaults and Key Bindings.” For example, pageDown:, moveToBeginningOfLine:, and capitalizeWord: are methods invoked by keyboard actions when the bound key is pressed. Such actions are sent to the first responder, and the methods handling these actions can be implemented in that view or in a superview further up the responder chain.
See “Overriding the keyDown: Method” for more information about the handling of keyboard actions.
Character (or characters) for insertion as text.
If the application object processes a key event and it turns out not to be a key equivalent or a key interface control event, it then sends it to the key window in a sendEvent: message. The window object invokes the keyDown: method in the first responder, from whence the key event travels up the responder chain until it is handled. At this point, the key event can be either one or more Unicode character to be inserted into a view’s displayed text , a key or key combination to be specially interpreted, or a keyboard-action event.
See “Handling Key Events” for more information about how key events are dispatched and handled.
An NSWindow object monitors tracking-rectangle events and dispatches these events directly to the owning object in mouseEntered: and mouseExited: messages. The owner is specified in the second parameter of the NSTrackingArea method initWithRect:options:owner:userInfo: and the NSView methodaddTrackingRect:owner:userData:assumeInside:. “Mouse-Tracking and Cursor-Update Events” describes how to set up tracking rectangles and handle the related events.
Periodic events (type NSPeriodic) are generated by the application at a specified frequency and placed in the event queue. However, unlike most other types of events, periodic events aren’t dispatched using the sendEvent: mechanism of NSApplication and NSWindow. Instead the object registering for the periodic events typically retrieves them in a modal event loop using the nextEventMatchingMask:untilDate:inMode:dequeue: method. See “Other Types of Events” for more information about periodic events.
Last updated: 2007-03-16