DNESS / cocos2d-iphone

Automatically exported from code.google.com/p/cocos2d-iphone
1 stars 0 forks source link

CCMenu in locked state after losing a touch #895

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
What steps will reproduce the problem?
1. Create a scene with a menu that has some menu items
2. Add a UIScrollView or probably any other UIView to the main window, 
partially covering the screen
3. Touch one of the menu items and then drag your finger to the UIView and let 
up your finger
4. Try to interact with the menu again

What is the expected output? What do you see instead?
The menu becomes locked because it never received a CCTouchesEnded or 
CCTouchesCanceled event. The menu should remain responsive to touches.

What cocos2d version / SVN revision are you using ?
cocos2d v0.99.0

What iPhoneSDK are you using ?
xCode 3.2, iPod w/ 3.1.3

Debug or Release ?
Release

Does this happens on device ? or on the simulator ? or on both ?
Both

Please provide any additional information below.

Here is my fix for CCMenu:

/* cocos2d for iPhone
 *
 * http://www.cocos2d-iphone.org
 *
 * Copyright (C) 2008,2009 Ricardo Quesada
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the 'cocos2d for iPhone' license.
 *
 * You will find a copy of this license within the cocos2d for iPhone
 * distribution inside the "LICENSE" file.
 *
 */

#import <UIKit/UIKit.h>

#import "CCMenuItem.h"
#import "CCLayer.h"

typedef enum  {
    kMenuStateWaiting,
    kMenuStateTrackingTouch
} MenuState;

/** A CCMenu
 * 
 * Features and Limitation:
 *  - You can add MenuItem objects in runtime using addChild:
 *  - But the only accecpted children are MenuItem objects
 */
@interface CCMenu : CCLayer <CCRGBAProtocol>
{
    MenuState state;
    CCMenuItem *selectedItem;
    GLubyte     opacity_;
    ccColor3B   color_;
}

/** creates a CCMenu with it's items */
+ (id) menuWithItems: (CCMenuItem*) item, ... NS_REQUIRES_NIL_TERMINATION;

/** initializes a CCMenu with it's items */
- (id) initWithItems: (CCMenuItem*) item vaList: (va_list) args;

/** align items vertically */
-(void) alignItemsVertically;
/** align items vertically with padding
 @since v0.7.2
 */
-(void) alignItemsVerticallyWithPadding:(float) padding;

/** align items horizontally */
-(void) alignItemsHorizontally;
/** align items horizontally with padding
 @since v0.7.2
 */
-(void) alignItemsHorizontallyWithPadding: (float) padding;

/** align items in rows of columns */
-(void) alignItemsInColumns: (NSNumber *) columns, ... 
NS_REQUIRES_NIL_TERMINATION;
-(void) alignItemsInColumns: (NSNumber *) columns vaList: (va_list) args;

/** align items in columns of rows */
-(void) alignItemsInRows: (NSNumber *) rows, ... NS_REQUIRES_NIL_TERMINATION;
-(void) alignItemsInRows: (NSNumber *) rows vaList: (va_list) args;

/** Unselect all child nodes **/
-(void) unselectAllItems;

/** conforms to CCRGBAProtocol protocol */
@property (nonatomic,readonly) GLubyte opacity;
/** conforms to CCRGBAProtocol protocol */
@property (nonatomic,readonly) ccColor3B color;

@end

/* cocos2d for iPhone
 *
 * http://www.cocos2d-iphone.org
 *
 * Copyright (C) 2008,2009 Ricardo Quesada
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the 'cocos2d for iPhone' license.
 *
 * You will find a copy of this license within the cocos2d for iPhone
 * distribution inside the "LICENSE" file.
 *
 */

#import "CCMenu.h"
#import "CCDirector.h"
#import "CCTouchDispatcher.h"
#import "Support/CGPointExtension.h"

enum {
    kDefaultPadding =  5,
};

@interface CCMenu (Private)
// returns touched menu item, if any
-(CCMenuItem *) itemForTouch: (UITouch *) touch;
@end

@implementation CCMenu

@synthesize opacity=opacity_, color=color_;

- (id) init
{
    NSException* myException = [NSException
                                exceptionWithName:@"MenuInit"
                                reason:@"Use initWithItems instead"
                                userInfo:nil];
    @throw myException;
}

+(id) menuWithItems: (CCMenuItem*) item, ...
{
    va_list args;
    va_start(args,item);

    id s = [[[self alloc] initWithItems: item vaList:args] autorelease];

    va_end(args);
    return s;
}

