guiSliderCtrl.cc 16 KB

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