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.
Post new comment