guiScrollCtrl.cc 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989
  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. IMPLEMENT_CONOBJECT(GuiScrollCtrl);
  33. // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //
  34. GuiScrollCtrl::GuiScrollCtrl()
  35. {
  36. mBounds.extent.set(200,200);
  37. mChildMargin.set(0,0);
  38. mBorderThickness = 1;
  39. mScrollBarThickness = 16;
  40. mScrollBarArrowBtnLength = 16;
  41. mScrollBarDragTolerance = 130;
  42. stateDepressed = false;
  43. curHitRegion = None;
  44. mWillFirstRespond = true;
  45. mUseConstantHeightThumb = false;
  46. mIsContainer = true;
  47. mForceVScrollBar = ScrollBarAlwaysOn;
  48. mForceHScrollBar = ScrollBarAlwaysOn;
  49. }
  50. static EnumTable::Enums scrollBarEnums[] =
  51. {
  52. { GuiScrollCtrl::ScrollBarAlwaysOn, "alwaysOn" },
  53. { GuiScrollCtrl::ScrollBarAlwaysOff, "alwaysOff" },
  54. { GuiScrollCtrl::ScrollBarDynamic, "dynamic" },
  55. };
  56. static EnumTable gScrollBarTable(3, &scrollBarEnums[0]);
  57. ConsoleMethod(GuiScrollCtrl, scrollToTop, void, 2, 2, "() Use the scrollToTop method to scroll the scroll control to the top of the child content area.\n"
  58. "@return No return value.\n"
  59. "@sa scrollToBottom")
  60. {
  61. object->scrollTo( 0, 0 );
  62. }
  63. ConsoleMethod(GuiScrollCtrl, scrollToBottom, void, 2, 2, "() Use the scrollToBottom method to scroll the scroll control to the bottom of the child content area.\n"
  64. "@return No return value.\n"
  65. "@sa scrollToTop")
  66. {
  67. object->scrollTo( 0, 0x7FFFFFFF );
  68. }
  69. ConsoleMethod(GuiScrollCtrl, setScrollPosition, void, 4, 4, "(x, y) - scrolls the scroll control to the specified position.")
  70. {
  71. object->scrollTo(dAtoi(argv[2]), dAtoi(argv[3]));
  72. }
  73. ConsoleMethod(GuiScrollCtrl, getScrollPositionX, S32, 2, 2, "() - get the current x scroll position of the scroll control.")
  74. {
  75. return object->getChildRelPos().x;
  76. }
  77. ConsoleMethod(GuiScrollCtrl, getScrollPositionY, S32, 2, 2, "() - get the current y scroll position of the scroll control.")
  78. {
  79. return object->getChildRelPos().y;
  80. }
  81. ConsoleMethod(GuiScrollCtrl, computeSizes, void, 2, 2, "() - refresh all the contents in this scroll container.")
  82. {
  83. return object->computeSizes();
  84. }
  85. ConsoleMethod(GuiScrollCtrl, getUseScrollEvents, bool, 2, 2, "() - get the current scroll callback state of the scroll control.")
  86. {
  87. return object->getUseScrollEvents();
  88. }
  89. ConsoleMethod(GuiScrollCtrl, setUseScrollEvents, void, 3, 3, "() - set the scroll callback state of the scroll control.")
  90. {
  91. object->setUseScrollEvents(dAtoi(argv[2]));
  92. }
  93. void GuiScrollCtrl::initPersistFields()
  94. {
  95. Parent::initPersistFields();
  96. addField("willFirstRespond", TypeBool, Offset(mWillFirstRespond, GuiScrollCtrl));
  97. addField("hScrollBar", TypeEnum, Offset(mForceHScrollBar, GuiScrollCtrl), 1, &gScrollBarTable);
  98. addField("vScrollBar", TypeEnum, Offset(mForceVScrollBar, GuiScrollCtrl), 1, &gScrollBarTable);
  99. addField("constantThumbHeight", TypeBool, Offset(mUseConstantHeightThumb, GuiScrollCtrl));
  100. addField("childMargin", TypePoint2I, Offset(mChildMargin, GuiScrollCtrl));
  101. }
  102. void GuiScrollCtrl::resize(const Point2I &newPos, const Point2I &newExt)
  103. {
  104. Parent::resize(newPos, newExt);
  105. computeSizes();
  106. }
  107. void GuiScrollCtrl::childResized(GuiControl *child)
  108. {
  109. Parent::childResized(child);
  110. computeSizes();
  111. }
  112. bool GuiScrollCtrl::onWake()
  113. {
  114. if (! Parent::onWake())
  115. return false;
  116. mTextureHandle = mProfile->mTextureHandle;
  117. mTextureHandle.setFilter(GL_NEAREST);;
  118. bool result;
  119. result = mProfile->constructBitmapArray() >= BmpStates * BmpCount;
  120. //AssertFatal(result, "Failed to create the bitmap array");
  121. if (!result)
  122. Con::warnf("Failed to create the bitmap array for %s", mProfile->mBitmapName);
  123. mBitmapBounds = mProfile->mBitmapArrayRects.address();
  124. //init
  125. mBaseThumbSize = mBitmapBounds[BmpStates * BmpVThumbTopCap].extent.y +
  126. mBitmapBounds[BmpStates * BmpVThumbBottomCap].extent.y;
  127. mScrollBarThickness = mBitmapBounds[BmpStates * BmpVPage].extent.x;
  128. mScrollBarArrowBtnLength = mBitmapBounds[BmpStates * BmpUp].extent.y;
  129. computeSizes();
  130. return true;
  131. }
  132. void GuiScrollCtrl::onSleep()
  133. {
  134. Parent::onSleep();
  135. mTextureHandle = NULL;
  136. }
  137. bool GuiScrollCtrl::calcChildExtents()
  138. {
  139. // right now the scroll control really only deals well with a single
  140. // child control for its content
  141. if (!size())
  142. return false;
  143. GuiControl *ctrl = (GuiControl *) front();
  144. mChildExt = ctrl->mBounds.extent;
  145. mChildPos = ctrl->mBounds.point;
  146. return true;
  147. }
  148. RectI lastVisRect;
  149. void GuiScrollCtrl::scrollRectVisible(RectI rect)
  150. {
  151. // rect is passed in virtual client space
  152. if(rect.extent.x > mContentExt.x)
  153. rect.extent.x = mContentExt.x;
  154. if(rect.extent.y > mContentExt.y)
  155. rect.extent.y = mContentExt.y;
  156. // Determine the points bounding the requested rectangle
  157. Point2I rectUpperLeft = rect.point;
  158. Point2I rectLowerRight = rect.point + rect.extent;
  159. lastVisRect = rect;
  160. // Determine the points bounding the actual visible area...
  161. Point2I visUpperLeft = mChildRelPos;
  162. Point2I visLowerRight = mChildRelPos + mContentExt;
  163. Point2I delta(0,0);
  164. // We basically try to make sure that first the top left of the given
  165. // rect is visible, and if it is, then that the bottom right is visible.
  166. // Make sure the rectangle is visible along the X axis...
  167. if(rectUpperLeft.x < visUpperLeft.x)
  168. delta.x = rectUpperLeft.x - visUpperLeft.x;
  169. else if(rectLowerRight.x > visLowerRight.x)
  170. delta.x = rectLowerRight.x - visLowerRight.x;
  171. // Make sure the rectangle is visible along the Y axis...
  172. if(rectUpperLeft.y < visUpperLeft.y)
  173. delta.y = rectUpperLeft.y - visUpperLeft.y;
  174. else if(rectLowerRight.y > visLowerRight.y)
  175. delta.y = rectLowerRight.y - visLowerRight.y;
  176. // If we had any changes, scroll, otherwise don't.
  177. if(delta.x || delta.y)
  178. scrollDelta(delta.x, delta.y);
  179. }
  180. void GuiScrollCtrl::addObject(SimObject *object)
  181. {
  182. Parent::addObject(object);
  183. computeSizes();
  184. }
  185. GuiControl* GuiScrollCtrl::findHitControl(const Point2I &pt, S32 initialLayer)
  186. {
  187. if(pt.x < mProfile->mBorderThickness || pt.y < mProfile->mBorderThickness)
  188. return this;
  189. if(pt.x >= mBounds.extent.x - mProfile->mBorderThickness - (mHasVScrollBar ? mScrollBarThickness : 0) ||
  190. pt.y >= mBounds.extent.y - mProfile->mBorderThickness - (mHasHScrollBar ? mScrollBarThickness : 0))
  191. return this;
  192. return Parent::findHitControl(pt, initialLayer);
  193. }
  194. void GuiScrollCtrl::computeSizes()
  195. {
  196. S32 thickness = (mProfile ? mProfile->mBorderThickness : 1);
  197. Point2I borderExtent(thickness, thickness);
  198. mContentPos = borderExtent + mChildMargin;
  199. mContentExt = mBounds.extent - (mChildMargin * 2)
  200. - (borderExtent * 2);
  201. Point2I childLowerRight;
  202. mHBarEnabled = false;
  203. mVBarEnabled = false;
  204. mHasVScrollBar = (mForceVScrollBar == ScrollBarAlwaysOn);
  205. mHasHScrollBar = (mForceHScrollBar == ScrollBarAlwaysOn);
  206. setUpdate();
  207. if (calcChildExtents())
  208. {
  209. childLowerRight = mChildPos + mChildExt;
  210. if (mHasVScrollBar)
  211. mContentExt.x -= mScrollBarThickness;
  212. if (mHasHScrollBar)
  213. mContentExt.y -= mScrollBarThickness;
  214. if (mChildExt.x > mContentExt.x && (mForceHScrollBar == ScrollBarDynamic))
  215. {
  216. mHasHScrollBar = true;
  217. mContentExt.y -= mScrollBarThickness;
  218. }
  219. if (mChildExt.y > mContentExt.y && (mForceVScrollBar == ScrollBarDynamic))
  220. {
  221. mHasVScrollBar = true;
  222. mContentExt.x -= mScrollBarThickness;
  223. // If Extent X Changed, check Horiz Scrollbar.
  224. if (mChildExt.x > mContentExt.x && !mHasHScrollBar && (mForceHScrollBar == ScrollBarDynamic))
  225. {
  226. mHasHScrollBar = true;
  227. mContentExt.y -= mScrollBarThickness;
  228. }
  229. }
  230. Point2I contentLowerRight = mContentPos + mContentExt;
  231. // see if the child controls need to be repositioned (null space in control)
  232. Point2I delta(0,0);
  233. if (mChildPos.x > mContentPos.x)
  234. delta.x = mContentPos.x - mChildPos.x;
  235. else if (contentLowerRight.x > childLowerRight.x)
  236. {
  237. S32 diff = contentLowerRight.x - childLowerRight.x;
  238. delta.x = getMin(mContentPos.x - mChildPos.x, diff);
  239. }
  240. //reposition the children if the child extent > the scroll content extent
  241. if (mChildPos.y > mContentPos.y)
  242. delta.y = mContentPos.y - mChildPos.y;
  243. else if (contentLowerRight.y > childLowerRight.y)
  244. {
  245. S32 diff = contentLowerRight.y - childLowerRight.y;
  246. delta.y = getMin(mContentPos.y - mChildPos.y, diff);
  247. }
  248. // apply the deltas to the children...
  249. if (delta.x || delta.y)
  250. {
  251. SimGroup::iterator i;
  252. for(i = begin(); i != end();i++)
  253. {
  254. GuiControl *ctrl = (GuiControl *) (*i);
  255. ctrl->mBounds.point += delta;
  256. }
  257. mChildPos += delta;
  258. childLowerRight += delta;
  259. }
  260. // enable needed scroll bars
  261. if (mChildExt.x > mContentExt.x)
  262. mHBarEnabled = true;
  263. if (mChildExt.y > mContentExt.y)
  264. mVBarEnabled = true;
  265. mChildRelPos = mContentPos - mChildPos;
  266. }
  267. // build all the rectangles and such...
  268. calcScrollRects();
  269. calcThumbs();
  270. }
  271. void GuiScrollCtrl::calcScrollRects(void)
  272. {
  273. S32 thickness = ( mProfile ? mProfile->mBorderThickness : 1 );
  274. if (mHasHScrollBar)
  275. {
  276. mLeftArrowRect.set(thickness,
  277. mBounds.extent.y - thickness - mScrollBarThickness - 1,
  278. mScrollBarArrowBtnLength,
  279. mScrollBarThickness);
  280. mRightArrowRect.set(mBounds.extent.x - thickness - (mHasVScrollBar ? mScrollBarThickness : 0) - mScrollBarArrowBtnLength,
  281. mBounds.extent.y - thickness - mScrollBarThickness - 1,
  282. mScrollBarArrowBtnLength,
  283. mScrollBarThickness);
  284. mHTrackRect.set(mLeftArrowRect.point.x + mLeftArrowRect.extent.x,
  285. mLeftArrowRect.point.y,
  286. mRightArrowRect.point.x - (mLeftArrowRect.point.x + mLeftArrowRect.extent.x),
  287. mScrollBarThickness);
  288. }
  289. if (mHasVScrollBar)
  290. {
  291. mUpArrowRect.set(mBounds.extent.x - thickness - mScrollBarThickness,
  292. thickness,
  293. mScrollBarThickness,
  294. mScrollBarArrowBtnLength);
  295. mDownArrowRect.set(mBounds.extent.x - thickness - mScrollBarThickness,
  296. mBounds.extent.y - thickness - mScrollBarArrowBtnLength - (mHasHScrollBar ? ( mScrollBarThickness + 1 ) : 0),
  297. mScrollBarThickness,
  298. mScrollBarArrowBtnLength);
  299. mVTrackRect.set(mUpArrowRect.point.x,
  300. mUpArrowRect.point.y + mUpArrowRect.extent.y,
  301. mScrollBarThickness,
  302. mDownArrowRect.point.y - (mUpArrowRect.point.y + mUpArrowRect.extent.y) );
  303. }
  304. }
  305. void GuiScrollCtrl::calcThumbs()
  306. {
  307. if (mHBarEnabled)
  308. {
  309. U32 trackSize = mHTrackRect.len_x();
  310. if (mUseConstantHeightThumb)
  311. mHThumbSize = mBaseThumbSize;
  312. else
  313. mHThumbSize = getMax(mBaseThumbSize, S32((mContentExt.x * trackSize) / mChildExt.x));
  314. mHThumbPos = mHTrackRect.point.x + (mChildRelPos.x * (trackSize - mHThumbSize)) / (mChildExt.x - mContentExt.x);
  315. }
  316. if (mVBarEnabled)
  317. {
  318. U32 trackSize = mVTrackRect.len_y();
  319. if (mUseConstantHeightThumb)
  320. mVThumbSize = mBaseThumbSize;
  321. else
  322. mVThumbSize = getMax(mBaseThumbSize, S32((mContentExt.y * trackSize) / mChildExt.y));
  323. mVThumbPos = mVTrackRect.point.y + (mChildRelPos.y * (trackSize - mVThumbSize)) / (mChildExt.y - mContentExt.y);
  324. }
  325. }
  326. void GuiScrollCtrl::scrollDelta(S32 deltaX, S32 deltaY)
  327. {
  328. scrollTo(mChildRelPos.x + deltaX, mChildRelPos.y + deltaY);
  329. }
  330. void GuiScrollCtrl::scrollTo(S32 x, S32 y)
  331. {
  332. if(!size())
  333. return;
  334. // keep scroll start state
  335. Point2I startPoint = mChildPos;
  336. Point2I startPointRel = mChildRelPos;
  337. setUpdate();
  338. if (x > mChildExt.x - mContentExt.x)
  339. x = mChildExt.x - mContentExt.x;
  340. if (x < 0)
  341. x = 0;
  342. if (y > mChildExt.y - mContentExt.y)
  343. y = mChildExt.y - mContentExt.y;
  344. if (y < 0)
  345. y = 0;
  346. Point2I delta(x - mChildRelPos.x, y - mChildRelPos.y);
  347. mChildRelPos += delta;
  348. mChildPos -= delta;
  349. for(SimSet::iterator i = begin(); i != end();i++)
  350. {
  351. GuiControl *ctrl = (GuiControl *) (*i);
  352. ctrl->mBounds.point -= delta;
  353. }
  354. calcThumbs();
  355. // Execute callback
  356. if (mUseScrollEvents)
  357. {
  358. char buf[4][32];
  359. dSprintf(buf[0], 32, "%d %d", startPoint.x, startPoint.y);
  360. dSprintf(buf[1], 32, "%d %d", startPointRel.x, startPointRel.y);
  361. dSprintf(buf[2], 32, "%d %d", mChildPos.x, mChildPos.y);
  362. dSprintf(buf[3], 32, "%d %d", mChildRelPos.x, mChildRelPos.y);
  363. Con::executef(this, 5, "onScroll", buf[0], buf[1], buf[2], buf[3]);
  364. }
  365. }
  366. GuiScrollCtrl::Region GuiScrollCtrl::findHitRegion(const Point2I &pt)
  367. {
  368. if (mVBarEnabled && mHasVScrollBar)
  369. {
  370. if (mUpArrowRect.pointInRect(pt))
  371. return UpArrow;
  372. else if (mDownArrowRect.pointInRect(pt))
  373. return DownArrow;
  374. else if (mVTrackRect.pointInRect(pt))
  375. {
  376. if (pt.y < mVThumbPos)
  377. return UpPage;
  378. else if (pt.y < mVThumbPos + mVThumbSize)
  379. return VertThumb;
  380. else
  381. return DownPage;
  382. }
  383. }
  384. if (mHBarEnabled && mHasHScrollBar)
  385. {
  386. if (mLeftArrowRect.pointInRect(pt))
  387. return LeftArrow;
  388. else if (mRightArrowRect.pointInRect(pt))
  389. return RightArrow;
  390. else if (mHTrackRect.pointInRect(pt))
  391. {
  392. if (pt.x < mHThumbPos)
  393. return LeftPage;
  394. else if (pt.x < mHThumbPos + mHThumbSize)
  395. return HorizThumb;
  396. else
  397. return RightPage;
  398. }
  399. }
  400. return None;
  401. }
  402. bool GuiScrollCtrl::wantsTabListMembership()
  403. {
  404. return true;
  405. }
  406. bool GuiScrollCtrl::loseFirstResponder()
  407. {
  408. setUpdate();
  409. return true;
  410. }
  411. bool GuiScrollCtrl::becomeFirstResponder()
  412. {
  413. setUpdate();
  414. return mWillFirstRespond;
  415. }
  416. bool GuiScrollCtrl::onKeyDown(const GuiEvent &event)
  417. {
  418. if (mWillFirstRespond)
  419. {
  420. switch (event.keyCode)
  421. {
  422. case KEY_RIGHT:
  423. scrollByRegion(RightArrow);
  424. return true;
  425. case KEY_LEFT:
  426. scrollByRegion(LeftArrow);
  427. return true;
  428. case KEY_DOWN:
  429. scrollByRegion(DownArrow);
  430. return true;
  431. case KEY_UP:
  432. scrollByRegion(UpArrow);
  433. return true;
  434. case KEY_PAGE_UP:
  435. scrollByRegion(UpPage);
  436. return true;
  437. case KEY_PAGE_DOWN:
  438. scrollByRegion(DownPage);
  439. return true;
  440. }
  441. }
  442. return Parent::onKeyDown(event);
  443. }
  444. void GuiScrollCtrl::onMouseDown(const GuiEvent &event)
  445. {
  446. mouseLock();
  447. setUpdate();
  448. Point2I curMousePos = globalToLocalCoord(event.mousePoint);
  449. curHitRegion = findHitRegion(curMousePos);
  450. stateDepressed = true;
  451. // Set a 0.5 second delay before we start scrolling
  452. mLastUpdated = Platform::getVirtualMilliseconds() + 500;
  453. scrollByRegion(curHitRegion);
  454. if (curHitRegion == VertThumb)
  455. {
  456. mChildRelPosAnchor = mChildRelPos;
  457. mThumbMouseDelta = curMousePos.y - mVThumbPos;
  458. }
  459. else if (curHitRegion == HorizThumb)
  460. {
  461. mChildRelPosAnchor = mChildRelPos;
  462. mThumbMouseDelta = curMousePos.x - mHThumbPos;
  463. }
  464. }
  465. void GuiScrollCtrl::onMouseUp(const GuiEvent &)
  466. {
  467. mouseUnlock();
  468. setUpdate();
  469. curHitRegion = None;
  470. stateDepressed = false;
  471. }
  472. void GuiScrollCtrl::onMouseDragged(const GuiEvent &event)
  473. {
  474. Point2I curMousePos = globalToLocalCoord(event.mousePoint);
  475. setUpdate();
  476. if ( (curHitRegion != VertThumb) && (curHitRegion != HorizThumb) )
  477. {
  478. Region hit = findHitRegion(curMousePos);
  479. if (hit != curHitRegion)
  480. stateDepressed = false;
  481. else
  482. stateDepressed = true;
  483. return;
  484. }
  485. // ok... if the mouse is 'near' the scroll bar, scroll with it
  486. // otherwise, snap back to the previous position.
  487. if (curHitRegion == VertThumb)
  488. {
  489. if (curMousePos.x >= mVTrackRect.point.x - mScrollBarDragTolerance &&
  490. curMousePos.x <= mVTrackRect.point.x + mVTrackRect.extent.x - 1 + mScrollBarDragTolerance &&
  491. curMousePos.y >= mVTrackRect.point.y - mScrollBarDragTolerance &&
  492. curMousePos.y <= mVTrackRect.point.y + mVTrackRect.extent.y - 1 + mScrollBarDragTolerance)
  493. {
  494. S32 newVThumbPos = curMousePos.y - mThumbMouseDelta;
  495. if(newVThumbPos != mVThumbPos)
  496. {
  497. S32 newVPos = (newVThumbPos - mVTrackRect.point.y) *
  498. (mChildExt.y - mContentExt.y) /
  499. (mVTrackRect.extent.y - mVThumbSize);
  500. scrollTo(mChildRelPosAnchor.x, newVPos);
  501. }
  502. }
  503. else
  504. scrollTo(mChildRelPosAnchor.x, mChildRelPosAnchor.y);
  505. }
  506. else if (curHitRegion == HorizThumb)
  507. {
  508. if (curMousePos.x >= mHTrackRect.point.x - mScrollBarDragTolerance &&
  509. curMousePos.x <= mHTrackRect.point.x + mHTrackRect.extent.x - 1 + mScrollBarDragTolerance &&
  510. curMousePos.y >= mHTrackRect.point.y - mScrollBarDragTolerance &&
  511. curMousePos.y <= mHTrackRect.point.y + mHTrackRect.extent.y - 1 + mScrollBarDragTolerance)
  512. {
  513. S32 newHThumbPos = curMousePos.x - mThumbMouseDelta;
  514. if(newHThumbPos != mHThumbPos)
  515. {
  516. S32 newHPos = (newHThumbPos - mHTrackRect.point.x) *
  517. (mChildExt.x - mContentExt.x) /
  518. (mHTrackRect.extent.x - mHThumbSize);
  519. scrollTo(newHPos, mChildRelPosAnchor.y);
  520. }
  521. }
  522. else
  523. scrollTo(mChildRelPosAnchor.x, mChildRelPosAnchor.y);
  524. }
  525. }
  526. bool GuiScrollCtrl::onMouseWheelUp(const GuiEvent &event)
  527. {
  528. if ( !mAwake || !mVisible )
  529. return( false );
  530. Point2I previousPos = mChildPos;
  531. scrollByRegion((event.modifier & SI_CTRL) ? UpPage : UpArrow);
  532. // Tell the kids that the mouse moved (relatively):
  533. iterator itr;
  534. for ( itr = begin(); itr != end(); itr++ )
  535. {
  536. GuiControl* grandKid = static_cast<GuiControl*>( *itr );
  537. grandKid->onMouseMove( event );
  538. }
  539. // If no scrolling happened (already at the top), pass it on to the parent.
  540. GuiControl* parent = getParent();
  541. if (parent && (previousPos == mChildPos))
  542. return parent->onMouseWheelUp(event);
  543. return true;
  544. }
  545. bool GuiScrollCtrl::onMouseWheelDown(const GuiEvent &event)
  546. {
  547. if ( !mAwake || !mVisible )
  548. return( false );
  549. Point2I previousPos = mChildPos;
  550. scrollByRegion((event.modifier & SI_CTRL) ? DownPage : DownArrow);
  551. // Tell the kids that the mouse moved (relatively):
  552. iterator itr;
  553. for ( itr = begin(); itr != end(); itr++ )
  554. {
  555. GuiControl* grandKid = static_cast<GuiControl *>( *itr );
  556. grandKid->onMouseMove( event );
  557. }
  558. // If no scrolling happened (already at the bottom), pass it on to the parent.
  559. GuiControl* parent = getParent();
  560. if (parent && (previousPos == mChildPos))
  561. return parent->onMouseWheelDown(event);
  562. return true;
  563. }
  564. void GuiScrollCtrl::onPreRender()
  565. {
  566. Parent::onPreRender();
  567. // Short circuit if not depressed to save cycles
  568. if( stateDepressed != true )
  569. return;
  570. //default to one second, though it shouldn't be necessary
  571. U32 timeThreshold = 1000;
  572. // We don't want to scroll by pages at an interval the same as when we're scrolling
  573. // using the arrow buttons, so adjust accordingly.
  574. switch( curHitRegion )
  575. {
  576. case UpPage:
  577. case DownPage:
  578. case LeftPage:
  579. case RightPage:
  580. timeThreshold = 200;
  581. break;
  582. case UpArrow:
  583. case DownArrow:
  584. case LeftArrow:
  585. case RightArrow:
  586. timeThreshold = 20;
  587. break;
  588. default:
  589. // Neither a button or a page, don't scroll (shouldn't get here)
  590. return;
  591. break;
  592. };
  593. S32 timeElapsed = Platform::getVirtualMilliseconds() - mLastUpdated;
  594. if ( ( timeElapsed > 0 ) && ( timeElapsed > (S32)timeThreshold ) )
  595. {
  596. mLastUpdated = Platform::getVirtualMilliseconds();
  597. scrollByRegion(curHitRegion);
  598. }
  599. }
  600. void GuiScrollCtrl::scrollByRegion(Region reg)
  601. {
  602. setUpdate();
  603. if(!size())
  604. return;
  605. GuiControl *content = (GuiControl *) front();
  606. U32 rowHeight, columnWidth;
  607. U32 pageHeight, pageWidth;
  608. content->getScrollLineSizes(&rowHeight, &columnWidth);
  609. if(rowHeight >= (U32)mContentExt.y)
  610. pageHeight = 1;
  611. else
  612. pageHeight = mContentExt.y - rowHeight;
  613. if(columnWidth >= (U32)mContentExt.x)
  614. pageWidth = 1;
  615. else
  616. pageWidth = mContentExt.x - columnWidth;
  617. if (mVBarEnabled)
  618. {
  619. switch(reg)
  620. {
  621. case UpPage:
  622. scrollDelta(0, -(S32)pageHeight);
  623. break;
  624. case DownPage:
  625. scrollDelta(0, pageHeight);
  626. break;
  627. case UpArrow:
  628. scrollDelta(0, -(S32)rowHeight);
  629. break;
  630. case DownArrow:
  631. scrollDelta(0, rowHeight);
  632. break;
  633. case LeftArrow:
  634. case RightArrow:
  635. case LeftPage:
  636. case RightPage:
  637. case VertThumb:
  638. case HorizThumb:
  639. case None:
  640. {
  641. //Con::errorf("Unhandled case in GuiScrollCtrl::scrollByRegion");
  642. }
  643. }
  644. }
  645. if (mHBarEnabled)
  646. {
  647. switch(reg)
  648. {
  649. case LeftPage:
  650. scrollDelta(-(S32)pageWidth, 0);
  651. break;
  652. case RightPage:
  653. scrollDelta(pageWidth, 0);
  654. break;
  655. case LeftArrow:
  656. scrollDelta(-(S32)columnWidth, 0);
  657. break;
  658. case RightArrow:
  659. scrollDelta(columnWidth, 0);
  660. break;
  661. case UpArrow:
  662. case DownArrow:
  663. case UpPage:
  664. case DownPage:
  665. case VertThumb:
  666. case HorizThumb:
  667. case None:
  668. {
  669. //Con::errorf("Unhandled case in GuiScrollCtrl::scrollByRegion");
  670. break;
  671. }
  672. }
  673. }
  674. }
  675. void GuiScrollCtrl::onRender(Point2I offset, const RectI &updateRect)
  676. {
  677. RectI r(offset.x, offset.y, mBounds.extent.x, mBounds.extent.y);
  678. if (mProfile->mOpaque)
  679. dglDrawRectFill(r, mProfile->mFillColor);
  680. if (mProfile->mBorder)
  681. renderFilledBorder(r, mProfile);
  682. // draw scroll bars
  683. if (mHasVScrollBar)
  684. drawVScrollBar(offset);
  685. if (mHasHScrollBar)
  686. drawHScrollBar(offset);
  687. //draw the scroll corner
  688. if (mHasVScrollBar && mHasHScrollBar)
  689. drawScrollCorner(offset);
  690. // draw content controls
  691. // create a rect to intersect with the updateRect
  692. RectI contentRect(mContentPos.x + offset.x, mContentPos.y + offset.y, mContentExt.x, mContentExt.y);
  693. if(contentRect.intersect(updateRect))
  694. renderChildControls(offset, contentRect);
  695. // Finally draw the last vis rect (debug aid, BJG)
  696. //RectI renderRect = lastVisRect;
  697. //renderRect.point += mContentPos + offset;
  698. //dglSetClipRect(r);
  699. //dglDrawRect(renderRect, ColorI(0, 255, 0));
  700. }
  701. void GuiScrollCtrl::drawBorder( const Point2I &offset, bool /*isFirstResponder*/ )
  702. {
  703. }
  704. void GuiScrollCtrl::drawVScrollBar(const Point2I &offset)
  705. {
  706. Point2I pos = offset + mUpArrowRect.point;
  707. S32 bitmap = (mVBarEnabled ? ((curHitRegion == UpArrow && stateDepressed) ?
  708. BmpStates * BmpUp + BmpHilite : BmpStates * BmpUp) : BmpStates * BmpUp + BmpDisabled);
  709. dglClearBitmapModulation();
  710. dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[bitmap]);
  711. pos.y += mScrollBarArrowBtnLength;
  712. S32 end;
  713. if (mVBarEnabled)
  714. end = mVThumbPos + offset.y;
  715. else
  716. end = mDownArrowRect.point.y + offset.y;
  717. bitmap = (mVBarEnabled ? ((curHitRegion == DownPage && stateDepressed) ?
  718. BmpStates * BmpVPage + BmpHilite : BmpStates * BmpVPage) : BmpStates * BmpVPage + BmpDisabled);
  719. if (end > pos.y)
  720. {
  721. dglClearBitmapModulation();
  722. dglDrawBitmapStretchSR(mTextureHandle, RectI(pos, Point2I(mBitmapBounds[bitmap].extent.x, end - pos.y)), mBitmapBounds[bitmap]);
  723. }
  724. pos.y = end;
  725. if (mVBarEnabled)
  726. {
  727. bool thumbSelected = (curHitRegion == VertThumb && stateDepressed);
  728. S32 ttop = (thumbSelected ? BmpStates * BmpVThumbTopCap + BmpHilite : BmpStates * BmpVThumbTopCap);
  729. S32 tmid = (thumbSelected ? BmpStates * BmpVThumb + BmpHilite : BmpStates * BmpVThumb);
  730. S32 tbot = (thumbSelected ? BmpStates * BmpVThumbBottomCap + BmpHilite : BmpStates * BmpVThumbBottomCap);
  731. // draw the thumb
  732. dglClearBitmapModulation();
  733. dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[ttop]);
  734. pos.y += mBitmapBounds[ttop].extent.y;
  735. end = mVThumbPos + mVThumbSize - mBitmapBounds[tbot].extent.y + offset.y;
  736. if (end > pos.y)
  737. {
  738. dglClearBitmapModulation();
  739. dglDrawBitmapStretchSR(mTextureHandle, RectI(pos, Point2I(mBitmapBounds[tmid].extent.x, end - pos.y)), mBitmapBounds[tmid]);
  740. }
  741. pos.y = end;
  742. dglClearBitmapModulation();
  743. dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[tbot]);
  744. pos.y += mBitmapBounds[tbot].extent.y;
  745. end = mVTrackRect.point.y + mVTrackRect.extent.y - 1 + offset.y;
  746. bitmap = (curHitRegion == DownPage && stateDepressed) ? BmpStates * BmpVPage + BmpHilite : BmpStates * BmpVPage;
  747. if (end > pos.y)
  748. {
  749. dglClearBitmapModulation();
  750. dglDrawBitmapStretchSR(mTextureHandle, RectI(pos, Point2I(mBitmapBounds[bitmap].extent.x, end - pos.y)), mBitmapBounds[bitmap]);
  751. }
  752. pos.y = end;
  753. }
  754. bitmap = (mVBarEnabled ? ((curHitRegion == DownArrow && stateDepressed ) ?
  755. BmpStates * BmpDown + BmpHilite : BmpStates * BmpDown) : BmpStates * BmpDown + BmpDisabled);
  756. dglClearBitmapModulation();
  757. dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[bitmap]);
  758. }
  759. void GuiScrollCtrl::drawHScrollBar(const Point2I &offset)
  760. {
  761. S32 bitmap;
  762. //draw the left arrow
  763. bitmap = (mHBarEnabled ? ((curHitRegion == LeftArrow && stateDepressed) ?
  764. BmpStates * BmpLeft + BmpHilite : BmpStates * BmpLeft) : BmpStates * BmpLeft + BmpDisabled);
  765. Point2I pos = offset;
  766. pos += mLeftArrowRect.point;
  767. dglClearBitmapModulation();
  768. dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[bitmap]);
  769. pos.x += mLeftArrowRect.extent.x;
  770. //draw the left page
  771. S32 end;
  772. if (mHBarEnabled)
  773. end = mHThumbPos + offset.x;
  774. else
  775. end = mRightArrowRect.point.x + offset.x;
  776. bitmap = (mHBarEnabled ? ((curHitRegion == LeftPage && stateDepressed) ?
  777. BmpStates * BmpHPage + BmpHilite : BmpStates * BmpHPage) : BmpStates * BmpHPage + BmpDisabled);
  778. if (end > pos.x)
  779. {
  780. dglClearBitmapModulation();
  781. dglDrawBitmapStretchSR(mTextureHandle, RectI(pos, Point2I(end - pos.x, mBitmapBounds[bitmap].extent.y)), mBitmapBounds[bitmap]);
  782. }
  783. pos.x = end;
  784. //draw the thumb and the rightPage
  785. if (mHBarEnabled)
  786. {
  787. bool thumbSelected = (curHitRegion == HorizThumb && stateDepressed);
  788. S32 ttop = (thumbSelected ? BmpStates * BmpHThumbLeftCap + BmpHilite : BmpStates * BmpHThumbLeftCap );
  789. S32 tmid = (thumbSelected ? BmpStates * BmpHThumb + BmpHilite : BmpStates * BmpHThumb);
  790. S32 tbot = (thumbSelected ? BmpStates * BmpHThumbRightCap + BmpHilite : BmpStates * BmpHThumbRightCap);
  791. // draw the thumb
  792. dglClearBitmapModulation();
  793. dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[ttop]);
  794. pos.x += mBitmapBounds[ttop].extent.x;
  795. end = mHThumbPos + mHThumbSize - mBitmapBounds[tbot].extent.x + offset.x;
  796. if (end > pos.x)
  797. {
  798. dglClearBitmapModulation();
  799. dglDrawBitmapStretchSR(mTextureHandle, RectI(pos, Point2I(end - pos.x, mBitmapBounds[tmid].extent.y)), mBitmapBounds[tmid]);
  800. }
  801. pos.x = end;
  802. dglClearBitmapModulation();
  803. dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[tbot]);
  804. pos.x += mBitmapBounds[tbot].extent.x;
  805. end = mHTrackRect.point.x + mHTrackRect.extent.x - 1 + offset.x;
  806. bitmap = ((curHitRegion == RightPage && stateDepressed) ? BmpStates * BmpHPage + BmpHilite : BmpStates * BmpHPage);
  807. if (end > pos.x)
  808. {
  809. dglClearBitmapModulation();
  810. dglDrawBitmapStretchSR(mTextureHandle, RectI(pos, Point2I(end - pos.x, mBitmapBounds[bitmap].extent.y)), mBitmapBounds[bitmap]);
  811. }
  812. pos.x = end;
  813. }
  814. bitmap = (mHBarEnabled ? ((curHitRegion == RightArrow && stateDepressed) ?
  815. BmpStates * BmpRight + BmpHilite : BmpStates * BmpRight) : BmpStates * BmpRight + BmpDisabled);
  816. dglClearBitmapModulation();
  817. dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[bitmap]);
  818. }
  819. void GuiScrollCtrl::drawScrollCorner(const Point2I &offset)
  820. {
  821. Point2I pos = offset;
  822. pos.x += mRightArrowRect.point.x + mRightArrowRect.extent.x;
  823. pos.y += mRightArrowRect.point.y;
  824. dglClearBitmapModulation();
  825. dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[BmpStates * BmpResize]);
  826. }
  827. void GuiScrollCtrl::autoScroll(Region reg)
  828. {
  829. scrollByRegion(reg);
  830. }