Detecting Mouse Down NSEvent in NSView Subviews

Posted in |

I want to detect mouse down events in the subviews of an NSView so that I can highlight the NSView whenever any of its subviews are clicked.

The first thing we need is to build a list of the window subviews that want to be informed whenever one their subviews is clicked. We we are going to need an NSWindow subclass, say MGSMyWindow.


@interface MGSMyWindow : NSWindow {
	NSHashTable *_clickViews;
}

- (void)addClickView:(NSView *)aView;
@end

The use of NSHashTable indicates that garbage collection is required.

We initialise our window like so:



/*
 
 NSWindow designated initialiser
 
 */
- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation
{
	if ((self = [super initWithContentRect:contentRect styleMask:windowStyle backing:bufferingType defer:deferCreation])) {
		_clickViews = [NSHashTable hashTableWithWeakObjects];
	}
	
	return self;
}

If a subview, X say, of the content view of our MGSMyWindow instance wants to be informed whenever one of its subviews is clicked then X must add itself to our windows list of click views. So MGSMyWindow defines:


/*
 
 add a click view
 
 click view must be a sub view of the NSWindow contentView
 
 */
- (void)addClickView:(NSView *)aView
{
	if ([aView isDescendantOf:[self contentView]] && [aView respondsToSelector:@selector(subviewClicked:)]) {
		
		// _clickViews will maintain a weak ref to aView so we don't need
		// to remove it
		[_clickViews addObject:aView];
	}
}

Now our window knows which subviews are interested parties. All that remains is to notify them when a subview is clicked. By overriding NSWindow -sendEvent: we can examine the window event stream and look for mouse down events in subviews of our _clickViews.


/*
 
 send event
 
 This action method dispatches mouse and keyboard events sent to the window by the NSApplication object.
 
 */
- (void)sendEvent:(NSEvent *)event
{
	
	// look for mouse down
	if ([event type] == NSLeftMouseDown) {
		
		// look for deepest subview
		NSView *deepView = [[self contentView] hitTest:[event locationInWindow]];
		if (deepView) {
			for (NSView *aClickView in [_clickViews allObjects]) {
				if ([deepView isDescendantOf:aClickView]) {
					[(id)aClickView subviewClicked:deepView];
					break;
				}
			}
		}			
	}
	
	[super sendEvent:event];

}

This approach could be easily generalised to provide notification for any or a range of NSEvents. Rather than sending a specialised -subviewClicked:(NSView *)view a generic -subviewEvent:(NSEvent *)event could be sent, the receiver being left to operate upon the event type.

Submitted by Jonathan Mitchell on Sat, 01/02/2010 - 23:18

Post new comment

The content of this field is kept private and will not be shown publicly.
CAPTCHA
Let us know you are human.
13 + 6 =
Solve this simple math problem and enter the result. E.g. for 1+3, enter 4.