guiScrollCtrl.cc 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "console/consoleTypes.h"
  23. #include "console/console.h"
  24. #include "graphics/gBitmap.h"
  25. #include "graphics/TextureManager.h"
  26. #include "io/resource/resourceManager.h"
  27. #include "platform/event.h"
  28. #include "graphics/dgl.h"
  29. #include "gui/guiArrayCtrl.h"
  30. #include "gui/containers/guiScrollCtrl.h"
  31. #include "gui/guiDefaultControlRender.h"
  32. #include "guiScrollCtrl_ScriptBinding.h"
  33. static EnumTable::Enums scrollBarEnums[] =
  34. {
  35. { GuiScrollCtrl::ScrollBarAlwaysOn, "alwaysOn" },
  36. { GuiScrollCtrl::ScrollBarAlwaysOff, "alwaysOff" },
  37. { GuiScrollCtrl::ScrollBarDynamic, "dynamic" },
  38. };
  39. static EnumTable gScrollBarTable(3, &scrollBarEnums[0]);
  40. IMPLEMENT_CONOBJECT(GuiScrollCtrl);
  41. // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //
  42. GuiScrollCtrl::GuiScrollCtrl()
  43. {
  44. mBounds.extent.set(200,200);
  45. mScrollBarThickness = 16;
  46. mScrollBarDragTolerance = 130;
  47. mDepressed = false;
  48. curHitRegion = Content;
  49. mActive = true;
  50. mShowArrowButtons = true;
  51. mBaseThumbSize = (mScrollBarThickness * 2);
  52. mUseConstantHeightThumb = false;
  53. mIsContainer = true;
  54. mForceVScrollBar = ScrollBarAlwaysOn;
  55. mForceHScrollBar = ScrollBarAlwaysOn;
  56. mThumbProfile = NULL;
  57. mTrackProfile = NULL;
  58. mArrowProfile = NULL;
  59. mScrollOffset.set(0, 0);
  60. mContentExt.set(200,200);
  61. setField("thumbProfile", "GuiScrollThumbProfile");
  62. setField("arrowProfile", "GuiScrollArrowProfile");
  63. setField("trackProfile", "GuiScrollTrackProfile");
  64. setField("profile", "GuiScrollProfile");
  65. mEventBubbled = false;
  66. }
  67. void GuiScrollCtrl::initPersistFields()
  68. {
  69. Parent::initPersistFields();
  70. addField("hScrollBar", TypeEnum, Offset(mForceHScrollBar, GuiScrollCtrl), 1, &gScrollBarTable);
  71. addField("vScrollBar", TypeEnum, Offset(mForceVScrollBar, GuiScrollCtrl), 1, &gScrollBarTable);
  72. addField("constantThumbHeight", TypeBool, Offset(mUseConstantHeightThumb, GuiScrollCtrl));
  73. addField("scrollBarThickness", TypeS32, Offset(mScrollBarThickness, GuiScrollCtrl));
  74. addField("showArrowButtons", TypeBool, Offset(mShowArrowButtons, GuiScrollCtrl));
  75. addField("thumbProfile", TypeGuiProfile, Offset(mThumbProfile, GuiScrollCtrl));
  76. addField("trackProfile", TypeGuiProfile, Offset(mTrackProfile, GuiScrollCtrl));
  77. addField("arrowProfile", TypeGuiProfile, Offset(mArrowProfile, GuiScrollCtrl));
  78. }
  79. void GuiScrollCtrl::resize(const Point2I &newPos, const Point2I &newExt)
  80. {
  81. Parent::resize(newPos, newExt);
  82. computeSizes();
  83. }
  84. void GuiScrollCtrl::childResized(GuiControl *child)
  85. {
  86. Parent::childResized(child);
  87. computeSizes();
  88. }
  89. void GuiScrollCtrl::addObject(SimObject* object)
  90. {
  91. Parent::addObject(object);
  92. computeSizes();
  93. }
  94. bool GuiScrollCtrl::onWake()
  95. {
  96. if (! Parent::onWake())
  97. return false;
  98. if (mThumbProfile != NULL)
  99. mThumbProfile->incRefCount();
  100. if (mTrackProfile != NULL)
  101. mTrackProfile->incRefCount();
  102. if (mArrowProfile != NULL)
  103. mArrowProfile->incRefCount();
  104. return true;
  105. }
  106. void GuiScrollCtrl::onSleep()
  107. {
  108. Parent::onSleep();
  109. if (mThumbProfile != NULL)
  110. mThumbProfile->decRefCount();
  111. if (mTrackProfile != NULL)
  112. mTrackProfile->decRefCount();
  113. if (mArrowProfile != NULL)
  114. mArrowProfile->decRefCount();
  115. }
  116. void GuiScrollCtrl::setControlThumbProfile(GuiControlProfile* prof)
  117. {
  118. AssertFatal(prof, "GuiScrollCtrl::setControlThumbProfile: invalid thumb profile");
  119. if (prof == mThumbProfile)
  120. return;
  121. if (mAwake)
  122. mThumbProfile->decRefCount();
  123. mThumbProfile = prof;
  124. if (mAwake)
  125. mThumbProfile->incRefCount();
  126. }
  127. void GuiScrollCtrl::setControlTrackProfile(GuiControlProfile* prof)
  128. {
  129. AssertFatal(prof, "GuiScrollCtrl::setControlTrackProfile: invalid track profile");
  130. if (prof == mTrackProfile)
  131. return;
  132. if (mAwake)
  133. mTrackProfile->decRefCount();
  134. mTrackProfile = prof;
  135. if (mAwake)
  136. mTrackProfile->incRefCount();
  137. }
  138. void GuiScrollCtrl::setControlArrowProfile(GuiControlProfile* prof)
  139. {
  140. AssertFatal(prof, "GuiScrollCtrl::setControlArrowProfile: invalid Arrow profile");
  141. if (prof == mArrowProfile)
  142. return;
  143. if (mAwake)
  144. mArrowProfile->decRefCount();
  145. mArrowProfile = prof;
  146. if (mAwake)
  147. mArrowProfile->incRefCount();
  148. }
  149. GuiControl* GuiScrollCtrl::findHitControl(const Point2I& pt, S32 initialLayer)
  150. {
  151. Point2I localPt = localToGlobalCoord(pt);
  152. if (mChildArea.pointInRect(localPt))
  153. {
  154. iterator i = end(); // find in z order (last to first)
  155. while (i != begin())
  156. {
  157. i--;
  158. GuiControl* ctrl = static_cast<GuiControl*>(*i);
  159. if (initialLayer >= 0 && ctrl->mLayer > initialLayer)
  160. {
  161. continue;
  162. }
  163. else if (ctrl->mVisible && ctrl->pointInControl(pt - ctrl->mRenderInsetLT) && ctrl->mUseInput)
  164. {
  165. Point2I ptemp = pt - (ctrl->mBounds.point + ctrl->mRenderInsetLT);
  166. GuiControl* hitCtrl = ctrl->findHitControl(ptemp);
  167. if (hitCtrl->mUseInput)
  168. return hitCtrl;
  169. }
  170. }
  171. }
  172. return this;
  173. }
  174. GuiScrollCtrl::Region GuiScrollCtrl::findHitRegion(const Point2I& pt)
  175. {
  176. if (mVBarEnabled && mHasVScrollBar)
  177. {
  178. if (mShowArrowButtons && mUpArrowRect.pointInRect(pt))
  179. return UpArrow;
  180. else if (mShowArrowButtons && mDownArrowRect.pointInRect(pt))
  181. return DownArrow;
  182. else if (mVTrackRect.pointInRect(pt))
  183. {
  184. S32 y = pt.y - mVTrackRect.point.y;
  185. if (y < mVThumbPos)
  186. return UpPage;
  187. else if (y < mVThumbPos + mVThumbSize)
  188. return VertThumb;
  189. else
  190. return DownPage;
  191. }
  192. }
  193. if (mHBarEnabled && mHasHScrollBar)
  194. {
  195. if (mShowArrowButtons && mLeftArrowRect.pointInRect(pt))
  196. return LeftArrow;
  197. else if (mShowArrowButtons && mRightArrowRect.pointInRect(pt))
  198. return RightArrow;
  199. else if (mHTrackRect.pointInRect(pt))
  200. {
  201. S32 x = pt.x - mHTrackRect.point.x;
  202. if (x < mHThumbPos)
  203. return LeftPage;
  204. else if (x < mHThumbPos + mHThumbSize)
  205. return HorizThumb;
  206. else
  207. return RightPage;
  208. }
  209. }
  210. return Content;
  211. }
  212. #pragma region CalculationFunctions
  213. void GuiScrollCtrl::computeSizes()
  214. {
  215. calcContentExtents();
  216. mHBarEnabled = false;
  217. mVBarEnabled = false;
  218. mHasVScrollBar = (mForceVScrollBar == ScrollBarAlwaysOn);
  219. mHasHScrollBar = (mForceHScrollBar == ScrollBarAlwaysOn);
  220. setUpdate();
  221. if (calcChildExtents())
  222. {
  223. if (mChildExt.x > mContentExt.x && (mForceHScrollBar == ScrollBarDynamic))
  224. {
  225. mHasHScrollBar = true;
  226. }
  227. if (mChildExt.y > mContentExt.y && (mForceVScrollBar == ScrollBarDynamic))
  228. {
  229. mHasVScrollBar = true;
  230. // If Extent X Changed, check Horiz Scrollbar.
  231. if (mChildExt.x > mContentExt.x && !mHasHScrollBar && (mForceHScrollBar == ScrollBarDynamic))
  232. {
  233. mHasHScrollBar = true;
  234. }
  235. }
  236. if (mHasVScrollBar)
  237. mContentExt.x -= mScrollBarThickness;
  238. if (mHasHScrollBar)
  239. mContentExt.y -= mScrollBarThickness;
  240. // enable needed scroll bars
  241. if (mChildExt.x > mContentExt.x)
  242. mHBarEnabled = true;
  243. if (mChildExt.y > mContentExt.y)
  244. mVBarEnabled = true;
  245. //Are we now over-scrolled?
  246. calcScrollOffset();
  247. }
  248. // build all the rectangles and such...
  249. Point2I zero = mBounds.point.Zero;
  250. RectI ctrlRect = applyMargins(zero, mBounds.extent, NormalState, mProfile);
  251. RectI fillRect = applyBorders(ctrlRect.point, ctrlRect.extent, NormalState, mProfile);
  252. calcScrollRects(fillRect);
  253. calcThumbs();
  254. }
  255. void GuiScrollCtrl::calcContentExtents()
  256. {
  257. RectI ctrlRect = applyMargins(mBounds.point, mBounds.extent, NormalState, mProfile);
  258. RectI fillRect = applyBorders(ctrlRect.point, ctrlRect.extent, NormalState, mProfile);
  259. RectI contentRect = applyPadding(fillRect.point, fillRect.extent, NormalState, mProfile);
  260. mContentExt = contentRect.extent;
  261. }
  262. bool GuiScrollCtrl::calcChildExtents()
  263. {
  264. if (!size())
  265. return false;
  266. mChildExt = Point2I(0,0);
  267. for (iterator itr = begin(); itr != end(); ++itr)
  268. {
  269. GuiControl* child = dynamic_cast<GuiControl*>(*itr);
  270. mChildExt.setMax(child->getExtent() + child->getPosition());
  271. }
  272. return true;
  273. }
  274. void GuiScrollCtrl::calcScrollOffset()
  275. {
  276. if ((mScrollOffset.x + mContentExt.x) > mChildExt.x)
  277. mScrollOffset.x = getMax(mChildExt.x - mContentExt.x, 0);
  278. if ((mScrollOffset.y + mContentExt.y) > mChildExt.y)
  279. mScrollOffset.y = getMax(mChildExt.y - mContentExt.y, 0);
  280. }
  281. void GuiScrollCtrl::calcScrollRects(RectI &fillRect)
  282. {
  283. if (mHasVScrollBar)
  284. {
  285. RectI vScrollRect = RectI(fillRect.point.x + fillRect.extent.x - mScrollBarThickness, fillRect.point.y, mScrollBarThickness, fillRect.extent.y);
  286. if (mHasHScrollBar)
  287. {
  288. vScrollRect.extent.y -= mScrollBarThickness;
  289. }
  290. mVTrackRect = RectI(vScrollRect);
  291. if (mShowArrowButtons)
  292. {
  293. mUpArrowRect = RectI(vScrollRect.point.x, vScrollRect.point.y, vScrollRect.extent.x, mScrollBarThickness);
  294. mDownArrowRect = RectI(vScrollRect.point.x, vScrollRect.point.y + vScrollRect.extent.y - mScrollBarThickness, vScrollRect.extent.x, mScrollBarThickness);
  295. mVTrackRect = RectI(vScrollRect.point.x, vScrollRect.point.y + mScrollBarThickness, vScrollRect.extent.x, vScrollRect.extent.y - (2 * mScrollBarThickness));
  296. }
  297. }
  298. if (mHasHScrollBar)
  299. {
  300. RectI hScrollRect = RectI(fillRect.point.x, fillRect.point.y + fillRect.extent.y - mScrollBarThickness, fillRect.extent.x, mScrollBarThickness);
  301. if (mHasVScrollBar)
  302. {
  303. hScrollRect.extent.x -= mScrollBarThickness;
  304. }
  305. mHTrackRect = RectI(hScrollRect);
  306. if (mShowArrowButtons)
  307. {
  308. mLeftArrowRect = RectI(hScrollRect.point.x, hScrollRect.point.y, mScrollBarThickness, hScrollRect.extent.y);
  309. mRightArrowRect = RectI(hScrollRect.point.x + hScrollRect.extent.x - mScrollBarThickness, hScrollRect.point.y, mScrollBarThickness, hScrollRect.extent.y);
  310. mHTrackRect = RectI(hScrollRect.point.x + mScrollBarThickness, hScrollRect.point.y, hScrollRect.extent.x - (2 * mScrollBarThickness), hScrollRect.extent.y);
  311. }
  312. }
  313. }
  314. void GuiScrollCtrl::calcThumbs()
  315. {
  316. if (mHBarEnabled)
  317. {
  318. S32 totalArea = mChildExt.x - mContentExt.x;
  319. if (totalArea <= 0)
  320. {
  321. mHBarEnabled = false;
  322. mHThumbSize = mBaseThumbSize;
  323. mHThumbPos = 0;
  324. }
  325. else
  326. {
  327. U32 trackSize = mHTrackRect.len_x();
  328. if (mUseConstantHeightThumb)
  329. mHThumbSize = mBaseThumbSize;
  330. else
  331. mHThumbSize = getMax(mBaseThumbSize, S32((mContentExt.x * trackSize) / mChildExt.x));
  332. F32 fraction = (F32)mScrollOffset.x / (F32)totalArea;
  333. mHThumbPos = roundf((trackSize - mHThumbSize) * fraction);
  334. }
  335. }
  336. if (mVBarEnabled)
  337. {
  338. S32 totalArea = mChildExt.y - mContentExt.y;
  339. if (totalArea <= 0)
  340. {
  341. mVBarEnabled = false;
  342. mVThumbSize = mBaseThumbSize;
  343. mVThumbPos = 0;
  344. }
  345. else
  346. {
  347. U32 trackSize = mVTrackRect.len_y();
  348. if (mUseConstantHeightThumb)
  349. mVThumbSize = mBaseThumbSize;
  350. else
  351. mVThumbSize = getMax(mBaseThumbSize, S32((mContentExt.y * trackSize) / mChildExt.y));
  352. F32 fraction = (F32)mScrollOffset.y / (F32)totalArea;
  353. mVThumbPos = roundf((trackSize - mVThumbSize) * fraction);
  354. }
  355. }
  356. }
  357. #pragma endregion
  358. #pragma region ScrollingFunctions
  359. void GuiScrollCtrl::scrollDelta(S32 deltaX, S32 deltaY)
  360. {
  361. mScrollOffset.x += deltaX;
  362. mScrollOffset.y += deltaY;
  363. mScrollOffset.setMin(mChildExt - mContentExt);
  364. mScrollOffset.setMax(mScrollOffset.Zero);
  365. calcThumbs();
  366. }
  367. void GuiScrollCtrl::scrollTo(S32 x, S32 y)
  368. {
  369. if(!size())
  370. return;
  371. setUpdate();
  372. if (x > mChildExt.x - mContentExt.x)
  373. x = mChildExt.x - mContentExt.x;
  374. if (x < 0)
  375. x = 0;
  376. if (y > mChildExt.y - mContentExt.y)
  377. y = mChildExt.y - mContentExt.y;
  378. if (y < 0)
  379. y = 0;
  380. mScrollOffset.set(x, y);
  381. calcThumbs();
  382. }
  383. void GuiScrollCtrl::scrollByRegion(Region reg)
  384. {
  385. setUpdate();
  386. if (!size())
  387. return;
  388. GuiControl* content = (GuiControl*)front();
  389. U32 rowHeight, columnWidth;
  390. U32 pageHeight, pageWidth;
  391. content->getScrollLineSizes(&rowHeight, &columnWidth);
  392. if (rowHeight >= (U32)mContentExt.y)
  393. pageHeight = 1;
  394. else
  395. pageHeight = mContentExt.y - rowHeight;
  396. if (columnWidth >= (U32)mContentExt.x)
  397. pageWidth = 1;
  398. else
  399. pageWidth = mContentExt.x - columnWidth;
  400. if (mVBarEnabled)
  401. {
  402. switch (reg)
  403. {
  404. case UpPage:
  405. scrollDelta(0, -(S32)pageHeight);
  406. break;
  407. case DownPage:
  408. scrollDelta(0, pageHeight);
  409. break;
  410. case UpArrow:
  411. scrollDelta(0, -(S32)rowHeight);
  412. break;
  413. case DownArrow:
  414. scrollDelta(0, rowHeight);
  415. break;
  416. case LeftArrow:
  417. case RightArrow:
  418. case LeftPage:
  419. case RightPage:
  420. case VertThumb:
  421. case HorizThumb:
  422. case Content:
  423. //Con::errorf("Unhandled case in GuiScrollCtrl::scrollByRegion");
  424. break;
  425. }
  426. }
  427. if (mHBarEnabled)
  428. {
  429. switch (reg)
  430. {
  431. case LeftPage:
  432. scrollDelta(-(S32)pageWidth, 0);
  433. break;
  434. case RightPage:
  435. scrollDelta(pageWidth, 0);
  436. break;
  437. case LeftArrow:
  438. scrollDelta(-(S32)columnWidth, 0);
  439. break;
  440. case RightArrow:
  441. scrollDelta(columnWidth, 0);
  442. break;
  443. case UpArrow:
  444. case DownArrow:
  445. case UpPage:
  446. case DownPage:
  447. case VertThumb:
  448. case HorizThumb:
  449. case Content:
  450. //Con::errorf("Unhandled case in GuiScrollCtrl::scrollByRegion");
  451. break;
  452. }
  453. }
  454. }
  455. void GuiScrollCtrl::scrollRectVisible(RectI rect)
  456. {
  457. // rect is passed in virtual client space
  458. if (rect.extent.x > mContentExt.x)
  459. rect.extent.x = mContentExt.x;
  460. if (rect.extent.y > mContentExt.y)
  461. rect.extent.y = mContentExt.y;
  462. // Determine the points bounding the requested rectangle
  463. Point2I rectUpperLeft = rect.point;
  464. Point2I rectLowerRight = rect.point + rect.extent;
  465. // Determine the points bounding the actual visible area...
  466. Point2I visUpperLeft = mScrollOffset;
  467. Point2I visLowerRight = mContentExt + mScrollOffset;
  468. Point2I delta(0, 0);
  469. // We basically try to make sure that first the top left of the given
  470. // rect is visible, and if it is, then that the bottom right is visible.
  471. // Make sure the rectangle is visible along the X axis...
  472. if (rectUpperLeft.x < visUpperLeft.x)
  473. delta.x = rectUpperLeft.x - visUpperLeft.x;
  474. else if (rectLowerRight.x > visLowerRight.x)
  475. delta.x = rectLowerRight.x - visLowerRight.x;
  476. // Make sure the rectangle is visible along the Y axis...
  477. if (rectUpperLeft.y < visUpperLeft.y)
  478. delta.y = rectUpperLeft.y - visUpperLeft.y;
  479. else if (rectLowerRight.y > visLowerRight.y)
  480. delta.y = rectLowerRight.y - visLowerRight.y;
  481. // If we had any changes, scroll, otherwise don't.
  482. if (delta.x || delta.y)
  483. scrollDelta(delta.x, delta.y);
  484. }
  485. #pragma endregion
  486. #pragma region Event_Processing
  487. void GuiScrollCtrl::onTouchMove(const GuiEvent& event)
  488. {
  489. curHitRegion = findHitRegion(globalToLocalCoord(event.mousePoint));
  490. GuiControl* parent = getParent();
  491. if (parent)
  492. parent->onTouchMove(event);
  493. }
  494. void GuiScrollCtrl::onTouchLeave(const GuiEvent &event)
  495. {
  496. if (!mDepressed)
  497. {
  498. curHitRegion = Content;
  499. }
  500. }
  501. bool GuiScrollCtrl::onKeyDown(const GuiEvent &event)
  502. {
  503. switch (event.keyCode)
  504. {
  505. case KEY_RIGHT:
  506. scrollByRegion(RightArrow);
  507. return true;
  508. case KEY_LEFT:
  509. scrollByRegion(LeftArrow);
  510. return true;
  511. case KEY_DOWN:
  512. scrollByRegion(DownArrow);
  513. return true;
  514. case KEY_UP:
  515. scrollByRegion(UpArrow);
  516. return true;
  517. case KEY_PAGE_UP:
  518. scrollByRegion(UpPage);
  519. return true;
  520. case KEY_PAGE_DOWN:
  521. scrollByRegion(DownPage);
  522. return true;
  523. }
  524. return Parent::onKeyDown(event);
  525. }
  526. void GuiScrollCtrl::onTouchDown(const GuiEvent &event)
  527. {
  528. mouseLock();
  529. setUpdate();
  530. Point2I curMousePos = globalToLocalCoord(event.mousePoint);
  531. curHitRegion = findHitRegion(curMousePos);
  532. mDepressed = true;
  533. mEventBubbled = false;
  534. // Set a 0.5 second delay before we start scrolling
  535. mLastUpdated = Platform::getVirtualMilliseconds() + 500;
  536. scrollByRegion(curHitRegion);
  537. if (curHitRegion == VertThumb)
  538. {
  539. mScrollOffsetAnchor = mScrollOffset;
  540. mThumbMouseDelta = curMousePos.y - mVThumbPos;
  541. }
  542. else if (curHitRegion == HorizThumb)
  543. {
  544. mScrollOffsetAnchor = mScrollOffset;
  545. mThumbMouseDelta = curMousePos.x - mHThumbPos;
  546. }
  547. else if (curHitRegion == Content)
  548. {
  549. GuiControl* parent = getParent();
  550. if (parent)
  551. {
  552. parent->onTouchDown(event);
  553. mEventBubbled = true;
  554. }
  555. }
  556. }
  557. void GuiScrollCtrl::onTouchUp(const GuiEvent &event)
  558. {
  559. mouseUnlock();
  560. setUpdate();
  561. if (mEventBubbled)
  562. {
  563. GuiControl* parent = getParent();
  564. if (parent)
  565. {
  566. parent->onTouchUp(event);
  567. }
  568. mEventBubbled = false;
  569. }
  570. curHitRegion = Content;
  571. mDepressed = false;
  572. }
  573. void GuiScrollCtrl::onTouchDragged(const GuiEvent &event)
  574. {
  575. Point2I curMousePos = globalToLocalCoord(event.mousePoint);
  576. setUpdate();
  577. if (mEventBubbled)
  578. {
  579. GuiControl* parent = getParent();
  580. if (parent)
  581. {
  582. parent->onTouchDragged(event);
  583. return;
  584. }
  585. }
  586. if ( (curHitRegion != VertThumb) && (curHitRegion != HorizThumb) )
  587. {
  588. Region hit = findHitRegion(curMousePos);
  589. if (hit != curHitRegion)
  590. mDepressed = false;
  591. else
  592. mDepressed = true;
  593. return;
  594. }
  595. // ok... if the mouse is 'near' the scroll bar, scroll with it
  596. // otherwise, snap back to the previous position.
  597. if (curHitRegion == VertThumb)
  598. {
  599. if (curMousePos.x >= mVTrackRect.point.x - mScrollBarDragTolerance &&
  600. curMousePos.x <= mVTrackRect.point.x + mVTrackRect.extent.x - 1 + mScrollBarDragTolerance &&
  601. curMousePos.y >= mVTrackRect.point.y - mScrollBarDragTolerance &&
  602. curMousePos.y <= mVTrackRect.point.y + mVTrackRect.extent.y - 1 + mScrollBarDragTolerance)
  603. {
  604. S32 newVThumbPos = curMousePos.y - mThumbMouseDelta;
  605. if(newVThumbPos != mVThumbPos)
  606. {
  607. S32 newVPos = (newVThumbPos) *
  608. (mChildExt.y - mContentExt.y) /
  609. (mVTrackRect.extent.y - mVThumbSize);
  610. scrollTo(mScrollOffset.x, newVPos);
  611. }
  612. }
  613. else
  614. scrollTo(mScrollOffset.x, mScrollOffsetAnchor.y);
  615. }
  616. else if (curHitRegion == HorizThumb)
  617. {
  618. if (curMousePos.x >= mHTrackRect.point.x - mScrollBarDragTolerance &&
  619. curMousePos.x <= mHTrackRect.point.x + mHTrackRect.extent.x - 1 + mScrollBarDragTolerance &&
  620. curMousePos.y >= mHTrackRect.point.y - mScrollBarDragTolerance &&
  621. curMousePos.y <= mHTrackRect.point.y + mHTrackRect.extent.y - 1 + mScrollBarDragTolerance)
  622. {
  623. S32 newHThumbPos = curMousePos.x - mThumbMouseDelta;
  624. if(newHThumbPos != mHThumbPos)
  625. {
  626. S32 newHPos = (newHThumbPos) *
  627. (mChildExt.x - mContentExt.x) /
  628. (mHTrackRect.extent.x - mHThumbSize);
  629. scrollTo(newHPos, mScrollOffset.y);
  630. }
  631. }
  632. else
  633. scrollTo(mScrollOffsetAnchor.x, mScrollOffset.y);
  634. }
  635. }
  636. void GuiScrollCtrl::onMouseWheelUp(const GuiEvent &event)
  637. {
  638. if ( !mAwake || !mVisible )
  639. return;
  640. Point2I previousPos = mScrollOffset;
  641. scrollByRegion((event.modifier & SI_CTRL) ? UpPage : UpArrow);
  642. // Tell the kids that the mouse moved (relatively):
  643. iterator itr;
  644. for ( itr = begin(); itr != end(); itr++ )
  645. {
  646. GuiControl* grandKid = static_cast<GuiControl*>( *itr );
  647. grandKid->onTouchMove( event );
  648. }
  649. // If no scrolling happened (already at the top), pass it on to the parent.
  650. GuiControl* parent = getParent();
  651. if (parent && (previousPos == mScrollOffset))
  652. parent->onMouseWheelUp(event);
  653. }
  654. void GuiScrollCtrl::onMouseWheelDown(const GuiEvent &event)
  655. {
  656. if ( !mAwake || !mVisible )
  657. return;
  658. Point2I previousPos = mScrollOffset;
  659. scrollByRegion((event.modifier & SI_CTRL) ? DownPage : DownArrow);
  660. // Tell the kids that the mouse moved (relatively):
  661. iterator itr;
  662. for ( itr = begin(); itr != end(); itr++ )
  663. {
  664. GuiControl* grandKid = static_cast<GuiControl *>( *itr );
  665. grandKid->onTouchMove( event );
  666. }
  667. // If no scrolling happened (already at the bottom), pass it on to the parent.
  668. GuiControl* parent = getParent();
  669. if (parent && (previousPos == mScrollOffset))
  670. parent->onMouseWheelDown(event);
  671. }
  672. #pragma endregion
  673. #pragma region rendering
  674. void GuiScrollCtrl::onPreRender()
  675. {
  676. Parent::onPreRender();
  677. // Short circuit if not depressed to save cycles
  678. if( mDepressed != true )
  679. return;
  680. //default to one second, though it shouldn't be necessary
  681. U32 timeThreshold = 1000;
  682. // We don't want to scroll by pages at an interval the same as when we're scrolling
  683. // using the arrow buttons, so adjust accordingly.
  684. switch( curHitRegion )
  685. {
  686. case UpPage:
  687. case DownPage:
  688. case LeftPage:
  689. case RightPage:
  690. timeThreshold = 200;
  691. break;
  692. case UpArrow:
  693. case DownArrow:
  694. case LeftArrow:
  695. case RightArrow:
  696. timeThreshold = 20;
  697. break;
  698. default:
  699. // Neither a button or a page, don't scroll (shouldn't get here)
  700. return;
  701. break;
  702. };
  703. S32 timeElapsed = Platform::getVirtualMilliseconds() - mLastUpdated;
  704. if ( ( timeElapsed > 0 ) && ( timeElapsed > (S32)timeThreshold ) )
  705. {
  706. mLastUpdated = Platform::getVirtualMilliseconds();
  707. scrollByRegion(curHitRegion);
  708. }
  709. }
  710. void GuiScrollCtrl::onRender(Point2I offset, const RectI &updateRect)
  711. {
  712. RectI ctrlRect = applyMargins(offset, mBounds.extent, NormalState, mProfile);
  713. if (!ctrlRect.isValidRect())
  714. {
  715. return;
  716. }
  717. renderUniversalRect(ctrlRect, mProfile, NormalState);
  718. RectI fillRect = applyBorders(ctrlRect.point, ctrlRect.extent, NormalState, mProfile);
  719. RectI contentRect = applyScrollBarSpacing(fillRect.point, fillRect.extent);
  720. mChildArea.set(contentRect.point, contentRect.extent);
  721. renderVScrollBar(offset);
  722. renderHScrollBar(offset);
  723. if (contentRect.isValidRect())
  724. {
  725. renderChildControls(offset, contentRect, updateRect);
  726. }
  727. }
  728. RectI GuiScrollCtrl::applyScrollBarSpacing(Point2I offset, Point2I extent)
  729. {
  730. RectI contentRect = RectI(offset, extent);
  731. if (mHasVScrollBar && mHasHScrollBar)
  732. {
  733. contentRect.extent.x -= mScrollBarThickness;
  734. contentRect.extent.y -= mScrollBarThickness;
  735. }
  736. else if (mHasVScrollBar)
  737. {
  738. contentRect.extent.x -= mScrollBarThickness;
  739. }
  740. else if (mHasHScrollBar)
  741. {
  742. contentRect.extent.y -= mScrollBarThickness;
  743. }
  744. return contentRect;
  745. }
  746. GuiControlState GuiScrollCtrl::getRegionCurrentState(GuiScrollCtrl::Region region)
  747. {
  748. GuiControlState currentState = GuiControlState::NormalState;
  749. if (!mActive)
  750. {
  751. currentState = GuiControlState::DisabledState;
  752. }
  753. else if (curHitRegion == region && mDepressed)
  754. {
  755. currentState = GuiControlState::SelectedState;
  756. }
  757. else if (curHitRegion == region)
  758. {
  759. currentState = GuiControlState::HighlightState;
  760. }
  761. return currentState;
  762. }
  763. void GuiScrollCtrl::renderBorderedRectWithArrow(RectI& bounds, GuiControlProfile* profile, GuiControlState state, GuiDirection direction)
  764. {
  765. if (!profile)
  766. {
  767. return;
  768. }
  769. renderUniversalRect(bounds, profile, state);
  770. RectI ctrlRect = applyMargins(bounds.point, bounds.extent, state, profile);
  771. RectI fillRect = applyBorders(ctrlRect.point, ctrlRect.extent, state, profile);
  772. RectI contentRect = applyPadding(fillRect.point, fillRect.extent, state, profile);
  773. if (contentRect.isValidRect())
  774. {
  775. Point2I p1, p2, p3;
  776. switch (direction)
  777. {
  778. case GuiDirection::Up:
  779. p1 = Point2I(contentRect.point.x + (contentRect.extent.x / 2), contentRect.point.y);
  780. p2 = Point2I(contentRect.point.x, contentRect.point.y + contentRect.extent.y);
  781. p3 = Point2I(contentRect.point.x + contentRect.extent.x, contentRect.point.y + contentRect.extent.y);
  782. break;
  783. case GuiDirection::Down:
  784. p1 = Point2I(contentRect.point.x, contentRect.point.y);
  785. p2 = Point2I(contentRect.point.x + (contentRect.extent.x / 2), contentRect.point.y + contentRect.extent.y);
  786. p3 = Point2I(contentRect.point.x + contentRect.extent.x, contentRect.point.y);
  787. break;
  788. case GuiDirection::Left:
  789. p1 = Point2I(contentRect.point.x + contentRect.extent.x, contentRect.point.y);
  790. p2 = Point2I(contentRect.point.x, contentRect.point.y + (contentRect.extent.y/2));
  791. p3 = Point2I(contentRect.point.x + contentRect.extent.x, contentRect.point.y + contentRect.extent.y);
  792. break;
  793. case GuiDirection::Right:
  794. p1 = Point2I(contentRect.point.x, contentRect.point.y);
  795. p2 = Point2I(contentRect.point.x, contentRect.point.y + contentRect.extent.y);
  796. p3 = Point2I(contentRect.point.x + contentRect.extent.x, contentRect.point.y + (contentRect.extent.y/2));
  797. break;
  798. }
  799. dglDrawTriangleFill(p1, p2, p3, profile->getFontColor(state));
  800. }
  801. }
  802. void GuiScrollCtrl::renderVScrollBar(const Point2I& offset)
  803. {
  804. if(mHasVScrollBar && mTrackProfile && mThumbProfile)
  805. {
  806. if(mVBarEnabled)
  807. {
  808. if (mShowArrowButtons && mArrowProfile)
  809. {
  810. RectI upArrowExtent = RectI(mUpArrowRect.point + offset, mUpArrowRect.extent);
  811. renderBorderedRectWithArrow(upArrowExtent, mArrowProfile, getRegionCurrentState(Region::UpArrow), GuiDirection::Up);
  812. RectI downArrowExtent = RectI(mDownArrowRect.point + offset, mDownArrowRect.extent);
  813. renderBorderedRectWithArrow(downArrowExtent, mArrowProfile, getRegionCurrentState(Region::DownArrow), GuiDirection::Down);
  814. }
  815. RectI mVTrackExtent = RectI(mVTrackRect.point + offset, mVTrackRect.extent);
  816. renderUniversalRect(mVTrackExtent, mTrackProfile, GuiControlState::NormalState);
  817. //The Thumb
  818. GuiControlState thumbState = getRegionCurrentState(Region::VertThumb);
  819. RectI vThumb = RectI(mVTrackRect.point.x + offset.x, mVTrackRect.point.y + mVThumbPos + offset.y, mScrollBarThickness, mVThumbSize);
  820. RectI vThumbWithMargins = applyMargins(vThumb.point, vThumb.extent, thumbState, mThumbProfile);
  821. renderUniversalRect(vThumbWithMargins, mThumbProfile, thumbState);
  822. }
  823. else
  824. {
  825. if (mShowArrowButtons && mArrowProfile)
  826. {
  827. RectI upArrowExtent = RectI(mUpArrowRect.point + offset, mUpArrowRect.extent);
  828. renderBorderedRectWithArrow(upArrowExtent, mArrowProfile, GuiControlState::DisabledState, GuiDirection::Up);
  829. RectI downArrowExtent = RectI(mDownArrowRect.point + offset, mDownArrowRect.extent);
  830. renderBorderedRectWithArrow(downArrowExtent, mArrowProfile, GuiControlState::DisabledState, GuiDirection::Down);
  831. }
  832. RectI mVTrackExtent = RectI(mVTrackRect.point + offset, mVTrackRect.extent);
  833. renderUniversalRect(mVTrackExtent, mTrackProfile, GuiControlState::DisabledState);
  834. }
  835. }
  836. }
  837. void GuiScrollCtrl::renderHScrollBar(const Point2I& offset)
  838. {
  839. if(mHasHScrollBar && mTrackProfile && mThumbProfile)
  840. {
  841. if (mHBarEnabled)
  842. {
  843. if (mShowArrowButtons && mArrowProfile)
  844. {
  845. RectI leftArrowBounds = RectI(mLeftArrowRect.point + offset, mLeftArrowRect.extent);
  846. renderBorderedRectWithArrow(leftArrowBounds, mArrowProfile, getRegionCurrentState(Region::LeftArrow), GuiDirection::Left);
  847. RectI rightArrowBounds = RectI(mRightArrowRect.point + offset, mRightArrowRect.extent);
  848. renderBorderedRectWithArrow(rightArrowBounds, mArrowProfile, getRegionCurrentState(Region::RightArrow), GuiDirection::Right);
  849. }
  850. RectI hTrackBounds = RectI(mHTrackRect.point + offset, mHTrackRect.extent);
  851. renderUniversalRect(hTrackBounds, mTrackProfile, GuiControlState::NormalState);
  852. //The Thumb
  853. GuiControlState thumbState = getRegionCurrentState(Region::HorizThumb);
  854. RectI hThumb = RectI(mHTrackRect.point.x + mHThumbPos + offset.x, mHTrackRect.point.y + offset.y, mHThumbSize, mScrollBarThickness);
  855. RectI hThumbWithMargins = applyMargins(hThumb.point, hThumb.extent, thumbState, mThumbProfile);
  856. renderUniversalRect(hThumbWithMargins, mThumbProfile, thumbState);
  857. }
  858. else
  859. {
  860. if (mShowArrowButtons && mArrowProfile)
  861. {
  862. RectI leftArrowBounds = RectI(mLeftArrowRect.point + offset, mLeftArrowRect.extent);
  863. renderBorderedRectWithArrow(leftArrowBounds, mArrowProfile, GuiControlState::DisabledState, GuiDirection::Left);
  864. RectI rightArrowBounds = RectI(mRightArrowRect.point + offset, mRightArrowRect.extent);
  865. renderBorderedRectWithArrow(rightArrowBounds, mArrowProfile, GuiControlState::DisabledState, GuiDirection::Right);
  866. }
  867. RectI hTrackBounds = RectI(mHTrackRect.point + offset, mHTrackRect.extent);
  868. renderUniversalRect(hTrackBounds, mTrackProfile, GuiControlState::DisabledState);
  869. }
  870. }
  871. }
  872. void GuiScrollCtrl::renderChildControls(Point2I offset, RectI content, const RectI& updateRect)
  873. {
  874. // offset is the upper-left corner of this control in screen coordinates. It should almost always be the same offset passed into the onRender method.
  875. // updateRect is the area that this control was allowed to draw in. It should almost always be the same as the value in onRender.
  876. // content is the area that child controls are allowed to draw in.
  877. RectI clipRect = content;
  878. if (clipRect.intersect(dglGetClipRect()))
  879. {
  880. S32 size = objectList.size();
  881. S32 size_cpy = size;
  882. //Get the border profiles - padding is actually applied here...
  883. GuiBorderProfile* leftProfile = mProfile->getLeftBorder();
  884. GuiBorderProfile* topProfile = mProfile->getTopBorder();
  885. S32 leftSize = (leftProfile) ? leftProfile->getPadding(NormalState) : 0;
  886. S32 topSize = (topProfile) ? topProfile->getPadding(NormalState) : 0;
  887. Point2I ltPadding = Point2I(leftSize, topSize);
  888. //-Mat look through our vector all normal-like, trying to use an iterator sometimes gives us
  889. //bad cast on good objects
  890. for (S32 count = 0; count < objectList.size(); count++)
  891. {
  892. GuiControl* ctrl = (GuiControl*)objectList[count];
  893. if (ctrl == NULL) {
  894. Con::errorf("GuiControl::renderChildControls() object %i is NULL", count);
  895. continue;
  896. }
  897. if (ctrl->mVisible)
  898. {
  899. ctrl->mRenderInsetLT = (ltPadding + content.point - offset) - mScrollOffset;
  900. ctrl->mRenderInsetRB = mBounds.extent - (ctrl->mRenderInsetLT + content.extent);
  901. Point2I childPosition = ltPadding + content.point + ctrl->getPosition() - mScrollOffset;
  902. RectI childClip(childPosition, ctrl->getExtent());
  903. if (childClip.intersect(clipRect))
  904. {
  905. RectI old = dglGetClipRect();
  906. dglSetClipRect(clipRect);
  907. glDisable(GL_CULL_FACE);
  908. ctrl->onRender(childPosition, RectI(childPosition, ctrl->getExtent()));
  909. dglSetClipRect(old);
  910. }
  911. }
  912. size_cpy = objectList.size(); // CHRIS: i know its wierd but the size of the list changes sometimes during execution of this loop
  913. if (size != size_cpy)
  914. {
  915. size = size_cpy;
  916. count--; // CHRIS: just to make sure one wasnt skipped.
  917. }
  918. }
  919. }
  920. }
  921. #pragma endregion