guiScrollBar.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. /*
  2. Copyright (C) 2002-2013 Nikolaus Gebhardt
  3. This file is part of the "Irrlicht Engine".
  4. For conditions of distribution and use, see copyright notice in irrlicht.h
  5. Modified 2019.05.01 by stujones11, Stuart Jones <stujones111@gmail.com>
  6. This is a heavily modified copy of the Irrlicht CGUIScrollBar class
  7. which includes automatic scaling of the thumb slider and hiding of
  8. the arrow buttons where there is insufficient space.
  9. */
  10. #include "guiScrollBar.h"
  11. #include "guiButton.h"
  12. #include <IGUISkin.h>
  13. GUIScrollBar::GUIScrollBar(IGUIEnvironment *environment, IGUIElement *parent, s32 id,
  14. core::rect<s32> rectangle, bool horizontal, bool auto_scale,
  15. ISimpleTextureSource *tsrc) :
  16. IGUIElement(EGUIET_ELEMENT, environment, parent, id, rectangle),
  17. up_button(nullptr), down_button(nullptr), is_dragging(false),
  18. is_horizontal(horizontal), is_auto_scaling(auto_scale),
  19. dragged_by_slider(false), tray_clicked(false), scroll_pos(0),
  20. draw_center(0), thumb_size(0), min_pos(0), max_pos(100), small_step(10),
  21. large_step(50), drag_offset(0), page_size(100), border_size(0),
  22. m_tsrc(tsrc)
  23. {
  24. refreshControls();
  25. setNotClipped(false);
  26. setTabStop(true);
  27. setTabOrder(-1);
  28. setPos(0);
  29. }
  30. bool GUIScrollBar::OnEvent(const SEvent &event)
  31. {
  32. if (isEnabled()) {
  33. switch (event.EventType) {
  34. case EET_KEY_INPUT_EVENT:
  35. if (event.KeyInput.PressedDown) {
  36. const s32 old_pos = scroll_pos;
  37. bool absorb = true;
  38. switch (event.KeyInput.Key) {
  39. case KEY_LEFT:
  40. case KEY_UP:
  41. setPos(scroll_pos - small_step);
  42. break;
  43. case KEY_RIGHT:
  44. case KEY_DOWN:
  45. setPos(scroll_pos + small_step);
  46. break;
  47. case KEY_HOME:
  48. setPos(min_pos);
  49. break;
  50. case KEY_PRIOR:
  51. setPos(scroll_pos - large_step);
  52. break;
  53. case KEY_END:
  54. setPos(max_pos);
  55. break;
  56. case KEY_NEXT:
  57. setPos(scroll_pos + large_step);
  58. break;
  59. default:
  60. absorb = false;
  61. }
  62. if (scroll_pos != old_pos) {
  63. SEvent e;
  64. e.EventType = EET_GUI_EVENT;
  65. e.GUIEvent.Caller = this;
  66. e.GUIEvent.Element = nullptr;
  67. e.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;
  68. Parent->OnEvent(e);
  69. }
  70. if (absorb)
  71. return true;
  72. }
  73. break;
  74. case EET_GUI_EVENT:
  75. if (event.GUIEvent.EventType == EGET_BUTTON_CLICKED) {
  76. if (event.GUIEvent.Caller == up_button)
  77. setPos(scroll_pos - small_step);
  78. else if (event.GUIEvent.Caller == down_button)
  79. setPos(scroll_pos + small_step);
  80. SEvent e;
  81. e.EventType = EET_GUI_EVENT;
  82. e.GUIEvent.Caller = this;
  83. e.GUIEvent.Element = nullptr;
  84. e.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;
  85. Parent->OnEvent(e);
  86. return true;
  87. } else if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST)
  88. if (event.GUIEvent.Caller == this)
  89. is_dragging = false;
  90. break;
  91. case EET_MOUSE_INPUT_EVENT: {
  92. const core::position2di p(event.MouseInput.X, event.MouseInput.Y);
  93. bool is_inside = isPointInside(p);
  94. switch (event.MouseInput.Event) {
  95. case EMIE_MOUSE_WHEEL:
  96. if (Environment->hasFocus(this)) {
  97. s8 d = event.MouseInput.Wheel < 0 ? -1 : 1;
  98. s8 h = is_horizontal ? 1 : -1;
  99. setPos(getPos() + (d * small_step * h));
  100. SEvent e;
  101. e.EventType = EET_GUI_EVENT;
  102. e.GUIEvent.Caller = this;
  103. e.GUIEvent.Element = nullptr;
  104. e.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;
  105. Parent->OnEvent(e);
  106. return true;
  107. }
  108. break;
  109. case EMIE_LMOUSE_PRESSED_DOWN: {
  110. if (is_inside) {
  111. is_dragging = true;
  112. dragged_by_slider = slider_rect.isPointInside(p);
  113. core::vector2di corner = slider_rect.UpperLeftCorner;
  114. drag_offset = is_horizontal ? p.X - corner.X : p.Y - corner.Y;
  115. tray_clicked = !dragged_by_slider;
  116. if (tray_clicked) {
  117. const s32 new_pos = getPosFromMousePos(p);
  118. const s32 old_pos = scroll_pos;
  119. setPos(new_pos);
  120. // drag in the middle
  121. drag_offset = thumb_size / 2;
  122. // report the scroll event
  123. if (scroll_pos != old_pos && Parent) {
  124. SEvent e;
  125. e.EventType = EET_GUI_EVENT;
  126. e.GUIEvent.Caller = this;
  127. e.GUIEvent.Element = nullptr;
  128. e.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;
  129. Parent->OnEvent(e);
  130. }
  131. }
  132. Environment->setFocus(this);
  133. return true;
  134. }
  135. break;
  136. }
  137. case EMIE_LMOUSE_LEFT_UP:
  138. case EMIE_MOUSE_MOVED: {
  139. if (!event.MouseInput.isLeftPressed())
  140. is_dragging = false;
  141. if (!is_dragging) {
  142. if (event.MouseInput.Event == EMIE_MOUSE_MOVED)
  143. break;
  144. return is_inside;
  145. }
  146. if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
  147. is_dragging = false;
  148. if (!dragged_by_slider) {
  149. if (is_inside) {
  150. dragged_by_slider = slider_rect.isPointInside(p);
  151. tray_clicked = !dragged_by_slider;
  152. }
  153. if (!dragged_by_slider) {
  154. tray_clicked = false;
  155. if (event.MouseInput.Event == EMIE_MOUSE_MOVED)
  156. return is_inside;
  157. }
  158. }
  159. const s32 new_pos = getPosFromMousePos(p);
  160. const s32 old_pos = scroll_pos;
  161. setPos(new_pos);
  162. if (scroll_pos != old_pos && Parent) {
  163. SEvent e;
  164. e.EventType = EET_GUI_EVENT;
  165. e.GUIEvent.Caller = this;
  166. e.GUIEvent.Element = nullptr;
  167. e.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;
  168. Parent->OnEvent(e);
  169. }
  170. return is_inside;
  171. }
  172. default:
  173. break;
  174. }
  175. } break;
  176. default:
  177. break;
  178. }
  179. }
  180. return IGUIElement::OnEvent(event);
  181. }
  182. void GUIScrollBar::draw()
  183. {
  184. if (!IsVisible)
  185. return;
  186. IGUISkin *skin = Environment->getSkin();
  187. if (!skin)
  188. return;
  189. video::SColor icon_color = skin->getColor(
  190. isEnabled() ? EGDC_WINDOW_SYMBOL : EGDC_GRAY_WINDOW_SYMBOL);
  191. if (icon_color != current_icon_color)
  192. refreshControls();
  193. slider_rect = AbsoluteRect;
  194. skin->draw2DRectangle(this, skin->getColor(EGDC_SCROLLBAR), slider_rect,
  195. &AbsoluteClippingRect);
  196. if (core::isnotzero(range())) {
  197. if (is_horizontal) {
  198. slider_rect.UpperLeftCorner.X = AbsoluteRect.UpperLeftCorner.X +
  199. draw_center - thumb_size / 2;
  200. slider_rect.LowerRightCorner.X =
  201. slider_rect.UpperLeftCorner.X + thumb_size;
  202. } else {
  203. slider_rect.UpperLeftCorner.Y = AbsoluteRect.UpperLeftCorner.Y +
  204. draw_center - thumb_size / 2;
  205. slider_rect.LowerRightCorner.Y =
  206. slider_rect.UpperLeftCorner.Y + thumb_size;
  207. }
  208. skin->draw3DButtonPaneStandard(this, slider_rect, &AbsoluteClippingRect);
  209. }
  210. IGUIElement::draw();
  211. }
  212. void GUIScrollBar::updateAbsolutePosition()
  213. {
  214. IGUIElement::updateAbsolutePosition();
  215. refreshControls();
  216. setPos(scroll_pos);
  217. }
  218. s32 GUIScrollBar::getPosFromMousePos(const core::position2di &pos) const
  219. {
  220. s32 w, p;
  221. s32 offset = dragged_by_slider ? drag_offset : thumb_size / 2;
  222. if (is_horizontal) {
  223. w = RelativeRect.getWidth() - border_size * 2 - thumb_size;
  224. p = pos.X - AbsoluteRect.UpperLeftCorner.X - border_size - offset;
  225. } else {
  226. w = RelativeRect.getHeight() - border_size * 2 - thumb_size;
  227. p = pos.Y - AbsoluteRect.UpperLeftCorner.Y - border_size - offset;
  228. }
  229. return core::isnotzero(range()) ? s32(f32(p) / f32(w) * range() + 0.5f) + min_pos : 0;
  230. }
  231. void GUIScrollBar::setPos(const s32 &pos)
  232. {
  233. s32 thumb_area = 0;
  234. s32 thumb_min = 0;
  235. if (is_horizontal) {
  236. thumb_min = RelativeRect.getHeight();
  237. thumb_area = RelativeRect.getWidth() - border_size * 2;
  238. } else {
  239. thumb_min = RelativeRect.getWidth();
  240. thumb_area = RelativeRect.getHeight() - border_size * 2;
  241. }
  242. if (is_auto_scaling)
  243. thumb_size = (s32)std::fmin(S32_MAX,
  244. thumb_area / (f32(page_size) / f32(thumb_area + border_size * 2)));
  245. thumb_size = core::s32_clamp(thumb_size, thumb_min, thumb_area);
  246. scroll_pos = core::s32_clamp(pos, min_pos, max_pos);
  247. f32 f = core::isnotzero(range()) ? (f32(thumb_area) - f32(thumb_size)) / range()
  248. : 1.0f;
  249. draw_center = s32((f32(scroll_pos - min_pos) * f) + (f32(thumb_size) * 0.5f)) +
  250. border_size;
  251. }
  252. void GUIScrollBar::setSmallStep(const s32 &step)
  253. {
  254. small_step = step > 0 ? step : 10;
  255. }
  256. void GUIScrollBar::setLargeStep(const s32 &step)
  257. {
  258. large_step = step > 0 ? step : 50;
  259. }
  260. void GUIScrollBar::setMax(const s32 &max)
  261. {
  262. max_pos = max;
  263. if (min_pos > max_pos)
  264. min_pos = max_pos;
  265. bool enable = core::isnotzero(range());
  266. up_button->setEnabled(enable);
  267. down_button->setEnabled(enable);
  268. setPos(scroll_pos);
  269. }
  270. void GUIScrollBar::setMin(const s32 &min)
  271. {
  272. min_pos = min;
  273. if (max_pos < min_pos)
  274. max_pos = min_pos;
  275. bool enable = core::isnotzero(range());
  276. up_button->setEnabled(enable);
  277. down_button->setEnabled(enable);
  278. setPos(scroll_pos);
  279. }
  280. void GUIScrollBar::setPageSize(const s32 &size)
  281. {
  282. page_size = size;
  283. setPos(scroll_pos);
  284. }
  285. void GUIScrollBar::setArrowsVisible(ArrowVisibility visible)
  286. {
  287. arrow_visibility = visible;
  288. refreshControls();
  289. }
  290. s32 GUIScrollBar::getPos() const
  291. {
  292. return scroll_pos;
  293. }
  294. void GUIScrollBar::refreshControls()
  295. {
  296. IGUISkin *skin = Environment->getSkin();
  297. IGUISpriteBank *sprites = nullptr;
  298. current_icon_color = video::SColor(255, 255, 255, 255);
  299. if (skin) {
  300. sprites = skin->getSpriteBank();
  301. current_icon_color =
  302. skin->getColor(isEnabled() ? EGDC_WINDOW_SYMBOL
  303. : EGDC_GRAY_WINDOW_SYMBOL);
  304. }
  305. if (is_horizontal) {
  306. s32 h = RelativeRect.getHeight();
  307. border_size = RelativeRect.getWidth() < h * 4 ? 0 : h;
  308. if (!up_button) {
  309. core::rect<s32> up_button_rect(0, 0, h, h);
  310. up_button = GUIButton::addButton(Environment, up_button_rect, m_tsrc,
  311. this, -1, L"");
  312. up_button->setSubElement(true);
  313. up_button->setTabStop(false);
  314. }
  315. if (sprites) {
  316. up_button->setSpriteBank(sprites);
  317. up_button->setSprite(EGBS_BUTTON_UP,
  318. s32(skin->getIcon(EGDI_CURSOR_LEFT)),
  319. current_icon_color);
  320. up_button->setSprite(EGBS_BUTTON_DOWN,
  321. s32(skin->getIcon(EGDI_CURSOR_LEFT)),
  322. current_icon_color);
  323. }
  324. up_button->setRelativePosition(core::rect<s32>(0, 0, h, h));
  325. up_button->setAlignment(EGUIA_UPPERLEFT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT,
  326. EGUIA_LOWERRIGHT);
  327. if (!down_button) {
  328. core::rect<s32> down_button_rect(
  329. RelativeRect.getWidth() - h, 0,
  330. RelativeRect.getWidth(), h
  331. );
  332. down_button = GUIButton::addButton(Environment, down_button_rect, m_tsrc,
  333. this, -1, L"");
  334. down_button->setSubElement(true);
  335. down_button->setTabStop(false);
  336. }
  337. if (sprites) {
  338. down_button->setSpriteBank(sprites);
  339. down_button->setSprite(EGBS_BUTTON_UP,
  340. s32(skin->getIcon(EGDI_CURSOR_RIGHT)),
  341. current_icon_color);
  342. down_button->setSprite(EGBS_BUTTON_DOWN,
  343. s32(skin->getIcon(EGDI_CURSOR_RIGHT)),
  344. current_icon_color);
  345. }
  346. down_button->setRelativePosition(
  347. core::rect<s32>(RelativeRect.getWidth() - h, 0,
  348. RelativeRect.getWidth(), h));
  349. down_button->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT,
  350. EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT);
  351. } else {
  352. s32 w = RelativeRect.getWidth();
  353. border_size = RelativeRect.getHeight() < w * 4 ? 0 : w;
  354. if (!up_button) {
  355. core::rect<s32> up_button_rect(0, 0, w, w);
  356. up_button = GUIButton::addButton(Environment, up_button_rect, m_tsrc,
  357. this, -1, L"");
  358. up_button->setSubElement(true);
  359. up_button->setTabStop(false);
  360. }
  361. if (sprites) {
  362. up_button->setSpriteBank(sprites);
  363. up_button->setSprite(EGBS_BUTTON_UP,
  364. s32(skin->getIcon(EGDI_CURSOR_UP)),
  365. current_icon_color);
  366. up_button->setSprite(EGBS_BUTTON_DOWN,
  367. s32(skin->getIcon(EGDI_CURSOR_UP)),
  368. current_icon_color);
  369. }
  370. up_button->setRelativePosition(core::rect<s32>(0, 0, w, w));
  371. up_button->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT,
  372. EGUIA_UPPERLEFT, EGUIA_UPPERLEFT);
  373. if (!down_button) {
  374. core::rect<s32> down_button_rect(
  375. 0, RelativeRect.getHeight() - w,
  376. w, RelativeRect.getHeight()
  377. );
  378. down_button = GUIButton::addButton(Environment, down_button_rect, m_tsrc,
  379. this, -1, L"");
  380. down_button->setSubElement(true);
  381. down_button->setTabStop(false);
  382. }
  383. if (sprites) {
  384. down_button->setSpriteBank(sprites);
  385. down_button->setSprite(EGBS_BUTTON_UP,
  386. s32(skin->getIcon(EGDI_CURSOR_DOWN)),
  387. current_icon_color);
  388. down_button->setSprite(EGBS_BUTTON_DOWN,
  389. s32(skin->getIcon(EGDI_CURSOR_DOWN)),
  390. current_icon_color);
  391. }
  392. down_button->setRelativePosition(
  393. core::rect<s32>(0, RelativeRect.getHeight() - w, w,
  394. RelativeRect.getHeight()));
  395. down_button->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT,
  396. EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT);
  397. }
  398. bool visible;
  399. if (arrow_visibility == DEFAULT)
  400. visible = (border_size != 0);
  401. else if (arrow_visibility == HIDE) {
  402. visible = false;
  403. border_size = 0;
  404. } else {
  405. visible = true;
  406. if (is_horizontal)
  407. border_size = RelativeRect.getHeight();
  408. else
  409. border_size = RelativeRect.getWidth();
  410. }
  411. up_button->setVisible(visible);
  412. down_button->setVisible(visible);
  413. }