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