PageRenderTime 100ms CodeModel.GetById 16ms app.highlight 79ms RepoModel.GetById 1ms app.codeStats 0ms

/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
 26
 27
 28#import "CCMenu.h"
 29#import "CCDirector.h"
 30#import "CCTouchDispatcher.h"
 31#import "Support/CGPointExtension.h"
 32
 33enum {
 34	kDefaultPadding =  5,
 35};
 36
 37@interface CCMenu (Private)
 38// returns touched menu item, if any
 39-(CCMenuItem *) itemForTouch: (UITouch *) touch;
 40@end
 41
 42@implementation CCMenu
 43
 44@synthesize opacity=opacity_, color=color_;
 45
 46- (id) init
 47{
 48	NSException* myException = [NSException
 49								exceptionWithName:@"MenuInit"
 50								reason:@"Use initWithItems instead"
 51								userInfo:nil];
 52	@throw myException;
 53}
 54
 55+(id) menuWithItems: (CCMenuItem*) item, ...
 56{
 57	va_list args;
 58	va_start(args,item);
 59	
 60	id s = [[[self alloc] initWithItems: item vaList:args] autorelease];
 61	
 62	va_end(args);
 63	return s;
 64}
 65
 66-(id) initWithItems: (CCMenuItem*) item vaList: (va_list) args
 67{
 68	if( (self=[super init]) ) {
 69
 70		self.isTouchEnabled = YES;
 71		
 72		// menu in the center of the screen
 73		CGSize s = [[CCDirector sharedDirector] winSize];
 74		
 75		self.isRelativeAnchorPoint = NO;
 76		anchorPoint_ = ccp(0.5f, 0.5f);
 77		[self setContentSize:s];
 78		
 79		// XXX: in v0.7, winSize should return the visible size
 80		// XXX: so the bar calculation should be done there
 81		CGRect r = [[UIApplication sharedApplication] statusBarFrame];
 82		ccDeviceOrientation orientation = [[CCDirector sharedDirector] deviceOrientation];
 83		if( orientation == CCDeviceOrientationLandscapeLeft || orientation == CCDeviceOrientationLandscapeRight )
 84			s.height -= r.size.width;
 85		else
 86			s.height -= r.size.height;
 87		self.position = ccp(s.width/2, s.height/2);
 88
 89		int z=0;
 90		
 91		if (item) {
 92			[self addChild: item z:z];
 93			CCMenuItem *i = va_arg(args, CCMenuItem*);
 94			while(i) {
 95				z++;
 96				[self addChild: i z:z];
 97				i = va_arg(args, CCMenuItem*);
 98			}
 99		}
100	//	[self alignItemsVertically];
101		
102		selectedItem = nil;
103		state = kMenuStateWaiting;
104	}
105	
106	return self;
107}
108
109-(void) dealloc
110{
111	[super dealloc];
112}
113
114/*
115 * override add:
116 */
117-(id) addChild:(CCMenuItem*)child z:(int)z tag:(int) aTag
118{
119	NSAssert( [child isKindOfClass:[CCMenuItem class]], @"Menu only supports MenuItem objects as children");
120	return [super addChild:child z:z tag:aTag];
121}
122	
123#pragma mark Menu - Events
124
125-(void) registerWithTouchDispatcher
126{
127	[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:INT_MIN+1 swallowsTouches:YES];
128}
129
130-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
131{
132	if( state != kMenuStateWaiting || !visible_ )
133		return NO;
134	
135	selectedItem = [self itemForTouch:touch];
136	[selectedItem selected];
137	
138	if( selectedItem ) {
139		state = kMenuStateTrackingTouch;
140		return YES;
141	}
142	return NO;
143}
144
145-(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
146{
147	NSAssert(state == kMenuStateTrackingTouch, @"[Menu ccTouchEnded] -- invalid state");
148	
149	[selectedItem unselected];
150	[selectedItem activate];
151	
152	state = kMenuStateWaiting;
153}
154
155-(void) ccTouchCancelled:(UITouch *)touch withEvent:(UIEvent *)event
156{
157	NSAssert(state == kMenuStateTrackingTouch, @"[Menu ccTouchCancelled] -- invalid state");
158	
159	[selectedItem unselected];
160	
161	state = kMenuStateWaiting;
162}
163
164-(void) ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
165{
166	NSAssert(state == kMenuStateTrackingTouch, @"[Menu ccTouchMoved] -- invalid state");
167	
168	CCMenuItem *currentItem = [self itemForTouch:touch];
169	
170	if (currentItem != selectedItem) {
171		[selectedItem unselected];
172		selectedItem = currentItem;
173		[selectedItem selected];
174	}
175}
176
177#pragma mark Menu - Alignment
178-(void) alignItemsVertically
179{
180	return [self alignItemsVerticallyWithPadding:kDefaultPadding];
181}
182-(void) alignItemsVerticallyWithPadding:(float)padding
183{
184	float height = -padding;
185	
186	CCMenuItem *item;
187	CCARRAY_FOREACH(children_, item)
188	    height += item.contentSize.height * item.scaleY + padding;
189
190	float y = height / 2.0f;
191	
192	CCARRAY_FOREACH(children_, item) {
193		CGSize itemSize = item.contentSize;
194	    [item setPosition:ccp(0, y - itemSize.height * item.scaleY / 2.0f)];
195	    y -= itemSize.height * item.scaleY + padding;
196	}
197}
198
199-(void) alignItemsHorizontally
200{
201	return [self alignItemsHorizontallyWithPadding:kDefaultPadding];
202}
203
204-(void) alignItemsHorizontallyWithPadding:(float)padding
205{
206	
207	float width = -padding;
208	CCMenuItem *item;
209	CCARRAY_FOREACH(children_, item)
210	    width += item.contentSize.width * item.scaleX + padding;
211
212	float x = -width / 2.0f;
213	
214	CCARRAY_FOREACH(children_, item){
215		CGSize itemSize = item.contentSize;
216		[item setPosition:ccp(x + itemSize.width * item.scaleX / 2.0f, 0)];
217		x += itemSize.width * item.scaleX + padding;
218	}
219}
220
221-(void) alignItemsInColumns: (NSNumber *) columns, ...
222{
223	va_list args;
224	va_start(args, columns);
225	
226	[self alignItemsInColumns:columns vaList:args];
227	
228	va_end(args);
229}
230
231-(void) alignItemsInColumns: (NSNumber *) columns vaList: (va_list) args
232{
233	NSMutableArray *rows = [[NSMutableArray alloc] initWithObjects:columns, nil];
234	columns = va_arg(args, NSNumber*);
235	while(columns) {
236        [rows addObject:columns];
237		columns = va_arg(args, NSNumber*);
238	}
239    
240	int height = -5;
241    NSUInteger row = 0, rowHeight = 0, columnsOccupied = 0, rowColumns;
242	CCMenuItem *item;
243	CCARRAY_FOREACH(children_, item){
244		NSAssert( row < [rows count], @"Too many menu items for the amount of rows/columns.");
245        
246		rowColumns = [(NSNumber *) [rows objectAtIndex:row] unsignedIntegerValue];
247		NSAssert( rowColumns, @"Can't have zero columns on a row");
248        
249		rowHeight = fmaxf(rowHeight, item.contentSize.height);
250		++columnsOccupied;
251        
252		if(columnsOccupied >= rowColumns) {
253			height += rowHeight + 5;
254
255			columnsOccupied = 0;
256			rowHeight = 0;
257			++row;
258		}
259	}
260	NSAssert( !columnsOccupied, @"Too many rows/columns for available menu items." );
261
262	CGSize winSize = [[CCDirector sharedDirector] winSize];
263    
264	row = 0; rowHeight = 0; rowColumns = 0;
265	float w, x, y = height / 2;
266	CCARRAY_FOREACH(children_, item) {
267		if(rowColumns == 0) {
268			rowColumns = [(NSNumber *) [rows objectAtIndex:row] unsignedIntegerValue];
269			w = winSize.width / (1 + rowColumns);
270			x = w;
271		}
272
273		CGSize itemSize = item.contentSize;
274		rowHeight = fmaxf(rowHeight, itemSize.height);
275		[item setPosition:ccp(x - winSize.width / 2,
276							  y - itemSize.height / 2)];
277            
278		x += w + 10;
279		++columnsOccupied;
280		
281		if(columnsOccupied >= rowColumns) {
282			y -= rowHeight + 5;
283			
284			columnsOccupied = 0;
285			rowColumns = 0;
286			rowHeight = 0;
287			++row;
288		}
289	}
290
291	[rows release];
292}
293
294-(void) alignItemsInRows: (NSNumber *) rows, ...
295{
296	va_list args;
297	va_start(args, rows);
298	
299	[self alignItemsInRows:rows vaList:args];
300	
301	va_end(args);
302}
303
304-(void) alignItemsInRows: (NSNumber *) rows vaList: (va_list) args
305{
306	NSMutableArray *columns = [[NSMutableArray alloc] initWithObjects:rows, nil];
307	rows = va_arg(args, NSNumber*);
308	while(rows) {
309		[columns addObject:rows];
310		rows = va_arg(args, NSNumber*);
311	}
312
313	NSMutableArray *columnWidths = [[NSMutableArray alloc] init];
314	NSMutableArray *columnHeights = [[NSMutableArray alloc] init];
315	
316	int width = -10, columnHeight = -5;
317	NSUInteger column = 0, columnWidth = 0, rowsOccupied = 0, columnRows;
318	CCMenuItem *item;
319	CCARRAY_FOREACH(children_, item){
320		NSAssert( column < [columns count], @"Too many menu items for the amount of rows/columns.");
321		
322		columnRows = [(NSNumber *) [columns objectAtIndex:column] unsignedIntegerValue];
323		NSAssert( columnRows, @"Can't have zero rows on a column");
324		
325		CGSize itemSize = item.contentSize;
326		columnWidth = fmaxf(columnWidth, itemSize.width);
327		columnHeight += itemSize.height + 5;
328		++rowsOccupied;
329		
330		if(rowsOccupied >= columnRows) {
331			[columnWidths addObject:[NSNumber numberWithUnsignedInteger:columnWidth]];
332			[columnHeights addObject:[NSNumber numberWithUnsignedInteger:columnHeight]];
333			width += columnWidth + 10;
334			
335			rowsOccupied = 0;
336			columnWidth = 0;
337			columnHeight = -5;
338			++column;
339		}
340	}
341	NSAssert( !rowsOccupied, @"Too many rows/columns for available menu items.");
342	
343	CGSize winSize = [[CCDirector sharedDirector] winSize];
344	
345	column = 0; columnWidth = 0; columnRows = 0;
346	float x = -width / 2, y;
347	
348	CCARRAY_FOREACH(children_, item){
349		if(columnRows == 0) {
350			columnRows = [(NSNumber *) [columns objectAtIndex:column] unsignedIntegerValue];
351			y = ([(NSNumber *) [columnHeights objectAtIndex:column] intValue] + winSize.height) / 2;
352		}
353		
354		CGSize itemSize = item.contentSize;
355		columnWidth = fmaxf(columnWidth, itemSize.width);
356		[item setPosition:ccp(x + [(NSNumber *) [columnWidths objectAtIndex:column] unsignedIntegerValue] / 2,
357							  y - winSize.height / 2)];
358		
359		y -= itemSize.height + 10;
360		++rowsOccupied;
361		
362		if(rowsOccupied >= columnRows) {
363			x += columnWidth + 5;
364			
365			rowsOccupied = 0;
366			columnRows = 0;
367			columnWidth = 0;
368			++column;
369		}
370	}
371	
372	[columns release];
373	[columnWidths release];
374	[columnHeights release];
375}
376
377#pragma mark Menu - Opacity Protocol
378
379/** Override synthesized setOpacity to recurse items */
380- (void) setOpacity:(GLubyte)newOpacity
381{
382	opacity_ = newOpacity;
383	id<CCRGBAProtocol> item;
384	CCARRAY_FOREACH(children_, item)
385		[item setOpacity:opacity_];
386}
387
388-(void) setColor:(ccColor3B)color
389{
390	color_ = color;
391	id<CCRGBAProtocol> item;
392	CCARRAY_FOREACH(children_, item)
393		[item setColor:color_];
394}
395
396#pragma mark Menu - Private
397
398-(CCMenuItem *) itemForTouch: (UITouch *) touch
399{
400	CGPoint touchLocation = [touch locationInView: [touch view]];
401	touchLocation = [[CCDirector sharedDirector] convertToGL: touchLocation];
402	
403	CCMenuItem* item;
404	CCARRAY_FOREACH(children_, item){
405		// ignore invisible and disabled items: issue #779, #866
406		if ( [item visible] && [item isEnabled] ) {
407
408			CGPoint local = [item convertToNodeSpace:touchLocation];
409
410			CGRect r = [item rect];
411			r.origin = CGPointZero;
412			
413			if( CGRectContainsPoint( r, local ) )
414				return item;
415		}
416	}
417	return nil;
418}
419@end