MenuItemView/TrackView.m
/* |
Copyright (C) 2017 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
Abstract: |
The NSView that handles the label color tracking. |
*/ |
#import "TrackView.h" |
@interface TrackView () |
@property (strong) NSMutableArray *viewTrackingAreas; |
@property NSInteger whichTrackedID; // indicates the currently tracked label |
@property BOOL trackEntered; // indicates we are currently inside a label tracking area |
@end |
#pragma mark - |
@implementation TrackView |
// key for dictionary in NSTrackingAreas's userInfo |
NSString *kTrackerKey = @"whichTracker"; |
// key values for dictionary in NSTrackingAreas's userInfo, |
// which tracking area is being tracked |
typedef NS_ENUM(NSInteger, trackingAreaIDs) { |
kTrackingArea1 = 1, |
kTrackingArea2, |
kTrackingArea3, |
kTrackingArea4, |
kTrackingArea5, |
kTrackingArea6, |
kTrackingArea7, |
kTrackingArea8 |
}; |
// ------------------------------------------------------------------------------- |
// awakeFromNib: |
// |
// Setup the tracking areas for each colored dot. Each colored dot is a separate |
// NSView for easy management/tracking purposes. |
// ------------------------------------------------------------------------------- |
- (void)awakeFromNib |
{ |
[super awakeFromNib]; |
[self setupTrackingAreas]; |
} |
// ------------------------------------------------------------------------------- |
// setupTrackingAreas: |
// ------------------------------------------------------------------------------- |
- (void)setupTrackingAreas |
{ |
if (self.viewTrackingAreas == nil) |
{ |
// load all the suviews and add tracking areas to them |
NSArray *viewsToFrame = self.subviews; |
_viewTrackingAreas = [NSMutableArray array]; // keep all tracking areas in an array |
// determine the tracking options |
NSTrackingAreaOptions trackingOptions = NSTrackingEnabledDuringMouseDrag | |
NSTrackingMouseEnteredAndExited | |
NSTrackingActiveInActiveApp | |
NSTrackingActiveAlways; |
for (NSUInteger index = 1; index < self.subviews.count; index++) |
{ |
// make tracking data (to be stored in NSTrackingArea's userInfo) |
// so we can later determine which tracking area is focused on |
// |
NSDictionary *trackerData = @{kTrackerKey: @(index)}; |
NSTrackingArea *trackingArea = [[NSTrackingArea alloc] |
initWithRect:[viewsToFrame[index-1] frame] |
// note: since we are working with this view's coordinate system, |
// we need to use the 'frame' for each subview instead of its bounds |
options:trackingOptions |
owner:self |
userInfo:trackerData]; |
[self.viewTrackingAreas addObject:trackingArea]; // keep track of this tracking area for later disposal |
[self addTrackingArea:trackingArea]; // add the tracking area to the view/window |
} |
} |
} |
// ------------------------------------------------------------------------------- |
// drawRect:rect |
// |
// Returns the NSColor corresponding to the sub-view index. |
// ------------------------------------------------------------------------------- |
- (NSColor *)colorForViewIndex:(NSInteger)viewIndex |
{ |
NSColor *returnColor = nil; |
switch (viewIndex) |
{ |
case kTrackingArea1: // grey |
returnColor = [NSColor grayColor]; |
break; |
case kTrackingArea2: // purple |
returnColor = [NSColor purpleColor]; |
break; |
case kTrackingArea3: // blue |
returnColor = [NSColor blueColor]; |
break; |
case kTrackingArea4: // green |
returnColor = [NSColor greenColor]; |
break; |
case kTrackingArea5: // yellow |
returnColor = [NSColor yellowColor]; |
break; |
case kTrackingArea6: // orange |
returnColor = [NSColor orangeColor]; |
break; |
case kTrackingArea7: // red |
returnColor = [NSColor redColor]; |
break; |
case kTrackingArea8: // none (black) |
returnColor = [NSColor blackColor]; |
break; |
} |
return returnColor; |
} |
// ------------------------------------------------------------------------------- |
// drawRect:rect |
// |
// Examine all the sub-view colored dots and color them with their appropriate colors. |
// ------------------------------------------------------------------------------- |
- (void)drawRect:(NSRect)rect |
{ |
NSArray *viewsToFrame = self.subviews; |
NSInteger viewCount = viewsToFrame.count - 1; |
for (NSInteger index = 1; index <= viewCount; index++) |
{ |
if (self.whichTrackedID == index) |
{ |
// obtain the bezier path (a filled circle) to draw in |
NSBezierPath *theFill = [NSBezierPath bezierPathWithOvalInRect:[viewsToFrame[self.whichTrackedID - 1] frame]]; |
NSColor *theColor = [self colorForViewIndex:self.whichTrackedID]; |
if (self.trackEntered) |
{ |
// if we are tracking inside any label, we want to brighten the color to show selection feedback |
// take the current label color and brighten it |
CGFloat hue, saturation, brightness, alpha; |
[[theColor |
colorUsingColorSpaceName:NSDeviceRGBColorSpace] getHue:&hue |
saturation:&saturation brightness:&brightness alpha:&alpha]; |
theColor = [NSColor colorWithDeviceHue:hue saturation:saturation-.60 brightness:brightness + 40.0 alpha:alpha]; |
} |
// finally, render the color change (light or dark) |
[self lockFocus]; |
[theColor set]; |
[theFill fill]; |
[self unlockFocus]; |
} |
else |
{ |
// obtain the bezier path (a filled circle) to draw in |
NSBezierPath *theFill = [NSBezierPath bezierPathWithOvalInRect:[viewsToFrame[index-1] frame]]; |
// fill the area with the appropriate color |
[[self colorForViewIndex:index] set]; |
[theFill fill]; |
} |
} |
} |
// ------------------------------------------------------------------------------- |
// getTrackerIDFromDict:dict |
// |
// Used in obtaining dictionary entry info from the 'userData', used by each |
// mouse event method. It helps determine which tracking area is being tracked. |
// ------------------------------------------------------------------------------- |
- (int)getTrackerIDFromDict:(NSDictionary *)dict |
{ |
id whichTracker = dict[kTrackerKey]; |
return [whichTracker intValue]; |
} |
// ------------------------------------------------------------------------------- |
// mouseEntered:event |
// |
// Because we installed NSTrackingArea to our NSImageView, this method will be called. |
// ------------------------------------------------------------------------------- |
- (void)mouseEntered:(NSEvent *)event |
{ |
// which tracking area is being tracked? |
_whichTrackedID = [self getTrackerIDFromDict:event.userData]; |
_trackEntered = YES; |
[self setNeedsDisplay:YES]; // force update the currently tracked label back to its original color |
} |
// ------------------------------------------------------------------------------- |
// mouseExited:event |
// |
// Because we installed NSTrackingArea to our NSImageView, this method will be called. |
// ------------------------------------------------------------------------------- |
- (void)mouseExited:(NSEvent *)event |
{ |
// which tracking area is being tracked? |
_whichTrackedID = [self getTrackerIDFromDict:event.userData]; |
_trackEntered = NO; |
[self setNeedsDisplay:YES]; // force update the currently tracked label to a lighter color |
} |
// ------------------------------------------------------------------------------- |
// mouseDown:event |
// ------------------------------------------------------------------------------- |
- (void)mouseUp:(NSEvent*)event |
{ |
NSPoint mousePoint = [self convertPoint:event.locationInWindow fromView:nil]; |
// figure out which label color was clicked on at mouseUp time |
NSArray *labelViews = self.subviews; |
for (NSUInteger index = 1; index < labelViews.count; index++) |
{ |
NSRect labelRect = [labelViews[index-1] frame]; |
if (NSPointInRect(mousePoint, labelRect)) |
{ |
// here you would respond to the particular label selection... |
NSLog(@"label %ld was selected", index); |
} |
} |
// on mouse up, we want to dismiss the menu being tracked |
NSMenu *menu = self.enclosingMenuItem.menu; |
[menu cancelTracking]; |
// we are no longer tracking, reset the tracked label color (if any) |
_whichTrackedID = NSNotFound; |
[self setNeedsDisplay:YES]; |
} |
@end |
Copyright © 2017 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2017-03-09