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.

647 lines
20 KiB

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