diff --git a/AppKit/CPColorPanel.j b/AppKit/CPColorPanel.j index 36df15bba..06c404595 100644 --- a/AppKit/CPColorPanel.j +++ b/AppKit/CPColorPanel.j @@ -148,10 +148,13 @@ var SharedColorPanel = nil, */ - (void)setColor:(CPColor)aColor { + if ([_color isEqual:aColor]) + return; + _color = aColor; [_previewView setBackgroundColor:_color]; - // Push color via Responder Chain + // Push color via Responder Chain (targets First Responder, i.e., the active CPColorWell) [CPApp sendAction:@selector(changeColor:) to:nil from:self]; if (_target && _action) @@ -163,7 +166,7 @@ var SharedColorPanel = nil, if (_activePicker) [_activePicker setColor:_color]; - + if (_opacitySlider) [_opacitySlider setFloatValue:[_color alphaComponent]]; } @@ -432,7 +435,7 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie"; [self setBackgroundColor:[CPColor grayColor]]; - [self registerForDraggedTypes:[CPArray arrayWithObjects:CPColorDragType]]; + [self registerForDraggedTypes:[CPArray arrayWithObjects:@"CPColorDragType"]]; var whiteColor = [CPColor whiteColor]; @@ -555,18 +558,22 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie"; [dragView addSubview:dragFillView]; + var pasteboard = [CPPasteboard pasteboardWithName:CPDragPboard]; + [pasteboard declareTypes:[CPArray arrayWithObject:@"CPColorDragType"] owner:self]; + [pasteboard setData:[CPKeyedArchiver archivedDataWithRootObject:_dragColor] forType:@"CPColorDragType"]; + [self dragView:dragView at:CGPointMake(point.x - bounds.size.width / 2.0, point.y - bounds.size.height / 2.0) offset:CGPointMake(0.0, 0.0) event:anEvent - pasteboard:[CPPasteboard pasteboardWithName:CPDragPboard] + pasteboard:pasteboard source:self slideBack:YES]; } - (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType { - if (aType == CPColorDragType) + if (aType == @"CPColorDragType") [aPasteboard setData:[CPKeyedArchiver archivedDataWithRootObject:_dragColor] forType:aType]; } @@ -576,10 +583,10 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie"; pasteboard = [aSender draggingPasteboard], swatch = nil; - if (![pasteboard availableTypeFromArray:[CPColorDragType]] || location.x > [self bounds].size.width - 1 || location.x < 1) + if (![pasteboard availableTypeFromArray:[@"CPColorDragType"]] || location.x > [self bounds].size.width - 1 || location.x < 1) return NO; - [self setColor:[CPKeyedUnarchiver unarchiveObjectWithData:[pasteboard dataForType:CPColorDragType]] atIndex:FLOOR(location.x / 13)]; + [self setColor:[CPKeyedUnarchiver unarchiveObjectWithData:[pasteboard dataForType:@"CPColorDragType"]] atIndex:FLOOR(location.x / 13)]; } @end @@ -594,7 +601,7 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie"; { self = [super initWithFrame:aFrame]; - [self registerForDraggedTypes:[CPArray arrayWithObjects:CPColorDragType]]; + [self registerForDraggedTypes:[CPArray arrayWithObjects:@"CPColorDragType"]]; return self; } @@ -613,10 +620,10 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie"; { var pasteboard = [aSender draggingPasteboard]; - if (![pasteboard availableTypeFromArray:[CPColorDragType]]) + if (![pasteboard availableTypeFromArray:[@"CPColorDragType"]]) return NO; - var color = [CPKeyedUnarchiver unarchiveObjectWithData:[pasteboard dataForType:CPColorDragType]]; + var color = [CPKeyedUnarchiver unarchiveObjectWithData:[pasteboard dataForType:@"CPColorDragType"]]; [_colorPanel setColor:color updatePicker:YES]; } @@ -639,18 +646,22 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie"; [dragView addSubview:dragFillView]; + var pasteboard = [CPPasteboard pasteboardWithName:CPDragPboard]; + [pasteboard declareTypes:[CPArray arrayWithObject:@"CPColorDragType"] owner:self]; + [pasteboard setData:[CPKeyedArchiver archivedDataWithRootObject:[self backgroundColor]] forType:@"CPColorDragType"]; + [self dragView:dragView at:CGPointMake(point.x - bounds.size.width / 2.0, point.y - bounds.size.height / 2.0) offset:CGPointMake(0.0, 0.0) event:anEvent - pasteboard:[CPPasteboard pasteboardWithName:CPDragPboard] + pasteboard:pasteboard source:self slideBack:YES]; } - (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType { - if (aType == CPColorDragType) + if (aType == @"CPColorDragType") [aPasteboard setData:[CPKeyedArchiver archivedDataWithRootObject:[self backgroundColor]] forType:aType]; } diff --git a/AppKit/CPColorWell.j b/AppKit/CPColorWell.j index 7c2ece7d7..73ed88b8a 100644 --- a/AppKit/CPColorWell.j +++ b/AppKit/CPColorWell.j @@ -26,6 +26,8 @@ @import "CPColor.j" @import "CPColorPanel.j" +var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiveNotification"; + /*! @ingroup appkit @class CPColorWell @@ -38,6 +40,7 @@ { BOOL _bordered; CPColor _color; + BOOL _isChangingColorFromPanel; // Guard flag to prevent recursion } + (Class)_binderClassForBinding:(CPString)aBinding @@ -72,6 +75,31 @@ [theBinding reverseSetValueFor:@"color"]; } +- (BOOL)isFirstResponder +{ + return [[self window] firstResponder] === self; +} + +- (BOOL)acceptsFirstResponder +{ + return YES; +} + +- (void)activate:(BOOL)shouldBeExclusive +{ + [[self window] makeFirstResponder:self]; + [[CPColorPanel sharedColorPanel] orderFront:self]; +} + +/*! + Deactivates the color well. +*/ +- (void)deactivate +{ + if ([self isFirstResponder]) + [[self window] makeFirstResponder:nil]; +} + - (id)initWithFrame:(CGRect)aFrame { self = [super initWithFrame:aFrame]; @@ -81,8 +109,8 @@ _color = [CPColor whiteColor]; [self setBordered:YES]; - // Register for Drag & Drop - [self registerForDraggedTypes:[CPArray arrayWithObject:CPColorDragType]]; + // Register using string literal to avoid dependency issues + [self registerForDraggedTypes:[CPArray arrayWithObject:@"CPColorDragType"]]; } return self; @@ -126,15 +154,16 @@ */ - (void)setColor:(CPColor)aColor { - if (_color == aColor) + if ([_color isEqual:aColor]) return; _color = aColor; [self setNeedsLayout]; - // If we are active, update the panel to match us - if ([self isFirstResponder]) + // Only push back to the panel if we initiated the change (not if the panel pushed it to us) + // AND if we are the current focus. + if (!_isChangingColorFromPanel && [self isFirstResponder]) [[CPColorPanel sharedColorPanel] setColor:_color]; } @@ -148,13 +177,17 @@ } /*! - Standard action method sent by CPColorPanel up the responder chain. + Standard action method sent by CPColorPanel via the Responder Chain. */ - (void)changeColor:(id)aSender { if ([aSender isKindOfClass:[CPColorPanel class]]) { + _isChangingColorFromPanel = YES; [self setColor:[aSender color]]; + _isChangingColorFromPanel = NO; + + // Forward the action to our target (e.g. controller) [self sendAction:[self action] to:[self target]]; } } @@ -167,43 +200,19 @@ If exclusive is \c YES, deactivates any other CPColorWells. \c NO, keeps them active. @param shouldBeExclusive whether other color wells should be deactivated. */ -- (void)activate:(BOOL)shouldBeExclusive -{ - [[self window] makeFirstResponder:self]; - [[CPColorPanel sharedColorPanel] orderFront:self]; -} - -/*! - Deactivates the color well. -*/ -- (void)deactivate -{ - if ([self isFirstResponder]) - [[self window] makeFirstResponder:nil]; -} - -/*! - Returns \c YES if the color well is active (is First Responder). -*/ -- (BOOL)isActive -{ - return [self isFirstResponder]; -} - -- (BOOL)acceptsFirstResponder -{ - return [self isEnabled]; -} - - (BOOL)becomeFirstResponder { [self setThemeState:CPThemeStateFirstResponder]; var panel = [CPColorPanel sharedColorPanel]; - [panel setColor:_color]; - // Ensure panel knows who to talk to if it's already open - [panel setTarget:nil]; + // Sync panel to our current color + [panel setColor:_color]; + + // EXPLICITLY set ourselves as the target. + // This ensures that when the user clicks the panel (making Panel key), + // the panel still knows to send messages back to us. + [panel setTarget:self]; [panel setAction:@selector(changeColor:)]; [[CPNotificationCenter defaultCenter] postNotificationName:_CPColorWellDidBecomeExclusiveNotification object:self]; @@ -214,6 +223,13 @@ - (BOOL)resignFirstResponder { [self unsetThemeState:CPThemeStateFirstResponder]; + + var panel = [CPColorPanel sharedColorPanel]; + + // Clean up if we were the target + if ([panel target] == self) + [panel setTarget:nil]; + return YES; } @@ -224,7 +240,7 @@ { if (![self isEnabled]) return; - + [self activate:YES]; } @@ -235,7 +251,7 @@ { var pasteboard = [sender draggingPasteboard]; - if ([[pasteboard types] containsObject:CPColorDragType]) + if ([[pasteboard types] containsObject:@"CPColorDragType"]) { [self setThemeState:CPThemeStateHighlighted]; return CPDragOperationCopy; @@ -253,17 +269,20 @@ { var pasteboard = [sender draggingPasteboard]; - if ([[pasteboard types] containsObject:CPColorDragType]) + if ([[pasteboard types] containsObject:@"CPColorDragType"]) { - var data = [pasteboard dataForType:CPColorDragType], + var data = [pasteboard dataForType:@"CPColorDragType"], newColor = [CPKeyedUnarchiver unarchiveObjectWithData:data]; - if (newColor) + if (newColor && [newColor isKindOfClass:[CPColor class]]) { [self setColor:newColor]; [self sendAction:[self action] to:[self target]]; - [self activate:YES]; // Become active after drop + + // Activate nicely after drop + [self activate:YES]; [self unsetThemeState:CPThemeStateHighlighted]; + return YES; } } @@ -386,7 +405,7 @@ var CPColorWellColorKey = "CPColorWellColorKey", { _color = [aCoder decodeObjectForKey:CPColorWellColorKey]; [self setBordered:[aCoder decodeBoolForKey:CPColorWellBorderedKey]]; - [self registerForDraggedTypes:[CPArray arrayWithObject:CPColorDragType]]; + [self registerForDraggedTypes:[CPArray arrayWithObject:@"CPColorDragType"]]; } return self;