-(id) initWithItems: (CCMenuItem*) item vaList: (va_list) args
{
    if( (self=[super init]) ) {

        self.isTouchEnabled = YES;

        // menu in the center of the screen
        CGSize s = [[CCDirector sharedDirector] winSize];

        self.isRelativeAnchorPoint = NO;
        anchorPoint_ = ccp(0.5f, 0.5f);
        [self setContentSize:s];

        // XXX: in v0.7, winSize should return the visible size
        // XXX: so the bar calculation should be done there
        CGRect r = [[UIApplication sharedApplication] statusBarFrame];
        ccDeviceOrientation orientation = [[CCDirector sharedDirector] deviceOrientation];
        if( orientation == CCDeviceOrientationLandscapeLeft || orientation == CCDeviceOrientationLandscapeRight )
            s.height -= r.size.width;
        else
            s.height -= r.size.height;
        self.position = ccp(s.width/2, s.height/2);

        int z=0;

        if (item) {
            [self addChild: item z:z];
            CCMenuItem *i = va_arg(args, CCMenuItem*);
            while(i) {
                z++;
                [self addChild: i z:z];
                i = va_arg(args, CCMenuItem*);
            }
        }
    //  [self alignItemsVertically];

        selectedItem = nil;
        //state = kMenuStateWaiting;
    }

    return self;
}

-(void) dealloc
{
    [super dealloc];
}

/*
 * override add:
 */
-(id) addChild:(CCMenuItem*)child z:(int)z tag:(int) aTag
{
    NSAssert( [child isKindOfClass:[CCMenuItem class]], @"Menu only supports MenuItem objects as children");
    return [super addChild:child z:z tag:aTag];
}

-(void) unselectAllItems
{
    for(CCNode* node in [self children])
    {
        if([node isKindOfClass:[CCMenuItem class]] && ![node isEqual:selectedItem])
        {
            CCMenuItem* menuItem = (CCMenuItem*)node;
            [menuItem unselected];
        }
    }
}
#pragma mark Menu - Events

//- (BOOL)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
//{
//  UITouch *touch = [touches anyObject];   
//  MenuItem *item = [self itemForTouch:touch];
//  
//  if( item ) {
//      [item selected];
//      selectedItem = item;
//      return kEventHandled;
//  }
//  
//  return kEventIgnored;
//}
//
//- (BOOL)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
//{
//  UITouch *touch = [touches anyObject];   
//  MenuItem *item = [self itemForTouch:touch];
//  
//  if( item ) {
//      [item unselected];
//      [item activate];
//      return kEventHandled;
//      
//  } else if( selectedItem ) {
//      [selectedItem unselected];
//      selectedItem = nil;
//      
//      // don't return kEventHandled here, since we are not handling it!
//  }
//  return kEventIgnored;
//}
//
//- (BOOL)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
//{
//  UITouch *touch = [touches anyObject];   
//  MenuItem *item = [self itemForTouch:touch];
//  
//  // "mouse" draged inside a button
//  if( item ) {
//      if( item != selectedItem ) {
//          if( selectedItem  )
//              [selectedItem unselected];
//          [item selected];
//          selectedItem = item;
//          return kEventHandled;
//      }
//      
//      // "mouse" draged outside the selected button
//  } else {
//      if( selectedItem ) {
//          [selectedItem unselected];
//          selectedItem = nil;
//          
//          // don't return kEventHandled here, since we are not handling it!
//      }
//  }
//  
//  return kEventIgnored;
//}

-(void) registerWithTouchDispatcher
{
    [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:INT_MIN+1 swallowsTouches:YES];
}

-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
    //if( state != kMenuStateWaiting ) return NO;

    selectedItem = [self itemForTouch:touch];
    [selectedItem selected];

    if( selectedItem ) {
        //state = kMenuStateTrackingTouch;
        return YES;
    }
    return NO;
}

-(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
    //NSAssert(state == kMenuStateTrackingTouch, @"[Menu ccTouchEnded] -- invalid state");

    [selectedItem unselected];
    [selectedItem activate];

    //state = kMenuStateWaiting;
}

-(void) ccTouchCancelled:(UITouch *)touch withEvent:(UIEvent *)event
{
    //NSAssert(state == kMenuStateTrackingTouch, @"[Menu ccTouchCancelled] -- invalid state");

    [selectedItem unselected];

    //state = kMenuStateWaiting;
}

