llvirtualtrackball.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. /**
  2. * @file llvirtualtrackball.cpp
  3. * @brief LLVirtualTrackball class definition
  4. *
  5. * $LicenseInfo:firstyear=2018&license=viewergpl$
  6. *
  7. * Copyright (c) 2018, 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 "linden_common.h"
  33. #include "llvirtualtrackball.h"
  34. #include "llbutton.h"
  35. #include "llpanel.h"
  36. #include "llrender.h"
  37. #include "lltextbox.h"
  38. static const std::string LL_SUN_MOON_TRACKBALL_TAG = "sun_moon_trackball";
  39. static LLRegisterWidget<LLVirtualTrackball> r33(LL_SUN_MOON_TRACKBALL_TAG);
  40. // UI elements constants
  41. constexpr S32 TRACKBALL_WIDTH = 150;
  42. constexpr S32 TRACKBALL_HEIGHT = 150;
  43. constexpr S32 TRACKBALL_BTN_SIZE = 32; // Width & height
  44. constexpr S32 TRACKBALL_AXIS_OFFSET = 16; // Offset from left/top sides
  45. const std::string TRACKBALL_LABEL_N = "N";
  46. const std::string TRACKBALL_LABEL_S = "S";
  47. const std::string TRACKBALL_LABEL_W = "E";
  48. const std::string TRACKBALL_LABEL_E = "W";
  49. LLVirtualTrackball::LLVirtualTrackball(const std::string& name,
  50. const LLRect& ui_rect,
  51. void (*commit_cb)(LLUICtrl*, void*),
  52. void* userdata)
  53. : LLUICtrl(name, ui_rect, true, commit_cb, userdata,
  54. FOLLOWS_TOP | FOLLOWS_LEFT),
  55. mPrevX(0),
  56. mPrevY(0),
  57. mIncrementMouse(DEG_TO_RAD * 0.5f),
  58. mIncrementBtn(DEG_TO_RAD * 3.f),
  59. mThumbMode(SUN),
  60. mImgMoonBack(LLUI::getUIImage("track_control_moon_back.png")),
  61. mImgMoonFront(LLUI::getUIImage("track_control_moon_front.png")),
  62. mImgSunBack(LLUI::getUIImage("track_control_sun_back.png")),
  63. mImgSunFront(LLUI::getUIImage("track_control_sun_front.png")),
  64. mImgSphere(LLUI::getUIImage("track_control_sphere.png"))
  65. {
  66. LLRect border_rect = getLocalRect();
  67. mBorder = new LLViewBorder("border", border_rect);
  68. addChild(mBorder);
  69. S32 center_x = border_rect.getCenterX();
  70. S32 center_y = border_rect.getCenterY();
  71. // Offset from the axis for left/top sides
  72. S32 axis_offset_rb = TRACKBALL_BTN_SIZE - TRACKBALL_AXIS_OFFSET;
  73. LLRect rect = LLRect(center_x - TRACKBALL_AXIS_OFFSET, border_rect.mTop,
  74. center_x + axis_offset_rb,
  75. border_rect.mTop - TRACKBALL_BTN_SIZE);
  76. mBtnRotateTop = new LLButton("button_rotate_top", rect,
  77. "track_control_rotate_top.png",
  78. "track_control_rotate_top_active.png",
  79. "", onRotateTopClick, this);
  80. mBtnRotateTop->setHeldDownCallback(onRotateTopClickNoSound);
  81. addChild(mBtnRotateTop);
  82. rect.translate(0, -TRACKBALL_BTN_SIZE / 2);
  83. mLabelN = new LLTextBox("labelN", rect, TRACKBALL_LABEL_N);
  84. addChild(mLabelN);
  85. rect = LLRect(border_rect.mRight - TRACKBALL_BTN_SIZE,
  86. center_y + TRACKBALL_AXIS_OFFSET,
  87. border_rect.mRight, center_y - axis_offset_rb);
  88. mBtnRotateRight = new LLButton("button_rotate_right", rect,
  89. "track_control_rotate_right_side.png",
  90. "track_control_rotate_right_side_active.png",
  91. "", onRotateRightClick, this);
  92. mBtnRotateRight->setHeldDownCallback(onRotateRightClickNoSound);
  93. addChild(mBtnRotateRight);
  94. mLabelW = new LLTextBox("labelW", rect, TRACKBALL_LABEL_W);
  95. addChild(mLabelW);
  96. rect = LLRect(center_x - TRACKBALL_AXIS_OFFSET,
  97. border_rect.mBottom + TRACKBALL_BTN_SIZE,
  98. center_x + axis_offset_rb, border_rect.mBottom);
  99. mBtnRotateBottom = new LLButton("button_rotate_bottom", rect,
  100. "track_control_rotate_bottom.png",
  101. "track_control_rotate_bottom_active.png",
  102. "", onRotateBottomClick, this);
  103. mBtnRotateBottom->setHeldDownCallback(onRotateBottomClickNoSound);
  104. addChild(mBtnRotateBottom);
  105. mLabelS = new LLTextBox("labelS", rect, TRACKBALL_LABEL_S);
  106. addChild(mLabelS);
  107. rect = LLRect(border_rect.mLeft, center_y + TRACKBALL_AXIS_OFFSET,
  108. border_rect.mLeft + TRACKBALL_BTN_SIZE,
  109. center_y - axis_offset_rb);
  110. mBtnRotateLeft = new LLButton("button_rotate_left", rect,
  111. "track_control_rotate_left_side.png",
  112. "track_control_rotate_left_side_active.png",
  113. "", onRotateLeftClick, this);
  114. mBtnRotateLeft->setHeldDownCallback(onRotateLeftClickNoSound);
  115. addChild(mBtnRotateLeft);
  116. rect.translate(TRACKBALL_BTN_SIZE / 2, 0);
  117. mLabelE = new LLTextBox("labelE", rect, TRACKBALL_LABEL_E);
  118. addChild(mLabelE);
  119. S32 half_width = mImgSphere->getWidth() / 2;
  120. S32 half_height = mImgSphere->getHeight() / 2;
  121. rect = LLRect(center_x - half_width, center_y + half_height,
  122. center_x + half_width, center_y - half_height);
  123. mTouchArea = new LLPanel("touch area", rect);
  124. addChild(mTouchArea);
  125. }
  126. bool LLVirtualTrackball::pointInTouchCircle(S32 x, S32 y) const
  127. {
  128. S32 x1 = x - mTouchArea->getRect().getCenterX();
  129. S32 y1 = y - mTouchArea->getRect().getCenterY();
  130. S32 radius = mTouchArea->getRect().getWidth() / 2;
  131. return x1 * x1 + y1 * y1 <= radius * radius;
  132. }
  133. void LLVirtualTrackball::drawThumb(S32 x, S32 y, EThumbMode mode,
  134. bool upper_hemi)
  135. {
  136. LLUIImage* thumb;
  137. if (mode == EThumbMode::SUN)
  138. {
  139. thumb = upper_hemi ? mImgSunFront : mImgSunBack;
  140. }
  141. else
  142. {
  143. thumb = upper_hemi ? mImgMoonFront : mImgMoonBack;
  144. }
  145. S32 half_width = thumb->getWidth() / 2;
  146. S32 half_height = thumb->getHeight() / 2;
  147. thumb->draw(LLRect(x - half_width, y + half_height, x + half_width,
  148. y - half_height));
  149. }
  150. //virtual
  151. void LLVirtualTrackball::draw()
  152. {
  153. const LLRect& rect = mTouchArea->getRect();
  154. S32 half_width = rect.getWidth() / 2;
  155. S32 half_height = rect.getHeight() / 2;
  156. LLVector3 draw_point = LLVector3::x_axis * mValue;
  157. draw_point.mV[VX] = (draw_point.mV[VX] + 1.f) * half_width + rect.mLeft;
  158. draw_point.mV[VY] = (draw_point.mV[VY] + 1.f) * half_height + rect.mBottom;
  159. bool upper = draw_point.mV[VZ] >= 0.f;
  160. mImgSphere->draw(rect, upper ? UI_VERTEX_COLOR : UI_VERTEX_COLOR % 0.5f);
  161. drawThumb(draw_point.mV[VX], draw_point.mV[VY], mThumbMode, upper);
  162. if (sDebugRects)
  163. {
  164. gGL.color4fv(LLColor4::red.mV);
  165. gl_circle_2d(rect.getCenterX(), rect.getCenterY(),
  166. mImgSphere->getWidth() / 2, 60, false);
  167. gl_circle_2d(draw_point.mV[VX], draw_point.mV[VY],
  168. mImgSunFront->getWidth() / 2, 12, false);
  169. }
  170. bool enabled = isInEnabledChain();
  171. mLabelN->setVisible(enabled);
  172. mLabelS->setVisible(enabled);
  173. mLabelW->setVisible(enabled);
  174. mLabelE->setVisible(enabled);
  175. mBtnRotateTop->setVisible(enabled);
  176. mBtnRotateBottom->setVisible(enabled);
  177. mBtnRotateLeft->setVisible(enabled);
  178. mBtnRotateRight->setVisible(enabled);
  179. LLView::draw();
  180. }
  181. //virtual
  182. bool LLVirtualTrackball::handleKeyHere(KEY key, MASK mask)
  183. {
  184. switch (key)
  185. {
  186. case KEY_DOWN:
  187. onRotateTopClick(this);
  188. break;
  189. case KEY_LEFT:
  190. onRotateRightClick(this);
  191. break;
  192. case KEY_UP:
  193. onRotateBottomClick(this);
  194. break;
  195. case KEY_RIGHT:
  196. onRotateLeftClick(this);
  197. break;
  198. default:
  199. return false;
  200. }
  201. return true;
  202. }
  203. //virtual
  204. bool LLVirtualTrackball::handleHover(S32 x, S32 y, MASK mask)
  205. {
  206. if (hasMouseCapture())
  207. {
  208. if (mDragMode == DRAG_SCROLL)
  209. {
  210. // Trackball (move to roll) mode
  211. LLQuaternion delta;
  212. F32 rot_x = x - mPrevX;
  213. F32 rot_y = y - mPrevY;
  214. F32 abs_rot_x = fabsf(rot_x);
  215. if (abs_rot_x > 1.f)
  216. {
  217. // Changing X: rotate around Y axis
  218. delta.setAngleAxis(mIncrementMouse * abs_rot_x, 0.f,
  219. rot_x < 0.f ? -1.f : 1.f, 0.f);
  220. mValue *= delta;
  221. }
  222. F32 abs_rot_y = fabsf(rot_y);
  223. if (abs_rot_y > 1.f)
  224. {
  225. // Changing Y: rotate around X axis
  226. delta.setAngleAxis(mIncrementMouse * abs_rot_y,
  227. rot_y < 0.f ? 1.f : -1.f, 0.f, 0.f);
  228. mValue *= delta;
  229. }
  230. }
  231. else
  232. {
  233. // Set on click mode
  234. if (!pointInTouchCircle(x, y))
  235. {
  236. // Do not drag outside the circle
  237. return true;
  238. }
  239. const LLRect& rect = mTouchArea->getRect();
  240. F32 radius = rect.getWidth() / 2;
  241. F32 xx = x - rect.getCenterX();
  242. F32 yy = y - rect.getCenterY();
  243. F32 dist = sqrtf(xx * xx + yy * yy);
  244. F32 altitude = llclamp(acosf(dist / radius), 0.f, F_PI_BY_TWO);
  245. F32 azimuth = llclamp(acosf(xx / dist), 0.f, F_PI);
  246. if (yy < 0.f)
  247. {
  248. azimuth = F_TWO_PI - azimuth;
  249. }
  250. LLVector3 draw_point = LLVector3::x_axis * mValue;
  251. if (draw_point.mV[VZ] >= 0.f)
  252. {
  253. if (is_approx_zero(altitude)) // do not change the hemisphere
  254. {
  255. altitude = -F_APPROXIMATELY_ZERO;
  256. }
  257. else
  258. {
  259. altitude = -altitude;
  260. }
  261. }
  262. mValue.setAngleAxis(altitude, 0.f, 1.f, 0.f);
  263. LLQuaternion az_quat;
  264. az_quat.setAngleAxis(azimuth, 0.f, 0.f, 1.f);
  265. mValue *= az_quat;
  266. }
  267. mValue.normalize();
  268. mPrevX = x;
  269. mPrevY = y;
  270. onCommit();
  271. }
  272. return true;
  273. }
  274. //virtual
  275. bool LLVirtualTrackball::handleMouseUp(S32 x, S32 y, MASK mask)
  276. {
  277. if (hasMouseCapture())
  278. {
  279. mPrevX = mPrevY = 0;
  280. gFocusMgr.setMouseCapture(NULL);
  281. make_ui_sound("UISndClickRelease");
  282. }
  283. return LLView::handleMouseUp(x, y, mask);
  284. }
  285. //virtual
  286. bool LLVirtualTrackball::handleMouseDown(S32 x, S32 y, MASK mask)
  287. {
  288. if (pointInTouchCircle(x, y))
  289. {
  290. mPrevX = x;
  291. mPrevY = y;
  292. gFocusMgr.setMouseCapture(this);
  293. mDragMode = mask == MASK_CONTROL ? DRAG_SCROLL : DRAG_SET;
  294. make_ui_sound("UISndClick");
  295. }
  296. return LLView::handleMouseDown(x, y, mask);
  297. }
  298. //virtual
  299. LLSD LLVirtualTrackball::getValue() const
  300. {
  301. return mValue.getValue();
  302. }
  303. void LLVirtualTrackball::setValue(F32 x, F32 y, F32 z, F32 w)
  304. {
  305. mValue.set(x, y, z, w);
  306. }
  307. //virtual
  308. void LLVirtualTrackball::setValue(const LLSD& value)
  309. {
  310. if (value.isArray() && value.size() == 4)
  311. {
  312. mValue.setValue(value);
  313. }
  314. }
  315. void LLVirtualTrackball::setValueAndCommit(const LLQuaternion& value)
  316. {
  317. mValue = value;
  318. onCommit();
  319. }
  320. void LLVirtualTrackball::getAzimuthAndElevationDeg(F32& azim, F32& elev)
  321. {
  322. mValue.getAzimuthAndElevation(azim, elev);
  323. azim *= RAD_TO_DEG;
  324. elev *= RAD_TO_DEG;
  325. }
  326. //static
  327. void LLVirtualTrackball::onRotateTopClick(void* userdata)
  328. {
  329. LLVirtualTrackball* self = (LLVirtualTrackball*)userdata;
  330. if (self && self->getEnabled())
  331. {
  332. LLQuaternion delta;
  333. delta.setAngleAxis(self->mIncrementBtn, 1.f, 0.f, 0.f);
  334. self->setValueAndCommit(self->mValue * delta);
  335. make_ui_sound("UISndClick");
  336. }
  337. }
  338. //static
  339. void LLVirtualTrackball::onRotateBottomClick(void* userdata)
  340. {
  341. LLVirtualTrackball* self = (LLVirtualTrackball*)userdata;
  342. if (self && self->getEnabled())
  343. {
  344. LLQuaternion delta;
  345. delta.setAngleAxis(self->mIncrementBtn, -1.f, 0.f, 0.f);
  346. self->setValueAndCommit(self->mValue * delta);
  347. make_ui_sound("UISndClick");
  348. }
  349. }
  350. //static
  351. void LLVirtualTrackball::onRotateLeftClick(void* userdata)
  352. {
  353. LLVirtualTrackball* self = (LLVirtualTrackball*)userdata;
  354. if (self && self->getEnabled())
  355. {
  356. LLQuaternion delta;
  357. delta.setAngleAxis(self->mIncrementBtn, 0.f, 1.f, 0.f);
  358. self->setValueAndCommit(self->mValue * delta);
  359. make_ui_sound("UISndClick");
  360. }
  361. }
  362. //static
  363. void LLVirtualTrackball::onRotateRightClick(void* userdata)
  364. {
  365. LLVirtualTrackball* self = (LLVirtualTrackball*)userdata;
  366. if (self && self->getEnabled())
  367. {
  368. LLQuaternion delta;
  369. delta.setAngleAxis(self->mIncrementBtn, 0.f, -1.f, 0.f);
  370. self->setValueAndCommit(self->mValue * delta);
  371. make_ui_sound("UISndClick");
  372. }
  373. }
  374. //static
  375. void LLVirtualTrackball::onRotateTopClickNoSound(void* userdata)
  376. {
  377. LLVirtualTrackball* self = (LLVirtualTrackball*)userdata;
  378. if (self && self->getEnabled())
  379. {
  380. LLQuaternion delta;
  381. delta.setAngleAxis(self->mIncrementBtn, 1.f, 0.f, 0.f);
  382. self->setValueAndCommit(self->mValue * delta);
  383. }
  384. }
  385. //static
  386. void LLVirtualTrackball::onRotateBottomClickNoSound(void* userdata)
  387. {
  388. LLVirtualTrackball* self = (LLVirtualTrackball*)userdata;
  389. if (self && self->getEnabled())
  390. {
  391. LLQuaternion delta;
  392. delta.setAngleAxis(self->mIncrementBtn, -1.f, 0.f, 0.f);
  393. self->setValueAndCommit(self->mValue * delta);
  394. }
  395. }
  396. //static
  397. void LLVirtualTrackball::onRotateLeftClickNoSound(void* userdata)
  398. {
  399. LLVirtualTrackball* self = (LLVirtualTrackball*)userdata;
  400. if (self && self->getEnabled())
  401. {
  402. LLQuaternion delta;
  403. delta.setAngleAxis(self->mIncrementBtn, 0.f, 1.f, 0.f);
  404. self->setValueAndCommit(self->mValue * delta);
  405. }
  406. }
  407. //static
  408. void LLVirtualTrackball::onRotateRightClickNoSound(void* userdata)
  409. {
  410. LLVirtualTrackball* self = (LLVirtualTrackball*)userdata;
  411. if (self && self->getEnabled())
  412. {
  413. LLQuaternion delta;
  414. delta.setAngleAxis(self->mIncrementBtn, 0.f, -1.f, 0.f);
  415. self->setValueAndCommit(self->mValue * delta);
  416. }
  417. }
  418. //virtual
  419. const std::string& LLVirtualTrackball::getTag() const
  420. {
  421. return LL_SUN_MOON_TRACKBALL_TAG;
  422. }
  423. //virtual
  424. LLXMLNodePtr LLVirtualTrackball::getXML(bool save_children) const
  425. {
  426. LLXMLNodePtr node = LLUICtrl::getXML();
  427. node->setName(LL_SUN_MOON_TRACKBALL_TAG);
  428. node->createChild("increment_angle_mouse",
  429. true)->setIntValue(mIncrementMouse);
  430. node->createChild("increment_angle_btn", true)->setIntValue(mIncrementBtn);
  431. node->createChild("thumb_mode",
  432. true)->setStringValue(mThumbMode == MOON ? "moon"
  433. : "sun");
  434. return node;
  435. }
  436. //static
  437. LLView* LLVirtualTrackball::fromXML(LLXMLNodePtr node, LLView* parent,
  438. LLUICtrlFactory* factory)
  439. {
  440. std::string name = LL_SUN_MOON_TRACKBALL_TAG;
  441. node->getAttributeString("name", name);
  442. LLRect rect;
  443. createRect(node, rect, parent,
  444. LLRect(0, TRACKBALL_HEIGHT, TRACKBALL_WIDTH, 0));
  445. LLUICtrlCallback callback = NULL;
  446. LLVirtualTrackball* trackball = new LLVirtualTrackball(name, rect,
  447. callback, NULL);
  448. F32 increment_angle_mouse = 0.5f;
  449. node->getAttributeF32("increment_angle_mouse", increment_angle_mouse);
  450. trackball->mIncrementMouse = DEG_TO_RAD * increment_angle_mouse;
  451. F32 increment_angle_btn = 3.f;
  452. node->getAttributeF32("increment_angle_btn", increment_angle_btn);
  453. trackball->mIncrementBtn = DEG_TO_RAD * increment_angle_btn;
  454. std::string thumb_mode;
  455. node->getAttributeString("thumb_mode", thumb_mode);
  456. trackball->mThumbMode = thumb_mode == "moon" ? MOON : SUN;
  457. trackball->initFromXML(node, parent);
  458. return trackball;
  459. }