Permalink
Browse files

[NEW] Added ability to draw placeholders for flow items which either …

…do not have an image assigned or have not yet had their image loaded.
  • Loading branch information...
mattball committed Mar 31, 2009
1 parent 5817bbb commit b73d80abbf3ba90390bed07eb456b02e86d311fa
Showing with 121 additions and 8 deletions.
  1. +8 −1 MBCoverFlowImageLoadOperation.h
  2. +11 −2 MBCoverFlowImageLoadOperation.m
  3. +12 −0 MBCoverFlowView.h
  4. +90 −5 MBCoverFlowView.m
@@ -17,6 +17,7 @@
@interface MBCoverFlowImageLoadOperation : NSOperation {
CALayer *_layer;
NSString *_imageKeyPath;
+ NSImage *_placeholder;
}
/**
@@ -32,7 +33,7 @@
*
* @return The initialized MBCoverFlowImageLoadOperation object.
*/
-- (id)initWithLayer:(CALayer *)layer imageKeyPath:(NSString *)imageKeyPath;
+- (id)initWithLayer:(CALayer *)layer imageKeyPath:(NSString *)imageKeyPath placeholder:(NSImage *)placeholder;
/**
* @name Relationships
@@ -51,4 +52,10 @@
*/
@property (nonatomic, copy) NSString *imageKeyPath;
+/**
+ * @brief The image which will be displayed if there is no image content
+ * for the layer.
+ */
+@property (nonatomic, retain) NSImage *placeholder;
+
@end
@@ -12,13 +12,14 @@
@implementation MBCoverFlowImageLoadOperation
-@synthesize layer=_layer, imageKeyPath=_imageKeyPath;
+@synthesize layer=_layer, imageKeyPath=_imageKeyPath, placeholder=_placeholder;
-- (id)initWithLayer:(CALayer *)layer imageKeyPath:(NSString *)imageKeyPath;
+- (id)initWithLayer:(CALayer *)layer imageKeyPath:(NSString *)imageKeyPath placeholder:(NSImage *)placeholder
{
if (self = [super init]) {
_layer = [layer retain];
_imageKeyPath = [imageKeyPath copy];
+ _placeholder = [placeholder retain];
}
return self;
}
@@ -27,6 +28,7 @@ - (void)dealloc
{
self.layer = nil;
self.imageKeyPath = nil;
+ self.placeholder = nil;
[super dealloc];
}
@@ -47,6 +49,13 @@ - (void)main
image = (NSImage *)object;
}
+ if (!image) {
+ image = self.placeholder;
+ [self.layer setValue:[NSNumber numberWithBool:NO] forKey:@"hasImage"];
+ } else {
+ [self.layer setValue:[NSNumber numberWithBool:YES] forKey:@"hasImage"];
+ }
+
CGImageRef imageRef = [image imageRef];
CALayer *imageLayer = [[self.layer sublayers] objectAtIndex:0];
View
@@ -61,6 +61,9 @@
NSString *_imageKeyPath;
NSOperationQueue *_imageLoadQueue;
+
+ NSImage *_placeholder;
+ NSImage *_placeholderIcon;
}
/**
@@ -106,6 +109,15 @@
*/
@property (nonatomic, retain) NSViewController *accessoryController;
+/**
+ * @brief The icon which will be displayed for items which have not had
+ * image data loaded.
+ * @details This image should preferably be a template icon (using NSImage's
+ * \c -setTemplate: method), so that the view can color the icon
+ * appropriately.
+ */
+@property (nonatomic, retain) NSImage *placeholderIcon;
+
/**
* @name Managing the Selection
*/
View
@@ -34,6 +34,7 @@ of this software and associated documentation files (the "Software"), to deal
// Constants
const float MBCoverFlowViewCellSpacing = 14.0;
+const float MBCoverFlowViewPlaceholderHeight = 600;
const float MBCoverFlowViewTopMargin = 30.0;
const float MBCoverFlowViewBottomMargin = 20.0;
@@ -65,14 +66,16 @@ - (CALayer *)_newLayer;
- (void)_scrollerChange:(MBCoverFlowScroller *)scroller;
- (void)_refreshLayer:(CALayer *)layer;
- (CALayer *)_layerForObject:(id)object;
+- (void)_recachePlaceholder;
@end
@implementation MBCoverFlowView
@synthesize accessoryController=_accessoryController, selectionIndex=_selectionIndex,
itemSize=_itemSize, content=_content, showsScrollbar=_showsScrollbar,
- autoresizesItems=_autoresizesItems, imageKeyPath=_imageKeyPath;
+ autoresizesItems=_autoresizesItems, imageKeyPath=_imageKeyPath,
+ placeholderIcon=_placeholderIcon;
#pragma mark -
#pragma mark Life Cycle
@@ -83,6 +86,8 @@ - (id)initWithFrame:(NSRect)frameRect
_imageLoadQueue = [[NSOperationQueue alloc] init];
[_imageLoadQueue setMaxConcurrentOperationCount:1];
+ _placeholderIcon = [[NSImage imageNamed:NSImageNameQuickLookTemplate] retain];
+
_autoresizesItems = YES;
[self setAutoresizesSubviews:YES];
@@ -238,12 +243,17 @@ - (id)initWithFrame:(NSRect)frameRect
- (void)dealloc
{
+ if (_placeholder) {
+ [_placeholder release];
+ }
+
[_scroller release];
[_scrollLayer release];
[_containerLayer release];
self.accessoryController = nil;
self.content = nil;
self.imageKeyPath = nil;
+ self.placeholderIcon = nil;
CGImageRelease(_shadowImage);
[_imageLoadQueue release];
_imageLoadQueue = nil;
@@ -253,6 +263,7 @@ - (void)dealloc
- (void)awakeFromNib
{
[self setWantsLayer:YES];
+ [self _recachePlaceholder];
}
#pragma mark -
@@ -473,6 +484,7 @@ - (void)setItemSize:(NSSize)newSize
[_rightGradientLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMinX relativeTo:@"superlayer" attribute:kCAConstraintMaxX scale:.5 offset:[self itemSize].width / 2]];
// Update the view
+ [self _recachePlaceholder];
[self.layer setNeedsLayout];
CALayer *layer = [[_scrollLayer sublayers] objectAtIndex:self.selectionIndex];
@@ -600,21 +612,21 @@ - (CALayer *)_newLayer
frame.size = NSSizeToCGSize([self itemSize]);
[imageLayer setBounds:frame];
- [imageLayer setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)];
+ imageLayer.contents = (id)[_placeholder imageRef];
imageLayer.name = @"image";
[layer setBounds:frame];
- [layer setBackgroundColor:CGColorGetConstantColor(kCGColorClear)];
[layer setValue:[NSNumber numberWithInteger:[[_scrollLayer sublayers] count]] forKey:@"index"];
[layer setSublayers:[NSArray arrayWithObject:imageLayer]];
[layer setSublayerTransform:sublayerTransform];
+ [layer setValue:[NSNumber numberWithBool:NO] forKey:@"hasImage"];
CALayer *reflectionLayer = [CALayer layer];
frame.origin.y = -frame.size.height;
[reflectionLayer setFrame:frame];
reflectionLayer.name = @"reflection";
reflectionLayer.transform = CATransform3DMakeScale(1, -1, 1);
- [reflectionLayer setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)];
+ reflectionLayer.contents = (id)[_placeholder imageRef];
[imageLayer addSublayer:reflectionLayer];
CALayer *gradientLayer = [CALayer layer];
@@ -656,9 +668,10 @@ - (void)_refreshLayer:(CALayer *)layer
NSInteger index = [self.content indexOfObject:object];
[layer setValue:[NSNumber numberWithInteger:index] forKey:@"index"];
+ [layer setValue:[NSNumber numberWithBool:NO] forKey:@"hasImage"];
// Create the operation
- NSOperation *operation = [[MBCoverFlowImageLoadOperation alloc] initWithLayer:layer imageKeyPath:self.imageKeyPath];
+ NSOperation *operation = [[MBCoverFlowImageLoadOperation alloc] initWithLayer:layer imageKeyPath:self.imageKeyPath placeholder:_placeholder];
[_imageLoadQueue addOperation:operation];
[operation release];
}
@@ -673,6 +686,78 @@ - (CALayer *)_layerForObject:(id)object
return nil;
}
+- (void)_recachePlaceholder
+{
+ if (_placeholder) {
+ [_placeholder release];
+ _placeholder = nil;
+ }
+
+ NSSize itemSize = self.itemSize;
+ NSSize placeholderSize;
+ placeholderSize.height = MBCoverFlowViewPlaceholderHeight;
+ placeholderSize.width = itemSize.width * placeholderSize.height/itemSize.height;
+
+ _placeholder = [[NSImage alloc] initWithSize:placeholderSize];
+ [_placeholder lockFocus];
+ NSColor *topColor = [NSColor colorWithCalibratedWhite:0.15 alpha:1.0];
+ NSColor *bottomColor = [NSColor colorWithCalibratedWhite:0.0 alpha:1.0];
+ NSGradient *gradient = [[NSGradient alloc] initWithStartingColor:topColor endingColor:bottomColor];
+ [gradient drawInRect:NSMakeRect(0, 0, placeholderSize.width, placeholderSize.height) relativeCenterPosition:NSMakePoint(0, 1)];
+ [gradient release];
+
+ // Draw the top bevel line
+ NSColor *bevelColor = [NSColor colorWithCalibratedWhite:0.3 alpha:1.0];
+ [bevelColor set];
+ NSRectFill(NSMakeRect(0, placeholderSize.height-5.0, placeholderSize.width, 5.0));
+
+ NSColor *bottomBevelColor = [NSColor colorWithCalibratedWhite:0.1 alpha:1.0];
+ [bottomBevelColor set];
+ NSRectFill(NSMakeRect(0, 0, placeholderSize.width, 5.0));
+
+ // Draw the placeholder icon
+ if (self.placeholderIcon) {
+ NSRect iconRect;
+ iconRect.size.height = placeholderSize.height/2;
+ iconRect.size.width = iconRect.size.height * [self placeholderIcon].size.width/[self placeholderIcon].size.height;
+
+ if (iconRect.size.width > placeholderSize.width * 0.666) {
+ iconRect.size.width = placeholderSize.width/2;
+ iconRect.size.height = iconRect.size.width * [self placeholderIcon].size.height/[self placeholderIcon].size.width;
+ }
+
+ iconRect.origin.x = (placeholderSize.width - iconRect.size.width)/2;
+ iconRect.origin.y = (placeholderSize.height - iconRect.size.height)/2;
+
+ NSImage *icon = [[NSImage alloc] initWithSize:iconRect.size];
+ [icon lockFocus];
+ NSColor *iconTopColor = [NSColor colorWithCalibratedRed:0.380 green:0.400 blue:0.427 alpha:1.0];
+ NSColor *iconBottomColor = [NSColor colorWithCalibratedRed:0.224 green:0.255 blue:0.302 alpha:1.0];
+ NSGradient *iconGradient = [[NSGradient alloc] initWithStartingColor:iconTopColor endingColor:iconBottomColor];
+ [iconGradient drawInRect:NSMakeRect(0, 0, iconRect.size.width, iconRect.size.width) angle:-90.0];
+ [iconGradient release];
+ [self.placeholderIcon drawInRect:NSMakeRect(0, 0, iconRect.size.width, iconRect.size.height) fromRect:NSZeroRect operation:NSCompositeDestinationIn fraction:1.0];
+ [icon unlockFocus];
+
+ [icon drawInRect:iconRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
+ [icon release];
+ }
+
+ [_placeholder unlockFocus];
+
+ // Update the placeholder for all necessary items
+ for (CALayer *layer in [_scrollLayer sublayers]) {
+ if (![[layer valueForKey:@"hasImage"] boolValue]) {
+ CALayer *imageLayer = [[self.layer sublayers] objectAtIndex:0];
+ CALayer *reflectionLayer = [[imageLayer sublayers] objectAtIndex:0];
+ CGImageRef placeholderRef = [_placeholder imageRef];
+ imageLayer.contents = (id)placeholderRef;
+ reflectionLayer.contents = (id)placeholderRef;
+ NSLog(@"test");
+ }
+ }
+}
+
#pragma mark -
#pragma mark Protocol Methods

0 comments on commit b73d80a

Please sign in to comment.