Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

622 lines
18 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Basic button control
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include <stdio.h>
  8. #include <utlsymbol.h>
  9. #include <vgui/IBorder.h>
  10. #include <vgui/IInput.h>
  11. #include <vgui/IScheme.h>
  12. #include <vgui/ISurface.h>
  13. #include <vgui/ISystem.h>
  14. #include <vgui/IVGui.h>
  15. #include <vgui/MouseCode.h>
  16. #include <vgui/KeyCode.h>
  17. #include <KeyValues.h>
  18. #include "URLButton.h"
  19. #include <vgui_controls/FocusNavGroup.h>
  20. // memdbgon must be the last include file in a .cpp file!!!
  21. #include <tier0/memdbgon.h>
  22. using namespace vgui;
  23. DECLARE_BUILD_FACTORY_DEFAULT_TEXT( URLButton, URLButton );
  24. //-----------------------------------------------------------------------------
  25. // Purpose: Constructor
  26. //-----------------------------------------------------------------------------
  27. URLButton::URLButton(Panel *parent, const char *panelName, const char *text, Panel *pActionSignalTarget, const char *pCmd ) : Label(parent, panelName, text)
  28. {
  29. Init();
  30. if ( pActionSignalTarget && pCmd )
  31. {
  32. AddActionSignalTarget( pActionSignalTarget );
  33. SetCommand( pCmd );
  34. }
  35. }
  36. //-----------------------------------------------------------------------------
  37. // Purpose: Constructor
  38. //-----------------------------------------------------------------------------
  39. URLButton::URLButton(Panel *parent, const char *panelName, const wchar_t *wszText, Panel *pActionSignalTarget, const char *pCmd ) : Label(parent, panelName, wszText)
  40. {
  41. Init();
  42. if ( pActionSignalTarget && pCmd )
  43. {
  44. AddActionSignalTarget( pActionSignalTarget );
  45. SetCommand( pCmd );
  46. }
  47. }
  48. //-----------------------------------------------------------------------------
  49. // Purpose:
  50. //-----------------------------------------------------------------------------
  51. void URLButton::Init()
  52. {
  53. _buttonFlags.SetFlag( USE_CAPTURE_MOUSE | BUTTON_BORDER_ENABLED );
  54. _mouseClickMask = 0;
  55. _actionMessage = NULL;
  56. m_bSelectionStateSaved = false;
  57. SetTextInset(0, 0);
  58. SetMouseClickEnabled( MOUSE_LEFT, true );
  59. SetButtonActivationType(ACTIVATE_ONPRESSEDANDRELEASED);
  60. // labels have this off by default, but we need it on
  61. SetPaintBackgroundEnabled( true );
  62. }
  63. //-----------------------------------------------------------------------------
  64. // Purpose: Destructor
  65. //-----------------------------------------------------------------------------
  66. URLButton::~URLButton()
  67. {
  68. if (_actionMessage)
  69. {
  70. _actionMessage->deleteThis();
  71. }
  72. }
  73. //-----------------------------------------------------------------------------
  74. // Purpose:
  75. //-----------------------------------------------------------------------------
  76. void URLButton::SetButtonActivationType(ActivationType_t activationType)
  77. {
  78. _activationType = activationType;
  79. }
  80. //-----------------------------------------------------------------------------
  81. // Purpose: Set button border attribute enabled.
  82. //-----------------------------------------------------------------------------
  83. void URLButton::SetButtonBorderEnabled( bool state )
  84. {
  85. if ( state != _buttonFlags.IsFlagSet( BUTTON_BORDER_ENABLED ) )
  86. {
  87. _buttonFlags.SetFlag( BUTTON_BORDER_ENABLED, state );
  88. InvalidateLayout(false);
  89. }
  90. }
  91. //-----------------------------------------------------------------------------
  92. // Purpose: Set button selected state.
  93. //-----------------------------------------------------------------------------
  94. void URLButton::SetSelected( bool state )
  95. {
  96. if ( _buttonFlags.IsFlagSet( SELECTED ) != state )
  97. {
  98. _buttonFlags.SetFlag( SELECTED, state );
  99. RecalculateDepressedState();
  100. InvalidateLayout(false);
  101. }
  102. }
  103. //-----------------------------------------------------------------------------
  104. // Purpose: Set button force depressed state.
  105. //-----------------------------------------------------------------------------
  106. void URLButton::ForceDepressed(bool state)
  107. {
  108. if ( _buttonFlags.IsFlagSet( FORCE_DEPRESSED ) != state )
  109. {
  110. _buttonFlags.SetFlag( FORCE_DEPRESSED, state );
  111. RecalculateDepressedState();
  112. InvalidateLayout(false);
  113. }
  114. }
  115. //-----------------------------------------------------------------------------
  116. // Purpose: Set button depressed state with respect to the force depressed state.
  117. //-----------------------------------------------------------------------------
  118. void URLButton::RecalculateDepressedState( void )
  119. {
  120. bool newState;
  121. if (!IsEnabled())
  122. {
  123. newState = false;
  124. }
  125. else
  126. {
  127. newState = _buttonFlags.IsFlagSet( FORCE_DEPRESSED ) ? true : (_buttonFlags.IsFlagSet(ARMED) && _buttonFlags.IsFlagSet( SELECTED ) );
  128. }
  129. _buttonFlags.SetFlag( DEPRESSED, newState );
  130. }
  131. //-----------------------------------------------------------------------------
  132. // Purpose: Sets whether or not the button captures all mouse input when depressed
  133. // Defaults to true
  134. // Should be set to false for things like menu items where there is a higher-level mouse capture
  135. //-----------------------------------------------------------------------------
  136. void URLButton::SetUseCaptureMouse( bool state )
  137. {
  138. _buttonFlags.SetFlag( USE_CAPTURE_MOUSE, state );
  139. }
  140. //-----------------------------------------------------------------------------
  141. // Purpose: Check if mouse capture is enabled.
  142. // Output : Returns true on success, false on failure.
  143. //-----------------------------------------------------------------------------
  144. bool URLButton::IsUseCaptureMouseEnabled( void )
  145. {
  146. return _buttonFlags.IsFlagSet( USE_CAPTURE_MOUSE );
  147. }
  148. //-----------------------------------------------------------------------------
  149. // Purpose: Set armed state.
  150. //-----------------------------------------------------------------------------
  151. void URLButton::SetArmed(bool state)
  152. {
  153. if ( _buttonFlags.IsFlagSet( ARMED ) != state )
  154. {
  155. _buttonFlags.SetFlag( ARMED, state );
  156. RecalculateDepressedState();
  157. InvalidateLayout(false);
  158. }
  159. }
  160. //-----------------------------------------------------------------------------
  161. // Purpose: Check armed state
  162. //-----------------------------------------------------------------------------
  163. bool URLButton::IsArmed()
  164. {
  165. return _buttonFlags.IsFlagSet( ARMED );
  166. }
  167. KeyValues *URLButton::GetActionMessage()
  168. {
  169. return _actionMessage->MakeCopy();
  170. }
  171. //-----------------------------------------------------------------------------
  172. // Purpose: Activate a button click.
  173. //-----------------------------------------------------------------------------
  174. void URLButton::DoClick()
  175. {
  176. SetSelected(true);
  177. FireActionSignal();
  178. SetSelected(false);
  179. }
  180. //-----------------------------------------------------------------------------
  181. // Purpose: Check selected state
  182. //-----------------------------------------------------------------------------
  183. bool URLButton::IsSelected()
  184. {
  185. return _buttonFlags.IsFlagSet( SELECTED );
  186. }
  187. //-----------------------------------------------------------------------------
  188. // Purpose: Check depressed state
  189. //-----------------------------------------------------------------------------
  190. bool URLButton::IsDepressed()
  191. {
  192. return _buttonFlags.IsFlagSet( DEPRESSED );
  193. }
  194. //-----------------------------------------------------------------------------
  195. // Drawing focus box?
  196. //-----------------------------------------------------------------------------
  197. bool URLButton::IsDrawingFocusBox()
  198. {
  199. return _buttonFlags.IsFlagSet( DRAW_FOCUS_BOX );
  200. }
  201. void URLButton::DrawFocusBox( bool bEnable )
  202. {
  203. _buttonFlags.SetFlag( DRAW_FOCUS_BOX, bEnable );
  204. }
  205. //-----------------------------------------------------------------------------
  206. // Purpose: Paint button on screen
  207. //-----------------------------------------------------------------------------
  208. void URLButton::Paint(void)
  209. {
  210. BaseClass::Paint();
  211. int x, y;
  212. int controlWidth, controlHeight, textWidth, textHeight;
  213. GetSize(controlWidth, controlHeight);
  214. GetContentSize(textWidth, textHeight);
  215. x = textWidth;
  216. y = controlHeight - 4;
  217. surface()->DrawSetColor(GetButtonFgColor());
  218. surface()->DrawLine(0, y, x, y);
  219. }
  220. //-----------------------------------------------------------------------------
  221. // Purpose: Perform graphical layout of button.
  222. //-----------------------------------------------------------------------------
  223. void URLButton::PerformLayout()
  224. {
  225. // set our color
  226. SetFgColor(GetButtonFgColor());
  227. SetBgColor(GetButtonBgColor());
  228. BaseClass::PerformLayout();
  229. }
  230. //-----------------------------------------------------------------------------
  231. // Purpose: Get button foreground color
  232. // Output : Color
  233. //-----------------------------------------------------------------------------
  234. Color URLButton::GetButtonFgColor()
  235. {
  236. return _defaultFgColor;
  237. }
  238. //-----------------------------------------------------------------------------
  239. // Purpose: Get button background color
  240. //-----------------------------------------------------------------------------
  241. Color URLButton::GetButtonBgColor()
  242. {
  243. return _defaultBgColor;
  244. }
  245. //-----------------------------------------------------------------------------
  246. // Purpose: Called when key focus is received
  247. //-----------------------------------------------------------------------------
  248. void URLButton::OnSetFocus()
  249. {
  250. InvalidateLayout(false);
  251. BaseClass::OnSetFocus();
  252. }
  253. //-----------------------------------------------------------------------------
  254. // Purpose: Respond when focus is killed
  255. //-----------------------------------------------------------------------------
  256. void URLButton::OnKillFocus()
  257. {
  258. InvalidateLayout(false);
  259. BaseClass::OnKillFocus();
  260. }
  261. //-----------------------------------------------------------------------------
  262. // Purpose:
  263. //-----------------------------------------------------------------------------
  264. void URLButton::ApplySchemeSettings(IScheme *pScheme)
  265. {
  266. BaseClass::ApplySchemeSettings(pScheme);
  267. _defaultFgColor = GetSchemeColor("Button.TextColor", Color(255, 255, 255, 255), pScheme);
  268. _defaultBgColor = GetSchemeColor("Button.BgColor", Color(0, 0, 0, 255), pScheme);
  269. InvalidateLayout();
  270. }
  271. //-----------------------------------------------------------------------------
  272. // Purpose: Set button to be mouse clickable or not.
  273. //-----------------------------------------------------------------------------
  274. void URLButton::SetMouseClickEnabled(MouseCode code,bool state)
  275. {
  276. if(state)
  277. {
  278. //set bit to 1
  279. _mouseClickMask|=1<<((int)(code+1));
  280. }
  281. else
  282. {
  283. //set bit to 0
  284. _mouseClickMask&=~(1<<((int)(code+1)));
  285. }
  286. }
  287. //-----------------------------------------------------------------------------
  288. // Purpose: sets the command to send when the button is pressed
  289. //-----------------------------------------------------------------------------
  290. void URLButton::SetCommand( const char *command )
  291. {
  292. SetCommand(new KeyValues("Command", "command", command));
  293. }
  294. //-----------------------------------------------------------------------------
  295. // Purpose: sets the message to send when the button is pressed
  296. //-----------------------------------------------------------------------------
  297. void URLButton::SetCommand( KeyValues *message )
  298. {
  299. // delete the old message
  300. if (_actionMessage)
  301. {
  302. _actionMessage->deleteThis();
  303. }
  304. _actionMessage = message;
  305. }
  306. //-----------------------------------------------------------------------------
  307. // Purpose: Peeks at the message to send when button is pressed
  308. // Input : -
  309. // Output : KeyValues
  310. //-----------------------------------------------------------------------------
  311. KeyValues *URLButton::GetCommand()
  312. {
  313. return _actionMessage;
  314. }
  315. //-----------------------------------------------------------------------------
  316. // Purpose: Message targets that the button has been pressed
  317. //-----------------------------------------------------------------------------
  318. void URLButton::FireActionSignal()
  319. {
  320. // message-based action signal
  321. if (_actionMessage)
  322. {
  323. // see if it's a url
  324. if (!stricmp(_actionMessage->GetName(), "command")
  325. && !strnicmp(_actionMessage->GetString("command", ""), "url ", strlen("url "))
  326. && strstr(_actionMessage->GetString("command", ""), "://"))
  327. {
  328. // it's a command to launch a url, run it
  329. system()->ShellExecute("open", _actionMessage->GetString("command", " ") + 4);
  330. }
  331. PostActionSignal(_actionMessage->MakeCopy());
  332. }
  333. }
  334. //-----------------------------------------------------------------------------
  335. // Purpose: gets info about the button
  336. //-----------------------------------------------------------------------------
  337. bool URLButton::RequestInfo(KeyValues *outputData)
  338. {
  339. if (!stricmp(outputData->GetName(), "GetState"))
  340. {
  341. outputData->SetInt("state", IsSelected());
  342. return true;
  343. }
  344. else if ( !stricmp( outputData->GetName(), "GetCommand" ))
  345. {
  346. if ( _actionMessage )
  347. {
  348. outputData->SetString( "command", _actionMessage->GetString( "command", "" ) );
  349. }
  350. else
  351. {
  352. outputData->SetString( "command", "" );
  353. }
  354. return true;
  355. }
  356. return BaseClass::RequestInfo(outputData);
  357. }
  358. //-----------------------------------------------------------------------------
  359. // Purpose: Get control settings for editing
  360. //-----------------------------------------------------------------------------
  361. void URLButton::GetSettings( KeyValues *outResourceData )
  362. {
  363. BaseClass::GetSettings(outResourceData);
  364. if (_actionMessage)
  365. {
  366. outResourceData->SetString("command", _actionMessage->GetString("command", ""));
  367. }
  368. outResourceData->SetInt("default", _buttonFlags.IsFlagSet( DEFAULT_BUTTON ) );
  369. if ( m_bSelectionStateSaved )
  370. {
  371. outResourceData->SetInt( "selected", IsSelected() );
  372. }
  373. }
  374. //-----------------------------------------------------------------------------
  375. // Purpose:
  376. //-----------------------------------------------------------------------------
  377. void URLButton::ApplySettings( KeyValues *inResourceData )
  378. {
  379. BaseClass::ApplySettings(inResourceData);
  380. const char *cmd = inResourceData->GetString("command", "");
  381. if (*cmd)
  382. {
  383. // add in the command
  384. SetCommand(cmd);
  385. }
  386. // saved selection state
  387. int iSelected = inResourceData->GetInt( "selected", -1 );
  388. if ( iSelected != -1 )
  389. {
  390. SetSelected( iSelected != 0 );
  391. m_bSelectionStateSaved = true;
  392. }
  393. }
  394. //-----------------------------------------------------------------------------
  395. // Purpose: Describes editing details
  396. //-----------------------------------------------------------------------------
  397. const char *URLButton::GetDescription( void )
  398. {
  399. static char buf[1024];
  400. Q_snprintf(buf, sizeof(buf), "%s, string command, int default", BaseClass::GetDescription());
  401. return buf;
  402. }
  403. //-----------------------------------------------------------------------------
  404. // Purpose:
  405. //-----------------------------------------------------------------------------
  406. void URLButton::OnSetState(int state)
  407. {
  408. SetSelected((bool)state);
  409. Repaint();
  410. }
  411. //-----------------------------------------------------------------------------
  412. // Purpose:
  413. //-----------------------------------------------------------------------------
  414. void URLButton::OnCursorEntered()
  415. {
  416. if (IsEnabled())
  417. {
  418. SetArmed(true);
  419. }
  420. }
  421. //-----------------------------------------------------------------------------
  422. // Purpose:
  423. //-----------------------------------------------------------------------------
  424. void URLButton::OnCursorExited()
  425. {
  426. if ( !_buttonFlags.IsFlagSet( BUTTON_KEY_DOWN ) )
  427. {
  428. SetArmed(false);
  429. }
  430. }
  431. //-----------------------------------------------------------------------------
  432. // Purpose:
  433. //-----------------------------------------------------------------------------
  434. void URLButton::OnMousePressed(MouseCode code)
  435. {
  436. if (!IsEnabled())
  437. return;
  438. if (_activationType == ACTIVATE_ONPRESSED)
  439. {
  440. if ( IsKeyBoardInputEnabled() )
  441. {
  442. RequestFocus();
  443. }
  444. DoClick();
  445. return;
  446. }
  447. if (IsUseCaptureMouseEnabled() && _activationType == ACTIVATE_ONPRESSEDANDRELEASED)
  448. {
  449. {
  450. if ( IsKeyBoardInputEnabled() )
  451. {
  452. RequestFocus();
  453. }
  454. SetSelected(true);
  455. Repaint();
  456. }
  457. // lock mouse input to going to this button
  458. input()->SetMouseCapture(GetVPanel());
  459. }
  460. }
  461. //-----------------------------------------------------------------------------
  462. // Purpose:
  463. //-----------------------------------------------------------------------------
  464. void URLButton::OnMouseDoublePressed(MouseCode code)
  465. {
  466. OnMousePressed(code);
  467. }
  468. //-----------------------------------------------------------------------------
  469. // Purpose:
  470. //-----------------------------------------------------------------------------
  471. void URLButton::OnMouseReleased(MouseCode code)
  472. {
  473. // ensure mouse capture gets released
  474. if (IsUseCaptureMouseEnabled())
  475. {
  476. input()->SetMouseCapture(NULL);
  477. }
  478. if (_activationType == ACTIVATE_ONPRESSED)
  479. return;
  480. if (!IsSelected() && _activationType == ACTIVATE_ONPRESSEDANDRELEASED)
  481. return;
  482. // it has to be both enabled and (mouse over the button or using a key) to fire
  483. if ( IsEnabled() && ( GetVPanel() == input()->GetMouseOver() || _buttonFlags.IsFlagSet( BUTTON_KEY_DOWN ) ) )
  484. {
  485. DoClick();
  486. }
  487. else
  488. {
  489. SetSelected(false);
  490. }
  491. // make sure the button gets unselected
  492. Repaint();
  493. }
  494. //-----------------------------------------------------------------------------
  495. // Purpose:
  496. //-----------------------------------------------------------------------------
  497. void URLButton::OnKeyCodePressed(KeyCode code)
  498. {
  499. if (code == KEY_SPACE || code == KEY_ENTER)
  500. {
  501. SetArmed(true);
  502. _buttonFlags.SetFlag( BUTTON_KEY_DOWN );
  503. OnMousePressed(MOUSE_LEFT);
  504. if (IsUseCaptureMouseEnabled()) // undo the mouse capture since its a fake mouse click!
  505. {
  506. input()->SetMouseCapture(NULL);
  507. }
  508. }
  509. else
  510. {
  511. _buttonFlags.ClearFlag( BUTTON_KEY_DOWN );
  512. BaseClass::OnKeyCodePressed(code);
  513. }
  514. }
  515. //-----------------------------------------------------------------------------
  516. // Purpose:
  517. //-----------------------------------------------------------------------------
  518. void URLButton::OnKeyCodeReleased(KeyCode code)
  519. {
  520. if (_buttonFlags.IsFlagSet( BUTTON_KEY_DOWN ) && (code == KEY_SPACE || code == KEY_ENTER))
  521. {
  522. SetArmed(true);
  523. OnMouseReleased(MOUSE_LEFT);
  524. }
  525. else
  526. {
  527. BaseClass::OnKeyCodeReleased(code);
  528. }
  529. _buttonFlags.ClearFlag( BUTTON_KEY_DOWN );
  530. SetArmed(false);
  531. }
  532. //-----------------------------------------------------------------------------
  533. // Purpose: Size the object to its button and text. - only works from in ApplySchemeSettings or PerformLayout()
  534. //-----------------------------------------------------------------------------
  535. void URLButton::SizeToContents()
  536. {
  537. int wide, tall;
  538. GetContentSize(wide, tall);
  539. SetSize(wide + Label::Content, tall + Label::Content);
  540. }