guiTabBookCtrl.cc 25 KB

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