guiRolloutCtrl.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "gui/containers/guiRolloutCtrl.h"
  23. #include "gui/containers/guiScrollCtrl.h"
  24. //////////////////////////////////////////////////////////////////////////
  25. // GuiRolloutCtrl
  26. //////////////////////////////////////////////////////////////////////////
  27. IMPLEMENT_CONOBJECT(GuiRolloutCtrl);
  28. GuiRolloutCtrl::GuiRolloutCtrl()
  29. {
  30. mBounds.set(0,0,200,20);
  31. mExpanded.set(0,0,200,60);
  32. mCaption = StringTable->EmptyString;
  33. mIsExpanded = true;
  34. mIsAnimating = false;
  35. mCollapsing = false;
  36. mAnimateDestHeight = 40;
  37. mAnimateStep = 1;
  38. mCanSave = false;
  39. mDefaultHeight = 40;
  40. mMargin.set( 2,2 );
  41. mIsContainer = true;
  42. mCanCollapse = true;
  43. // Make sure we receive our ticks.
  44. setProcessTicks();
  45. }
  46. GuiRolloutCtrl::~GuiRolloutCtrl()
  47. {
  48. }
  49. //////////////////////////////////////////////////////////////////////////
  50. // Persistence
  51. //////////////////////////////////////////////////////////////////////////
  52. void GuiRolloutCtrl::initPersistFields()
  53. {
  54. Parent::initPersistFields();
  55. addField("Caption", TypeCaseString, Offset(mCaption, GuiRolloutCtrl));
  56. addField("Margin", TypePoint2I, Offset(mMargin, GuiRolloutCtrl));
  57. addField("DefaultHeight", TypeS32, Offset(mDefaultHeight, GuiRolloutCtrl));
  58. addProtectedField( "Collapsed", TypeBool, Offset( mIsExpanded, GuiRolloutCtrl), &setCollapsed, &defaultProtectedGetFn, "" );
  59. addField("ClickCollapse", TypeBool, Offset(mCanCollapse, GuiRolloutCtrl));
  60. }
  61. //////////////////////////////////////////////////////////////////////////
  62. // Scene Events
  63. //////////////////////////////////////////////////////////////////////////
  64. bool GuiRolloutCtrl::onAdd()
  65. {
  66. if( !Parent::onAdd() )
  67. return false;
  68. mHasTexture = ( mProfile ? mProfile->constructBitmapArray() : false );
  69. if( mHasTexture )
  70. mBitmapBounds = mProfile->mBitmapArrayRects.address();
  71. // Calculate Heights for this control
  72. calculateHeights();
  73. return true;
  74. }
  75. bool GuiRolloutCtrl::onWake()
  76. {
  77. if (! Parent::onWake())
  78. return false;
  79. if( !mIsAnimating && mIsExpanded )
  80. sizeToContents();
  81. return true;
  82. }
  83. void GuiRolloutCtrl::addObject( SimObject *obj )
  84. {
  85. // Call Parent.
  86. Parent::addObject( obj );
  87. sizeToContents();
  88. }
  89. void GuiRolloutCtrl::removeObject( SimObject *obj )
  90. {
  91. // Call Parent.
  92. Parent::removeObject( obj );
  93. // Recalculate our rectangles.
  94. calculateHeights();
  95. }
  96. //////////////////////////////////////////////////////////////////////////
  97. // Mouse Events
  98. //////////////////////////////////////////////////////////////////////////
  99. void GuiRolloutCtrl::onMouseDown( const GuiEvent &event )
  100. {
  101. mouseLock();
  102. }
  103. void GuiRolloutCtrl::onMouseUp( const GuiEvent &event )
  104. {
  105. Point2I localPoint = globalToLocalCoord( event.mousePoint );
  106. if( mCanCollapse && mHeader.pointInRect( localPoint ) && !mIsAnimating && isMouseLocked() )
  107. {
  108. if( !mIsExpanded )
  109. animateTo( mExpanded.extent.y );
  110. else
  111. animateTo( mHeader.extent.y );
  112. }
  113. if( isMouseLocked() )
  114. mouseUnlock();
  115. }
  116. //////////////////////////////////////////////////////////////////////////
  117. // Control Sizing Helpers
  118. //////////////////////////////////////////////////////////////////////////
  119. void GuiRolloutCtrl::calculateHeights()
  120. {
  121. S32 barHeight = 20;
  122. if( mHasTexture && mProfile->mBitmapArrayRects.size() >= NumBitmaps )
  123. {
  124. // Store Header Rectangle
  125. mHeader.set( 0, 0, mBounds.extent.x, mProfile->mBitmapArrayRects[ CollapsedCenter ].extent.y );
  126. // Bottom Bar Max
  127. barHeight = mProfile->mBitmapArrayRects[ BottomLeftHeader ].extent.y
  128. + mProfile->mBitmapArrayRects[ TopLeftHeader ].extent.y;
  129. }
  130. else
  131. {
  132. mHeader.set( 0, 0, mBounds.extent.x, barHeight );
  133. }
  134. GuiControl *content = dynamic_cast<GuiControl*>( at(0) );
  135. if( content != NULL )
  136. mExpanded.set( 0, 0, mBounds.extent.x , barHeight + content->getHeight() + (mMargin.y * 2) );
  137. else
  138. mExpanded.set( 0, 0, mBounds.extent.x, barHeight + mDefaultHeight );
  139. }
  140. void GuiRolloutCtrl::resize( const Point2I &newPosition, const Point2I &newExtent )
  141. {
  142. Parent::resize( newPosition, newExtent );
  143. // Recalculate Heights and resize ourself appropriately.
  144. calculateHeights();
  145. GuiControl *content = dynamic_cast<GuiControl*>( at(0) );
  146. // Size Content Properly?!
  147. if( content != NULL )
  148. {
  149. mChildRect.set( mMargin.x, mHeader.extent.y + mMargin.y , mBounds.extent.x - (mMargin.x * 2), content->mBounds.extent.y - ( mMargin.y * 2 ) );
  150. content->resize( mChildRect.point, mChildRect.extent );
  151. }
  152. }
  153. void GuiRolloutCtrl::sizeToContents()
  154. {
  155. calculateHeights();
  156. // Set destination height
  157. if( size() > 0 )
  158. instantExpand();
  159. else
  160. instantCollapse();
  161. }
  162. void GuiRolloutCtrl::instantExpand()
  163. {
  164. mAnimateDestHeight = mExpanded.extent.y;
  165. mCollapsing = false;
  166. mIsExpanded = true;
  167. mIsAnimating = false;
  168. resize( mBounds.point + mExpanded.point, mExpanded.extent );
  169. }
  170. void GuiRolloutCtrl::instantCollapse()
  171. {
  172. mAnimateDestHeight = mHeader.extent.y;
  173. mCollapsing = false;
  174. mIsExpanded = false;
  175. mIsAnimating = false;
  176. resize( mBounds.point + mHeader.point, mHeader.extent );
  177. }
  178. void GuiRolloutCtrl::childResized(GuiControl *child)
  179. {
  180. Parent::childResized( child );
  181. calculateHeights();
  182. }
  183. //////////////////////////////////////////////////////////////////////////
  184. // Control Sizing Animation Functions
  185. //////////////////////////////////////////////////////////////////////////
  186. void GuiRolloutCtrl::animateTo( S32 height )
  187. {
  188. // We do nothing if we're already animating
  189. if( mIsAnimating )
  190. return;
  191. bool collapsing = (bool)( mBounds.extent.y > height );
  192. // If we're already at the destination height, bail
  193. if( mBounds.extent.y >= height && !collapsing )
  194. {
  195. mIsExpanded = true;
  196. return;
  197. }
  198. // If we're already at the destination height, bail
  199. if( mBounds.extent.y <= height && collapsing )
  200. {
  201. mIsExpanded = false;
  202. return;
  203. }
  204. // Set destination height
  205. mAnimateDestHeight = height;
  206. // Set Animation Mode
  207. mCollapsing = collapsing;
  208. // Set Animation Step (Increment)
  209. if( collapsing )
  210. mAnimateStep = (S32)mFloor( (F32)( mBounds.extent.y - height ) / 2.0f );
  211. else
  212. mAnimateStep = (S32)mFloor( (F32)( height - mBounds.extent.y ) / 2.0f );
  213. // Start our animation
  214. mIsAnimating = true;
  215. }
  216. void GuiRolloutCtrl::processTick()
  217. {
  218. // We do nothing here if we're NOT animating
  219. if( !mIsAnimating )
  220. return;
  221. // Sanity check to fix non collapsing panels.
  222. if( mAnimateStep == 0 )
  223. mAnimateStep = 1;
  224. // We're collapsing ourself down (Hiding our contents)
  225. if( mCollapsing )
  226. {
  227. if( mBounds.extent.y < mAnimateDestHeight )
  228. mBounds.extent.y = mAnimateDestHeight;
  229. else if( ( mBounds.extent.y - mAnimateStep ) < mAnimateDestHeight )
  230. mBounds.extent.y = mAnimateDestHeight;
  231. if( mBounds.extent.y == mAnimateDestHeight )
  232. mIsAnimating = false;
  233. else
  234. mBounds.extent.y -= mAnimateStep;
  235. if( !mIsAnimating )
  236. mIsExpanded = false;
  237. }
  238. else // We're expanding ourself (Showing our contents)
  239. {
  240. if( mBounds.extent.y > mAnimateDestHeight )
  241. mBounds.extent.y = mAnimateDestHeight;
  242. else if( ( mBounds.extent.y + mAnimateStep ) > mAnimateDestHeight )
  243. mBounds.extent.y = mAnimateDestHeight;
  244. if( mBounds.extent.y == mAnimateDestHeight )
  245. mIsAnimating = false;
  246. else
  247. mBounds.extent.y += mAnimateStep;
  248. if( !mIsAnimating )
  249. mIsExpanded = true;
  250. }
  251. if( !mIsAnimating )
  252. {
  253. if( mCollapsing && isMethod("onCollapsed") )
  254. Con::executef( this, 2, "onCollapsed" );
  255. else if( !mCollapsing && isMethod("onExpanded") )
  256. Con::executef( this, 2, "onExpanded" );
  257. calculateHeights();
  258. }
  259. GuiControl* parent = getParent();
  260. if( parent )
  261. {
  262. parent->childResized( this );
  263. // if our parent's parent is a scroll control, scrollvisible.
  264. GuiScrollCtrl* scroll = dynamic_cast<GuiScrollCtrl*>(parent->getParent());
  265. if(scroll)
  266. {
  267. scroll->scrollRectVisible(mBounds);
  268. }
  269. }
  270. }
  271. //////////////////////////////////////////////////////////////////////////
  272. // Control Rendering
  273. //////////////////////////////////////////////////////////////////////////
  274. void GuiRolloutCtrl::onRender(Point2I offset, const RectI &updateRect)
  275. {
  276. if( !mProfile || mProfile->mFont.isNull() )
  277. return;
  278. // Calculate actual world bounds for rendering
  279. RectI worldBounds( offset, mBounds.extent );
  280. if( mProfile->mBitmapArrayRects.size() >= NumBitmaps )
  281. {
  282. dglClearBitmapModulation();
  283. // Draw Rollout From Skin
  284. if( !mIsExpanded )
  285. renderFixedBitmapBordersFilled( worldBounds, 1, mProfile );
  286. else// Draw Border
  287. renderSizableBitmapBordersFilledIndex( worldBounds, TopLeftHeader, mProfile );
  288. // Draw Caption ( Vertically Centered )
  289. ColorI currColor;
  290. dglGetBitmapModulation( &currColor );
  291. Point2I textPosition = mHeader.point + offset + mProfile->mTextOffset;
  292. dglSetBitmapModulation( mProfile->mFontColor );
  293. renderJustifiedText( textPosition, mHeader.extent, mCaption );
  294. dglSetBitmapModulation( currColor );
  295. // If we're collapsed we contain the first child as our content
  296. // thus we don't render it when collapsed. but to support modified
  297. // rollouts with custom header buttons etc we still render our other
  298. // children. -JDD
  299. GuiControl *pChild = dynamic_cast<GuiControl*>( at(0) );
  300. if(pChild)
  301. {
  302. if( !mIsExpanded && pChild->isVisible() )
  303. pChild->setVisible( false );
  304. else if( mIsExpanded && !pChild->isVisible())
  305. pChild->setVisible( true );
  306. }
  307. renderChildControls(offset, updateRect);
  308. }
  309. }
  310. //////////////////////////////////////////////////////////////////////////
  311. // Console
  312. //////////////////////////////////////////////////////////////////////////
  313. ConsoleMethod( GuiRolloutCtrl, isExpanded, bool, 2, 2, "() @return Returns true if object is expanded, false otherwise")
  314. {
  315. return object->isExpanded();
  316. }
  317. ConsoleMethod( GuiRolloutCtrl, collapse, void, 2, 2, "() Collapses control.\n @return No return value.")
  318. {
  319. object->collapse();
  320. }
  321. ConsoleMethod( GuiRolloutCtrl, expand, void, 2, 2, "() Expands control\n @return No return value.")
  322. {
  323. object->expand();
  324. }
  325. ConsoleMethod( GuiRolloutCtrl, instantCollapse, void, 2, 2, "() Collapses control without animation.\n @return No return value.")
  326. {
  327. object->instantCollapse();
  328. }
  329. ConsoleMethod( GuiRolloutCtrl, instantExpand, void, 2, 2, "() Expands control without animation.\n @return No return value.")
  330. {
  331. object->instantExpand();
  332. }
  333. ConsoleMethod( GuiRolloutCtrl, sizeToContents, void, 2, 2, "() Resizes control to fit its contents\n @return No return value.")
  334. {
  335. object->sizeToContents();
  336. }