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.

554 lines
14 KiB

  1. #include <vgui_controls/JoypadFocus.h>
  2. #include <vgui_controls/ImagePanelColored.h>
  3. #include <vgui/MouseCode.h>
  4. #include <vgui/ISystem.h>
  5. #include <vgui/ISurface.h>
  6. #include "keydefs.h"
  7. // memdbgon must be the last include file in a .cpp file!!!
  8. #include "tier0/memdbgon.h"
  9. CJoypadFocus::CJoypadFocus()
  10. {
  11. m_bJoypadMode = false;
  12. m_bDebugOutput = false;
  13. m_iModal = 0;
  14. m_FocusAreas.Purge();
  15. m_CurrentFocus.bClickOnFocus = false;
  16. m_CurrentFocus.hPanel = NULL;
  17. m_KeyNum[JF_KEY_UP] = K_AXISY_POS;
  18. m_KeyNum[JF_KEY_DOWN] = K_AXISY_NEG;
  19. m_KeyNum[JF_KEY_LEFT] = K_AXISX_NEG;
  20. m_KeyNum[JF_KEY_RIGHT] = K_AXISX_POS;
  21. m_KeyNum[JF_KEY_CONFIRM] = K_JOY1;
  22. m_KeyNum[JF_KEY_CANCEL] = K_JOY2;
  23. for (int i=0;i<NUM_JF_KEYS;i++)
  24. {
  25. m_bKeyDown[i] = false;
  26. m_fNextKeyRepeatTime[i] = 0;
  27. }
  28. }
  29. void CJoypadFocus::SetJoypadCodes(int iUpCode, int iDownCode, int iLeftCode, int iRightCode, int iConfirmCode, int iCancelCode)
  30. {
  31. m_KeyNum[JF_KEY_UP] = iUpCode;
  32. m_KeyNum[JF_KEY_DOWN] = iDownCode;
  33. m_KeyNum[JF_KEY_LEFT] = iLeftCode;
  34. m_KeyNum[JF_KEY_RIGHT] = iRightCode;
  35. m_KeyNum[JF_KEY_CONFIRM] = iConfirmCode;
  36. m_KeyNum[JF_KEY_CANCEL] = iCancelCode;
  37. }
  38. void CJoypadFocus::AddToFocusList(vgui::Panel* pPanel, bool bClickOnFocus, bool bModal)
  39. {
  40. if (!pPanel)
  41. return;
  42. FocusArea Focus;
  43. Focus.hPanel = pPanel;
  44. Focus.bClickOnFocus = bClickOnFocus;
  45. Focus.bModal = bModal;
  46. if (bModal)
  47. m_iModal++;
  48. vgui::PHandle hOtherPanel;
  49. for (int i=0;i<m_FocusAreas.Count();i++)
  50. {
  51. hOtherPanel = m_FocusAreas[i].hPanel;
  52. if (hOtherPanel == Focus.hPanel) // check if it's already here
  53. return;
  54. }
  55. if (m_bDebugOutput)
  56. Msg("adding panel to joypad focus list: %s:%s\n", pPanel->GetName(), pPanel->GetClassName());
  57. //if (GetFocusPanel() == NULL)
  58. //{
  59. //SetFocusPanel(pPanel, bClickOnFocus);
  60. //}
  61. m_FocusAreas.AddToTail(Focus);
  62. }
  63. // sets focus to one of the panels in the list of focus areas
  64. void CJoypadFocus::SetFocusPanel(int index)
  65. {
  66. if (index < 0 || index >= m_FocusAreas.Count())
  67. return;
  68. SetFocusPanel(m_FocusAreas[index].hPanel, m_FocusAreas[index].bClickOnFocus);
  69. }
  70. void CJoypadFocus::SetFocusPanel(vgui::Panel* pPanel, bool bClickOnFocus)
  71. {
  72. m_CurrentFocus.hPanel = pPanel;
  73. m_CurrentFocus.bClickOnFocus = bClickOnFocus;
  74. if (pPanel)
  75. {
  76. if (m_bDebugOutput)
  77. Msg("Setcur jfocus: %s:%s ", pPanel->GetName(), pPanel->GetClassName());
  78. if (!m_hOutline.Get())
  79. {
  80. CJoypadOutline *pOutline = new CJoypadOutline(NULL, "JoypadOutline");
  81. m_hOutline = pOutline;
  82. if (m_bDebugOutput)
  83. {
  84. if (pOutline)
  85. Msg("spawned outline\n");
  86. else
  87. Msg("Outline is zero!!\n");
  88. }
  89. }
  90. if (m_hOutline.Get())
  91. {
  92. if (pPanel->GetParent())
  93. m_hOutline->SetParent(pPanel->GetParent());
  94. else
  95. m_hOutline->SetParent(pPanel);
  96. int x, y, w, t;
  97. pPanel->GetJoypadCursorBounds(x, y, w, t);
  98. m_hOutline->MoveToFront();
  99. m_hOutline->SizeTo(x, y, w, t);
  100. m_hOutline->SetMouseInputEnabled(false);
  101. }
  102. if (bClickOnFocus)
  103. {
  104. ClickFocusPanel(true, false);
  105. ClickFocusPanel(false, false);
  106. }
  107. }
  108. else
  109. {
  110. if (m_bDebugOutput)
  111. Msg("Cleared currently focused joypad panel\n");
  112. }
  113. }
  114. vgui::Panel* CJoypadFocus::GetFocusPanel()
  115. {
  116. return m_CurrentFocus.hPanel;
  117. }
  118. void CJoypadFocus::RemoveFromFocusList(vgui::Panel* pPanel)
  119. {
  120. if (!pPanel)
  121. return;
  122. if (m_bDebugOutput)
  123. Msg("removing panel from joypad focus list: %s:%s\n", pPanel->GetName(), pPanel->GetClassName());
  124. vgui::PHandle hPanel;
  125. hPanel = pPanel;
  126. for (int i=m_FocusAreas.Count()-1;i>=0;i--)
  127. {
  128. if (m_FocusAreas[i].hPanel == hPanel)
  129. {
  130. if (m_FocusAreas[i].bModal)
  131. m_iModal--;
  132. m_FocusAreas.Remove(i);
  133. }
  134. }
  135. }
  136. bool CJoypadFocus::OnJoypadButtonPressed(int keynum)
  137. {
  138. if (!IsJoypadMode())
  139. return false;
  140. // don't allow multiple direction presses at once
  141. int iDirectionPress = -1;
  142. for (int i=0;i<4;i++)
  143. {
  144. if (m_KeyNum[i] == keynum)
  145. iDirectionPress = i;
  146. }
  147. bool bDirectionAlreadyDown = false;
  148. if (iDirectionPress != -1)
  149. {
  150. for (int i=0;i<4;i++)
  151. {
  152. if (m_bKeyDown[i] && i != iDirectionPress) // if it's a direction we're already pushing, we allow it?
  153. bDirectionAlreadyDown = true;
  154. }
  155. // abort if we're pushing a direction and another direction is already down
  156. if (bDirectionAlreadyDown)
  157. {
  158. return true;
  159. }
  160. }
  161. for (int i=0;i<NUM_JF_KEYS;i++)
  162. {
  163. if (m_KeyNum[i] == keynum)
  164. {
  165. m_bKeyDown[i] = true;
  166. m_fNextKeyRepeatTime[i] = vgui::system()->GetTimeMillis() + JF_KEY_REPEAT_DELAY;
  167. }
  168. }
  169. if (keynum == m_KeyNum[JF_KEY_UP])
  170. {
  171. int index = FindNextPanel(GetFocusPanel(), 270);
  172. if (index != -1)
  173. SetFocusPanel(index);
  174. return (GetFocusPanel() != NULL);
  175. }
  176. else if (keynum == m_KeyNum[JF_KEY_DOWN])
  177. {
  178. int index = FindNextPanel(GetFocusPanel(), 90);
  179. if (index != -1)
  180. SetFocusPanel(index);
  181. return (GetFocusPanel() != NULL);
  182. }
  183. else if (keynum == m_KeyNum[JF_KEY_LEFT])
  184. {
  185. int index = FindNextPanel(GetFocusPanel(), 180);
  186. if (index != -1)
  187. SetFocusPanel(index);
  188. return (GetFocusPanel() != NULL);
  189. }
  190. else if (keynum == m_KeyNum[JF_KEY_RIGHT])
  191. {
  192. int index = FindNextPanel(GetFocusPanel(), 0);
  193. if (index != -1)
  194. SetFocusPanel(index);
  195. return (GetFocusPanel() != NULL);
  196. }
  197. else if (keynum == m_KeyNum[JF_KEY_CONFIRM])
  198. {
  199. if (m_CurrentFocus.bClickOnFocus)
  200. DoubleClickFocusPanel(false);
  201. else
  202. ClickFocusPanel(true, false);
  203. return (GetFocusPanel() != NULL);
  204. }
  205. else if (keynum == m_KeyNum[JF_KEY_CANCEL])
  206. {
  207. ClickFocusPanel(true, true);
  208. return (GetFocusPanel() != NULL);
  209. }
  210. // don't swallow non-direction or confirm/cancel keys
  211. return false;
  212. }
  213. bool CJoypadFocus::OnJoypadButtonReleased(int keynum)
  214. {
  215. if (!IsJoypadMode())
  216. return false;
  217. for (int i=0;i<NUM_JF_KEYS;i++)
  218. {
  219. if (m_KeyNum[i] == keynum)
  220. {
  221. m_bKeyDown[i] = false;
  222. m_fNextKeyRepeatTime[i] = 0;
  223. }
  224. }
  225. if (keynum == m_KeyNum[JF_KEY_CONFIRM])
  226. {
  227. ClickFocusPanel(false, false);
  228. return (GetFocusPanel() != NULL);
  229. }
  230. else if (keynum == m_KeyNum[JF_KEY_CANCEL])
  231. {
  232. ClickFocusPanel(false, true);
  233. return (GetFocusPanel() != NULL);
  234. }
  235. // don't swallow non confirm/cancel buttons
  236. return false;
  237. }
  238. void CJoypadFocus::CheckKeyRepeats()
  239. {
  240. float curtime = vgui::system()->GetTimeMillis();
  241. for (int i=0;i<NUM_JF_KEYS;i++)
  242. {
  243. if (m_fNextKeyRepeatTime[i]!=0 && curtime > m_fNextKeyRepeatTime[i])
  244. {
  245. // player is holding down the specified key, send another press
  246. if (m_bDebugOutput)
  247. Msg("Sending key repeat\n");
  248. OnJoypadButtonPressed(m_KeyNum[i]);
  249. m_fNextKeyRepeatTime[i] = curtime + JF_KEY_REPEAT_INTERVAL;
  250. }
  251. }
  252. }
  253. void CJoypadFocus::ClickFocusPanel(bool bDown, bool bRightMouse)
  254. {
  255. vgui::Panel *pOther = GetFocusPanel();
  256. if (pOther)
  257. {
  258. if (bDown)
  259. pOther->OnMousePressed(bRightMouse ? vgui::MOUSE_RIGHT : vgui::MOUSE_LEFT);
  260. else
  261. pOther->OnMouseReleased(bRightMouse ? vgui::MOUSE_RIGHT : vgui::MOUSE_LEFT);
  262. }
  263. }
  264. void CJoypadFocus::DoubleClickFocusPanel(bool bRightMouse)
  265. {
  266. vgui::Panel *pOther = GetFocusPanel();
  267. if (pOther)
  268. {
  269. pOther->OnMouseDoublePressed(bRightMouse ? vgui::MOUSE_RIGHT : vgui::MOUSE_LEFT);
  270. }
  271. }
  272. // find a panel in the specified direction
  273. int CJoypadFocus::FindNextPanel(vgui::Panel *pSource, float angle)
  274. {
  275. if (!pSource)
  276. {
  277. // no panel selected, should pick the top left most one
  278. int iBestIndex = -1;
  279. float fBestRating = -1;
  280. for (int i=0;i<m_FocusAreas.Count();i++)
  281. {
  282. if (m_iModal>0 && !m_FocusAreas[i].bModal)
  283. continue;
  284. vgui::Panel *pPanel = m_FocusAreas[i].hPanel;
  285. if (!pPanel)
  286. continue;
  287. int x, y;
  288. pPanel->GetPos(x, y);
  289. float fRating = x * 1.3 + y;
  290. if (fBestRating == -1 || fRating < fBestRating)
  291. {
  292. fBestRating = fRating;
  293. iBestIndex = i;
  294. }
  295. }
  296. return iBestIndex;
  297. }
  298. if (m_bDebugOutput)
  299. Msg("angle = %f ", angle);
  300. float radangle = angle * (3.14159265f / 180.0f);
  301. float xdir = cos(radangle);
  302. float ydir = sin(radangle);
  303. if (m_bDebugOutput)
  304. Msg("xdir = %f ydir = %f\n", xdir, ydir);
  305. //Vector2D dir(xdir, ydir);
  306. //dir.NormalizeInPlace(); // normalization unnecessary?
  307. // find the centre of our panel
  308. int x, y, w, t;
  309. pSource->GetBounds(x, y, w, t);
  310. int posx = w * 0.5f;
  311. int posy = t * 0.5f;
  312. pSource->LocalToScreen(posx, posy);
  313. // position the source dot at the middle of the edge in the direction we're searching in
  314. posx += (w * 0.5f) * xdir - xdir;
  315. posy += (t * 0.5f) * ydir - ydir;
  316. //Vector2D vecSource(posx, posy);
  317. // go through all panels, see if they're in the right direction and rate them
  318. vgui::Panel *pBest = NULL;
  319. int iBestIndex = -1;
  320. float fBestRating = -1;
  321. for (int i=0;i<m_FocusAreas.Count();i++)
  322. {
  323. vgui::Panel* pOther = m_FocusAreas[i].hPanel;
  324. if (!pOther || pOther == pSource || !IsPanelReallyVisible(pOther))
  325. continue;
  326. if (m_iModal>0 && !m_FocusAreas[i].bModal)
  327. continue;
  328. // check if it's within our arc
  329. int w2, t2;
  330. pOther->GetSize(w2, t2);
  331. int posx2 = w2 * 0.5f;
  332. int posy2 = t2 * 0.5f;
  333. pOther->LocalToScreen(posx2, posy2);
  334. // pick the point in our bounds closest to the source point
  335. if (posx < posx2)
  336. posx2 = max((posx2 - w2 * 0.5f), posx);
  337. else if (posx > posx2)
  338. posx2 = min((posx2 + w2 * 0.5f), posx);
  339. if (posy < posy2)
  340. posy2 = max((posy2 - t2 * 0.5f), posy);
  341. else if (posy > posy2)
  342. posy2 = min((posy2 + t2 * 0.5f), posy);
  343. //Vector2D vecOther(posx2, posy2);
  344. float diffx = posx2 - posx;
  345. float diffy = posy2 - posy;
  346. float diff_len = sqrt(diffx * diffx + diffy * diffy);
  347. if (diff_len <= 0)
  348. diff_len = 0.1f;
  349. float diffx_norm = diffx / diff_len;
  350. float diffy_norm = diffy / diff_len;
  351. float the_dot = 0;
  352. the_dot += diffx_norm * xdir;
  353. the_dot += diffy_norm * ydir;
  354. //Vector2D difference;
  355. //difference = vecOther - vecSource;
  356. //Vector2D diffnorm = difference;
  357. //diffnorm.NormalizeInPlace();
  358. if (m_bDebugOutput)
  359. Msg("Checking panel %i (%s). diff=%f,%f dir=%f, %f dot=%f\n", i, pOther->GetClassName(), diffx, diffy, xdir, ydir, the_dot);
  360. if (the_dot > 0.3f)
  361. {
  362. // this panel is in the right direction, now rate it
  363. float fRating = -1;
  364. if (angle == 0 || angle == 90 || angle == 180 || angle == 270) // we're searching in a perpendicular direction, so we can do a more accurate rating
  365. {
  366. fRating = 0;
  367. // double perpendicular distance cost
  368. if (angle == 90 || angle == 270)
  369. {
  370. if (m_bDebugOutput)
  371. Msg(" vertical rating: %f * 3 + %f\n", fabs(diffx), fabs(diffy));
  372. fRating = fabs(diffy) + (fabs(diffx) * 3.0f);
  373. }
  374. else
  375. {
  376. if (m_bDebugOutput)
  377. Msg(" horiz rating: %f + %f * 3\n", fabs(diffx), fabs(diffy));
  378. fRating = fabs(diffx) + (fabs(diffy) * 3.0f);
  379. }
  380. }
  381. else // strange angle, just rate based on distance
  382. {
  383. if (m_bDebugOutput)
  384. Msg(" distancebased rating\n");
  385. fRating = diff_len;
  386. }
  387. if (m_bDebugOutput)
  388. Msg(" Panel is in right dir, rating = %f\n", fRating);
  389. // if this panel is better, remember it
  390. if (pBest == NULL || (fRating != -1 && fRating < fBestRating))
  391. {
  392. if (m_bDebugOutput)
  393. Msg(" this is the new best!\n");
  394. pBest = pOther;
  395. iBestIndex = i;
  396. fBestRating = fRating;
  397. }
  398. }
  399. }
  400. return iBestIndex;
  401. }
  402. bool CJoypadFocus::IsPanelReallyVisible(vgui::Panel *pPanel)
  403. {
  404. while (pPanel->IsVisible() && pPanel->GetAlpha() >= 255) // todo: make required alpha an arg
  405. {
  406. pPanel = pPanel->GetParent();
  407. if (!pPanel)
  408. return true; // got to the top without hitting something that wasn't visible
  409. }
  410. return false;
  411. }
  412. // ================================================================================
  413. CJoypadFocus* g_pJoypadFocus = NULL;
  414. CJoypadFocus* GetJoypadFocus()
  415. {
  416. if (g_pJoypadFocus == NULL)
  417. {
  418. g_pJoypadFocus = new CJoypadFocus();
  419. }
  420. return g_pJoypadFocus;
  421. }
  422. // ================================================================================
  423. CJoypadOutline::CJoypadOutline(vgui::Panel *parent, const char *name) : vgui::Panel(parent, name)
  424. {
  425. SetMouseInputEnabled(false);
  426. SetAlpha(0);
  427. m_hLastFocusPanel = NULL;
  428. //m_pImagePanel = new vgui::ImagePanelColored(this, "ImagePanel");
  429. //m_pImagePanel->SetImage("swarm/JoypadCursor");
  430. //m_pImagePanel->SetShouldScaleImage(true);
  431. }
  432. void CJoypadOutline::ApplySchemeSettings(vgui::IScheme* pScheme)
  433. {
  434. BaseClass::ApplySchemeSettings(pScheme);
  435. SetPaintBackgroundEnabled(false);
  436. //SetPaintBackgroundType(0);
  437. //SetBgColor(Color(255,0,0,64));
  438. }
  439. void CJoypadOutline::Paint()
  440. {
  441. long curtime = vgui::system()->GetTimeMillis();
  442. int pulse_time = 1000;
  443. int half_pulse_time = pulse_time / 2;
  444. long remainder = curtime % pulse_time;
  445. if (remainder > half_pulse_time)
  446. remainder = half_pulse_time - (remainder - half_pulse_time);
  447. float fWhite = float(remainder) / float(half_pulse_time);
  448. //int red = 66.0f + (255.0f - 66.0f) * fWhite;
  449. //int green = 142.0f + (255.0f - 142.0f) * fWhite;
  450. //int blue = 192.0f + (255.0f - 192.0f) * fWhite;
  451. int red = 255.0f + (255.0f - 255) * fWhite;
  452. int green = 0 + (255.0f - 0) * fWhite;
  453. int blue = 0;
  454. Color col(red, green, blue, 255);
  455. DrawBox( 0, 0, GetWide(), GetTall(), col, 0.8f, true );
  456. }
  457. void CJoypadOutline::GetCornerTextureSize( int& w, int& h )
  458. {
  459. BaseClass::GetCornerTextureSize(w, h);
  460. int sw, sh;
  461. vgui::surface()->GetScreenSize( sw, sh );
  462. float fScale = sh / 768.0f;
  463. w *= fScale * 0.25f;
  464. h *= fScale * 0.25f;
  465. }
  466. // hide/show us and position us over the focused panel
  467. void CJoypadOutline::OnThink()
  468. {
  469. if (!GetJoypadFocus())
  470. return;
  471. vgui::Panel* pFocus = GetJoypadFocus()->GetFocusPanel();
  472. //Msg("outline thinking, focus = %s\n", pFocus ? pFocus->GetClassName() : "none");
  473. if (pFocus && GetJoypadFocus()->IsJoypadMode())
  474. {
  475. if (pFocus != m_hLastFocusPanel.Get())
  476. {
  477. m_hLastFocusPanel = pFocus;
  478. SetAlpha(255);
  479. }
  480. if (pFocus->GetParent())
  481. SetParent(pFocus->GetParent());
  482. else
  483. SetParent(pFocus);
  484. // make sure we're positioned over the focused panel
  485. int x, y, w, t;
  486. pFocus->GetJoypadCursorBounds(x, y, w, t);
  487. SizeTo(x, y, w, t);
  488. GetJoypadFocus()->CheckKeyRepeats();
  489. SetMouseInputEnabled(false);
  490. }
  491. else
  492. {
  493. SetAlpha(0);
  494. SetMouseInputEnabled(false);
  495. }
  496. }
  497. void CJoypadOutline::SizeTo(int x, int y, int w, int t)
  498. {
  499. SetBounds(x, y, w, t);
  500. //m_pImagePanel->SetBounds(0, 0, w, t);
  501. }