Counter Strike : Global Offensive Source Code
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.

668 lines
20 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include <vgui/IScheme.h>
  8. #include <vgui/IVGui.h>
  9. #include "vgui/ISurface.h"
  10. #include <keyvalues.h>
  11. #include <vgui_controls/Controls.h>
  12. #include <vgui_controls/Menu.h>
  13. #include <vgui_controls/MenuItem.h>
  14. #include <vgui_controls/TextImage.h>
  15. // memdbgon must be the last include file in a .cpp file!!!
  16. #include <tier0/memdbgon.h>
  17. using namespace vgui;
  18. const int KEYBINDING_INSET = 5;
  19. //-----------------------------------------------------------------------------
  20. // Purpose: Check box image
  21. //-----------------------------------------------------------------------------
  22. class MenuItemCheckImage : public TextImage
  23. {
  24. public:
  25. MenuItemCheckImage(MenuItem *item) : TextImage( "g" )
  26. {
  27. _menuItem = item;
  28. SetSize(20, 13);
  29. }
  30. virtual void Paint()
  31. {
  32. DrawSetTextFont(GetFont());
  33. // draw background
  34. DrawSetTextColor(_menuItem->GetBgColor());
  35. DrawPrintChar(0, 0, 'g');
  36. // draw check
  37. if (_menuItem->IsChecked())
  38. {
  39. if (_menuItem->IsEnabled())
  40. {
  41. DrawSetTextColor(_menuItem->GetButtonFgColor());
  42. DrawPrintChar(0, 2, 'a');
  43. }
  44. else if (!_menuItem->IsEnabled())
  45. {
  46. // draw disabled version, with embossed look
  47. // offset image
  48. DrawSetTextColor(_menuItem->GetDisabledFgColor1());
  49. DrawPrintChar(1, 3, 'a');
  50. // overlayed image
  51. DrawSetTextColor(_menuItem->GetDisabledFgColor2());
  52. DrawPrintChar(0, 2, 'a');
  53. }
  54. }
  55. }
  56. private:
  57. MenuItem *_menuItem;
  58. };
  59. DECLARE_BUILD_FACTORY_DEFAULT_TEXT( MenuItem, MenuItem );
  60. //-----------------------------------------------------------------------------
  61. // Purpose: Constructor
  62. // Input: parent - the parent of this menu item, usually a menu
  63. // text - the name of the menu item as it appears in the menu
  64. // cascadeMenu - if this item triggers the opening of a cascading menu
  65. // provide a pointer to it.
  66. // MenuItems cannot be both checkable and trigger a cascade menu.
  67. //-----------------------------------------------------------------------------
  68. MenuItem::MenuItem(Menu *parent, const char *panelName, const char *text, Menu *cascadeMenu, bool checkable) : Button(parent, panelName, text)
  69. {
  70. m_pCascadeMenu = cascadeMenu;
  71. m_bCheckable = checkable;
  72. SetButtonActivationType(ACTIVATE_ONRELEASED);
  73. m_pUserData = NULL;
  74. m_pCurrentKeyBinding = NULL;
  75. // only one arg should be passed in.
  76. Assert (!(cascadeMenu && checkable));
  77. Init();
  78. }
  79. //-----------------------------------------------------------------------------
  80. // Purpose: Constructor
  81. // Input: parent - the parent of this menu item, usually a menu
  82. // text - the name of the menu item as it appears in the menu
  83. // cascadeMenu - if this item triggers the opening of a cascading menu
  84. // provide a pointer to it.
  85. // MenuItems cannot be both checkable and trigger a cascade menu.
  86. //-----------------------------------------------------------------------------
  87. MenuItem::MenuItem(Menu *parent, const char *panelName, const wchar_t *wszText, Menu *cascadeMenu, bool checkable) : Button(parent, panelName, wszText)
  88. {
  89. m_pCascadeMenu = cascadeMenu;
  90. m_bCheckable = checkable;
  91. SetButtonActivationType(ACTIVATE_ONRELEASED);
  92. m_pUserData = NULL;
  93. m_pCurrentKeyBinding = NULL;
  94. // only one arg should be passed in.
  95. Assert (!(cascadeMenu && checkable));
  96. Init();
  97. }
  98. //-----------------------------------------------------------------------------
  99. // Purpose: Destructor
  100. //-----------------------------------------------------------------------------
  101. MenuItem::~MenuItem()
  102. {
  103. delete m_pCascadeMenu;
  104. delete m_pCascadeArrow;
  105. delete m_pCheck;
  106. if (m_pUserData)
  107. {
  108. m_pUserData->deleteThis();
  109. }
  110. delete m_pCurrentKeyBinding;
  111. }
  112. //-----------------------------------------------------------------------------
  113. // Purpose: Basic initializer
  114. //-----------------------------------------------------------------------------
  115. void MenuItem::Init( void )
  116. {
  117. m_pCascadeArrow = NULL;
  118. m_pCheck = NULL;
  119. if (m_pCascadeMenu)
  120. {
  121. m_pCascadeMenu->SetParent(this);
  122. m_pCascadeArrow = new TextImage("4"); // this makes a right pointing arrow.
  123. m_pCascadeMenu->AddActionSignalTarget(this);
  124. }
  125. else if (m_bCheckable)
  126. {
  127. // move the text image over so we have room for the check
  128. SetTextImageIndex(1);
  129. m_pCheck = new MenuItemCheckImage(this);
  130. SetImageAtIndex(0, m_pCheck, CHECK_INSET);
  131. SetChecked(false);
  132. }
  133. SetButtonBorderEnabled( false );
  134. SetUseCaptureMouse( false );
  135. SetContentAlignment( Label::a_west );
  136. // note menus handle all the sizing of menuItem panels
  137. }
  138. //-----------------------------------------------------------------------------
  139. // Purpose:
  140. //-----------------------------------------------------------------------------
  141. Menu *MenuItem::GetParentMenu()
  142. {
  143. return (Menu *)GetParent();
  144. }
  145. //-----------------------------------------------------------------------------
  146. // Purpose: Layout the Textimage and the Arrow part of the menuItem
  147. //-----------------------------------------------------------------------------
  148. void MenuItem::PerformLayout()
  149. {
  150. Button::PerformLayout();
  151. // make the arrow image match the button layout.
  152. // this will make it brighten and dim like the menu buttons.
  153. if (m_pCascadeArrow)
  154. {
  155. m_pCascadeArrow->SetColor(GetButtonFgColor());
  156. }
  157. }
  158. //-----------------------------------------------------------------------------
  159. // Purpose: Close the cascading menu if we have one.
  160. //-----------------------------------------------------------------------------
  161. void MenuItem::CloseCascadeMenu()
  162. {
  163. if (m_pCascadeMenu)
  164. {
  165. if (m_pCascadeMenu->IsVisible())
  166. {
  167. m_pCascadeMenu->SetVisible(false);
  168. }
  169. // disarm even if menu wasn't visible!
  170. SetArmed(false);
  171. }
  172. }
  173. //-----------------------------------------------------------------------------
  174. // Purpose: Handle cursor moving in a menuItem.
  175. //-----------------------------------------------------------------------------
  176. void MenuItem::OnCursorMoved(int x, int y)
  177. {
  178. // if menu is in keymode and we moved the mouse
  179. // highlight this item
  180. if (GetParentMenu()->GetMenuMode() == Menu::KEYBOARD)
  181. {
  182. OnCursorEntered();
  183. }
  184. // chain up to parent
  185. CallParentFunction(new KeyValues("OnCursorMoved", "x", x, "y", y));
  186. }
  187. //-----------------------------------------------------------------------------
  188. // Purpose: Handle mouse cursor entering a menuItem.
  189. //-----------------------------------------------------------------------------
  190. void MenuItem::OnCursorEntered()
  191. {
  192. // post a message to the parent menu.
  193. // forward the message on to the parent of this menu.
  194. KeyValues *msg = new KeyValues ("CursorEnteredMenuItem");
  195. // tell the parent this menuitem is the one that was entered so it can highlight it
  196. msg->SetPtr("VPanel", (void*)GetVPanel());
  197. ivgui()->PostMessage(GetVParent(), msg, NULL);
  198. }
  199. //-----------------------------------------------------------------------------
  200. // Purpose: Handle mouse cursor exiting a menuItem.
  201. //-----------------------------------------------------------------------------
  202. void MenuItem::OnCursorExited()
  203. {
  204. // post a message to the parent menu.
  205. // forward the message on to the parent of this menu.
  206. KeyValues *msg = new KeyValues ("CursorExitedMenuItem");
  207. // tell the parent this menuitem is the one that was entered so it can unhighlight it
  208. msg->SetPtr("VPanel", (void*)GetVPanel());
  209. ivgui()->PostMessage(GetVParent(), msg, NULL);
  210. }
  211. //-----------------------------------------------------------------------------
  212. // Purpose: Handle mouse cursor exiting a menuItem.
  213. //-----------------------------------------------------------------------------
  214. void MenuItem::OnKeyCodeReleased(KeyCode code)
  215. {
  216. if (GetParentMenu()->GetMenuMode() == Menu::KEYBOARD && m_pCascadeMenu)
  217. {
  218. return;
  219. }
  220. // only disarm if we are not opening a cascading menu using keys.
  221. Button::OnKeyCodeReleased(code);
  222. }
  223. //-----------------------------------------------------------------------------
  224. // Purpose: Highlight a menu item
  225. // Menu item buttons highlight if disabled, but won't activate.
  226. //-----------------------------------------------------------------------------
  227. void MenuItem::ArmItem()
  228. {
  229. // close all other menus
  230. GetParentMenu()->CloseOtherMenus(this);
  231. // arm the menuItem.
  232. Button::SetArmed(true);
  233. // When you have a submenu with no scroll bar the menu
  234. // border will not be drawn correctly. This fixes it.
  235. Menu *parent = GetParentMenu();
  236. if ( parent )
  237. {
  238. parent->ForceCalculateWidth();
  239. }
  240. Repaint();
  241. }
  242. //-----------------------------------------------------------------------------
  243. // Purpose: Unhighlight a menu item
  244. //-----------------------------------------------------------------------------
  245. void MenuItem::DisarmItem()
  246. {
  247. // normal behaviour is that the button becomes unarmed
  248. // do not unarm if there is a cascading menu. CloseCascadeMenu handles this.
  249. // and the menu handles it since we close at different times depending
  250. // on whether menu is handling mouse or key events.
  251. if (!m_pCascadeMenu)
  252. {
  253. Button::OnCursorExited();
  254. }
  255. // When you have a submenu with no scroll bar the menu
  256. // border will not be drawn correctly. This fixes it.
  257. Menu *parent = GetParentMenu();
  258. if ( parent )
  259. {
  260. parent->ForceCalculateWidth();
  261. }
  262. Repaint();
  263. }
  264. bool MenuItem::IsItemArmed()
  265. {
  266. return Button::IsArmed();
  267. }
  268. //-----------------------------------------------------------------------------
  269. // Purpose: Pass kill focus events up to parent, This will tell all panels
  270. // in the hierarchy to hide themselves, and enables cascading menus to
  271. // all disappear on selecting an item at the end of the tree.
  272. //-----------------------------------------------------------------------------
  273. void MenuItem::OnKillFocus()
  274. {
  275. GetParentMenu()->OnKillFocus();
  276. }
  277. //-----------------------------------------------------------------------------
  278. // Purpose: fire the menu item as if it has been selected and
  279. // Tell the owner that it is closing
  280. //-----------------------------------------------------------------------------
  281. void MenuItem::FireActionSignal()
  282. {
  283. // cascading menus items don't trigger the parent menu to disappear
  284. // (they trigger the cascading menu to open/close when cursor is moved over/off them)
  285. if (!m_pCascadeMenu)
  286. {
  287. KeyValues *kv = new KeyValues("MenuItemSelected");
  288. kv->SetPtr("panel", this);
  289. ivgui()->PostMessage(GetVParent(), kv, GetVPanel());
  290. // ivgui()->PostMessage(GetVParent(), new KeyValues("MenuItemSelected"), GetVPanel());
  291. Button::FireActionSignal();
  292. // toggle the check next to the item if it is checkable
  293. if (m_bCheckable)
  294. {
  295. SetChecked( !m_bChecked );
  296. }
  297. }
  298. else
  299. {
  300. // if we are in keyboard mode, open the child menu.
  301. if (GetParentMenu()->GetMenuMode() == Menu::KEYBOARD)
  302. {
  303. OpenCascadeMenu();
  304. }
  305. }
  306. }
  307. //-----------------------------------------------------------------------------
  308. // Purpose: Opens the cascading menu.
  309. //-----------------------------------------------------------------------------
  310. void MenuItem::OpenCascadeMenu()
  311. {
  312. if (m_pCascadeMenu)
  313. {
  314. // perform layout on menu, this way it will open in the right spot
  315. // if the window's been moved
  316. m_pCascadeMenu->PerformLayout();
  317. m_pCascadeMenu->SetVisible(true);
  318. ArmItem();
  319. }
  320. }
  321. //-----------------------------------------------------------------------------
  322. // Purpse: Return true if this item triggers a cascading menu
  323. //-----------------------------------------------------------------------------
  324. bool MenuItem::HasMenu()
  325. {
  326. return (m_pCascadeMenu != NULL);
  327. }
  328. //-----------------------------------------------------------------------------
  329. // Purpose: Apply the resource scheme to the menu.
  330. //-----------------------------------------------------------------------------
  331. void MenuItem::ApplySchemeSettings(IScheme *pScheme)
  332. {
  333. // chain back first
  334. Button::ApplySchemeSettings(pScheme);
  335. // get color settings
  336. SetDefaultColor(GetSchemeColor("Menu.TextColor", GetFgColor(), pScheme), GetSchemeColor("Menu.BgColor", GetBgColor(), pScheme));
  337. SetArmedColor(GetSchemeColor("Menu.ArmedTextColor", GetFgColor(), pScheme), GetSchemeColor("Menu.ArmedBgColor", GetBgColor(), pScheme));
  338. SetDepressedColor(GetSchemeColor("Menu.ArmedTextColor", GetFgColor(), pScheme), GetSchemeColor("Menu.ArmedBgColor", GetBgColor(), pScheme));
  339. SetTextInset(atoi(pScheme->GetResourceString("Menu.TextInset")), 0);
  340. // reload images since applyschemesettings in label wipes them out.
  341. if ( m_pCascadeArrow )
  342. {
  343. m_pCascadeArrow->SetFont(pScheme->GetFont("Marlett", IsProportional() ));
  344. m_pCascadeArrow->ResizeImageToContent();
  345. AddImage(m_pCascadeArrow, 0);
  346. }
  347. else if (m_bCheckable)
  348. {
  349. m_pCheck->SetFont( pScheme->GetFont("Marlett", IsProportional()));
  350. SetImageAtIndex(0, m_pCheck, CHECK_INSET);
  351. m_pCheck->ResizeImageToContent();
  352. }
  353. if ( m_pCurrentKeyBinding )
  354. {
  355. m_pCurrentKeyBinding->SetFont(pScheme->GetFont("Default", IsProportional() ));
  356. m_pCurrentKeyBinding->ResizeImageToContent();
  357. }
  358. // Have the menu redo the layout
  359. // Get the parent to resize
  360. Menu * parent = GetParentMenu();
  361. if ( parent )
  362. {
  363. parent->ForceCalculateWidth();
  364. }
  365. }
  366. //-----------------------------------------------------------------------------
  367. // Purpose: Return the size of the text portion of the label.
  368. // for normal menu items this is the same as the label size, but for
  369. // cascading menus it gives you the size of the text portion only, without
  370. // the arrow.
  371. //-----------------------------------------------------------------------------
  372. void MenuItem::GetTextImageSize(int &wide, int &tall)
  373. {
  374. GetTextImage()->GetSize(wide, tall);
  375. }
  376. //-----------------------------------------------------------------------------
  377. // Purpose: Set the size of the text portion of the label.
  378. // For normal menu items this is the same as the label size, but for
  379. // cascading menus it sizes textImage portion only, without
  380. // the arrow.
  381. //-----------------------------------------------------------------------------
  382. void MenuItem::SetTextImageSize(int wide, int tall)
  383. {
  384. GetTextImage()->SetSize(wide, tall);
  385. }
  386. //-----------------------------------------------------------------------------
  387. // Purpose: Return the size of the arrow portion of the label.
  388. // If the menuItem is not a cascading menu, 0 is returned.
  389. //-----------------------------------------------------------------------------
  390. void MenuItem::GetArrowImageSize(int &wide, int &tall)
  391. {
  392. wide = 0, tall = 0;
  393. if (m_pCascadeArrow)
  394. {
  395. m_pCascadeArrow->GetSize(wide, tall);
  396. return;
  397. }
  398. }
  399. //-----------------------------------------------------------------------------
  400. // Purpose: Return the size of the check portion of the label.
  401. //-----------------------------------------------------------------------------
  402. void MenuItem::GetCheckImageSize(int &wide, int &tall)
  403. {
  404. wide = 0, tall = 0;
  405. if (m_pCheck)
  406. {
  407. // resize the image to the contents size
  408. m_pCheck->ResizeImageToContent();
  409. m_pCheck->GetSize(wide, tall);
  410. // include the inset for the check, since nobody but us know about the inset
  411. wide += CHECK_INSET;
  412. return;
  413. }
  414. }
  415. //-----------------------------------------------------------------------------
  416. // Purpose: Return a the menu that this menuItem contains
  417. // This is useful when the parent menu's commands must be
  418. // sent through all menus that are open as well (like hotkeys)
  419. //-----------------------------------------------------------------------------
  420. Menu *MenuItem::GetMenu()
  421. {
  422. return m_pCascadeMenu;
  423. }
  424. //-----------------------------------------------------------------------------
  425. // Purpose: Get the border style for the button. Menu items have no border so
  426. // return null.
  427. //-----------------------------------------------------------------------------
  428. IBorder *MenuItem::GetBorder(bool depressed, bool armed, bool selected, bool keyfocus)
  429. {
  430. return NULL;
  431. }
  432. //-----------------------------------------------------------------------------
  433. // Purpose: Set the menu to key mode if a child menu goes into keymode
  434. //-----------------------------------------------------------------------------
  435. void MenuItem::OnKeyModeSet()
  436. {
  437. // send the message to this parent in case this is a cascading menu
  438. ivgui()->PostMessage(GetVParent(), new KeyValues("KeyModeSet"), GetVPanel());
  439. }
  440. //-----------------------------------------------------------------------------
  441. // Purpose: Return if this menuitem is checkable or not
  442. // This is used by menus to perform the layout properly.
  443. //-----------------------------------------------------------------------------
  444. bool MenuItem::IsCheckable()
  445. {
  446. return m_bCheckable;
  447. }
  448. //-----------------------------------------------------------------------------
  449. // Purpose: Return if this menuitem is checked or not
  450. //-----------------------------------------------------------------------------
  451. bool MenuItem::IsChecked()
  452. {
  453. return m_bChecked;
  454. }
  455. //-----------------------------------------------------------------------------
  456. // Purpose: Set the checked state of a checkable menuitem
  457. // Does nothing if item is not checkable
  458. //-----------------------------------------------------------------------------
  459. void MenuItem::SetChecked(bool state)
  460. {
  461. if (m_bCheckable)
  462. {
  463. m_bChecked = state;
  464. }
  465. }
  466. //-----------------------------------------------------------------------------
  467. // Purpose:
  468. //-----------------------------------------------------------------------------
  469. bool MenuItem::CanBeDefaultButton(void)
  470. {
  471. return false;
  472. }
  473. //-----------------------------------------------------------------------------
  474. // Purpose:
  475. //-----------------------------------------------------------------------------
  476. KeyValues *MenuItem::GetUserData()
  477. {
  478. if ( HasMenu() )
  479. {
  480. return m_pCascadeMenu->GetItemUserData( m_pCascadeMenu->GetActiveItem() );
  481. }
  482. else
  483. {
  484. return m_pUserData;
  485. }
  486. }
  487. //-----------------------------------------------------------------------------
  488. // Purpose: sets the user data
  489. //-----------------------------------------------------------------------------
  490. void MenuItem::SetUserData(const KeyValues *kv)
  491. {
  492. if (m_pUserData)
  493. {
  494. m_pUserData->deleteThis();
  495. m_pUserData = NULL;
  496. }
  497. if ( kv )
  498. {
  499. m_pUserData = kv->MakeCopy();
  500. }
  501. }
  502. //-----------------------------------------------------------------------------
  503. // Purpose: Passing in NULL removes this object
  504. // Input : *keyName -
  505. //-----------------------------------------------------------------------------
  506. void MenuItem::SetCurrentKeyBinding( char const *keyName )
  507. {
  508. if ( !keyName )
  509. {
  510. delete m_pCurrentKeyBinding;
  511. m_pCurrentKeyBinding = NULL;
  512. return;
  513. }
  514. if ( !m_pCurrentKeyBinding )
  515. {
  516. m_pCurrentKeyBinding = new TextImage( keyName );
  517. }
  518. else
  519. {
  520. char curtext[ 256 ];
  521. m_pCurrentKeyBinding->GetText( curtext, sizeof( curtext ) );
  522. if ( !Q_strcmp( curtext, keyName ) )
  523. return;
  524. m_pCurrentKeyBinding->SetText( keyName );
  525. }
  526. InvalidateLayout( false, true );
  527. }
  528. void MenuItem::Paint()
  529. {
  530. BaseClass::Paint();
  531. if ( !m_pCurrentKeyBinding )
  532. return;
  533. int w, h;
  534. GetSize( w, h );
  535. int iw, ih;
  536. m_pCurrentKeyBinding->GetSize( iw, ih );
  537. int x = w - iw - KEYBINDING_INSET;
  538. int y = ( h - ih ) / 2;
  539. if ( IsEnabled() )
  540. {
  541. m_pCurrentKeyBinding->SetPos( x, y );
  542. m_pCurrentKeyBinding->SetColor( GetButtonFgColor() );
  543. m_pCurrentKeyBinding->Paint();
  544. }
  545. else
  546. {
  547. m_pCurrentKeyBinding->SetPos( x + 1 , y + 1 );
  548. m_pCurrentKeyBinding->SetColor( GetDisabledFgColor1() );
  549. m_pCurrentKeyBinding->Paint();
  550. surface()->DrawFlushText();
  551. m_pCurrentKeyBinding->SetPos( x, y );
  552. m_pCurrentKeyBinding->SetColor( GetDisabledFgColor2() );
  553. m_pCurrentKeyBinding->Paint();
  554. }
  555. }
  556. void MenuItem::GetContentSize( int& cw, int &ch )
  557. {
  558. BaseClass::GetContentSize( cw, ch );
  559. if ( !m_pCurrentKeyBinding )
  560. return;
  561. int iw, ih;
  562. m_pCurrentKeyBinding->GetSize( iw, ih );
  563. cw += iw + KEYBINDING_INSET;
  564. ch = MAX( ch, ih );
  565. }
  566. // If we are doing "centered" menus, then we want to recenter the menu text in the text area if there is room.
  567. void MenuItem::RepositionTextImage( int &x, int &y, TextImage *pTextImage )
  568. {
  569. if ( _contentAlignment != a_center )
  570. {
  571. return;
  572. }
  573. int iw, ih;
  574. int cw, ch;
  575. pTextImage->GetSize( iw, ih );
  576. pTextImage->GetContentSize( cw, ch );
  577. if ( cw < iw )
  578. {
  579. x = x + ( iw - cw ) / 2;
  580. }
  581. }