/gui/textfield.d

http://github.com/wilkie/djehuty · D · 672 lines · 390 code · 196 blank · 86 comment · 65 complexity · 1100387ee0904f40bc443c92a4804c78 MD5 · raw file

  1. module gui.textfield;
  2. import gui.widget;
  3. import djehuty;
  4. import graphics.graphics;
  5. template ControlPrintCSTRList() {
  6. const char[] ControlPrintCSTRList = `
  7. this(int x, int y, int width, int height, string value) {
  8. super(x,y,width,height,value);
  9. }
  10. this(int x, int y, int width, int height, string value) {
  11. super(x,y,width,height,value);
  12. }
  13. `;
  14. }
  15. // Description: This control provides a standard one line text field.
  16. class TextField : Widget {
  17. public:
  18. enum Signal : uint {
  19. Selected,
  20. Unselected,
  21. Changed,
  22. }
  23. this(int x, int y, int width, int height, string value) {
  24. super(x,y,width,height);
  25. _value = value.dup;
  26. }
  27. override void onAdd() {
  28. _clr_highlight = Color.fromRGB(0xf8,0xf8,0xf8);
  29. _clr_outline = Color.DarkGray;
  30. _clr_background = Color.White;
  31. Graphics grp = _view.lock();
  32. _font = new Font(FontSans, 8, 400, false, false, false);
  33. grp.font = (_font);
  34. if (_value.length > 0)
  35. {
  36. grp.measureText(_value, _value_size);
  37. }
  38. else
  39. {
  40. grp.measureText(" ", _value_size);
  41. _value_size.x = 0;
  42. }
  43. _view.unlock();
  44. }
  45. void SelectionClear() {
  46. //deletes the selection
  47. if (_caret_pos != _sel_start)
  48. {
  49. InternalSelectionClear();
  50. RefreshViewport(_caret_pos);
  51. }
  52. }
  53. override bool onMouseMove(ref Mouse mouseProps) {
  54. //find the section when the left mouse button is pressed
  55. if (mouseProps.clicks != 1) {
  56. return false;
  57. }
  58. if (mouseProps.leftDown) {
  59. Graphics grp = _view.lock();
  60. //grp.font = (_font);
  61. uint sel_start = 0;
  62. int x1 = mouseProps.x - (this.left + 3);
  63. if (x1 < 0) {
  64. sel_start = 0;
  65. }
  66. else {
  67. int x_test;
  68. Size s;
  69. sel_start = _first_char;
  70. grp.measureText(_value[_first_char.._first_char+1],s);
  71. x_test = (s.x / 2);
  72. while (x_test < x1 && sel_start < _value.length) {
  73. //proceed further down the string
  74. sel_start++;
  75. //get length of total string so far
  76. grp.measureText(_value[_first_char..sel_start],s);
  77. x_test = s.x;
  78. //get length of current character
  79. grp.measureText(_value[sel_start..sel_start+1],s);
  80. //add half of the current character
  81. x_test += (s.x / 2);
  82. }
  83. }
  84. if (_sel_start != sel_start) {
  85. _sel_start = sel_start;
  86. _view.unlock();
  87. RefreshViewport(sel_start);
  88. return true;
  89. }
  90. _view.unlock();
  91. RefreshViewport(sel_start);
  92. }
  93. return false;
  94. }
  95. override bool onPrimaryMouseDown(ref Mouse mouseProps) {
  96. //move caret
  97. // -- this section takes 'x' in terms of the control's position
  98. // and gets the position under x. Should be good for determining
  99. // selections
  100. int x1 = mouseProps.x - (this.left + 3);
  101. if (x1 < 0) { _caret_pos = 0; return true; }
  102. Graphics grp = _view.lock();
  103. //grp.font = (_font);
  104. int x_test;
  105. Size s;
  106. _caret_pos = _first_char;
  107. grp.measureText(_value[_first_char.._first_char+1], s);
  108. x_test = (s.x / 2);
  109. while (x_test < x1 && _caret_pos < _value.length) {
  110. //proceed further down the string
  111. _caret_pos++;
  112. //get length of total string so far
  113. grp.measureText(_value[_first_char.._caret_pos-_first_char], s);
  114. x_test = s.x;
  115. //get length of current character
  116. grp.measureText(_value[_caret_pos.._caret_pos+1], s);
  117. //add half of the current character
  118. x_test += (s.x / 2);
  119. }
  120. //void out any previous selection
  121. _sel_start = _caret_pos;
  122. //capture mouse
  123. requestCapture();
  124. if ((mouseProps.clicks % 3) == 0) {
  125. //select line
  126. _caret_pos = 0;
  127. _sel_start = _value.length;
  128. }
  129. if ((mouseProps.clicks % 2) == 0) {
  130. //select text from the current caret position
  131. //grab a word
  132. _caret_pos = 0;
  133. _sel_start = _value.length;
  134. }
  135. else {
  136. }
  137. _view.unlock();
  138. return true;
  139. }
  140. override bool onPrimaryMouseUp(ref Mouse mouseProps) {
  141. requestRelease();
  142. return false;
  143. }
  144. override bool onGotFocus(bool withWindow) {
  145. return true;
  146. }
  147. override bool onLostFocus(bool withWindow) {
  148. return true;
  149. }
  150. // Key Presses
  151. override bool onKeyDown(Key key) {
  152. switch(key.code) {
  153. case Key.Left:
  154. if (_caret_pos != 0) {
  155. _sel_start = --_caret_pos;
  156. }
  157. RefreshViewport(_caret_pos);
  158. break;
  159. case Key.Right:
  160. if (_caret_pos != _value.length) {
  161. _sel_start = ++_caret_pos;
  162. }
  163. RefreshViewport(_caret_pos);
  164. break;
  165. case Key.Up:
  166. break;
  167. case Key.Down:
  168. break;
  169. case Key.Delete:
  170. if (_caret_pos == _sel_start) {
  171. if ( _caret_pos != _value.length ) {
  172. //delete the character to the right of the caret
  173. string str_partA, str_partB;
  174. str_partA = _value.substring(0, _caret_pos);
  175. str_partB = _value.substring(_caret_pos+1);
  176. _value = str_partA;
  177. _value ~= str_partB;
  178. //load the font, and get the size of the text when drawn
  179. Graphics grp = _view.lock();
  180. //grp.font = (_font);
  181. if (_value.length > 0) {
  182. grp.measureText(_value, _value_size);
  183. }
  184. else {
  185. grp.measureText(" ", _value_size);
  186. _value_size.x = 0;
  187. }
  188. _view.unlock();
  189. RefreshViewport(_caret_pos);
  190. //FIRE_EVENT(id, EventValueChanged, 0, 0);
  191. }
  192. }
  193. else {
  194. //clear selection
  195. SelectionClear();
  196. }
  197. break;
  198. case Key.Backspace:
  199. //erase
  200. if (_caret_pos == _sel_start) {
  201. if (_caret_pos != 0) {
  202. string str_partA, str_partB;
  203. _caret_pos--;
  204. str_partA = _value.substring(0, _caret_pos);
  205. str_partB = _value.substring(_caret_pos+1);
  206. _value = str_partA;
  207. _value ~= str_partB;
  208. _sel_start = _caret_pos;
  209. //load the font, and get the size of the text when drawn
  210. Graphics grp = _view.lock();
  211. //grp.font = (_font);
  212. if (_value.length > 0)
  213. {
  214. grp.measureText(_value, _value_size);
  215. }
  216. else
  217. {
  218. grp.measureText(" ", _value_size);
  219. _value_size.x = 0;
  220. }
  221. _view.unlock();
  222. RefreshViewport(_caret_pos);
  223. //FIRE_EVENT(id, EventValueChanged, 0, 0);
  224. }
  225. }
  226. else {
  227. //erase the selection
  228. SelectionClear();
  229. }
  230. break;
  231. default:
  232. //check for keyboard shortcut
  233. break;
  234. }
  235. return true;
  236. }
  237. override bool onKeyChar(dchar character) {
  238. //if this character is a character we can write out, then
  239. //alter the text of the field
  240. //add the letter into the text field
  241. //if there is a selection, clear it first
  242. SelectionClear();
  243. string str_partA;
  244. string str_partB;
  245. str_partA = _value.substring(0, _caret_pos);
  246. str_partB = _value.substring(_caret_pos);
  247. _sel_start = ++_caret_pos;
  248. _value = str_partA;
  249. _value ~= Unicode.toUtf8([character]);
  250. _value ~= str_partB;
  251. //load the font, and get the size of the text when drawn
  252. Graphics grp = _view.lock();
  253. //grp.font = (_font);
  254. if (_value.length > 0) {
  255. grp.measureText(_value, _value_size);
  256. }
  257. else {
  258. grp.measureText(" ", _value_size);
  259. _value_size.x = 0;
  260. }
  261. _view.unlock();
  262. RefreshViewport(_caret_pos);
  263. //FIRE_EVENT(id, EventValueChanged, 0, 0);
  264. return true;
  265. }
  266. override void onDraw(ref Graphics g) {
  267. //Draw Background of Button
  268. Brush brush = new Brush(_clr_background);
  269. Pen pen = new Pen(_clr_outline);
  270. g.brush = (brush);
  271. g.pen = (pen);
  272. g.drawRect(this.left, this.top, this.right, this.bottom);
  273. //Draw 3D Effect
  274. pen.setColor(_clr_highlight);
  275. g.pen = (pen);
  276. g.drawLine(this.left + 1, this.top + 1, this.right - 1, this.top + 1);
  277. g.drawLine(this.left + 1, this.top + 1, this.left + 1, this.bottom - 1);
  278. //Drawing Transparent Text
  279. g.setTextModeTransparent();
  280. //create Atmosphere for Selections
  281. brush.setColor(Color.Black);
  282. pen.setColor(Color.Black);
  283. //Draw Text
  284. Rect bounds;
  285. bounds.left = this.left + 1;
  286. bounds.right = this.right - 1;
  287. bounds.top = this.top + 1;
  288. bounds.bottom = this.bottom - 1;
  289. //g.font = (_font);
  290. g.forecolor = Color.Black;
  291. if (_caret_pos == _sel_start || (_caret_pos < _first_char && _sel_start < _first_char)) {
  292. //no selection visible
  293. g.drawText(this.left+3, this.top+2, _value[_first_char.._value.length]);
  294. //Draw Caret
  295. if (_caret_pos >= _first_char && _focused) {
  296. int x, y;
  297. y = this.top + 2;
  298. x = this.left + 3;
  299. Size s;
  300. if (_caret_pos > _first_char) {
  301. g.measureText(_value[_first_char.._caret_pos],s);
  302. x += s.x;
  303. }
  304. if (x < bounds.right) {
  305. g.drawLine(x,y,x,y+_value_size.y);
  306. }
  307. }
  308. }
  309. else {
  310. int x, y, x2, y2;
  311. Size s;
  312. uint from_pos;
  313. uint to_pos;
  314. if (_sel_start < _caret_pos) {
  315. from_pos = _sel_start;
  316. to_pos = _caret_pos;
  317. }
  318. else {
  319. from_pos = _caret_pos;
  320. to_pos = _sel_start;
  321. }
  322. x = this.left + 3;
  323. y = this.top + 2;
  324. //left side of the selection
  325. if (from_pos < _first_char) {
  326. from_pos = _first_char;
  327. }
  328. g.drawText(x,y,_value[_first_char..from_pos]);
  329. g.measureText(_value[_first_char..from_pos],s);
  330. x += s.x;
  331. g.forecolor = (Color.White);
  332. //draw background rect within the control's bounds
  333. g.measureText(_value[from_pos..to_pos],s);
  334. //get the width of the line above, use that to determine x2, the
  335. //width of the selection rectangle.
  336. //y2 is the height of the line with some room
  337. //x2 is reused, since it determines the position of the next part
  338. //of the string below when printing
  339. x2 = x + s.x;
  340. y2 = y + _value_size.y + 1;
  341. if (x2 >= this.right) {
  342. x2 = this.right - 1;
  343. }
  344. g.drawRect(x,y,x2,y2);
  345. //the selection
  346. g.drawText(x,y,_value[from_pos..to_pos]);
  347. g.forecolor = (Color.Black);
  348. //the right side of the selection
  349. g.drawText(x2, y, _value[to_pos.._value.length]);
  350. }
  351. }
  352. void text(string newTitle) {
  353. _value = newTitle.dup;
  354. }
  355. string text() {
  356. return _value.dup;
  357. }
  358. protected:
  359. string _value;
  360. private:
  361. void RefreshViewport(uint onPos) {
  362. //check to see if onPos is already within viewing area
  363. //check to see what direction we are travelling
  364. //if left... from onPos go backwards a certain distance
  365. //if right... from onPos go forwards a certain distance
  366. //set _first_char accordingly
  367. if (_caret_pos > _value.length) {
  368. _caret_pos = _value.length;
  369. }
  370. Size s;
  371. //the width to move
  372. int movement_width = 45;
  373. int current_movement = 0;
  374. uint i;
  375. Graphics grp = _view.lock();
  376. if (onPos > _first_char) {
  377. //check to see if it is within the viewable area
  378. grp.measureText(_value[_first_char..onPos], s);
  379. if ((s.x + 3) < this.width) {
  380. //we are good
  381. _view.unlock();
  382. return;
  383. }
  384. else {
  385. //we are moving right
  386. //go forward
  387. i = onPos;
  388. while(current_movement < movement_width) {
  389. if (i > _value.length) {
  390. i = _value.length;
  391. break;
  392. }
  393. grp.measureText(_value[i..i+1], s);
  394. current_movement += s.x;
  395. i++;
  396. }
  397. //i is the rightmost character
  398. //go backwards from onPos to find
  399. //the first character
  400. i = onPos;
  401. while(current_movement < this.width) {
  402. if (i<=0) {
  403. i = 0;
  404. break;
  405. }
  406. i--;
  407. grp.measureText(_value[i..i+1],s);
  408. current_movement += s.x;
  409. }
  410. //i is the new first character
  411. _first_char = i + 1;
  412. }
  413. }
  414. else {
  415. //we are moving left
  416. //go backward
  417. i = onPos;
  418. while(current_movement < movement_width) {
  419. if (i<=0) {
  420. i = 0;
  421. break;
  422. }
  423. i--;
  424. grp.measureText(_value[i..i+1],s);
  425. current_movement += s.x;
  426. }
  427. //i is the new first character
  428. _first_char = i;
  429. }
  430. _view.unlock();
  431. }
  432. void InternalSelectionClear() {
  433. //deletes the selection
  434. if (_caret_pos != _sel_start) {
  435. if (_caret_pos < _sel_start) {
  436. //swap the two positions, so that
  437. //m_sel_start is at the beginning of the selection
  438. //and m_caret_pos marks the end
  439. _sel_start ^= _caret_pos;
  440. _caret_pos ^= _sel_start;
  441. _sel_start ^= _caret_pos;
  442. }
  443. string str_partA, str_partB;
  444. // new_string = substring(0 ---> _sel_start)
  445. str_partA = _value.substring(0, _sel_start);
  446. // new_string += substring(_caret_pos ---> <EOS>);
  447. str_partB = _value.substring(_caret_pos);
  448. _value = str_partA;
  449. _value ~= str_partB;
  450. //turn off the selection
  451. //make the selection start the new caret position
  452. _caret_pos = _sel_start;
  453. //load the font, and get the size of the text when drawn
  454. Graphics grp = _view.lock();
  455. //grp.font = (_font);
  456. if (_value.length) {
  457. grp.measureText(_value, _value_size);
  458. }
  459. else {
  460. grp.measureText(" ", _value_size);
  461. _value_size.x = 0;
  462. }
  463. _view.unlock();
  464. }
  465. }
  466. Size _value_size;
  467. Font _font;
  468. uint _first_char; //the first character drawn
  469. uint _caret_pos; //the caret position within the string
  470. uint _caret_on; //if caret is currently drawn
  471. uint _sel_start; //the start of the selection
  472. //_caret_pos is the end
  473. Color _clr_background;
  474. Color _clr_highlight;
  475. Color _clr_outline;
  476. }