lljoystickbutton.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849
  1. /**
  2. * @file lljoystickbutton.cpp
  3. * @brief LLJoystick class implementation
  4. *
  5. * $LicenseInfo:firstyear=2001&license=viewergpl$
  6. *
  7. * Copyright (c) 2001-2009, Linden Research, Inc.
  8. *
  9. * Second Life Viewer Source Code
  10. * The source code in this file ("Source Code") is provided by Linden Lab
  11. * to you under the terms of the GNU General Public License, version 2.0
  12. * ("GPL"), unless you have obtained a separate licensing agreement
  13. * ("Other License"), formally executed by you and Linden Lab. Terms of
  14. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16. *
  17. * There are special exceptions to the terms and conditions of the GPL as
  18. * it is applied to this Source Code. View the full text of the exception
  19. * in the file doc/FLOSS-exception.txt in this software distribution, or
  20. * online at
  21. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22. *
  23. * By copying, modifying or distributing this software, you acknowledge
  24. * that you have read and understood your obligations described above,
  25. * and agree to abide by those obligations.
  26. *
  27. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29. * COMPLETENESS OR PERFORMANCE.
  30. * $/LicenseInfo$
  31. */
  32. #include "llviewerprecompiledheaders.h"
  33. #include "lljoystickbutton.h"
  34. #include "llgl.h"
  35. #include "llrender.h"
  36. #include "llagent.h"
  37. #include "llfloatermove.h"
  38. #include "llviewertexturelist.h"
  39. static const std::string LL_JOYSTICK_SLIDE = "joystick_slide";
  40. static LLRegisterWidget<LLJoystickAgentSlide> r1(LL_JOYSTICK_SLIDE);
  41. static const std::string LL_JOYSTICK_TURN = "joystick_turn";
  42. static LLRegisterWidget<LLJoystickAgentTurn> r2(LL_JOYSTICK_TURN);
  43. constexpr F32 NUDGE_TIME = 0.25f; // In seconds
  44. constexpr F32 ORBIT_NUDGE_RATE = 0.05f; // Fraction of normal speed
  45. LLJoystick::LLJoystick(const std::string& name, LLRect rect,
  46. const std::string& default_image,
  47. const std::string& selected_image,
  48. EJoystickQuadrant initial_quadrant)
  49. : LLButton(name, rect, default_image, selected_image, NULL, NULL, NULL),
  50. mInitialQuadrant(initial_quadrant),
  51. mInitialOffset(0, 0),
  52. mLastMouse(0, 0),
  53. mFirstMouse(0, 0),
  54. mVertSlopNear(0),
  55. mVertSlopFar(0),
  56. mHorizSlopNear(0),
  57. mHorizSlopFar(0),
  58. mHeldDown(false),
  59. mHeldDownTimer()
  60. {
  61. setHeldDownCallback(&LLJoystick::onHeldDown);
  62. setCallbackUserData(this);
  63. }
  64. void LLJoystick::updateSlop()
  65. {
  66. mVertSlopNear = getRect().getHeight();
  67. mVertSlopFar = getRect().getHeight() * 2;
  68. mHorizSlopNear = getRect().getWidth();
  69. mHorizSlopFar = getRect().getWidth() * 2;
  70. // Compute initial mouse offset based on initial quadrant.
  71. // Place the mouse evenly between the near and far zones.
  72. switch (mInitialQuadrant)
  73. {
  74. case JQ_ORIGIN:
  75. mInitialOffset.set(0, 0);
  76. break;
  77. case JQ_UP:
  78. mInitialOffset.mX = 0;
  79. mInitialOffset.mY = (mVertSlopNear + mVertSlopFar) / 2;
  80. break;
  81. case JQ_DOWN:
  82. mInitialOffset.mX = 0;
  83. mInitialOffset.mY = - (mVertSlopNear + mVertSlopFar) / 2;
  84. break;
  85. case JQ_LEFT:
  86. mInitialOffset.mX = - (mHorizSlopNear + mHorizSlopFar) / 2;
  87. mInitialOffset.mY = 0;
  88. break;
  89. case JQ_RIGHT:
  90. mInitialOffset.mX = (mHorizSlopNear + mHorizSlopFar) / 2;
  91. mInitialOffset.mY = 0;
  92. break;
  93. default:
  94. llerrs << "LLJoystick::LLJoystick() - bad switch case" << llendl;
  95. }
  96. }
  97. bool LLJoystick::handleMouseDown(S32 x, S32 y, MASK mask)
  98. {
  99. mLastMouse.set(x, y);
  100. mFirstMouse.set(x, y);
  101. mMouseDownTimer.reset();
  102. return LLButton::handleMouseDown(x, y, mask);
  103. }
  104. bool LLJoystick::handleMouseUp(S32 x, S32 y, MASK mask)
  105. {
  106. if (hasMouseCapture())
  107. {
  108. mLastMouse.set(x, y);
  109. mHeldDown = false;
  110. onMouseUp();
  111. }
  112. return LLButton::handleMouseUp(x, y, mask);
  113. }
  114. bool LLJoystick::handleHover(S32 x, S32 y, MASK mask)
  115. {
  116. if (hasMouseCapture())
  117. {
  118. mLastMouse.set(x, y);
  119. }
  120. return LLButton::handleHover(x, y, mask);
  121. }
  122. F32 LLJoystick::getElapsedHeldDownTime()
  123. {
  124. return mHeldDown ? getHeldDownTime() : 0.f;
  125. }
  126. //static
  127. void LLJoystick::onHeldDown(void* userdata)
  128. {
  129. LLJoystick* self = (LLJoystick*)userdata;
  130. if (self)
  131. {
  132. self->mHeldDown = true;
  133. self->onHeldDown();
  134. }
  135. }
  136. EJoystickQuadrant LLJoystick::selectQuadrant(LLXMLNodePtr node)
  137. {
  138. EJoystickQuadrant quadrant = JQ_RIGHT;
  139. if (node->hasAttribute("quadrant"))
  140. {
  141. std::string quadrant_name;
  142. node->getAttributeString("quadrant", quadrant_name);
  143. quadrant = quadrantFromName(quadrant_name);
  144. }
  145. return quadrant;
  146. }
  147. std::string LLJoystick::nameFromQuadrant(EJoystickQuadrant quadrant)
  148. {
  149. switch (quadrant)
  150. {
  151. case JQ_ORIGIN:
  152. return "origin";
  153. case JQ_UP:
  154. return "up";
  155. case JQ_DOWN:
  156. return "down";
  157. case JQ_LEFT:
  158. return "left";
  159. case JQ_RIGHT:
  160. return "right";
  161. default:
  162. break;
  163. }
  164. return "";
  165. }
  166. EJoystickQuadrant LLJoystick::quadrantFromName(const std::string& quadrant_str)
  167. {
  168. if (quadrant_str == "up")
  169. {
  170. return JQ_UP;
  171. }
  172. if (quadrant_str == "down")
  173. {
  174. return JQ_DOWN;
  175. }
  176. if (quadrant_str == "right")
  177. {
  178. return JQ_RIGHT;
  179. }
  180. if (quadrant_str == "left")
  181. {
  182. return JQ_LEFT;
  183. }
  184. return JQ_ORIGIN;
  185. }
  186. LLXMLNodePtr LLJoystick::getXML(bool save_children) const
  187. {
  188. LLXMLNodePtr node = LLButton::getXML();
  189. node->createChild("quadrant",
  190. true)->setStringValue(nameFromQuadrant(mInitialQuadrant));
  191. return node;
  192. }
  193. //-----------------------------------------------------------------------------
  194. // LLJoystickAgentTurn
  195. //-----------------------------------------------------------------------------
  196. void LLJoystickAgentTurn::onHeldDown()
  197. {
  198. F32 time = getElapsedHeldDownTime();
  199. updateSlop();
  200. S32 dx = mLastMouse.mX - mFirstMouse.mX + mInitialOffset.mX;
  201. S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY;
  202. F32 m = (F32)dx / abs(dy);
  203. if (m > 1.f)
  204. {
  205. m = 1.f;
  206. }
  207. else if (m < -1.f)
  208. {
  209. m = -1.f;
  210. }
  211. gAgent.moveYaw(-LLFloaterMove::getYawRate(time) * m);
  212. // Handle forward/back movement
  213. if (dy > mVertSlopFar)
  214. {
  215. // If mouse is forward of run region run forward
  216. gAgent.moveAt(1);
  217. }
  218. else if (dy > mVertSlopNear)
  219. {
  220. if (time < NUDGE_TIME)
  221. {
  222. gAgent.moveAtNudge(1);
  223. }
  224. else
  225. {
  226. // If mouse is forward of walk region walk forward
  227. // JC 9/5/2002 - Always run / move quickly.
  228. gAgent.moveAt(1);
  229. }
  230. }
  231. else if (dy < -mVertSlopFar)
  232. {
  233. // If mouse is behind run region run backward
  234. gAgent.moveAt(-1);
  235. }
  236. else if (dy < -mVertSlopNear)
  237. {
  238. if (time < NUDGE_TIME)
  239. {
  240. gAgent.moveAtNudge(-1);
  241. }
  242. else
  243. {
  244. // .If mouse is behind walk region walk backward
  245. // JC 9/5/2002 - Always run / move quickly.
  246. gAgent.moveAt(-1);
  247. }
  248. }
  249. }
  250. //virtual
  251. const std::string& LLJoystickAgentTurn::getTag() const
  252. {
  253. return LL_JOYSTICK_TURN;
  254. }
  255. LLXMLNodePtr LLJoystickAgentTurn::getXML(bool save_children) const
  256. {
  257. LLXMLNodePtr node = LLJoystick::getXML();
  258. node->setName(LL_JOYSTICK_TURN);
  259. return node;
  260. }
  261. LLView* LLJoystickAgentTurn::fromXML(LLXMLNodePtr node, LLView* parent,
  262. LLUICtrlFactory* factory)
  263. {
  264. std::string name = "button";
  265. node->getAttributeString("name", name);
  266. std::string image_unselected;
  267. if (node->hasAttribute("image_unselected"))
  268. {
  269. node->getAttributeString("image_unselected", image_unselected);
  270. }
  271. std::string image_selected;
  272. if (node->hasAttribute("image_selected"))
  273. {
  274. node->getAttributeString("image_selected", image_selected);
  275. }
  276. EJoystickQuadrant quad = JQ_ORIGIN;
  277. if (node->hasAttribute("quadrant"))
  278. {
  279. quad = selectQuadrant(node);
  280. }
  281. LLJoystickAgentTurn* button = new LLJoystickAgentTurn(name, LLRect(),
  282. image_unselected,
  283. image_selected,
  284. quad);
  285. if (node->hasAttribute("halign"))
  286. {
  287. LLFontGL::HAlign halign = selectFontHAlign(node);
  288. button->setHAlign(halign);
  289. }
  290. if (node->hasAttribute("scale_image"))
  291. {
  292. bool needs_scale = false;
  293. node->getAttributeBool("scale_image", needs_scale);
  294. button->setScaleImage(needs_scale);
  295. }
  296. button->initFromXML(node, parent);
  297. return button;
  298. }
  299. //-----------------------------------------------------------------------------
  300. // LLJoystickAgentSlide
  301. //-----------------------------------------------------------------------------
  302. void LLJoystickAgentSlide::onMouseUp()
  303. {
  304. F32 time = getElapsedHeldDownTime();
  305. if (time >= NUDGE_TIME)
  306. {
  307. return;
  308. }
  309. if (mInitialQuadrant == JQ_LEFT)
  310. {
  311. gAgent.moveLeftNudge(1);
  312. }
  313. else if (mInitialQuadrant == JQ_RIGHT)
  314. {
  315. gAgent.moveLeftNudge(-1);
  316. }
  317. }
  318. void LLJoystickAgentSlide::onHeldDown()
  319. {
  320. updateSlop();
  321. S32 dx = mLastMouse.mX - mFirstMouse.mX + mInitialOffset.mX;
  322. S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY;
  323. // handle left-right sliding
  324. if (dx > mHorizSlopNear)
  325. {
  326. gAgent.moveLeft(-1);
  327. }
  328. else if (dx < -mHorizSlopNear)
  329. {
  330. gAgent.moveLeft(1);
  331. }
  332. // Handle forward/back movement
  333. if (dy > mVertSlopFar)
  334. {
  335. // If mouse is forward of run region run forward
  336. gAgent.moveAt(1);
  337. }
  338. else if (dy > mVertSlopNear)
  339. {
  340. // Else if mouse is forward of walk region walk forward
  341. gAgent.moveAtNudge(1);
  342. }
  343. else if (dy < -mVertSlopFar)
  344. {
  345. // Else if mouse is behind run region run backward
  346. gAgent.moveAt(-1);
  347. }
  348. else if (dy < -mVertSlopNear)
  349. {
  350. // Else if mouse is behind walk region walk backward
  351. gAgent.moveAtNudge(-1);
  352. }
  353. }
  354. //virtual
  355. const std::string& LLJoystickAgentSlide::getTag() const
  356. {
  357. return LL_JOYSTICK_SLIDE;
  358. }
  359. LLXMLNodePtr LLJoystickAgentSlide::getXML(bool save_children) const
  360. {
  361. LLXMLNodePtr node = LLJoystick::getXML();
  362. node->setName(LL_JOYSTICK_SLIDE);
  363. return node;
  364. }
  365. //static
  366. LLView* LLJoystickAgentSlide::fromXML(LLXMLNodePtr node, LLView* parent,
  367. LLUICtrlFactory* factory)
  368. {
  369. std::string name = "button";
  370. node->getAttributeString("name", name);
  371. std::string image_unselected;
  372. if (node->hasAttribute("image_unselected"))
  373. {
  374. node->getAttributeString("image_unselected", image_unselected);
  375. }
  376. std::string image_selected;
  377. if (node->hasAttribute("image_selected"))
  378. {
  379. node->getAttributeString("image_selected", image_selected);
  380. }
  381. EJoystickQuadrant quad = JQ_ORIGIN;
  382. if (node->hasAttribute("quadrant"))
  383. {
  384. quad = selectQuadrant(node);
  385. }
  386. LLJoystickAgentSlide* button = new LLJoystickAgentSlide(name, LLRect(),
  387. image_unselected,
  388. image_selected,
  389. quad);
  390. if (node->hasAttribute("halign"))
  391. {
  392. LLFontGL::HAlign halign = selectFontHAlign(node);
  393. button->setHAlign(halign);
  394. }
  395. if (node->hasAttribute("scale_image"))
  396. {
  397. bool needs_scale = false;
  398. node->getAttributeBool("scale_image", needs_scale);
  399. button->setScaleImage(needs_scale);
  400. }
  401. button->initFromXML(node, parent);
  402. return button;
  403. }
  404. //-----------------------------------------------------------------------------
  405. // LLJoystickCameraRotate
  406. //-----------------------------------------------------------------------------
  407. LLJoystickCameraRotate::LLJoystickCameraRotate(const std::string& name,
  408. LLRect rect,
  409. const std::string& out_img,
  410. const std::string& in_img)
  411. : LLJoystick(name, rect, out_img, in_img, JQ_ORIGIN),
  412. mInLeft(false),
  413. mInTop(false),
  414. mInRight(false),
  415. mInBottom(false)
  416. {
  417. }
  418. void LLJoystickCameraRotate::updateSlop()
  419. {
  420. // Do the initial offset calculation based on mousedown location
  421. // Small fixed slop region
  422. mVertSlopNear = 16;
  423. mVertSlopFar = 32;
  424. mHorizSlopNear = 16;
  425. mHorizSlopFar = 32;
  426. }
  427. bool LLJoystickCameraRotate::handleMouseDown(S32 x, S32 y, MASK mask)
  428. {
  429. updateSlop();
  430. // Set initial offset based on initial click location
  431. S32 horiz_center = getRect().getWidth() / 2;
  432. S32 vert_center = getRect().getHeight() / 2;
  433. S32 dx = x - horiz_center;
  434. S32 dy = y - vert_center;
  435. if (dy > dx && dy > -dx)
  436. {
  437. // Top
  438. mInitialOffset.mX = 0;
  439. mInitialOffset.mY = (mVertSlopNear + mVertSlopFar) / 2;
  440. mInitialQuadrant = JQ_UP;
  441. }
  442. else if (dy > dx && dy <= -dx)
  443. {
  444. // Left
  445. mInitialOffset.mX = - (mHorizSlopNear + mHorizSlopFar) / 2;
  446. mInitialOffset.mY = 0;
  447. mInitialQuadrant = JQ_LEFT;
  448. }
  449. else if (dy <= dx && dy <= -dx)
  450. {
  451. // Bottom
  452. mInitialOffset.mX = 0;
  453. mInitialOffset.mY = - (mVertSlopNear + mVertSlopFar) / 2;
  454. mInitialQuadrant = JQ_DOWN;
  455. }
  456. else
  457. {
  458. // Right
  459. mInitialOffset.mX = (mHorizSlopNear + mHorizSlopFar) / 2;
  460. mInitialOffset.mY = 0;
  461. mInitialQuadrant = JQ_RIGHT;
  462. }
  463. return LLJoystick::handleMouseDown(x, y, mask);
  464. }
  465. void LLJoystickCameraRotate::onHeldDown()
  466. {
  467. updateSlop();
  468. S32 dx = mLastMouse.mX - mFirstMouse.mX + mInitialOffset.mX;
  469. S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY;
  470. // Left-right rotation
  471. if (dx > mHorizSlopNear)
  472. {
  473. gAgent.unlockView();
  474. gAgent.setOrbitLeftKey(getOrbitRate());
  475. }
  476. else if (dx < -mHorizSlopNear)
  477. {
  478. gAgent.unlockView();
  479. gAgent.setOrbitRightKey(getOrbitRate());
  480. }
  481. // Over/under rotation
  482. if (dy > mVertSlopNear)
  483. {
  484. gAgent.unlockView();
  485. gAgent.setOrbitUpKey(getOrbitRate());
  486. }
  487. else if (dy < -mVertSlopNear)
  488. {
  489. gAgent.unlockView();
  490. gAgent.setOrbitDownKey(getOrbitRate());
  491. }
  492. }
  493. F32 LLJoystickCameraRotate::getOrbitRate()
  494. {
  495. F32 time = getElapsedHeldDownTime();
  496. if (time >= NUDGE_TIME)
  497. {
  498. return 1;
  499. }
  500. return ORBIT_NUDGE_RATE + time * (1.f - ORBIT_NUDGE_RATE)/ NUDGE_TIME;
  501. }
  502. // Only used for drawing
  503. void LLJoystickCameraRotate::setToggleState(bool left, bool top, bool right,
  504. bool bottom)
  505. {
  506. mInLeft = left;
  507. mInTop = top;
  508. mInRight = right;
  509. mInBottom = bottom;
  510. }
  511. void LLJoystickCameraRotate::draw()
  512. {
  513. LLGLSUIDefault gls_ui;
  514. getImageUnselected()->draw(0, 0);
  515. if (mInTop)
  516. {
  517. drawRotatedImage(getImageSelected()->getImage(), 0);
  518. }
  519. if (mInRight)
  520. {
  521. drawRotatedImage(getImageSelected()->getImage(), 1);
  522. }
  523. if (mInBottom)
  524. {
  525. drawRotatedImage(getImageSelected()->getImage(), 2);
  526. }
  527. if (mInLeft)
  528. {
  529. drawRotatedImage(getImageSelected()->getImage(), 3);
  530. }
  531. if (sDebugRects)
  532. {
  533. drawDebugRect();
  534. }
  535. }
  536. // Draws image rotated by multiples of 90 degrees
  537. void LLJoystickCameraRotate::drawRotatedImage(LLGLTexture* image,
  538. S32 rotations)
  539. {
  540. S32 width = image->getWidth();
  541. S32 height = image->getHeight();
  542. static const F32 uv[][2] =
  543. {
  544. { 1.f, 1.f },
  545. { 0.f, 1.f },
  546. { 0.f, 0.f },
  547. { 1.f, 0.f }
  548. };
  549. gGL.getTexUnit(0)->bind(image);
  550. gGL.color4fv(UI_VERTEX_COLOR.mV);
  551. gGL.begin(LLRender::TRIANGLES);
  552. {
  553. gGL.texCoord2fv(uv[rotations % 4]);
  554. gGL.vertex2i(width, height);
  555. gGL.texCoord2fv(uv[(rotations + 1) % 4]);
  556. gGL.vertex2i(0, height);
  557. gGL.texCoord2fv(uv[(rotations + 2) % 4]);
  558. gGL.vertex2i(0, 0);
  559. gGL.texCoord2fv(uv[rotations % 4]);
  560. gGL.vertex2i(width, height);
  561. gGL.texCoord2fv(uv[(rotations + 2) % 4]);
  562. gGL.vertex2i(0, 0);
  563. gGL.texCoord2fv(uv[(rotations + 3) % 4]);
  564. gGL.vertex2i(width, 0);
  565. }
  566. gGL.end();
  567. }
  568. //-----------------------------------------------------------------------------
  569. // LLJoystickCameraTrack
  570. //-----------------------------------------------------------------------------
  571. void LLJoystickCameraTrack::onHeldDown()
  572. {
  573. updateSlop();
  574. S32 dx = mLastMouse.mX - mFirstMouse.mX + mInitialOffset.mX;
  575. S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY;
  576. if (dx > mVertSlopNear)
  577. {
  578. gAgent.unlockView();
  579. gAgent.setPanRightKey(getOrbitRate());
  580. }
  581. else if (dx < -mVertSlopNear)
  582. {
  583. gAgent.unlockView();
  584. gAgent.setPanLeftKey(getOrbitRate());
  585. }
  586. if (dy > mVertSlopNear)
  587. {
  588. gAgent.unlockView();
  589. gAgent.setPanUpKey(getOrbitRate());
  590. }
  591. else if (dy < -mVertSlopNear)
  592. {
  593. gAgent.unlockView();
  594. gAgent.setPanDownKey(getOrbitRate());
  595. }
  596. }
  597. //-----------------------------------------------------------------------------
  598. // LLJoystickCameraZoom
  599. //-----------------------------------------------------------------------------
  600. LLJoystickCameraZoom::LLJoystickCameraZoom(const std::string& name,
  601. LLRect rect,
  602. const std::string& out_img,
  603. const std::string& plus_in_img,
  604. const std::string& minus_in_img)
  605. : LLJoystick(name, rect, out_img, LLStringUtil::null, JQ_ORIGIN),
  606. mInTop(false),
  607. mInBottom(false)
  608. {
  609. mPlusInImage = LLUI::getUIImage(plus_in_img);
  610. mMinusInImage = LLUI::getUIImage(minus_in_img);
  611. }
  612. bool LLJoystickCameraZoom::handleMouseDown(S32 x, S32 y, MASK mask)
  613. {
  614. bool handled = LLJoystick::handleMouseDown(x, y, mask);
  615. if (handled)
  616. {
  617. if (mFirstMouse.mY > getRect().getHeight() / 2)
  618. {
  619. mInitialQuadrant = JQ_UP;
  620. }
  621. else
  622. {
  623. mInitialQuadrant = JQ_DOWN;
  624. }
  625. }
  626. return handled;
  627. }
  628. void LLJoystickCameraZoom::onHeldDown()
  629. {
  630. updateSlop();
  631. constexpr F32 FAST_RATE = 2.5f; // Two and a half times the normal rate
  632. S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY;
  633. if (dy > mVertSlopFar)
  634. {
  635. // Zoom in fast
  636. gAgent.unlockView();
  637. gAgent.setOrbitInKey(FAST_RATE);
  638. }
  639. else if (dy > mVertSlopNear)
  640. {
  641. // Zoom in slow
  642. gAgent.unlockView();
  643. gAgent.setOrbitInKey(getOrbitRate());
  644. }
  645. else if (dy < -mVertSlopFar)
  646. {
  647. // Zoom out fast
  648. gAgent.unlockView();
  649. gAgent.setOrbitOutKey(FAST_RATE);
  650. }
  651. else if (dy < -mVertSlopNear)
  652. {
  653. // Zoom out slow
  654. gAgent.unlockView();
  655. gAgent.setOrbitOutKey(getOrbitRate());
  656. }
  657. }
  658. // Only used for drawing
  659. void LLJoystickCameraZoom::setToggleState(bool top, bool bottom)
  660. {
  661. mInTop = top;
  662. mInBottom = bottom;
  663. }
  664. void LLJoystickCameraZoom::draw()
  665. {
  666. if (mInTop)
  667. {
  668. mPlusInImage->draw(0,0);
  669. }
  670. else if (mInBottom)
  671. {
  672. mMinusInImage->draw(0,0);
  673. }
  674. else
  675. {
  676. getImageUnselected()->draw(0, 0);
  677. }
  678. if (sDebugRects)
  679. {
  680. drawDebugRect();
  681. }
  682. }
  683. void LLJoystickCameraZoom::updateSlop()
  684. {
  685. mVertSlopNear = getRect().getHeight() / 4;
  686. mVertSlopFar = getRect().getHeight() / 2;
  687. mHorizSlopNear = getRect().getWidth() / 4;
  688. mHorizSlopFar = getRect().getWidth() / 2;
  689. // Compute initial mouse offset based on initial quadrant. Place the mouse
  690. // evenly between the near and far zones.
  691. switch (mInitialQuadrant)
  692. {
  693. case JQ_ORIGIN:
  694. mInitialOffset.set(0, 0);
  695. break;
  696. case JQ_UP:
  697. mInitialOffset.mX = 0;
  698. mInitialOffset.mY = (mVertSlopNear + mVertSlopFar) / 2;
  699. break;
  700. case JQ_DOWN:
  701. mInitialOffset.mX = 0;
  702. mInitialOffset.mY = - (mVertSlopNear + mVertSlopFar) / 2;
  703. break;
  704. case JQ_LEFT:
  705. mInitialOffset.mX = - (mHorizSlopNear + mHorizSlopFar) / 2;
  706. mInitialOffset.mY = 0;
  707. break;
  708. case JQ_RIGHT:
  709. mInitialOffset.mX = (mHorizSlopNear + mHorizSlopFar) / 2;
  710. mInitialOffset.mY = 0;
  711. break;
  712. default:
  713. llerrs << "Bad switch case" << llendl;
  714. }
  715. }
  716. F32 LLJoystickCameraZoom::getOrbitRate()
  717. {
  718. F32 time = getElapsedHeldDownTime();
  719. if (time >= NUDGE_TIME)
  720. {
  721. return 1.f;
  722. }
  723. return ORBIT_NUDGE_RATE + time * (1.f - ORBIT_NUDGE_RATE) / NUDGE_TIME;
  724. }