guiScrollCtrl.cc 31 KB

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