guiScrollCtrl.cc 31 KB

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