guiButtonBaseCtrl.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  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. IMPLEMENT_CALLBACK(GuiButtonBaseCtrl, onHighlighted, void, (bool highlighted), (highlighted),
  70. "Called when the status of the button being highlighted changes.");
  71. ImplementEnumType( GuiButtonType,
  72. "Type of button control.\n\n"
  73. "@ingroup GuiButtons" )
  74. { GuiButtonBaseCtrl::ButtonTypePush, "PushButton", "A button that triggers an action when clicked." },
  75. { GuiButtonBaseCtrl::ButtonTypeCheck, "ToggleButton", "A button that is toggled between on and off state." },
  76. { GuiButtonBaseCtrl::ButtonTypeRadio, "RadioButton", "A button placed in groups for presenting choices." },
  77. EndImplementEnumType;
  78. //-----------------------------------------------------------------------------
  79. GuiButtonBaseCtrl::GuiButtonBaseCtrl()
  80. {
  81. mDepressed = false;
  82. mHighlighted = false;
  83. mActive = true;
  84. static StringTableEntry sButton = StringTable->insert( "Button" );
  85. mButtonText = sButton;
  86. mButtonTextID = StringTable->EmptyString();
  87. mStateOn = false;
  88. mRadioGroup = -1;
  89. mButtonType = ButtonTypePush;
  90. mUseMouseEvents = false;
  91. mMouseDragged = false;
  92. }
  93. //-----------------------------------------------------------------------------
  94. void GuiButtonBaseCtrl::initPersistFields()
  95. {
  96. docsURL;
  97. addGroup( "Button" );
  98. addField( "text", TypeCaseString, Offset(mButtonText, GuiButtonBaseCtrl),
  99. "Text label to display on button (if button class supports text labels)." );
  100. addField( "textID", TypeString, Offset(mButtonTextID, GuiButtonBaseCtrl),
  101. "ID of string in string table to use for text label on button.\n\n"
  102. "@see setTextID\n"
  103. "@see GuiControl::langTableMod\n"
  104. "@see LangTable\n\n" );
  105. addField( "groupNum", TypeS32, Offset(mRadioGroup, GuiButtonBaseCtrl),
  106. "Radio button toggle group number. All radio buttons that are assigned the same #groupNum and that "
  107. "are parented to the same control will synchronize their toggle state, i.e. if one radio button is toggled on "
  108. "all other radio buttons in its group will be toggled off.\n\n"
  109. "The default group is -1." );
  110. addField( "buttonType", TYPEID< ButtonType >(), Offset(mButtonType, GuiButtonBaseCtrl),
  111. "Button behavior type.\n" );
  112. addField( "useMouseEvents", TypeBool, Offset(mUseMouseEvents, GuiButtonBaseCtrl),
  113. "If true, mouse events will be passed on to script. Default is false.\n" );
  114. endGroup( "Button" );
  115. Parent::initPersistFields();
  116. }
  117. //-----------------------------------------------------------------------------
  118. bool GuiButtonBaseCtrl::onWake()
  119. {
  120. if(!Parent::onWake())
  121. return false;
  122. // is we have a script variable, make sure we're in sync
  123. if ( mConsoleVariable[0] )
  124. mStateOn = Con::getBoolVariable( mConsoleVariable );
  125. if(mButtonTextID && *mButtonTextID != 0)
  126. setTextID(mButtonTextID);
  127. return true;
  128. }
  129. //-----------------------------------------------------------------------------
  130. void GuiButtonBaseCtrl::setText( const char* text )
  131. {
  132. mButtonText = StringTable->insert(text, true);
  133. }
  134. //-----------------------------------------------------------------------------
  135. void GuiButtonBaseCtrl::setTextID(const char *id)
  136. {
  137. S32 n = Con::getIntVariable(id, -1);
  138. if(n != -1)
  139. {
  140. mButtonTextID = StringTable->insert(id);
  141. setTextID(n);
  142. }
  143. }
  144. //-----------------------------------------------------------------------------
  145. void GuiButtonBaseCtrl::setTextID(S32 id)
  146. {
  147. const UTF8 *str = getGUIString(id);
  148. if(str)
  149. setText((const char*)str);
  150. //mButtonTextID = id;
  151. }
  152. //-----------------------------------------------------------------------------
  153. const char *GuiButtonBaseCtrl::getText()
  154. {
  155. return mButtonText;
  156. }
  157. //-----------------------------------------------------------------------------
  158. void GuiButtonBaseCtrl::setStateOn( bool bStateOn )
  159. {
  160. if(!mActive)
  161. return;
  162. if(mButtonType == ButtonTypeCheck)
  163. {
  164. mStateOn = bStateOn;
  165. }
  166. else if(mButtonType == ButtonTypeRadio)
  167. {
  168. messageSiblings(mRadioGroup);
  169. mStateOn = bStateOn;
  170. }
  171. setUpdate();
  172. }
  173. //-----------------------------------------------------------------------------
  174. void GuiButtonBaseCtrl::acceleratorKeyPress(U32)
  175. {
  176. if( !mActive )
  177. return;
  178. //set the bool
  179. mDepressed = true;
  180. if (mProfile->mTabable)
  181. setFirstResponder();
  182. }
  183. //-----------------------------------------------------------------------------
  184. void GuiButtonBaseCtrl::acceleratorKeyRelease(U32)
  185. {
  186. if (! mActive)
  187. return;
  188. if (mDepressed)
  189. {
  190. //set the bool
  191. mDepressed = false;
  192. //perform the action
  193. onAction();
  194. }
  195. //update
  196. setUpdate();
  197. }
  198. //-----------------------------------------------------------------------------
  199. void GuiButtonBaseCtrl::onMouseDown(const GuiEvent &event)
  200. {
  201. if (! mActive)
  202. return;
  203. if (mProfile->mCanKeyFocus)
  204. setFirstResponder();
  205. if (mProfile->isSoundButtonDownValid())
  206. SFX->playOnce(mProfile->getSoundButtonDownProfile());
  207. mMouseDownPoint = event.mousePoint;
  208. mMouseDragged = false;
  209. if( mUseMouseEvents )
  210. onMouseDown_callback();
  211. //lock the mouse
  212. mouseLock();
  213. mDepressed = true;
  214. // If we have a double click then execute the alt command.
  215. if ( event.mouseClickCount == 2 )
  216. {
  217. onDoubleClick_callback();
  218. execAltConsoleCallback();
  219. }
  220. //update
  221. setUpdate();
  222. }
  223. //-----------------------------------------------------------------------------
  224. void GuiButtonBaseCtrl::onMouseEnter(const GuiEvent &event)
  225. {
  226. setUpdate();
  227. if( mUseMouseEvents )
  228. onMouseEnter_callback();
  229. if(isMouseLocked())
  230. {
  231. mDepressed = true;
  232. mHighlighted = true;
  233. onHighlighted_callback(true);
  234. }
  235. else
  236. {
  237. if (mProfile->isSoundButtonOverValid())
  238. SFX->playOnce(mProfile->getSoundButtonOverProfile());
  239. mHighlighted = true;
  240. onHighlighted_callback(true);
  241. }
  242. }
  243. //-----------------------------------------------------------------------------
  244. void GuiButtonBaseCtrl::onMouseLeave(const GuiEvent &)
  245. {
  246. setUpdate();
  247. if( mUseMouseEvents )
  248. onMouseLeave_callback();
  249. if( isMouseLocked() )
  250. mDepressed = false;
  251. mHighlighted = false;
  252. onHighlighted_callback(false);
  253. }
  254. //-----------------------------------------------------------------------------
  255. void GuiButtonBaseCtrl::onMouseUp(const GuiEvent &event)
  256. {
  257. mouseUnlock();
  258. if( !mActive )
  259. return;
  260. setUpdate();
  261. if( mUseMouseEvents )
  262. onMouseUp_callback();
  263. //if we released the mouse within this control, perform the action
  264. if( mDepressed )
  265. onAction();
  266. mDepressed = false;
  267. mMouseDragged = false;
  268. }
  269. //-----------------------------------------------------------------------------
  270. void GuiButtonBaseCtrl::onRightMouseUp(const GuiEvent &event)
  271. {
  272. onRightClick_callback();
  273. Parent::onRightMouseUp( event );
  274. }
  275. //-----------------------------------------------------------------------------
  276. void GuiButtonBaseCtrl::onMouseDragged( const GuiEvent& event )
  277. {
  278. if( mUseMouseEvents )
  279. {
  280. // If we haven't started a drag yet, find whether we have moved past
  281. // the tolerance value.
  282. if( !mMouseDragged )
  283. {
  284. Point2I delta = mMouseDownPoint - event.mousePoint;
  285. if( mAbs( delta.x ) > 2 || mAbs( delta.y ) > 2 )
  286. mMouseDragged = true;
  287. }
  288. if( mMouseDragged )
  289. onMouseDragged_callback();
  290. }
  291. Parent::onMouseDragged( event );
  292. }
  293. //-----------------------------------------------------------------------------
  294. bool GuiButtonBaseCtrl::onKeyDown(const GuiEvent &event)
  295. {
  296. //if the control is a dead end, kill the event
  297. if (!mActive)
  298. return true;
  299. //see if the key down is a return or space or not
  300. if ((event.keyCode == KEY_RETURN || event.keyCode == KEY_SPACE)
  301. && event.modifier == 0)
  302. {
  303. if (mProfile->isSoundButtonDownValid())
  304. SFX->playOnce(mProfile->getSoundButtonDownProfile());
  305. return true;
  306. }
  307. //otherwise, pass the event to it's parent
  308. return Parent::onKeyDown(event);
  309. }
  310. //-----------------------------------------------------------------------------
  311. bool GuiButtonBaseCtrl::onKeyUp(const GuiEvent &event)
  312. {
  313. //if the control is a dead end, kill the event
  314. if (!mActive)
  315. return true;
  316. //see if the key down is a return or space or not
  317. if (mDepressed &&
  318. (event.keyCode == KEY_RETURN || event.keyCode == KEY_SPACE) &&
  319. event.modifier == 0)
  320. {
  321. onAction();
  322. return true;
  323. }
  324. //otherwise, pass the event to it's parent
  325. return Parent::onKeyUp(event);
  326. }
  327. //-----------------------------------------------------------------------------
  328. void GuiButtonBaseCtrl::setScriptValue(const char *value)
  329. {
  330. mStateOn = dAtob(value);
  331. // Update the console variable:
  332. if ( mConsoleVariable[0] )
  333. Con::setBoolVariable( mConsoleVariable, mStateOn );
  334. setUpdate();
  335. }
  336. //-----------------------------------------------------------------------------
  337. const char *GuiButtonBaseCtrl::getScriptValue()
  338. {
  339. return mStateOn ? "1" : "0";
  340. }
  341. //-----------------------------------------------------------------------------
  342. void GuiButtonBaseCtrl::onAction()
  343. {
  344. if(!mActive)
  345. return;
  346. if(mButtonType == ButtonTypeCheck)
  347. {
  348. mStateOn = mStateOn ? false : true;
  349. }
  350. else if(mButtonType == ButtonTypeRadio)
  351. {
  352. mStateOn = true;
  353. messageSiblings(mRadioGroup);
  354. }
  355. setUpdate();
  356. // Update the console variable:
  357. if ( mConsoleVariable[0] )
  358. Con::setBoolVariable( mConsoleVariable, mStateOn );
  359. onClick_callback();
  360. Parent::onAction();
  361. }
  362. //-----------------------------------------------------------------------------
  363. void GuiButtonBaseCtrl::onMessage( GuiControl *sender, S32 msg )
  364. {
  365. Parent::onMessage(sender, msg);
  366. if( mRadioGroup == msg && mButtonType == ButtonTypeRadio )
  367. {
  368. setUpdate();
  369. mStateOn = ( sender == this );
  370. // Update the console variable:
  371. if ( mConsoleVariable[0] )
  372. Con::setBoolVariable( mConsoleVariable, mStateOn );
  373. }
  374. }
  375. //=============================================================================
  376. // Console Methods.
  377. //=============================================================================
  378. // MARK: ---- Console Methods ----
  379. //-----------------------------------------------------------------------------
  380. DefineEngineMethod( GuiButtonBaseCtrl, performClick, void, (),,
  381. "Simulate a click on the button.\n"
  382. "This method will trigger the button's action just as if the button had been pressed by the "
  383. "user.\n\n" )
  384. {
  385. object->onAction();
  386. }
  387. //-----------------------------------------------------------------------------
  388. DefineEngineMethod( GuiButtonBaseCtrl, setText, void, ( const char* text ),,
  389. "Set the text displayed on the button's label.\n"
  390. "@param text The text to display as the button's text label.\n"
  391. "@note Not all buttons render text labels.\n\n"
  392. "@see getText\n"
  393. "@see setTextID\n" )
  394. {
  395. object->setText( text );
  396. }
  397. //-----------------------------------------------------------------------------
  398. DefineEngineMethod( GuiButtonBaseCtrl, setTextID, void, ( const char* id ),,
  399. "Set the text displayed on the button's label using a string from the string table "
  400. "assigned to the control.\n\n"
  401. "@param id Name of the variable that contains the integer string ID. Used to look up "
  402. "string in table.\n\n"
  403. "@note Not all buttons render text labels.\n\n"
  404. "@see setText\n"
  405. "@see getText\n"
  406. "@see GuiControl::langTableMod\n"
  407. "@see LangTable\n\n"
  408. "@ref Gui_i18n" )
  409. {
  410. object->setTextID( id );
  411. }
  412. //-----------------------------------------------------------------------------
  413. DefineEngineMethod( GuiButtonBaseCtrl, getText, const char*, (),,
  414. "Get the text display on the button's label (if any).\n\n"
  415. "@return The button's label." )
  416. {
  417. return object->getText( );
  418. }
  419. //-----------------------------------------------------------------------------
  420. DefineEngineMethod( GuiButtonBaseCtrl, setStateOn, void, ( bool isOn ), ( true ),
  421. "For toggle or radio buttons, set whether the button is currently activated or not. For radio buttons, "
  422. "toggling a button on will toggle all other radio buttons in its group to off.\n\n"
  423. "@param isOn If true, the button will be toggled on (if not already); if false, it will be toggled off.\n\n"
  424. "@note Toggling the state of a button with this method will <em>not</em> not trigger the action associated with the "
  425. "button. To do that, use performClick()." )
  426. {
  427. object->setStateOn( isOn );
  428. }
  429. //-----------------------------------------------------------------------------
  430. DefineEngineMethod( GuiButtonBaseCtrl, resetState, void, (),,
  431. "Reset the mousing state of the button.\n\n"
  432. "This method should not generally be called." )
  433. {
  434. object->resetState();
  435. }
  436. DefineEngineMethod(GuiButtonBaseCtrl, setHighlighted, void, (bool highlighted), (false),
  437. "Reset the mousing state of the button.\n\n"
  438. "This method should not generally be called.")
  439. {
  440. object->setHighlighted(highlighted);
  441. }
  442. DefineEngineMethod(GuiButtonBaseCtrl, isHighlighted, bool, (),,
  443. "Reset the mousing state of the button.\n\n"
  444. "This method should not generally be called.")
  445. {
  446. return object->isHighlighted();
  447. }