textbox.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  1. /*
  2. * File : textbox.c
  3. * This file is part of RT-Thread RTOS
  4. * COPYRIGHT (C) 2006 - 2009, RT-Thread Development Team
  5. *
  6. * The license and distribution terms for this file may be
  7. * found in the file LICENSE in this distribution or at
  8. * http://www.rt-thread.org/license/LICENSE
  9. *
  10. * Change Logs:
  11. * Date Author Notes
  12. * 2009-10-16 Bernard first version
  13. * 2011-01-224 Bernard fix backspace issue.
  14. * 2012-06-09 asml refactor
  15. * 2012-06-17 Grissiom misc cleanup and merge
  16. */
  17. #include <string.h>
  18. #include <rtgui/widgets/textbox.h>
  19. #include <rtgui/widgets/combobox.h>
  20. #include <rtgui/rtgui_system.h>
  21. #include <ctype.h>
  22. static void rtgui_textbox_draw_caret(rtgui_textbox_t *box, rt_uint16_t position);
  23. static rt_bool_t rtgui_textbox_onkey(struct rtgui_object* widget, rtgui_event_t* event);
  24. static rt_bool_t rtgui_textbox_onfocus(struct rtgui_object* widget, rtgui_event_t* event);
  25. static rt_bool_t rtgui_textbox_onunfocus(struct rtgui_object* widget, rtgui_event_t* event);
  26. static void _rtgui_textbox_constructor(rtgui_textbox_t *box)
  27. {
  28. rtgui_rect_t rect;
  29. RTGUI_WIDGET_FLAG(RTGUI_WIDGET(box)) |= RTGUI_WIDGET_FLAG_FOCUSABLE;
  30. rtgui_object_set_event_handler(RTGUI_OBJECT(box), rtgui_textbox_event_handler);
  31. rtgui_widget_set_onfocus(RTGUI_WIDGET(box), rtgui_textbox_onfocus);
  32. rtgui_widget_set_onunfocus(RTGUI_WIDGET(box), rtgui_textbox_onunfocus);
  33. #ifndef RTGUI_USING_SMALL_SIZE
  34. rtgui_widget_set_onkey(RTGUI_WIDGET(box),rtgui_textbox_onkey);
  35. #endif
  36. RTGUI_WIDGET_FOREGROUND(RTGUI_WIDGET(box)) = black;
  37. RTGUI_WIDGET_BACKGROUND(RTGUI_WIDGET(box)) = white;
  38. /* set default text align */
  39. RTGUI_WIDGET_TEXTALIGN(RTGUI_WIDGET(box)) = RTGUI_ALIGN_CENTER_VERTICAL;
  40. /* set proper of control */
  41. box->caret_timer = RT_NULL;
  42. box->caret = RT_NULL;
  43. box->line = box->line_begin = box->position = 0;
  44. box->flag = RTGUI_TEXTBOX_SINGLE;
  45. /* allocate default line buffer */
  46. box->text = RT_NULL;
  47. rtgui_font_get_metrics(RTGUI_WIDGET_FONT(RTGUI_WIDGET(box)), "H", &rect);
  48. box->font_width = rtgui_rect_width(rect);
  49. box->on_enter = RT_NULL;
  50. box->dis_length = 0;
  51. }
  52. static void _rtgui_textbox_deconstructor(rtgui_textbox_t *textbox)
  53. {
  54. if(textbox->text != RT_NULL)
  55. {
  56. rtgui_free(textbox->text);
  57. textbox->text = RT_NULL;
  58. }
  59. if(textbox->caret_timer != RT_NULL)
  60. {
  61. rtgui_timer_destory(textbox->caret_timer);
  62. textbox->caret_timer = RT_NULL;
  63. }
  64. if(textbox->caret != RT_NULL)
  65. {
  66. rtgui_free(textbox->caret);
  67. textbox->caret = RT_NULL;
  68. }
  69. }
  70. DEFINE_CLASS_TYPE(textbox, "textbox",
  71. RTGUI_WIDGET_TYPE,
  72. _rtgui_textbox_constructor,
  73. _rtgui_textbox_deconstructor,
  74. sizeof(struct rtgui_textbox));
  75. static void rtgui_textbox_get_caret_rect(rtgui_textbox_t *box, rtgui_rect_t *rect, rt_uint16_t position)
  76. {
  77. int font_h,box_h;
  78. rtgui_rect_t item_rect;
  79. RT_ASSERT(box != RT_NULL);
  80. rtgui_widget_get_rect(RTGUI_WIDGET(box), rect);
  81. rtgui_font_get_metrics(RTGUI_WIDGET_FONT(RTGUI_WIDGET(box)), "H", &item_rect);
  82. font_h = rtgui_rect_height(item_rect);
  83. box_h = rtgui_rect_height(*rect);
  84. rect->x1 += position * box->font_width + 2;
  85. rect->x2 = rect->x1 + 2;
  86. rect->y1 += (box_h - font_h) / 2;
  87. rect->y2 = rect->y1 + font_h;
  88. }
  89. static void rtgui_textbox_init_caret(rtgui_textbox_t *box, rt_uint16_t position)
  90. {
  91. int x, y;
  92. rtgui_color_t color;
  93. rtgui_rect_t rect;
  94. int ofs = 0;
  95. RT_ASSERT(box != RT_NULL);
  96. if (!RTGUI_WIDGET_IS_FOCUSED(RTGUI_WIDGET(box)))
  97. return;
  98. rtgui_textbox_get_caret_rect(box, &box->caret_rect, position);
  99. rect = box->caret_rect;
  100. rtgui_widget_rect_to_device(RTGUI_WIDGET(box), &rect);
  101. if (box->caret == RT_NULL)
  102. box->caret = rtgui_malloc(rtgui_rect_width(rect)*rtgui_rect_height(rect)*sizeof(rtgui_color_t));
  103. for (x = rect.x1; x < rect.x2; x++)
  104. {
  105. for(y = rect.y1; y < rect.y2; y++)
  106. {
  107. rtgui_graphic_driver_get_default()->ops->get_pixel(&color, x, y);
  108. *(box->caret + ofs) = color;
  109. ofs++;
  110. }
  111. }
  112. }
  113. /* draw caret */
  114. static void rtgui_textbox_draw_caret(rtgui_textbox_t *box, rt_uint16_t position)
  115. {
  116. int x,y;
  117. rtgui_color_t color;
  118. rtgui_rect_t rect;
  119. int ofs = 0;
  120. struct rtgui_dc *dc;
  121. RT_ASSERT(box != RT_NULL);
  122. if (box->caret == RT_NULL)
  123. return;
  124. dc = rtgui_dc_begin_drawing(RTGUI_WIDGET(box));
  125. if (dc == RT_NULL)
  126. return;
  127. rect = box->caret_rect;
  128. for(x = rect.x1; x < rect.x2; x++)
  129. {
  130. for(y = rect.y1; y < rect.y2; y++)
  131. {
  132. color = *(box->caret + ofs);
  133. ofs++;
  134. if(box->flag & RTGUI_TEXTBOX_CARET_SHOW)
  135. {
  136. color = ~color;
  137. }
  138. rtgui_dc_draw_color_point(dc, x, y, color);
  139. }
  140. }
  141. rtgui_dc_end_drawing(dc);
  142. }
  143. static void rtgui_textbox_timeout(rtgui_timer_t* timer, void* parameter)
  144. {
  145. rtgui_textbox_t* box;
  146. box = RTGUI_TEXTBOX(parameter);
  147. /* set caret flag */
  148. if(box->flag & RTGUI_TEXTBOX_CARET_SHOW)
  149. box->flag &= ~RTGUI_TEXTBOX_CARET_SHOW;
  150. else
  151. box->flag |= RTGUI_TEXTBOX_CARET_SHOW;
  152. rtgui_textbox_draw_caret(box,box->position);
  153. }
  154. static void rtgui_textbox_onmouse(rtgui_textbox_t* box, struct rtgui_event_mouse* event)
  155. {
  156. rt_size_t length;
  157. rt_uint16_t posbak = box->position;
  158. RT_ASSERT(box != RT_NULL);
  159. RT_ASSERT(event != RT_NULL);
  160. length = rt_strlen(box->text);
  161. if(event->button & RTGUI_MOUSE_BUTTON_LEFT && event->button & RTGUI_MOUSE_BUTTON_DOWN)
  162. {
  163. rt_int32_t x;
  164. /* single line text */
  165. /* set caret position */
  166. x = event->x - RTGUI_WIDGET(box)->extent.x1;
  167. if(x < 0)
  168. {
  169. box->position = 0;
  170. }
  171. else if(x > length * box->font_width)
  172. {
  173. box->position = length;
  174. }
  175. else
  176. {
  177. box->position = x / box->font_width;
  178. }
  179. if(box->flag & RTGUI_TEXTBOX_CARET_SHOW)
  180. {
  181. if(box->caret_timer != RT_NULL)
  182. rtgui_timer_stop(box->caret_timer);
  183. box->flag &= ~RTGUI_TEXTBOX_CARET_SHOW;
  184. rtgui_textbox_draw_caret(box, posbak);
  185. if(box->caret_timer != RT_NULL)
  186. rtgui_timer_start(box->caret_timer);
  187. }
  188. rtgui_textbox_init_caret(box, box->position);
  189. box->flag |= RTGUI_TEXTBOX_CARET_SHOW;
  190. rtgui_textbox_draw_caret(box,box->position);
  191. }
  192. }
  193. static rt_bool_t rtgui_textbox_onkey(struct rtgui_object* widget, rtgui_event_t* event)
  194. {
  195. rtgui_textbox_t* box = RTGUI_TEXTBOX(widget);
  196. struct rtgui_event_kbd* ekbd = (struct rtgui_event_kbd*)event;
  197. rt_size_t length;
  198. rt_uint16_t posbak = box->position;
  199. RT_ASSERT(box != RT_NULL);
  200. RT_ASSERT(ekbd != RT_NULL);
  201. /* handle the key at down time and nothing to do with up */
  202. if (RTGUI_KBD_IS_UP(ekbd))
  203. return RT_TRUE;
  204. if (box->dis_length == 0)
  205. {
  206. rtgui_rect_t rect;
  207. rtgui_widget_get_rect(RTGUI_WIDGET(box), &rect);
  208. if (box->font_width == 0)
  209. return RT_FALSE;
  210. box->dis_length = (rtgui_rect_width(rect) - 5) / box->font_width;
  211. }
  212. length = rt_strlen(box->text);
  213. if(ekbd->key == RTGUIK_DELETE)
  214. {/* delete latter character */
  215. if(box->position == length - 1)
  216. {
  217. box->text[box->position] = '\0';
  218. }
  219. else
  220. {
  221. char *c;
  222. /* remove character */
  223. for(c = &box->text[box->position]; c[1] != '\0'; c++)
  224. *c = c[1];
  225. *c = '\0';
  226. }
  227. }
  228. else if(ekbd->key == RTGUIK_BACKSPACE)
  229. {/* delete front character */
  230. if(box->position == length - 1)
  231. {
  232. box->text[box->position] = '\0';
  233. box->position --;
  234. }
  235. else if(box->position != 0)
  236. {
  237. /* remove current character */
  238. if(box->position != 0)
  239. {
  240. char *c;
  241. /* remove character */
  242. for(c = &box->text[box->position - 1]; c[1] != '\0'; c++)
  243. *c = c[1];
  244. *c = '\0';
  245. }
  246. box->position --;
  247. }
  248. }
  249. else if(ekbd->key == RTGUIK_LEFT)
  250. {/* move to prev */
  251. if(box->position > 0)
  252. {
  253. box->position --;
  254. }
  255. }
  256. else if(ekbd->key == RTGUIK_RIGHT)
  257. {/* move to next */
  258. if(box->position < length)
  259. {
  260. box->position ++;
  261. }
  262. }
  263. else if(ekbd->key == RTGUIK_HOME)
  264. {/* move cursor to start */
  265. box->position = 0;
  266. }
  267. else if(ekbd->key == RTGUIK_END)
  268. {/* move cursor to end */
  269. box->position = length;
  270. }
  271. else if(ekbd->key == RTGUIK_RETURN)
  272. {
  273. if(box->on_enter != RT_NULL)
  274. {
  275. box->on_enter(box, event);
  276. }
  277. }
  278. else if(ekbd->key == RTGUIK_NUMLOCK)
  279. {
  280. /* change numlock state */
  281. /*
  282. extern void update_number_lock(void);
  283. update_number_lock();
  284. */
  285. }
  286. else
  287. {
  288. if(isprint(ekbd->key))
  289. {/* it's may print character */
  290. /* no buffer on this line */
  291. if(box->flag & RTGUI_TEXTBOX_DIGIT)
  292. {/* only input digit */
  293. if(!isdigit(ekbd->key))
  294. {/* exception: '.' and '-' */
  295. if(ekbd->key != '.' && ekbd->key !='-')return RT_FALSE;
  296. if(ekbd->key == '.' && strchr(box->text,'.'))return RT_FALSE;
  297. if(ekbd->key == '-')
  298. {
  299. if(length+1 > box->line_length) return RT_FALSE;
  300. if(length+1 > box->dis_length) return RT_FALSE;
  301. if(strchr(box->text,'-'))
  302. {
  303. char* c;
  304. for(c = &box->text[0]; c != &box->text[length]; c++)
  305. *c = *(c+1);
  306. box->text[length] = '\0';
  307. box->position --;
  308. goto _exit;
  309. }
  310. else
  311. {
  312. char* c;
  313. for(c = &box->text[length]; c != &box->text[0]; c--)
  314. *c = *(c-1);
  315. box->text[0] = '-';
  316. box->text[length+1] = '\0';
  317. box->position ++;
  318. goto _exit;
  319. }
  320. }
  321. }
  322. //rt_kprintf("%c ",ekbd->key);//debug printf
  323. }
  324. if(length+1 > box->line_length) return RT_FALSE;
  325. if(length+1 > box->dis_length) return RT_FALSE;
  326. if(box->position <= length-1)
  327. {
  328. char* c;
  329. for(c = &box->text[length]; c != &box->text[box->position]; c--)
  330. *c = *(c-1);
  331. box->text[length+1] = '\0';
  332. }
  333. box->text[box->position] = ekbd->key;
  334. box->position ++;
  335. }
  336. }
  337. _exit:
  338. if(box->flag & RTGUI_TEXTBOX_CARET_SHOW)
  339. {
  340. if(box->caret_timer != RT_NULL)
  341. rtgui_timer_stop(box->caret_timer);
  342. box->flag &= ~RTGUI_TEXTBOX_CARET_SHOW;
  343. rtgui_textbox_draw_caret(box, posbak);/* refresh it */
  344. if(box->caret_timer != RT_NULL)
  345. rtgui_timer_start(box->caret_timer);
  346. }
  347. /* re-draw text box */
  348. rtgui_textbox_ondraw(box);
  349. rtgui_textbox_init_caret(box, box->position);
  350. box->flag |= RTGUI_TEXTBOX_CARET_SHOW;
  351. rtgui_textbox_draw_caret(box,box->position);
  352. return RT_TRUE;
  353. }
  354. static rt_bool_t rtgui_textbox_onfocus(struct rtgui_object* widget, rtgui_event_t* event)
  355. {
  356. rtgui_textbox_t* box = RTGUI_TEXTBOX(widget);
  357. /* if there is already a timer, don't create another one. */
  358. if (box->caret_timer == RT_NULL)
  359. {
  360. box->caret_timer = rtgui_timer_create(100, RT_TIMER_FLAG_PERIODIC,rtgui_textbox_timeout, box);
  361. /* set caret to show */
  362. box->flag |= RTGUI_TEXTBOX_CARET_SHOW;
  363. /* start caret timer */
  364. if(box->caret_timer != RT_NULL)
  365. rtgui_timer_start(box->caret_timer);
  366. }
  367. return RT_TRUE;
  368. }
  369. static rt_bool_t rtgui_textbox_onunfocus(struct rtgui_object* widget, rtgui_event_t* event)
  370. {
  371. rtgui_textbox_t* box = RTGUI_TEXTBOX(widget);
  372. /* stop caret timer */
  373. if(box->caret_timer != RT_NULL)
  374. {
  375. rtgui_timer_stop(box->caret_timer);
  376. rtgui_timer_destory(box->caret_timer);
  377. box->caret_timer = RT_NULL;
  378. }
  379. /* set caret to hide */
  380. box->flag &= ~RTGUI_TEXTBOX_CARET_SHOW;
  381. rtgui_textbox_draw_caret(box,box->position);
  382. if(box->on_enter != RT_NULL)
  383. box->on_enter(box,event);
  384. return RT_TRUE;
  385. }
  386. rtgui_textbox_t* rtgui_textbox_create(const char* text, rt_uint32_t flag)
  387. {
  388. rtgui_textbox_t* box;
  389. box = (struct rtgui_textbox*)rtgui_widget_create(RTGUI_TEXTBOX_TYPE);
  390. if(box != RT_NULL)
  391. {
  392. /* allocate default line buffer */
  393. rtgui_textbox_set_value(box, text);
  394. box->flag = flag;
  395. }
  396. return box;
  397. }
  398. void rtgui_textbox_destroy(rtgui_textbox_t* box)
  399. {
  400. rtgui_widget_destroy(RTGUI_WIDGET(box));
  401. }
  402. void rtgui_textbox_ondraw(rtgui_textbox_t* box)
  403. {
  404. /* draw button */
  405. rtgui_rect_t rect;
  406. struct rtgui_dc* dc;
  407. rtgui_color_t fc;
  408. RT_ASSERT(box != RT_NULL);
  409. /* begin drawing */
  410. dc = rtgui_dc_begin_drawing(RTGUI_WIDGET(box));
  411. if (dc == RT_NULL)
  412. return;
  413. /* get widget rect */
  414. rtgui_widget_get_rect(RTGUI_WIDGET(box), &rect);
  415. fc = RTGUI_WIDGET_FOREGROUND(RTGUI_WIDGET(box));
  416. rtgui_rect_inflate(&rect, -1);
  417. /* fill widget rect with white color */
  418. RTGUI_WIDGET_BACKGROUND(RTGUI_WIDGET(box)) = white;
  419. rtgui_dc_fill_rect(dc,&rect);
  420. rtgui_rect_inflate(&rect, 1);
  421. /* draw border */
  422. RTGUI_WIDGET_FOREGROUND(RTGUI_WIDGET(box)) = RTGUI_RGB(123, 158, 189);
  423. rtgui_dc_draw_rect(dc, &rect);
  424. /* draw text */
  425. RTGUI_WIDGET_FOREGROUND(RTGUI_WIDGET(box)) = fc;
  426. if(box->text != RT_NULL)
  427. {
  428. rect.x1 += RTGUI_WIDGET_DEFAULT_MARGIN;
  429. /* draw single text */
  430. if(box->flag & RTGUI_TEXTBOX_MASK)
  431. {
  432. /* draw '*' */
  433. rt_size_t len = rt_strlen(box->text);
  434. if(len > 0)
  435. {
  436. char *text_mask = rtgui_malloc(len + 1);
  437. rt_memset(text_mask, '*', len + 1);
  438. text_mask[len] = 0;
  439. rtgui_dc_draw_text(dc, text_mask, &rect);
  440. rtgui_free(text_mask);
  441. }
  442. }
  443. else
  444. {
  445. rtgui_dc_draw_text(dc, box->text, &rect);
  446. }
  447. }
  448. rtgui_dc_end_drawing(dc);
  449. }
  450. /* set textbox text */
  451. void rtgui_textbox_set_value(rtgui_textbox_t* box, const char* text)
  452. {
  453. if(box->text != RT_NULL)
  454. {/* yet exist something */
  455. /* free the old text */
  456. rtgui_free(box->text);
  457. box->text = RT_NULL;
  458. }
  459. /* no something */
  460. box->line_length = ((rt_strlen(text)+1)/RTGUI_TEXTBOX_LINE_MAX+1)*RTGUI_TEXTBOX_LINE_MAX;
  461. /* allocate line buffer */
  462. box->text = rtgui_malloc(box->line_length);
  463. rt_memset(box->text, 0, box->line_length);
  464. /* copy text */
  465. rt_memcpy(box->text, text, rt_strlen(text) + 1);
  466. /* set current position */
  467. box->position = rt_strlen(text);
  468. }
  469. const char* rtgui_textbox_get_value(rtgui_textbox_t* box)
  470. {
  471. return (const char*)box->text;
  472. }
  473. void rtgui_textbox_set_line_length(rtgui_textbox_t* box, rt_size_t length)
  474. {
  475. rt_uint8_t* new_line;
  476. RT_ASSERT(box != RT_NULL);
  477. /* invalid length */
  478. if(length <= 0)
  479. return;
  480. new_line = rtgui_malloc(length);
  481. if(length < box->line_length)
  482. {
  483. rt_memcpy(new_line, box->text, length - 1);
  484. new_line[length] = '\0';
  485. }
  486. else
  487. {
  488. rt_memcpy(new_line, (const char*)box->text, rt_strlen((const char*)box->text));
  489. }
  490. /* set line length */
  491. box->line_length = length;
  492. }
  493. /* get textbox text area */
  494. void rtgui_textbox_get_edit_rect(rtgui_textbox_t *box,rtgui_rect_t *rect)
  495. {
  496. rtgui_widget_get_rect(RTGUI_WIDGET(box), rect);
  497. rtgui_rect_inflate(rect,-1);
  498. }
  499. rt_bool_t rtgui_textbox_event_handler(struct rtgui_object *object, rtgui_event_t* event)
  500. {
  501. rtgui_widget_t *widget = RTGUI_WIDGET(object);
  502. rtgui_textbox_t* box = RTGUI_TEXTBOX(object);
  503. switch (event->type)
  504. {
  505. case RTGUI_EVENT_PAINT:
  506. #ifndef RTGUI_USING_SMALL_SIZE
  507. if(widget->on_draw != RT_NULL)
  508. widget->on_draw(RTGUI_OBJECT(widget), event);
  509. else
  510. #endif
  511. rtgui_textbox_ondraw(box);
  512. break;
  513. case RTGUI_EVENT_MOUSE_BUTTON:
  514. #ifndef RTGUI_USING_SMALL_SIZE
  515. if(widget->on_mouseclick != RT_NULL)
  516. widget->on_mouseclick(RTGUI_OBJECT(widget), event);
  517. else
  518. #endif
  519. rtgui_textbox_onmouse(box, (struct rtgui_event_mouse*)event);
  520. return RT_TRUE;
  521. case RTGUI_EVENT_KBD:
  522. #ifndef RTGUI_USING_SMALL_SIZE
  523. if(widget->on_key != RT_NULL)
  524. widget->on_key(RTGUI_OBJECT(widget), event);
  525. else
  526. #endif
  527. rtgui_textbox_onkey(RTGUI_OBJECT(box), (struct rtgui_event*)event);
  528. return RT_TRUE;
  529. default:
  530. return rtgui_widget_event_handler(RTGUI_OBJECT(widget),event);
  531. }
  532. return RT_FALSE;
  533. }