/* Copyright (C) 2009-2011 id Software LLC, a ZeniMax Media company. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #import #import #import "Carousel.h" #include @implementation idCarouselItem @synthesize image; @synthesize title; @synthesize hash; @end @implementation idCarousel // Move full turn at this percentage const static float CAROUSEL_CHANGE_DRAG = 0.2f; const static float CAROUSEL_BACKITEMS_ALPHA = 0.5f; const static float CAROUSEL_FRONTITEM_ALPHA = 1.0f; enum idCarouselDrag_t { CAROUSEL_DRAG_MOVE_NONE = 0, CAROUSEL_DRAG_MOVE_FORWARD, CAROUSEL_DRAG_MOVE_BACKWARD }; /* ======================== Lerp ======================== */ static CGFloat Lerp(CGFloat from, CGFloat to, CGFloat f ) { return from + ( ( to - from ) * f ); } /* ======================== LerpPoint ======================== */ static CGPoint LerpPoint(CGPoint a, CGPoint b, CGFloat t) { CGPoint p; p.x = Lerp(a.x, b.x, t); p.y = Lerp(a.y, b.y, t); return p; } /* ======================== LerpSize ======================== */ static CGSize LerpSize(CGSize a, CGSize b, CGFloat t) { CGSize s; s.width = Lerp(a.width, b.width, t); s.height = Lerp(a.height, b.height, t); return s; } /* ======================== LerpRect ======================== */ static CGRect LerpRect( CGRect a, CGRect b, CGFloat t ) { CGRect r; r.origin = LerpPoint( a.origin, b.origin, t ); r.size = LerpSize( a.size, b.size, t ); return r; } /* ======================== awakeFromNib ======================== */ - (void) awakeFromNib { mainRect = mainSelection.frame; prevRect = prevSelection.frame; nextRect = nextSelection.frame; // Allocate our Items Array carouselItems = [[ NSMutableArray alloc ] init ]; } /* ======================== touchesBegan ======================== */ - (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { (void)(event); hasDragged = 0; startDragPoint = [[touches anyObject] locationInView:self]; } /* ======================== touchesMoved ======================== */ - (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { (void)(event); if( prevSelection == nil || nextSelection == nil ) { // Nothing to transition to... dont swip transition return; } CGPoint movedPoint = [[touches anyObject] locationInView:self]; CGFloat offsetX = movedPoint.x - startDragPoint.x; // Find out where im at relative to the side of the screen CGFloat percentage = offsetX / self.frame.size.width; percentage = (float)fmin( percentage, 1.0f ); percentage = (float)fmax( percentage, -1.0f ); if( percentage > CAROUSEL_CHANGE_DRAG ) { hasDragged = CAROUSEL_DRAG_MOVE_FORWARD; } else if( percentage < -CAROUSEL_CHANGE_DRAG ) { hasDragged = CAROUSEL_DRAG_MOVE_BACKWARD; } if( percentage > 0 ) { // Going to the right of the screen.. CGRect newMainRect = LerpRect( mainRect, nextRect , percentage ); CGRect newNextRect = LerpRect( nextRect, prevRect , percentage ); CGRect newPrevRect = LerpRect( prevRect, mainRect , percentage ); mainSelection.frame = newMainRect; prevSelection.frame = newPrevRect; nextSelection.frame = newNextRect; mainSelection.alpha = Lerp( CAROUSEL_FRONTITEM_ALPHA, CAROUSEL_BACKITEMS_ALPHA, percentage ); prevSelection.alpha = Lerp( CAROUSEL_BACKITEMS_ALPHA, CAROUSEL_FRONTITEM_ALPHA, percentage ); } else { // Going the left. percentage = fabsf( percentage ); CGRect newMainRect = LerpRect( mainRect, prevRect , percentage ); CGRect newNextRect = LerpRect( nextRect, mainRect , percentage ); CGRect newPrevRect = LerpRect( prevRect, nextRect , percentage ); mainSelection.frame = newMainRect; prevSelection.frame = newPrevRect; nextSelection.frame = newNextRect; mainSelection.alpha = Lerp( CAROUSEL_FRONTITEM_ALPHA, CAROUSEL_BACKITEMS_ALPHA, percentage ); nextSelection.alpha = Lerp( CAROUSEL_BACKITEMS_ALPHA, CAROUSEL_FRONTITEM_ALPHA, percentage ); } } /* ======================== touchesEnded ======================== */ - (void) touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { (void)(event); (void)(touches); if( hasDragged == CAROUSEL_DRAG_MOVE_FORWARD ) { [ self RotateForward ]; } else if( hasDragged == CAROUSEL_DRAG_MOVE_BACKWARD ) { [ self RotateBackward ]; } else { [ self RotateToOrigin ]; } } /* ======================== Init ======================== */ - (void) Init { // Get the items At the new layout. idCarouselItem * cItem = [ carouselItems objectAtIndex: (NSUInteger)currentItem ]; idCarouselItem * nItem = [ carouselItems objectAtIndex: (NSUInteger)nextItem ]; idCarouselItem * pItem = [ carouselItems objectAtIndex: (NSUInteger)prevItem ]; // Set the Main Selection Text. [ selectionLabel setText: cItem.title ]; // Set the prev selection UIImage mainSelection.image = cItem.image; prevSelection.image = nItem.image; nextSelection.image = pItem.image; } /* ======================== AddCarouselItem ======================== */ - (void) AddCarouselItem:( NSString* )Image :( NSString* )Title :( int ) ItemHash { idCarouselItem * Item = [ idCarouselItem alloc ]; // Initialize the Item with the data passed in. Item.image = [UIImage imageNamed: Image ]; Item.title = Title; Item.hash = ItemHash; // Add the Item to our object array. [ carouselItems addObject: Item ]; // Set the Cur Item as the one just added. currentItem = 0; nextItem = 1; prevItem = (NSInteger)[ carouselItems count ] - 1; } /* ======================== GetSelectedItem_Index ======================== */ - (NSInteger) GetSelectedItem_Index { return currentItem; } /* ======================== GetSelectedItem_Hash ======================== */ - (int) GetSelectedItem_Hash { idCarouselItem * item = [ carouselItems objectAtIndex: (NSUInteger)currentItem ]; return item.hash; } /* ======================== MoveForward Interface Builder Direct Access to moving the Carousel ======================== */ - (IBAction) MoveForward { [ self RotateForward ]; } /* ======================== MoveBackward - Interface Builder Direct Access to moving the Carousel ======================== */ - (IBAction) MoveBackward { [ self RotateBackward ]; } /* ======================== RotateToOrigin ======================== */ - (void) RotateToOrigin { // Animate back to the correct position. // Move the Current Selection to Next Selection [UIView beginAnimations:nil context:nil]; [UIView setAnimationCurve:UIViewAnimationCurveEaseOut]; [UIView setAnimationBeginsFromCurrentState:YES]; // Move them into Place. mainSelection.frame = mainRect; prevSelection.frame = prevRect; nextSelection.frame = nextRect; mainSelection.alpha = 1.0f; nextSelection.alpha = CAROUSEL_BACKITEMS_ALPHA; prevSelection.alpha = CAROUSEL_BACKITEMS_ALPHA; [UIView commitAnimations]; } /* ======================== RotateForward ======================== */ - (void) RotateForward { if( prevSelection == nil || nextSelection == nil ) { // Flip book to the next selection. // increment the item indicies. currentItem++; nextItem++; prevItem++; // Check the Extents. if( currentItem >= (NSInteger)[ carouselItems count ] ) { currentItem = 0; } if( nextItem >= (NSInteger)[ carouselItems count ] ) { nextItem = 0; } if( prevItem >= (NSInteger)[ carouselItems count ] ) { prevItem = 0; } idCarouselItem * cItem = [ carouselItems objectAtIndex: (NSUInteger)currentItem ]; // Set the Main Selection Text. [ selectionLabel setText: cItem.title ]; // Set the prev selection UIImage mainSelection.image = cItem.image; [self sendActionsForControlEvents:UIControlEventValueChanged]; return; } UIImageView * tempMain = mainSelection; UIImageView * tempPrev = prevSelection; UIImageView * tempNext = nextSelection; // Move the Current Selection to Next Selection [UIView beginAnimations:nil context:nil]; [UIView setAnimationCurve:UIViewAnimationCurveEaseOut]; [UIView setAnimationBeginsFromCurrentState:YES]; // Move them into Place. mainSelection.frame = nextRect; prevSelection.frame = mainRect; nextSelection.frame = prevRect; // Darken the prev/next mainSelection.alpha = CAROUSEL_BACKITEMS_ALPHA; prevSelection.alpha = CAROUSEL_FRONTITEM_ALPHA; nextSelection.alpha = CAROUSEL_BACKITEMS_ALPHA; [UIView commitAnimations]; // Swap out the ImageViews so that they are in the correct order. nextSelection = tempMain; mainSelection = tempPrev; prevSelection = tempNext; // Make sure they are in back of the main selection. [ self sendSubviewToBack: nextSelection ]; [ self sendSubviewToBack: prevSelection ]; // increment the item indicies. currentItem++; nextItem++; prevItem++; // Check the Extents. if( currentItem >= (NSInteger)[ carouselItems count ] ) { currentItem = 0; } if( nextItem >= (NSInteger)[ carouselItems count ] ) { nextItem = 0; } if( prevItem >= (NSInteger)[ carouselItems count ] ) { prevItem = 0; } // Get the items At the new layout. idCarouselItem * cItem = [ carouselItems objectAtIndex: (NSUInteger)currentItem ]; idCarouselItem * nItem = [ carouselItems objectAtIndex: (NSUInteger)nextItem ]; idCarouselItem * pItem = [ carouselItems objectAtIndex: (NSUInteger)prevItem ]; // Set the Main Selection Text. [ selectionLabel setText: cItem.title ]; // Set the prev selection UIImage mainSelection.image = cItem.image; prevSelection.image = nItem.image; nextSelection.image = pItem.image; [self sendActionsForControlEvents:UIControlEventValueChanged]; } /* ======================== RotateBackward ======================== */ - (void) RotateBackward { if( nextSelection == nil || prevSelection == nil ) { // increment the item indicies. currentItem--; nextItem--; prevItem--; // Check the Extents. if( currentItem < 0 ) { currentItem = (NSInteger)[ carouselItems count ] - 1; } if( nextItem < 0 ) { nextItem = (NSInteger)[ carouselItems count ] - 1;; } if( prevItem < 0 ) { prevItem = (NSInteger)[ carouselItems count ] - 1;; } idCarouselItem * cItem = [ carouselItems objectAtIndex: (NSUInteger)currentItem ]; // Set the Main Selection Text. [ selectionLabel setText: cItem.title ]; // Set the prev selection UIImage mainSelection.image = cItem.image; [self sendActionsForControlEvents:UIControlEventValueChanged]; return; } UIImageView * tempMain = mainSelection; UIImageView * tempPrev = prevSelection; UIImageView * tempNext = nextSelection; // Move the Current Selection to Next Selection [UIView beginAnimations:nil context:nil]; [UIView setAnimationCurve:UIViewAnimationCurveEaseOut]; [UIView setAnimationBeginsFromCurrentState:YES]; // Move them into Place. mainSelection.frame = prevRect; prevSelection.frame = nextRect; nextSelection.frame = mainRect; // Darken the prev/next mainSelection.alpha = CAROUSEL_BACKITEMS_ALPHA; prevSelection.alpha = CAROUSEL_BACKITEMS_ALPHA; nextSelection.alpha = CAROUSEL_FRONTITEM_ALPHA; [UIView commitAnimations]; // Swap out the ImageViews so that they are in the correct order. nextSelection = tempPrev; mainSelection = tempNext; prevSelection = tempMain; // Make sure they are in back of the main selection. [ self sendSubviewToBack: nextSelection ]; [ self sendSubviewToBack: prevSelection ]; // increment the item indicies. currentItem--; nextItem--; prevItem--; // Check the Extents. if( currentItem < 0 ) { currentItem = (NSInteger)[ carouselItems count ] - 1; } if( nextItem < 0 ) { nextItem = (NSInteger)[ carouselItems count ] - 1;; } if( prevItem < 0 ) { prevItem = (NSInteger)[ carouselItems count ] - 1;; } // Get the items At the new layout. idCarouselItem * cItem = [ carouselItems objectAtIndex: (NSUInteger) currentItem ]; idCarouselItem * nItem = [ carouselItems objectAtIndex: (NSUInteger)nextItem ]; idCarouselItem * pItem = [ carouselItems objectAtIndex: (NSUInteger)prevItem ]; // Set the Main Selection Text. [ selectionLabel setText: cItem.title ]; // Set the prev selection UIImage mainSelection.image = cItem.image; prevSelection.image = nItem.image; nextSelection.image = pItem.image; [self sendActionsForControlEvents:UIControlEventValueChanged]; } @end