guiBitmapButtonCtrl.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/platform.h"
  23. #include "gui/buttons/guiBitmapButtonCtrl.h"
  24. #include "core/util/path.h"
  25. #include "console/console.h"
  26. #include "console/consoleTypes.h"
  27. #include "console/engineAPI.h"
  28. #include "gui/core/guiCanvas.h"
  29. #include "gui/core/guiDefaultControlRender.h"
  30. #include "gfx/gfxDrawUtil.h"
  31. ImplementEnumType( GuiBitmapMode,
  32. "Rendering behavior when placing bitmaps in controls.\n\n"
  33. "@ingroup GuiImages" )
  34. { GuiBitmapButtonCtrl::BitmapStretched, "Stretched", "Stretch bitmap to fit control extents." },
  35. { GuiBitmapButtonCtrl::BitmapCentered, "Centered", "Center bitmap in control." },
  36. EndImplementEnumType;
  37. //=============================================================================
  38. // GuiBitmapButtonCtrl
  39. //=============================================================================
  40. IMPLEMENT_CONOBJECT(GuiBitmapButtonCtrl);
  41. ConsoleDocClass( GuiBitmapButtonCtrl,
  42. "@brief A button that renders its various states (mouse over, pushed, etc.) from separate bitmaps.\n\n"
  43. "A bitmapped button is a push button that uses one or more texture images for rendering its individual states.\n\n"
  44. "To find the individual textures associated with the button, a naming scheme is used. For each state "
  45. "a suffix is appended to the texture file name given in the GuiBitmapButtonCtrl::bitmap field:\n"
  46. "- \"_n\": Normal state. This one will be active when no other state applies.\n"
  47. "- \"_h\": Highlighted state. This applies when the mouse is hovering over the button.\n"
  48. "- \"_d\": Depressed state. This applies when the left mouse button has been clicked on the button but not yet released.\n"
  49. "- \"_i\": Inactive state. This applies when the button control has been deactivated (GuiControl::setActive())\n\n"
  50. "If a bitmap for a particular state cannot be found, the default bitmap will be used. To disable all state-based "
  51. "bitmap functionality, set useStates to false which will make the control solely render from the bitmap specified "
  52. "in the bitmap field.\n\n"
  53. "@section guibitmapbutton_modifiers Per-Modifier Button Actions\n"
  54. "If GuiBitmapButtonCtrl::useModifiers is set to true, per-modifier button actions and textures are enabled. This functionality "
  55. "allows to associate different images and different actions with a button depending on which modifiers are pressed "
  56. "on the keyboard by the user.\n\n"
  57. "When enabled, this functionality alters the texture lookup above by prepending the following strings to the "
  58. "suffixes listed above:\n"
  59. "- \"\": Default. No modifier is pressed.\n"
  60. "- \"_ctrl\": Image to use when CTRL/CMD is down.\n"
  61. "- \"_alt\": Image to use when ALT is down.\n"
  62. "- \"_shift\": Image to use when SHIFT is down\n\n"
  63. "When this functionality is enabled, a new set of callbacks is used:\n"
  64. "- onDefaultClick: Button was clicked without a modifier being presssed.\n"
  65. "- onCtrlClick: Button was clicked with the CTRL/CMD key down.\n"
  66. "- onAltClick: Button was clicked with the ALT key down.\n"
  67. "- onShiftClick: Button was clicked with the SHIFT key down.\n\n"
  68. "GuiControl::command or GuiControl::onAction() still work as before when per-modifier functionality is enabled.\n\n"
  69. "Note that modifiers cannot be mixed. If two or more modifiers are pressed, a single one will take precedence over "
  70. "the remaining modifiers. The order of precedence corresponds to the order listed above.\n\n"
  71. "@tsexample\n"
  72. "// Create an OK button that will trigger an onOk() call on its parent when clicked:\n"
  73. "%okButton = new GuiBitmapButtonCtrl()\n"
  74. "{\n"
  75. " bitmap = \"art/gui/okButton\";\n"
  76. " autoFitExtents = true;\n"
  77. " command = \"$ThisControl.getParent().onOk();\";\n"
  78. "};\n"
  79. "@endtsexample\n\n"
  80. "@ingroup GuiButtons"
  81. );
  82. IMPLEMENT_CALLBACK( GuiBitmapButtonCtrl, onDefaultClick, void, (), (),
  83. "Called when per-modifier functionality is enabled and the user clicks on the button without any modifier pressed.\n"
  84. "@ref guibitmapbutton_modifiers" );
  85. IMPLEMENT_CALLBACK( GuiBitmapButtonCtrl, onCtrlClick, void, (), (),
  86. "Called when per-modifier functionality is enabled and the user clicks on the button with the CTRL key pressed.\n"
  87. "@ref guibitmapbutton_modifiers" );
  88. IMPLEMENT_CALLBACK( GuiBitmapButtonCtrl, onAltClick, void, (), (),
  89. "Called when per-modifier functionality is enabled and the user clicks on the button with the ALT key pressed.\n"
  90. "@ref guibitmapbutton_modifiers" );
  91. IMPLEMENT_CALLBACK( GuiBitmapButtonCtrl, onShiftClick, void, (), (),
  92. "Called when per-modifier functionality is enabled and the user clicks on the button with the SHIFT key pressed.\n"
  93. "@ref guibitmapbutton_modifiers" );
  94. //-----------------------------------------------------------------------------
  95. GuiBitmapButtonCtrl::GuiBitmapButtonCtrl()
  96. {
  97. mBitmapMode = BitmapStretched;
  98. mAutoFitExtents = false;
  99. mUseModifiers = false;
  100. mUseStates = true;
  101. setExtent( 140, 30 );
  102. }
  103. //-----------------------------------------------------------------------------
  104. void GuiBitmapButtonCtrl::initPersistFields()
  105. {
  106. addGroup( "Bitmap" );
  107. addProtectedField( "bitmap", TypeStringFilename, Offset( mBitmapName, GuiBitmapButtonCtrl ),
  108. &_setBitmap, &defaultProtectedGetFn,
  109. "Texture file to display on this button.\n"
  110. "If useStates is false, this will be the file that renders on the control. Otherwise, this will "
  111. "specify the default texture name to which the various state and modifier suffixes are appended "
  112. "to find the per-state and per-modifier (if enabled) textures." );
  113. addField( "bitmapMode", TYPEID< BitmapMode >(), Offset( mBitmapMode, GuiBitmapButtonCtrl ),
  114. "Behavior for fitting the bitmap to the control extents.\n"
  115. "If set to 'Stretched', the bitmap will be stretched both verticall and horizontally to fit inside "
  116. "the control's extents.\n\n"
  117. "If set to 'Centered', the bitmap will stay at its original resolution centered in the control's "
  118. "rectangle (getting clipped if the control is smaller than the texture)." );
  119. addProtectedField( "autoFitExtents", TypeBool, Offset( mAutoFitExtents, GuiBitmapButtonCtrl ),
  120. &_setAutoFitExtents, &defaultProtectedGetFn,
  121. "If true, the control's extents will be set to match the bitmap's extents when setting the bitmap.\n"
  122. "The bitmap extents will always be taken from the default/normal bitmap (in case the extents of the various "
  123. "bitmaps do not match up.)" );
  124. addField( "useModifiers", TypeBool, Offset( mUseModifiers, GuiBitmapButtonCtrl ),
  125. "If true, per-modifier button functionality is enabled.\n"
  126. "@ref guibitmapbutton_modifiers" );
  127. addField( "useStates", TypeBool, Offset( mUseStates, GuiBitmapButtonCtrl ),
  128. "If true, per-mouse state button functionality is enabled.\n"
  129. "Defaults to true.\n\n"
  130. "If you do not use per-state images on this button set this to false to speed up the loading process "
  131. "by inhibiting searches for the individual images." );
  132. endGroup( "Bitmap" );
  133. Parent::initPersistFields();
  134. }
  135. //-----------------------------------------------------------------------------
  136. bool GuiBitmapButtonCtrl::onWake()
  137. {
  138. if (! Parent::onWake())
  139. return false;
  140. setActive( true );
  141. setBitmap( mBitmapName );
  142. return true;
  143. }
  144. //-----------------------------------------------------------------------------
  145. void GuiBitmapButtonCtrl::onSleep()
  146. {
  147. if( dStricmp(mBitmapName, "texhandle") != 0 )
  148. for( U32 i = 0; i < NumModifiers; ++ i )
  149. {
  150. mTextures[ i ].mTextureNormal = NULL;
  151. mTextures[ i ].mTextureHilight = NULL;
  152. mTextures[ i ].mTextureDepressed = NULL;
  153. mTextures[ i ].mTextureInactive = NULL;
  154. }
  155. Parent::onSleep();
  156. }
  157. //-----------------------------------------------------------------------------
  158. bool GuiBitmapButtonCtrl::_setAutoFitExtents( void *object, const char *index, const char *data )
  159. {
  160. GuiBitmapButtonCtrl* ctrl = reinterpret_cast< GuiBitmapButtonCtrl* >( object );
  161. ctrl->setAutoFitExtents( dAtob( data ) );
  162. return false;
  163. }
  164. //-----------------------------------------------------------------------------
  165. bool GuiBitmapButtonCtrl::_setBitmap( void *object, const char *index, const char *data )
  166. {
  167. GuiBitmapButtonCtrl* ctrl = reinterpret_cast< GuiBitmapButtonCtrl* >( object );
  168. ctrl->setBitmap( data );
  169. return false;
  170. }
  171. //-----------------------------------------------------------------------------
  172. // Legacy method. Can just assign to bitmap field.
  173. DefineEngineMethod( GuiBitmapButtonCtrl, setBitmap, void, ( const char* path ),,
  174. "Set the bitmap to show on the button.\n"
  175. "@param path Path to the texture file in any of the supported formats.\n" )
  176. {
  177. object->setBitmap( path );
  178. }
  179. //-----------------------------------------------------------------------------
  180. void GuiBitmapButtonCtrl::inspectPostApply()
  181. {
  182. Parent::inspectPostApply();
  183. Torque::Path path( mBitmapName );
  184. const String& fileName = path.getFileName();
  185. if( mUseStates )
  186. {
  187. // If the filename points to a single state, automatically
  188. // cut off the state part. Makes it easy to select files in
  189. // the editor without having to go in and manually cut off the
  190. // state parts all the time.
  191. static String s_n = "_n";
  192. static String s_d = "_d";
  193. static String s_h = "_h";
  194. static String s_i = "_i";
  195. if( fileName.endsWith( s_n )
  196. || fileName.endsWith( s_d )
  197. || fileName.endsWith( s_h )
  198. || fileName.endsWith( s_i ) )
  199. {
  200. path.setFileName( fileName.substr( 0, fileName.length() - 2 ) );
  201. path.setExtension( String::EmptyString );
  202. }
  203. }
  204. setBitmap( path.getFullPath() );
  205. // if the extent is set to (0,0) in the gui editor and appy hit, this control will
  206. // set it's extent to be exactly the size of the normal bitmap (if present)
  207. if ((getWidth() == 0) && (getHeight() == 0) && mTextures[ 0 ].mTextureNormal)
  208. {
  209. setExtent( mTextures[ 0 ].mTextureNormal->getWidth(), mTextures[ 0 ].mTextureNormal->getHeight());
  210. }
  211. }
  212. //-----------------------------------------------------------------------------
  213. void GuiBitmapButtonCtrl::setAutoFitExtents( bool state )
  214. {
  215. mAutoFitExtents = state;
  216. if( mAutoFitExtents )
  217. setBitmap( mBitmapName );
  218. }
  219. //-----------------------------------------------------------------------------
  220. void GuiBitmapButtonCtrl::setBitmap( const String& name )
  221. {
  222. PROFILE_SCOPE( GuiBitmapButtonCtrl_setBitmap );
  223. mBitmapName = name;
  224. if( !isAwake() )
  225. return;
  226. if( !mBitmapName.isEmpty() )
  227. {
  228. if( dStricmp( mBitmapName, "texhandle" ) != 0 )
  229. {
  230. const U32 count = mUseModifiers ? NumModifiers : 1;
  231. for( U32 i = 0; i < count; ++ i )
  232. {
  233. static String modifiers[] =
  234. {
  235. "",
  236. "_ctrl",
  237. "_alt",
  238. "_shift"
  239. };
  240. static String s_n = "_n";
  241. static String s_d = "_d";
  242. static String s_h = "_h";
  243. static String s_i = "_i";
  244. String baseName = mBitmapName;
  245. if( mUseModifiers )
  246. baseName += modifiers[ i ];
  247. mTextures[ i ].mTextureNormal = GFXTexHandle( baseName, &GFXDefaultPersistentProfile, avar("%s() - mTextureNormal (line %d)", __FUNCTION__, __LINE__));
  248. if( mUseStates )
  249. {
  250. if( !mTextures[ i ].mTextureNormal )
  251. mTextures[ i ].mTextureNormal = GFXTexHandle( baseName + s_n, &GFXDefaultPersistentProfile, avar("%s() - mTextureNormal (line %d)", __FUNCTION__, __LINE__));
  252. mTextures[ i ].mTextureHilight = GFXTexHandle( baseName + s_h, &GFXDefaultPersistentProfile, avar("%s() - mTextureHighlight (line %d)", __FUNCTION__, __LINE__));
  253. if( !mTextures[ i ].mTextureHilight )
  254. mTextures[ i ].mTextureHilight = mTextures[ i ].mTextureNormal;
  255. mTextures[ i ].mTextureDepressed = GFXTexHandle( baseName + s_d, &GFXDefaultPersistentProfile, avar("%s() - mTextureDepressed (line %d)", __FUNCTION__, __LINE__));
  256. if( !mTextures[ i ].mTextureDepressed )
  257. mTextures[ i ].mTextureDepressed = mTextures[ i ].mTextureHilight;
  258. mTextures[ i ].mTextureInactive = GFXTexHandle( baseName + s_i, &GFXDefaultPersistentProfile, avar("%s() - mTextureInactive (line %d)", __FUNCTION__, __LINE__));
  259. if( !mTextures[ i ].mTextureInactive )
  260. mTextures[ i ].mTextureInactive = mTextures[ i ].mTextureNormal;
  261. }
  262. if( i == 0 && mTextures[ i ].mTextureNormal.isNull() && mTextures[ i ].mTextureHilight.isNull() && mTextures[ i ].mTextureDepressed.isNull() && mTextures[ i ].mTextureInactive.isNull() )
  263. {
  264. Con::warnf( "GuiBitmapButtonCtrl::setBitmap - Unable to load texture: %s", mBitmapName.c_str() );
  265. this->setBitmap( "core/art/unavailable" );
  266. return;
  267. }
  268. }
  269. }
  270. if( mAutoFitExtents && !mTextures[ 0 ].mTextureNormal.isNull() )
  271. setExtent( mTextures[ 0 ].mTextureNormal.getWidth(), mTextures[ 0 ].mTextureNormal.getHeight() );
  272. }
  273. else
  274. {
  275. for( U32 i = 0; i < NumModifiers; ++ i )
  276. {
  277. mTextures[ i ].mTextureNormal = NULL;
  278. mTextures[ i ].mTextureHilight = NULL;
  279. mTextures[ i ].mTextureDepressed = NULL;
  280. mTextures[ i ].mTextureInactive = NULL;
  281. }
  282. }
  283. setUpdate();
  284. }
  285. //-----------------------------------------------------------------------------
  286. void GuiBitmapButtonCtrl::setBitmapHandles(GFXTexHandle normal, GFXTexHandle highlighted, GFXTexHandle depressed, GFXTexHandle inactive)
  287. {
  288. const U32 count = mUseModifiers ? NumModifiers : 1;
  289. for( U32 i = 0; i < count; ++ i )
  290. {
  291. mTextures[ i ].mTextureNormal = normal;
  292. mTextures[ i ].mTextureHilight = highlighted;
  293. mTextures[ i ].mTextureDepressed = depressed;
  294. mTextures[ i ].mTextureInactive = inactive;
  295. if (!mTextures[ i ].mTextureHilight)
  296. mTextures[ i ].mTextureHilight = mTextures[ i ].mTextureNormal;
  297. if (!mTextures[ i ].mTextureDepressed)
  298. mTextures[ i ].mTextureDepressed = mTextures[ i ].mTextureHilight;
  299. if (!mTextures[ i ].mTextureInactive)
  300. mTextures[ i ].mTextureInactive = mTextures[ i ].mTextureNormal;
  301. if (mTextures[ i ].mTextureNormal.isNull() && mTextures[ i ].mTextureHilight.isNull() && mTextures[ i ].mTextureDepressed.isNull() && mTextures[ i ].mTextureInactive.isNull())
  302. {
  303. Con::warnf("GuiBitmapButtonCtrl::setBitmapHandles() - Invalid texture handles");
  304. setBitmap("core/art/unavailable");
  305. return;
  306. }
  307. }
  308. mBitmapName = "texhandle";
  309. }
  310. //------------------------------------------------------------------------------
  311. GuiBitmapButtonCtrl::Modifier GuiBitmapButtonCtrl::getCurrentModifier()
  312. {
  313. U8 modifierKeys = Input::getModifierKeys();
  314. if( modifierKeys & SI_PRIMARY_CTRL )
  315. return ModifierCtrl;
  316. else if( modifierKeys & SI_PRIMARY_ALT )
  317. return ModifierAlt;
  318. else if( modifierKeys & SI_SHIFT )
  319. return ModifierShift;
  320. return ModifierNone;
  321. }
  322. //------------------------------------------------------------------------------
  323. GFXTexHandle& GuiBitmapButtonCtrl::getTextureForCurrentState()
  324. {
  325. U32 index = ModifierNone;
  326. if( mUseModifiers )
  327. index = getCurrentModifier();
  328. if( !mUseStates )
  329. {
  330. if( mTextures[ index ].mTextureNormal )
  331. return mTextures[ 0 ].mTextureNormal;
  332. else
  333. return mTextures[ index ].mTextureNormal;
  334. }
  335. switch( getState() )
  336. {
  337. case NORMAL:
  338. if( !mTextures[ index ].mTextureNormal )
  339. return mTextures[ 0 ].mTextureNormal;
  340. else
  341. return mTextures[ index ].mTextureNormal;
  342. case HILIGHT:
  343. if( !mTextures[ index ].mTextureHilight )
  344. return mTextures[ 0 ].mTextureHilight;
  345. else
  346. return mTextures[ index ].mTextureHilight;
  347. case DEPRESSED:
  348. if( !mTextures[ index ].mTextureDepressed )
  349. return mTextures[ 0 ].mTextureDepressed;
  350. else
  351. return mTextures[ index ].mTextureDepressed;
  352. default:
  353. if( !mTextures[ index ].mTextureInactive )
  354. return mTextures[ 0 ].mTextureInactive;
  355. else
  356. return mTextures[ index ].mTextureInactive;
  357. }
  358. }
  359. //------------------------------------------------------------------------------
  360. void GuiBitmapButtonCtrl::onAction()
  361. {
  362. Parent::onAction();
  363. if( mUseModifiers )
  364. {
  365. switch( getCurrentModifier() )
  366. {
  367. case ModifierNone:
  368. onDefaultClick_callback();
  369. break;
  370. case ModifierCtrl:
  371. onCtrlClick_callback();
  372. break;
  373. case ModifierAlt:
  374. onAltClick_callback();
  375. break;
  376. case ModifierShift:
  377. onShiftClick_callback();
  378. break;
  379. default:
  380. break;
  381. }
  382. }
  383. }
  384. //------------------------------------------------------------------------------
  385. void GuiBitmapButtonCtrl::onRender(Point2I offset, const RectI& updateRect)
  386. {
  387. GFXTexHandle& texture = getTextureForCurrentState();
  388. if( texture )
  389. {
  390. renderButton( texture, offset, updateRect );
  391. renderChildControls( offset, updateRect );
  392. }
  393. else
  394. Parent::onRender(offset, updateRect);
  395. }
  396. //------------------------------------------------------------------------------
  397. void GuiBitmapButtonCtrl::renderButton( GFXTexHandle &texture, const Point2I &offset, const RectI& updateRect )
  398. {
  399. GFX->getDrawUtil()->clearBitmapModulation();
  400. switch( mBitmapMode )
  401. {
  402. case BitmapStretched:
  403. {
  404. RectI rect( offset, getExtent() );
  405. GFX->getDrawUtil()->drawBitmapStretch( texture, rect );
  406. break;
  407. }
  408. case BitmapCentered:
  409. {
  410. Point2I p = offset;
  411. p.x += getExtent().x / 2 - texture.getWidth() / 2;
  412. p.y += getExtent().y / 2 - texture.getHeight() / 2;
  413. GFX->getDrawUtil()->drawBitmap( texture, p );
  414. break;
  415. }
  416. }
  417. }
  418. //=============================================================================
  419. // GuiBitmapButtonTextCtrl.
  420. //=============================================================================
  421. IMPLEMENT_CONOBJECT( GuiBitmapButtonTextCtrl);
  422. ConsoleDocClass( GuiBitmapButtonTextCtrl,
  423. "@brief An extension of GuiBitmapButtonCtrl that additionally renders a text label on the bitmapped button.\n\n"
  424. "The text for the label is taken from the GuiButtonBaseCtrl::text property.\n\n"
  425. "For rendering, the label is placed, relative to the control's upper left corner, at the text offset specified in the "
  426. "control's profile (GuiControlProfile::textOffset) and justified according to the profile's setting (GuiControlProfile::justify).\n\n"
  427. "@see GuiControlProfile::textOffset\n"
  428. "@see GuiControlProfile::justify\n"
  429. "@ingroup GuiButtons"
  430. );
  431. //-----------------------------------------------------------------------------
  432. void GuiBitmapButtonTextCtrl::renderButton( GFXTexHandle &texture, const Point2I &offset, const RectI& updateRect )
  433. {
  434. Parent::renderButton( texture, offset, updateRect );
  435. Point2I textPos = offset;
  436. if(mDepressed)
  437. textPos += Point2I(1,1);
  438. // Make sure we take the profile's textOffset into account.
  439. textPos += mProfile->mTextOffset;
  440. GFX->getDrawUtil()->setBitmapModulation( mProfile->mFontColor );
  441. renderJustifiedText(textPos, getExtent(), mButtonText);
  442. }