| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 | //-----------------------------------------------------------------------------// Copyright (c) 2012 GarageGames, LLC//// 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.//-----------------------------------------------------------------------------#include "gui/containers/guiAutoScrollCtrl.h"#include "console/consoleTypes.h"#include "console/engineAPI.h"IMPLEMENT_CONOBJECT( GuiAutoScrollCtrl );ConsoleDocClass( GuiAutoScrollCtrl,   "@brief A container that scrolls its child control up over time.\n\n"      "This container can be used to scroll a single child control in either of the four directions.\n\n"      "@tsexample\n"   "// Create a GuiAutoScrollCtrl that scrolls a long text of credits.\n"   "new GuiAutoScrollCtrl( CreditsScroller )\n"   "{\n"   "   position = \"0 0\";\n"   "   extent = Canvas.extent.x SPC Canvas.extent.y;\n"   "\n"   "   scrollDirection = \"Up\"; // Scroll upwards.\n"   "   startDelay = 4; // Wait 4 seconds before starting to scroll.\n"   "   isLooping = false; // Don't loop the credits.\n"   "   scrollOutOfSight = true; // Scroll up fully.\n"   "\n"   "   new GuiMLTextCtrl()\n"   "   {\n"   "      text = $CREDITS;\n"   "   };\n"   "};\n"   "\n"   "function CreditsScroller::onComplete( %this )\n"   "{\n"   "   // Switch back to main menu after credits have rolled.\n"   "   Canvas.setContent( MainMenu );\n"   "}\n"   "\n"   "// Start rolling credits.\n"   "Canvas.setContent( CreditsScroller );\n"   "@endtsexample\n\n"      "@note Only the first child will be scrolled.\n\n"      "@ingroup GuiContainers");IMPLEMENT_CALLBACK( GuiAutoScrollCtrl, onTick, void, (), (),   "Called every 32ms on the control." );IMPLEMENT_CALLBACK( GuiAutoScrollCtrl, onStart, void, (), (),   "Called when the control starts to scroll." );IMPLEMENT_CALLBACK( GuiAutoScrollCtrl, onComplete, void, (), (),   "Called when the child control has been scrolled in entirety." );IMPLEMENT_CALLBACK( GuiAutoScrollCtrl, onReset, void, (), (),   "Called when the child control is reset to its initial position and the cycle starts again." );ImplementEnumType( GuiAutoScrollDirection,   "Direction in which to scroll the child control.\n\n"   "@ingroup GuiContainers" )   { GuiAutoScrollCtrl::Up, "Up", "Scroll from bottom towards top." },   { GuiAutoScrollCtrl::Down, "Down", "Scroll from top towards bottom." },   { GuiAutoScrollCtrl::Left, "Left", "Scroll from right towards left." },   { GuiAutoScrollCtrl::Right, "Right", "Scroll from left towards right." },EndImplementEnumType;//-----------------------------------------------------------------------------GuiAutoScrollCtrl::GuiAutoScrollCtrl()   : mDirection( Up ),     mIsLooping( true ),     mCurrentPhase( GuiAutoScrollCtrl::PhaseComplete ),     mCurrentTime( 0.f ),     mCompleteTime(F32_MAX),     mCurrentPosition(0.0f),     mStartDelay( 3.f ),     mResetDelay( 5.f ),     mChildBorder( 10 ),     mScrollOutOfSight( false ),     mScrollSpeed( 1.f ){   mIsContainer = true;}//-----------------------------------------------------------------------------void GuiAutoScrollCtrl::initPersistFields(){   docsURL;   addGroup( "Scrolling" );         addField( "scrollDirection", TYPEID< Direction >(), Offset( mDirection, GuiAutoScrollCtrl ),         "Direction in which the child control is moved." );      addField( "startDelay", TypeF32, Offset( mStartDelay, GuiAutoScrollCtrl ),         "Seconds to wait before starting to scroll." );      addField( "resetDelay", TypeF32, Offset( mResetDelay, GuiAutoScrollCtrl ),         "Seconds to wait after scrolling completes before resetting and starting over.\n\n"         "@note Only takes effect if #isLooping is true." );      addField( "childBorder", TypeS32, Offset( mChildBorder, GuiAutoScrollCtrl ),         "Padding to put around child control (in pixels)." );      addField( "scrollSpeed", TypeF32, Offset( mScrollSpeed, GuiAutoScrollCtrl ),         "Scrolling speed in pixels per second." );      addField( "isLooping", TypeBool, Offset( mIsLooping, GuiAutoScrollCtrl ),         "If true, the scrolling will reset to the beginning once completing a cycle." );      addField( "scrollOutOfSight", TypeBool, Offset( mScrollOutOfSight, GuiAutoScrollCtrl ),         "If true, the child control will be completely scrolled out of sight; otherwise it will only scroll "         "until the other end becomes visible." );            endGroup( "Scrolling" );   Parent::initPersistFields();}//-----------------------------------------------------------------------------bool GuiAutoScrollCtrl::onWake(){   if( !Parent::onWake() )      return false;         setProcessTicks( true );   return true;}//-----------------------------------------------------------------------------void GuiAutoScrollCtrl::onSleep(){   setProcessTicks( false );   Parent::onSleep();}//-----------------------------------------------------------------------------void GuiAutoScrollCtrl::onChildAdded( GuiControl* control ){   _reset( control );   Parent::onChildAdded( control );}//-----------------------------------------------------------------------------void GuiAutoScrollCtrl::onChildRemoved( GuiControl* control ){   mCurrentPhase = PhaseComplete;   Parent::onChildRemoved( control );}//-----------------------------------------------------------------------------bool GuiAutoScrollCtrl::_isScrollComplete() const{   if( empty() )      return true;         GuiControl* control = static_cast< GuiControl* >( at( 0 ) );   U32 axis = _getScrollAxis();   F32 amount = _getScrollAmount();      if( mScrollOutOfSight )   {      // If scrolling out of sight, scrolling is complete when the control's rectangle      // does not intersect our own rectangle anymore.            RectI thisRect( Point2I( 0, 0 ), getExtent() );      return !( thisRect.overlaps( control->getBounds() ) );   }   else   {      if( amount < 0 )         return ( control->getPosition()[ axis ] + control->getExtent()[ axis ] ) < ( getExtent()[ axis ] - mChildBorder );      else         return ( control->getPosition()[ axis ] >= mChildBorder );   }}//-----------------------------------------------------------------------------void GuiAutoScrollCtrl::_reset( GuiControl* control ){   U32 axis = _getScrollAxis();   U32 counterAxis = ( axis == 1 ? 0 : 1 );   Point2I newPosition( mChildBorder, mChildBorder );   Point2I newExtent = control->getExtent();      // Fit control on axis that is not scrolled.   newExtent[ counterAxis ] = getExtent()[ counterAxis ] - mChildBorder * 2;      // For the right and down scrolls, position the control away from the   // right/bottom edge of our control.      if( mDirection == Right )      newPosition.x = - ( newExtent.x - getExtent().x + mChildBorder );   else if( mDirection == Down )      newPosition.y = - ( newExtent.y - getExtent().y + mChildBorder );         // Set the child geometry.         control->setPosition( newPosition );   control->setExtent( newExtent );         // Reset counters.   mCurrentTime = 0.0f;   mCurrentPhase = PhaseInitial;   mCurrentPosition = control->getPosition()[ axis ];}//-----------------------------------------------------------------------------void GuiAutoScrollCtrl::reset(){   if( !empty() )      _reset( static_cast< GuiControl* >( at( 0 ) ) );}//-----------------------------------------------------------------------------bool GuiAutoScrollCtrl::resize( const Point2I &newPosition, const Point2I &newExtent ){   if( !Parent::resize( newPosition, newExtent ) )      return false;   for( iterator i = begin(); i != end(); ++ i )   {      GuiControl* control = static_cast< GuiControl* >( *i );      if( control )         _reset( control );   }   return true;}//-----------------------------------------------------------------------------void GuiAutoScrollCtrl::childResized( GuiControl* child ){   Parent::childResized( child );   _reset(child);}//-----------------------------------------------------------------------------void GuiAutoScrollCtrl::processTick(){   onTick_callback();}//-----------------------------------------------------------------------------void GuiAutoScrollCtrl::advanceTime( F32 timeDelta ){   if( mCurrentPhase == PhaseComplete )      return;         // Wait out initial delay.               if( ( mCurrentTime + timeDelta ) < mStartDelay)   {      mCurrentTime += timeDelta;      return;   }      // Start scrolling if we haven't already.      if( mCurrentPhase == PhaseInitial )   {      onStart_callback();      mCurrentPhase = PhaseScrolling;   }   GuiControl* control = static_cast< GuiControl* >( at( 0 ) );   if( !control ) // Should not happen.      return;   // If not yet complete, scroll some more.      if( !_isScrollComplete() )   {      U32 axis = _getScrollAxis();      F32 amount = _getScrollAmount();            mCurrentPosition += amount * timeDelta;      Point2I newPosition = control->getPosition();      newPosition[ axis ] = mCurrentPosition;            control->setPosition( newPosition );   }   else   {      mCurrentTime += timeDelta;            if( mCurrentPhase != PhaseComplete && mCurrentPhase != PhaseWait )      {         if( mCurrentPhase != PhaseWait )         {            onComplete_callback();            mCurrentPhase = PhaseComplete;         }                  mCompleteTime = mCurrentTime;      }            // Reset, if looping.            if( mIsLooping )      {         // Wait out reset time and restart.                  mCurrentPhase = PhaseWait;         if( mCurrentTime > ( mCompleteTime + mResetDelay ) )         {            onReset_callback();            _reset( control );         }      }   }}//-----------------------------------------------------------------------------void GuiAutoScrollCtrl::inspectPostApply(){   Parent::inspectPostApply();   reset();}//=============================================================================//    API.//=============================================================================// MARK: ---- API ----//-----------------------------------------------------------------------------DefineEngineMethod( GuiAutoScrollCtrl, reset, void, (),,   "Reset scrolling." ){   object->reset();}
 |