guiScrollCtrl.cc 34 KB

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