-(void) ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
    //NSAssert(state == kMenuStateTrackingTouch, @"[Menu ccTouchMoved] -- invalid state");

    CCMenuItem *currentItem = [self itemForTouch:touch];

    if (currentItem != selectedItem) {
        [selectedItem unselected];
        selectedItem = currentItem;
        [self unselectAllItems];
        [selectedItem selected];
    }
}

#pragma mark Menu - Alignment
-(void) alignItemsVertically
{
    return [self alignItemsVerticallyWithPadding:kDefaultPadding];
}
-(void) alignItemsVerticallyWithPadding:(float)padding
{
    float height = -padding;
    for(CCMenuItem *item in children_)
        height += [item contentSize].height * item.scaleY + padding;

    float y = height / 2.0f;
    for(CCMenuItem *item in children_) {
        [item setPosition:ccp(0, y - [item contentSize].height * item.scaleY / 2.0f)];
        y -= [item contentSize].height * item.scaleY + padding;
    }
}

-(void) alignItemsHorizontally
{
    return [self alignItemsHorizontallyWithPadding:kDefaultPadding];
}

-(void) alignItemsHorizontallyWithPadding:(float)padding
{

    float width = -padding;
    for(CCMenuItem* item in children_)
        width += [item contentSize].width * item.scaleX + padding;

    float x = -width / 2.0f;
    for(CCMenuItem* item in children_) {
        [item setPosition:ccp(x + [item contentSize].width * item.scaleX / 2.0f, 0)];
        x += [item contentSize].width * item.scaleX + padding;
    }
}

-(void) alignItemsInColumns: (NSNumber *) columns, ...
{
    va_list args;
    va_start(args, columns);

    [self alignItemsInColumns:columns vaList:args];

    va_end(args);
}

-(void) alignItemsInColumns: (NSNumber *) columns vaList: (va_list) args
{
    NSMutableArray *rows = [[NSMutableArray alloc] initWithObjects:columns, nil];
    columns = va_arg(args, NSNumber*);
    while(columns) {
        [rows addObject:columns];
        columns = va_arg(args, NSNumber*);
    }

    int height = -5;
    NSUInteger row = 0, rowHeight = 0, columnsOccupied = 0, rowColumns;
    for(CCMenuItem *item in children_) {
        NSAssert( row < [rows count], @"Too many menu items for the amount of rows/columns.");

        rowColumns = [(NSNumber *) [rows objectAtIndex:row] unsignedIntegerValue];
        NSAssert( rowColumns, @"Can't have zero columns on a row");

        rowHeight = fmaxf(rowHeight, [item contentSize].height);
        ++columnsOccupied;

        if(columnsOccupied >= rowColumns) {
            height += rowHeight + 5;

            columnsOccupied = 0;
            rowHeight = 0;
            ++row;
        }
    }
    NSAssert( !columnsOccupied, @"Too many rows/columns for available menu items." );

    CGSize winSize = [[CCDirector sharedDirector] winSize];

    row = 0; rowHeight = 0; rowColumns = 0;
    float w, x, y = height / 2;
    for(CCMenuItem *item in children_) {
        if(rowColumns == 0) {
            rowColumns = [(NSNumber *) [rows objectAtIndex:row] unsignedIntegerValue];
            w = winSize.width / (1 + rowColumns);
            x = w;
        }

        rowHeight = fmaxf(rowHeight, [item contentSize].height);
        [item setPosition:ccp(x - winSize.width / 2,
                              y - [item contentSize].height / 2)];

        x += w + 10;
        ++columnsOccupied;

        if(columnsOccupied >= rowColumns) {
            y -= rowHeight + 5;

            columnsOccupied = 0;
            rowColumns = 0;
            rowHeight = 0;
            ++row;
        }
    }

    [rows release];
}

-(void) alignItemsInRows: (NSNumber *) rows, ...
{
    va_list args;
    va_start(args, rows);

    [self alignItemsInRows:rows vaList:args];

    va_end(args);
}

