BsGUISliderHandle.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "math.h"
  4. #include "BsGUISliderHandle.h"
  5. #include "BsImageSprite.h"
  6. #include "BsGUISkin.h"
  7. #include "BsSpriteTexture.h"
  8. #include "BsGUIDimensions.h"
  9. #include "BsGUIMouseEvent.h"
  10. namespace BansheeEngine
  11. {
  12. const UINT32 GUISliderHandle::RESIZE_HANDLE_SIZE = 7;
  13. const String& GUISliderHandle::getGUITypeName()
  14. {
  15. static String name = "SliderHandle";
  16. return name;
  17. }
  18. GUISliderHandle::GUISliderHandle(GUISliderHandleFlags flags, const String& styleName, const GUIDimensions& dimensions)
  19. : GUIElement(styleName, dimensions), mFlags(flags), mMinHandleSize(0), mPctHandlePos(0.0f), mPctHandleSize(0.0f)
  20. , mStep(0.0f), mDragStartPos(0), mDragState(DragState::Normal), mMouseOverHandle(false), mHandleDragged(false)
  21. , mState(State::Normal)
  22. {
  23. mImageSprite = bs_new<ImageSprite>();
  24. // Calling virtual method is okay in this case
  25. styleUpdated();
  26. }
  27. GUISliderHandle::~GUISliderHandle()
  28. {
  29. bs_delete(mImageSprite);
  30. }
  31. GUISliderHandle* GUISliderHandle::create(GUISliderHandleFlags flags, const String& styleName)
  32. {
  33. return new (bs_alloc<GUISliderHandle>()) GUISliderHandle(flags,
  34. getStyleName<GUISliderHandle>(styleName), GUIDimensions::create());
  35. }
  36. GUISliderHandle* GUISliderHandle::create(GUISliderHandleFlags flags, const GUIOptions& options, const String& styleName)
  37. {
  38. return new (bs_alloc<GUISliderHandle>()) GUISliderHandle(flags,
  39. getStyleName<GUISliderHandle>(styleName), GUIDimensions::create(options));
  40. }
  41. void GUISliderHandle::_setHandleSize(float pct)
  42. {
  43. mPctHandleSize = Math::clamp01(pct);
  44. }
  45. void GUISliderHandle::_setHandlePos(float pct)
  46. {
  47. float maxPct = 1.0f;
  48. if (mStep > 0.0f && pct < maxPct)
  49. {
  50. pct = (pct + mStep * 0.5f) - fmod(pct + mStep * 0.5f, mStep);
  51. maxPct = Math::floor(1.0f / mStep) * mStep;
  52. }
  53. mPctHandlePos = Math::clamp(pct, 0.0f, maxPct);
  54. }
  55. float GUISliderHandle::getHandlePos() const
  56. {
  57. return mPctHandlePos;;
  58. }
  59. float GUISliderHandle::getStep() const
  60. {
  61. return mStep;
  62. }
  63. void GUISliderHandle::setStep(float step)
  64. {
  65. mStep = Math::clamp01(step);
  66. }
  67. UINT32 GUISliderHandle::getScrollableSize() const
  68. {
  69. return getMaxSize() - getHandleSize();
  70. }
  71. UINT32 GUISliderHandle::_getNumRenderElements() const
  72. {
  73. return mImageSprite->getNumRenderElements();
  74. }
  75. const SpriteMaterialInfo& GUISliderHandle::_getMaterial(UINT32 renderElementIdx, SpriteMaterial** material) const
  76. {
  77. *material = mImageSprite->getMaterial(renderElementIdx);
  78. return mImageSprite->getMaterialInfo(renderElementIdx);
  79. }
  80. void GUISliderHandle::_getMeshInfo(UINT32 renderElementIdx, UINT32& numVertices, UINT32& numIndices, GUIMeshType& type) const
  81. {
  82. UINT32 numQuads = mImageSprite->getNumQuads(renderElementIdx);
  83. numVertices = numQuads * 4;
  84. numIndices = numQuads * 6;
  85. type = GUIMeshType::Triangle;
  86. }
  87. void GUISliderHandle::updateRenderElementsInternal()
  88. {
  89. IMAGE_SPRITE_DESC desc;
  90. HSpriteTexture activeTex = getActiveTexture();
  91. if(SpriteTexture::checkIsLoaded(activeTex))
  92. desc.texture = activeTex.getInternalPtr();
  93. UINT32 handleSize = getHandleSize();
  94. if (mFlags.isSet(GUISliderHandleFlag::Horizontal))
  95. {
  96. if (handleSize == 0 && desc.texture != nullptr)
  97. {
  98. handleSize = desc.texture->getWidth();
  99. mPctHandleSize = handleSize / (float)getMaxSize();
  100. }
  101. desc.width = handleSize;
  102. desc.height = mLayoutData.area.height;
  103. }
  104. else
  105. {
  106. if (handleSize == 0 && desc.texture != nullptr)
  107. {
  108. handleSize = desc.texture->getHeight();
  109. mPctHandleSize = handleSize / (float)getMaxSize();
  110. }
  111. desc.width = mLayoutData.area.width;
  112. desc.height = handleSize;
  113. }
  114. desc.borderLeft = _getStyle()->border.left;
  115. desc.borderRight = _getStyle()->border.right;
  116. desc.borderTop = _getStyle()->border.top;
  117. desc.borderBottom = _getStyle()->border.bottom;
  118. desc.color = getTint();
  119. mImageSprite->update(desc, (UINT64)_getParentWidget());
  120. GUIElement::updateRenderElementsInternal();
  121. }
  122. void GUISliderHandle::updateClippedBounds()
  123. {
  124. mClippedBounds = mLayoutData.area;
  125. mClippedBounds.clip(mLayoutData.clipRect);
  126. }
  127. Vector2I GUISliderHandle::_getOptimalSize() const
  128. {
  129. HSpriteTexture activeTex = getActiveTexture();
  130. if(SpriteTexture::checkIsLoaded(activeTex))
  131. return Vector2I(activeTex->getWidth(), activeTex->getHeight());
  132. return Vector2I();
  133. }
  134. void GUISliderHandle::_fillBuffer(UINT8* vertices, UINT32* indices, UINT32 vertexOffset, UINT32 indexOffset,
  135. UINT32 maxNumVerts, UINT32 maxNumIndices, UINT32 renderElementIdx) const
  136. {
  137. UINT8* uvs = vertices + sizeof(Vector2);
  138. UINT32 vertexStride = sizeof(Vector2) * 2;
  139. UINT32 indexStride = sizeof(UINT32);
  140. Vector2I offset(mLayoutData.area.x, mLayoutData.area.y);
  141. Rect2I clipRect = mLayoutData.getLocalClipRect();
  142. if (mFlags.isSet(GUISliderHandleFlag::Horizontal))
  143. {
  144. offset.x += getHandlePosPx();
  145. clipRect.x -= getHandlePosPx();
  146. }
  147. else
  148. {
  149. offset.y += getHandlePosPx();
  150. clipRect.y -= getHandlePosPx();
  151. }
  152. mImageSprite->fillBuffer(vertices, uvs, indices, vertexOffset, indexOffset, maxNumVerts, maxNumIndices,
  153. vertexStride, indexStride, renderElementIdx, offset, clipRect);
  154. }
  155. bool GUISliderHandle::_mouseEvent(const GUIMouseEvent& ev)
  156. {
  157. UINT32 handleSize = getHandleSize();
  158. if(ev.getType() == GUIMouseEventType::MouseMove)
  159. {
  160. if (!_isDisabled())
  161. {
  162. if (mMouseOverHandle)
  163. {
  164. if (!isOnHandle(ev.getPosition()))
  165. {
  166. mMouseOverHandle = false;
  167. mState = State::Normal;
  168. _markLayoutAsDirty();
  169. return true;
  170. }
  171. }
  172. else
  173. {
  174. if (isOnHandle(ev.getPosition()))
  175. {
  176. mMouseOverHandle = true;
  177. mState = State::Hover;
  178. _markLayoutAsDirty();
  179. return true;
  180. }
  181. }
  182. }
  183. }
  184. bool jumpOnClick = mFlags.isSet(GUISliderHandleFlag::JumpOnClick);
  185. if(ev.getType() == GUIMouseEventType::MouseDown && (mMouseOverHandle || jumpOnClick))
  186. {
  187. if (!_isDisabled())
  188. {
  189. mState = State::Active;
  190. _markLayoutAsDirty();
  191. if (jumpOnClick)
  192. {
  193. float handlePosPx = 0.0f;
  194. if (mFlags.isSet(GUISliderHandleFlag::Horizontal))
  195. handlePosPx = (float)(ev.getPosition().x - (INT32)mLayoutData.area.x - handleSize * 0.5f);
  196. else
  197. handlePosPx = (float)(ev.getPosition().y - (INT32)mLayoutData.area.y - handleSize * 0.5f);
  198. setHandlePosPx((INT32)handlePosPx);
  199. onHandleMovedOrResized(mPctHandlePos, _getHandleSizePct());
  200. }
  201. bool isResizeable = mFlags.isSet(GUISliderHandleFlag::Resizeable);
  202. if (mFlags.isSet(GUISliderHandleFlag::Horizontal))
  203. {
  204. INT32 left = (INT32)mLayoutData.area.x + getHandlePosPx();
  205. if(isResizeable)
  206. {
  207. INT32 right = left + handleSize;
  208. INT32 clickPos = ev.getPosition().x;
  209. if(clickPos >= left && clickPos < (left + (INT32)RESIZE_HANDLE_SIZE))
  210. mDragState = DragState::LeftResize;
  211. else if(clickPos >= (right - (INT32)RESIZE_HANDLE_SIZE) && clickPos < right)
  212. mDragState = DragState::RightResize;
  213. else
  214. mDragState = DragState::Normal;
  215. }
  216. else
  217. mDragState = DragState::Normal;
  218. mDragStartPos = ev.getPosition().x - left;
  219. }
  220. else
  221. {
  222. INT32 top = (INT32)mLayoutData.area.y + getHandlePosPx();
  223. if(isResizeable)
  224. {
  225. INT32 bottom = top + handleSize;
  226. INT32 clickPos = ev.getPosition().y;
  227. if (clickPos >= top && clickPos < (top + (INT32)RESIZE_HANDLE_SIZE))
  228. mDragState = DragState::LeftResize;
  229. else if (clickPos >= (bottom - (INT32)RESIZE_HANDLE_SIZE) && clickPos < bottom)
  230. mDragState = DragState::RightResize;
  231. else
  232. mDragState = DragState::Normal;
  233. }
  234. else
  235. mDragState = DragState::Normal;
  236. mDragStartPos = ev.getPosition().y - top;
  237. }
  238. mHandleDragged = true;
  239. }
  240. return true;
  241. }
  242. if(ev.getType() == GUIMouseEventType::MouseDrag && mHandleDragged)
  243. {
  244. if (!_isDisabled())
  245. {
  246. INT32 handlePosPx;
  247. if (mFlags.isSet(GUISliderHandleFlag::Horizontal))
  248. handlePosPx = ev.getPosition().x - mDragStartPos - mLayoutData.area.x;
  249. else
  250. handlePosPx = ev.getPosition().y - mDragStartPos - mLayoutData.area.y;
  251. if (mDragState == DragState::Normal)
  252. {
  253. setHandlePosPx(handlePosPx);
  254. onHandleMovedOrResized(mPctHandlePos, _getHandleSizePct());
  255. }
  256. else // Resizing
  257. {
  258. if(mDragState == DragState::LeftResize)
  259. {
  260. INT32 right = getHandlePosPx() + handleSize;
  261. INT32 newHandleSize = right - handlePosPx;
  262. _setHandleSize(newHandleSize / (float)getMaxSize());
  263. setHandlePosPx(handlePosPx);
  264. onHandleMovedOrResized(mPctHandlePos, _getHandleSizePct());
  265. }
  266. else if(mDragState == DragState::RightResize)
  267. {
  268. INT32 left = getHandlePosPx();
  269. INT32 newHandleSize = handlePosPx - left;
  270. _setHandleSize(newHandleSize / (float)getMaxSize());
  271. onHandleMovedOrResized(mPctHandlePos, _getHandleSizePct());
  272. }
  273. }
  274. _markLayoutAsDirty();
  275. }
  276. return true;
  277. }
  278. if(ev.getType() == GUIMouseEventType::MouseOut)
  279. {
  280. if (!_isDisabled())
  281. {
  282. mMouseOverHandle = false;
  283. if (!mHandleDragged)
  284. {
  285. mState = State::Normal;
  286. _markLayoutAsDirty();
  287. }
  288. }
  289. return true;
  290. }
  291. if(ev.getType() == GUIMouseEventType::MouseUp)
  292. {
  293. if (!_isDisabled())
  294. {
  295. if (mMouseOverHandle)
  296. mState = State::Hover;
  297. else
  298. mState = State::Normal;
  299. if (!mHandleDragged)
  300. {
  301. // If we clicked above or below the scroll handle, scroll by one page
  302. INT32 handlePosPx = getHandlePosPx();
  303. if (!mFlags.isSet(GUISliderHandleFlag::JumpOnClick))
  304. {
  305. UINT32 stepSizePx = 0;
  306. if (mStep > 0.0f)
  307. stepSizePx = (UINT32)(mStep * getMaxSize());
  308. else
  309. stepSizePx = handleSize;
  310. INT32 handleOffset = 0;
  311. if (mFlags.isSet(GUISliderHandleFlag::Horizontal))
  312. {
  313. INT32 handleLeft = (INT32)mLayoutData.area.x + handlePosPx;
  314. INT32 handleRight = handleLeft + handleSize;
  315. if (ev.getPosition().x < handleLeft)
  316. handleOffset -= stepSizePx;
  317. else if (ev.getPosition().x > handleRight)
  318. handleOffset += stepSizePx;
  319. }
  320. else
  321. {
  322. INT32 handleTop = (INT32)mLayoutData.area.y + handlePosPx;
  323. INT32 handleBottom = handleTop + handleSize;
  324. if (ev.getPosition().y < handleTop)
  325. handleOffset -= stepSizePx;
  326. else if (ev.getPosition().y > handleBottom)
  327. handleOffset += stepSizePx;
  328. }
  329. handlePosPx += handleOffset;
  330. }
  331. setHandlePosPx(handlePosPx);
  332. onHandleMovedOrResized(mPctHandlePos, _getHandleSizePct());
  333. }
  334. mHandleDragged = false;
  335. _markLayoutAsDirty();
  336. }
  337. return true;
  338. }
  339. if(ev.getType() == GUIMouseEventType::MouseDragEnd)
  340. {
  341. if (!_isDisabled())
  342. {
  343. mHandleDragged = false;
  344. if (mMouseOverHandle)
  345. mState = State::Hover;
  346. else
  347. mState = State::Normal;
  348. _markLayoutAsDirty();
  349. }
  350. return true;
  351. }
  352. return false;
  353. }
  354. bool GUISliderHandle::isOnHandle(const Vector2I& pos) const
  355. {
  356. UINT32 handleSize = getHandleSize();
  357. if(mFlags.isSet(GUISliderHandleFlag::Horizontal))
  358. {
  359. INT32 left = (INT32)mLayoutData.area.x + getHandlePosPx();
  360. INT32 right = left + handleSize;
  361. if(pos.x >= left && pos.x < right)
  362. return true;
  363. }
  364. else
  365. {
  366. INT32 top = (INT32)mLayoutData.area.y + getHandlePosPx();
  367. INT32 bottom = top + handleSize;
  368. if(pos.y >= top && pos.y < bottom)
  369. return true;
  370. }
  371. return false;
  372. }
  373. INT32 GUISliderHandle::getHandlePosPx() const
  374. {
  375. UINT32 maxScrollAmount = getMaxSize() - getHandleSize();
  376. return Math::floorToInt(mPctHandlePos * maxScrollAmount);
  377. }
  378. UINT32 GUISliderHandle::getHandleSize() const
  379. {
  380. return std::max(mMinHandleSize, (UINT32)(getMaxSize() * mPctHandleSize));
  381. }
  382. float GUISliderHandle::_getHandleSizePct() const
  383. {
  384. return mPctHandleSize;
  385. }
  386. void GUISliderHandle::styleUpdated()
  387. {
  388. const GUIElementStyle* style = _getStyle();
  389. if (style != nullptr)
  390. {
  391. if (mFlags.isSet(GUISliderHandleFlag::Horizontal))
  392. mMinHandleSize = style->fixedWidth ? style->width : style->minWidth;
  393. else
  394. mMinHandleSize = style->fixedHeight ? style->height : style->minHeight;
  395. }
  396. }
  397. void GUISliderHandle::setHandlePosPx(INT32 pos)
  398. {
  399. float maxScrollAmount = (float)getMaxSize() - getHandleSize();
  400. _setHandlePos(pos / maxScrollAmount);
  401. }
  402. UINT32 GUISliderHandle::getMaxSize() const
  403. {
  404. UINT32 maxSize = mLayoutData.area.height;
  405. if(mFlags.isSet(GUISliderHandleFlag::Horizontal))
  406. maxSize = mLayoutData.area.width;
  407. return maxSize;
  408. }
  409. const HSpriteTexture& GUISliderHandle::getActiveTexture() const
  410. {
  411. switch(mState)
  412. {
  413. case State::Active:
  414. return _getStyle()->active.texture;
  415. case State::Hover:
  416. return _getStyle()->hover.texture;
  417. case State::Normal:
  418. return _getStyle()->normal.texture;
  419. }
  420. return _getStyle()->normal.texture;
  421. }
  422. }