/libs/cocos2d/CCMenu.m

http://github.com/kstenerud/ObjectAL-for-iPhone · Objective C · 419 lines · 297 code · 88 blank · 34 comment · 26 complexity · 70a5fb26222dc1ab25fa1d6f55488b2b MD5 · raw file

  1. /*
  2. * cocos2d for iPhone: http://www.cocos2d-iphone.org
  3. *
  4. * Copyright (c) 2008-2010 Ricardo Quesada
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. * THE SOFTWARE.
  23. *
  24. */
  25. #import "CCMenu.h"
  26. #import "CCDirector.h"
  27. #import "CCTouchDispatcher.h"
  28. #import "Support/CGPointExtension.h"
  29. enum {
  30. kDefaultPadding = 5,
  31. };
  32. @interface CCMenu (Private)
  33. // returns touched menu item, if any
  34. -(CCMenuItem *) itemForTouch: (UITouch *) touch;
  35. @end
  36. @implementation CCMenu
  37. @synthesize opacity=opacity_, color=color_;
  38. - (id) init
  39. {
  40. NSException* myException = [NSException
  41. exceptionWithName:@"MenuInit"
  42. reason:@"Use initWithItems instead"
  43. userInfo:nil];
  44. @throw myException;
  45. }
  46. +(id) menuWithItems: (CCMenuItem*) item, ...
  47. {
  48. va_list args;
  49. va_start(args,item);
  50. id s = [[[self alloc] initWithItems: item vaList:args] autorelease];
  51. va_end(args);
  52. return s;
  53. }
  54. -(id) initWithItems: (CCMenuItem*) item vaList: (va_list) args
  55. {
  56. if( (self=[super init]) ) {
  57. self.isTouchEnabled = YES;
  58. // menu in the center of the screen
  59. CGSize s = [[CCDirector sharedDirector] winSize];
  60. self.isRelativeAnchorPoint = NO;
  61. anchorPoint_ = ccp(0.5f, 0.5f);
  62. [self setContentSize:s];
  63. // XXX: in v0.7, winSize should return the visible size
  64. // XXX: so the bar calculation should be done there
  65. CGRect r = [[UIApplication sharedApplication] statusBarFrame];
  66. ccDeviceOrientation orientation = [[CCDirector sharedDirector] deviceOrientation];
  67. if( orientation == CCDeviceOrientationLandscapeLeft || orientation == CCDeviceOrientationLandscapeRight )
  68. s.height -= r.size.width;
  69. else
  70. s.height -= r.size.height;
  71. self.position = ccp(s.width/2, s.height/2);
  72. int z=0;
  73. if (item) {
  74. [self addChild: item z:z];
  75. CCMenuItem *i = va_arg(args, CCMenuItem*);
  76. while(i) {
  77. z++;
  78. [self addChild: i z:z];
  79. i = va_arg(args, CCMenuItem*);
  80. }
  81. }
  82. // [self alignItemsVertically];
  83. selectedItem = nil;
  84. state = kMenuStateWaiting;
  85. }
  86. return self;
  87. }
  88. -(void) dealloc
  89. {
  90. [super dealloc];
  91. }
  92. /*
  93. * override add:
  94. */
  95. -(id) addChild:(CCMenuItem*)child z:(int)z tag:(int) aTag
  96. {
  97. NSAssert( [child isKindOfClass:[CCMenuItem class]], @"Menu only supports MenuItem objects as children");
  98. return [super addChild:child z:z tag:aTag];
  99. }
  100. #pragma mark Menu - Events
  101. -(void) registerWithTouchDispatcher
  102. {
  103. [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:INT_MIN+1 swallowsTouches:YES];
  104. }
  105. -(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
  106. {
  107. if( state != kMenuStateWaiting || !visible_ )
  108. return NO;
  109. selectedItem = [self itemForTouch:touch];
  110. [selectedItem selected];
  111. if( selectedItem ) {
  112. state = kMenuStateTrackingTouch;
  113. return YES;
  114. }
  115. return NO;
  116. }
  117. -(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
  118. {
  119. NSAssert(state == kMenuStateTrackingTouch, @"[Menu ccTouchEnded] -- invalid state");
  120. [selectedItem unselected];
  121. [selectedItem activate];
  122. state = kMenuStateWaiting;
  123. }
  124. -(void) ccTouchCancelled:(UITouch *)touch withEvent:(UIEvent *)event
  125. {
  126. NSAssert(state == kMenuStateTrackingTouch, @"[Menu ccTouchCancelled] -- invalid state");
  127. [selectedItem unselected];
  128. state = kMenuStateWaiting;
  129. }
  130. -(void) ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
  131. {
  132. NSAssert(state == kMenuStateTrackingTouch, @"[Menu ccTouchMoved] -- invalid state");
  133. CCMenuItem *currentItem = [self itemForTouch:touch];
  134. if (currentItem != selectedItem) {
  135. [selectedItem unselected];
  136. selectedItem = currentItem;
  137. [selectedItem selected];
  138. }
  139. }
  140. #pragma mark Menu - Alignment
  141. -(void) alignItemsVertically
  142. {
  143. return [self alignItemsVerticallyWithPadding:kDefaultPadding];
  144. }
  145. -(void) alignItemsVerticallyWithPadding:(float)padding
  146. {
  147. float height = -padding;
  148. CCMenuItem *item;
  149. CCARRAY_FOREACH(children_, item)
  150. height += item.contentSize.height * item.scaleY + padding;
  151. float y = height / 2.0f;
  152. CCARRAY_FOREACH(children_, item) {
  153. CGSize itemSize = item.contentSize;
  154. [item setPosition:ccp(0, y - itemSize.height * item.scaleY / 2.0f)];
  155. y -= itemSize.height * item.scaleY + padding;
  156. }
  157. }
  158. -(void) alignItemsHorizontally
  159. {
  160. return [self alignItemsHorizontallyWithPadding:kDefaultPadding];
  161. }
  162. -(void) alignItemsHorizontallyWithPadding:(float)padding
  163. {
  164. float width = -padding;
  165. CCMenuItem *item;
  166. CCARRAY_FOREACH(children_, item)
  167. width += item.contentSize.width * item.scaleX + padding;
  168. float x = -width / 2.0f;
  169. CCARRAY_FOREACH(children_, item){
  170. CGSize itemSize = item.contentSize;
  171. [item setPosition:ccp(x + itemSize.width * item.scaleX / 2.0f, 0)];
  172. x += itemSize.width * item.scaleX + padding;
  173. }
  174. }
  175. -(void) alignItemsInColumns: (NSNumber *) columns, ...
  176. {
  177. va_list args;
  178. va_start(args, columns);
  179. [self alignItemsInColumns:columns vaList:args];
  180. va_end(args);
  181. }
  182. -(void) alignItemsInColumns: (NSNumber *) columns vaList: (va_list) args
  183. {
  184. NSMutableArray *rows = [[NSMutableArray alloc] initWithObjects:columns, nil];
  185. columns = va_arg(args, NSNumber*);
  186. while(columns) {
  187. [rows addObject:columns];
  188. columns = va_arg(args, NSNumber*);
  189. }
  190. int height = -5;
  191. NSUInteger row = 0, rowHeight = 0, columnsOccupied = 0, rowColumns;
  192. CCMenuItem *item;
  193. CCARRAY_FOREACH(children_, item){
  194. NSAssert( row < [rows count], @"Too many menu items for the amount of rows/columns.");
  195. rowColumns = [(NSNumber *) [rows objectAtIndex:row] unsignedIntegerValue];
  196. NSAssert( rowColumns, @"Can't have zero columns on a row");
  197. rowHeight = fmaxf(rowHeight, item.contentSize.height);
  198. ++columnsOccupied;
  199. if(columnsOccupied >= rowColumns) {
  200. height += rowHeight + 5;
  201. columnsOccupied = 0;
  202. rowHeight = 0;
  203. ++row;
  204. }
  205. }
  206. NSAssert( !columnsOccupied, @"Too many rows/columns for available menu items." );
  207. CGSize winSize = [[CCDirector sharedDirector] winSize];
  208. row = 0; rowHeight = 0; rowColumns = 0;
  209. float w, x, y = height / 2;
  210. CCARRAY_FOREACH(children_, item) {
  211. if(rowColumns == 0) {
  212. rowColumns = [(NSNumber *) [rows objectAtIndex:row] unsignedIntegerValue];
  213. w = winSize.width / (1 + rowColumns);
  214. x = w;
  215. }
  216. CGSize itemSize = item.contentSize;
  217. rowHeight = fmaxf(rowHeight, itemSize.height);
  218. [item setPosition:ccp(x - winSize.width / 2,
  219. y - itemSize.height / 2)];
  220. x += w + 10;
  221. ++columnsOccupied;
  222. if(columnsOccupied >= rowColumns) {
  223. y -= rowHeight + 5;
  224. columnsOccupied = 0;
  225. rowColumns = 0;
  226. rowHeight = 0;
  227. ++row;
  228. }
  229. }
  230. [rows release];
  231. }
  232. -(void) alignItemsInRows: (NSNumber *) rows, ...
  233. {
  234. va_list args;
  235. va_start(args, rows);
  236. [self alignItemsInRows:rows vaList:args];
  237. va_end(args);
  238. }
  239. -(void) alignItemsInRows: (NSNumber *) rows vaList: (va_list) args
  240. {
  241. NSMutableArray *columns = [[NSMutableArray alloc] initWithObjects:rows, nil];
  242. rows = va_arg(args, NSNumber*);
  243. while(rows) {
  244. [columns addObject:rows];
  245. rows = va_arg(args, NSNumber*);
  246. }
  247. NSMutableArray *columnWidths = [[NSMutableArray alloc] init];
  248. NSMutableArray *columnHeights = [[NSMutableArray alloc] init];
  249. int width = -10, columnHeight = -5;
  250. NSUInteger column = 0, columnWidth = 0, rowsOccupied = 0, columnRows;
  251. CCMenuItem *item;
  252. CCARRAY_FOREACH(children_, item){
  253. NSAssert( column < [columns count], @"Too many menu items for the amount of rows/columns.");
  254. columnRows = [(NSNumber *) [columns objectAtIndex:column] unsignedIntegerValue];
  255. NSAssert( columnRows, @"Can't have zero rows on a column");
  256. CGSize itemSize = item.contentSize;
  257. columnWidth = fmaxf(columnWidth, itemSize.width);
  258. columnHeight += itemSize.height + 5;
  259. ++rowsOccupied;
  260. if(rowsOccupied >= columnRows) {
  261. [columnWidths addObject:[NSNumber numberWithUnsignedInteger:columnWidth]];
  262. [columnHeights addObject:[NSNumber numberWithUnsignedInteger:columnHeight]];
  263. width += columnWidth + 10;
  264. rowsOccupied = 0;
  265. columnWidth = 0;
  266. columnHeight = -5;
  267. ++column;
  268. }
  269. }
  270. NSAssert( !rowsOccupied, @"Too many rows/columns for available menu items.");
  271. CGSize winSize = [[CCDirector sharedDirector] winSize];
  272. column = 0; columnWidth = 0; columnRows = 0;
  273. float x = -width / 2, y;
  274. CCARRAY_FOREACH(children_, item){
  275. if(columnRows == 0) {
  276. columnRows = [(NSNumber *) [columns objectAtIndex:column] unsignedIntegerValue];
  277. y = ([(NSNumber *) [columnHeights objectAtIndex:column] intValue] + winSize.height) / 2;
  278. }
  279. CGSize itemSize = item.contentSize;
  280. columnWidth = fmaxf(columnWidth, itemSize.width);
  281. [item setPosition:ccp(x + [(NSNumber *) [columnWidths objectAtIndex:column] unsignedIntegerValue] / 2,
  282. y - winSize.height / 2)];
  283. y -= itemSize.height + 10;
  284. ++rowsOccupied;
  285. if(rowsOccupied >= columnRows) {
  286. x += columnWidth + 5;
  287. rowsOccupied = 0;
  288. columnRows = 0;
  289. columnWidth = 0;
  290. ++column;
  291. }
  292. }
  293. [columns release];
  294. [columnWidths release];
  295. [columnHeights release];
  296. }
  297. #pragma mark Menu - Opacity Protocol
  298. /** Override synthesized setOpacity to recurse items */
  299. - (void) setOpacity:(GLubyte)newOpacity
  300. {
  301. opacity_ = newOpacity;
  302. id<CCRGBAProtocol> item;
  303. CCARRAY_FOREACH(children_, item)
  304. [item setOpacity:opacity_];
  305. }
  306. -(void) setColor:(ccColor3B)color
  307. {
  308. color_ = color;
  309. id<CCRGBAProtocol> item;
  310. CCARRAY_FOREACH(children_, item)
  311. [item setColor:color_];
  312. }
  313. #pragma mark Menu - Private
  314. -(CCMenuItem *) itemForTouch: (UITouch *) touch
  315. {
  316. CGPoint touchLocation = [touch locationInView: [touch view]];
  317. touchLocation = [[CCDirector sharedDirector] convertToGL: touchLocation];
  318. CCMenuItem* item;
  319. CCARRAY_FOREACH(children_, item){
  320. // ignore invisible and disabled items: issue #779, #866
  321. if ( [item visible] && [item isEnabled] ) {
  322. CGPoint local = [item convertToNodeSpace:touchLocation];
  323. CGRect r = [item rect];
  324. r.origin = CGPointZero;
  325. if( CGRectContainsPoint( r, local ) )
  326. return item;
  327. }
  328. }
  329. return nil;
  330. }
  331. @end