diff options
Diffstat (limited to 'Source/SPColorSelectorView.m')
-rw-r--r-- | Source/SPColorSelectorView.m | 436 |
1 files changed, 436 insertions, 0 deletions
diff --git a/Source/SPColorSelectorView.m b/Source/SPColorSelectorView.m new file mode 100644 index 00000000..9e80b79c --- /dev/null +++ b/Source/SPColorSelectorView.m @@ -0,0 +1,436 @@ +// +// $Id$ +// +// SPColorSelectorView.m +// sequel-pro +// +// Created by Max Lohrmann on 2013-10-20 +// Copyright (c) 2013 Max Lohrmann. All rights reserved. +// +// Adapted from: +// CCTColorLabelMenuItemView.m +// LabelPickerMenu +// +// Copyright (c) 2010 Dan Messing. All Rights Reserved. +// +// Based on: +// TrackView.m +// MenuItemView example code +// +// Copyright (C) Apple Inc. All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// More info at <http://code.google.com/p/sequel-pro/> + +#import "SPColorSelectorView.h" + +@interface SPColorSelectorView (Private) + +- (void)setupTrackingAreas; + +@end + +@implementation SPColorSelectorView + +@synthesize selectedTag; +@synthesize colorList; + +// key for dictionary in NSTrackingAreas's userInfo +NSString* kTrackerKey = @"whichTracker"; + +// key values for dictionary in NSTrackingAreas's userInfo, +// which tracking area is being tracked +enum trackingAreaIDs +{ + kTrackingAreaNone = -1, + kTrackingArea0, + kTrackingArea1, + kTrackingArea2, + kTrackingArea3, + kTrackingArea4, + kTrackingArea5, + kTrackingArea6 +}; + +// ------------------------------------------------------------------------------- +// initWithFrame: +// +// Setup the tracking areas for each colored dot. +// ------------------------------------------------------------------------------- +- (id)initWithFrame:(NSRect)frameRect +{ + if ( self = [super initWithFrame:frameRect] ) + { + selectedTag = kTrackingAreaNone; //we start out with no selection + observer = nil; + colorList = nil; + [self setupTrackingAreas]; + + //set ourselves as observer of selectedTag (need to mark view dirty) + [self addObserver:self forKeyPath:@"selectedTag" options:0 context:nil]; + } + return self; +} + +- (void)bind:(NSString *)binding toObject:(id)observableObject withKeyPath:(NSString *)keyPath options:(NSDictionary *)options +{ + if([binding isEqualToString:@"selectedTag"]) { + [observableObject addObserver:self forKeyPath:keyPath options:0 context:nil]; + observer = [observableObject retain]; + observerKeyPath = [keyPath copy]; + } + else { + [super bind:binding toObject:observableObject withKeyPath:keyPath options:options]; + } +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + if(object == self) { + [self setNeedsDisplay:YES]; + } + else if(object == observer) { + // You passed the binding identifier as the context when registering + // as an observer--use that to decide what to update... + + id newValue = [observer valueForKeyPath:observerKeyPath]; + + NSNumber *num = (NSNumber *)newValue; + + [self setSelectedTag:[num integerValue]]; + } +} + +// ------------------------------------------------------------------------------- +// dealloc: +// ------------------------------------------------------------------------------- +-(void)dealloc +{ + [trackingAreas release]; + [super dealloc]; +} + + +// ------------------------------------------------------------------------------- +// Returns the rectangle corresponding to the tracking area. +// ------------------------------------------------------------------------------- +- (NSRect)rectForColorViewAtIndex:(NSInteger)index +{ + CGFloat baseY = 2.0; + CGFloat baseX = 2.0; + + CGFloat marginR = 5.0; + + CGFloat width = 16.0; + CGFloat height = 16.0; + + NSRect returnRect = NSZeroRect; + switch (index) + { + case kTrackingAreaNone: + returnRect = NSMakeRect(baseX, baseY, width, height); + break; + + case kTrackingArea0: + returnRect = NSMakeRect(baseX+1*(width+marginR), baseY, width, height); + break; + + case kTrackingArea1: + returnRect = NSMakeRect(baseX+2*(width+marginR), baseY, width, height); + break; + + case kTrackingArea2: + returnRect = NSMakeRect(baseX+3*(width+marginR), baseY, width, height); + break; + + case kTrackingArea3: + returnRect = NSMakeRect(baseX+4*(width+marginR), baseY, width, height); + break; + + case kTrackingArea4: + returnRect = NSMakeRect(baseX+5*(width+marginR), baseY, width, height); + break; + + case kTrackingArea5: + returnRect = NSMakeRect(baseX+6*(width+marginR), baseY, width, height); + break; + + case kTrackingArea6: + returnRect = NSMakeRect(baseX+7*(width+marginR), baseY, width, height); + break; + } + return returnRect; +} + +// ------------------------------------------------------------------------------- +// Returns the color gradient corresponding to the tag. These colours were +// chosen to appear similar to those in Aperture 3. +// ------------------------------------------------------------------------------- + +- (NSGradient *)gradientForTag:(NSInteger)colorTag +{ + NSGradient *gradient = nil; + + //find base color item + NSColor *baseColor = (NSColor *)[colorList objectAtIndex:colorTag]; + if(!baseColor) + return nil; + + //create hightlight and shadow variants of color + NSColor *shadowColor = [baseColor shadowWithLevel:0.22]; + NSColor *highlightColor = [baseColor highlightWithLevel:0.22]; + + //build gradient + gradient = [[NSGradient alloc] initWithColorsAndLocations: + highlightColor, 0.0, + baseColor, 0.5, + shadowColor, 1.0, nil]; + + return [gradient autorelease]; +} + +// ------------------------------------------------------------------------------- +// setupTrackingAreas: +// ------------------------------------------------------------------------------- +- (void)setupTrackingAreas +{ + if (trackingAreas == nil) + { + trackingAreas = [NSMutableArray array]; // keep all tracking areas in an array + + // determine the tracking options + NSTrackingAreaOptions trackingOptions = NSTrackingEnabledDuringMouseDrag | + NSTrackingMouseEnteredAndExited | + NSTrackingActiveInActiveApp | + NSTrackingActiveAlways; + + NSInteger index; + for (index = kTrackingAreaNone; index <= kTrackingArea6; index++) + { + // make tracking data (to be stored in NSTrackingArea's userInfo) so we can later determine which tracking area is focused on + // + NSDictionary* trackerData = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInteger:index], kTrackerKey, nil]; + NSTrackingArea* trackingArea = [[NSTrackingArea alloc] initWithRect:[self rectForColorViewAtIndex:index] + options:trackingOptions + owner:self + userInfo:trackerData]; + + [trackingAreas addObject:trackingArea]; // keep track of this tracking area for later disposal + [self addTrackingArea: trackingArea]; // add the tracking area to the view/window + } + } +} + +#pragma mark - +#pragma mark Custom Drawing + +// ------------------------------------------------------------------------------- +// drawRect:rect +// +// Examine all the sub-view colored dots and color them with their appropriate colors. +// ------------------------------------------------------------------------------- +-(void)drawRect:(NSRect)rect +{ + // see if we should be drawing any of the tags as already selected + NSInteger currentlySelectedTag = [self selectedTag]; + + + NSInteger index; + for (index = kTrackingAreaNone; index <= kTrackingArea6; index++) + { + NSRect colorSquareRect = [self rectForColorViewAtIndex:index]; + + //make sure the color at index is actually defined + if(index >= 0 && [colorList objectAtIndex:index] == nil) + continue; + + //do not draw a selection around the X item + if (index > kTrackingAreaNone && index == currentlySelectedTag) + { + NSBezierPath *highlightPath = [NSBezierPath bezierPathWithOvalInRect:NSInsetRect(colorSquareRect, -1.5, -1.5)]; + [[NSColor colorWithCalibratedRed:0.76 green:0.78 blue:0.82 alpha:1.0] set]; + [highlightPath fill]; + + [[NSColor colorWithCalibratedWhite:0.6 alpha:1.0] set]; + [highlightPath setLineWidth:1.0]; + [highlightPath stroke]; + + } + else if (index == hoverTag && trackEntered) + { + // if we are tracking inside any tag, we want outline the color choice + NSBezierPath *highlightPath = [NSBezierPath bezierPathWithOvalInRect:NSInsetRect(colorSquareRect, -1.5, -1.5)]; + [[NSColor colorWithCalibratedWhite:0.94 alpha:1.0] set]; + [highlightPath fill]; + + [[NSColor colorWithCalibratedWhite:0.6 alpha:1.0] set]; + [highlightPath setLineWidth:1.0]; + [highlightPath stroke]; + } + + if(index == kTrackingAreaNone) { + + + [[NSColor disabledControlTextColor] set]; + //draw an X + NSBezierPath *left = [NSBezierPath bezierPath]; + [left setLineWidth:3.0]; + [left setLineCapStyle:NSButtLineCapStyle]; + [left moveToPoint:NSMakePoint(colorSquareRect.origin.x+4.0, colorSquareRect.origin.y+4.0)]; + [left lineToPoint:NSMakePoint(colorSquareRect.origin.x+12.0, colorSquareRect.origin.y+12.0)]; + [left moveToPoint:NSMakePoint(colorSquareRect.origin.x+12.0, colorSquareRect.origin.y+4.0)]; + [left lineToPoint:NSMakePoint(colorSquareRect.origin.x+4.0, colorSquareRect.origin.y+12.0)]; + [left stroke]; + + } + else { + // draw the gradient dot + NSGradient *gradient = [self gradientForTag:index]; + NSRect dotRect = NSInsetRect(colorSquareRect, 2.0, 2.0); + NSBezierPath *circlePath = [NSBezierPath bezierPathWithOvalInRect:dotRect]; + [gradient drawInBezierPath:circlePath angle:-90.0]; + + + // draw a highlight + + // top edge outline + gradient = [[NSGradient alloc] initWithColorsAndLocations: + [NSColor colorWithCalibratedWhite:1.0 alpha:0.18], 0.0, + [NSColor colorWithCalibratedWhite:1.0 alpha:0.0], 0.6, nil]; + circlePath = [NSBezierPath bezierPathWithOvalInRect:NSInsetRect(dotRect, 1.0, 1.0)]; + [circlePath appendBezierPath:[NSBezierPath bezierPathWithOvalInRect:NSMakeRect(dotRect.origin.x+1.0, dotRect.origin.y-2.0, dotRect.size.width-2.0, dotRect.size.height)]]; + [circlePath setWindingRule:NSEvenOddWindingRule]; + [gradient drawInBezierPath:circlePath angle:-90.0]; + [gradient release]; + + // top center gloss + gradient = [[NSGradient alloc] initWithStartingColor:[NSColor colorWithCalibratedWhite:1.0 alpha:0.18] + endingColor:[NSColor colorWithCalibratedWhite:1.0 alpha:0.0]]; + [gradient drawFromCenter:NSMakePoint(NSMidX(dotRect), NSMaxY(dotRect) - 2.0) + radius:0.0 + toCenter:NSMakePoint(NSMidX(dotRect), NSMaxY(dotRect) - 2.0) + radius:4.0 + options:0]; + [gradient release]; + + // draw a dark outline + circlePath = [NSBezierPath bezierPathWithOvalInRect:dotRect]; + gradient = [[NSGradient alloc] initWithStartingColor:[NSColor colorWithCalibratedWhite:0.0 alpha:0.12] + endingColor:[NSColor colorWithCalibratedWhite:0.0 alpha:0.46]]; + [circlePath appendBezierPath:[NSBezierPath bezierPathWithOvalInRect:NSInsetRect(dotRect, 1.0, 1.0)]]; + [circlePath setWindingRule:NSEvenOddWindingRule]; + [gradient drawInBezierPath:circlePath angle:-90.0]; + [gradient release]; + } + + + } + +} + + +#pragma mark - +#pragma mark Mouse Handling + +// ------------------------------------------------------------------------------- +// 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 objectForKey: 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? + hoverTag = [self getTrackerIDFromDict:[event userData]]; + trackEntered = YES; + + [self setNeedsDisplay:YES]; // force update the currently tracked tag 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? + hoverTag = NSNotFound; + trackEntered = NO; + + [self setNeedsDisplay:YES]; // force update the currently tracked tag to a lighter color +} + +- (void)rightMouseUp:(NSEvent *)theEvent +{ + [self mouseUp:theEvent]; +} + +// ------------------------------------------------------------------------------- +// mouseDown:event +// ------------------------------------------------------------------------------- +- (void)mouseUp:(NSEvent*)event +{ + NSPoint mousePoint = [self convertPoint:[[self window] mouseLocationOutsideOfEventStream] fromView:nil]; + + // figure out which tag color was clicked on at mouseUp time + NSInteger index; + for (index = kTrackingAreaNone; index <= kTrackingArea6; index++) + { + NSRect tagRect = [self rectForColorViewAtIndex:index]; + if (NSPointInRect(mousePoint, tagRect)) + { + if(index == selectedTag) //ignore non-changes + return; + + [self setSelectedTag:index]; + + if(observer != nil) { + [observer setValue:[NSNumber numberWithInteger:index] forKeyPath:observerKeyPath]; + } + + if(delegate != nil && [delegate respondsToSelector:@selector(colorSelectorDidChange:)]) { + [delegate colorSelectorDidChange:self]; + } + + [self setNeedsDisplay:YES]; + return; + } + } +} + + + + +@end |