/gui/hscrollbar.d

http://github.com/wilkie/djehuty · D · 681 lines · 439 code · 190 blank · 52 comment · 124 complexity · fe37056891ec1689ae969e280109da82 MD5 · raw file

  1. module gui.hscrollbar;
  2. import gui.widget;
  3. import core.color;
  4. import core.definitions;
  5. import core.string;
  6. import core.event;
  7. import graphics.graphics;
  8. import synch.timer;
  9. template ControlPrintCSTRList()
  10. {
  11. const char[] ControlPrintCSTRList = `
  12. this(int x, int y, int width, int height)
  13. {
  14. super(x,y,width,height);
  15. }
  16. `;
  17. }
  18. // Description: This control provides a standard horizontal scroll bar.
  19. class HScrollBar : Widget
  20. {
  21. public:
  22. enum Signal : uint {
  23. Selected,
  24. Unselected,
  25. Scrolled,
  26. }
  27. this(int x, int y, int width, int height) {
  28. super(x,y,width,height);
  29. m_clroutline = Color.fromRGB(0x80,0x80,0x80);
  30. m_clrarea = Color.fromRGB(0xe0, 0xe0, 0xe0);
  31. m_clrbutton = Color.fromRGB(0xc0, 0xc0, 0xc0);
  32. m_clrhighlight = Color.fromRGB(0xdd, 0xdd, 0xdd);
  33. m_clrnormal = Color.fromRGB(0,0,0);
  34. m_clrthumb = Color.fromRGB(0xc0, 0xc0, 0xc0);
  35. m_clrhover = Color.fromRGB(0xdd, 0xdd, 0xdd);
  36. _readyTimer = new Timer();
  37. _clickTimer = new Timer();
  38. _clickTimer.setInterval(50);
  39. _readyTimer.setInterval(100);
  40. push(_readyTimer);
  41. push(_clickTimer);
  42. }
  43. override void onAdd() {
  44. m_whatishovered = 0;
  45. m_isclicked=0;
  46. }
  47. override bool onSignal(Dispatcher dsp, uint signal) {
  48. if (dsp is _readyTimer) {
  49. readyTimerProc();
  50. }
  51. else if (dsp is _clickTimer) {
  52. clickTimerProc();
  53. }
  54. return true;
  55. }
  56. override void onDraw(ref Graphics g) {
  57. //a scroll bar is just a few rectangles (4)
  58. //one rectangle for the min arrow, one for max arrow
  59. //one more for the body
  60. //one more for the thumb
  61. long total_value_space = m_large_change + (m_max - m_min);
  62. float percent = cast(float)m_large_change / cast(float)total_value_space;
  63. m_area = (this.width - (this.height*2))+2;
  64. m_thumb_size = cast(int)(cast(float)m_area * percent);
  65. if (m_thumb_size < 10) { m_thumb_size = 10; }
  66. m_area -= m_thumb_size;
  67. percent = cast(float)(m_value - m_min) / cast(float)(m_max - m_min);
  68. m_thumb_pos_x = cast(int)(cast(float)m_area * percent) + this.left + this.height-1;
  69. m_thumb_pos_r = m_thumb_pos_x + m_thumb_size;
  70. //BODY
  71. Brush brsh = new Brush(m_clrarea);
  72. Pen pen = new Pen(m_clroutline);
  73. g.pen = (pen);
  74. g.brush = (brsh);
  75. g.drawRect(this.left, this.top, this.right,this.bottom);
  76. brsh.setColor(m_clrbutton);
  77. g.drawRect(this.left, this.top, this.left+this.height, this.bottom);
  78. g.drawRect(this.right-this.height, this.top, this.right, this.bottom);
  79. //THUMB
  80. brsh.setColor(m_clrthumb);
  81. g.brush = (brsh);
  82. g.drawRect(m_thumb_pos_x, this.top, m_thumb_pos_r, this.bottom);
  83. //Draw triangle images...
  84. //draw UP BUTTON
  85. brsh.setColor(m_clrnormal);
  86. pen.setColor(m_clrnormal);
  87. Pen pen_hlight = new Pen(m_clrhighlight);
  88. Brush brsh_hlight = new Brush(m_clrhighlight);
  89. if (m_whatishovered == 1) {
  90. g.brush = (brsh_hlight);
  91. g.pen = (pen_hlight);
  92. }
  93. else {
  94. g.brush = (brsh);
  95. g.pen = (pen);
  96. }
  97. int base, height;
  98. height = (this.height / 4); //height
  99. //from the 'height' we can draw a perfect triangle
  100. base = (height*2) - 1; //base
  101. int xH,yB; //main directional point of triangle:
  102. xH = this.top + ((this.height - base)/2);
  103. yB = this.left + ((this.height - height) /2);
  104. base--;
  105. height--;
  106. Coord pnt[3] = [ Coord(yB, xH+(base/2)), Coord(yB+height, xH), Coord(yB+height,xH+base) ];
  107. if (m_isclicked == 1) {
  108. pnt[0].x+=1;
  109. pnt[0].y+=1;
  110. pnt[1].x+=1;
  111. pnt[1].y+=1;
  112. pnt[2].x+=1;
  113. pnt[2].y+=1;
  114. }
  115. //DRAW_TRIANGLE(pnt);
  116. //draw DOWN BUTTON
  117. yB = this.right - ((this.height - height + 1)/2);
  118. Coord pnt2[3] = [ Coord(yB,xH+(base/2)), Coord(yB-height,xH), Coord(yB-height,xH+base) ];
  119. if (m_whatishovered == 2) {
  120. g.brush = (brsh_hlight);
  121. g.pen = (pen_hlight);
  122. }
  123. else
  124. {
  125. if (m_whatishovered == 1) {
  126. g.brush = (brsh);
  127. g.pen = (pen);
  128. }
  129. }
  130. if (m_isclicked == 2) {
  131. pnt2[0].x+=1;
  132. pnt2[0].y+=1;
  133. pnt2[1].x+=1;
  134. pnt2[1].y+=1;
  135. pnt2[2].x+=1;
  136. pnt2[2].y+=1;
  137. }
  138. //DRAW_TRIANGLE(pnt2);
  139. pen.setColor(m_clroutline);
  140. //THUMB BAR LINE DESIGN
  141. g.pen = (pen);
  142. int new_y = this.top + 2;
  143. int new_b = this.bottom - 2;
  144. if (m_thumb_size > 80 + base+4) {
  145. for (height = 10; height < 40; height+=4) {
  146. g.drawLine(height+m_thumb_pos_x, new_y, height+m_thumb_pos_x, new_b);
  147. }
  148. //highlight pen
  149. g.pen = (pen_hlight);
  150. for (height = 11; height < 41; height+=4) {
  151. g.drawLine(height+m_thumb_pos_x, new_y, height+m_thumb_pos_x, new_b);
  152. }
  153. //outline pen
  154. g.pen = (pen);
  155. for (height = m_thumb_size - 39; height < m_thumb_size - 9; height+=4) {
  156. g.drawLine(height+m_thumb_pos_x, new_y, height+m_thumb_pos_x, new_b);
  157. }
  158. //highlight pen
  159. g.pen = (pen_hlight);
  160. for (height = m_thumb_size - 38; height < m_thumb_size - 8; height+=4) {
  161. g.drawLine(height+m_thumb_pos_x, new_y, height+m_thumb_pos_x, new_b);
  162. }
  163. //draw rectangle
  164. yB = this.left + m_thumb_pos_x + ((m_thumb_size - base) / 2);
  165. if (m_whatishovered == 3) {
  166. g.brush = (brsh_hlight);
  167. g.drawRect(yB, xH, yB+base, xH+base);
  168. pen.setColor(m_clrnormal);
  169. }
  170. else {
  171. pen.setColor(m_clrnormal);
  172. g.brush = (brsh);
  173. g.pen = (pen);
  174. g.drawRect(yB, xH, yB+base, xH+base);
  175. }
  176. }
  177. else if (m_thumb_size > 25) {
  178. //find the rectangle's position
  179. //draw lines outward from that...
  180. yB = m_thumb_pos_x + ((m_thumb_size - base) / 2);
  181. //height = 10; height < 40
  182. //total_value_space is a counter; counts the number of lines that will fit
  183. for (total_value_space=0, height = yB + base + 2; height < m_thumb_pos_x + m_thumb_size - 9; height+=4, total_value_space++) {
  184. g.drawLine(height, new_y, height, new_b);
  185. }
  186. for (height = yB-3 ; total_value_space > 0; height-=4, total_value_space--) {
  187. g.drawLine(height, new_y, height, new_b);
  188. }
  189. //highlight pen
  190. g.pen = (pen_hlight);
  191. for (total_value_space=0, height = yB + base+3; height < m_thumb_pos_x + m_thumb_size - 8; height+=4, total_value_space++) {
  192. g.drawLine(height, new_y, height, new_b);
  193. }
  194. for (height = yB-2; total_value_space > 0; height-=4, total_value_space--) {
  195. g.drawLine(height, new_y, height, new_b);
  196. }
  197. if (m_whatishovered == 3) {
  198. g.brush = (brsh_hlight);
  199. g.drawRect(yB, xH, yB+base, xH+base);
  200. pen.setColor(m_clrnormal);
  201. }
  202. else {
  203. pen.setColor(m_clrnormal);
  204. g.brush = (brsh);
  205. g.pen = (pen);
  206. g.drawRect(yB, xH, yB+base, xH+base);
  207. }
  208. }
  209. else if(m_thumb_size > 15) {
  210. yB = this.left + m_thumb_pos_x + ((m_thumb_size - base) / 2);
  211. if (m_whatishovered == 3) {
  212. g.brush = (brsh_hlight);
  213. g.pen = (pen_hlight);
  214. g.drawRect(yB, xH, yB+base, xH+base);
  215. pen.setColor(m_clrnormal);
  216. }
  217. else {
  218. pen.setColor(m_clrnormal);
  219. g.brush = (brsh);
  220. g.pen = (pen);
  221. g.drawRect(yB, xH, yB+base, xH+base);
  222. }
  223. }
  224. g.brush = (brsh);
  225. g.pen = (pen_hlight);
  226. new_y--;
  227. new_b++;
  228. //UP BUTTON
  229. if (m_isclicked == 1) {
  230. g.pen = (pen);
  231. g.drawLine(this.left+1, new_y, this.left+1, new_b);
  232. g.drawLine(this.left+1, new_y, this.left+this.height-1, new_y);
  233. g.pen = (pen_hlight);
  234. }
  235. else {
  236. g.drawLine(this.left+1, new_y, this.left+1, new_b);
  237. g.drawLine(this.left+1, new_y, this.left+this.height-1, new_y);
  238. }
  239. //DOWN BUTTON
  240. if (m_isclicked == 2) {
  241. g.pen = (pen);
  242. g.drawLine(this.right-this.height+1, new_y, this.right-this.height+1, new_b);
  243. g.drawLine(this.right-this.height+1, new_y, this.right-1, new_y);
  244. g.pen = (pen_hlight);
  245. }
  246. else {
  247. g.drawLine(this.right-this.height+1, new_y, this.right-this.height+1, new_b);
  248. g.drawLine(this.right-this.height+1, new_y, this.right-1, new_y);
  249. }
  250. //THUMB BAR
  251. if (m_isclicked == 3) {
  252. g.pen = (pen);
  253. g.drawLine(m_thumb_pos_x+1, new_y, m_thumb_pos_x+1, new_b);
  254. g.drawLine(m_thumb_pos_x+1, new_y, m_thumb_pos_r-1, new_y);
  255. }
  256. else {
  257. g.drawLine(m_thumb_pos_x+1, new_y, m_thumb_pos_x+1, new_b);
  258. g.drawLine(m_thumb_pos_x+1, new_y, m_thumb_pos_r-1, new_y);
  259. }
  260. }
  261. override bool onMouseMove(ref Mouse mouseProps) {
  262. if (m_isclicked == 3) {
  263. //thumb bar is moving
  264. //move thumb bar and set value accordingly
  265. mouseProps.x -= m_thumb_offset;
  266. //y is now the y position of where the thumb would be now
  267. if (mouseProps.x < this.left + this.height) {
  268. mouseProps.x = this.left + this.height;
  269. }
  270. if (mouseProps.x > this.left + this.height + m_area) {
  271. mouseProps.x = this.left + this.height + m_area;
  272. }
  273. //compute value
  274. long old_value = m_value;
  275. m_value = ( cast(long) ( ( cast(float)(mouseProps.x - this.left - this.height) / cast(float)(m_area) ) * cast(float)(m_max - m_min) ) ) + m_min;
  276. if (m_value != old_value) {
  277. raiseSignal(Signal.Scrolled);
  278. return true;
  279. }
  280. return false;
  281. }
  282. //check if something is being hovered over
  283. if (mouseProps.y > this.top && mouseProps.y < this.bottom && mouseProps.x > this.left && mouseProps.x < this.right) {
  284. if (mouseProps.x - this.left < this.height) {
  285. //up button
  286. if (m_isclicked == 0 || m_isclicked == 1) {
  287. if (m_whatishovered != 1) {
  288. m_whatishovered = 1;
  289. return true;
  290. }
  291. }
  292. return false;
  293. }
  294. else if (mouseProps.x > this.right - this.height) {
  295. //down button
  296. if (m_isclicked == 0 || m_isclicked == 2) {
  297. if (m_whatishovered != 2) {
  298. m_whatishovered = 2;
  299. return true;
  300. }
  301. }
  302. return false;
  303. }
  304. else if (mouseProps.x > m_thumb_pos_x && mouseProps.x < m_thumb_pos_r) {
  305. //thumb bar
  306. if (m_isclicked == 0 || m_isclicked == 3) {
  307. if (m_whatishovered != 3) {
  308. m_whatishovered = 3;
  309. return true;
  310. }
  311. }
  312. return false;
  313. }
  314. else if (mouseProps.x < m_thumb_pos_x) {
  315. //inner area UP
  316. m_last_x = mouseProps.x;
  317. m_last_y = mouseProps.y;
  318. if (m_isclicked == 0 || m_isclicked == 4) {
  319. if (m_whatishovered != 4) {
  320. if (m_whatishovered != 0) {
  321. m_whatishovered = 4;
  322. return true;
  323. }
  324. m_whatishovered = 4;
  325. }
  326. }
  327. return false;
  328. }
  329. else {
  330. //inner area DOWN
  331. m_last_x = mouseProps.x;
  332. m_last_y = mouseProps.y;
  333. if (m_isclicked == 0 || m_isclicked == 5) {
  334. if (m_whatishovered != 5) {
  335. if (m_whatishovered != 0) {
  336. m_whatishovered = 5;
  337. return true;
  338. }
  339. m_whatishovered = 5;
  340. }
  341. }
  342. return false;
  343. }
  344. }
  345. //nothing
  346. if (m_whatishovered != 0) {
  347. m_whatishovered = 0;
  348. return true;
  349. }
  350. return false;
  351. }
  352. override bool onMouseLeave() {
  353. if (m_isclicked == 3) {
  354. return false;
  355. }
  356. if (m_whatishovered != 0) {
  357. m_whatishovered = 0;
  358. return true;
  359. }
  360. return false;
  361. }
  362. override bool onPrimaryMouseDown(ref Mouse mouseProps) {
  363. if (m_whatishovered != 0) {
  364. m_isclicked = m_whatishovered;
  365. requestCapture();
  366. if (m_isclicked == 3) {
  367. //thumb bar clicked
  368. //the number of pixels from the left edge of thumb bar is mouse = m_thumb_offset
  369. m_thumb_offset = mouseProps.x - m_thumb_pos_x;
  370. }
  371. else {
  372. //buttons / inner area clicked
  373. //stop timers if running
  374. _clickTimer.stop();
  375. _readyTimer.stop();
  376. _Move();
  377. _readyTimer.start();
  378. }
  379. return true;
  380. }
  381. return false;
  382. }
  383. override bool onPrimaryMouseUp(ref Mouse mouseProps) {
  384. if (m_isclicked > 0) {
  385. if (m_isclicked == 3) {
  386. m_isclicked = 0;
  387. onMouseMove(mouseProps);
  388. }
  389. requestRelease();
  390. //stop timers if they are running
  391. _clickTimer.stop();
  392. _readyTimer.stop();
  393. m_isclicked = 0;
  394. return true;
  395. }
  396. return false;
  397. }
  398. protected:
  399. void readyTimerProc() {
  400. //create real timer
  401. _clickTimer.start();
  402. _window.redraw();
  403. }
  404. void clickTimerProc() {
  405. _Move();
  406. _window.redraw();
  407. }
  408. void _Move() {
  409. float percent;
  410. //look at what is currently being hovered over
  411. switch (m_whatishovered) {
  412. case 1: //left button
  413. m_value -= m_small_change;
  414. if (m_value<m_min) { m_value = m_min; }
  415. raiseSignal(Signal.Scrolled);
  416. break;
  417. case 2: //right button
  418. m_value += m_small_change;
  419. if (m_value>m_max) { m_value = m_max; }
  420. raiseSignal(Signal.Scrolled);
  421. break;
  422. case 4: //inner area UP
  423. m_value -= m_large_change;
  424. if (m_value<m_min) { m_value = m_min; }
  425. //must check to see if we are hovering over the thumb bar
  426. percent = cast(float)m_large_change / cast(float)(m_large_change + (m_max - m_min));
  427. m_area = (this.height - (this.width*2))+2;
  428. m_thumb_size = cast(int)(cast(float)m_area * percent);
  429. if (m_thumb_size < 10) { m_thumb_size = 10; }
  430. m_area -= m_thumb_size;
  431. percent = cast(float)(m_value - m_min) / cast(float)(m_max - m_min);
  432. m_thumb_pos_x = cast(int)(cast(float)m_area * percent) + this.left + this.height-1;
  433. m_thumb_pos_r = m_thumb_pos_x + m_thumb_size;
  434. //compare last mouse coords with this state
  435. if (m_last_x > m_thumb_pos_x && m_last_x < m_thumb_pos_r) {
  436. //hmm
  437. //stop: we are hovering the thumb bar
  438. m_whatishovered = 3;
  439. _clickTimer.stop();
  440. }
  441. raiseSignal(Signal.Scrolled);
  442. break;
  443. case 5: //inner area DOWN
  444. m_value += m_large_change;
  445. if (m_value>m_max) { m_value = m_max; }
  446. //must check to see if we are hovering over the thumb bar
  447. percent = cast(float)m_large_change / cast(float)(m_large_change + (m_max - m_min));
  448. m_area = (this.height - (this.width*2))+2;
  449. m_thumb_size = cast(int)(cast(float)m_area * percent);
  450. if (m_thumb_size < 10) { m_thumb_size = 10; }
  451. m_area -= m_thumb_size;
  452. percent = cast(float)(m_value - m_min) / cast(float)(m_max - m_min);
  453. m_thumb_pos_x = cast(int)(cast(float)m_area * percent) + this.left + this.height-1;
  454. m_thumb_pos_r = m_thumb_pos_x + m_thumb_size;
  455. //compare last mouse coords with this state
  456. if (m_last_x >= m_thumb_pos_x && m_last_x <= m_thumb_pos_r) {
  457. //hmm
  458. //stop: we are hovering the thumb bar
  459. m_whatishovered = 3;
  460. _clickTimer.stop();
  461. }
  462. raiseSignal(Signal.Scrolled);
  463. break;
  464. default:
  465. break;
  466. }
  467. }
  468. Color m_clroutline;
  469. Color m_clrarea;
  470. Color m_clrbutton;
  471. Color m_clrthumb;
  472. Color m_clrhighlight;
  473. Color m_clrhover;
  474. Color m_clrnormal;
  475. int m_thumb_pos_x; //x (left) coord
  476. int m_thumb_pos_r; //r (right) coord
  477. int m_thumb_size;
  478. int m_area;
  479. Timer _clickTimer;
  480. Timer _readyTimer;
  481. int m_whatishovered;
  482. int m_isclicked;
  483. int m_thumb_offset;
  484. int m_last_x;
  485. int m_last_y;
  486. long m_min=0;
  487. long m_max=30;
  488. long m_value=0;
  489. long m_large_change=5;
  490. long m_small_change=1;
  491. }