guiSliderCtrl.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  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/console.h"
  23. #include "console/consoleTypes.h"
  24. #include "graphics/dgl.h"
  25. #include "graphics/TextureManager.h"
  26. #include "gui/guiSliderCtrl.h"
  27. #include "gui/guiDefaultControlRender.h"
  28. #include "platform/event.h"
  29. IMPLEMENT_CONOBJECT(GuiSliderCtrl);
  30. //----------------------------------------------------------------------------
  31. GuiSliderCtrl::GuiSliderCtrl(void)
  32. {
  33. mActive = true;
  34. mRange.set(0.0f, 1.0f);
  35. mTicks = 10;
  36. mValue = 0.5f;
  37. mThumbSize.set(8, 20);
  38. mShiftPoint = 5;
  39. mShiftExtent = 10;
  40. mDisplayValue = false;
  41. mMouseOver = false;
  42. mDepressed = false;
  43. mRendersChildren = false;
  44. mIsContainer = false;
  45. }
  46. //----------------------------------------------------------------------------
  47. void GuiSliderCtrl::initPersistFields()
  48. {
  49. Parent::initPersistFields();
  50. addGroup("Slider");
  51. addField("range", TypePoint2F, Offset(mRange, GuiSliderCtrl));
  52. addField("ticks", TypeS32, Offset(mTicks, GuiSliderCtrl));
  53. addField("value", TypeF32, Offset(mValue, GuiSliderCtrl));
  54. endGroup("Slider");
  55. }
  56. //----------------------------------------------------------------------------
  57. ConsoleMethod( GuiSliderCtrl, getValue, F32, 2, 2, "() Use the getValue method to get the current value of the slider.\n"
  58. "@return Returns current value of control (position of slider)"){
  59. return object->getValue();
  60. }
  61. //----------------------------------------------------------------------------
  62. void GuiSliderCtrl::setScriptValue(const char *val)
  63. {
  64. mValue = dAtof(val);
  65. updateThumb(mValue, false);
  66. }
  67. //----------------------------------------------------------------------------
  68. bool GuiSliderCtrl::onWake()
  69. {
  70. if (!Parent::onWake())
  71. return false;
  72. if (mThumbSize.y + mProfile->getFont(mFontSizeAdjust)->getHeight() - 4 <= (U32) mBounds.extent.y)
  73. mDisplayValue = true;
  74. else
  75. mDisplayValue = false;
  76. updateThumb(mValue, false, true);
  77. mHasTexture = mProfile->constructBitmapArray() >= NumBitmaps;
  78. if (mHasTexture)
  79. mBitmapBounds = mProfile->mBitmapArrayRects.address();
  80. return true;
  81. }
  82. //----------------------------------------------------------------------------
  83. void GuiSliderCtrl::onTouchDown(const GuiEvent &event)
  84. {
  85. if (!mActive || !mAwake || !mVisible)
  86. return;
  87. mouseLock();
  88. setFirstResponder();
  89. mDepressed = true;
  90. Point2I curMousePos = globalToLocalCoord(event.mousePoint);
  91. F32 value;
  92. if (mBounds.extent.x >= mBounds.extent.y)
  93. value = F32(curMousePos.x - mShiftPoint) / F32(mBounds.extent.x - mShiftExtent) * (mRange.y - mRange.x) + mRange.x;
  94. else
  95. value = F32(curMousePos.y) / F32(mBounds.extent.y) * (mRange.y - mRange.x) + mRange.x;
  96. updateThumb(value, !(event.modifier & SI_SHIFT));
  97. }
  98. //----------------------------------------------------------------------------
  99. void GuiSliderCtrl::onTouchDragged(const GuiEvent &event)
  100. {
  101. if (!mActive || !mAwake || !mVisible)
  102. return;
  103. Point2I curMousePos = globalToLocalCoord(event.mousePoint);
  104. F32 value;
  105. if (mBounds.extent.x >= mBounds.extent.y)
  106. value = F32(curMousePos.x - mShiftPoint) / F32(mBounds.extent.x - mShiftExtent) * (mRange.y - mRange.x) + mRange.x;
  107. else
  108. value = F32(curMousePos.y) / F32(mBounds.extent.y) * (mRange.y - mRange.x) + mRange.x;
  109. if (value > mRange.y)
  110. value = mRange.y;
  111. else if (value < mRange.x)
  112. value = mRange.x;
  113. if (!(event.modifier & SI_SHIFT) && mTicks > 2)
  114. {
  115. // If the shift key is held, snap to the nearest tick, if any are being drawn
  116. F32 tickStep = (mRange.y - mRange.x) / F32(mTicks + 1);
  117. F32 tickSteps = (value - mRange.x) / tickStep;
  118. S32 actualTick = S32(tickSteps + 0.5);
  119. value = actualTick * tickStep + mRange.x;
  120. AssertFatal(value <= mRange.y && value >= mRange.x, "Error, out of bounds value generated from shift-snap of slider");
  121. }
  122. Con::executef(this, 1, "onMouseDragged");
  123. updateThumb(value, !(event.modifier & SI_SHIFT));
  124. }
  125. //----------------------------------------------------------------------------
  126. void GuiSliderCtrl::onTouchUp(const GuiEvent &)
  127. {
  128. if (!mActive || !mAwake || !mVisible)
  129. return;
  130. mDepressed = false;
  131. mouseUnlock();
  132. execConsoleCallback();
  133. }
  134. void GuiSliderCtrl::onTouchEnter(const GuiEvent &event)
  135. {
  136. setUpdate();
  137. if (isMouseLocked())
  138. {
  139. mDepressed = true;
  140. mMouseOver = true;
  141. }
  142. else
  143. {
  144. mMouseOver = true;
  145. }
  146. }
  147. void GuiSliderCtrl::onTouchLeave(const GuiEvent &)
  148. {
  149. setUpdate();
  150. if (isMouseLocked())
  151. mDepressed = false;
  152. mMouseOver = false;
  153. }
  154. //----------------------------------------------------------------------------
  155. void GuiSliderCtrl::updateThumb(F32 _value, bool snap, bool onWake)
  156. {
  157. if (snap && mTicks > 1)
  158. {
  159. // If the shift key is held, snap to the nearest tick, if any are being drawn
  160. F32 tickStep = (mRange.y - mRange.x) / F32(mTicks + 1);
  161. F32 tickSteps = (_value - mRange.x) / tickStep;
  162. S32 actualTick = S32(tickSteps + 0.5);
  163. _value = actualTick * tickStep + mRange.x;
  164. AssertFatal(_value <= mRange.y && _value >= mRange.x, "Error, out of bounds value generated from shift-snap of slider");
  165. }
  166. mValue = _value;
  167. // clamp the thumb to legal values
  168. if (mValue < mRange.x) mValue = mRange.x;
  169. if (mValue > mRange.y) mValue = mRange.y;
  170. Point2I ext = mBounds.extent;
  171. ext.x -= (mShiftExtent + mThumbSize.x) / 2;
  172. // update the bounding thumb rect
  173. if (mBounds.extent.x >= mBounds.extent.y)
  174. { // HORZ thumb
  175. S32 mx = (S32) ((F32(ext.x) * (mValue - mRange.x) / (mRange.y - mRange.x)));
  176. S32 my = ext.y / 2;
  177. if (mDisplayValue)
  178. my = mThumbSize.y / 2;
  179. mThumb.point.x = mx - (mThumbSize.x / 2);
  180. mThumb.point.y = my - (mThumbSize.y / 2);
  181. mThumb.extent = mThumbSize;
  182. }
  183. else
  184. { // VERT thumb
  185. S32 mx = ext.x / 2;
  186. S32 my = (S32) ((F32(ext.y) * (mValue - mRange.x) / (mRange.y - mRange.x)));
  187. mThumb.point.x = mx - (mThumbSize.y / 2);
  188. mThumb.point.y = my - (mThumbSize.x / 2);
  189. mThumb.extent.x = mThumbSize.y;
  190. mThumb.extent.y = mThumbSize.x;
  191. }
  192. setFloatVariable(mValue);
  193. setUpdate();
  194. // Use the alt console command if you want to continually update:
  195. if (!onWake)
  196. execAltConsoleCallback();
  197. }
  198. //----------------------------------------------------------------------------
  199. void GuiSliderCtrl::onRender(Point2I offset, const RectI &updateRect)
  200. {
  201. Point2I pos(offset.x + mShiftPoint, offset.y);
  202. Point2I ext(mBounds.extent.x - mShiftExtent, mBounds.extent.y);
  203. RectI thumb = mThumb;
  204. if (mHasTexture)
  205. {
  206. if (mTicks > 0)
  207. {
  208. // TODO: tick marks should be positioned based on the bitmap dimentions.
  209. Point2I mid(ext.x, ext.y / 2);
  210. Point2I oldpos = pos;
  211. pos += Point2I(1, 0);
  212. glColor4f(0, 0, 0, 1);
  213. #if defined(TORQUE_OS_IOS) || defined(TORQUE_OS_ANDROID) || defined(TORQUE_OS_EMSCRIPTEN)
  214. // tick marks
  215. for (U32 t = 0; t <= (mTicks + 1); t++)
  216. {
  217. S32 x = (S32) (F32(mid.x + 1) / F32(mTicks + 1) * F32(t)) + pos.x;
  218. S32 y = pos.y + mid.y;
  219. GLfloat shiftVert[] = {
  220. (GLfloat) (x),
  221. (GLfloat) (y + mShiftPoint),
  222. (GLfloat) (x),
  223. (GLfloat) (y + mShiftPoint * 2.0f + 2.0f),
  224. };
  225. glVertexPointer(2, GL_FLOAT, 0, shiftVert);
  226. glDrawArrays(GL_LINES, 0, 2);
  227. }
  228. glColor4f(0.9f, 0.9f, 0.9f, 1.0f);
  229. // tick marks
  230. for (U32 t = 0; t <= (mTicks + 1); t++)
  231. {
  232. S32 x = (S32) (F32(mid.x + 1) / F32(mTicks + 1) * F32(t)) + pos.x + 1;
  233. S32 y = pos.y + mid.y + 1;
  234. GLfloat shiftVert[] = {
  235. (GLfloat) (x),
  236. (GLfloat) (y + mShiftPoint),
  237. (GLfloat) (x),
  238. (GLfloat) (y + mShiftPoint * 2 + 3),
  239. };
  240. glVertexPointer(2, GL_FLOAT, 0, shiftVert);
  241. glDrawArrays(GL_LINES, 0, 2);
  242. }
  243. #else
  244. glBegin(GL_LINES);
  245. // tick marks
  246. for (U32 t = 0; t <= (mTicks+1); t++)
  247. {
  248. S32 x = (S32)(F32(mid.x+1)/F32(mTicks+1)*F32(t)) + pos.x;
  249. S32 y = pos.y + mid.y;
  250. glVertex2i(x, y + mShiftPoint);
  251. glVertex2i(x, y + mShiftPoint*2 + 2);
  252. }
  253. glEnd();
  254. glColor4f(0.9f, 0.9f, 0.9f, 1.0f);
  255. glBegin(GL_LINES);
  256. // tick marks
  257. for (U32 t = 0; t <= (mTicks+1); t++)
  258. {
  259. S32 x = (S32)(F32(mid.x+1)/F32(mTicks+1)*F32(t)) + pos.x + 1;
  260. S32 y = pos.y + mid.y + 1;
  261. glVertex2i(x, y + mShiftPoint );
  262. glVertex2i(x, y + mShiftPoint*2 + 3);
  263. }
  264. glEnd();
  265. #endif
  266. pos = oldpos;
  267. }
  268. S32 index = SliderButtonNormal;
  269. if (mMouseOver)
  270. index = SliderButtonHighlight;
  271. dglClearBitmapModulation();
  272. //left border
  273. dglDrawBitmapSR(mProfile->mTextureHandle, Point2I(offset.x, offset.y + (mBounds.extent.y / 4)), mBitmapBounds[SliderLineLeft]);
  274. //right border
  275. dglDrawBitmapSR(mProfile->mTextureHandle, Point2I(offset.x + mBounds.extent.x - mBitmapBounds[SliderLineRight].extent.x, offset.y + (mBounds.extent.y / 4)), mBitmapBounds[SliderLineRight]);
  276. //draw our center piece to our slider control's border and stretch it
  277. RectI destRect;
  278. destRect.point.x = offset.x + mBitmapBounds[SliderLineLeft].extent.x;
  279. destRect.extent.x = mBounds.extent.x - mBitmapBounds[SliderLineLeft].extent.x - mBitmapBounds[SliderLineRight].extent.x;
  280. destRect.point.y = offset.y + (mBounds.extent.y / 4);
  281. destRect.extent.y = mBitmapBounds[SliderLineCenter].extent.y;
  282. RectI stretchRect;
  283. stretchRect = mBitmapBounds[SliderLineCenter];
  284. stretchRect.inset(1, 0);
  285. dglDrawBitmapStretchSR(mProfile->mTextureHandle, destRect, stretchRect);
  286. //draw our control slider button
  287. thumb.point += pos;
  288. dglDrawBitmapSR(mProfile->mTextureHandle, Point2I(thumb.point.x, offset.y), mBitmapBounds[index]);
  289. }
  290. else
  291. {
  292. // we're not usina a bitmap, draw procedurally.
  293. if (mBounds.extent.x >= mBounds.extent.y)
  294. {
  295. Point2I mid(ext.x, ext.y / 2);
  296. if (mDisplayValue)
  297. mid.set(ext.x, mThumbSize.y / 2);
  298. glColor4f(0, 0, 0, 1);
  299. #if defined(TORQUE_OS_IOS) || defined(TORQUE_OS_ANDROID) || defined(TORQUE_OS_EMSCRIPTEN)
  300. // tick marks
  301. for (U32 t = 0; t <= (mTicks + 1); t++)
  302. {
  303. S32 x = (S32) (F32(mid.x - 1) / F32(mTicks + 1) * F32(t));
  304. GLfloat verts[] = {
  305. static_cast<GLfloat>(pos.x + x), static_cast<GLfloat>(pos.y + mid.y - mShiftPoint),
  306. static_cast<GLfloat>(pos.x + x), static_cast<GLfloat>(pos.y + mid.y + mShiftPoint)
  307. };
  308. glVertexPointer(2, GL_FLOAT, 0, verts);
  309. glDrawArrays(GL_LINES, 0, 2);
  310. }
  311. GLfloat verts[] = {
  312. static_cast<GLfloat>(pos.x), static_cast<GLfloat>(pos.y + mid.y),
  313. static_cast<GLfloat>(pos.x + mid.x), static_cast<GLfloat>(pos.y + mid.y)
  314. };
  315. glVertexPointer(2, GL_FLOAT, 0, verts);
  316. glDrawArrays(GL_LINES, 0, 2);
  317. }
  318. else
  319. {
  320. Point2I mid(ext.x / 2, ext.y);
  321. glColor4f(0, 0, 0, 1);
  322. // tick marks
  323. for (U32 t = 0; t <= (mTicks + 1); t++)
  324. {
  325. S32 y = (S32) (F32(mid.y - 1) / F32(mTicks + 1) * F32(t));
  326. GLfloat verts[] = {
  327. static_cast<GLfloat>(pos.x + mid.x - mShiftPoint), static_cast<GLfloat>(pos.y + y),
  328. static_cast<GLfloat>(pos.x + mid.x + mShiftPoint), static_cast<GLfloat>(pos.y + y)
  329. };
  330. glVertexPointer(2, GL_FLOAT, 0, verts);
  331. glDrawArrays(GL_LINES, 0, 2);
  332. }
  333. GLfloat verts[] = {
  334. static_cast<GLfloat>(pos.x + mid.x), static_cast<GLfloat>(pos.y),
  335. static_cast<GLfloat>(pos.x + mid.x), static_cast<GLfloat>(pos.y + mid.y)
  336. };
  337. //for the horizontal line
  338. glVertexPointer(2, GL_FLOAT, 0, verts);
  339. glDrawArrays(GL_LINES, 0, 2);
  340. #else
  341. glBegin(GL_LINES);
  342. // horz rule
  343. glVertex2i(pos.x, pos.y+mid.y);
  344. glVertex2i(pos.x+mid.x, pos.y+mid.y);
  345. // tick marks
  346. for (U32 t = 0; t <= (mTicks+1); t++)
  347. {
  348. S32 x = (S32)(F32(mid.x-1)/F32(mTicks+1)*F32(t));
  349. glVertex2i(pos.x+x, pos.y+mid.y-mShiftPoint);
  350. glVertex2i(pos.x+x, pos.y+mid.y+mShiftPoint);
  351. }
  352. glEnd();
  353. }
  354. else
  355. {
  356. Point2I mid(ext.x/2, ext.y);
  357. glColor4f(0, 0, 0, 1);
  358. glBegin(GL_LINES);
  359. // horz rule
  360. glVertex2i(pos.x+mid.x, pos.y);
  361. glVertex2i(pos.x+mid.x, pos.y+mid.y);
  362. // tick marks
  363. for (U32 t = 0; t <= (mTicks+1); t++)
  364. {
  365. S32 y = (S32)(F32(mid.y-1)/F32(mTicks+1)*F32(t));
  366. glVertex2i(pos.x+mid.x-mShiftPoint, pos.y+y);
  367. glVertex2i(pos.x+mid.x+mShiftPoint, pos.y+y);
  368. }
  369. glEnd();
  370. #endif
  371. mDisplayValue = false;
  372. }
  373. // draw the thumb
  374. thumb.point += pos;
  375. renderUniversalRect(thumb, mProfile, NormalState);
  376. }
  377. if (mDisplayValue)
  378. {
  379. char buf[20];
  380. dSprintf(buf, sizeof(buf), "%0.3g", mValue);
  381. Point2I textStart = thumb.point;
  382. S32 txt_w = mProfile->getFont(mFontSizeAdjust)->getStrWidth((const UTF8 *) buf);
  383. textStart.x += (S32) ((thumb.extent.x / 2.0f));
  384. textStart.y += thumb.extent.y - 2; //19
  385. textStart.x -= (txt_w / 2);
  386. if (textStart.x < offset.x)
  387. textStart.x = offset.x;
  388. else if (textStart.x + txt_w > offset.x + mBounds.extent.x)
  389. textStart.x -= ((textStart.x + txt_w) - (offset.x + mBounds.extent.x));
  390. dglSetBitmapModulation(getFontColor(mProfile));
  391. dglDrawText(mProfile->getFont(mFontSizeAdjust), textStart, buf, mProfile->mFontColors);
  392. }
  393. renderChildControls(offset, mBounds, updateRect);
  394. }