textbox.c 14 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(box) = black;
  37. RTGUI_WIDGET_BACKGROUND(box) = white;
  38. /* set default text align */
  39. RTGUI_WIDGET_TEXTALIGN(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(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(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(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 == 0)
  231. return RT_FALSE;
  232. else if(box->position == length)
  233. {
  234. box->text[box->position-1] = '\0';
  235. box->position --;
  236. }
  237. else if(box->position != 0)
  238. {
  239. /* remove current character */
  240. if(box->position != 0)
  241. {
  242. char *c;
  243. /* remove character */
  244. for(c = &box->text[box->position - 1]; c[1] != '\0'; c++)
  245. *c = c[1];
  246. *c = '\0';
  247. }
  248. box->position --;
  249. }
  250. }
  251. else if(ekbd->key == RTGUIK_LEFT)
  252. {/* move to prev */
  253. if(box->position > 0)
  254. {
  255. box->position --;
  256. }
  257. }
  258. else if(ekbd->key == RTGUIK_RIGHT)
  259. {/* move to next */
  260. if(box->position < length)
  261. {
  262. box->position ++;
  263. }
  264. }
  265. else if(ekbd->key == RTGUIK_HOME)
  266. {/* move cursor to start */
  267. box->position = 0;
  268. }
  269. else if(ekbd->key == RTGUIK_END)
  270. {/* move cursor to end */
  271. box->position = length;
  272. }
  273. else if(ekbd->key == RTGUIK_RETURN)
  274. {
  275. if(box->on_enter != RT_NULL)
  276. {
  277. box->on_enter(box, event);
  278. }
  279. }
  280. else if(ekbd->key == RTGUIK_NUMLOCK)
  281. {
  282. /* change numlock state */
  283. /*
  284. extern void update_number_lock(void);
  285. update_number_lock();
  286. */
  287. }
  288. else
  289. {
  290. if(isprint(ekbd->key))
  291. {/* it's may print character */
  292. /* no buffer on this line */
  293. if(box->flag & RTGUI_TEXTBOX_DIGIT)
  294. {/* only input digit */
  295. if(!isdigit(ekbd->key))
  296. {/* exception: '.' and '-' */
  297. if(ekbd->key != '.' && ekbd->key !='-')return RT_FALSE;
  298. if(ekbd->key == '.' && strchr(box->text,'.'))return RT_FALSE;
  299. if(ekbd->key == '-')
  300. {
  301. if(length+1 > box->line_length) return RT_FALSE;
  302. if(length+1 > box->dis_length) return RT_FALSE;
  303. if(strchr(box->text,'-'))
  304. {
  305. char* c;
  306. for(c = &box->text[0]; c != &box->text[length]; c++)
  307. *c = *(c+1);
  308. box->text[length] = '\0';
  309. box->position --;
  310. goto _exit;
  311. }
  312. else
  313. {
  314. char* c;
  315. for(c = &box->text[length]; c != &box->text[0]; c--)
  316. *c = *(c-1);
  317. box->text[0] = '-';
  318. box->text[length+1] = '\0';
  319. box->position ++;
  320. goto _exit;
  321. }
  322. }
  323. }
  324. }
  325. if(length+1 > box->line_length) return RT_FALSE;
  326. if(length+1 > box->dis_length) return RT_FALSE;
  327. if(box->position <= length-1)
  328. {
  329. char* c;
  330. for(c = &box->text[length]; c != &box->text[box->position]; c--)
  331. *c = *(c-1);
  332. box->text[length+1] = '\0';
  333. }
  334. box->text[box->position] = ekbd->key;
  335. box->position ++;
  336. }
  337. }
  338. _exit:
  339. if(box->flag & RTGUI_TEXTBOX_CARET_SHOW)
  340. {
  341. if(box->caret_timer != RT_NULL)
  342. rtgui_timer_stop(box->caret_timer);
  343. box->flag &= ~RTGUI_TEXTBOX_CARET_SHOW;
  344. rtgui_textbox_draw_caret(box, posbak);/* refresh it */
  345. if(box->caret_timer != RT_NULL)
  346. rtgui_timer_start(box->caret_timer);
  347. }
  348. /* re-draw text box */
  349. rtgui_textbox_ondraw(box);
  350. rtgui_textbox_init_caret(box, box->position);
  351. box->flag |= RTGUI_TEXTBOX_CARET_SHOW;
  352. rtgui_textbox_draw_caret(box,box->position);
  353. return RT_TRUE;
  354. }
  355. static rt_bool_t rtgui_textbox_onfocus(struct rtgui_object* widget, rtgui_event_t* event)
  356. {
  357. rtgui_textbox_t* box = RTGUI_TEXTBOX(widget);
  358. /* if there is already a timer, don't create another one. */
  359. if (box->caret_timer == RT_NULL)
  360. {
  361. box->caret_timer = rtgui_timer_create(50, RT_TIMER_FLAG_PERIODIC,rtgui_textbox_timeout, box);
  362. /* set caret to show */
  363. box->flag |= RTGUI_TEXTBOX_CARET_SHOW;
  364. /* start caret timer */
  365. if(box->caret_timer != RT_NULL)
  366. rtgui_timer_start(box->caret_timer);
  367. }
  368. return RT_TRUE;
  369. }
  370. static rt_bool_t rtgui_textbox_onunfocus(struct rtgui_object* widget, rtgui_event_t* event)
  371. {
  372. rtgui_textbox_t* box = RTGUI_TEXTBOX(widget);
  373. /* stop caret timer */
  374. if(box->caret_timer != RT_NULL)
  375. {
  376. rtgui_timer_stop(box->caret_timer);
  377. rtgui_timer_destory(box->caret_timer);
  378. box->caret_timer = RT_NULL;
  379. }
  380. /* set caret to hide */
  381. box->flag &= ~RTGUI_TEXTBOX_CARET_SHOW;
  382. rtgui_textbox_draw_caret(box,box->position);
  383. if(box->on_enter != RT_NULL)
  384. box->on_enter(box,event);
  385. return RT_TRUE;
  386. }
  387. rtgui_textbox_t* rtgui_textbox_create(const char* text, rt_uint32_t flag)
  388. {
  389. rtgui_textbox_t* box;
  390. box = (struct rtgui_textbox*)rtgui_widget_create(RTGUI_TEXTBOX_TYPE);
  391. if(box != RT_NULL)
  392. {
  393. /* allocate default line buffer */
  394. rtgui_textbox_set_value(box, text);
  395. box->flag = flag;
  396. }
  397. return box;
  398. }
  399. void rtgui_textbox_destroy(rtgui_textbox_t* box)
  400. {
  401. rtgui_widget_destroy(RTGUI_WIDGET(box));
  402. }
  403. void rtgui_textbox_ondraw(rtgui_textbox_t* box)
  404. {
  405. /* draw button */
  406. rtgui_rect_t rect;
  407. struct rtgui_dc* dc;
  408. rtgui_color_t fc;
  409. RT_ASSERT(box != RT_NULL);
  410. /* begin drawing */
  411. dc = rtgui_dc_begin_drawing(RTGUI_WIDGET(box));
  412. if (dc == RT_NULL)
  413. return;
  414. /* get widget rect */
  415. rtgui_widget_get_rect(RTGUI_WIDGET(box), &rect);
  416. fc = RTGUI_WIDGET_FOREGROUND(box);
  417. rtgui_rect_inflate(&rect, -1);
  418. /* fill widget rect with white color */
  419. RTGUI_WIDGET_BACKGROUND(box) = white;
  420. rtgui_dc_fill_rect(dc,&rect);
  421. rtgui_rect_inflate(&rect, 1);
  422. /* draw border */
  423. RTGUI_WIDGET_FOREGROUND(box) = RTGUI_RGB(123, 158, 189);
  424. rtgui_dc_draw_rect(dc, &rect);
  425. /* draw text */
  426. RTGUI_WIDGET_FOREGROUND(box) = fc;
  427. if(box->text != RT_NULL)
  428. {
  429. rect.x1 += RTGUI_WIDGET_DEFAULT_MARGIN;
  430. /* draw single text */
  431. if(box->flag & RTGUI_TEXTBOX_MASK)
  432. {
  433. /* draw '*' */
  434. rt_size_t len = rt_strlen(box->text);
  435. if(len > 0)
  436. {
  437. char *text_mask = rtgui_malloc(len + 1);
  438. rt_memset(text_mask, '*', len + 1);
  439. text_mask[len] = 0;
  440. rtgui_dc_draw_text(dc, text_mask, &rect);
  441. rtgui_free(text_mask);
  442. }
  443. }
  444. else
  445. {
  446. rtgui_dc_draw_text(dc, box->text, &rect);
  447. }
  448. }
  449. rtgui_dc_end_drawing(dc);
  450. }
  451. /* set textbox text */
  452. void rtgui_textbox_set_value(rtgui_textbox_t* box, const char* text)
  453. {
  454. if(box->text != RT_NULL)
  455. {/* yet exist something */
  456. /* free the old text */
  457. rtgui_free(box->text);
  458. box->text = RT_NULL;
  459. }
  460. /* no something */
  461. box->line_length = ((rt_strlen(text)+1)/RTGUI_TEXTBOX_LINE_MAX+1)*RTGUI_TEXTBOX_LINE_MAX;
  462. /* allocate line buffer */
  463. box->text = rtgui_malloc(box->line_length);
  464. rt_memset(box->text, 0, box->line_length);
  465. /* copy text */
  466. rt_memcpy(box->text, text, rt_strlen(text) + 1);
  467. /* set current position */
  468. box->position = rt_strlen(text);
  469. }
  470. const char* rtgui_textbox_get_value(rtgui_textbox_t* box)
  471. {
  472. return (const char*)box->text;
  473. }
  474. void rtgui_textbox_set_line_length(rtgui_textbox_t* box, rt_size_t length)
  475. {
  476. rt_uint8_t* new_line;
  477. RT_ASSERT(box != RT_NULL);
  478. /* invalid length */
  479. if(length <= 0)
  480. return;
  481. new_line = rtgui_malloc(length);
  482. if(length < box->line_length)
  483. {
  484. rt_memcpy(new_line, box->text, length - 1);
  485. new_line[length] = '\0';
  486. }
  487. else
  488. {
  489. rt_memcpy(new_line, (const char*)box->text, rt_strlen((const char*)box->text));
  490. }
  491. /* set line length */
  492. box->line_length = length;
  493. }
  494. /* get textbox text area */
  495. void rtgui_textbox_get_edit_rect(rtgui_textbox_t *box,rtgui_rect_t *rect)
  496. {
  497. rtgui_widget_get_rect(RTGUI_WIDGET(box), rect);
  498. rtgui_rect_inflate(rect,-1);
  499. }
  500. rt_bool_t rtgui_textbox_event_handler(struct rtgui_object *object, rtgui_event_t* event)
  501. {
  502. rtgui_widget_t *widget = RTGUI_WIDGET(object);
  503. rtgui_textbox_t* box = RTGUI_TEXTBOX(object);
  504. switch (event->type)
  505. {
  506. case RTGUI_EVENT_PAINT:
  507. #ifndef RTGUI_USING_SMALL_SIZE
  508. if(widget->on_draw != RT_NULL)
  509. widget->on_draw(RTGUI_OBJECT(widget), event);
  510. else
  511. #endif
  512. rtgui_textbox_ondraw(box);
  513. break;
  514. case RTGUI_EVENT_MOUSE_BUTTON:
  515. #ifndef RTGUI_USING_SMALL_SIZE
  516. if(widget->on_mouseclick != RT_NULL)
  517. widget->on_mouseclick(RTGUI_OBJECT(widget), event);
  518. else
  519. #endif
  520. rtgui_textbox_onmouse(box, (struct rtgui_event_mouse*)event);
  521. return RT_TRUE;
  522. case RTGUI_EVENT_KBD:
  523. #ifndef RTGUI_USING_SMALL_SIZE
  524. if(widget->on_key != RT_NULL)
  525. widget->on_key(RTGUI_OBJECT(widget), event);
  526. else
  527. #endif
  528. rtgui_textbox_onkey(RTGUI_OBJECT(box), (struct rtgui_event*)event);
  529. return RT_TRUE;
  530. default:
  531. return rtgui_widget_event_handler(RTGUI_OBJECT(widget),event);
  532. }
  533. return RT_FALSE;
  534. }