guiTabBookCtrl.cpp 27 KB

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