![]() |
API 0.9.5
|
00001 /* 00002 * CPColorPanel.j 00003 * AppKit 00004 * 00005 * Created by Ross Boucher. 00006 * Copyright 2008, 280 North, Inc. 00007 * 00008 * This library is free software; you can redistribute it and/or 00009 * modify it under the terms of the GNU Lesser General Public 00010 * License as published by the Free Software Foundation; either 00011 * version 2.1 of the License, or (at your option) any later version. 00012 * 00013 * This library is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 * Lesser General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU Lesser General Public 00019 * License along with this library; if not, write to the Free Software 00020 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00021 */ 00022 00023 00024 00025 CPColorPanelColorDidChangeNotification = @"CPColorPanelColorDidChangeNotification"; 00026 00027 var PREVIEW_HEIGHT = 20.0, 00028 TOOLBAR_HEIGHT = 32.0, 00029 SWATCH_HEIGHT = 14.0, 00030 ICON_WIDTH = 32.0, 00031 ICON_PADDING = 12.0; 00032 00033 var SharedColorPanel = nil, 00034 ColorPickerClasses = []; 00035 00036 /* 00037 A color wheel 00038 @global 00039 @group CPColorPanelMode 00040 */ 00041 CPWheelColorPickerMode = 1; 00042 /* 00043 Slider based picker 00044 @global 00045 @group CPColorPanelMode 00046 */ 00047 CPSliderColorPickerMode = 2; 00048 00049 CPColorPickerViewWidth = 265; 00050 CPColorPickerViewHeight = 370; 00051 00060 @implementation CPColorPanel : CPPanel 00061 { 00062 _CPColorPanelToolbar _toolbar; 00063 _CPColorPanelSwatches _swatchView; 00064 _CPColorPanelPreview _previewView; 00065 00066 CPSlider _opacitySlider; 00067 00068 CPArray _colorPickers; 00069 CPView _currentView; 00070 id _activePicker; 00071 00072 CPColor _color; 00073 00074 id _target; 00075 SEL _action; 00076 00077 int _mode; 00078 } 00079 00084 + (void)provideColorPickerClass:(Class)aColorPickerSubclass 00085 { 00086 ColorPickerClasses.push(aColorPickerSubclass); 00087 } 00088 00092 + (CPColorPanel)sharedColorPanel 00093 { 00094 if (!SharedColorPanel) 00095 SharedColorPanel = [[CPColorPanel alloc] init]; 00096 00097 return SharedColorPanel; 00098 } 00099 00104 + (void)setPickerMode:(CPColorPanelMode)mode 00105 { 00106 var panel = [CPColorPanel sharedColorPanel]; 00107 [panel setMode:mode]; 00108 } 00109 00110 /* 00111 To obtain the color panel, use \c +sharedColorPanel. 00112 @ignore 00113 */ 00114 - (id)init 00115 { 00116 self = [super initWithContentRect:CGRectMake(500.0, 50.0, 219.0, 370.0) 00117 styleMask:(CPTitledWindowMask | CPClosableWindowMask | CPResizableWindowMask)]; 00118 00119 if (self) 00120 { 00121 [[self contentView] setBackgroundColor:[CPColor colorWithWhite:0.95 alpha:1.0]]; 00122 00123 [self setTitle:@"Color Panel"]; 00124 [self setLevel:CPFloatingWindowLevel]; 00125 00126 [self setFloatingPanel:YES]; 00127 [self setBecomesKeyOnlyIfNeeded:YES]; 00128 00129 [self setMinSize:CGSizeMake(219.0, 363.0)]; 00130 [self setMaxSize:CGSizeMake(323.0, 537.0)]; 00131 } 00132 00133 return self; 00134 } 00135 00139 - (void)setColor:(CPColor)aColor 00140 { 00141 _color = aColor; 00142 [_previewView setBackgroundColor: _color]; 00143 00144 [CPApp sendAction:@selector(changeColor:) to:nil from:self]; 00145 00146 if (_target && _action) 00147 [CPApp sendAction:_action to:_target from:self]; 00148 00149 [[CPNotificationCenter defaultCenter] 00150 postNotificationName:CPColorPanelColorDidChangeNotification 00151 object:self]; 00152 00153 [_activePicker setColor:_color]; 00154 [_opacitySlider setFloatValue:[_color alphaComponent]]; 00155 } 00156 00162 - (void)setColor:(CPColor)aColor updatePicker:(BOOL)bool 00163 { 00164 [self setColor:aColor]; 00165 00166 if (bool) 00167 [_activePicker setColor:_color]; 00168 } 00169 00173 - (CPColor)color 00174 { 00175 return _color; 00176 } 00177 00178 - (float)opacity 00179 { 00180 return [_opacitySlider floatValue]; 00181 } 00182 00187 - (void)setTarget:(id)aTarget 00188 { 00189 _target = aTarget; 00190 } 00191 00196 - (id)target 00197 { 00198 return _target; 00199 } 00200 00206 - (void)setAction:(selector)anAction 00207 { 00208 _action = anAction; 00209 } 00210 00214 - (selector)action 00215 { 00216 return _action; 00217 } 00218 00223 - (void)setMode:(CPColorPanelMode)mode 00224 { 00225 _mode = mode; 00226 } 00227 00228 - (void)_setPicker:(id)sender 00229 { 00230 var picker = _colorPickers[[sender tag]], 00231 view = [picker provideNewView:NO]; 00232 00233 if (!view) 00234 view = [picker provideNewView:YES]; 00235 00236 if (view == _currentView) 00237 return; 00238 00239 if (_currentView) 00240 [view setFrame:[_currentView frame]]; 00241 else 00242 { 00243 var height = (TOOLBAR_HEIGHT + 10 + PREVIEW_HEIGHT + 5 + SWATCH_HEIGHT + 32), 00244 bounds = [[self contentView] bounds]; 00245 00246 [view setFrameSize:CPSizeMake(bounds.size.width - 10, bounds.size.height - height)]; 00247 [view setFrameOrigin:CPPointMake(5, height)]; 00248 } 00249 00250 [_currentView removeFromSuperview]; 00251 [[self contentView] addSubview:view]; 00252 00253 _currentView = view; 00254 _activePicker = picker; 00255 00256 [picker setColor:[self color]]; 00257 } 00258 00262 - (CPColorPanelMode)mode 00263 { 00264 return _mode; 00265 } 00266 00267 - (void)orderFront:(id)aSender 00268 { 00269 [self _loadContentsIfNecessary]; 00270 [super orderFront:aSender]; 00271 } 00272 00273 /* @ignore */ 00274 - (void)_loadContentsIfNecessary 00275 { 00276 if (_toolbar) 00277 return; 00278 00279 if (!_color) 00280 _color = [CPColor whiteColor]; 00281 00282 _colorPickers = []; 00283 00284 var count = [ColorPickerClasses count]; 00285 for (var i = 0; i < count; i++) 00286 { 00287 var currentPickerClass = ColorPickerClasses[i], 00288 currentPicker = [[currentPickerClass alloc] initWithPickerMask:0 colorPanel:self]; 00289 00290 _colorPickers.push(currentPicker); 00291 } 00292 00293 var contentView = [self contentView], 00294 bounds = [contentView bounds]; 00295 00296 _toolbar = [[CPView alloc] initWithFrame:CGRectMake(0, 6, CGRectGetWidth(bounds), TOOLBAR_HEIGHT)]; 00297 [_toolbar setAutoresizingMask: CPViewWidthSizable]; 00298 00299 var totalToolbarWidth = count * ICON_WIDTH + (count - 1) * ICON_PADDING, 00300 leftOffset = (CGRectGetWidth(bounds) - totalToolbarWidth) / 2.0, 00301 buttonForLater = nil; 00302 00303 for (var i = 0; i < count; i++) 00304 { 00305 var image = [_colorPickers[i] provideNewButtonImage], 00306 highlightImage = [_colorPickers[i] provideNewAlternateButtonImage], 00307 button = [[CPButton alloc] initWithFrame:CGRectMake(leftOffset + i * (ICON_WIDTH + ICON_PADDING), 0, ICON_WIDTH, ICON_WIDTH)]; 00308 00309 [button setTag:i]; 00310 [button setTarget:self]; 00311 [button setAction:@selector(_setPicker:)]; 00312 [button setBordered:NO]; 00313 [button setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin]; 00314 00315 [button setImage:image]; 00316 [button setAlternateImage:highlightImage]; 00317 00318 [_toolbar addSubview:button]; 00319 00320 if (!buttonForLater) 00321 buttonForLater = button; 00322 } 00323 00324 // FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox 00325 var previewBox = [[CPView alloc] initWithFrame:CGRectMake(76, TOOLBAR_HEIGHT + 10, CGRectGetWidth(bounds) - 86, PREVIEW_HEIGHT)]; 00326 00327 _previewView = [[_CPColorPanelPreview alloc] initWithFrame:CGRectInset([previewBox bounds], 2.0, 2.0)]; 00328 00329 [_previewView setColorPanel:self]; 00330 [_previewView setAutoresizingMask:CPViewWidthSizable]; 00331 00332 [previewBox setBackgroundColor:[CPColor colorWithWhite:0.8 alpha:1.0]]; 00333 [previewBox setAutoresizingMask:CPViewWidthSizable]; 00334 00335 [previewBox addSubview:_previewView]; 00336 00337 var _previewLabel = [[CPTextField alloc] initWithFrame: CPRectMake(10, TOOLBAR_HEIGHT + 10, 60, 15)]; 00338 [_previewLabel setStringValue:"Preview:"]; 00339 [_previewLabel setTextColor:[CPColor blackColor]]; 00340 [_previewLabel setAlignment:CPRightTextAlignment]; 00341 00342 // FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox 00343 var swatchBox = [[CPView alloc] initWithFrame:CGRectMake(76, TOOLBAR_HEIGHT + 10 + PREVIEW_HEIGHT + 5, CGRectGetWidth(bounds) - 86, SWATCH_HEIGHT + 2.0)]; 00344 00345 [swatchBox setBackgroundColor:[CPColor colorWithWhite:0.8 alpha:1.0]]; 00346 [swatchBox setAutoresizingMask:CPViewWidthSizable]; 00347 00348 _swatchView = [[_CPColorPanelSwatches alloc] initWithFrame:CGRectInset([swatchBox bounds], 1.0, 1.0)]; 00349 00350 [_swatchView setColorPanel:self]; 00351 [_swatchView setAutoresizingMask:CPViewWidthSizable]; 00352 00353 [swatchBox addSubview:_swatchView]; 00354 00355 var _swatchLabel = [[CPTextField alloc] initWithFrame: CPRectMake(10, TOOLBAR_HEIGHT + 8 + PREVIEW_HEIGHT + 6, 60, 15)]; 00356 [_swatchLabel setStringValue: "Swatches:"]; 00357 [_swatchLabel setTextColor:[CPColor blackColor]]; 00358 [_swatchLabel setAlignment:CPRightTextAlignment]; 00359 00360 00361 var opacityLabel = [[CPTextField alloc] initWithFrame: CPRectMake(10, TOOLBAR_HEIGHT + PREVIEW_HEIGHT + 35, 60, 20)]; 00362 [opacityLabel setStringValue: "Opacity:"]; 00363 [opacityLabel setTextColor:[CPColor blackColor]]; 00364 [opacityLabel setAlignment:CPRightTextAlignment]; 00365 00366 _opacitySlider = [[CPSlider alloc] initWithFrame:CGRectMake(76, TOOLBAR_HEIGHT + PREVIEW_HEIGHT + 34, CGRectGetWidth(bounds) - 86, 20.0)]; 00367 00368 [_opacitySlider setMinValue:0.0]; 00369 [_opacitySlider setMaxValue:1.0]; 00370 [_opacitySlider setAutoresizingMask:CPViewWidthSizable]; 00371 00372 [_opacitySlider setTarget:self]; 00373 [_opacitySlider setAction:@selector(setOpacity:)]; 00374 00375 [contentView addSubview:_toolbar]; 00376 [contentView addSubview:previewBox]; 00377 [contentView addSubview:_previewLabel]; 00378 [contentView addSubview:swatchBox]; 00379 [contentView addSubview:_swatchLabel]; 00380 [contentView addSubview:opacityLabel]; 00381 [contentView addSubview:_opacitySlider]; 00382 00383 _target = nil; 00384 _action = nil; 00385 _activePicker = nil; 00386 00387 [_previewView setBackgroundColor: _color]; 00388 00389 if (buttonForLater) 00390 [self _setPicker:buttonForLater]; 00391 } 00392 00393 - (void)setOpacity:(id)sender 00394 { 00395 var components = [[self color] components], 00396 alpha = [sender floatValue]; 00397 00398 [self setColor:[_color colorWithAlphaComponent:alpha] updatePicker:YES]; 00399 } 00400 00401 @end 00402 00403 00404 CPColorDragType = "CPColorDragType"; 00405 var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie"; 00406 00407 /* @ignore */ 00408 @implementation _CPColorPanelSwatches : CPView 00409 { 00410 CPView[] _swatches; 00411 CPColor _dragColor; 00412 CPColorPanel _colorPanel; 00413 CPCookie _swatchCookie; 00414 } 00415 00416 - (id)initWithFrame:(CPRect)aFrame 00417 { 00418 self = [super initWithFrame:aFrame]; 00419 00420 [self setBackgroundColor: [CPColor grayColor]]; 00421 00422 [self registerForDraggedTypes:[CPArray arrayWithObjects:CPColorDragType]]; 00423 00424 var whiteColor = [CPColor whiteColor]; 00425 00426 _swatchCookie = [[CPCookie alloc] initWithName: CPColorPanelSwatchesCookie]; 00427 var colorList = [self startingColorList]; 00428 00429 _swatches = []; 00430 00431 for (var i = 0; i < 50; i++) 00432 { 00433 // FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox 00434 var view = [[CPView alloc] initWithFrame: CPRectMake(13 * i + 1, 1, 12, 12)], 00435 fillView = [[CPView alloc] initWithFrame:CGRectInset([view bounds], 1.0, 1.0)]; 00436 00437 [view setBackgroundColor:whiteColor]; 00438 [fillView setBackgroundColor: (i < colorList.length) ? colorList[i] : whiteColor]; 00439 00440 [view addSubview:fillView]; 00441 00442 [self addSubview: view]; 00443 00444 _swatches.push(view); 00445 } 00446 00447 return self; 00448 } 00449 00450 - (BOOL)isOpaque 00451 { 00452 return YES; 00453 } 00454 00455 - (CPArray)startingColorList 00456 { 00457 var cookieValue = [_swatchCookie value]; 00458 00459 if (cookieValue == "") 00460 { 00461 return [ 00462 [CPColor blackColor], 00463 [CPColor darkGrayColor], 00464 [CPColor grayColor], 00465 [CPColor lightGrayColor], 00466 [CPColor whiteColor], 00467 [CPColor redColor], 00468 [CPColor greenColor], 00469 [CPColor blueColor], 00470 [CPColor yellowColor] 00471 ]; 00472 } 00473 00474 var cookieValue = eval(cookieValue), 00475 result = []; 00476 00477 for (var i = 0; i < cookieValue.length; i++) 00478 result.push([CPColor colorWithHexString: cookieValue[i]]); 00479 00480 return result; 00481 } 00482 00483 - (CPArray)saveColorList 00484 { 00485 var result = []; 00486 // FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox 00487 for (var i = 0; i < _swatches.length; i++) 00488 result.push([[[_swatches[i] subviews][0] backgroundColor] hexString]); 00489 00490 var future = new Date(); 00491 future.setYear(2019); 00492 00493 [_swatchCookie setValue: JSON.stringify(result) expires:future domain: nil]; 00494 } 00495 00496 - (void)setColorPanel:(CPColorPanel)panel 00497 { 00498 _colorPanel = panel; 00499 } 00500 00501 - (CPColorPanel)colorPanel 00502 { 00503 return _colorPanel; 00504 } 00505 00506 - (CPColor)colorAtIndex:(int)index 00507 { 00508 return [[_swatches[index] subviews][0] backgroundColor]; 00509 } 00510 00511 - (void)setColor:(CPColor)aColor atIndex:(int)index 00512 { 00513 // FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox 00514 [[_swatches[index] subviews][0] setBackgroundColor:aColor]; 00515 [self saveColorList]; 00516 } 00517 00518 - (void)mouseUp:(CPEvent)anEvent 00519 { 00520 var point = [self convertPoint:[anEvent locationInWindow] fromView:nil], 00521 bounds = [self bounds]; 00522 00523 if (!CGRectContainsPoint(bounds, point) || point.x > [self bounds].size.width - 1 || point.x < 1) 00524 return NO; 00525 00526 [_colorPanel setColor: [self colorAtIndex:FLOOR(point.x / 13)] updatePicker: YES]; 00527 } 00528 00529 - (void)mouseDragged:(CPEvent)anEvent 00530 { 00531 var point = [self convertPoint:[anEvent locationInWindow] fromView:nil]; 00532 00533 if (point.x > [self bounds].size.width - 1 || point.x < 1) 00534 return NO; 00535 00536 [[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:[CPArray arrayWithObject:CPColorDragType] owner:self]; 00537 00538 var swatch = _swatches[FLOOR(point.x / 13)]; 00539 00540 // FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox 00541 _dragColor = [[swatch subviews][0] backgroundColor]; 00542 00543 var bounds = CPRectCreateCopy([swatch bounds]); 00544 00545 // FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox 00546 var dragView = [[CPView alloc] initWithFrame: bounds], 00547 dragFillView = [[CPView alloc] initWithFrame:CGRectInset(bounds, 1.0, 1.0)]; 00548 00549 [dragView setBackgroundColor:[CPColor blackColor]]; 00550 [dragFillView setBackgroundColor:_dragColor]; 00551 00552 [dragView addSubview:dragFillView]; 00553 00554 [self dragView: dragView 00555 at: CPPointMake(point.x - bounds.size.width / 2.0, point.y - bounds.size.height / 2.0) 00556 offset: CPPointMake(0.0, 0.0) 00557 event: anEvent 00558 pasteboard: nil 00559 source: self 00560 slideBack: YES]; 00561 } 00562 00563 - (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType 00564 { 00565 if (aType == CPColorDragType) 00566 [aPasteboard setData:[CPKeyedArchiver archivedDataWithRootObject:_dragColor] forType:aType]; 00567 } 00568 00569 - (void)performDragOperation:(id <CPDraggingInfo>)aSender 00570 { 00571 var location = [self convertPoint:[aSender draggingLocation] fromView:nil], 00572 pasteboard = [aSender draggingPasteboard], 00573 swatch = nil; 00574 00575 if (![pasteboard availableTypeFromArray:[CPColorDragType]] || location.x > [self bounds].size.width - 1 || location.x < 1) 00576 return NO; 00577 00578 [self setColor:[CPKeyedUnarchiver unarchiveObjectWithData:[pasteboard dataForType:CPColorDragType]] atIndex: FLOOR(location.x / 13)]; 00579 } 00580 00581 @end 00582 00583 /* @ignore */ 00584 @implementation _CPColorPanelPreview : CPView 00585 { 00586 CPColorPanel _colorPanel; 00587 } 00588 00589 - (id)initWithFrame:(CPRect)aFrame 00590 { 00591 self = [super initWithFrame:aFrame]; 00592 00593 [self registerForDraggedTypes:[CPArray arrayWithObjects:CPColorDragType]]; 00594 00595 return self; 00596 } 00597 00598 - (void)setColorPanel:(CPColorPanel)aPanel 00599 { 00600 _colorPanel = aPanel; 00601 } 00602 00603 - (CPColorPanel)colorPanel 00604 { 00605 return _colorPanel; 00606 } 00607 00608 - (void)performDragOperation:(id <CPDraggingInfo>)aSender 00609 { 00610 var pasteboard = [aSender draggingPasteboard]; 00611 00612 if (![pasteboard availableTypeFromArray:[CPColorDragType]]) 00613 return NO; 00614 00615 var color = [CPKeyedUnarchiver unarchiveObjectWithData:[pasteboard dataForType:CPColorDragType]]; 00616 [_colorPanel setColor:color updatePicker:YES]; 00617 } 00618 00619 - (BOOL)isOpaque 00620 { 00621 return YES; 00622 } 00623 00624 - (void)mouseDragged:(CPEvent)anEvent 00625 { 00626 var point = [self convertPoint:[anEvent locationInWindow] fromView:nil]; 00627 00628 [[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:[CPColorDragType] owner:self]; 00629 00630 var bounds = CPRectMake(0, 0, 15, 15); 00631 00632 // FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox 00633 var dragView = [[CPView alloc] initWithFrame: bounds], 00634 dragFillView = [[CPView alloc] initWithFrame:CGRectInset(bounds, 1.0, 1.0)]; 00635 00636 [dragView setBackgroundColor:[CPColor blackColor]]; 00637 [dragFillView setBackgroundColor:[self backgroundColor]]; 00638 00639 [dragView addSubview:dragFillView]; 00640 00641 [self dragView: dragView 00642 at: CPPointMake(point.x - bounds.size.width / 2.0, point.y - bounds.size.height / 2.0) 00643 offset: CPPointMake(0.0, 0.0) 00644 event: anEvent 00645 pasteboard: nil 00646 source: self 00647 slideBack: YES]; 00648 } 00649 00650 - (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType 00651 { 00652 if (aType == CPColorDragType) 00653 [aPasteboard setData:[CPKeyedArchiver archivedDataWithRootObject:[self backgroundColor]] forType:aType]; 00654 } 00655 00656 @end 00657 00658 00659 [CPColorPanel provideColorPickerClass:CPColorWheelColorPicker]; 00660 [CPColorPanel provideColorPickerClass:CPSliderColorPicker];