-(void) alignItemsInRows: (NSNumber *) rows vaList: (va_list) args
{
    NSMutableArray *columns = [[NSMutableArray alloc] initWithObjects:rows, nil];
    rows = va_arg(args, NSNumber*);
    while(rows) {
        [columns addObject:rows];
        rows = va_arg(args, NSNumber*);
    }

    NSMutableArray *columnWidths = [[NSMutableArray alloc] init];
    NSMutableArray *columnHeights = [[NSMutableArray alloc] init];

    int width = -10, columnHeight = -5;
    NSUInteger column = 0, columnWidth = 0, rowsOccupied = 0, columnRows;
    for(CCMenuItem *item in children_) {
        NSAssert( column < [columns count], @"Too many menu items for the amount of rows/columns.");

        columnRows = [(NSNumber *) [columns objectAtIndex:column] unsignedIntegerValue];
        NSAssert( columnRows, @"Can't have zero rows on a column");

        columnWidth = fmaxf(columnWidth, [item contentSize].width);
        columnHeight += [item contentSize].height + 5;
        ++rowsOccupied;

        if(rowsOccupied >= columnRows) {
            [columnWidths addObject:[NSNumber numberWithUnsignedInteger:columnWidth]];
            [columnHeights addObject:[NSNumber numberWithUnsignedInteger:columnHeight]];
            width += columnWidth + 10;

            rowsOccupied = 0;
            columnWidth = 0;
            columnHeight = -5;
            ++column;
        }
    }
    NSAssert( !rowsOccupied, @"Too many rows/columns for available menu items.");

    CGSize winSize = [[CCDirector sharedDirector] winSize];

    column = 0; columnWidth = 0; columnRows = 0;
    float x = -width / 2, y;
    for(CCMenuItem *item in children_) {
        if(columnRows == 0) {
            columnRows = [(NSNumber *) [columns objectAtIndex:column] unsignedIntegerValue];
            y = ([(NSNumber *) [columnHeights objectAtIndex:column] intValue] + winSize.height) / 2;
        }

        columnWidth = fmaxf(columnWidth, [item contentSize].width);
        [item setPosition:ccp(x + [(NSNumber *) [columnWidths objectAtIndex:column] unsignedIntegerValue] / 2,
                              y - winSize.height / 2)];

        y -= [item contentSize].height + 10;
        ++rowsOccupied;

        if(rowsOccupied >= columnRows) {
            x += columnWidth + 5;

            rowsOccupied = 0;
            columnRows = 0;
            columnWidth = 0;
            ++column;
        }
    }

    [columns release];
    [columnWidths release];
    [columnHeights release];
}

#pragma mark Menu - Opacity Protocol

/** Override synthesized setOpacity to recurse items */
- (void) setOpacity:(GLubyte)newOpacity
{
    opacity_ = newOpacity;
    for(id<CCRGBAProtocol> item in children_)
        [item setOpacity:opacity_];
}

-(void) setColor:(ccColor3B)color
{
    color_ = color;
    for(id<CCRGBAProtocol> item in children_)
        [item setColor:color_];
}

#pragma mark Menu - Private

-(CCMenuItem *) itemForTouch: (UITouch *) touch
{
    CGPoint touchLocation = [touch locationInView: [touch view]];
    touchLocation = [[CCDirector sharedDirector] convertToGL: touchLocation];

    for( CCMenuItem* item in children_ ) {
        CGPoint local = [item convertToNodeSpace:touchLocation];

        CGRect r = [item rect];
        r.origin = CGPointZero;

        if( CGRectContainsPoint( r, local ) )
        {
            return item;
        }
    }
    return nil;
}
@end

Original issue reported on code.google.com by aogden%u...@gtempaccount.com on 24 Jun 2010 at 12:04

GoogleCodeExporter commented 9 years ago

Original comment by ricardoq...@gmail.com on 26 Jun 2010 at 10:07

GoogleCodeExporter commented 9 years ago

Original comment by ricardoq...@gmail.com on 14 Jul 2010 at 9:48

GoogleCodeExporter commented 9 years ago
Could also post the diff ? It's much easier for me to apply a patch, than to 
replace the whole file.

Instructions of how to generate a patch:
http://www.cocos2d-iphone.org/wiki/doku.php/faq#how_do_i_submit_a_patch

thanks.

Original comment by ricardoq...@gmail.com on 16 Aug 2010 at 6:11

GoogleCodeExporter commented 9 years ago

Original comment by ricardoq...@gmail.com on 17 Mar 2011 at 7:02

GoogleCodeExporter commented 9 years ago
rescheduled for v1.1

Original comment by ricardoq...@gmail.com on 13 Jul 2011 at 9:44

GoogleCodeExporter commented 9 years ago
The issue was that cocos uses [touch view] to get the touch coordinates instead 
of using it's own view to convert the coordinates. 

This test case demonstrates that touches still work in combination with a 
uiscrollview. 

https://github.com/cocos2d/cocos2d-iphone/commit/8b40670800f0fa0ae083deeb13bd267
caf0501c8

Original comment by marcotil...@gmail.com on 18 Sep 2012 at 11:40