guiTabBookCtrl.cpp
Engine/source/gui/containers/guiTabBookCtrl.cpp
Public Functions
ConsoleDocClass(GuiTabBookCtrl , "@brief A container \<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Create \<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@note Only GuiTabPageCtrls must be added <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> GuiTabBookCtrls. If an object of <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> different " "class is added <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the control, it will be reassigned <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> either the active page or the " "tab book 's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">parent.\n\n</a>" " @see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">GuiTabPageCtrl\n</a>" " @ingroup GuiContainers" )
DefineEngineMethod(GuiTabBookCtrl , addPage , void , (const char *title) , ("") , "Add <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> tab page <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">control.\n\n</a>" "@param title Title text <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the tab page header." )
DefineEngineMethod(GuiTabBookCtrl , getSelectedPage , S32 , () , "Get the index of the currently selected tab <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">page.\n\n</a>" "@return Index of the selected tab page or -1 <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> no tab page is selected." )
DefineEngineMethod(GuiTabBookCtrl , selectPage , void , (S32 index) , "Set the selected tab <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">page.\n\n</a>" "@param index Index of the tab page." )
IMPLEMENT_CALLBACK(GuiTabBookCtrl , onTabRightClick , void , (const String &text, U32 index) , (text, index) , "Called when the user right-clicks on <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> tab page <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">header.\n\n</a>" "@param text Text of the page header <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the tab that is being <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">selected.\n</a>" "@param index Index of the tab page being selected." )
IMPLEMENT_CALLBACK(GuiTabBookCtrl , onTabSelected , void , (const String &text, U32 index) , (text, index) , "Called when <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> tab page is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">selected.\n\n</a>" "@param text Text of the page header <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the tab that is being <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">selected.\n</a>" "@param index Index of the tab page being selected." )
ImplementEnumType(GuiTabPosition , "Where the <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> should put the tab headers <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> selecting individual <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">pages.\n\n</a>" "@ingroup GuiContainers" )
Detailed Description
Public Functions
ConsoleDocClass(GuiTabBookCtrl , "@brief A container \<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Create \<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@note Only GuiTabPageCtrls must be added <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> GuiTabBookCtrls. If an object of <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> different " "class is added <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the control, it will be reassigned <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> either the active page or the " "tab book 's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">parent.\n\n</a>" " @see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">GuiTabPageCtrl\n</a>" " @ingroup GuiContainers" )
DefineEngineMethod(GuiTabBookCtrl , addPage , void , (const char *title) , ("") , "Add <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> tab page <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">control.\n\n</a>" "@param title Title text <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the tab page header." )
DefineEngineMethod(GuiTabBookCtrl , getSelectedPage , S32 , () , "Get the index of the currently selected tab <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">page.\n\n</a>" "@return Index of the selected tab page or -1 <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> no tab page is selected." )
DefineEngineMethod(GuiTabBookCtrl , selectPage , void , (S32 index) , "Set the selected tab <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">page.\n\n</a>" "@param index Index of the tab page." )
IMPLEMENT_CALLBACK(GuiTabBookCtrl , onTabRightClick , void , (const String &text, U32 index) , (text, index) , "Called when the user right-clicks on <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> tab page <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">header.\n\n</a>" "@param text Text of the page header <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the tab that is being <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">selected.\n</a>" "@param index Index of the tab page being selected." )
IMPLEMENT_CALLBACK(GuiTabBookCtrl , onTabSelected , void , (const String &text, U32 index) , (text, index) , "Called when <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> tab page is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">selected.\n\n</a>" "@param text Text of the page header <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the tab that is being <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">selected.\n</a>" "@param index Index of the tab page being selected." )
IMPLEMENT_CONOBJECT(GuiTabBookCtrl )
ImplementEnumType(GuiTabPosition , "Where the <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> should put the tab headers <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> selecting individual <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">pages.\n\n</a>" "@ingroup GuiContainers" )
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "gui/containers/guiTabBookCtrl.h" 25#include "console/engineAPI.h" 26#include "gui/core/guiCanvas.h" 27#include "gui/editor/guiEditCtrl.h" 28#include "gui/controls/guiPopUpCtrl.h" 29#include "gui/core/guiDefaultControlRender.h" 30#include "gfx/gfxDrawUtil.h" 31 32 33IMPLEMENT_CONOBJECT( GuiTabBookCtrl ); 34 35ConsoleDocClass( GuiTabBookCtrl, 36 "@brief A container \n\n" 37 38 "@tsexample\n" 39 "// Create \n" 40 "@endtsexample\n\n" 41 42 "@note Only GuiTabPageCtrls must be added to GuiTabBookCtrls. If an object of a different " 43 "class is added to the control, it will be reassigned to either the active page or the " 44 "tab book's parent.\n\n" 45 46 "@see GuiTabPageCtrl\n" 47 48 "@ingroup GuiContainers" 49); 50 51ImplementEnumType( GuiTabPosition, 52 "Where the control should put the tab headers for selecting individual pages.\n\n" 53 "@ingroup GuiContainers" ) 54 { GuiTabBookCtrl::AlignTop, "Top", "Tab headers on top edge." }, 55 { GuiTabBookCtrl::AlignBottom,"Bottom", "Tab headers on bottom edge." } 56EndImplementEnumType; 57 58IMPLEMENT_CALLBACK( GuiTabBookCtrl, onTabSelected, void, ( const String& text, U32 index ), ( text, index ), 59 "Called when a new tab page is selected.\n\n" 60 "@param text Text of the page header for the tab that is being selected.\n" 61 "@param index Index of the tab page being selected." ); 62IMPLEMENT_CALLBACK( GuiTabBookCtrl, onTabRightClick, void, ( const String& text, U32 index ), ( text, index ), 63 "Called when the user right-clicks on a tab page header.\n\n" 64 "@param text Text of the page header for the tab that is being selected.\n" 65 "@param index Index of the tab page being selected." ); 66 67 68//----------------------------------------------------------------------------- 69 70GuiTabBookCtrl::GuiTabBookCtrl() 71{ 72 VECTOR_SET_ASSOCIATION( mPages ); 73 74 mTabHeight = 24; 75 mTabPosition = AlignTop; 76 mActivePage = NULL; 77 mHoverTab = NULL; 78 mHasTexture = false; 79 mBitmapBounds = NULL; 80 setExtent( 400, 300 ); 81 mPageRect = RectI(0,0,0,0); 82 mTabRect = RectI(0,0,0,0); 83 mFrontTabPadding = 0; 84 85 mPages.reserve(12); 86 mTabMargin = 7; 87 mMinTabWidth = 64; 88 mIsContainer = true; 89 mSelectedPageNum = -1; 90 mDefaultPageNum = -1; 91 92 mAllowReorder = false; 93 mDraggingTab = false; 94 mDraggingTabRect = false; 95 mIsFirstWake = true; 96} 97 98//----------------------------------------------------------------------------- 99 100void GuiTabBookCtrl::initPersistFields() 101{ 102 addGroup( "TabBook" ); 103 104 addField( "tabPosition", TYPEID< TabPosition >(), Offset( mTabPosition, GuiTabBookCtrl ), 105 "Where to place the tab page headers." ); 106 addField( "tabMargin", TypeS32, Offset( mTabMargin, GuiTabBookCtrl ), 107 "Spacing to put between individual tab page headers." ); 108 addField( "minTabWidth", TypeS32, Offset( mMinTabWidth, GuiTabBookCtrl ), 109 "Minimum width allocated to a tab page header." ); 110 addField( "tabHeight", TypeS32, Offset( mTabHeight, GuiTabBookCtrl ), 111 "Height of tab page headers." ); 112 addField( "allowReorder", TypeBool, Offset( mAllowReorder, GuiTabBookCtrl ), 113 "Whether reordering tabs with the mouse is allowed." ); 114 addField( "defaultPage", TypeS32, Offset( mDefaultPageNum, GuiTabBookCtrl ), 115 "Index of page to select on first onWake() call (-1 to disable)." ); 116 117 addProtectedField( "selectedPage", TypeS32, Offset( mSelectedPageNum, GuiTabBookCtrl ), 118 &_setSelectedPage, &defaultProtectedGetFn, 119 "Index of currently selected page." ); 120 121 addField( "frontTabPadding", TypeS32, Offset( mFrontTabPadding, GuiTabBookCtrl ), 122 "X offset of first tab page header." ); 123 124 endGroup( "TabBook" ); 125 126 Parent::initPersistFields(); 127} 128 129//----------------------------------------------------------------------------- 130 131void GuiTabBookCtrl::onChildRemoved( GuiControl* child ) 132{ 133 for (S32 i = 0; i < mPages.size(); i++ ) 134 { 135 GuiTabPageCtrl* tab = mPages[i].Page; 136 if( tab == child ) 137 { 138 mPages.erase( i ); 139 break; 140 } 141 } 142 143 // Calculate Page Information 144 calculatePageTabs(); 145 146 // Active Index. 147 mSelectedPageNum = getMin( mSelectedPageNum, mPages.size() - 1 ); 148 149 if ( mSelectedPageNum != -1 ) 150 { 151 // Select Page. 152 selectPage( mSelectedPageNum ); 153 } 154} 155 156//----------------------------------------------------------------------------- 157 158void GuiTabBookCtrl::onChildAdded( GuiControl *child ) 159{ 160 GuiTabPageCtrl *page = dynamic_cast<GuiTabPageCtrl*>(child); 161 if( !page ) 162 { 163 Con::warnf("GuiTabBookCtrl::onChildAdded - attempting to add NON GuiTabPageCtrl as child page"); 164 SimObject *simObj = reinterpret_cast<SimObject*>(child); 165 removeObject( simObj ); 166 if( mActivePage ) 167 { 168 mActivePage->addObject( simObj ); 169 } 170 else 171 { 172 Con::warnf("GuiTabBookCtrl::onChildAdded - unable to find active page to reassign ownership of new child control to, placing on parent"); 173 GuiControl *rent = getParent(); 174 if( rent ) 175 rent->addObject( simObj ); 176 } 177 return; 178 } 179 180 TabHeaderInfo newPage; 181 182 newPage.Page = page; 183 newPage.TabRow = -1; 184 newPage.TabColumn = -1; 185 186 mPages.push_back( newPage ); 187 188 // Calculate Page Information 189 calculatePageTabs(); 190 191 if( page->getFitBook() ) 192 fitPage( page ); 193 194 // Select this Page 195 selectPage( page ); 196} 197 198//----------------------------------------------------------------------------- 199 200bool GuiTabBookCtrl::reOrder(SimObject* obj, SimObject* target) 201{ 202 if ( !Parent::reOrder(obj, target) ) 203 return false; 204 205 // Store the Selected Page. 206 GuiTabPageCtrl *selectedPage = NULL; 207 if ( mSelectedPageNum != -1 ) 208 selectedPage = mPages[mSelectedPageNum].Page; 209 210 // Determine the Target Page Index. 211 S32 targetIndex = -1; 212 for( S32 i = 0; i < mPages.size(); i++ ) 213 { 214 const TabHeaderInfo &info = mPages[i]; 215 if ( info.Page == target ) 216 { 217 targetIndex = i; 218 break; 219 } 220 } 221 222 if ( targetIndex == -1 ) 223 { 224 return false; 225 } 226 227 for( S32 i = 0; i < mPages.size(); i++ ) 228 { 229 const TabHeaderInfo &info = mPages[i]; 230 if ( info.Page == obj ) 231 { 232 // Store Info. 233 TabHeaderInfo objPage = info; 234 235 // Remove. 236 mPages.erase( i ); 237 // Insert. 238 mPages.insert( targetIndex, objPage ); 239 240 break; 241 } 242 } 243 244 // Update Tabs. 245 calculatePageTabs(); 246 247 // Reselect Page. 248 selectPage( selectedPage ); 249 250 return true; 251} 252 253//----------------------------------------------------------------------------- 254 255bool GuiTabBookCtrl::acceptsAsChild( SimObject* object ) const 256{ 257 // Only accept tab pages. 258 return ( dynamic_cast< GuiTabPageCtrl* >( object ) != NULL ); 259} 260 261//----------------------------------------------------------------------------- 262 263bool GuiTabBookCtrl::onWake() 264{ 265 if (! Parent::onWake()) 266 return false; 267 268 mHasTexture = mProfile->constructBitmapArray() > 0; 269 if( mHasTexture ) 270 { 271 mBitmapBounds = mProfile->mBitmapArrayRects.address(); 272 mTabHeight = mBitmapBounds[TabSelected].extent.y; 273 } 274 275 calculatePageTabs(); 276 277 if( mIsFirstWake ) 278 { 279 // Awaken all pages, visible or not. We need to do this so 280 // any pages that make use of a language table for their label 281 // are correctly initialized. 282 for ( U32 i = 0; i < mPages.size(); ++i) 283 { 284 if ( !mPages[i].Page->isAwake() ) 285 { 286 mPages[i].Page->awaken(); 287 } 288 } 289 290 if( mDefaultPageNum >= 0 && mDefaultPageNum < mPages.size() ) 291 selectPage( mDefaultPageNum ); 292 293 mIsFirstWake = false; 294 } 295 296 return true; 297} 298 299//----------------------------------------------------------------------------- 300 301void GuiTabBookCtrl::addNewPage( const char* text ) 302{ 303 GuiTabPageCtrl* page = new GuiTabPageCtrl(); 304 305 if( text ) 306 page->setText( text ); 307 308 page->registerObject(); 309 addObject( page ); 310} 311 312//----------------------------------------------------------------------------- 313 314bool GuiTabBookCtrl::resize(const Point2I &newPosition, const Point2I &newExtent) 315{ 316 bool result = Parent::resize( newPosition, newExtent ); 317 318 calculatePageTabs(); 319 320 return result; 321} 322 323//----------------------------------------------------------------------------- 324 325void GuiTabBookCtrl::childResized(GuiControl *child) 326{ 327 Parent::childResized( child ); 328 329 //child->resize( mPageRect.point, mPageRect.extent ); 330} 331 332//----------------------------------------------------------------------------- 333 334void GuiTabBookCtrl::onMouseDown(const GuiEvent &event) 335{ 336 mDraggingTab = false; 337 mDraggingTabRect = false; 338 Point2I localMouse = globalToLocalCoord( event.mousePoint ); 339 if( mTabRect.pointInRect( localMouse ) ) 340 { 341 GuiTabPageCtrl *tab = findHitTab( localMouse ); 342 if( tab != NULL ) 343 { 344 selectPage( tab ); 345 mDraggingTab = mAllowReorder; 346 } 347 else 348 { 349 mDraggingTabRect = true; 350 } 351 } 352} 353 354//----------------------------------------------------------------------------- 355 356void GuiTabBookCtrl::onMouseUp(const GuiEvent &event) 357{ 358 Parent::onMouseUp( event ); 359 360 mDraggingTab = false; 361 mDraggingTabRect = false; 362} 363 364//----------------------------------------------------------------------------- 365 366void GuiTabBookCtrl::onMouseDragged(const GuiEvent &event) 367{ 368 Parent::onMouseDragged( event ); 369 370 if ( !mDraggingTab ) 371 return; 372 373 GuiTabPageCtrl *selectedPage = NULL; 374 if ( mSelectedPageNum != -1 ) 375 selectedPage = mPages[mSelectedPageNum].Page; 376 377 if ( !selectedPage ) 378 return; 379 380 Point2I localMouse = globalToLocalCoord( event.mousePoint ); 381 if( mTabRect.pointInRect( localMouse ) ) 382 { 383 GuiTabPageCtrl *tab = findHitTab( localMouse ); 384 if( tab != NULL && tab != selectedPage ) 385 { 386 S32 targetIndex = -1; 387 for( S32 i = 0; i < mPages.size(); i++ ) 388 { 389 if( mPages[i].Page == tab ) 390 { 391 targetIndex = i; 392 break; 393 } 394 } 395 396 if ( targetIndex > mSelectedPageNum ) 397 { 398 reOrder( tab, selectedPage ); 399 } 400 else 401 { 402 reOrder( selectedPage, tab ); 403 } 404 } 405 } 406} 407 408//----------------------------------------------------------------------------- 409 410void GuiTabBookCtrl::onMouseMove(const GuiEvent &event) 411{ 412 Point2I localMouse = globalToLocalCoord( event.mousePoint ); 413 if( mTabRect.pointInRect( localMouse ) ) 414 { 415 GuiTabPageCtrl *tab = findHitTab( localMouse ); 416 if( tab != NULL && mHoverTab != tab ) 417 mHoverTab = tab; 418 else if ( !tab ) 419 mHoverTab = NULL; 420 } 421 Parent::onMouseMove( event ); 422} 423 424//----------------------------------------------------------------------------- 425 426void GuiTabBookCtrl::onMouseLeave( const GuiEvent &event ) 427{ 428 Parent::onMouseLeave( event ); 429 430 mHoverTab = NULL; 431} 432 433//----------------------------------------------------------------------------- 434 435bool GuiTabBookCtrl::onMouseDownEditor(const GuiEvent &event, Point2I offset) 436{ 437 bool handled = false; 438 Point2I localMouse = globalToLocalCoord( event.mousePoint ); 439 440 if( mTabRect.pointInRect( localMouse ) ) 441 { 442 GuiTabPageCtrl *tab = findHitTab( localMouse ); 443 if( tab != NULL ) 444 { 445 selectPage( tab ); 446 handled = true; 447 } 448 } 449 450#ifdef TORQUE_TOOLS 451 // This shouldn't be called if it's not design time, but check just incase 452 if ( GuiControl::smDesignTime ) 453 { 454 // If we clicked in the editor and our addset is the tab book 455 // ctrl, select the child ctrl so we can edit it's properties 456 GuiEditCtrl* edit = GuiControl::smEditorHandle; 457 if( edit && ( edit->getAddSet() == this ) && mActivePage != NULL ) 458 edit->select( mActivePage ); 459 } 460#endif 461 462 // Return whether we handled this or not. 463 return handled; 464 465} 466 467//----------------------------------------------------------------------------- 468 469void GuiTabBookCtrl::onRightMouseUp( const GuiEvent& event ) 470{ 471 Point2I localMouse = globalToLocalCoord( event.mousePoint ); 472 if( mTabRect.pointInRect( localMouse ) ) 473 { 474 GuiTabPageCtrl* tab = findHitTab( localMouse ); 475 if( tab ) 476 onTabRightClick_callback( tab->getText(), getPageNum( tab ) ); 477 } 478} 479 480//----------------------------------------------------------------------------- 481 482void GuiTabBookCtrl::onRender(Point2I offset, const RectI &updateRect) 483{ 484 RectI tabRect = mTabRect; 485 tabRect.point += offset; 486 RectI pageRect = mPageRect; 487 pageRect.point += offset; 488 489 // We're so nice we'll store the old modulation before we clear it for our rendering! :) 490 ColorI oldModulation; 491 GFX->getDrawUtil()->getBitmapModulation( &oldModulation ); 492 493 // Wipe it out 494 GFX->getDrawUtil()->clearBitmapModulation(); 495 496 Parent::onRender(offset, updateRect); 497 498 // Clip to tab area 499 RectI savedClipRect = GFX->getClipRect(); 500 RectI clippedTabRect = tabRect; 501 clippedTabRect.intersect( savedClipRect ); 502 GFX->setClipRect( clippedTabRect ); 503 504 // Render our tabs 505 renderTabs( offset, tabRect ); 506 507 // Restore Rect. 508 GFX->setClipRect( savedClipRect ); 509 510 // Restore old modulation 511 GFX->getDrawUtil()->setBitmapModulation( oldModulation ); 512} 513 514//----------------------------------------------------------------------------- 515 516void GuiTabBookCtrl::renderTabs( const Point2I &offset, const RectI &tabRect ) 517{ 518 // If the tab size is zero, don't render tabs, 519 // assuming it's a tab-less book 520 if( mPages.empty() || mTabHeight <= 0 ) 521 return; 522 523 for( S32 i = 0; i < mPages.size(); i++ ) 524 { 525 const TabHeaderInfo ¤tTabInfo = mPages[i]; 526 RectI tabBounds = mPages[i].TabRect; 527 tabBounds.point += offset; 528 GuiTabPageCtrl *tab = mPages[i].Page; 529 if( tab != NULL ) 530 renderTab( tabBounds, tab ); 531 532 // If we're on the last tab, draw the nice end piece 533 if( i + 1 == mPages.size() ) 534 { 535 Point2I tabEndPoint = Point2I(currentTabInfo.TabRect.point.x + currentTabInfo.TabRect.extent.x + offset.x, currentTabInfo.TabRect.point.y + offset.y); 536 Point2I tabEndExtent = Point2I((tabRect.point.x + tabRect.extent.x) - tabEndPoint.x, currentTabInfo.TabRect.extent.y); 537 RectI tabEndRect = RectI(tabEndPoint,tabEndExtent); 538 539 GFX->setClipRect( tabEndRect ); 540 541 // As it turns out the last tab can be outside the viewport in which 542 // case trying to render causes a DX assert. Could be better if 543 // setClipRect returned a bool. 544 if ( GFX->getViewport().isValidRect() ) 545 renderFixedBitmapBordersFilled( tabEndRect, TabEnds + 1, mProfile ); 546 } 547 } 548} 549 550//----------------------------------------------------------------------------- 551 552void GuiTabBookCtrl::renderTab(const RectI& tabRect, GuiTabPageCtrl *tab) 553{ 554 StringTableEntry text = tab->getText(); 555 ColorI oldColor; 556 557 GFX->getDrawUtil()->getBitmapModulation( &oldColor ); 558 559 // Is this a skinned control? 560 if( mHasTexture && mProfile->mBitmapArrayRects.size() >= 9 ) 561 { 562 S32 indexMultiplier = 1; 563 switch( mTabPosition ) 564 { 565 case AlignTop: 566 case AlignBottom: 567 568 if ( mActivePage == tab ) 569 indexMultiplier += TabSelected; 570 else if( mHoverTab == tab ) 571 indexMultiplier += TabHover; 572 else 573 indexMultiplier += TabNormal; 574 break; 575 } 576 577 renderFixedBitmapBordersFilled( tabRect, indexMultiplier, mProfile ); 578 } 579 else 580 { 581 // If this isn't a skinned control or the bitmap is simply missing, handle it WELL 582 if ( mActivePage == tab ) 583 GFX->getDrawUtil()->drawRectFill(tabRect, mProfile->mFillColor); 584 else if( mHoverTab == tab ) 585 GFX->getDrawUtil()->drawRectFill(tabRect, mProfile->mFillColorHL); 586 else 587 GFX->getDrawUtil()->drawRectFill(tabRect, mProfile->mFillColorNA); 588 589 } 590 591 592 GFX->getDrawUtil()->setBitmapModulation(mProfile->mFontColor); 593 594 switch( mTabPosition ) 595 { 596 case AlignTop: 597 case AlignBottom: 598 renderJustifiedText( tabRect.point, tabRect.extent, text); 599 break; 600 } 601 602 GFX->getDrawUtil()->setBitmapModulation( oldColor); 603 604} 605 606//----------------------------------------------------------------------------- 607 608void GuiTabBookCtrl::setUpdate() 609{ 610 Parent::setUpdate(); 611 612 setUpdateRegion(Point2I(0,0), getExtent()); 613 614 calculatePageTabs(); 615} 616 617//----------------------------------------------------------------------------- 618 619S32 GuiTabBookCtrl::calculatePageTabWidth( GuiTabPageCtrl *page ) 620{ 621 if( !page ) 622 return mMinTabWidth; 623 624 const char* text = page->getText(); 625 626 if( !text || dStrlen(text) == 0 || mProfile == NULL || mProfile->mFont == NULL ) 627 return mMinTabWidth; 628 629 GFont *font = mProfile->mFont; 630 631 return font->getStrNWidth( text, dStrlen(text) ); 632 633} 634 635//----------------------------------------------------------------------------- 636 637const RectI GuiTabBookCtrl::getClientRect() 638{ 639 640 if( !mProfile || mProfile->mBitmapArrayRects.size() < NumBitmaps ) 641 return Parent::getClientRect(); 642 643 return mPageRect; 644} 645 646//----------------------------------------------------------------------------- 647 648void GuiTabBookCtrl::calculatePageTabs() 649{ 650 // Short Circuit. 651 // 652 // If the tab size is zero, don't render tabs, 653 // assuming it's a tab-less book 654 if( mPages.empty() || mTabHeight <= 0 ) 655 { 656 mPageRect.point.x = 0; 657 mPageRect.point.y = 0; 658 mPageRect.extent.x = getWidth(); 659 mPageRect.extent.y = getHeight(); 660 return; 661 } 662 663 S32 currRow = 0; 664 S32 currColumn = 0; 665 S32 currX = mFrontTabPadding; 666 S32 maxWidth = 0; 667 668 for( S32 i = 0; i < mPages.size(); i++ ) 669 { 670 // Fetch Tab Width 671 S32 tabWidth = calculatePageTabWidth( mPages[i].Page ) + ( mTabMargin * 2 ); 672 tabWidth = getMax( tabWidth, mMinTabWidth ); 673 TabHeaderInfo &info = mPages[i]; 674 switch( mTabPosition ) 675 { 676 case AlignTop: 677 case AlignBottom: 678 // If we're going to go outside our bounds 679 // with this tab move it down a row 680 if( currX + tabWidth > getWidth() ) 681 { 682 // Calculate and Advance State. 683 maxWidth = getMax( tabWidth, maxWidth ); 684 balanceRow( currRow, currX ); 685 info.TabRow = ++currRow; 686 // Reset Necessaries 687 info.TabColumn = currColumn = maxWidth = currX = 0; 688 } 689 else 690 { 691 info.TabRow = currRow; 692 info.TabColumn = currColumn++; 693 } 694 695 // Calculate Tabs Bounding Rect 696 info.TabRect.point.x = currX; 697 info.TabRect.extent.x = tabWidth; 698 info.TabRect.extent.y = mTabHeight; 699 700 // Adjust Y Point based on alignment 701 if( mTabPosition == AlignTop ) 702 info.TabRect.point.y = ( info.TabRow * mTabHeight ); 703 else 704 info.TabRect.point.y = getHeight() - ( ( 1 + info.TabRow ) * mTabHeight ); 705 706 currX += tabWidth; 707 break; 708 }; 709 } 710 711 currRow++; 712 currColumn++; 713 714 Point2I localPoint = getExtent(); 715 716 // Calculate 717 switch( mTabPosition ) 718 { 719 case AlignTop: 720 721 localPoint.y -= getTop(); 722 723 mTabRect.point.x = 0; 724 mTabRect.extent.x = localPoint.x; 725 mTabRect.point.y = 0; 726 mTabRect.extent.y = currRow * mTabHeight; 727 728 mPageRect.point.x = 0; 729 mPageRect.point.y = mTabRect.extent.y; 730 mPageRect.extent.x = mTabRect.extent.x; 731 mPageRect.extent.y = getHeight() - mTabRect.extent.y; 732 733 break; 734 case AlignBottom: 735 mTabRect.point.x = 0; 736 mTabRect.extent.x = localPoint.x; 737 mTabRect.extent.y = currRow * mTabHeight; 738 mTabRect.point.y = getHeight() - mTabRect.extent.y; 739 740 mPageRect.point.x = 0; 741 mPageRect.point.y = 0; 742 mPageRect.extent.x = mTabRect.extent.x; 743 mPageRect.extent.y = localPoint.y - mTabRect.extent.y; 744 745 break; 746 } 747} 748 749//----------------------------------------------------------------------------- 750 751void GuiTabBookCtrl::balanceRow( S32 row, S32 totalTabWidth ) 752{ 753 // Short Circuit. 754 // 755 // If the tab size is zero, don't render tabs, 756 // and assume it's a tab-less tab-book - JDD 757 if( mPages.empty() || mTabHeight <= 0 ) 758 return; 759 760 Vector<TabHeaderInfo*> rowTemp; 761 rowTemp.clear(); 762 763 for( S32 i = 0; i < mPages.size(); i++ ) 764 { 765 TabHeaderInfo &info = mPages[i]; 766 767 if(info.TabRow == row ) 768 rowTemp.push_back( &mPages[i] ); 769 } 770 771 if( rowTemp.empty() ) 772 return; 773 774 // Balance the tabs across the remaining space 775 S32 spaceToDivide = getWidth() - totalTabWidth; 776 S32 pointDelta = 0; 777 for( S32 i = 0; i < rowTemp.size(); i++ ) 778 { 779 TabHeaderInfo &info = *rowTemp[i]; 780 S32 extraSpace = (S32)spaceToDivide / ( rowTemp.size() ); 781 info.TabRect.extent.x += extraSpace; 782 info.TabRect.point.x += pointDelta; 783 pointDelta += extraSpace; 784 } 785} 786 787//----------------------------------------------------------------------------- 788 789GuiTabPageCtrl *GuiTabBookCtrl::findHitTab( const GuiEvent &event ) 790{ 791 return findHitTab( event.mousePoint ); 792} 793 794//----------------------------------------------------------------------------- 795 796GuiTabPageCtrl *GuiTabBookCtrl::findHitTab( Point2I hitPoint ) 797{ 798 // Short Circuit. 799 // 800 // If the tab size is zero, don't render tabs, 801 // and assume it's a tab-less tab-book - JDD 802 if( mPages.empty() || mTabHeight <= 0 ) 803 return NULL; 804 805 for( S32 i = 0; i < mPages.size(); i++ ) 806 { 807 if( mPages[i].TabRect.pointInRect( hitPoint ) ) 808 return mPages[i].Page; 809 } 810 return NULL; 811} 812 813//----------------------------------------------------------------------------- 814 815void GuiTabBookCtrl::selectPage( S32 index ) 816{ 817 if( mPages.empty() || index < 0 ) 818 return; 819 820 if( mPages.size() <= index ) 821 index = mPages.size() - 1; 822 823 // Select the page 824 selectPage( mPages[ index ].Page ); 825} 826 827//----------------------------------------------------------------------------- 828 829void GuiTabBookCtrl::selectPage( GuiTabPageCtrl *page ) 830{ 831 // Return if already selected. 832 if( mSelectedPageNum >= 0 && mSelectedPageNum < mPages.size() && mPages[ mSelectedPageNum ].Page == page ) 833 return; 834 835 mSelectedPageNum = -1; 836 837 Vector<TabHeaderInfo>::iterator i = mPages.begin(); 838 for( S32 index = 0; i != mPages.end() ; i++, index++ ) 839 { 840 GuiTabPageCtrl *tab = (*i).Page; 841 if( page == tab ) 842 { 843 mActivePage = tab; 844 tab->setVisible( true ); 845 846 mSelectedPageNum = index; 847 848 // Notify User 849 onTabSelected_callback( tab->getText(), index ); 850 } 851 else 852 tab->setVisible( false ); 853 } 854 setUpdateLayout( updateSelf ); 855} 856 857//----------------------------------------------------------------------------- 858 859bool GuiTabBookCtrl::_setSelectedPage( void *object, const char *index, const char *data ) 860{ 861 GuiTabBookCtrl* book = reinterpret_cast< GuiTabBookCtrl* >( object ); 862 book->selectPage( dAtoi( data ) ); 863 return false; 864} 865//----------------------------------------------------------------------------- 866 867bool GuiTabBookCtrl::onKeyDown(const GuiEvent &event) 868{ 869 // Tab = Next Page 870 // Ctrl-Tab = Previous Page 871 if( 0 && event.keyCode == KEY_TAB ) 872 { 873 if( event.modifier & SI_PRIMARY_CTRL ) 874 selectPrevPage(); 875 else 876 selectNextPage(); 877 878 return true; 879 } 880 881 return Parent::onKeyDown( event ); 882} 883 884//----------------------------------------------------------------------------- 885 886void GuiTabBookCtrl::selectNextPage() 887{ 888 if( mPages.empty() ) 889 return; 890 891 if( mActivePage == NULL ) 892 mActivePage = mPages[0].Page; 893 894 S32 nI = 0; 895 for( ; nI < mPages.size(); nI++ ) 896 { 897 GuiTabPageCtrl *tab = mPages[ nI ].Page; 898 if( tab == mActivePage ) 899 { 900 if( nI == ( mPages.size() - 1 ) ) 901 selectPage( 0 ); 902 else if ( nI + 1 <= ( mPages.size() - 1 ) ) 903 selectPage( nI + 1 ); 904 else 905 selectPage( 0 ); 906 return; 907 } 908 } 909} 910 911//----------------------------------------------------------------------------- 912 913void GuiTabBookCtrl::selectPrevPage() 914{ 915 if( mPages.empty() ) 916 return; 917 918 if( mActivePage == NULL ) 919 mActivePage = mPages[0].Page; 920 921 S32 nI = 0; 922 for( ; nI < mPages.size(); nI++ ) 923 { 924 GuiTabPageCtrl *tab = mPages[ nI ].Page; 925 if( tab == mActivePage ) 926 { 927 if( nI == 0 ) 928 selectPage( mPages.size() - 1 ); 929 else 930 selectPage( nI - 1 ); 931 return; 932 } 933 } 934} 935 936//----------------------------------------------------------------------------- 937 938void GuiTabBookCtrl::fitPage( GuiTabPageCtrl* page ) 939{ 940 page->resize( mPageRect.point, mPageRect.extent ); 941} 942 943//----------------------------------------------------------------------------- 944 945S32 GuiTabBookCtrl::getPageNum( GuiTabPageCtrl* page ) const 946{ 947 const U32 numPages = mPages.size(); 948 for( U32 i = 0; i < numPages; ++ i ) 949 if( mPages[ i ].Page == page ) 950 return i; 951 952 return -1; 953} 954 955//============================================================================= 956// API. 957//============================================================================= 958// MARK: ---- API ---- 959 960//----------------------------------------------------------------------------- 961 962DefineEngineMethod( GuiTabBookCtrl, addPage, void, ( const char* title ), ( "" ), 963 "Add a new tab page to the control.\n\n" 964 "@param title Title text for the tab page header." ) 965{ 966 object->addNewPage( title ); 967} 968 969//----------------------------------------------------------------------------- 970 971DefineEngineMethod( GuiTabBookCtrl, selectPage, void, ( S32 index ),, 972 "Set the selected tab page.\n\n" 973 "@param index Index of the tab page." ) 974{ 975 object->selectPage( index ); 976} 977 978//----------------------------------------------------------------------------- 979 980DefineEngineMethod( GuiTabBookCtrl, getSelectedPage, S32, (),, 981 "Get the index of the currently selected tab page.\n\n" 982 "@return Index of the selected tab page or -1 if no tab page is selected." ) 983{ 984 return object->getSelectedPageNum(); 985} 986