guiButtonBaseCtrl.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "gui/buttons/guiButtonBaseCtrl.h"
  23. #include "console/console.h"
  24. #include "console/engineAPI.h"
  25. #include "gfx/gfxDevice.h"
  26. #include "gui/core/guiCanvas.h"
  27. #include "i18n/lang.h"
  28. #include "sfx/sfxSystem.h"
  29. #include "sfx/sfxTrack.h"
  30. IMPLEMENT_CONOBJECT( GuiButtonBaseCtrl );
  31. ConsoleDocClass( GuiButtonBaseCtrl,
  32. "@brief The base class for the various button controls.\n\n"
  33. "This is the base class for the various types of button controls. If no more specific functionality is required than "
  34. "offered by this class, then it can be instantiated and used directly. Otherwise, its subclasses should be used:\n"
  35. "- GuiRadioCtrl (radio buttons)\n"
  36. "- GuiCheckBoxCtrl (checkboxes)\n"
  37. "- GuiButtonCtrl (push buttons with text labels)\n"
  38. "- GuiBitmapButtonCtrl (bitmapped buttons)\n"
  39. "- GuiBitmapButtonTextCtrl (bitmapped buttons with a text label)\n"
  40. "- GuiToggleButtonCtrl (toggle buttons, i.e. push buttons with \"sticky\" behavior)\n"
  41. "- GuiSwatchButtonCtrl (color swatch buttons)\n"
  42. "- GuiBorderButtonCtrl (push buttons for surrounding child controls)\n\n"
  43. "@ingroup GuiButtons"
  44. );
  45. IMPLEMENT_CALLBACK( GuiButtonBaseCtrl, onMouseDown, void, (), (),
  46. "If #useMouseEvents is true, this is called when the left mouse button is pressed on an (active) "
  47. "button." );
  48. IMPLEMENT_CALLBACK( GuiButtonBaseCtrl, onMouseUp, void, (), (),
  49. "If #useMouseEvents is true, this is called when the left mouse button is release over an (active) "
  50. "button.\n\n"
  51. "@note To trigger actions, better use onClick() since onMouseUp() will also be called when the mouse was "
  52. "not originally pressed on the button." );
  53. IMPLEMENT_CALLBACK( GuiButtonBaseCtrl, onClick, void, (), (),
  54. "Called when the primary action of the button is triggered (e.g. by a left mouse click)." );
  55. IMPLEMENT_CALLBACK( GuiButtonBaseCtrl, onDoubleClick, void, (), (),
  56. "Called when the left mouse button is double-clicked on the button." );
  57. IMPLEMENT_CALLBACK( GuiButtonBaseCtrl, onRightClick, void, (), (),
  58. "Called when the right mouse button is clicked on the button." );
  59. IMPLEMENT_CALLBACK( GuiButtonBaseCtrl, onMouseEnter, void, (), (),
  60. "If #useMouseEvents is true, this is called when the mouse cursor moves over the button (only if the button "
  61. "is the front-most visible control, though)." );
  62. IMPLEMENT_CALLBACK( GuiButtonBaseCtrl, onMouseLeave, void, (), (),
  63. "If #useMouseEvents is true, this is called when the mouse cursor moves off the button (only if the button "
  64. "had previously received an onMouseEvent() event)." );
  65. IMPLEMENT_CALLBACK( GuiButtonBaseCtrl, onMouseDragged, void, (), (),
  66. "If #useMouseEvents is true, this is called when a left mouse button drag is detected, i.e. when the user "
  67. "pressed the left mouse button on the control and then moves the mouse over a certain distance threshold with "
  68. "the mouse button still pressed." );
  69. ImplementEnumType( GuiButtonType,
  70. "Type of button control.\n\n"
  71. "@ingroup GuiButtons" )
  72. { GuiButtonBaseCtrl::ButtonTypePush, "PushButton", "A button that triggers an action when clicked." },
  73. { GuiButtonBaseCtrl::ButtonTypeCheck, "ToggleButton", "A button that is toggled between on and off state." },
  74. { GuiButtonBaseCtrl::ButtonTypeRadio, "RadioButton", "A button placed in groups for presenting choices." },
  75. EndImplementEnumType;
  76. //-----------------------------------------------------------------------------
  77. GuiButtonBaseCtrl::GuiButtonBaseCtrl()
  78. {
  79. mDepressed = false;
  80. mHighlighted = false;
  81. mActive = true;
  82. static StringTableEntry sButton = StringTable->insert( "Button" );
  83. mButtonText = sButton;
  84. mButtonTextID = StringTable->EmptyString();
  85. mStateOn = false;
  86. mRadioGroup = -1;
  87. mButtonType = ButtonTypePush;
  88. mUseMouseEvents = false;
  89. mMouseDragged = false;
  90. }
  91. //-----------------------------------------------------------------------------
  92. void GuiButtonBaseCtrl::initPersistFields()
  93. {
  94. docsURL;
  95. addGroup( "Button" );
  96. addField( "text", TypeCaseString, Offset(mButtonText, GuiButtonBaseCtrl),
  97. "Text label to display on button (if button class supports text labels)." );
  98. addField( "textID", TypeString, Offset(mButtonTextID, GuiButtonBaseCtrl),
  99. "ID of string in string table to use for text label on button.\n\n"
  100. "@see setTextID\n"
  101. "@see GuiControl::langTableMod\n"
  102. "@see LangTable\n\n" );
  103. addField( "groupNum", TypeS32, Offset(mRadioGroup, GuiButtonBaseCtrl),
  104. "Radio button toggle group number. All radio buttons that are assigned the same #groupNum and that "
  105. "are parented to the same control will synchronize their toggle state, i.e. if one radio button is toggled on "
  106. "all other radio buttons in its group will be toggled off.\n\n"
  107. "The default group is -1." );
  108. addField( "buttonType", TYPEID< ButtonType >(), Offset(mButtonType, GuiButtonBaseCtrl),
  109. "Button behavior type.\n" );
  110. addField( "useMouseEvents", TypeBool, Offset(mUseMouseEvents, GuiButtonBaseCtrl),
  111. "If true, mouse events will be passed on to script. Default is false.\n" );
  112. endGroup( "Button" );
  113. Parent::initPersistFields();
  114. }
  115. //-----------------------------------------------------------------------------
  116. bool GuiButtonBaseCtrl::onWake()
  117. {
  118. if(!Parent::onWake())
  119. return false;
  120. // is we have a script variable, make sure we're in sync
  121. if ( mConsoleVariable[0] )
  122. mStateOn = Con::getBoolVariable( mConsoleVariable );
  123. if(mButtonTextID && *mButtonTextID != 0)
  124. setTextID(mButtonTextID);
  125. return true;
  126. }
  127. //-----------------------------------------------------------------------------
  128. void GuiButtonBaseCtrl::setText( const char* text )
  129. {
  130. mButtonText = StringTable->insert(text, true);
  131. }
  132. //-----------------------------------------------------------------------------
  133. void GuiButtonBaseCtrl::setTextID(const char *id)
  134. {
  135. S32 n = Con::getIntVariable(id, -1);
  136. if(n != -1)
  137. {
  138. mButtonTextID = StringTable->insert(id);
  139. setTextID(n);
  140. }
  141. }
  142. //-----------------------------------------------------------------------------
  143. void GuiButtonBaseCtrl::setTextID(S32 id)
  144. {
  145. const UTF8 *str = getGUIString(id);
  146. if(str)
  147. setText((const char*)str);
  148. //mButtonTextID = id;
  149. }
  150. //-----------------------------------------------------------------------------
  151. const char *GuiButtonBaseCtrl::getText()
  152. {
  153. return mButtonText;
  154. }
  155. //-----------------------------------------------------------------------------
  156. void GuiButtonBaseCtrl::setStateOn( bool bStateOn )
  157. {
  158. if(!mActive)
  159. return;
  160. if(mButtonType == ButtonTypeCheck)
  161. {
  162. mStateOn = bStateOn;
  163. }
  164. else if(mButtonType == ButtonTypeRadio)
  165. {
  166. messageSiblings(mRadioGroup);
  167. mStateOn = bStateOn;
  168. }
  169. setUpdate();
  170. }
  171. //-----------------------------------------------------------------------------
  172. void GuiButtonBaseCtrl::acceleratorKeyPress(U32)
  173. {
  174. if( !mActive )
  175. return;
  176. //set the bool
  177. mDepressed = true;
  178. if (mProfile->mTabable)
  179. setFirstResponder();
  180. }
  181. //-----------------------------------------------------------------------------
  182. void GuiButtonBaseCtrl::acceleratorKeyRelease(U32)
  183. {
  184. if (! mActive)
  185. return;
  186. if (mDepressed)
  187. {
  188. //set the bool
  189. mDepressed = false;
  190. //perform the action
  191. onAction();
  192. }
  193. //update
  194. setUpdate();
  195. }
  196. //-----------------------------------------------------------------------------
  197. void GuiButtonBaseCtrl::onMouseDown(const GuiEvent &event)
  198. {
  199. if (! mActive)
  200. return;
  201. if (mProfile->mCanKeyFocus)
  202. setFirstResponder();
  203. if (mProfile->isSoundButtonDownValid())
  204. SFX->playOnce(mProfile->getSoundButtonDownProfile());
  205. mMouseDownPoint = event.mousePoint;
  206. mMouseDragged = false;
  207. if( mUseMouseEvents )
  208. onMouseDown_callback();
  209. //lock the mouse
  210. mouseLock();
  211. mDepressed = true;
  212. // If we have a double click then execute the alt command.
  213. if ( event.mouseClickCount == 2 )
  214. {
  215. onDoubleClick_callback();
  216. execAltConsoleCallback();
  217. }
  218. //update
  219. setUpdate();
  220. }
  221. //-----------------------------------------------------------------------------
  222. void GuiButtonBaseCtrl::onMouseEnter(const GuiEvent &event)
  223. {
  224. setUpdate();
  225. if( mUseMouseEvents )
  226. onMouseEnter_callback();
  227. if(isMouseLocked())
  228. {
  229. mDepressed = true;
  230. mHighlighted = true;
  231. }
  232. else
  233. {
  234. if (mProfile->isSoundButtonOverValid())
  235. SFX->playOnce(mProfile->getSoundButtonOverProfile());
  236. mHighlighted = true;
  237. }
  238. }
  239. //-----------------------------------------------------------------------------
  240. void GuiButtonBaseCtrl::onMouseLeave(const GuiEvent &)
  241. {
  242. setUpdate();
  243. if( mUseMouseEvents )
  244. onMouseLeave_callback();
  245. if( isMouseLocked() )
  246. mDepressed = false;
  247. mHighlighted = false;
  248. }
  249. //-----------------------------------------------------------------------------
  250. void GuiButtonBaseCtrl::onMouseUp(const GuiEvent &event)
  251. {
  252. mouseUnlock();
  253. if( !mActive )
  254. return;
  255. setUpdate();
  256. if( mUseMouseEvents )
  257. onMouseUp_callback();
  258. //if we released the mouse within this control, perform the action
  259. if( mDepressed )
  260. onAction();
  261. mDepressed = false;
  262. mMouseDragged = false;
  263. }
  264. //-----------------------------------------------------------------------------
  265. void GuiButtonBaseCtrl::onRightMouseUp(const GuiEvent &event)
  266. {
  267. onRightClick_callback();
  268. Parent::onRightMouseUp( event );
  269. }
  270. //-----------------------------------------------------------------------------
  271. void GuiButtonBaseCtrl::onMouseDragged( const GuiEvent& event )
  272. {
  273. if( mUseMouseEvents )
  274. {
  275. // If we haven't started a drag yet, find whether we have moved past
  276. // the tolerance value.
  277. if( !mMouseDragged )
  278. {
  279. Point2I delta = mMouseDownPoint - event.mousePoint;
  280. if( mAbs( delta.x ) > 2 || mAbs( delta.y ) > 2 )
  281. mMouseDragged = true;
  282. }
  283. if( mMouseDragged )
  284. onMouseDragged_callback();
  285. }
  286. Parent::onMouseDragged( event );
  287. }
  288. //-----------------------------------------------------------------------------
  289. bool GuiButtonBaseCtrl::onKeyDown(const GuiEvent &event)
  290. {
  291. //if the control is a dead end, kill the event
  292. if (!mActive)
  293. return true;
  294. //see if the key down is a return or space or not
  295. if ((event.keyCode == KEY_RETURN || event.keyCode == KEY_SPACE)
  296. && event.modifier == 0)
  297. {
  298. if (mProfile->isSoundButtonDownValid())
  299. SFX->playOnce(mProfile->getSoundButtonDownProfile());
  300. return true;
  301. }
  302. //otherwise, pass the event to it's parent
  303. return Parent::onKeyDown(event);
  304. }
  305. //-----------------------------------------------------------------------------
  306. bool GuiButtonBaseCtrl::onKeyUp(const GuiEvent &event)
  307. {
  308. //if the control is a dead end, kill the event
  309. if (!mActive)
  310. return true;
  311. //see if the key down is a return or space or not
  312. if (mDepressed &&
  313. (event.keyCode == KEY_RETURN || event.keyCode == KEY_SPACE) &&
  314. event.modifier == 0)
  315. {
  316. onAction();
  317. return true;
  318. }
  319. //otherwise, pass the event to it's parent
  320. return Parent::onKeyUp(event);
  321. }
  322. //-----------------------------------------------------------------------------
  323. void GuiButtonBaseCtrl::setScriptValue(const char *value)
  324. {
  325. mStateOn = dAtob(value);
  326. // Update the console variable:
  327. if ( mConsoleVariable[0] )
  328. Con::setBoolVariable( mConsoleVariable, mStateOn );
  329. setUpdate();
  330. }
  331. //-----------------------------------------------------------------------------
  332. const char *GuiButtonBaseCtrl::getScriptValue()
  333. {
  334. return mStateOn ? "1" : "0";
  335. }
  336. //-----------------------------------------------------------------------------
  337. void GuiButtonBaseCtrl::onAction()
  338. {
  339. if(!mActive)
  340. return;
  341. if(mButtonType == ButtonTypeCheck)
  342. {
  343. mStateOn = mStateOn ? false : true;
  344. }
  345. else if(mButtonType == ButtonTypeRadio)
  346. {
  347. mStateOn = true;
  348. messageSiblings(mRadioGroup);
  349. }
  350. setUpdate();
  351. // Update the console variable:
  352. if ( mConsoleVariable[0] )
  353. Con::setBoolVariable( mConsoleVariable, mStateOn );
  354. onClick_callback();
  355. Parent::onAction();
  356. }
  357. //-----------------------------------------------------------------------------
  358. void GuiButtonBaseCtrl::onMessage( GuiControl *sender, S32 msg )
  359. {
  360. Parent::onMessage(sender, msg);
  361. if( mRadioGroup == msg && mButtonType == ButtonTypeRadio )
  362. {
  363. setUpdate();
  364. mStateOn = ( sender == this );
  365. // Update the console variable:
  366. if ( mConsoleVariable[0] )
  367. Con::setBoolVariable( mConsoleVariable, mStateOn );
  368. }
  369. }
  370. //=============================================================================
  371. // Console Methods.
  372. //=============================================================================
  373. // MARK: ---- Console Methods ----
  374. //-----------------------------------------------------------------------------
  375. DefineEngineMethod( GuiButtonBaseCtrl, performClick, void, (),,
  376. "Simulate a click on the button.\n"
  377. "This method will trigger the button's action just as if the button had been pressed by the "
  378. "user.\n\n" )
  379. {
  380. object->onAction();
  381. }
  382. //-----------------------------------------------------------------------------
  383. DefineEngineMethod( GuiButtonBaseCtrl, setText, void, ( const char* text ),,
  384. "Set the text displayed on the button's label.\n"
  385. "@param text The text to display as the button's text label.\n"
  386. "@note Not all buttons render text labels.\n\n"
  387. "@see getText\n"
  388. "@see setTextID\n" )
  389. {
  390. object->setText( text );
  391. }
  392. //-----------------------------------------------------------------------------
  393. DefineEngineMethod( GuiButtonBaseCtrl, setTextID, void, ( const char* id ),,
  394. "Set the text displayed on the button's label using a string from the string table "
  395. "assigned to the control.\n\n"
  396. "@param id Name of the variable that contains the integer string ID. Used to look up "
  397. "string in table.\n\n"
  398. "@note Not all buttons render text labels.\n\n"
  399. "@see setText\n"
  400. "@see getText\n"
  401. "@see GuiControl::langTableMod\n"
  402. "@see LangTable\n\n"
  403. "@ref Gui_i18n" )
  404. {
  405. object->setTextID( id );
  406. }
  407. //-----------------------------------------------------------------------------
  408. DefineEngineMethod( GuiButtonBaseCtrl, getText, const char*, (),,
  409. "Get the text display on the button's label (if any).\n\n"
  410. "@return The button's label." )
  411. {
  412. return object->getText( );
  413. }
  414. //-----------------------------------------------------------------------------
  415. DefineEngineMethod( GuiButtonBaseCtrl, setStateOn, void, ( bool isOn ), ( true ),
  416. "For toggle or radio buttons, set whether the button is currently activated or not. For radio buttons, "
  417. "toggling a button on will toggle all other radio buttons in its group to off.\n\n"
  418. "@param isOn If true, the button will be toggled on (if not already); if false, it will be toggled off.\n\n"
  419. "@note Toggling the state of a button with this method will <em>not</em> not trigger the action associated with the "
  420. "button. To do that, use performClick()." )
  421. {
  422. object->setStateOn( isOn );
  423. }
  424. //-----------------------------------------------------------------------------
  425. DefineEngineMethod( GuiButtonBaseCtrl, resetState, void, (),,
  426. "Reset the mousing state of the button.\n\n"
  427. "This method should not generally be called." )
  428. {
  429. object->resetState();
  430. }
  431. DefineEngineMethod(GuiButtonBaseCtrl, setHighlighted, void, (bool highlighted), (false),
  432. "Reset the mousing state of the button.\n\n"
  433. "This method should not generally be called.")
  434. {
  435. object->setHighlighted(highlighted);
  436. }
  437. DefineEngineMethod(GuiButtonBaseCtrl, isHighlighted, bool, (),,
  438. "Reset the mousing state of the button.\n\n"
  439. "This method should not generally be called.")
  440. {
  441. return object->isHighlighted();
  442. }