guiTabBookCtrl.cc 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921
  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 "console/console.h"
  23. #include "console/consoleTypes.h"
  24. #include "graphics/dgl.h"
  25. #include "sim/simBase.h"
  26. #include "gui/guiCanvas.h"
  27. #include "gui/containers/guiTabBookCtrl.h"
  28. #include "platform/event.h"
  29. #include "io/fileStream.h"
  30. #include "gui/containers/guiScrollCtrl.h"
  31. #include "gui/editor/guiEditCtrl.h"
  32. #include "gui/guiPopUpCtrl.h"
  33. #include "gui/guiDefaultControlRender.h"
  34. #include "guiTabBookCtrl_ScriptBinding.h"
  35. // So we can set tab alignment via gui editor
  36. static EnumTable::Enums tabAlignEnums[] =
  37. {
  38. { GuiTabBookCtrl::AlignTop, "Top" },
  39. { GuiTabBookCtrl::AlignLeft, "Left" },
  40. { GuiTabBookCtrl::AlignBottom,"Bottom" },
  41. { GuiTabBookCtrl::AlignRight, "Right" }
  42. };
  43. static EnumTable gTabAlignEnums(4,&tabAlignEnums[0]);
  44. IMPLEMENT_CONOBJECT(GuiTabBookCtrl);
  45. GuiTabBookCtrl::GuiTabBookCtrl()
  46. {
  47. VECTOR_SET_ASSOCIATION(mPages);
  48. mLastFontHeight = 0;
  49. mTabPosition = GuiTabBookCtrl::AlignTop;
  50. mLastTabPosition = mTabPosition;
  51. mActivePage = NULL;
  52. mHoverTab = NULL;
  53. mHasTexture = false;
  54. mBitmapBounds = NULL;
  55. mBounds.extent.set( 400, 300 );
  56. mPageRect = RectI(0,0,0,0);
  57. mTabRect = RectI(0,0,0,0);
  58. mPages.reserve(12);
  59. mMinTabWidth = 64;
  60. mTabWidth = 64;
  61. mIsContainer = true;
  62. mTabProfile = NULL;
  63. setField("TabProfile", "GuiTabProfile");
  64. }
  65. void GuiTabBookCtrl::initPersistFields()
  66. {
  67. Parent::initPersistFields();
  68. addField("TabPosition", TypeEnum, Offset(mTabPosition,GuiTabBookCtrl), 1, &gTabAlignEnums );
  69. addField("MinTabWidth", TypeS32, Offset(mMinTabWidth,GuiTabBookCtrl));
  70. addField("TabProfile", TypeGuiProfile, Offset(mTabProfile, GuiTabBookCtrl));
  71. }
  72. // Empty for now, will implement for handling design time context menu for manipulating pages
  73. ConsoleMethod( GuiTabBookCtrl, addPage, void, 2, 2, "() Empty")
  74. {
  75. object->addNewPage();
  76. }
  77. //ConsoleMethod( GuiTabBookCtrl, removePage, void, 2, 2, "()")
  78. //{
  79. //}
  80. bool GuiTabBookCtrl::onAdd()
  81. {
  82. Parent::onAdd();
  83. return true;
  84. }
  85. void GuiTabBookCtrl::onRemove()
  86. {
  87. Parent::onRemove();
  88. }
  89. void GuiTabBookCtrl::onChildRemoved( GuiControl* child )
  90. {
  91. for (S32 i = 0; i < mPages.size(); i++ )
  92. {
  93. GuiTabPageCtrl* tab = mPages[i].Page;
  94. if( tab == child )
  95. {
  96. if( tab == mActivePage )
  97. mActivePage = NULL;
  98. mPages.erase( i );
  99. break;
  100. }
  101. }
  102. if( mPages.empty() )
  103. mActivePage = NULL;
  104. else if (mActivePage == NULL )
  105. mActivePage = static_cast<GuiTabPageCtrl*>(mPages[0].Page);
  106. }
  107. void GuiTabBookCtrl::onChildAdded( GuiControl *child )
  108. {
  109. GuiTabPageCtrl *page = dynamic_cast<GuiTabPageCtrl*>(child);
  110. if( !page )
  111. {
  112. Con::warnf("GuiTabBookCtrl::onChildAdded - attempting to add NON GuiTabPageCtrl as child page");
  113. SimObject *simObj = reinterpret_cast<SimObject*>(child);
  114. removeObject( simObj );
  115. if( mActivePage )
  116. {
  117. mActivePage->addObject( simObj );
  118. }
  119. else
  120. {
  121. Con::warnf("GuiTabBookCtrl::onChildAdded - unable to find active page to reassign ownership of new child control to, placing on parent");
  122. GuiControl *rent = getParent();
  123. if( rent )
  124. rent->addObject( simObj );
  125. }
  126. return;
  127. }
  128. TabHeaderInfo newPage;
  129. newPage.Page = page;
  130. newPage.TabRow = -1;
  131. newPage.TabColumn = -1;
  132. mPages.push_back( newPage );
  133. // Calculate Page Information
  134. calculatePageTabs();
  135. child->resize( mPageRect.point, mPageRect.extent );
  136. }
  137. bool GuiTabBookCtrl::onWake()
  138. {
  139. if (! Parent::onWake())
  140. return false;
  141. mHasTexture = mProfile->constructBitmapArray();
  142. if( mHasTexture )
  143. mBitmapBounds = mProfile->mBitmapArrayRects.address();
  144. //increment the tab profile
  145. mTabProfile->incRefCount();
  146. return true;
  147. }
  148. void GuiTabBookCtrl::onSleep()
  149. {
  150. Parent::onSleep();
  151. //decrement the tab profile referrence
  152. if (mTabProfile != NULL)
  153. mTabProfile->decRefCount();
  154. }
  155. void GuiTabBookCtrl::setControlTabProfile(GuiControlProfile* prof)
  156. {
  157. AssertFatal(prof, "GuiTabBookCtrl::setControlTabProfile: invalid tab profile");
  158. if (prof == mTabProfile)
  159. return;
  160. if (mAwake)
  161. mTabProfile->decRefCount();
  162. mTabProfile = prof;
  163. if (mAwake)
  164. mTabProfile->incRefCount();
  165. }
  166. void GuiTabBookCtrl::addNewPage()
  167. {
  168. char textbuf[1024];
  169. GuiTabPageCtrl * page = new GuiTabPageCtrl();
  170. page->setField("profile", "GuiTabPageProfile");
  171. dSprintf(textbuf, sizeof(textbuf), "TabBookPage%d_%d", getId(), page->getId());
  172. page->registerObject(textbuf);
  173. this->addObject( page );
  174. }
  175. void GuiTabBookCtrl::resize(const Point2I &newPosition, const Point2I &newExtent)
  176. {
  177. Parent::resize( newPosition, newExtent );
  178. calculatePageTabs();
  179. // Resize Children
  180. SimSet::iterator i;
  181. for(i = begin(); i != end(); i++)
  182. {
  183. GuiControl *ctrl = static_cast<GuiControl *>(*i);
  184. ctrl->resize( mPageRect.point, mPageRect.extent );
  185. }
  186. }
  187. void GuiTabBookCtrl::childResized(GuiControl *child)
  188. {
  189. //Parent::childResized( child );
  190. child->resize( mPageRect.point, mPageRect.extent );
  191. }
  192. Point2I GuiTabBookCtrl::getTabLocalCoord(const Point2I &src)
  193. {
  194. //Get the border profiles
  195. GuiBorderProfile *leftProfile = mProfile->getLeftBorder();
  196. GuiBorderProfile *topProfile = mProfile->getTopBorder();
  197. S32 leftSize = (leftProfile) ? leftProfile->getMargin(NormalState) + leftProfile->getBorder(NormalState) + leftProfile->getPadding(NormalState) : 0;
  198. S32 topSize = (topProfile) ? topProfile->getMargin(NormalState) + topProfile->getBorder(NormalState) + topProfile->getPadding(NormalState) : 0;
  199. Point2I ret = Point2I(src.x - leftSize, src.y - topSize);
  200. ret.x -= mTabRect.point.x;
  201. ret.y -= mTabRect.point.y;
  202. return ret;
  203. }
  204. void GuiTabBookCtrl::onTouchDown(const GuiEvent &event)
  205. {
  206. Point2I localMouse = globalToLocalCoord( event.mousePoint );
  207. if( mTabRect.pointInRect( localMouse ) )
  208. {
  209. Point2I tabLocalMouse = getTabLocalCoord(localMouse);
  210. GuiTabPageCtrl *tab = findHitTab(tabLocalMouse);
  211. if( tab != NULL && tab->isActive() )
  212. selectPage( tab );
  213. }
  214. }
  215. void GuiTabBookCtrl::onTouchMove(const GuiEvent &event)
  216. {
  217. Point2I localMouse = globalToLocalCoord( event.mousePoint );
  218. if( mTabRect.pointInRect( localMouse ) )
  219. {
  220. Point2I tabLocalMouse = getTabLocalCoord(localMouse);
  221. GuiTabPageCtrl *tab = findHitTab(tabLocalMouse);
  222. if( tab != NULL && mHoverTab != tab )
  223. mHoverTab = tab;
  224. else if ( !tab )
  225. mHoverTab = NULL;
  226. }
  227. else
  228. {
  229. mHoverTab = NULL;
  230. }
  231. Parent::onTouchMove( event );
  232. }
  233. void GuiTabBookCtrl::onTouchLeave( const GuiEvent &event )
  234. {
  235. mHoverTab = NULL;
  236. }
  237. bool GuiTabBookCtrl::onMouseDownEditor(const GuiEvent &event, Point2I offset)
  238. {
  239. bool handled = false;
  240. Point2I localMouse = globalToLocalCoord( event.mousePoint );
  241. if( mTabRect.pointInRect( localMouse ) )
  242. {
  243. GuiTabPageCtrl *tab = findHitTab( localMouse );
  244. if( tab != NULL )
  245. {
  246. selectPage( tab );
  247. handled = true;
  248. }
  249. }
  250. // This shouldn't be called if it's not design time, but check just incase
  251. if ( GuiControl::smDesignTime )
  252. {
  253. // If we clicked in the editor and our addset is the tab book
  254. // ctrl, select the child ctrl so we can edit it's properties
  255. GuiEditCtrl* edit = GuiControl::smEditorHandle;
  256. if( edit && ( edit->getAddSet() == this ) && mActivePage != NULL )
  257. edit->select( mActivePage );
  258. }
  259. // Return whether we handled this or not.
  260. return handled;
  261. }
  262. void GuiTabBookCtrl::onPreRender()
  263. {
  264. // sometimes we need to resize because of a changed persistent field
  265. // that's what this does
  266. solveDirty();
  267. }
  268. void GuiTabBookCtrl::onRender(Point2I offset, const RectI &updateRect)
  269. {
  270. RectI ctrlRect = applyMargins(offset + mTabRect.point, mTabRect.extent, NormalState, mProfile);
  271. if (!ctrlRect.isValidRect())
  272. {
  273. return;
  274. }
  275. renderUniversalRect(ctrlRect, mProfile, NormalState);
  276. RectI fillRect = applyBorders(ctrlRect.point, ctrlRect.extent, NormalState, mProfile);
  277. RectI contentRect = applyPadding(fillRect.point, fillRect.extent, NormalState, mProfile);
  278. if (contentRect.isValidRect())
  279. {
  280. renderTabs(contentRect.point);
  281. }
  282. if(mPageRect.isValidRect())
  283. {
  284. // Render Children
  285. renderChildControls(offset, mBounds, updateRect);
  286. }
  287. }
  288. void GuiTabBookCtrl::renderTabs( const Point2I &offset )
  289. {
  290. // If the tab size is zero, don't render tabs,
  291. // and assume it's a tab-less tab-book - JDD
  292. if( mPages.empty())
  293. return;
  294. for( S32 i = 0; i < mPages.size(); i++ )
  295. {
  296. RectI tabBounds = mPages[i].TabRect;
  297. tabBounds.point += offset;
  298. GuiTabPageCtrl *tab = mPages[i].Page;
  299. if( tab != NULL )
  300. renderTab( tabBounds, tab );
  301. }
  302. }
  303. void GuiTabBookCtrl::renderTab( RectI tabRect, GuiTabPageCtrl *tab )
  304. {
  305. StringTableEntry text = tab->getText();
  306. GuiControlState currentState = GuiControlState::NormalState;
  307. if (mActivePage == tab)
  308. {
  309. currentState = SelectedState;
  310. }
  311. else if (mHoverTab == tab)
  312. {
  313. currentState = HighlightState;
  314. }
  315. RectI ctrlRect = applyMargins(tabRect.point, tabRect.extent, currentState, mTabProfile);
  316. if (!ctrlRect.isValidRect())
  317. {
  318. return;
  319. }
  320. renderUniversalRect(ctrlRect, mTabProfile, currentState);
  321. //Render Text
  322. dglSetBitmapModulation(mTabProfile->getFontColor(currentState));
  323. RectI fillRect = applyBorders(ctrlRect.point, ctrlRect.extent, currentState, mTabProfile);
  324. RectI contentRect = applyPadding(fillRect.point, fillRect.extent, currentState, mTabProfile);
  325. TextRotationOptions rot = tRotateNone;
  326. if (mTabPosition == AlignLeft)
  327. {
  328. rot = tRotateLeft;
  329. }
  330. else if(mTabPosition == AlignRight)
  331. {
  332. rot = tRotateRight;
  333. }
  334. renderText(contentRect.point, contentRect.extent, text, mTabProfile, rot);
  335. /*
  336. // Is this a skinned control?
  337. if( mHasTexture && mProfile->mBitmapArrayRects.size() >= 9 )
  338. {
  339. S32 indexMultiplier = 1;
  340. switch( mTabPosition )
  341. {
  342. case AlignTop:
  343. case AlignBottom:
  344. if ( mActivePage == tab )
  345. indexMultiplier += TabSelected;
  346. else if( mHoverTab == tab )
  347. indexMultiplier += TabHover;
  348. else
  349. indexMultiplier += TabNormal;
  350. //dglDrawBitmapStretchSR(mProfile->mTextureHandle,tabRect,stretchRect, ( mTabPosition == AlignBottom ) ? GFlip_Y : 0 );
  351. break;
  352. case AlignLeft:
  353. case AlignRight:
  354. if ( mActivePage == tab )
  355. indexMultiplier += TabSelectedVertical;
  356. else if( mHoverTab == tab )
  357. indexMultiplier += TabHoverVertical;
  358. else
  359. indexMultiplier += TabNormalVertical;
  360. //dglDrawBitmapStretchSR(mProfile->mTextureHandle,tabRect,stretchRect, ( mTabPosition == AlignRight ) ? GFlip_X : 0 );
  361. break;
  362. }
  363. renderFixedBitmapBordersFilled( tabRect, indexMultiplier, mProfile );
  364. }
  365. else
  366. {
  367. // If this isn't a skinned control or the bitmap is simply missing, handle it WELL
  368. if ( mActivePage == tab )
  369. dglDrawRectFill(tabRect, mProfile->mFillColor);
  370. else if( mHoverTab == tab )
  371. dglDrawRectFill(tabRect, mProfile->mFillColorHL);
  372. else
  373. dglDrawRectFill(tabRect, mProfile->mFillColorNA);
  374. }
  375. dglSetBitmapModulation(mProfile->mFontColor);
  376. switch( mTabPosition )
  377. {
  378. case AlignTop:
  379. case AlignBottom:
  380. renderJustifiedTextRot( tabRect.point, tabRect.extent, text, 0);
  381. break;
  382. case AlignLeft:
  383. renderJustifiedTextRot( tabRect.point, tabRect.extent, text, -90 );
  384. break;
  385. case AlignRight:
  386. renderJustifiedTextRot( tabRect.point, tabRect.extent, text, -90 );
  387. break;
  388. }
  389. */
  390. }
  391. // This is nothing but a clever hack to allow the tab page children
  392. // to cast this to a GuiControl* so that the file doesn't need to have circular
  393. // includes. generic method overriding for the win!
  394. void GuiTabBookCtrl::setUpdate()
  395. {
  396. Parent::setUpdate();
  397. setUpdateRegion(Point2I(0,0), mBounds.extent);
  398. calculatePageTabs();
  399. }
  400. void GuiTabBookCtrl::solveDirty()
  401. {
  402. bool dirty = false;
  403. if( mTabPosition != mLastTabPosition )
  404. {
  405. mLastTabPosition = mTabPosition;
  406. dirty = true;
  407. }
  408. if( mTabProfile != NULL && mTabProfile->mFont != NULL && mTabProfile->mFont->getHeight() != mLastFontHeight )
  409. {
  410. mLastFontHeight = mTabProfile->mFont->getHeight();
  411. dirty = true;
  412. }
  413. if( mTabWidth != mLastTabWidth )
  414. {
  415. mLastTabWidth = mTabWidth;
  416. dirty = true;
  417. }
  418. if( dirty )
  419. {
  420. resize( mBounds.point, mBounds.extent );
  421. }
  422. }
  423. S32 GuiTabBookCtrl::calculatePageTabWidth( GuiTabPageCtrl *page )
  424. {
  425. if( !page )
  426. return mTabWidth;
  427. StringTableEntry text = page->getText();
  428. if( !text || dStrlen(text) == 0 || !mTabProfile || !mTabProfile->mFont || mTabProfile->mFont == '\0' )
  429. return mTabWidth;
  430. S32 textLength = mTabProfile->mFont->getStrNWidth(text, dStrlen(text));
  431. Point2I outerExtent = getOuterExtent(Point2I(textLength, textLength), NormalState, mTabProfile);
  432. if (mTabPosition == AlignTop || mTabPosition == AlignBottom)
  433. {
  434. return outerExtent.x;
  435. }
  436. else
  437. {
  438. return outerExtent.y;
  439. }
  440. }
  441. void GuiTabBookCtrl::calculatePageTabs()
  442. {
  443. // Short Circuit.
  444. //
  445. // If the tab size is zero, don't render tabs,
  446. // and assume it's a tab-less tab-book - JDD
  447. if( mPages.empty())
  448. return;
  449. S32 currRow = 0;
  450. S32 currColumn = 0;
  451. S32 currX = 0;
  452. S32 currY = 0;
  453. S32 tabHeight = 0;
  454. RectI innerRect = getInnerRect(mBounds.point, mBounds.extent, NormalState, mProfile);
  455. Point2I fontBasedBounds = getOuterExtent(Point2I(mTabProfile->mFont->getHeight(), mTabProfile->mFont->getHeight()), NormalState, mTabProfile);
  456. if (mTabPosition == AlignTop || mTabPosition == AlignBottom)
  457. {
  458. tabHeight = fontBasedBounds.y;
  459. }
  460. else
  461. {
  462. tabHeight = fontBasedBounds.x;
  463. }
  464. for( S32 i = 0; i < mPages.size(); i++ )
  465. {
  466. // Fetch Tab Width
  467. S32 tabWidth = calculatePageTabWidth( mPages[i].Page );
  468. tabWidth = getMax( tabWidth, mMinTabWidth );
  469. TabHeaderInfo &info = mPages[i];
  470. switch( mTabPosition )
  471. {
  472. case AlignTop:
  473. case AlignBottom:
  474. // If we're going to go outside our bounds
  475. // with this tab move it down a row
  476. if( currX + tabWidth > innerRect.extent.x )
  477. {
  478. // Calculate and Advance State.
  479. balanceRow( currRow, currX );
  480. info.TabRow = ++currRow;
  481. // Reset Necessaries
  482. info.TabColumn = currColumn = currX = 0;
  483. }
  484. else
  485. {
  486. info.TabRow = currRow;
  487. info.TabColumn = currColumn++;
  488. }
  489. // Calculate Tabs Bounding Rect
  490. info.TabRect.point.x = currX;
  491. info.TabRect.point.y = (info.TabRow * tabHeight);
  492. info.TabRect.extent.x = tabWidth;
  493. info.TabRect.extent.y = tabHeight;
  494. currX += tabWidth;
  495. break;
  496. case AlignLeft:
  497. case AlignRight:
  498. // If we're going to go outside our bounds
  499. // with this tab move it down a row
  500. if( currY + tabWidth > innerRect.extent.y )
  501. {
  502. // Balance Tab Column.
  503. balanceColumn( currColumn, currY );
  504. // Calculate and Advance State.
  505. info.TabColumn = ++currColumn;
  506. info.TabRow = currRow = currY = 0;
  507. }
  508. else
  509. {
  510. info.TabColumn = currColumn;
  511. info.TabRow = currRow++;
  512. }
  513. // Calculate Tabs Bounding Rect
  514. info.TabRect.point.x = (info.TabColumn * tabHeight);
  515. info.TabRect.point.y = currY;
  516. info.TabRect.extent.x = tabHeight;
  517. info.TabRect.extent.y = tabWidth;
  518. currY += tabWidth;
  519. break;
  520. };
  521. }
  522. currRow++;
  523. currColumn++;
  524. Point2I outerExtent = getOuterExtent(Point2I(currColumn * tabHeight, currRow * tabHeight), NormalState, mProfile);
  525. // Calculate
  526. switch( mTabPosition )
  527. {
  528. case AlignTop:
  529. mTabRect.point.x = 0;
  530. mTabRect.point.y = 0;
  531. mTabRect.extent.x = mBounds.extent.x;
  532. mTabRect.extent.y = outerExtent.y;
  533. mPageRect.point.x = 0;
  534. mPageRect.point.y = mTabRect.extent.y;
  535. mPageRect.extent.x = mTabRect.extent.x;
  536. mPageRect.extent.y = mBounds.extent.y - mTabRect.extent.y;
  537. break;
  538. case AlignBottom:
  539. mTabRect.point.x = 0;
  540. mTabRect.point.y = mBounds.extent.y - mTabRect.extent.y;
  541. mTabRect.extent.x = mBounds.extent.x;
  542. mTabRect.extent.y = outerExtent.y;
  543. mPageRect.point.x = 0;
  544. mPageRect.point.y = 0;
  545. mPageRect.extent.x = mTabRect.extent.x;
  546. mPageRect.extent.y = mBounds.extent.y - mTabRect.extent.y;
  547. break;
  548. case AlignLeft:
  549. mTabRect.point.x = 0;
  550. mTabRect.point.y = 0;
  551. mTabRect.extent.x = outerExtent.x;
  552. mTabRect.extent.y = mBounds.extent.y;
  553. mPageRect.point.x = mTabRect.extent.x;
  554. mPageRect.point.y = 0;
  555. mPageRect.extent.x = mBounds.extent.x - mTabRect.extent.x;
  556. mPageRect.extent.y = mBounds.extent.y;
  557. break;
  558. case AlignRight:
  559. mTabRect.point.x = mBounds.extent.x - mTabRect.extent.x;
  560. mTabRect.point.y = 0;
  561. mTabRect.extent.x = outerExtent.x;
  562. mTabRect.extent.y = mBounds.extent.y;
  563. mPageRect.point.x = 0;
  564. mPageRect.point.y = 0;
  565. mPageRect.extent.x = mBounds.extent.x - mTabRect.extent.x;
  566. mPageRect.extent.y = mTabRect.extent.y;
  567. break;
  568. };
  569. }
  570. void GuiTabBookCtrl::balanceColumn( S32 column , S32 totalTabWidth )
  571. {
  572. // Short Circuit.
  573. //
  574. // If the tab size is zero, don't render tabs,
  575. // and assume it's a tab-less tab-book - JDD
  576. if( mPages.empty())
  577. return;
  578. Vector<TabHeaderInfo*> rowTemp;
  579. rowTemp.clear();
  580. for( S32 i = 0; i < mPages.size(); i++ )
  581. {
  582. TabHeaderInfo &info = mPages[i];
  583. if(info.TabColumn == column )
  584. rowTemp.push_back( &mPages[i] );
  585. }
  586. if( rowTemp.empty() )
  587. return;
  588. // Balance the tabs across the remaining space
  589. RectI innerRect = getInnerRect(mBounds.point, mBounds.extent, NormalState, mProfile);
  590. S32 spaceToDivide = innerRect.extent.y - totalTabWidth;
  591. S32 pointDelta = 0;
  592. for( S32 i = 0; i < rowTemp.size(); i++ )
  593. {
  594. TabHeaderInfo &info = *rowTemp[i];
  595. S32 extraSpace = (S32)( spaceToDivide / rowTemp.size() );
  596. info.TabRect.extent.y += extraSpace;
  597. info.TabRect.point.y += pointDelta;
  598. pointDelta += extraSpace;
  599. }
  600. }
  601. void GuiTabBookCtrl::balanceRow( S32 row, S32 totalTabWidth )
  602. {
  603. // Short Circuit.
  604. //
  605. // If the tab size is zero, don't render tabs,
  606. // and assume it's a tab-less tab-book - JDD
  607. if( mPages.empty())
  608. return;
  609. Vector<TabHeaderInfo*> rowTemp;
  610. rowTemp.clear();
  611. for( S32 i = 0; i < mPages.size(); i++ )
  612. {
  613. TabHeaderInfo &info = mPages[i];
  614. if(info.TabRow == row )
  615. rowTemp.push_back( &mPages[i] );
  616. }
  617. if( rowTemp.empty() )
  618. return;
  619. // Balance the tabs across the remaining space
  620. RectI innerRect = getInnerRect(mBounds.point, mBounds.extent, NormalState, mProfile);
  621. S32 spaceToDivide = innerRect.extent.x - 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. GuiTabPageCtrl *GuiTabBookCtrl::findHitTab( const GuiEvent &event )
  633. {
  634. return findHitTab( event.mousePoint );
  635. }
  636. GuiTabPageCtrl *GuiTabBookCtrl::findHitTab( Point2I hitPoint )
  637. {
  638. // Short Circuit.
  639. //
  640. // If the tab size is zero, don't render tabs,
  641. // and assume it's a tab-less tab-book - JDD
  642. if( mPages.empty())
  643. return NULL;
  644. for( S32 i = 0; i < mPages.size(); i++ )
  645. {
  646. if( mPages[i].TabRect.pointInRect( hitPoint ) )
  647. return mPages[i].Page;
  648. }
  649. return NULL;
  650. }
  651. void GuiTabBookCtrl::selectPage( S32 index )
  652. {
  653. if( index < 0 || index >= mPages.size())
  654. return;
  655. // Select the page
  656. selectPage( mPages[ index ].Page );
  657. }
  658. void GuiTabBookCtrl::selectPage( GuiTabPageCtrl *page )
  659. {
  660. Vector<TabHeaderInfo>::iterator i = mPages.begin();
  661. for( ; i != mPages.end() ; i++ )
  662. {
  663. GuiTabPageCtrl *tab = reinterpret_cast<GuiTabPageCtrl*>((*i).Page);
  664. if( page == tab )
  665. {
  666. mActivePage = tab;
  667. tab->setVisible( true );
  668. // Notify User
  669. char *retBuffer = Con::getReturnBuffer( 512 );
  670. dStrcpy( retBuffer, tab->getText() );
  671. Con::executef( this, 2, "onTabSelected", retBuffer );
  672. }
  673. else
  674. tab->setVisible( false );
  675. }
  676. }
  677. void GuiTabBookCtrl::selectPage( const char* pageName )
  678. {
  679. Vector<TabHeaderInfo>::iterator i = mPages.begin();
  680. for( ; i != mPages.end() ; i++ )
  681. {
  682. GuiTabPageCtrl *tab = reinterpret_cast<GuiTabPageCtrl*>((*i).Page);
  683. if( dStricmp( pageName, tab->getText() ) == 0 )
  684. {
  685. mActivePage = tab;
  686. tab->setVisible( true );
  687. // Notify User
  688. char *retBuffer = Con::getReturnBuffer( 512 );
  689. dStrcpy( retBuffer, tab->getText() );
  690. Con::executef( this, 2, "onTabSelected", retBuffer );
  691. }
  692. else
  693. tab->setVisible( false );
  694. }
  695. }
  696. bool GuiTabBookCtrl::onKeyDown(const GuiEvent &event)
  697. {
  698. // Tab = Next Page
  699. // Ctrl-Tab = Previous Page
  700. if( 0 && event.keyCode == KEY_TAB )
  701. {
  702. if( event.modifier & SI_CTRL )
  703. selectPrevPage();
  704. else
  705. selectNextPage();
  706. return true;
  707. }
  708. return Parent::onKeyDown( event );
  709. }
  710. void GuiTabBookCtrl::selectNextPage()
  711. {
  712. if( mPages.empty() )
  713. return;
  714. if( mActivePage == NULL )
  715. mActivePage = mPages[0].Page;
  716. S32 nI = 0;
  717. for( ; nI < mPages.size(); nI++ )
  718. {
  719. GuiTabPageCtrl *tab = mPages[ nI ].Page;
  720. if( tab == mActivePage )
  721. {
  722. if( nI == ( mPages.size() - 1 ) )
  723. selectPage( 0 );
  724. else if ( nI + 1 <= ( mPages.size() - 1 ) )
  725. selectPage( nI + 1 );
  726. else
  727. selectPage( 0 );
  728. // Notify User
  729. if( isMethod( "onTabSelected" ) )
  730. {
  731. char *retBuffer = Con::getReturnBuffer( 512 );
  732. dStrcpy( retBuffer, tab->getText() );
  733. Con::executef( this, 2, "onTabSelected", retBuffer );
  734. }
  735. return;
  736. }
  737. }
  738. }
  739. void GuiTabBookCtrl::selectPrevPage()
  740. {
  741. if( mPages.empty() )
  742. return;
  743. if( mActivePage == NULL )
  744. mActivePage = mPages[0].Page;
  745. S32 nI = 0;
  746. for( ; nI < mPages.size(); nI++ )
  747. {
  748. GuiTabPageCtrl *tab = mPages[ nI ].Page;
  749. if( tab == mActivePage )
  750. {
  751. if( nI == 0 )
  752. selectPage( mPages.size() - 1 );
  753. else
  754. selectPage( nI - 1 );
  755. // Notify User
  756. if( isMethod( "onTabSelected" ) )
  757. {
  758. char *retBuffer = Con::getReturnBuffer( 512 );
  759. dStrcpy( retBuffer, tab->getText() );
  760. Con::executef( this, 2, "onTabSelected", retBuffer );
  761. }
  762. return;
  763. }
  764. }
  765. }