CCScrollView for cocos2d-x that works?

cocos2d-x, cross-platform

Solution

Check out the forums on Cocos2d-x's site for the latest one. They're being reworked right now by the author based on this. http://www.cocos2d-x.org/boards/18/topics/1090?r=10835#message-10835

Problem

Edit: With enough rewriting, and commenting, I have it running, will post final below for others. Edit2: I've been more updating my own version, and I've learned the original coder was not very good with C++, you may want to go over everything in there before using it, check out comments below post The currently available CCScrollView for cocos2d-x suffers from one major flaw: It's messed up. Specifically, the cpp function headers do not match the .h file's. The cpp file refers to UIEvent and NSMutableArray, not X-Platform. The scroll view itself must have an array added, which limits your ability to use a for loop to create your menu items (if creating menu items), since you can't use a mutable one, and they don't subclass each other like iOS so you can't just use a mutable then say CCArray * array = mutable->getMutableArray() or similar. Also, the defines are outdated (though fixable through adding the CCX_SAFE_DELETE via the translation rules on the cocos2d-x site), and a variety of random templates trying to create NS and UI objects that are not even within the project for I have no clue why. The basic idea is, aside from the addition of using mutable arrays, this class is way too messed up to bother translating to C++. I don't believe the author compiled the version he has up, as the headers don't match. I thought I downloaded both (there's 2 of each) the cpp and .h files, and both have UIEvent instead of CC along with the other issues. Note: I have fixed a variety of the above, and changed SelectorProtocol to CCObject in various places, but it's just getting overwhelming. Perhaps I should use the Parallax node instead? This is just ridiculous, perhaps it's great for Cocos2d but it's not working on c2d-X. Anyway, I'll be recoding and searching, thanks for any suggestions! Edit: Here's the classes I'm now using. Not done editing, but these will get anyone started, and compiling on more than just iPhone Header: ``` #ifndef __CCSCROLLLAYER__ #define __CCSCROLLLAYER__ // CCScrollLayer.h // // Copyright 2010 DK101 // http://dk101.net/2010/11/30/implementing-page-scrolling-in-cocos2d/ // // Copyright 2010 Giv Parvaneh. // http://www.givp.org/blog/2010/12/30/scrolling-menus-in-cocos2d/ // // Copyright 2011 Stepan Generalov // // Copyright 2011 Eli Yukelzon // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // Original source: https://github.com/cocos2d/cocos2d-iphone-extensions/tree/master/Extensions/CCScrollLayer // Last updated: October 1, 2011 #include "cocos2d.h" namespace cocos2d { class CCScrollLayer; class CCScrollLayerDelegate { public: /** Called when scroll layer begins scrolling. * Usefull to cancel CCTouchDispatcher standardDelegates. */ virtual void scrollLayerScrollingStarted(CCScrollLayer* sender) {} /** Called at the end of moveToPage: * Doesn't get called in selectPage: */ virtual void scrollLayerScrolledToPageNumber(CCScrollLayer* sender, unsigned int page) {} }; /* It is a very clean and elegant subclass of CCLayer that lets you pass-in an array of layers and it will then create a smooth scroller. Complete with the "snapping" effect. You can create screens with anything that can be added to a CCLayer. */ class CCScrollLayer : public CCLayer { int currentScreen; //added int totalScreens; float scrollWidth; float scrollHeight; float startWidth; float startHeight; int startSwipe; public: //CCScrollLayer(); ~CCScrollLayer(); static CCScrollLayer* nodeWithLayers(CCArray* layers, int widthOffset); bool initWithLayers(CCArray* layers, int widthOffset); /** Updates all pages positions & adds them as children if needed. * Can be used to update position of pages after screen reshape, or * for update after dynamic page add/remove. */ void updatePages(); /** Adds new page and reorders pages trying to set given number for newly added page. * If number > pages count - adds new page to the right end of the scroll layer. * If number <= 0 - adds new page to the left end of the scroll layer. * @attention Designated addPage method. */ void addPage(CCLayer* aPage, unsigned int pageNumber); /** Adds new page to the right end of the scroll layer. */ void addPage(CCLayer* aPage); /** Removes page if it's one of scroll layers pages (not children) * Does nothing if page not found. */ void removePage(CCLayer* aPage); /** Removes page with given number. Doesn nothing if there's no page for such number. */ void removePageWithNumber(unsigned int pageNumber); /* Moves scrollLayer to page with given number & invokes delegate * method scrollLayer:scrolledToPageNumber: at the end of CCMoveTo action. * Does nothing if number >= totalScreens or < 0. */ void moveToPage(unsigned int pageNumber); /* Immedeatly moves scrollLayer to page with given number without running CCMoveTo. * Does nothing if number >= totalScreens or < 0. */ void selectPage(unsigned int pageNumber); CC_SYNTHESIZE(CCScrollLayerDelegate*, m_pDelegate, Delegate); /** Calibration property. Minimum moving touch length that is enough * to cancel menu items and start scrolling a layer. */ CC_SYNTHESIZE(CGFloat, m_fMinimumTouchLengthToSlide, MinimumTouchLengthToSlide); /** Calibration property. Minimum moving touch length that is enough to change * the page, without snapping back to the previous selected page. */ CC_SYNTHESIZE(CGFloat, m_fMinimumTouchLengthToChangePage, MinimumTouchLengthToChangePage); /** If YES - when starting scrolling CCScrollLayer will claim touches, that are * already claimed by others targetedTouchDelegates by calling CCTouchDispatcher#touchesCancelled * Usefull to have ability to scroll with touch above menus in pages. * If NO - scrolling will start, but no touches will be cancelled. * Default is YES. */ CC_SYNTHESIZE(bool, m_bStealTouches, StealTouches); /** Whenever show or not white/grey dots under the scroll layer. * If yes - dots will be rendered in parents transform (rendered after scroller visit). */ CC_SYNTHESIZE(bool, m_bShowPagesIndicator, ShowPagesIndicator); /** Position of dots center in parent coordinates. * (Default value is screenWidth/2, screenHeight/4) */ CC_SYNTHESIZE_PASS_BY_REF(CCPoint, m_tPagesIndicatorPosition, PagesIndicatorPosition); /** Total pages available in scrollLayer. */ unsigned int getTotalScreens() const; /** Current page number, that is shown. Belongs to the [0, totalScreen] interval. */ CC_SYNTHESIZE_READONLY(unsigned int, m_uCurrentScreen, CurrentScreen); /** Offset, that can be used to let user see next/previous page. */ CC_SYNTHESIZE(CGFloat, m_fPagesWidthOffset, PagesWidthOffset); /** Offset that can be used to let user see empty space over first or last page. */ CC_SYNTHESIZE(CGFloat, m_fMarginOffset, MarginOffset); /** Array of pages CCLayer's */ CC_SYNTHESIZE_READONLY(CCArray*, m_pLayers, Pages); protected: // The x coord of initial point the user starts their swipe. CGFloat m_fStartSwipe; // Internal state of scrollLayer (scrolling or idle). int m_iState; bool m_bStealingTouchInProgress; // Holds the touch that started the scroll CCTouch* m_pScrollTouch; //void visit(); //void moveToPageEnded(); unsigned int pageNumberForPosition(const CCPoint& position); CCPoint positionForPageWithNumber(unsigned int pageNumber); void claimTouch(CCTouch* pTouch); void cancelAndStoleTouch(CCTouch* pTouch, CCEvent* pEvent); //void registerWithTouchDispatcher(); bool ccTouchBegan(CCTouch* pTouch, CCEvent* pEvent); void ccTouchMoved(CCTouch* pTouch, CCEvent* pEvent); void ccTouchEnded(CCTouch* pTouch, CCEvent* pEvent); //void ccTouchCancelled(CCTouch* pTouch, CCEvent* pEvent); }; } //end namespace #endif ``` CPP ``` // CCScrollLayer.cpp // Museum // // Created by GParvaneh on 29/12/2010. // Copyright 2010. All rights reserved. // Ported to C++ by Lior Tamam on 03/04/2011 #include "CCScrollLayer.h" using namespace cocos2d; CCScrollLayer* CCScrollLayer::nodeWithLayers(CCArray* layers, int widthOffset) { CCScrollLayer *pRet = new CCScrollLayer(); if (pRet && pRet->initWithLayers(layers, widthOffset)) { pRet->autorelease(); return pRet; } CCX_SAFE_DELETE(pRet); return NULL; } bool CCScrollLayer::initWithLayers(CCArray* layers, int widthOffset) { if (CCLayer::init()) { // Make sure the layer accepts touches CCTouchDispatcher::sharedDispatcher()->addTargetedDelegate(this,0,true); // Set up the starting variables //if(!widthOffset) { // widthOffset = 0; } currentScreen = 1; // offset added to show preview of next/previous screens scrollWidth = (int)CCDirector::sharedDirector()->getWinSize().width - widthOffset; scrollHeight = (int)CCDirector::sharedDirector()->getWinSize().height; startWidth = scrollWidth; startHeight = scrollHeight; // Loop through the array and add the screens unsigned int i; for (i=0; i<layers->count(); i++) { CCLayer* l = (CCLayer*)layers->objectAtIndex(i); //l->setAnchorPoint(ccp(0,0)); //l->setPosition(ccp((i*scrollWidth),0)); addChild(l); } // Setup a count of the available screens totalScreens = layers->count(); return true; } else { return false; } } void CCScrollLayer::setMaximumScrollHeight(float maxHeight) { //Make the offset match expected pixels (include the current screen if at ccp(0,0) maxHeight -= CCDirector::sharedDirector()->getWinSize().height; maximumScrollHeight = maxHeight; } CCScrollLayer::~CCScrollLayer() { CCTouchDispatcher::sharedDispatcher()->removeDelegate(this); CCLayer::onExit(); } bool CCScrollLayer::ccTouchBegan(CCTouch *touch, CCEvent *withEvent) { // // CCPoint touchPoint = touch->locationInView(); // touchPoint = CCDirector::sharedDirector()->convertToGL(touchPoint); // // startSwipe = (int)touchPoint.y; return true; } void CCScrollLayer::ccTouchMoved(CCTouch *touch, CCEvent *withEvent) { CCPoint touchPoint = touch->locationInView(); CCPoint prevPoint = touch->previousLocationInView(); touchPoint = CCDirector::sharedDirector()->convertToGL(touchPoint); prevPoint = CCDirector::sharedDirector()->convertToGL(prevPoint); CCPoint difference = ccp( touchPoint.x - prevPoint.x , touchPoint.y - prevPoint.y); CCPoint currentPos = this->getPosition(); currentPos = ccp( currentPos.x, currentPos.y+difference.y); if (currentPos.y > maximumScrollHeight) { currentPos.y = maximumScrollHeight; //this->setPositionY(maximumScrollHeight); } else if (currentPos.y < 0) { currentPos.y = 0; // this->setPositionY(0); } this->setPosition(currentPos); } /* void CCScrollLayer::ccTouchEnded(CCTouch *touch, CCEvent *withEvent) { //CCPoint touchPoint = touch->locationInView(); //touchPoint = CCDirector::sharedDirector()->convertToGL(touchPoint); int newX = (int)touchPoint.x; if ( (newX - startSwipe) < -scrollWidth / 3 && (currentScreen+1) <= totalScreens ) { // this->moveToNextPage(); } else if ( (newX - startSwipe) > scrollWidth / 3 && (currentScreen-1) > 0 ) { // this->moveToPreviousPage(); } else { // this->moveToPage(currentScreen); } } */ ```

Original source