guiTabBookCtrl.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 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/guiTabBookCtrl.h"
  23. #include "console/engineAPI.h"
  24. #include "gui/core/guiCanvas.h"
  25. #include "gui/editor/guiEditCtrl.h"
  26. #include "gui/controls/guiPopUpCtrl.h"
  27. #include "gui/core/guiDefaultControlRender.h"
  28. #include "gfx/gfxDrawUtil.h"
  29. IMPLEMENT_CONOBJECT( GuiTabBookCtrl );
  30. ConsoleDocClass( GuiTabBookCtrl,
  31. "@brief A container \n\n"
  32. "@tsexample\n"
  33. "// Create \n"
  34. "@endtsexample\n\n"
  35. "@note Only GuiTabPageCtrls must be added to GuiTabBookCtrls. If an object of a different "
  36. "class is added to the control, it will be reassigned to either the active page or the "
  37. "tab book's parent.\n\n"
  38. "@see GuiTabPageCtrl\n"
  39. "@ingroup GuiContainers"
  40. );
  41. ImplementEnumType( GuiTabPosition,
  42. "Where the control should put the tab headers for selecting individual pages.\n\n"
  43. "@ingroup GuiContainers" )
  44. { GuiTabBookCtrl::AlignTop, "Top", "Tab headers on top edge." },
  45. { GuiTabBookCtrl::AlignBottom,"Bottom", "Tab headers on bottom edge." }
  46. EndImplementEnumType;
  47. IMPLEMENT_CALLBACK( GuiTabBookCtrl, onTabSelected, void, ( const String& text, U32 index ), ( text, index ),
  48. "Called when a new tab page is selected.\n\n"
  49. "@param text Text of the page header for the tab that is being selected.\n"
  50. "@param index Index of the tab page being selected." );
  51. IMPLEMENT_CALLBACK( GuiTabBookCtrl, onTabRightClick, void, ( const String& text, U32 index ), ( text, index ),
  52. "Called when the user right-clicks on a tab page header.\n\n"
  53. "@param text Text of the page header for the tab that is being selected.\n"
  54. "@param index Index of the tab page being selected." );
  55. //-----------------------------------------------------------------------------
  56. GuiTabBookCtrl::GuiTabBookCtrl()
  57. {
  58. VECTOR_SET_ASSOCIATION( mPages );
  59. mTabHeight = 24;
  60. mTabPosition = AlignTop;
  61. mActivePage = NULL;
  62. mHoverTab = NULL;
  63. mHasTexture = false;
  64. mBitmapBounds = NULL;
  65. setExtent( 400, 300 );
  66. mPageRect = RectI(0,0,0,0);
  67. mTabRect = RectI(0,0,0,0);
  68. mFrontTabPadding = 0;
  69. mPages.reserve(12);
  70. mTabMargin = 7;
  71. mMinTabWidth = 64;
  72. mIsContainer = true;
  73. mSelectedPageNum = -1;
  74. mDefaultPageNum = -1;
  75. mAllowReorder = false;
  76. mDraggingTab = false;
  77. mDraggingTabRect = false;
  78. mIsFirstWake = true;
  79. }
  80. //-----------------------------------------------------------------------------
  81. void GuiTabBookCtrl::initPersistFields()
  82. {
  83. docsURL;
  84. addGroup( "TabBook" );
  85. addField( "tabPosition", TYPEID< TabPosition >(), Offset( mTabPosition, GuiTabBookCtrl ),
  86. "Where to place the tab page headers." );
  87. addField( "tabMargin", TypeS32, Offset( mTabMargin, GuiTabBookCtrl ),
  88. "Spacing to put between individual tab page headers." );
  89. addField( "minTabWidth", TypeS32, Offset( mMinTabWidth, GuiTabBookCtrl ),
  90. "Minimum width allocated to a tab page header." );
  91. addField( "tabHeight", TypeS32, Offset( mTabHeight, GuiTabBookCtrl ),
  92. "Height of tab page headers." );
  93. addField( "allowReorder", TypeBool, Offset( mAllowReorder, GuiTabBookCtrl ),
  94. "Whether reordering tabs with the mouse is allowed." );
  95. addField( "defaultPage", TypeS32, Offset( mDefaultPageNum, GuiTabBookCtrl ),
  96. "Index of page to select on first onWake() call (-1 to disable)." );
  97. addProtectedField( "selectedPage", TypeS32, Offset( mSelectedPageNum, GuiTabBookCtrl ),
  98. &_setSelectedPage, &defaultProtectedGetFn,
  99. "Index of currently selected page." );
  100. addField( "frontTabPadding", TypeS32, Offset( mFrontTabPadding, GuiTabBookCtrl ),
  101. "X offset of first tab page header." );
  102. endGroup( "TabBook" );
  103. Parent::initPersistFields();
  104. }
  105. //-----------------------------------------------------------------------------
  106. void GuiTabBookCtrl::onChildRemoved( GuiControl* child )
  107. {
  108. for (S32 i = 0; i < mPages.size(); i++ )
  109. {
  110. GuiTabPageCtrl* tab = mPages[i].Page;
  111. if( tab == child )
  112. {
  113. mPages.erase( i );
  114. break;
  115. }
  116. }
  117. // Calculate Page Information
  118. calculatePageTabs();
  119. // Active Index.
  120. mSelectedPageNum = getMin( mSelectedPageNum, mPages.size() - 1 );
  121. if ( mSelectedPageNum != -1 )
  122. {
  123. // Select Page.
  124. selectPage( mSelectedPageNum );
  125. }
  126. }
  127. //-----------------------------------------------------------------------------
  128. void GuiTabBookCtrl::onChildAdded( GuiControl *child )
  129. {
  130. GuiTabPageCtrl *page = dynamic_cast<GuiTabPageCtrl*>(child);
  131. if( !page )
  132. {
  133. Con::warnf("GuiTabBookCtrl::onChildAdded - attempting to add NON GuiTabPageCtrl as child page");
  134. SimObject *simObj = reinterpret_cast<SimObject*>(child);
  135. removeObject( simObj );
  136. if( mActivePage )
  137. {
  138. mActivePage->addObject( simObj );
  139. }
  140. else
  141. {
  142. Con::warnf("GuiTabBookCtrl::onChildAdded - unable to find active page to reassign ownership of new child control to, placing on parent");
  143. GuiControl *rent = getParent();
  144. if( rent )
  145. rent->addObject( simObj );
  146. }
  147. return;
  148. }
  149. TabHeaderInfo newPage;
  150. newPage.Page = page;
  151. newPage.TabRow = -1;
  152. newPage.TabColumn = -1;
  153. mPages.push_back( newPage );
  154. // Calculate Page Information
  155. calculatePageTabs();
  156. if( page->getFitBook() )
  157. fitPage( page );
  158. // Select this Page
  159. selectPage( page );
  160. }
  161. //-----------------------------------------------------------------------------
  162. bool GuiTabBookCtrl::reOrder(SimObject* obj, SimObject* target)
  163. {
  164. if ( !Parent::reOrder(obj, target) )
  165. return false;
  166. // Store the Selected Page.
  167. GuiTabPageCtrl *selectedPage = NULL;
  168. if ( mSelectedPageNum != -1 )
  169. selectedPage = mPages[mSelectedPageNum].Page;
  170. // Determine the Target Page Index.
  171. S32 targetIndex = -1;
  172. for( S32 i = 0; i < mPages.size(); i++ )
  173. {
  174. const TabHeaderInfo &info = mPages[i];
  175. if ( info.Page == target )
  176. {
  177. targetIndex = i;
  178. break;
  179. }
  180. }
  181. if ( targetIndex == -1 )
  182. {
  183. return false;
  184. }
  185. for( S32 i = 0; i < mPages.size(); i++ )
  186. {
  187. const TabHeaderInfo &info = mPages[i];
  188. if ( info.Page == obj )
  189. {
  190. // Store Info.
  191. TabHeaderInfo objPage = info;
  192. // Remove.
  193. mPages.erase( i );
  194. // Insert.
  195. mPages.insert( targetIndex, objPage );
  196. break;
  197. }
  198. }
  199. // Update Tabs.
  200. calculatePageTabs();
  201. // Reselect Page.
  202. selectPage( selectedPage );
  203. return true;
  204. }
  205. //-----------------------------------------------------------------------------
  206. bool GuiTabBookCtrl::acceptsAsChild( SimObject* object ) const
  207. {
  208. // Only accept tab pages.
  209. return ( dynamic_cast< GuiTabPageCtrl* >( object ) != NULL );
  210. }
  211. //-----------------------------------------------------------------------------
  212. bool GuiTabBookCtrl::onWake()
  213. {
  214. if (! Parent::onWake())
  215. return false;
  216. mHasTexture = mProfile->constructBitmapArray() > 0;
  217. if( mHasTexture )
  218. {
  219. mBitmapBounds = mProfile->mBitmapArrayRects.address();
  220. mTabHeight = mBitmapBounds[TabSelected].extent.y;
  221. }
  222. calculatePageTabs();
  223. if( mIsFirstWake )
  224. {
  225. // Awaken all pages, visible or not. We need to do this so
  226. // any pages that make use of a language table for their label
  227. // are correctly initialized.
  228. for ( U32 i = 0; i < mPages.size(); ++i)
  229. {
  230. if ( !mPages[i].Page->isAwake() )
  231. {
  232. mPages[i].Page->awaken();
  233. }
  234. }
  235. if( mDefaultPageNum >= 0 && mDefaultPageNum < mPages.size() )
  236. selectPage( mDefaultPageNum );
  237. mIsFirstWake = false;
  238. }
  239. return true;
  240. }
  241. //-----------------------------------------------------------------------------
  242. void GuiTabBookCtrl::addNewPage( const char* text )
  243. {
  244. GuiTabPageCtrl* page = new GuiTabPageCtrl();
  245. if( text )
  246. page->setText( text );
  247. page->registerObject();
  248. addObject( page );
  249. }
  250. //-----------------------------------------------------------------------------
  251. bool GuiTabBookCtrl::resize(const Point2I &newPosition, const Point2I &newExtent)
  252. {
  253. bool result = Parent::resize( newPosition, newExtent );
  254. calculatePageTabs();
  255. return result;
  256. }
  257. //-----------------------------------------------------------------------------
  258. void GuiTabBookCtrl::childResized(GuiControl *child)
  259. {
  260. Parent::childResized( child );
  261. //child->resize( mPageRect.point, mPageRect.extent );
  262. }
  263. //-----------------------------------------------------------------------------
  264. void GuiTabBookCtrl::onMouseDown(const GuiEvent &event)
  265. {
  266. mDraggingTab = false;
  267. mDraggingTabRect = false;
  268. Point2I localMouse = globalToLocalCoord( event.mousePoint );
  269. if( mTabRect.pointInRect( localMouse ) )
  270. {
  271. GuiTabPageCtrl *tab = findHitTab( localMouse );
  272. if( tab != NULL )
  273. {
  274. selectPage( tab );
  275. mDraggingTab = mAllowReorder;
  276. }
  277. else
  278. {
  279. mDraggingTabRect = true;
  280. }
  281. }
  282. }
  283. //-----------------------------------------------------------------------------
  284. void GuiTabBookCtrl::onMouseUp(const GuiEvent &event)
  285. {
  286. Parent::onMouseUp( event );
  287. mDraggingTab = false;
  288. mDraggingTabRect = false;
  289. }
  290. //-----------------------------------------------------------------------------
  291. void GuiTabBookCtrl::onMouseDragged(const GuiEvent &event)
  292. {
  293. Parent::onMouseDragged( event );
  294. if ( !mDraggingTab )
  295. return;
  296. GuiTabPageCtrl *selectedPage = NULL;
  297. if ( mSelectedPageNum != -1 )
  298. selectedPage = mPages[mSelectedPageNum].Page;
  299. if ( !selectedPage )
  300. return;
  301. Point2I localMouse = globalToLocalCoord( event.mousePoint );
  302. if( mTabRect.pointInRect( localMouse ) )
  303. {
  304. GuiTabPageCtrl *tab = findHitTab( localMouse );
  305. if( tab != NULL && tab != selectedPage )
  306. {
  307. S32 targetIndex = -1;
  308. for( S32 i = 0; i < mPages.size(); i++ )
  309. {
  310. if( mPages[i].Page == tab )
  311. {
  312. targetIndex = i;
  313. break;
  314. }
  315. }
  316. if ( targetIndex > mSelectedPageNum )
  317. {
  318. reOrder( tab, selectedPage );
  319. }
  320. else
  321. {
  322. reOrder( selectedPage, tab );
  323. }
  324. }
  325. }
  326. }
  327. //-----------------------------------------------------------------------------
  328. void GuiTabBookCtrl::onMouseMove(const GuiEvent &event)
  329. {
  330. Point2I localMouse = globalToLocalCoord( event.mousePoint );
  331. if( mTabRect.pointInRect( localMouse ) )
  332. {
  333. GuiTabPageCtrl *tab = findHitTab( localMouse );
  334. if( tab != NULL && mHoverTab != tab )
  335. mHoverTab = tab;
  336. else if ( !tab )
  337. mHoverTab = NULL;
  338. }
  339. Parent::onMouseMove( event );
  340. }
  341. //-----------------------------------------------------------------------------
  342. void GuiTabBookCtrl::onMouseLeave( const GuiEvent &event )
  343. {
  344. Parent::onMouseLeave( event );
  345. mHoverTab = NULL;
  346. }
  347. //-----------------------------------------------------------------------------
  348. bool GuiTabBookCtrl::onMouseDownEditor(const GuiEvent &event, Point2I offset)
  349. {
  350. bool handled = false;
  351. Point2I localMouse = globalToLocalCoord( event.mousePoint );
  352. if( mTabRect.pointInRect( localMouse ) )
  353. {
  354. GuiTabPageCtrl *tab = findHitTab( localMouse );
  355. if( tab != NULL )
  356. {
  357. selectPage( tab );
  358. handled = true;
  359. }
  360. }
  361. #ifdef TORQUE_TOOLS
  362. // This shouldn't be called if it's not design time, but check just incase
  363. if ( GuiControl::smDesignTime )
  364. {
  365. // If we clicked in the editor and our addset is the tab book
  366. // ctrl, select the child ctrl so we can edit it's properties
  367. GuiEditCtrl* edit = GuiControl::smEditorHandle;
  368. if( edit && ( edit->getAddSet() == this ) && mActivePage != NULL )
  369. edit->select( mActivePage );
  370. }
  371. #endif
  372. // Return whether we handled this or not.
  373. return handled;
  374. }
  375. //-----------------------------------------------------------------------------
  376. void GuiTabBookCtrl::onRightMouseUp( const GuiEvent& event )
  377. {
  378. Point2I localMouse = globalToLocalCoord( event.mousePoint );
  379. if( mTabRect.pointInRect( localMouse ) )
  380. {
  381. GuiTabPageCtrl* tab = findHitTab( localMouse );
  382. if( tab )
  383. onTabRightClick_callback( tab->getText(), getPageNum( tab ) );
  384. }
  385. }
  386. //-----------------------------------------------------------------------------
  387. void GuiTabBookCtrl::onRender(Point2I offset, const RectI &updateRect)
  388. {
  389. RectI tabRect = mTabRect;
  390. tabRect.point += offset;
  391. RectI pageRect = mPageRect;
  392. pageRect.point += offset;
  393. // We're so nice we'll store the old modulation before we clear it for our rendering! :)
  394. ColorI oldModulation;
  395. GFX->getDrawUtil()->getBitmapModulation( &oldModulation );
  396. // Wipe it out
  397. GFX->getDrawUtil()->clearBitmapModulation();
  398. Parent::onRender(offset, updateRect);
  399. // Clip to tab area
  400. RectI savedClipRect = GFX->getClipRect();
  401. RectI clippedTabRect = tabRect;
  402. clippedTabRect.intersect( savedClipRect );
  403. GFX->setClipRect( clippedTabRect );
  404. // Render our tabs
  405. renderTabs( offset, tabRect );
  406. // Restore Rect.
  407. GFX->setClipRect( savedClipRect );
  408. // Restore old modulation
  409. GFX->getDrawUtil()->setBitmapModulation( oldModulation );
  410. }
  411. //-----------------------------------------------------------------------------
  412. void GuiTabBookCtrl::renderTabs( const Point2I &offset, const RectI &tabRect )
  413. {
  414. // If the tab size is zero, don't render tabs,
  415. // assuming it's a tab-less book
  416. if( mPages.empty() || mTabHeight <= 0 )
  417. return;
  418. for( S32 i = 0; i < mPages.size(); i++ )
  419. {
  420. const TabHeaderInfo &currentTabInfo = mPages[i];
  421. RectI tabBounds = mPages[i].TabRect;
  422. tabBounds.point += offset;
  423. GuiTabPageCtrl *tab = mPages[i].Page;
  424. if( tab != NULL )
  425. renderTab( tabBounds, tab );
  426. // If we're on the last tab, draw the nice end piece
  427. if( i + 1 == mPages.size() )
  428. {
  429. Point2I tabEndPoint = Point2I(currentTabInfo.TabRect.point.x + currentTabInfo.TabRect.extent.x + offset.x, currentTabInfo.TabRect.point.y + offset.y);
  430. Point2I tabEndExtent = Point2I((tabRect.point.x + tabRect.extent.x) - tabEndPoint.x, currentTabInfo.TabRect.extent.y);
  431. RectI tabEndRect = RectI(tabEndPoint,tabEndExtent);
  432. GFX->setClipRect( tabEndRect );
  433. // As it turns out the last tab can be outside the viewport in which
  434. // case trying to render causes a DX assert. Could be better if
  435. // setClipRect returned a bool.
  436. if ( GFX->getViewport().isValidRect() )
  437. renderFixedBitmapBordersFilled( tabEndRect, TabEnds + 1, mProfile );
  438. }
  439. }
  440. }
  441. //-----------------------------------------------------------------------------
  442. void GuiTabBookCtrl::renderTab(const RectI& tabRect, GuiTabPageCtrl *tab)
  443. {
  444. StringTableEntry text = tab->getText();
  445. ColorI oldColor;
  446. GFX->getDrawUtil()->getBitmapModulation( &oldColor );
  447. // Is this a skinned control?
  448. if( mHasTexture && mProfile->mBitmapArrayRects.size() >= 9 )
  449. {
  450. S32 indexMultiplier = 1;
  451. switch( mTabPosition )
  452. {
  453. case AlignTop:
  454. case AlignBottom:
  455. if ( mActivePage == tab )
  456. indexMultiplier += TabSelected;
  457. else if( mHoverTab == tab )
  458. indexMultiplier += TabHover;
  459. else
  460. indexMultiplier += TabNormal;
  461. break;
  462. }
  463. renderFixedBitmapBordersFilled( tabRect, indexMultiplier, mProfile );
  464. }
  465. else
  466. {
  467. // If this isn't a skinned control or the bitmap is simply missing, handle it WELL
  468. if ( mActivePage == tab )
  469. GFX->getDrawUtil()->drawRectFill(tabRect, mProfile->mFillColor);
  470. else if( mHoverTab == tab )
  471. GFX->getDrawUtil()->drawRectFill(tabRect, mProfile->mFillColorHL);
  472. else
  473. GFX->getDrawUtil()->drawRectFill(tabRect, mProfile->mFillColorNA);
  474. }
  475. GFX->getDrawUtil()->setBitmapModulation(mProfile->mFontColor);
  476. switch( mTabPosition )
  477. {
  478. case AlignTop:
  479. case AlignBottom:
  480. renderJustifiedText( tabRect.point, tabRect.extent, text);
  481. break;
  482. }
  483. GFX->getDrawUtil()->setBitmapModulation( oldColor);
  484. }
  485. //-----------------------------------------------------------------------------
  486. void GuiTabBookCtrl::setUpdate()
  487. {
  488. Parent::setUpdate();
  489. setUpdateRegion(Point2I(0,0), getExtent());
  490. calculatePageTabs();
  491. }
  492. //-----------------------------------------------------------------------------
  493. S32 GuiTabBookCtrl::calculatePageTabWidth( GuiTabPageCtrl *page )
  494. {
  495. if( !page )
  496. return mMinTabWidth;
  497. const char* text = page->getText();
  498. if( !text || dStrlen(text) == 0 || mProfile == NULL || mProfile->mFont == NULL )
  499. return mMinTabWidth;
  500. GFont *font = mProfile->mFont;
  501. return font->getStrNWidth( text, dStrlen(text) );
  502. }
  503. //-----------------------------------------------------------------------------
  504. const RectI GuiTabBookCtrl::getClientRect()
  505. {
  506. if( !mProfile || mProfile->mBitmapArrayRects.size() < NumBitmaps )
  507. return Parent::getClientRect();
  508. return mPageRect;
  509. }
  510. //-----------------------------------------------------------------------------
  511. void GuiTabBookCtrl::calculatePageTabs()
  512. {
  513. // Short Circuit.
  514. //
  515. // If the tab size is zero, don't render tabs,
  516. // assuming it's a tab-less book
  517. if( mPages.empty() || mTabHeight <= 0 )
  518. {
  519. mPageRect.point.x = 0;
  520. mPageRect.point.y = 0;
  521. mPageRect.extent.x = getWidth();
  522. mPageRect.extent.y = getHeight();
  523. return;
  524. }
  525. S32 currRow = 0;
  526. S32 currColumn = 0;
  527. S32 currX = mFrontTabPadding;
  528. S32 maxWidth = 0;
  529. for( S32 i = 0; i < mPages.size(); i++ )
  530. {
  531. // Fetch Tab Width
  532. S32 tabWidth = calculatePageTabWidth( mPages[i].Page ) + ( mTabMargin * 2 );
  533. tabWidth = getMax( tabWidth, mMinTabWidth );
  534. TabHeaderInfo &info = mPages[i];
  535. switch( mTabPosition )
  536. {
  537. case AlignTop:
  538. case AlignBottom:
  539. // If we're going to go outside our bounds
  540. // with this tab move it down a row
  541. if( currX + tabWidth > getWidth() )
  542. {
  543. // Calculate and Advance State.
  544. maxWidth = getMax( tabWidth, maxWidth );
  545. balanceRow( currRow, currX );
  546. info.TabRow = ++currRow;
  547. // Reset Necessaries
  548. info.TabColumn = currColumn = maxWidth = currX = 0;
  549. }
  550. else
  551. {
  552. info.TabRow = currRow;
  553. info.TabColumn = currColumn++;
  554. }
  555. // Calculate Tabs Bounding Rect
  556. info.TabRect.point.x = currX;
  557. info.TabRect.extent.x = tabWidth;
  558. info.TabRect.extent.y = mTabHeight;
  559. // Adjust Y Point based on alignment
  560. if( mTabPosition == AlignTop )
  561. info.TabRect.point.y = ( info.TabRow * mTabHeight );
  562. else
  563. info.TabRect.point.y = getHeight() - ( ( 1 + info.TabRow ) * mTabHeight );
  564. currX += tabWidth;
  565. break;
  566. };
  567. }
  568. currRow++;
  569. currColumn++;
  570. Point2I localPoint = getExtent();
  571. // Calculate
  572. switch( mTabPosition )
  573. {
  574. case AlignTop:
  575. localPoint.y -= getTop();
  576. mTabRect.point.x = 0;
  577. mTabRect.extent.x = localPoint.x;
  578. mTabRect.point.y = 0;
  579. mTabRect.extent.y = currRow * mTabHeight;
  580. mPageRect.point.x = 0;
  581. mPageRect.point.y = mTabRect.extent.y;
  582. mPageRect.extent.x = mTabRect.extent.x;
  583. mPageRect.extent.y = getHeight() - mTabRect.extent.y;
  584. break;
  585. case AlignBottom:
  586. mTabRect.point.x = 0;
  587. mTabRect.extent.x = localPoint.x;
  588. mTabRect.extent.y = currRow * mTabHeight;
  589. mTabRect.point.y = getHeight() - mTabRect.extent.y;
  590. mPageRect.point.x = 0;
  591. mPageRect.point.y = 0;
  592. mPageRect.extent.x = mTabRect.extent.x;
  593. mPageRect.extent.y = localPoint.y - mTabRect.extent.y;
  594. break;
  595. }
  596. }
  597. //-----------------------------------------------------------------------------
  598. void GuiTabBookCtrl::balanceRow( S32 row, S32 totalTabWidth )
  599. {
  600. // Short Circuit.
  601. //
  602. // If the tab size is zero, don't render tabs,
  603. // and assume it's a tab-less tab-book - JDD
  604. if( mPages.empty() || mTabHeight <= 0 )
  605. return;
  606. Vector<TabHeaderInfo*> rowTemp;
  607. rowTemp.clear();
  608. for( S32 i = 0; i < mPages.size(); i++ )
  609. {
  610. TabHeaderInfo &info = mPages[i];
  611. if(info.TabRow == row )
  612. rowTemp.push_back( &mPages[i] );
  613. }
  614. if( rowTemp.empty() )
  615. return;
  616. // Balance the tabs across the remaining space
  617. S32 spaceToDivide = getWidth() - totalTabWidth;
  618. S32 pointDelta = 0;
  619. for( S32 i = 0; i < rowTemp.size(); i++ )
  620. {
  621. TabHeaderInfo &info = *rowTemp[i];
  622. S32 extraSpace = (S32)spaceToDivide / ( rowTemp.size() );
  623. info.TabRect.extent.x += extraSpace;
  624. info.TabRect.point.x += pointDelta;
  625. pointDelta += extraSpace;
  626. }
  627. }
  628. //-----------------------------------------------------------------------------
  629. GuiTabPageCtrl *GuiTabBookCtrl::findHitTab( const GuiEvent &event )
  630. {
  631. return findHitTab( event.mousePoint );
  632. }
  633. //-----------------------------------------------------------------------------
  634. GuiTabPageCtrl *GuiTabBookCtrl::findHitTab( Point2I hitPoint )
  635. {
  636. // Short Circuit.
  637. //
  638. // If the tab size is zero, don't render tabs,
  639. // and assume it's a tab-less tab-book - JDD
  640. if( mPages.empty() || mTabHeight <= 0 )
  641. return NULL;
  642. for( S32 i = 0; i < mPages.size(); i++ )
  643. {
  644. if( mPages[i].TabRect.pointInRect( hitPoint ) )
  645. return mPages[i].Page;
  646. }
  647. return NULL;
  648. }
  649. //-----------------------------------------------------------------------------
  650. void GuiTabBookCtrl::selectPage( S32 index )
  651. {
  652. if( mPages.empty() || index < 0 )
  653. return;
  654. if( mPages.size() <= index )
  655. index = mPages.size() - 1;
  656. // Select the page
  657. selectPage( mPages[ index ].Page );
  658. }
  659. //-----------------------------------------------------------------------------
  660. void GuiTabBookCtrl::selectPage( GuiTabPageCtrl *page )
  661. {
  662. // Return if already selected.
  663. if( mSelectedPageNum >= 0 && mSelectedPageNum < mPages.size() && mPages[ mSelectedPageNum ].Page == page )
  664. return;
  665. mSelectedPageNum = -1;
  666. Vector<TabHeaderInfo>::iterator i = mPages.begin();
  667. for( S32 index = 0; i != mPages.end() ; i++, index++ )
  668. {
  669. GuiTabPageCtrl *tab = (*i).Page;
  670. if( page == tab )
  671. {
  672. mActivePage = tab;
  673. tab->setVisible( true );
  674. mSelectedPageNum = index;
  675. // Notify User
  676. onTabSelected_callback( tab->getText(), index );
  677. }
  678. else
  679. tab->setVisible( false );
  680. }
  681. setUpdateLayout( updateSelf );
  682. }
  683. //-----------------------------------------------------------------------------
  684. bool GuiTabBookCtrl::_setSelectedPage( void *object, const char *index, const char *data )
  685. {
  686. GuiTabBookCtrl* book = reinterpret_cast< GuiTabBookCtrl* >( object );
  687. book->selectPage( dAtoi( data ) );
  688. return false;
  689. }
  690. //-----------------------------------------------------------------------------
  691. bool GuiTabBookCtrl::onKeyDown(const GuiEvent &event)
  692. {
  693. // Tab = Next Page
  694. // Ctrl-Tab = Previous Page
  695. if( 0 && event.keyCode == KEY_TAB )
  696. {
  697. if( event.modifier & SI_PRIMARY_CTRL )
  698. selectPrevPage();
  699. else
  700. selectNextPage();
  701. return true;
  702. }
  703. return Parent::onKeyDown( event );
  704. }
  705. //-----------------------------------------------------------------------------
  706. void GuiTabBookCtrl::selectNextPage()
  707. {
  708. if( mPages.empty() )
  709. return;
  710. if( mActivePage == NULL )
  711. mActivePage = mPages[0].Page;
  712. S32 nI = 0;
  713. for( ; nI < mPages.size(); nI++ )
  714. {
  715. GuiTabPageCtrl *tab = mPages[ nI ].Page;
  716. if( tab == mActivePage )
  717. {
  718. if( nI == ( mPages.size() - 1 ) )
  719. selectPage( 0 );
  720. else if ( nI + 1 <= ( mPages.size() - 1 ) )
  721. selectPage( nI + 1 );
  722. else
  723. selectPage( 0 );
  724. return;
  725. }
  726. }
  727. }
  728. //-----------------------------------------------------------------------------
  729. void GuiTabBookCtrl::selectPrevPage()
  730. {
  731. if( mPages.empty() )
  732. return;
  733. if( mActivePage == NULL )
  734. mActivePage = mPages[0].Page;
  735. S32 nI = 0;
  736. for( ; nI < mPages.size(); nI++ )
  737. {
  738. GuiTabPageCtrl *tab = mPages[ nI ].Page;
  739. if( tab == mActivePage )
  740. {
  741. if( nI == 0 )
  742. selectPage( mPages.size() - 1 );
  743. else
  744. selectPage( nI - 1 );
  745. return;
  746. }
  747. }
  748. }
  749. //-----------------------------------------------------------------------------
  750. void GuiTabBookCtrl::fitPage( GuiTabPageCtrl* page )
  751. {
  752. page->resize( mPageRect.point, mPageRect.extent );
  753. }
  754. //-----------------------------------------------------------------------------
  755. S32 GuiTabBookCtrl::getPageNum( GuiTabPageCtrl* page ) const
  756. {
  757. const U32 numPages = mPages.size();
  758. for( U32 i = 0; i < numPages; ++ i )
  759. if( mPages[ i ].Page == page )
  760. return i;
  761. return -1;
  762. }
  763. //=============================================================================
  764. // API.
  765. //=============================================================================
  766. // MARK: ---- API ----
  767. //-----------------------------------------------------------------------------
  768. DefineEngineMethod( GuiTabBookCtrl, addPage, void, ( const char* title ), ( "" ),
  769. "Add a new tab page to the control.\n\n"
  770. "@param title Title text for the tab page header." )
  771. {
  772. object->addNewPage( title );
  773. }
  774. //-----------------------------------------------------------------------------
  775. DefineEngineMethod( GuiTabBookCtrl, selectPage, void, ( S32 index ),,
  776. "Set the selected tab page.\n\n"
  777. "@param index Index of the tab page." )
  778. {
  779. object->selectPage( index );
  780. }
  781. //-----------------------------------------------------------------------------
  782. DefineEngineMethod( GuiTabBookCtrl, getSelectedPage, S32, (),,
  783. "Get the index of the currently selected tab page.\n\n"
  784. "@return Index of the selected tab page or -1 if no tab page is selected." )
  785. {
  786. return object->getSelectedPageNum();
  787. }