- //
- // PSCoverFlow.m
- // CoverFlow
- //
- // Created by Adam Czubernat on 23.12.2013.
- // Copyright (c) 2013 Perforce Software, Inc. All rights reserved.
- //
- #import "PSCoverFlow.h"
- #import <QuartzCore/QuartzCore.h>
- @interface PSCoverFlow () {
- CAScrollLayer *scrollLayer;
- CATransform3D perspective;
- CATransform3D leftTransform;
- CATransform3D rightTransform;
- CGSize size;
- NSInteger selectedIndex;
- }
- - (void)selectIndex:(NSInteger)index;
- - (void)postSelectionChange;
- - (void)layoutLayers;
- - (void)layoutShadowLayer:(CALayer *)layer;
- @end
- @implementation PSCoverFlow
- @synthesize delegate;
- - (id)initWithFrame:(NSRect)frameRect {
- self = [super initWithFrame:frameRect];
- if (self) {
- self.layer = [CALayer layer];
- self.wantsLayer = YES;
- scrollLayer = [CAScrollLayer layer];
- scrollLayer.frame = (CGRect) { CGPointZero, frameRect.size };
- scrollLayer.scrollMode = kCAScrollHorizontally;
- scrollLayer.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
- scrollLayer.layoutManager = self;
- [self.layer addSublayer:scrollLayer];
- perspective = CATransform3DIdentity;
- perspective.m34 = - 1.0f / 800.0f;
- // Gradient mask
- CAGradientLayer *gradient = [CAGradientLayer layer];
- gradient.frame = (CGRect) { CGPointZero, frameRect.size };
- gradient.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
- id clearColor = (__bridge id)CGColorGetConstantColor(kCGColorClear);
- id opaqueColor = (__bridge id)CGColorGetConstantColor(kCGColorBlack);
- gradient.colors = @[ clearColor, opaqueColor, opaqueColor, clearColor ];
- gradient.startPoint = CGPointMake(-0.1f, 0);
- gradient.endPoint = CGPointMake(1.1f, 0);
- self.layer.mask = gradient;
- }
- return self;
- }
- - (void)setFrame:(NSRect)frameRect {
- [CATransaction begin];
- [CATransaction setDisableActions:YES];
- [super setFrame:frameRect];
- [self layoutLayers];
- [CATransaction commit];
- }
- - (void)viewDidMoveToWindow {
- if (self.window)
- [self reload];
- }
- #pragma mark - CALayoutManager
- - (void)layoutSublayersOfLayer:(CALayer *)layer {
- CGFloat spacing = size.width * 0.8f;
- [scrollLayer scrollToPoint:(CGPoint) {
- size.width * selectedIndex - (self.frame.size.width - size.width) / 2.0f, 0.0f
- }];
- [layer.sublayers enumerateObjectsUsingBlock:^(CALayer *sublayer, NSUInteger idx, BOOL *stop) {
- CATransform3D transform = CATransform3DIdentity;
- if (idx < selectedIndex) {
- CGFloat x = spacing * (selectedIndex - idx);
- transform = CATransform3DTranslate(transform, x, 0, 0);
- transform = CATransform3DConcat(leftTransform, transform);
- } else if (idx > selectedIndex) {
- CGFloat x = -spacing * (idx - selectedIndex);
- transform = CATransform3DTranslate(transform, x, 0, 0);
- transform = CATransform3DConcat(rightTransform, transform);
- }
- sublayer.transform = transform;
- }];
- }
- #pragma mark - Private
- - (void)postSelectionChange {
- if (!self.superview)
- return;
- [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(postSelectionChange) object:nil];
- if ([delegate respondsToSelector:@selector(coverFlow:didSelectIndex:)])
- [delegate coverFlow:self didSelectIndex:selectedIndex];
- }
- - (void)selectIndex:(NSInteger)index {
- if (index >= scrollLayer.sublayers.count || index < 0 || index == selectedIndex)
- return;
- selectedIndex = index;
- [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(postSelectionChange) object:nil];
- [self performSelector:@selector(postSelectionChange) withObject:nil afterDelay:0.2f];
- [CATransaction begin];
- [CATransaction setAnimationTimingFunction:
- [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
- [scrollLayer setNeedsLayout];
- [CATransaction commit];
- }
- - (void)layoutLayers {
- CGFloat angle = 60.0f * M_PI/180;
- CGFloat recess = -200.0f;
- CGFloat sizeScale = 0.85f;
- size = self.frame.size;
- size.width = size.height *= sizeScale;
- CGFloat margin = size.width * 0.6f;
- CGFloat frameMargin = self.frame.size.height * (1.0f - sizeScale) / 2.0f;
- leftTransform = CATransform3DTranslate(perspective, -margin, 0, recess);
- leftTransform = CATransform3DRotate(leftTransform, angle, 0, 1, 0);
- rightTransform = CATransform3DTranslate(perspective, margin, 0, recess);
- rightTransform = CATransform3DRotate(rightTransform, -angle, 0, 1, 0);
- [scrollLayer.sublayers enumerateObjectsUsingBlock:^(CALayer *layer, NSUInteger idx, BOOL *stop) {
- // Resize layers
- layer.frame = (CGRect) { idx * size.width, frameMargin, size };
- [self layoutShadowLayer:layer];
- }];
- }
- - (void)layoutShadowLayer:(CALayer *)layer {
- NSImage *image = layer.contents;
- if (!image) {
- layer.shadowOpacity = 0;
- return;
- }
- // Shadow aspect fit
- CGRect rect = { .size = [layer.contents size] };
- CGFloat ratio = fminf(size.width / rect.size.width, size.height / rect.size.height);
- rect.size = (CGSize) { rect.size.width * ratio, rect.size.height * ratio };
- rect.origin = (CGPoint) { (size.width - rect.size.width) / 2.0f, (size.height - rect.size.height) / 2.0f };
- // Resize shadow
- CGPathRef shadowPath = CGPathCreateWithRect(rect, NULL);
- layer.shadowRadius = size.height * 0.02f;
- layer.shadowPath = shadowPath;
- layer.shadowOpacity = 1.0f;
- CGPathRelease(shadowPath);
- }
- #pragma mark - Public
- - (void)reload {
- [scrollLayer.sublayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)];
- NSInteger imagesCount = 0;
- if ([delegate respondsToSelector:@selector(numberOfImagesInCoverFlow:)])
- imagesCount = [delegate numberOfImagesInCoverFlow:self];
- for (NSInteger index = 0; index < imagesCount; index++) {
- CALayer *layer = [CALayer layer];
- layer.shouldRasterize = YES;
- layer.contentsGravity = kCAGravityResizeAspect;
- CGColorRef color = CGColorCreateGenericGray(0, 0.2f);
- layer.borderColor = color;
- CGColorRelease(color);
- layer.shadowOffset = CGSizeZero;
- layer.shadowColor = CGColorGetConstantColor(kCGColorBlack);
- [scrollLayer addSublayer:layer];
- }
- [self reloadImagesInRange:(NSRange) { 0, imagesCount }];
- [self selectIndex:0];
- [self layoutLayers];
- }
- - (void)reloadImagesInRange:(NSRange)range {
- NSInteger count = MIN(range.location + range.length, scrollLayer.sublayers.count);
- for (NSInteger index = range.location; index < count; index++) {
- NSImage *image = nil;
- if ([delegate respondsToSelector:@selector(coverFlow:imageForIndex:)])
- image = [delegate coverFlow:self imageForIndex:index];
- // image = image ?: [NSImage imageNamed:NSImageNameQuickLookTemplate];
- CALayer *layer = [scrollLayer.sublayers objectAtIndex:index];
- layer.contents = image;
- CGColorRef color = image ? nil : CGColorCreateGenericGray(0.7, 0.9f);
- layer.backgroundColor = color;
- CGColorRelease(color);
- layer.borderWidth = image ? 0 : 2.0f;
- [self layoutShadowLayer:layer];
- }
- }
- - (NSInteger)selectedIndex {
- return selectedIndex;
- }
- - (void)setSelectedIndex:(NSInteger)index {
- if (index >= scrollLayer.sublayers.count || index < 0 || index == selectedIndex)
- return;
- selectedIndex = index;
- [CATransaction begin];
- [CATransaction setAnimationTimingFunction:
- [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
- [scrollLayer setNeedsLayout];
- [CATransaction commit];
- }
- #pragma mark - NSResponder
- - (BOOL)acceptsFirstResponder {
- return YES;
- }
- - (void)mouseDown:(NSEvent *)theEvent {
- CGPoint point = [self convertPoint:theEvent.locationInWindow fromView:nil];
- CALayer *layer = [scrollLayer hitTest:point];
- NSInteger index = [scrollLayer.sublayers indexOfObjectIdenticalTo:layer];
- if (index != NSNotFound)
- [self selectIndex:index];
- }
- - (void)scrollWheel:(NSEvent *)theEvent {
- CGFloat delta = theEvent.deltaX ?: theEvent.deltaY;
- if (fabs(delta) > 1.0f)
- [self selectIndex:selectedIndex + (delta > 0 ? -1 : 1)];
- }
- - (void)keyDown:(NSEvent *)event {
- unichar keyChar = [[event charactersIgnoringModifiers] characterAtIndex:0];
- if (keyChar == NSLeftArrowFunctionKey)
- [self moveLeft:nil];
- else if (keyChar == NSRightArrowFunctionKey)
- [self moveRight:nil];
- else if (keyChar == NSUpArrowFunctionKey)
- [self moveUp:nil];
- else if (keyChar == NSDownArrowFunctionKey)
- [self moveDown:nil];
- else
- [super keyDown:event];
- }
- - (void)moveUp:(id)sender {
- [self moveLeft:sender];
- }
- - (void)moveDown:(id)sender {
- [self moveRight:sender];
- }
- - (void)moveLeft:(id)sender {
- [self selectIndex:selectedIndex - 1];
- }
- - (void)moveRight:(id)sender {
- [self selectIndex:selectedIndex + 1];
- }
- @end
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 15071 | alan_petersen |
Populate -o //guest/perforce_software/piper/... //guest/alan_petersen/piper/.... |
10 years ago | |
//guest/perforce_software/piper/mac/R1.0/Perforce/Classes/Views/PSCoverFlow.m | |||||
#1 | 11254 | alan_petersen | Populate //guest/perforce_software/piper/mac/R1.0/... from //guest/perforce_software/pipe...r/mac/main/.... « |
10 years ago | |
//guest/perforce_software/piper/mac/main/Perforce/Classes/Views/PSCoverFlow.m | |||||
#1 | 11252 | alan_petersen | Rename/move file(s) | 10 years ago | |
//guest/perforce_software/piper/mac/Perforce/Classes/Views/PSCoverFlow.m | |||||
#1 | 10744 | alan_petersen | Rename/move file(s) | 10 years ago | |
//guest/perforce_software/piper/Perforce/Classes/Views/PSCoverFlow.m | |||||
#2 | 8985 | Matt Attaway |
Update the copyright on a couple files No functional change. |
11 years ago | |
#1 | 8919 | Matt Attaway | Initial add of Piper, a lightweight Perforce client for artists and designers. | 11 years ago |