Torque3D Documentation / _generateds / guiScrollCtrl.cpp

guiScrollCtrl.cpp

Engine/source/gui/containers/guiScrollCtrl.cpp

More...

Public Variables

Public Functions

ConsoleDocClass(GuiScrollCtrl , "@brief A container that allows <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> view one or more possibly larger controls inside its area by " "providing horizontal and/or vertical scroll <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bars.\n\n</a>" "@ingroup GuiContainers" )
DefineEngineMethod(GuiScrollCtrl , computeSizes , void , () , "Refresh sizing and positioning of child controls." )
DefineEngineMethod(GuiScrollCtrl , getScrollPosition , Point2I , () , "Get the current coordinates of the scrolled <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">content.\n\n</a>" "@return The current position of the scrolled content." )
DefineEngineMethod(GuiScrollCtrl , getScrollPositionX , S32 , () , "Get the current X coordinate of the scrolled <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">content.\n\n</a>" "@return The current X coordinate of the scrolled content." )
DefineEngineMethod(GuiScrollCtrl , getScrollPositionY , S32 , () , "Get the current Y coordinate of the scrolled content." "@return The current Y coordinate of the scrolled content." )
DefineEngineMethod(GuiScrollCtrl , scrollToBottom , void , () , "Scroll all the way <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the bottom of the vertical scrollbar and the left of the horizontal bar." )
DefineEngineMethod(GuiScrollCtrl , scrollToObject , void , (GuiControl *control) , "Scroll the <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> so that the given child @<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">visible.\n\n</a>" "@param <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> A child control." )
DefineEngineMethod(GuiScrollCtrl , scrollToTop , void , () , "Scroll all the way <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the top of the vertical and left of the horizontal scrollbar." )
DefineEngineMethod(GuiScrollCtrl , setScrollPosition , void , (S32 x, S32 y) , "Set the position of the scrolled <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">content.\n\n</a>" "@param x Position on X <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">axis.\n</a>" "@param y Position on y <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">axis.\n</a>" )
IMPLEMENT_CALLBACK(GuiScrollCtrl , onScroll , void , () , () , "Called each time the child controls are scrolled by some amount." )
ImplementEnumType(GuiScrollBarBehavior , "Display behavior of <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> scroll bar. Determines when <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> scrollbar will be <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">visible.\n\n</a>" "@ingroup GuiContainers" )

Detailed Description

Public Variables

 EndImplementEnumType 

Public Functions

ConsoleDocClass(GuiScrollCtrl , "@brief A container that allows <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> view one or more possibly larger controls inside its area by " "providing horizontal and/or vertical scroll <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bars.\n\n</a>" "@ingroup GuiContainers" )

DefineEngineMethod(GuiScrollCtrl , computeSizes , void , () , "Refresh sizing and positioning of child controls." )

DefineEngineMethod(GuiScrollCtrl , getScrollPosition , Point2I , () , "Get the current coordinates of the scrolled <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">content.\n\n</a>" "@return The current position of the scrolled content." )

DefineEngineMethod(GuiScrollCtrl , getScrollPositionX , S32 , () , "Get the current X coordinate of the scrolled <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">content.\n\n</a>" "@return The current X coordinate of the scrolled content." )

DefineEngineMethod(GuiScrollCtrl , getScrollPositionY , S32 , () , "Get the current Y coordinate of the scrolled content." "@return The current Y coordinate of the scrolled content." )

DefineEngineMethod(GuiScrollCtrl , scrollToBottom , void , () , "Scroll all the way <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the bottom of the vertical scrollbar and the left of the horizontal bar." )

DefineEngineMethod(GuiScrollCtrl , scrollToObject , void , (GuiControl *control) , "Scroll the <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> so that the given child @<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">visible.\n\n</a>" "@param <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> A child control." )

DefineEngineMethod(GuiScrollCtrl , scrollToTop , void , () , "Scroll all the way <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the top of the vertical and left of the horizontal scrollbar." )

DefineEngineMethod(GuiScrollCtrl , setScrollPosition , void , (S32 x, S32 y) , "Set the position of the scrolled <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">content.\n\n</a>" "@param x Position on X <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">axis.\n</a>" "@param y Position on y <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">axis.\n</a>" )

IMPLEMENT_CALLBACK(GuiScrollCtrl , onScroll , void , () , () , "Called each time the child controls are scrolled by some amount." )

IMPLEMENT_CONOBJECT(GuiScrollCtrl )

ImplementEnumType(GuiScrollBarBehavior , "Display behavior of <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> scroll bar. Determines when <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> scrollbar will be <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">visible.\n\n</a>" "@ingroup GuiContainers" )

   1
   2//-----------------------------------------------------------------------------
   3// Copyright (c) 2012 GarageGames, LLC
   4//
   5// Permission is hereby granted, free of charge, to any person obtaining a copy
   6// of this software and associated documentation files (the "Software"), to
   7// deal in the Software without restriction, including without limitation the
   8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   9// sell copies of the Software, and to permit persons to whom the Software is
  10// furnished to do so, subject to the following conditions:
  11//
  12// The above copyright notice and this permission notice shall be included in
  13// all copies or substantial portions of the Software.
  14//
  15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21// IN THE SOFTWARE.
  22//-----------------------------------------------------------------------------
  23
  24#include "platform/platform.h"
  25#include "platform/typetraits.h"
  26#include "gui/containers/guiScrollCtrl.h"
  27#include "console/engineAPI.h"
  28#include "console/console.h"
  29#include "gfx/bitmap/gBitmap.h"
  30#include "gui/core/guiDefaultControlRender.h"
  31#include "gfx/gfxDevice.h"
  32#include "gfx/gfxDrawUtil.h"
  33#include "gui/core/guiCanvas.h"
  34
  35
  36IMPLEMENT_CONOBJECT( GuiScrollCtrl );
  37
  38ConsoleDocClass( GuiScrollCtrl,
  39   "@brief A container that allows to view one or more possibly larger controls inside its area by "
  40      "providing horizontal and/or vertical scroll bars.\n\n"
  41   
  42   "@ingroup GuiContainers"
  43);
  44
  45ImplementEnumType( GuiScrollBarBehavior,
  46   "Display behavior of a scroll bar.  Determines when a scrollbar will be visible.\n\n"
  47   "@ingroup GuiContainers" )
  48   { GuiScrollCtrl::ScrollBarAlwaysOn,     "alwaysOn",   "Always visible." },
  49   { GuiScrollCtrl::ScrollBarAlwaysOff,    "alwaysOff",  "Never visible." },
  50   { GuiScrollCtrl::ScrollBarDynamic,      "dynamic",    "Only visible when actually needed, i.e. when the child control(s) exceed the visible space on the given axis." },
  51EndImplementEnumType;
  52
  53IMPLEMENT_CALLBACK( GuiScrollCtrl, onScroll, void, (), (),
  54   "Called each time the child controls are scrolled by some amount." );
  55
  56//-----------------------------------------------------------------------------
  57
  58GuiScrollCtrl::GuiScrollCtrl()
  59 : mBorderThickness( 1 ),
  60   mChildMargin( 0, 0 ),
  61   mScrollBarThickness( 16 ),
  62   mScrollBarArrowBtnLength( 16 ),
  63   mScrollBarDragTolerance( 130 ),
  64   mStateDepressed( false ),
  65   mHitRegion( None ),
  66   mForceVScrollBar( ScrollBarAlwaysOn ),
  67   mUseConstantHeightThumb( false ),
  68   mWillFirstRespond( true ),
  69   mForceHScrollBar( ScrollBarAlwaysOn ),
  70   mLockHorizScroll( false ),
  71   mLockVertScroll( false ),
  72   mIgnoreChildResized( false ),
  73   mAnimating( false ),
  74   mScrollAnimSpeed( -1 ),
  75   mChildPos(0, 0),
  76   mChildExt(0, 0),
  77   mScrollTargetPos( -1, -1 ),
  78   mBaseThumbSize(0),
  79   mHBarEnabled(false),
  80   mVBarEnabled(false),
  81   mHasHScrollBar(false),
  82   mHasVScrollBar(false),
  83   mHThumbSize(1),
  84   mHThumbPos(0),
  85   mVThumbSize(1),
  86   mVThumbPos(0),
  87   mThumbMouseDelta(0)
  88{
  89   mBitmapBounds = NULL;
  90   mIsContainer = true;
  91   setExtent(200,200);
  92   mLastPreRender = Platform::getVirtualMilliseconds();
  93   mLastUpdated = Platform::getVirtualMilliseconds();
  94}
  95
  96//-----------------------------------------------------------------------------
  97
  98void GuiScrollCtrl::initPersistFields()
  99{
 100   addGroup( "Scolling" );
 101   
 102      addField( "willFirstRespond",     TypeBool,    Offset(mWillFirstRespond, GuiScrollCtrl));
 103      addField( "hScrollBar",           TYPEID< ScrollBarBehavior >(),    Offset(mForceHScrollBar, GuiScrollCtrl),
 104         "When to display the horizontal scrollbar.");
 105      addField( "vScrollBar",           TYPEID< ScrollBarBehavior >(),    Offset(mForceVScrollBar, GuiScrollCtrl),
 106         "When to display the vertical scrollbar.");
 107      addField( "lockHorizScroll",      TypeBool,    Offset(mLockHorizScroll, GuiScrollCtrl),
 108         "Horizontal scrolling not allowed if set.");
 109      addField( "lockVertScroll",       TypeBool,    Offset(mLockVertScroll, GuiScrollCtrl),
 110         "Vertical scrolling not allowed if set.");
 111      addField( "constantThumbHeight",  TypeBool,    Offset(mUseConstantHeightThumb, GuiScrollCtrl));
 112      addField( "childMargin",          TypePoint2I, Offset(mChildMargin, GuiScrollCtrl),
 113         "Padding region to put around child contents." );
 114      addField( "mouseWheelScrollSpeed", TypeS32,    Offset(mScrollAnimSpeed, GuiScrollCtrl),
 115         "Pixels/Tick - if not positive then mousewheel scrolling occurs instantly (like other scrolling).");
 116      
 117   endGroup( "Scrolling" );
 118
 119   Parent::initPersistFields();
 120}
 121
 122//-----------------------------------------------------------------------------
 123
 124bool GuiScrollCtrl::resize(const Point2I &newPos, const Point2I &newExt)
 125{
 126   if( !Parent::resize(newPos, newExt) )
 127      return false;
 128
 129   computeSizes();
 130   return true;
 131}
 132
 133//-----------------------------------------------------------------------------
 134
 135void GuiScrollCtrl::childResized(GuiControl *child)
 136{
 137   if ( mIgnoreChildResized )
 138      return;
 139
 140   Parent::childResized(child);
 141   computeSizes();
 142}
 143
 144//-----------------------------------------------------------------------------
 145
 146bool GuiScrollCtrl::onWake()
 147{
 148   if (! Parent::onWake())
 149      return false;
 150
 151   mTextureObject = mProfile->mTextureObject;
 152   if (mTextureObject && (mProfile->constructBitmapArray() >= BmpStates * BmpCount))
 153   {
 154      mBitmapBounds = mProfile->mBitmapArrayRects.address();
 155
 156      //init
 157      mBaseThumbSize = mBitmapBounds[BmpStates * BmpVThumbTopCap].extent.y +
 158         mBitmapBounds[BmpStates * BmpVThumbBottomCap].extent.y;
 159      mScrollBarThickness      = mBitmapBounds[BmpStates * BmpVPage].extent.x;
 160      mScrollBarArrowBtnLength = mBitmapBounds[BmpStates * BmpUp].extent.y;
 161      computeSizes();
 162   } 
 163   else
 164   {
 165      Con::warnf("No texture loaded for scroll control named %s with profile %s", getName(), mProfile->getName());
 166   }
 167   
 168   return true;
 169}
 170
 171//-----------------------------------------------------------------------------
 172
 173void GuiScrollCtrl::onSleep()
 174{
 175   // Reset the mouse tracking state of this control
 176   //  when it is put to sleep
 177   mStateDepressed = false;
 178   mHitRegion = None;
 179
 180   Parent::onSleep();
 181   mTextureObject = NULL;
 182}
 183
 184//-----------------------------------------------------------------------------
 185
 186bool GuiScrollCtrl::calcChildExtents()
 187{
 188   // scroll control should deal well with multiple gui controls
 189   if( !size() )
 190      return false;
 191
 192   // Find size and relative position of the client rectangle.
 193   
 194   Point2I maxPos( TypeTraits< S32 >::MIN, TypeTraits< S32 >::MIN );
 195   Point2I minPos( TypeTraits< S32 >::MAX, TypeTraits< S32 >::MAX );
 196   
 197   bool haveVisibleChild = false;
 198   for( U32 i = 0; i < size(); i++ )
 199   {
 200      GuiControl *ctrl = (GuiControl*)at(i);
 201      if( ctrl->isVisible() )
 202      {
 203         haveVisibleChild = true;
 204         
 205         minPos.x = getMin( ctrl->getPosition().x, minPos.x );
 206         minPos.y = getMin( ctrl->getPosition().y, minPos.y );
 207
 208         // This is +1 but the remaining code here all works with extents +1.
 209         Point2I ctrlMax = ctrl->getPosition() + ctrl->getExtent();
 210                  
 211         maxPos.x = getMax( ctrlMax.x, maxPos.x );
 212         maxPos.y = getMax( ctrlMax.y, maxPos.y );
 213      }
 214   }
 215   
 216   if( !haveVisibleChild )
 217      return false;
 218   
 219   mChildPos = minPos;
 220   mChildExt = maxPos - minPos;
 221
 222   return true;
 223}
 224
 225//-----------------------------------------------------------------------------
 226
 227void GuiScrollCtrl::scrollRectVisible(RectI rect)
 228{
 229   // rect is passed in virtual client space
 230   if(rect.extent.x > mContentExt.x)
 231      rect.extent.x = mContentExt.x;
 232   if(rect.extent.y > mContentExt.y)
 233      rect.extent.y = mContentExt.y;
 234
 235   // Determine the points bounding the requested rectangle
 236   Point2I rectUpperLeft  = rect.point;
 237   Point2I rectLowerRight = rect.point + rect.extent;
 238
 239   // Determine the points bounding the actual visible area...
 240   Point2I visUpperLeft = mChildRelPos;
 241   Point2I visLowerRight = mChildRelPos + mContentExt;
 242   Point2I delta(0,0);
 243
 244   // We basically try to make sure that first the top left of the given
 245   // rect is visible, and if it is, then that the bottom right is visible.
 246
 247   // Make sure the rectangle is visible along the X axis...
 248   if(rectUpperLeft.x < visUpperLeft.x)
 249      delta.x = rectUpperLeft.x - visUpperLeft.x;
 250   else if(rectLowerRight.x > visLowerRight.x)
 251      delta.x = rectLowerRight.x - visLowerRight.x;
 252
 253   // Make sure the rectangle is visible along the Y axis...
 254   if(rectUpperLeft.y < visUpperLeft.y)
 255      delta.y = rectUpperLeft.y - visUpperLeft.y;
 256   else if(rectLowerRight.y > visLowerRight.y)
 257      delta.y = rectLowerRight.y - visLowerRight.y;
 258
 259   // If we had any changes, scroll, otherwise don't.
 260   if(delta.x || delta.y)
 261      scrollDelta(delta.x, delta.y);
 262}
 263
 264//-----------------------------------------------------------------------------
 265
 266bool GuiScrollCtrl::isPointVisible( const Point2I& point )
 267{
 268   return    ( point.x >= mChildRelPos.x && point.x <= ( mChildRelPos.x + mContentExt.x ) )
 269          && ( point.y >= mChildRelPos.y && point.y <= ( mChildRelPos.y + mContentExt.y ) );
 270}
 271
 272//-----------------------------------------------------------------------------
 273
 274bool GuiScrollCtrl::isRectCompletelyVisible(const RectI& rect)
 275{
 276   // rect is passed in virtual client space
 277   // Determine the points bounding the requested rectangle
 278   Point2I rectUpperLeft  = rect.point;
 279   Point2I rectLowerRight = rect.point + rect.extent;
 280
 281   // Determine the points bounding the actual visible area...
 282   Point2I visUpperLeft = mChildRelPos;
 283   Point2I visLowerRight = mChildRelPos + mContentExt;
 284
 285   // Make sure the rectangle is visible along the X axis...
 286   if(rectUpperLeft.x < visUpperLeft.x)
 287      return false;
 288   else if(rectLowerRight.x > visLowerRight.x)
 289      return false;
 290
 291   // Make sure the rectangle is visible along the Y axis...
 292   if(rectUpperLeft.y < visUpperLeft.y)
 293      return false;
 294   else if(rectLowerRight.y > visLowerRight.y)
 295      return false;
 296
 297   return true;
 298}
 299
 300//-----------------------------------------------------------------------------
 301
 302void GuiScrollCtrl::addObject(SimObject *object)
 303{
 304   Parent::addObject(object);
 305   computeSizes();
 306}
 307
 308//-----------------------------------------------------------------------------
 309
 310GuiControl* GuiScrollCtrl::findHitControl(const Point2I &pt, S32 initialLayer)
 311{
 312   if(pt.x < mProfile->mBorderThickness || pt.y < mProfile->mBorderThickness)
 313      return this;
 314   if(pt.x >= getWidth() - mProfile->mBorderThickness - (mHasVScrollBar ? mScrollBarThickness : 0) ||
 315      pt.y >= getHeight() - mProfile->mBorderThickness - (mHasHScrollBar ? mScrollBarThickness : 0))
 316      return this;
 317   return Parent::findHitControl(pt, initialLayer);
 318}
 319
 320//-----------------------------------------------------------------------------
 321
 322void GuiScrollCtrl::computeSizes()
 323{
 324   S32 thickness = (mProfile ? mProfile->mBorderThickness : 1);
 325   Point2I borderExtent(thickness, thickness);
 326   mContentPos = borderExtent + mChildMargin;
 327   mContentExt = getExtent() - (mChildMargin * 2)
 328                                - (borderExtent * 2);
 329
 330   Point2I childLowerRight;
 331
 332   mHBarEnabled = false;
 333   mVBarEnabled = false;
 334   mHasVScrollBar = (mForceVScrollBar == ScrollBarAlwaysOn);
 335   mHasHScrollBar = (mForceHScrollBar == ScrollBarAlwaysOn);
 336
 337   setUpdate();
 338
 339   if (calcChildExtents())
 340   {
 341      childLowerRight = mChildPos + mChildExt;
 342
 343      if (mHasVScrollBar)
 344         mContentExt.x -= mScrollBarThickness;
 345      if (mHasHScrollBar)
 346         mContentExt.y -= mScrollBarThickness;
 347      if (mChildExt.x > mContentExt.x && (mForceHScrollBar == ScrollBarDynamic))
 348      {
 349         mHasHScrollBar = true;
 350         mContentExt.y -= mScrollBarThickness;
 351      }
 352      if (mChildExt.y > mContentExt.y && (mForceVScrollBar == ScrollBarDynamic))
 353      {
 354         mHasVScrollBar = true;
 355         mContentExt.x -= mScrollBarThickness;
 356
 357         // If Extent X Changed, check Horiz Scrollbar.
 358         if (mChildExt.x > mContentExt.x && !mHasHScrollBar && (mForceHScrollBar == ScrollBarDynamic))
 359         {
 360            mHasHScrollBar = true;
 361            mContentExt.y -= mScrollBarThickness;
 362         }
 363      }
 364      Point2I contentLowerRight = mContentPos + mContentExt;
 365
 366      // see if the child controls need to be repositioned (null space in control)
 367      Point2I delta(0,0);
 368
 369      if (mChildPos.x > mContentPos.x)
 370         delta.x = mContentPos.x - mChildPos.x;
 371      else if (contentLowerRight.x > childLowerRight.x)
 372      {
 373         S32 diff = contentLowerRight.x - childLowerRight.x;
 374         delta.x = getMin(mContentPos.x - mChildPos.x, diff);
 375      }
 376
 377      //reposition the children if the child extent > the scroll content extent
 378      if (mChildPos.y > mContentPos.y)
 379         delta.y = mContentPos.y - mChildPos.y;
 380      else if (contentLowerRight.y > childLowerRight.y)
 381      {
 382         S32 diff = contentLowerRight.y - childLowerRight.y;
 383         delta.y = getMin(mContentPos.y - mChildPos.y, diff);
 384      }
 385
 386      // apply the deltas to the children...
 387      if (delta.x || delta.y)
 388      {
 389         SimGroup::iterator i;
 390         for(i = begin(); i != end();i++)
 391         {
 392            GuiControl *ctrl = (GuiControl *) (*i);
 393            ctrl->setPosition( ctrl->getPosition() + delta );
 394         }
 395         mChildPos += delta;
 396         childLowerRight += delta;
 397      }
 398      // enable needed scroll bars
 399      if (mChildExt.x > mContentExt.x)
 400         mHBarEnabled = true;
 401      if (mChildExt.y > mContentExt.y)
 402         mVBarEnabled = true;
 403      mChildRelPos = mContentPos - mChildPos;
 404   }
 405
 406   // Prevent resizing our children from recalling this function!
 407   mIgnoreChildResized = true;
 408
 409   if ( mLockVertScroll )
 410   {
 411      // If vertical scroll is locked we size our child's height to our own
 412      SimGroup::iterator i;
 413      for(i = begin(); i != end();i++)
 414      {
 415         GuiControl *ctrl = (GuiControl *) (*i);
 416         ctrl->setHeight( mContentExt.y  );
 417      }
 418   }
 419
 420   if ( mLockHorizScroll )
 421   {
 422      // If horizontal scroll is locked we size our child's width to our own
 423      SimGroup::iterator i;
 424      for(i = begin(); i != end();i++)
 425      {
 426         GuiControl *ctrl = (GuiControl *) (*i);
 427         ctrl->setWidth( mContentExt.x  );
 428      }
 429   }
 430
 431   mIgnoreChildResized = false;
 432
 433   // build all the rectangles and such...
 434   calcScrollRects();
 435   calcThumbs();
 436}
 437
 438//-----------------------------------------------------------------------------
 439
 440void GuiScrollCtrl::calcScrollRects(void)
 441{
 442   S32 thickness = ( mProfile ? mProfile->mBorderThickness : 1 );
 443   if (mHasHScrollBar)
 444   {
 445      mLeftArrowRect.set(thickness,
 446                        getHeight() - thickness - mScrollBarThickness,
 447                        mScrollBarArrowBtnLength,
 448                        mScrollBarThickness);
 449
 450      mRightArrowRect.set(getWidth() - thickness - (mHasVScrollBar ? mScrollBarThickness - 1 : 0) - mScrollBarArrowBtnLength,
 451                        getHeight() - thickness - mScrollBarThickness,
 452                        mScrollBarArrowBtnLength,
 453                        mScrollBarThickness);
 454      mHTrackRect.set(mLeftArrowRect.point.x + mLeftArrowRect.extent.x,
 455                        mLeftArrowRect.point.y,
 456                        mRightArrowRect.point.x - (mLeftArrowRect.point.x + mLeftArrowRect.extent.x),
 457                        mScrollBarThickness);
 458   }
 459   if (mHasVScrollBar)
 460   {
 461      mUpArrowRect.set(getWidth() - thickness - mScrollBarThickness,
 462                        thickness,
 463                        mScrollBarThickness,
 464                        mScrollBarArrowBtnLength);
 465      mDownArrowRect.set(getWidth() - thickness - mScrollBarThickness,
 466                        getHeight() - thickness - (mHasHScrollBar ? mScrollBarThickness - 1 : 0) - mScrollBarArrowBtnLength,
 467                        mScrollBarThickness,
 468                        mScrollBarArrowBtnLength);
 469      mVTrackRect.set(mUpArrowRect.point.x,
 470                        mUpArrowRect.point.y + mUpArrowRect.extent.y,
 471                        mScrollBarThickness,
 472                        mDownArrowRect.point.y - (mUpArrowRect.point.y + mUpArrowRect.extent.y) );
 473   }
 474}
 475
 476//-----------------------------------------------------------------------------
 477
 478void GuiScrollCtrl::calcThumbs()
 479{
 480   if (mHBarEnabled && mChildExt.x > 0)
 481   {
 482      U32 trackSize = mHTrackRect.len_x();
 483
 484      if (mUseConstantHeightThumb)
 485         mHThumbSize = mBaseThumbSize;
 486      else
 487         mHThumbSize = getMax(mBaseThumbSize, ( S32 )mCeil( ( F32 )( mContentExt.x * trackSize) / ( F32 )mChildExt.x ) );
 488
 489      mHThumbPos = mHTrackRect.point.x + (mChildRelPos.x * (trackSize - mHThumbSize)) / (mChildExt.x - mContentExt.x);
 490   }
 491   if (mVBarEnabled && mChildExt.y > 0)
 492   {
 493      U32 trackSize = mVTrackRect.len_y();
 494
 495      if (mUseConstantHeightThumb)
 496         mVThumbSize = mBaseThumbSize;
 497      else
 498         mVThumbSize = getMax(mBaseThumbSize, ( S32 )mCeil( ( F32 )( mContentExt.y * trackSize ) / ( F32 )mChildExt.y ) );
 499
 500      mVThumbPos = mVTrackRect.point.y + (mChildRelPos.y * (trackSize - mVThumbSize)) / (mChildExt.y - mContentExt.y);
 501   }
 502}
 503
 504//-----------------------------------------------------------------------------
 505
 506void GuiScrollCtrl::scrollDelta(S32 deltaX, S32 deltaY)
 507{
 508   scrollTo(mChildRelPos.x + deltaX, mChildRelPos.y + deltaY);
 509
 510   onScroll_callback();
 511}
 512
 513//-----------------------------------------------------------------------------
 514
 515void GuiScrollCtrl::scrollDeltaAnimate(S32 x, S32 y)
 516{
 517   if ( !size() )
 518      return;
 519
 520   if ( mAnimating )
 521      mScrollTargetPos += Point2I( x, y );
 522   else
 523      mScrollTargetPos = mChildRelPos + Point2I( x, y );
 524
 525   setUpdate();
 526
 527   mScrollTargetPos.setMin( mChildExt - mContentExt );
 528   mScrollTargetPos.setMax( Point2I::Zero );
 529   
 530   mAnimating = true;
 531}
 532
 533//-----------------------------------------------------------------------------
 534
 535void GuiScrollCtrl::scrollTo(S32 x, S32 y)
 536{
 537   if( !size() )
 538      return;
 539
 540   if ( x == mChildRelPos.x && y == mChildRelPos.y )
 541      return;
 542
 543   setUpdate();
 544   if (x > mChildExt.x - mContentExt.x)
 545      x = mChildExt.x - mContentExt.x;
 546   if (x < 0)
 547      x = 0;
 548
 549   if (y > mChildExt.y - mContentExt.y)
 550      y = mChildExt.y - mContentExt.y;
 551   if (y < 0)
 552      y = 0;
 553
 554   Point2I delta(x - mChildRelPos.x, y - mChildRelPos.y);
 555   mChildRelPos += delta;
 556   mChildPos -= delta;
 557
 558   for(SimSet::iterator i = begin(); i != end();i++)
 559   {
 560      GuiControl *ctrl = (GuiControl *) (*i);
 561      ctrl->setPosition( ctrl->getPosition() - delta );
 562   }
 563   calcThumbs();
 564
 565   onScroll_callback();
 566}
 567
 568//-----------------------------------------------------------------------------
 569
 570void GuiScrollCtrl::scrollToObject(GuiControl *targetControl)
 571{
 572   bool        isValidChild     = false;
 573   Point2I     relativePosition = targetControl->getPosition();
 574   GuiControl* parentControl    = targetControl->getParent();
 575   while (parentControl)
 576   {
 577      GuiScrollCtrl* scrollControl = dynamic_cast<GuiScrollCtrl*>(parentControl);
 578      if (scrollControl == this)
 579      {
 580         relativePosition += scrollControl->getChildRelPos();
 581         isValidChild      = true;
 582         break;
 583      }
 584
 585      relativePosition += parentControl->getPosition();
 586      parentControl     = parentControl->getParent();
 587   }
 588
 589   if (isValidChild)
 590   {
 591      scrollRectVisible(RectI(relativePosition, targetControl->getExtent()));
 592   }
 593   else
 594   {
 595      Con::errorf("GuiScrollCtrl::scrollToObject() - Specified object is not a child of this scroll control (%d)!", targetControl->getId());
 596   }
 597}
 598
 599//-----------------------------------------------------------------------------
 600
 601GuiScrollCtrl::Region GuiScrollCtrl::findHitRegion(const Point2I &pt)
 602{
 603   if (mVBarEnabled && mHasVScrollBar)
 604   {
 605      if (mUpArrowRect.pointInRect(pt))
 606         return UpArrow;
 607      else if (mDownArrowRect.pointInRect(pt))
 608         return DownArrow;
 609      else if (mVTrackRect.pointInRect(pt))
 610      {
 611         if (pt.y < mVThumbPos)
 612            return UpPage;
 613         else if (pt.y < mVThumbPos + mVThumbSize)
 614            return VertThumb;
 615         else
 616            return DownPage;
 617      }
 618   }
 619   if (mHBarEnabled && mHasHScrollBar)
 620   {
 621      if (mLeftArrowRect.pointInRect(pt))
 622         return LeftArrow;
 623      else if (mRightArrowRect.pointInRect(pt))
 624         return RightArrow;
 625      else if (mHTrackRect.pointInRect(pt))
 626      {
 627         if (pt.x < mHThumbPos)
 628            return LeftPage;
 629         else if (pt.x < mHThumbPos + mHThumbSize)
 630            return HorizThumb;
 631         else
 632            return RightPage;
 633      }
 634   }
 635   return None;
 636}
 637
 638//-----------------------------------------------------------------------------
 639
 640bool GuiScrollCtrl::wantsTabListMembership()
 641{
 642   return true;
 643}
 644
 645//-----------------------------------------------------------------------------
 646
 647bool GuiScrollCtrl::loseFirstResponder()
 648{
 649   setUpdate();
 650   return true;
 651}
 652
 653//-----------------------------------------------------------------------------
 654
 655bool GuiScrollCtrl::becomeFirstResponder()
 656{
 657   setUpdate();
 658   return mWillFirstRespond;
 659}
 660
 661//-----------------------------------------------------------------------------
 662
 663bool GuiScrollCtrl::onKeyDown(const GuiEvent &event)
 664{
 665   if (mWillFirstRespond)
 666   {
 667      switch (event.keyCode)
 668      {
 669         case KEY_RIGHT:
 670            scrollByRegion(RightArrow);
 671            return true;
 672
 673         case KEY_LEFT:
 674            scrollByRegion(LeftArrow);
 675            return true;
 676
 677         case KEY_DOWN:
 678            scrollByRegion(DownArrow);
 679            return true;
 680
 681         case KEY_UP:
 682            scrollByRegion(UpArrow);
 683            return true;
 684
 685         case KEY_PAGE_UP:
 686            scrollByRegion(UpPage);
 687            return true;
 688
 689         case KEY_PAGE_DOWN:
 690            scrollByRegion(DownPage);
 691            return true;
 692            
 693         default:
 694            break;
 695      }
 696   }
 697   return Parent::onKeyDown(event);
 698}
 699
 700//-----------------------------------------------------------------------------
 701
 702void GuiScrollCtrl::_onMouseDown( const GuiEvent &event, bool lockMouse )
 703{
 704   if( lockMouse )
 705   {
 706      mouseLock();
 707      mStateDepressed = true;
 708   }
 709
 710   setUpdate();
 711
 712   Point2I curMousePos = globalToLocalCoord(event.mousePoint);
 713   mHitRegion = findHitRegion(curMousePos);
 714
 715   // Set a 0.5 second delay before we start scrolling
 716   mLastUpdated = Platform::getVirtualMilliseconds() + 500;
 717
 718   scrollByRegion(mHitRegion);
 719
 720   if (mHitRegion == VertThumb)
 721   {
 722      mChildRelPosAnchor = mChildRelPos;
 723      mThumbMouseDelta = curMousePos.y - mVThumbPos;
 724   }
 725   else if (mHitRegion == HorizThumb)
 726   {
 727      mChildRelPosAnchor = mChildRelPos;
 728      mThumbMouseDelta = curMousePos.x - mHThumbPos;
 729   }
 730}
 731
 732//-----------------------------------------------------------------------------
 733
 734void GuiScrollCtrl::onMouseDown(const GuiEvent &event)
 735{
 736   _onMouseDown( event, true );
 737}
 738
 739//-----------------------------------------------------------------------------
 740
 741bool GuiScrollCtrl::onMouseDownEditor( const GuiEvent& event, Point2I offset )
 742{
 743   // If ALT is pressed while clicking on a horizontal or vertical scrollbar,
 744   // do a scroll.
 745   
 746   if( event.modifier & SI_PRIMARY_ALT )
 747   {
 748      Region hitRegion = findHitRegion( globalToLocalCoord( event.mousePoint ) );
 749      if( hitRegion != None )
 750      {
 751         _onMouseDown( event, false );
 752         return true;
 753      }
 754   }
 755   
 756   return false;
 757}
 758
 759//-----------------------------------------------------------------------------
 760
 761void GuiScrollCtrl::onMouseUp(const GuiEvent &)
 762{
 763   mouseUnlock();
 764
 765   setUpdate();
 766
 767   mHitRegion = None;
 768   mStateDepressed = false;
 769}
 770
 771//-----------------------------------------------------------------------------
 772
 773void GuiScrollCtrl::onMouseDragged(const GuiEvent &event)
 774{
 775   Point2I curMousePos = globalToLocalCoord(event.mousePoint);
 776   setUpdate();
 777
 778   if ( (mHitRegion != VertThumb) && (mHitRegion != HorizThumb) )
 779   {
 780      Region hit = findHitRegion(curMousePos);
 781      if (hit != mHitRegion)
 782         mStateDepressed = false;
 783      else
 784         mStateDepressed = true;
 785      return;
 786   }
 787
 788   // ok... if the mouse is 'near' the scroll bar, scroll with it
 789   // otherwise, snap back to the previous position.
 790
 791   if (mHitRegion == VertThumb)
 792   {
 793      if (curMousePos.x >= mVTrackRect.point.x - mScrollBarDragTolerance &&
 794         curMousePos.x <= mVTrackRect.point.x + mVTrackRect.extent.x - 1 + mScrollBarDragTolerance &&
 795         curMousePos.y >= mVTrackRect.point.y - mScrollBarDragTolerance &&
 796         curMousePos.y <= mVTrackRect.point.y + mVTrackRect.extent.y - 1 + mScrollBarDragTolerance)
 797      {
 798         S32 newVThumbPos = curMousePos.y - mThumbMouseDelta;
 799         if(newVThumbPos != mVThumbPos)
 800         {
 801            S32 newVPos = (newVThumbPos - mVTrackRect.point.y) *
 802                          (mChildExt.y - mContentExt.y) /
 803                          (mVTrackRect.extent.y - mVThumbSize);
 804
 805            scrollTo(mChildRelPosAnchor.x, newVPos);
 806         }
 807      }
 808      else
 809         scrollTo(mChildRelPosAnchor.x, mChildRelPosAnchor.y);
 810   }
 811   else if (mHitRegion == HorizThumb)
 812   {
 813      if (curMousePos.x >= mHTrackRect.point.x - mScrollBarDragTolerance &&
 814         curMousePos.x <= mHTrackRect.point.x + mHTrackRect.extent.x - 1 + mScrollBarDragTolerance &&
 815         curMousePos.y >= mHTrackRect.point.y - mScrollBarDragTolerance &&
 816         curMousePos.y <= mHTrackRect.point.y + mHTrackRect.extent.y - 1 + mScrollBarDragTolerance)
 817      {
 818         S32 newHThumbPos = curMousePos.x - mThumbMouseDelta;
 819         if(newHThumbPos != mHThumbPos)
 820         {
 821            S32 newHPos = (newHThumbPos - mHTrackRect.point.x) *
 822                          (mChildExt.x - mContentExt.x) /
 823                          (mHTrackRect.extent.x - mHThumbSize);
 824
 825            scrollTo(newHPos, mChildRelPosAnchor.y);
 826         }
 827      }
 828      else
 829         scrollTo(mChildRelPosAnchor.x, mChildRelPosAnchor.y);
 830   }
 831}
 832
 833//-----------------------------------------------------------------------------
 834
 835bool GuiScrollCtrl::onMouseWheelUp(const GuiEvent &event)
 836{
 837   if ( !mAwake || !mVisible )
 838      return false;
 839
 840   scrollByMouseWheel( event );
 841
 842   return true;
 843}
 844
 845//-----------------------------------------------------------------------------
 846
 847bool GuiScrollCtrl::onMouseWheelDown(const GuiEvent &event)
 848{
 849   if ( !mAwake || !mVisible )
 850      return false;
 851
 852   scrollByMouseWheel( event );   
 853
 854   return true;
 855}
 856
 857//-----------------------------------------------------------------------------
 858
 859void GuiScrollCtrl::updateChildMousePos()
 860{      
 861   // We pass a fake GuiEvent to child controls onMouseMove
 862   // since although the mouse has not moved 'they' have.
 863   //
 864   // Its possible this could cause problems if a GuiControl
 865   // responds to more than just the mouse position in the onMouseMove
 866   // event, like for example doing something different depending on
 867   // a modifier key, which we aren't filling in to the structure!
 868
 869   GuiEvent event;
 870   event.mousePoint = getRoot()->getCursorPos();
 871
 872   iterator itr;
 873   for ( itr = begin(); itr != end(); itr++ )
 874   {
 875      GuiControl *child = static_cast<GuiControl*>( *itr );
 876      child->onMouseMove( event );
 877   }
 878}
 879
 880//-----------------------------------------------------------------------------
 881
 882void GuiScrollCtrl::onPreRender()
 883{
 884   Parent::onPreRender();
 885
 886   S32 currentTime = Platform::getVirtualMilliseconds();
 887   S32 deltaMs = currentTime - mLastPreRender;
 888   mLastPreRender = currentTime;
 889
 890   // Update mouse-wheel scroll animation if we are currently doing one...
 891
 892   if ( mAnimating )
 893   {            
 894      //U32 frames = Con::getIntVariable( "$frames", 0 );
 895      //frames++;
 896      //Con::setIntVariable( "$frames", frames );
 897
 898      F32 deltaTicks = deltaMs / 32.0f;
 899
 900      if ( mScrollAnimSpeed <= 0 )
 901      {
 902         scrollTo( mScrollTargetPos.x, mScrollTargetPos.y );
 903      }      
 904      else
 905      {
 906         S32 maxPixels = deltaTicks * mScrollAnimSpeed;
 907
 908         Point2I toTarget = mScrollTargetPos - mChildRelPos;
 909         S32 signx = toTarget.x > 0 ? 1 : -1;
 910         S32 signy = toTarget.y > 0 ? 1 : -1;
 911
 912         S32 deltaX = getMin( mAbs(toTarget.x), maxPixels ) * signx;
 913         S32 deltaY = getMin( mAbs(toTarget.y), maxPixels ) * signy;
 914
 915         scrollDelta( deltaX, deltaY );
 916      }
 917
 918      if ( mChildRelPos == mScrollTargetPos )   
 919      {
 920         //Con::printf( "Animated Frames : %d", frames );
 921         //Con::setIntVariable( "$frames", 0 );
 922         mAnimating = false;
 923      }
 924
 925      updateChildMousePos();
 926   }
 927
 928   // Now scroll in response to a 'depressed state' if appropriate...
 929
 930   // Short circuit if not depressed to save cycles
 931   if( mStateDepressed != true )
 932      return;
 933   
 934   //default to one second, though it shouldn't be necessary
 935   U32 timeThreshold = 1000;
 936
 937   // We don't want to scroll by pages at an interval the same as when we're scrolling
 938   // using the arrow buttons, so adjust accordingly.
 939   switch( mHitRegion )
 940   {
 941   case UpPage:
 942   case DownPage:
 943   case LeftPage:
 944   case RightPage:
 945      timeThreshold = 200;
 946      break;
 947   case UpArrow:
 948   case DownArrow:
 949   case LeftArrow:
 950   case RightArrow:
 951      timeThreshold = 20;
 952      break;
 953   default:
 954      // Neither a button or a page, don't scroll (shouldn't get here)
 955      return;
 956      break;
 957   };
 958
 959   S32 timeElapsed = Platform::getVirtualMilliseconds() - mLastUpdated;
 960
 961   if ( ( timeElapsed > 0 ) && ( timeElapsed > timeThreshold ) )
 962   {
 963      mLastUpdated = Platform::getVirtualMilliseconds();
 964      scrollByRegion(mHitRegion);
 965   }
 966}
 967
 968//-----------------------------------------------------------------------------
 969
 970void GuiScrollCtrl::scrollByRegion(Region reg)
 971{
 972   setUpdate();
 973   if(!size())
 974      return;
 975   GuiControl *content = (GuiControl *) front();
 976   U32 rowHeight, columnWidth;
 977   U32 pageHeight, pageWidth;
 978
 979   content->getScrollLineSizes(&rowHeight, &columnWidth);
 980
 981   if(rowHeight >= mContentExt.y)
 982      pageHeight = 1;
 983   else
 984      pageHeight = mContentExt.y - rowHeight;
 985
 986   if(columnWidth >= mContentExt.x)
 987      pageWidth = 1;
 988   else
 989      pageWidth = mContentExt.x - columnWidth;
 990
 991   if (mVBarEnabled)
 992   {
 993      switch(reg)
 994      {
 995         case UpPage:
 996            scrollDelta(0, -(S32)pageHeight);
 997            break;
 998         case DownPage:
 999            scrollDelta(0, pageHeight);
1000            break;
1001         case UpArrow:
1002            scrollDelta(0, -(S32)rowHeight);
1003            break;
1004         case DownArrow:
1005            scrollDelta(0, rowHeight);
1006            break;
1007         default:
1008            break;
1009      }
1010   }
1011
1012   if (mHBarEnabled)
1013   {
1014      switch(reg)
1015      {
1016         case LeftPage:
1017            scrollDelta(-(S32)pageWidth, 0);
1018            break;
1019         case RightPage:
1020            scrollDelta(pageWidth, 0);
1021            break;
1022         case LeftArrow:
1023            scrollDelta(-(S32)columnWidth, 0);
1024            break;
1025         case RightArrow:
1026            scrollDelta(columnWidth, 0);
1027            break;
1028         default:
1029            break;
1030      }
1031   }
1032}
1033
1034//-----------------------------------------------------------------------------
1035
1036void GuiScrollCtrl::scrollByMouseWheel( const GuiEvent &event )
1037{
1038   setUpdate();
1039   if ( !size() )
1040      return;
1041
1042   if( event.mouseAxis == 1 )
1043      scrollDeltaAnimate( 0, -event.fval );
1044   else
1045      scrollDeltaAnimate( -event.fval, 0 );
1046}
1047
1048//-----------------------------------------------------------------------------
1049
1050void GuiScrollCtrl::onRender(Point2I offset, const RectI &updateRect)
1051{
1052   // draw content controls
1053   // create a rect to intersect with the updateRect
1054   RectI contentRect(mContentPos.x + offset.x, mContentPos.y + offset.y, mContentExt.x, mContentExt.y);
1055   contentRect.intersect(updateRect);
1056   
1057   // Always call parent
1058   Parent::onRender(offset, contentRect);
1059   
1060   if( mTextureObject )
1061   {
1062      // Reset the ClipRect as the parent call can modify it when rendering
1063      // the child controls
1064      GFX->setClipRect( updateRect );
1065
1066      //draw the scroll corner
1067      if (mHasVScrollBar && mHasHScrollBar)
1068         drawScrollCorner(offset);
1069
1070      // draw scroll bars
1071      if (mHasVScrollBar)
1072         drawVScrollBar(offset);
1073
1074      if (mHasHScrollBar)
1075         drawHScrollBar(offset);
1076   }
1077}
1078
1079//-----------------------------------------------------------------------------
1080
1081void GuiScrollCtrl::drawBorder( const Point2I &offset, bool /*isFirstResponder*/ )
1082{
1083}
1084
1085//-----------------------------------------------------------------------------
1086
1087void GuiScrollCtrl::drawVScrollBar(const Point2I &offset)
1088{
1089    if ( mTextureObject.isNull() )
1090    {
1091        return;
1092    }
1093
1094    // Start Point.
1095    Point2I pos = ( offset + mUpArrowRect.point );
1096
1097    // Up Arrow.
1098    S32 upArrowBitmap = ( BmpStates * BmpUp );
1099    if ( !mVBarEnabled )
1100    {
1101        upArrowBitmap += BmpDisabled;
1102    }
1103    else if ( mHitRegion == UpArrow && mStateDepressed )
1104    {
1105        upArrowBitmap += BmpHilite;
1106    }
1107
1108    // Render Up Arrow.
1109    GFXDrawUtil* drawUtil = GFX->getDrawUtil();
1110    drawUtil->clearBitmapModulation();
1111    drawUtil->drawBitmapSR(mTextureObject, pos, mBitmapBounds[upArrowBitmap]);
1112
1113    // Update Pos.
1114    pos.y += mBitmapBounds[upArrowBitmap].extent.y;
1115
1116    // Track.
1117    S32 trackBitmap = ( BmpStates * BmpVPage );
1118    if ( !mVBarEnabled )
1119    {
1120        trackBitmap += BmpDisabled;
1121    }
1122    else if ( mHitRegion == DownPage && mStateDepressed )
1123    {
1124        trackBitmap += BmpHilite;
1125    }
1126
1127    // Determine the Track Rect.
1128    RectI trackRect;
1129    trackRect.point    = pos;
1130    trackRect.extent.x = mBitmapBounds[trackBitmap].extent.x;
1131    trackRect.extent.y = ( offset.y + mDownArrowRect.point.y ) - pos.y;
1132
1133    // Render Track?
1134    if ( trackRect.extent.y > 0 )
1135    {
1136        // Render Track.
1137       drawUtil->clearBitmapModulation();
1138       drawUtil->drawBitmapStretchSR(mTextureObject, trackRect, mBitmapBounds[trackBitmap]);
1139    }
1140
1141    // Update Pos.
1142    pos.y += trackRect.extent.y;
1143
1144    // Down Arrow.
1145    S32 downArrowBitmap = ( BmpStates * BmpDown );
1146    if ( !mVBarEnabled )
1147    {
1148        downArrowBitmap += BmpDisabled;
1149    }
1150    else if ( mHitRegion == DownArrow && mStateDepressed )
1151    {
1152        downArrowBitmap += BmpHilite;
1153    }
1154
1155    // Render Down Arrow.
1156    drawUtil->clearBitmapModulation();
1157    drawUtil->drawBitmapSR(mTextureObject, pos, mBitmapBounds[downArrowBitmap]);
1158
1159    // Render the Thumb?
1160    if ( !mVBarEnabled )
1161    {
1162        // Nope.
1163        return;
1164    }
1165
1166    // Reset the Pos.
1167    pos.y = ( offset.y + mVThumbPos );
1168
1169    // Determine the Bitmaps.
1170    S32 thumbBitmapTop    = ( BmpStates * BmpVThumbTopCap );
1171    S32 thumbBitmapMiddle = ( BmpStates * BmpVThumb );
1172    S32 thumbBitmapBottom = ( BmpStates * BmpVThumbBottomCap );
1173
1174    if ( mHitRegion == VertThumb && mStateDepressed )
1175    {
1176        thumbBitmapTop    += BmpHilite;
1177        thumbBitmapMiddle += BmpHilite;
1178        thumbBitmapBottom += BmpHilite;
1179    }
1180
1181    // Render Thumb Top.
1182    drawUtil->clearBitmapModulation();
1183    drawUtil->drawBitmapSR(mTextureObject, pos, mBitmapBounds[thumbBitmapTop]);
1184
1185    // Update Pos.
1186    pos.y += mBitmapBounds[thumbBitmapTop].extent.y;
1187
1188    // Determine the Thumb Rect.
1189    RectI thumbRect;
1190    thumbRect.point    = pos;
1191    thumbRect.extent.x = mBitmapBounds[thumbBitmapMiddle].extent.x;
1192    thumbRect.extent.y = mVThumbSize - ( mBitmapBounds[thumbBitmapTop].extent.y + mBitmapBounds[thumbBitmapBottom].extent.y );
1193
1194    // Render Thumb?
1195    if ( thumbRect.extent.y > 0 )
1196    {
1197        // Render Track.
1198       drawUtil->clearBitmapModulation();
1199       drawUtil->drawBitmapStretchSR(mTextureObject, thumbRect, mBitmapBounds[thumbBitmapMiddle]);
1200    }
1201
1202    // Update Pos.
1203    pos.y += thumbRect.extent.y;
1204
1205    // Render the Thumb Bottom.
1206    drawUtil->clearBitmapModulation();
1207    drawUtil->drawBitmapSR(mTextureObject, pos, mBitmapBounds[thumbBitmapBottom]);
1208}
1209
1210//-----------------------------------------------------------------------------
1211
1212void GuiScrollCtrl::drawHScrollBar(const Point2I &offset)
1213{
1214    if ( mTextureObject.isNull() )
1215    {
1216        return;
1217    }
1218
1219    // Start Point.
1220    Point2I pos = ( offset + mLeftArrowRect.point );
1221
1222    // Left Arrow.
1223    S32 leftArrowBitmap = ( BmpStates * BmpLeft );
1224    if ( !mHBarEnabled )
1225    {
1226        leftArrowBitmap += BmpDisabled;
1227    }
1228    else if ( mHitRegion == LeftArrow && mStateDepressed )
1229    {
1230        leftArrowBitmap += BmpHilite;
1231    }
1232
1233    // Render Up Arrow.
1234    GFXDrawUtil* drawUtil = GFX->getDrawUtil();
1235    drawUtil->clearBitmapModulation();
1236    drawUtil->drawBitmapSR(mTextureObject, pos, mBitmapBounds[leftArrowBitmap]);
1237
1238    // Update Pos.
1239    pos.x += mBitmapBounds[leftArrowBitmap].extent.x;
1240
1241    // Track.
1242    S32 trackBitmap = ( BmpStates * BmpHPage );
1243    if ( !mHBarEnabled )
1244    {
1245        trackBitmap += BmpDisabled;
1246    }
1247    else if ( mHitRegion == LeftPage && mStateDepressed )
1248    {
1249        trackBitmap += BmpHilite;
1250    }
1251
1252    // Determine the Track Rect.
1253    RectI trackRect;
1254    trackRect.point    = pos;
1255    trackRect.extent.x = ( offset.x + mRightArrowRect.point.x ) - pos.x;
1256    trackRect.extent.y = mBitmapBounds[trackBitmap].extent.y;
1257
1258    // Render Track?
1259    if ( trackRect.extent.x > 0 )
1260    {
1261        // Render Track.
1262       drawUtil->clearBitmapModulation();
1263       drawUtil->drawBitmapStretchSR(mTextureObject, trackRect, mBitmapBounds[trackBitmap]);
1264    }
1265
1266    // Update Pos.
1267    pos.x += trackRect.extent.x;
1268
1269    // Right Arrow.
1270    S32 rightArrowBitmap = ( BmpStates * BmpRight );
1271    if ( !mHBarEnabled )
1272    {
1273        rightArrowBitmap += BmpDisabled;
1274    }
1275    else if ( mHitRegion == RightArrow && mStateDepressed )
1276    {
1277        rightArrowBitmap += BmpHilite;
1278    }
1279
1280    // Render Right Arrow.
1281    drawUtil->clearBitmapModulation();
1282    drawUtil->drawBitmapSR(mTextureObject, pos, mBitmapBounds[rightArrowBitmap]);
1283
1284    // Render the Thumb?
1285    if ( !mHBarEnabled )
1286    {
1287        // Nope.
1288        return;
1289    }
1290
1291    // Reset the Pos.
1292    pos.x = ( offset.x + mHThumbPos );
1293
1294    // Determine the Bitmaps.
1295    S32 thumbBitmapLeft   = ( BmpStates * BmpHThumbLeftCap );
1296    S32 thumbBitmapMiddle = ( BmpStates * BmpHThumb );
1297    S32 thumbBitmapRight  = ( BmpStates * BmpHThumbRightCap );
1298
1299    if ( mHitRegion == HorizThumb && mStateDepressed )
1300    {
1301        thumbBitmapLeft   += BmpHilite;
1302        thumbBitmapMiddle += BmpHilite;
1303        thumbBitmapRight  += BmpHilite;
1304    }
1305
1306    // Render Thumb Left.
1307    drawUtil->clearBitmapModulation();
1308    drawUtil->drawBitmapSR(mTextureObject, pos, mBitmapBounds[thumbBitmapLeft]);
1309
1310    // Update Pos.
1311    pos.x += mBitmapBounds[thumbBitmapLeft].extent.x;
1312
1313    // Determine the Thumb Rect.
1314    RectI thumbRect;
1315    thumbRect.point    = pos;
1316    thumbRect.extent.x = mHThumbSize - ( mBitmapBounds[thumbBitmapLeft].extent.x + mBitmapBounds[thumbBitmapRight].extent.x );
1317    thumbRect.extent.y = mBitmapBounds[thumbBitmapMiddle].extent.y;
1318
1319    // Render Thumb?
1320    if ( thumbRect.extent.x > 0 )
1321    {
1322        // Render Track.
1323       drawUtil->clearBitmapModulation();
1324       drawUtil->drawBitmapStretchSR(mTextureObject, thumbRect, mBitmapBounds[thumbBitmapMiddle]);
1325    }
1326
1327    // Update Pos.
1328    pos.x += thumbRect.extent.x;
1329
1330    // Render the Thumb Bottom.
1331    drawUtil->clearBitmapModulation();
1332    drawUtil->drawBitmapSR(mTextureObject, pos, mBitmapBounds[thumbBitmapRight]);
1333}
1334
1335//-----------------------------------------------------------------------------
1336
1337void GuiScrollCtrl::drawScrollCorner(const Point2I &offset)
1338{
1339   Point2I pos = offset;
1340   pos.x += mRightArrowRect.point.x + mRightArrowRect.extent.x - 1;
1341   pos.y += mRightArrowRect.point.y;
1342   GFX->getDrawUtil()->clearBitmapModulation();
1343   GFX->getDrawUtil()->drawBitmapSR(mTextureObject, pos, mBitmapBounds[BmpStates * BmpResize]);
1344}
1345
1346//-----------------------------------------------------------------------------
1347
1348void GuiScrollCtrl::autoScroll(Region reg)
1349{
1350   scrollByRegion(reg);
1351}
1352
1353//=============================================================================
1354//    API.
1355//=============================================================================
1356// MARK: ---- API ----
1357
1358//-----------------------------------------------------------------------------
1359
1360DefineEngineMethod( GuiScrollCtrl, scrollToTop, void, (),,
1361   "Scroll all the way to the top of the vertical and left of the horizontal scrollbar." )
1362{
1363   object->scrollTo( 0, 0 );
1364}
1365
1366//-----------------------------------------------------------------------------
1367
1368DefineEngineMethod( GuiScrollCtrl, scrollToBottom, void, (),,
1369   "Scroll all the way to the bottom of the vertical scrollbar and the left of the horizontal bar." )
1370{
1371   object->scrollTo( 0, 0x7FFFFFFF );
1372}
1373
1374//-----------------------------------------------------------------------------
1375
1376DefineEngineMethod( GuiScrollCtrl, setScrollPosition, void, ( S32 x, S32 y ),,
1377   "Set the position of the scrolled content.\n\n"
1378   "@param x Position on X axis.\n"
1379   "@param y Position on y axis.\n" )
1380{
1381   object->scrollTo( x, y );
1382}
1383
1384//-----------------------------------------------------------------------------
1385
1386DefineEngineMethod( GuiScrollCtrl, scrollToObject, void, ( GuiControl* control ),,
1387   "Scroll the control so that the given child @a control is visible.\n\n"
1388   "@param control A child control." )
1389{
1390   if( control )
1391      object->scrollToObject( control );
1392}
1393
1394//-----------------------------------------------------------------------------
1395
1396DefineEngineMethod( GuiScrollCtrl, getScrollPosition, Point2I, (),,
1397   "Get the current coordinates of the scrolled content.\n\n"
1398   "@return The current position of the scrolled content." )
1399{
1400   return object->getChildRelPos();
1401}
1402
1403//-----------------------------------------------------------------------------
1404
1405DefineEngineMethod( GuiScrollCtrl, getScrollPositionX, S32, (),,
1406   "Get the current X coordinate of the scrolled content.\n\n"
1407   "@return The current X coordinate of the scrolled content." )
1408{
1409   return object->getChildRelPos().x;
1410}
1411
1412//-----------------------------------------------------------------------------
1413
1414DefineEngineMethod( GuiScrollCtrl, getScrollPositionY, S32, (),,
1415   "Get the current Y coordinate of the scrolled content."
1416   "@return The current Y coordinate of the scrolled content." )
1417{
1418   return object->getChildRelPos().y;
1419}
1420
1421//-----------------------------------------------------------------------------
1422
1423DefineEngineMethod( GuiScrollCtrl, computeSizes, void, (),,
1424   "Refresh sizing and positioning of child controls." )
1425{
1426   object->computeSizes();
1427}
1428