Monday, October 19, 2009

Custom color picker (2)




This is the second in a two-part series about a custom color picker. The first post is here.

The bindings had me stumped for a while. I found that for a custom view with its own class, InterfaceBuilder will not let me bind to a variable in that class. The screenshot shows that the view is not one of the allowed options:



So, I put the variables into the AppDelegate. We set the default values in applicationDidFinishLaunching. The tricky part was to then get the values into the view class in time for the first drawing of the window. I send a message from the AppDelegate to the view instructing it to load, in the same function that sets the defaults.

Also, I didn't like the way the boxes were getting stroked, so I did it myself. I'm not sure if it's really necessary since I've also spread the boxes out a bit now. Anyway, it all looks very stylish, particularly the action of the stepper.


#import "MyColorView.h"

@implementation MyColorView
@synthesize mA;
@synthesize numColors;
@synthesize maxColors;

- (id)initWithFrame:(NSRect)frameRect {
self = [super initWithFrame:frameRect];
if (nil == self) { return nil; }
NSLog(@"MyView initWithFrame %@", self);
return self;
}

- (void)setUpArray {
NSLog(@"MyView setUpArray %@", self);
CL = [NSColorList
colorListNamed:@"Crayons"];
[CL retain];
[self setMA:[NSMutableArray arrayWithCapacity:10]];

// hard-coded 30 x 30 squares, 10 of them
// view is actually 36 px high
f = 30;
maxN =( int)[ self bounds].size.width / f;
[self setMaxColors:[NSNumber numberWithInt:maxN]];
[self setNumColors:[NSNumber numberWithInt:4]];

NSArray *a = [CL allKeys];
for (i = 0; i < [[AD maxColors] intValue]; i++) {
R = NSMakeRect((f+5)*i + 3,3,f,f);
path = [NSBezierPath bezierPathWithRect:R];
[path setLineWidth:3];
mD = [NSMutableDictionary dictionaryWithCapacity:3];
[mD setObject:path forKey:@"path"];
c = [CL colorWithKey:[a objectAtIndex:i]];
[mD setObject:c forKey:@"color"];
[mA addObject:mD];
}

//NSLog(@"MyView array = %@", [self mA]);
selectedRect = -1;
[self setNeedsDisplay:YES];
}

- (void)drawRect:(NSRect)rect{
NSLog(@"MyView drawRect for %@ colors", [AD numColors]);
[[NSColor whiteColor] set];
//NSRectFill([self bounds]);
c = [CL colorWithKey:@"Lemon"];

for (i = 0; i < [[AD numColors] intValue]; i++) {
mD = [mA objectAtIndex:i];
id obj = [mD objectForKey:@"path"];
if (i == selectedRect) {
[self drawRedLines:obj];
[[NSColor whiteColor] set];
}
else {
[[mD objectForKey:@"color" ]set];
}
[obj fill];
}
}

- (void)drawRedLines:(id)obj {
p1 = [obj bounds].origin;
[[NSColor redColor] set];
NSRectFill(
NSMakeRect(p1.x,0,30,3));
NSRectFill(
NSMakeRect(p1.x,33,30,3));
NSRectFill(
NSMakeRect(p1.x-3,0,3,36));
NSRectFill(
NSMakeRect(p1.x+30,0,3,36));
}

- (BOOL)acceptsFirstResponder {
return YES;
}

- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent {
return YES;
}

- (IBAction)mouseDown:(NSEvent *)e {
NSLog(@"mouseDown");
p = [self convertPoint:[e locationInWindow]
fromView:nil];
for (i = 0; i < [[AD numColors] intValue]; i++ ) {
id obj = [[self mA] objectAtIndex:i];
path = [obj objectForKey:@"path"];
if (NSPointInRect(p, [path bounds])) {
if (selectedRect == i) {
selectedRect = -1;
}
else {
selectedRect = i;
[[NSColorPanel sharedColorPanel]
makeKeyAndOrderFront:self];
}

NSLog(@"selectedRect %i", selectedRect);
break;
}
}
[self setNeedsDisplay:YES];
}

- (void)mouseDownOutsideView {
NSLog(@"mouseDownOutsideView");
selectedRect = -1;
[[NSColorPanel sharedColorPanel] orderOut:(id)self];
[self setNeedsDisplay:YES];
}

- (IBAction)changeColor:(id)sender {
NSColor *colorChoice = [sender color];
[[NSColorPanel sharedColorPanel] orderOut:(id)self];

NSLog(@"changeColor %@", colorChoice);
if (selectedRect == -1) { return; }

[[[self mA] objectAtIndex:selectedRect]
setObject:colorChoice
forKey:@"color"];
selectedRect = -1;

[self setNeedsDisplay:YES];
}

- (IBAction)stepperChanged:(id)sender {
NSLog(@"stepperChanged %@ %@",
[AD numColors], [AD maxColors]);
[self setNeedsDisplay:YES];
}

@end