diff options
Diffstat (limited to 'libs/cocos2d/CCMenu.m')
-rwxr-xr-x | libs/cocos2d/CCMenu.m | 523 |
1 files changed, 523 insertions, 0 deletions
diff --git a/libs/cocos2d/CCMenu.m b/libs/cocos2d/CCMenu.m new file mode 100755 index 0000000..c3cb8f2 --- /dev/null +++ b/libs/cocos2d/CCMenu.m | |||
@@ -0,0 +1,523 @@ | |||
1 | /* | ||
2 | * cocos2d for iPhone: http://www.cocos2d-iphone.org | ||
3 | * | ||
4 | * Copyright (c) 2008-2010 Ricardo Quesada | ||
5 | * Copyright (c) 2011 Zynga Inc. | ||
6 | * | ||
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
8 | * of this software and associated documentation files (the "Software"), to deal | ||
9 | * in the Software without restriction, including without limitation the rights | ||
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
11 | * copies of the Software, and to permit persons to whom the Software is | ||
12 | * furnished to do so, subject to the following conditions: | ||
13 | * | ||
14 | * The above copyright notice and this permission notice shall be included in | ||
15 | * all copies or substantial portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
23 | * THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | |||
28 | |||
29 | #import "CCMenu.h" | ||
30 | #import "CCDirector.h" | ||
31 | #import "Support/CGPointExtension.h" | ||
32 | #import "ccMacros.h" | ||
33 | |||
34 | #import <Availability.h> | ||
35 | #ifdef __IPHONE_OS_VERSION_MAX_ALLOWED | ||
36 | #import "Platforms/iOS/CCDirectorIOS.h" | ||
37 | #import "Platforms/iOS/CCTouchDispatcher.h" | ||
38 | #elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) | ||
39 | #import "Platforms/Mac/MacGLView.h" | ||
40 | #import "Platforms/Mac/CCDirectorMac.h" | ||
41 | #endif | ||
42 | |||
43 | enum { | ||
44 | kDefaultPadding = 5, | ||
45 | }; | ||
46 | |||
47 | @implementation CCMenu | ||
48 | |||
49 | @synthesize opacity = opacity_, color = color_; | ||
50 | |||
51 | - (id) init | ||
52 | { | ||
53 | NSAssert(NO, @"CCMenu: Init not supported."); | ||
54 | [self release]; | ||
55 | return nil; | ||
56 | } | ||
57 | |||
58 | +(id) menuWithItems: (CCMenuItem*) item, ... | ||
59 | { | ||
60 | va_list args; | ||
61 | va_start(args,item); | ||
62 | |||
63 | id s = [[[self alloc] initWithItems: item vaList:args] autorelease]; | ||
64 | |||
65 | va_end(args); | ||
66 | return s; | ||
67 | } | ||
68 | |||
69 | -(id) initWithItems: (CCMenuItem*) item vaList: (va_list) args | ||
70 | { | ||
71 | if( (self=[super init]) ) { | ||
72 | |||
73 | #ifdef __IPHONE_OS_VERSION_MAX_ALLOWED | ||
74 | self.isTouchEnabled = YES; | ||
75 | #elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) | ||
76 | self.isMouseEnabled = YES; | ||
77 | #endif | ||
78 | |||
79 | // menu in the center of the screen | ||
80 | CGSize s = [[CCDirector sharedDirector] winSize]; | ||
81 | |||
82 | self.isRelativeAnchorPoint = NO; | ||
83 | anchorPoint_ = ccp(0.5f, 0.5f); | ||
84 | [self setContentSize:s]; | ||
85 | |||
86 | // XXX: in v0.7, winSize should return the visible size | ||
87 | // XXX: so the bar calculation should be done there | ||
88 | #ifdef __IPHONE_OS_VERSION_MAX_ALLOWED | ||
89 | CGRect r = [[UIApplication sharedApplication] statusBarFrame]; | ||
90 | ccDeviceOrientation orientation = [[CCDirector sharedDirector] deviceOrientation]; | ||
91 | if( orientation == CCDeviceOrientationLandscapeLeft || orientation == CCDeviceOrientationLandscapeRight ) | ||
92 | s.height -= r.size.width; | ||
93 | else | ||
94 | s.height -= r.size.height; | ||
95 | #endif | ||
96 | self.position = ccp(s.width/2, s.height/2); | ||
97 | |||
98 | int z=0; | ||
99 | |||
100 | if (item) { | ||
101 | [self addChild: item z:z]; | ||
102 | CCMenuItem *i = va_arg(args, CCMenuItem*); | ||
103 | while(i) { | ||
104 | z++; | ||
105 | [self addChild: i z:z]; | ||
106 | i = va_arg(args, CCMenuItem*); | ||
107 | } | ||
108 | } | ||
109 | // [self alignItemsVertically]; | ||
110 | |||
111 | selectedItem_ = nil; | ||
112 | state_ = kCCMenuStateWaiting; | ||
113 | } | ||
114 | |||
115 | return self; | ||
116 | } | ||
117 | |||
118 | -(void) dealloc | ||
119 | { | ||
120 | [super dealloc]; | ||
121 | } | ||
122 | |||
123 | /* | ||
124 | * override add: | ||
125 | */ | ||
126 | -(void) addChild:(CCMenuItem*)child z:(NSInteger)z tag:(NSInteger) aTag | ||
127 | { | ||
128 | NSAssert( [child isKindOfClass:[CCMenuItem class]], @"Menu only supports MenuItem objects as children"); | ||
129 | [super addChild:child z:z tag:aTag]; | ||
130 | } | ||
131 | |||
132 | - (void) onExit | ||
133 | { | ||
134 | if(state_ == kCCMenuStateTrackingTouch) | ||
135 | { | ||
136 | [selectedItem_ unselected]; | ||
137 | state_ = kCCMenuStateWaiting; | ||
138 | selectedItem_ = nil; | ||
139 | } | ||
140 | [super onExit]; | ||
141 | } | ||
142 | |||
143 | #pragma mark Menu - Touches | ||
144 | |||
145 | #ifdef __IPHONE_OS_VERSION_MAX_ALLOWED | ||
146 | -(void) registerWithTouchDispatcher | ||
147 | { | ||
148 | [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:kCCMenuTouchPriority swallowsTouches:YES]; | ||
149 | } | ||
150 | |||
151 | -(CCMenuItem *) itemForTouch: (UITouch *) touch | ||
152 | { | ||
153 | CGPoint touchLocation = [touch locationInView: [touch view]]; | ||
154 | touchLocation = [[CCDirector sharedDirector] convertToGL: touchLocation]; | ||
155 | |||
156 | CCMenuItem* item; | ||
157 | CCARRAY_FOREACH(children_, item){ | ||
158 | // ignore invisible and disabled items: issue #779, #866 | ||
159 | if ( [item visible] && [item isEnabled] ) { | ||
160 | |||
161 | CGPoint local = [item convertToNodeSpace:touchLocation]; | ||
162 | CGRect r = [item rect]; | ||
163 | r.origin = CGPointZero; | ||
164 | |||
165 | if( CGRectContainsPoint( r, local ) ) | ||
166 | return item; | ||
167 | } | ||
168 | } | ||
169 | return nil; | ||
170 | } | ||
171 | |||
172 | -(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event | ||
173 | { | ||
174 | if( state_ != kCCMenuStateWaiting || !visible_ ) | ||
175 | return NO; | ||
176 | |||
177 | selectedItem_ = [self itemForTouch:touch]; | ||
178 | [selectedItem_ selected]; | ||
179 | |||
180 | if( selectedItem_ ) { | ||
181 | state_ = kCCMenuStateTrackingTouch; | ||
182 | return YES; | ||
183 | } | ||
184 | return NO; | ||
185 | } | ||
186 | |||
187 | -(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event | ||
188 | { | ||
189 | NSAssert(state_ == kCCMenuStateTrackingTouch, @"[Menu ccTouchEnded] -- invalid state"); | ||
190 | |||
191 | [selectedItem_ unselected]; | ||
192 | [selectedItem_ activate]; | ||
193 | |||
194 | state_ = kCCMenuStateWaiting; | ||
195 | } | ||
196 | |||
197 | -(void) ccTouchCancelled:(UITouch *)touch withEvent:(UIEvent *)event | ||
198 | { | ||
199 | NSAssert(state_ == kCCMenuStateTrackingTouch, @"[Menu ccTouchCancelled] -- invalid state"); | ||
200 | |||
201 | [selectedItem_ unselected]; | ||
202 | |||
203 | state_ = kCCMenuStateWaiting; | ||
204 | } | ||
205 | |||
206 | -(void) ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event | ||
207 | { | ||
208 | NSAssert(state_ == kCCMenuStateTrackingTouch, @"[Menu ccTouchMoved] -- invalid state"); | ||
209 | |||
210 | CCMenuItem *currentItem = [self itemForTouch:touch]; | ||
211 | |||
212 | if (currentItem != selectedItem_) { | ||
213 | [selectedItem_ unselected]; | ||
214 | selectedItem_ = currentItem; | ||
215 | [selectedItem_ selected]; | ||
216 | } | ||
217 | } | ||
218 | |||
219 | #pragma mark Menu - Mouse | ||
220 | |||
221 | #elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) | ||
222 | |||
223 | -(NSInteger) mouseDelegatePriority | ||
224 | { | ||
225 | return kCCMenuMousePriority+1; | ||
226 | } | ||
227 | |||
228 | -(CCMenuItem *) itemForMouseEvent: (NSEvent *) event | ||
229 | { | ||
230 | CGPoint location = [(CCDirectorMac*)[CCDirector sharedDirector] convertEventToGL:event]; | ||
231 | |||
232 | CCMenuItem* item; | ||
233 | CCARRAY_FOREACH(children_, item){ | ||
234 | // ignore invisible and disabled items: issue #779, #866 | ||
235 | if ( [item visible] && [item isEnabled] ) { | ||
236 | |||
237 | CGPoint local = [item convertToNodeSpace:location]; | ||
238 | |||
239 | CGRect r = [item rect]; | ||
240 | r.origin = CGPointZero; | ||
241 | |||
242 | if( CGRectContainsPoint( r, local ) ) | ||
243 | return item; | ||
244 | } | ||
245 | } | ||
246 | return nil; | ||
247 | } | ||
248 | |||
249 | -(BOOL) ccMouseUp:(NSEvent *)event | ||
250 | { | ||
251 | if( ! visible_ ) | ||
252 | return NO; | ||
253 | |||
254 | if(state_ == kCCMenuStateTrackingTouch) { | ||
255 | if( selectedItem_ ) { | ||
256 | [selectedItem_ unselected]; | ||
257 | [selectedItem_ activate]; | ||
258 | } | ||
259 | state_ = kCCMenuStateWaiting; | ||
260 | |||
261 | return YES; | ||
262 | } | ||
263 | return NO; | ||
264 | } | ||
265 | |||
266 | -(BOOL) ccMouseDown:(NSEvent *)event | ||
267 | { | ||
268 | if( ! visible_ ) | ||
269 | return NO; | ||
270 | |||
271 | selectedItem_ = [self itemForMouseEvent:event]; | ||
272 | [selectedItem_ selected]; | ||
273 | |||
274 | if( selectedItem_ ) { | ||
275 | state_ = kCCMenuStateTrackingTouch; | ||
276 | return YES; | ||
277 | } | ||
278 | |||
279 | return NO; | ||
280 | } | ||
281 | |||
282 | -(BOOL) ccMouseDragged:(NSEvent *)event | ||
283 | { | ||
284 | if( ! visible_ ) | ||
285 | return NO; | ||
286 | |||
287 | if(state_ == kCCMenuStateTrackingTouch) { | ||
288 | CCMenuItem *currentItem = [self itemForMouseEvent:event]; | ||
289 | |||
290 | if (currentItem != selectedItem_) { | ||
291 | [selectedItem_ unselected]; | ||
292 | selectedItem_ = currentItem; | ||
293 | [selectedItem_ selected]; | ||
294 | } | ||
295 | |||
296 | return YES; | ||
297 | } | ||
298 | return NO; | ||
299 | } | ||
300 | |||
301 | #endif // Mac Mouse support | ||
302 | |||
303 | #pragma mark Menu - Alignment | ||
304 | -(void) alignItemsVertically | ||
305 | { | ||
306 | [self alignItemsVerticallyWithPadding:kDefaultPadding]; | ||
307 | } | ||
308 | -(void) alignItemsVerticallyWithPadding:(float)padding | ||
309 | { | ||
310 | float height = -padding; | ||
311 | |||
312 | CCMenuItem *item; | ||
313 | CCARRAY_FOREACH(children_, item) | ||
314 | height += item.contentSize.height * item.scaleY + padding; | ||
315 | |||
316 | float y = height / 2.0f; | ||
317 | |||
318 | CCARRAY_FOREACH(children_, item) { | ||
319 | CGSize itemSize = item.contentSize; | ||
320 | [item setPosition:ccp(0, y - itemSize.height * item.scaleY / 2.0f)]; | ||
321 | y -= itemSize.height * item.scaleY + padding; | ||
322 | } | ||
323 | } | ||
324 | |||
325 | -(void) alignItemsHorizontally | ||
326 | { | ||
327 | [self alignItemsHorizontallyWithPadding:kDefaultPadding]; | ||
328 | } | ||
329 | |||
330 | -(void) alignItemsHorizontallyWithPadding:(float)padding | ||
331 | { | ||
332 | |||
333 | float width = -padding; | ||
334 | CCMenuItem *item; | ||
335 | CCARRAY_FOREACH(children_, item) | ||
336 | width += item.contentSize.width * item.scaleX + padding; | ||
337 | |||
338 | float x = -width / 2.0f; | ||
339 | |||
340 | CCARRAY_FOREACH(children_, item){ | ||
341 | CGSize itemSize = item.contentSize; | ||
342 | [item setPosition:ccp(x + itemSize.width * item.scaleX / 2.0f, 0)]; | ||
343 | x += itemSize.width * item.scaleX + padding; | ||
344 | } | ||
345 | } | ||
346 | |||
347 | -(void) alignItemsInColumns: (NSNumber *) columns, ... | ||
348 | { | ||
349 | va_list args; | ||
350 | va_start(args, columns); | ||
351 | |||
352 | [self alignItemsInColumns:columns vaList:args]; | ||
353 | |||
354 | va_end(args); | ||
355 | } | ||
356 | |||
357 | -(void) alignItemsInColumns: (NSNumber *) columns vaList: (va_list) args | ||
358 | { | ||
359 | NSMutableArray *rows = [[NSMutableArray alloc] initWithObjects:columns, nil]; | ||
360 | columns = va_arg(args, NSNumber*); | ||
361 | while(columns) { | ||
362 | [rows addObject:columns]; | ||
363 | columns = va_arg(args, NSNumber*); | ||
364 | } | ||
365 | |||
366 | int height = -5; | ||
367 | NSUInteger row = 0, rowHeight = 0, columnsOccupied = 0, rowColumns; | ||
368 | CCMenuItem *item; | ||
369 | CCARRAY_FOREACH(children_, item){ | ||
370 | NSAssert( row < [rows count], @"Too many menu items for the amount of rows/columns."); | ||
371 | |||
372 | rowColumns = [(NSNumber *) [rows objectAtIndex:row] unsignedIntegerValue]; | ||
373 | NSAssert( rowColumns, @"Can't have zero columns on a row"); | ||
374 | |||
375 | rowHeight = fmaxf(rowHeight, item.contentSize.height); | ||
376 | ++columnsOccupied; | ||
377 | |||
378 | if(columnsOccupied >= rowColumns) { | ||
379 | height += rowHeight + 5; | ||
380 | |||
381 | columnsOccupied = 0; | ||
382 | rowHeight = 0; | ||
383 | ++row; | ||
384 | } | ||
385 | } | ||
386 | NSAssert( !columnsOccupied, @"Too many rows/columns for available menu items." ); | ||
387 | |||
388 | CGSize winSize = [[CCDirector sharedDirector] winSize]; | ||
389 | |||
390 | row = 0; rowHeight = 0; rowColumns = 0; | ||
391 | float w, x, y = height / 2; | ||
392 | CCARRAY_FOREACH(children_, item) { | ||
393 | if(rowColumns == 0) { | ||
394 | rowColumns = [(NSNumber *) [rows objectAtIndex:row] unsignedIntegerValue]; | ||
395 | w = winSize.width / (1 + rowColumns); | ||
396 | x = w; | ||
397 | } | ||
398 | |||
399 | CGSize itemSize = item.contentSize; | ||
400 | rowHeight = fmaxf(rowHeight, itemSize.height); | ||
401 | [item setPosition:ccp(x - winSize.width / 2, | ||
402 | y - itemSize.height / 2)]; | ||
403 | |||
404 | x += w; | ||
405 | ++columnsOccupied; | ||
406 | |||
407 | if(columnsOccupied >= rowColumns) { | ||
408 | y -= rowHeight + 5; | ||
409 | |||
410 | columnsOccupied = 0; | ||
411 | rowColumns = 0; | ||
412 | rowHeight = 0; | ||
413 | ++row; | ||
414 | } | ||
415 | } | ||
416 | |||
417 | [rows release]; | ||
418 | } | ||
419 | |||
420 | -(void) alignItemsInRows: (NSNumber *) rows, ... | ||
421 | { | ||
422 | va_list args; | ||
423 | va_start(args, rows); | ||
424 | |||
425 | [self alignItemsInRows:rows vaList:args]; | ||
426 | |||
427 | va_end(args); | ||
428 | } | ||
429 | |||
430 | -(void) alignItemsInRows: (NSNumber *) rows vaList: (va_list) args | ||
431 | { | ||
432 | NSMutableArray *columns = [[NSMutableArray alloc] initWithObjects:rows, nil]; | ||
433 | rows = va_arg(args, NSNumber*); | ||
434 | while(rows) { | ||
435 | [columns addObject:rows]; | ||
436 | rows = va_arg(args, NSNumber*); | ||
437 | } | ||
438 | |||
439 | NSMutableArray *columnWidths = [[NSMutableArray alloc] init]; | ||
440 | NSMutableArray *columnHeights = [[NSMutableArray alloc] init]; | ||
441 | |||
442 | int width = -10, columnHeight = -5; | ||
443 | NSUInteger column = 0, columnWidth = 0, rowsOccupied = 0, columnRows; | ||
444 | CCMenuItem *item; | ||
445 | CCARRAY_FOREACH(children_, item){ | ||
446 | NSAssert( column < [columns count], @"Too many menu items for the amount of rows/columns."); | ||
447 | |||
448 | columnRows = [(NSNumber *) [columns objectAtIndex:column] unsignedIntegerValue]; | ||
449 | NSAssert( columnRows, @"Can't have zero rows on a column"); | ||
450 | |||
451 | CGSize itemSize = item.contentSize; | ||
452 | columnWidth = fmaxf(columnWidth, itemSize.width); | ||
453 | columnHeight += itemSize.height + 5; | ||
454 | ++rowsOccupied; | ||
455 | |||
456 | if(rowsOccupied >= columnRows) { | ||
457 | [columnWidths addObject:[NSNumber numberWithUnsignedInteger:columnWidth]]; | ||
458 | [columnHeights addObject:[NSNumber numberWithUnsignedInteger:columnHeight]]; | ||
459 | width += columnWidth + 10; | ||
460 | |||
461 | rowsOccupied = 0; | ||
462 | columnWidth = 0; | ||
463 | columnHeight = -5; | ||
464 | ++column; | ||
465 | } | ||
466 | } | ||
467 | NSAssert( !rowsOccupied, @"Too many rows/columns for available menu items."); | ||
468 | |||
469 | CGSize winSize = [[CCDirector sharedDirector] winSize]; | ||
470 | |||
471 | column = 0; columnWidth = 0; columnRows = 0; | ||
472 | float x = -width / 2, y; | ||
473 | |||
474 | CCARRAY_FOREACH(children_, item){ | ||
475 | if(columnRows == 0) { | ||
476 | columnRows = [(NSNumber *) [columns objectAtIndex:column] unsignedIntegerValue]; | ||
477 | y = ([(NSNumber *) [columnHeights objectAtIndex:column] intValue] + winSize.height) / 2; | ||
478 | } | ||
479 | |||
480 | CGSize itemSize = item.contentSize; | ||
481 | columnWidth = fmaxf(columnWidth, itemSize.width); | ||
482 | [item setPosition:ccp(x + [(NSNumber *) [columnWidths objectAtIndex:column] unsignedIntegerValue] / 2, | ||
483 | y - winSize.height / 2)]; | ||
484 | |||
485 | y -= itemSize.height + 10; | ||
486 | ++rowsOccupied; | ||
487 | |||
488 | if(rowsOccupied >= columnRows) { | ||
489 | x += columnWidth + 5; | ||
490 | |||
491 | rowsOccupied = 0; | ||
492 | columnRows = 0; | ||
493 | columnWidth = 0; | ||
494 | ++column; | ||
495 | } | ||
496 | } | ||
497 | |||
498 | [columns release]; | ||
499 | [columnWidths release]; | ||
500 | [columnHeights release]; | ||
501 | } | ||
502 | |||
503 | #pragma mark Menu - Opacity Protocol | ||
504 | |||
505 | /** Override synthesized setOpacity to recurse items */ | ||
506 | - (void) setOpacity:(GLubyte)newOpacity | ||
507 | { | ||
508 | opacity_ = newOpacity; | ||
509 | |||
510 | id<CCRGBAProtocol> item; | ||
511 | CCARRAY_FOREACH(children_, item) | ||
512 | [item setOpacity:opacity_]; | ||
513 | } | ||
514 | |||
515 | -(void) setColor:(ccColor3B)color | ||
516 | { | ||
517 | color_ = color; | ||
518 | |||
519 | id<CCRGBAProtocol> item; | ||
520 | CCARRAY_FOREACH(children_, item) | ||
521 | [item setColor:color_]; | ||
522 | } | ||
523 | @end | ||