Changes in uspace/lib/ui/src/entry.c [db3895d:6df564c] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/lib/ui/src/entry.c
rdb3895d r6df564c 37 37 */ 38 38 39 #include <clipboard.h> 39 40 #include <errno.h> 40 41 #include <gfx/context.h> 42 #include <gfx/cursor.h> 41 43 #include <gfx/render.h> 42 44 #include <gfx/text.h> 45 #include <macros.h> 43 46 #include <stdlib.h> 44 47 #include <str.h> … … 53 56 static void ui_entry_ctl_destroy(void *); 54 57 static errno_t ui_entry_ctl_paint(void *); 58 static ui_evclaim_t ui_entry_ctl_kbd_event(void *, kbd_event_t *); 55 59 static ui_evclaim_t ui_entry_ctl_pos_event(void *, pos_event_t *); 56 60 … … 59 63 ui_entry_vpad = 4, 60 64 ui_entry_hpad_text = 1, 61 ui_entry_vpad_text = 0 65 ui_entry_vpad_text = 0, 66 ui_entry_cursor_overshoot = 1, 67 ui_entry_cursor_width = 2, 68 ui_entry_sel_hpad = 0, 69 ui_entry_sel_vpad = 2, 70 /** Additional amount to scroll to the left after revealing cursor */ 71 ui_entry_left_scroll_margin = 30 62 72 }; 63 73 … … 66 76 .destroy = ui_entry_ctl_destroy, 67 77 .paint = ui_entry_ctl_paint, 78 .kbd_event = ui_entry_ctl_kbd_event, 68 79 .pos_event = ui_entry_ctl_pos_event 69 80 }; … … 102 113 entry->halign = gfx_halign_left; 103 114 *rentry = entry; 115 104 116 return EOK; 105 117 } … … 146 158 { 147 159 entry->halign = halign; 160 ui_entry_scroll_update(entry, true); 161 ui_entry_paint(entry); 162 } 163 164 /** Set text entry read-only flag. 165 * 166 * @param entry Text entry 167 * @param read_only True iff entry is to be read-only. 168 */ 169 void ui_entry_set_read_only(ui_entry_t *entry, bool read_only) 170 { 171 entry->read_only = read_only; 148 172 } 149 173 … … 164 188 free(entry->text); 165 189 entry->text = tcopy; 190 entry->pos = str_size(text); 191 entry->sel_start = entry->pos; 192 193 ui_entry_scroll_update(entry, false); 194 ui_entry_paint(entry); 166 195 167 196 return EOK; 168 197 } 169 198 199 /** Get entry text. 200 * 201 * @return Pointer to entry text. 202 */ 203 const char *ui_entry_get_text(ui_entry_t *entry) 204 { 205 return entry->text; 206 } 207 208 /** Paint cursor. 209 * 210 * @param entry Text entry 211 * @param pos Cursor position (top-left corner of next character) 212 * @return EOK on success or an error code 213 */ 214 static errno_t ui_entry_paint_cursor(ui_entry_t *entry, gfx_coord2_t *pos) 215 { 216 ui_resource_t *res; 217 gfx_rect_t rect; 218 gfx_font_metrics_t metrics; 219 errno_t rc; 220 221 res = ui_window_get_res(entry->window); 222 223 if (res->textmode) { 224 rc = gfx_cursor_set_pos(res->gc, pos); 225 return rc; 226 } 227 228 gfx_font_get_metrics(res->font, &metrics); 229 230 rect.p0.x = pos->x; 231 rect.p0.y = pos->y - ui_entry_cursor_overshoot; 232 rect.p1.x = pos->x + ui_entry_cursor_width; 233 rect.p1.y = pos->y + metrics.ascent + metrics.descent + 1 + 234 ui_entry_cursor_overshoot; 235 236 rc = gfx_set_color(res->gc, res->entry_fg_color); 237 if (rc != EOK) 238 goto error; 239 240 rc = gfx_fill_rect(res->gc, &rect); 241 if (rc != EOK) 242 goto error; 243 244 return EOK; 245 error: 246 return rc; 247 } 248 249 /** Return width of text before cursor. 250 * 251 * @param entry Text entry 252 * @return Widht of text before cursor 253 */ 254 static gfx_coord_t ui_entry_lwidth(ui_entry_t *entry) 255 { 256 ui_resource_t *res; 257 uint8_t tmp; 258 gfx_coord_t width; 259 260 res = ui_window_get_res(entry->window); 261 262 tmp = entry->text[entry->pos]; 263 264 entry->text[entry->pos] = '\0'; 265 width = gfx_text_width(res->font, entry->text); 266 entry->text[entry->pos] = tmp; 267 268 return width; 269 } 270 170 271 /** Paint text entry. 171 272 * … … 176 277 { 177 278 ui_resource_t *res; 279 ui_entry_geom_t geom; 178 280 gfx_text_fmt_t fmt; 179 281 gfx_coord2_t pos; 180 gfx_ coord_t hpad;181 gfx_coord _t vpad;282 gfx_text_fmt_t cfmt; 283 gfx_coord2_t cpos; 182 284 gfx_rect_t inside; 285 unsigned off1, off2; 286 gfx_rect_t sel; 287 char c; 183 288 errno_t rc; 184 289 185 290 res = ui_window_get_res(entry->window); 186 291 187 if (res->textmode) { 188 hpad = ui_entry_hpad_text; 189 vpad = ui_entry_vpad_text; 190 } else { 191 hpad = ui_entry_hpad; 192 vpad = ui_entry_vpad; 193 } 292 ui_entry_get_geom(entry, &geom); 194 293 195 294 if (res->textmode == false) { … … 212 311 goto error; 213 312 214 switch (entry->halign) { 215 case gfx_halign_left: 216 case gfx_halign_justify: 217 pos.x = inside.p0.x + hpad; 218 break; 219 case gfx_halign_center: 220 pos.x = (inside.p0.x + inside.p1.x) / 2; 221 break; 222 case gfx_halign_right: 223 pos.x = inside.p1.x - hpad - 1; 224 break; 225 } 226 227 pos.y = inside.p0.y + vpad; 313 pos = geom.text_pos; 228 314 229 315 gfx_text_fmt_init(&fmt); 230 316 fmt.color = res->entry_fg_color; 231 fmt.halign = entry->halign;317 fmt.halign = gfx_halign_left; 232 318 fmt.valign = gfx_valign_top; 233 319 320 rc = gfx_set_clip_rect(res->gc, &inside); 321 if (rc != EOK) 322 goto error; 323 324 off1 = min(entry->pos, entry->sel_start); 325 off2 = max(entry->pos, entry->sel_start); 326 327 /* Render initial segment before start of selection */ 328 c = entry->text[off1]; 329 entry->text[off1] = '\0'; 330 234 331 rc = gfx_puttext(res->font, &pos, &fmt, entry->text); 332 if (rc != EOK) { 333 (void) gfx_set_clip_rect(res->gc, NULL); 334 goto error; 335 } 336 337 gfx_text_cont(res->font, &pos, &fmt, entry->text, &cpos, &cfmt); 338 entry->text[off1] = c; 339 340 /* Render selected text */ 341 342 if (off1 != off2) { 343 c = entry->text[off2]; 344 entry->text[off2] = '\0'; 345 cfmt.color = res->entry_sel_text_fg_color; 346 347 gfx_text_rect(res->font, &cpos, &cfmt, entry->text + off1, &sel); 348 sel.p0.x -= ui_entry_sel_hpad; 349 sel.p0.y -= ui_entry_sel_vpad; 350 sel.p1.x += ui_entry_sel_hpad; 351 sel.p1.y += ui_entry_sel_vpad; 352 353 rc = gfx_set_color(res->gc, res->entry_sel_text_bg_color); 354 if (rc != EOK) 355 goto error; 356 357 rc = gfx_fill_rect(res->gc, &sel); 358 if (rc != EOK) 359 goto error; 360 361 rc = gfx_puttext(res->font, &cpos, &cfmt, entry->text + off1); 362 if (rc != EOK) { 363 (void) gfx_set_clip_rect(res->gc, NULL); 364 goto error; 365 } 366 367 gfx_text_cont(res->font, &cpos, &cfmt, entry->text + off1, 368 &cpos, &cfmt); 369 370 entry->text[off2] = c; 371 } 372 373 /* Render trailing, non-selected text */ 374 cfmt.color = res->entry_fg_color; 375 376 rc = gfx_puttext(res->font, &cpos, &cfmt, entry->text + off2); 377 if (rc != EOK) { 378 (void) gfx_set_clip_rect(res->gc, NULL); 379 goto error; 380 } 381 382 if (entry->active) { 383 /* Cursor */ 384 pos.x += ui_entry_lwidth(entry); 385 386 rc = ui_entry_paint_cursor(entry, &pos); 387 if (rc != EOK) { 388 (void) gfx_set_clip_rect(res->gc, NULL); 389 goto error; 390 } 391 } 392 393 rc = gfx_set_clip_rect(res->gc, NULL); 235 394 if (rc != EOK) 236 395 goto error; … … 245 404 } 246 405 406 /** Find position in text entry. 407 * 408 * @param entry Text entry 409 * @param fpos Position for which we need to find text offset 410 * @return Corresponding byte offset in entry text 411 */ 412 size_t ui_entry_find_pos(ui_entry_t *entry, gfx_coord2_t *fpos) 413 { 414 ui_resource_t *res; 415 ui_entry_geom_t geom; 416 gfx_text_fmt_t fmt; 417 418 res = ui_window_get_res(entry->window); 419 420 ui_entry_get_geom(entry, &geom); 421 422 gfx_text_fmt_init(&fmt); 423 fmt.halign = gfx_halign_left; 424 fmt.valign = gfx_valign_top; 425 426 return gfx_text_find_pos(res->font, &geom.text_pos, &fmt, 427 entry->text, fpos); 428 } 429 247 430 /** Destroy text entry control. 248 431 * … … 268 451 } 269 452 270 /** Handle text entry control position event. 271 * 272 * @param arg Argument (ui_entry_t *) 453 /** Delete selected text. 454 * 455 * @param entry Text entry 456 */ 457 void ui_entry_delete_sel(ui_entry_t *entry) 458 { 459 size_t off1; 460 size_t off2; 461 462 off1 = min(entry->sel_start, entry->pos); 463 off2 = max(entry->sel_start, entry->pos); 464 465 memmove(entry->text + off1, entry->text + off2, 466 str_size(entry->text + off2) + 1); 467 468 entry->pos = off1; 469 entry->sel_start = off1; 470 ui_entry_scroll_update(entry, false); 471 ui_entry_paint(entry); 472 } 473 474 /** Insert string at cursor position. 475 * 476 * @param entry Text entry 477 * @param str String 478 * @return EOK on success, ENOMEM if out of memory 479 */ 480 errno_t ui_entry_insert_str(ui_entry_t *entry, const char *str) 481 { 482 uint8_t tmp; 483 char *ltext = NULL; 484 char *newtext; 485 char *oldtext; 486 int rc; 487 488 /* Do we have a selection? */ 489 if (entry->sel_start != entry->pos) 490 ui_entry_delete_sel(entry); 491 492 tmp = entry->text[entry->pos]; 493 entry->text[entry->pos] = '\0'; 494 ltext = str_dup(entry->text); 495 if (ltext == NULL) 496 return ENOMEM; 497 498 entry->text[entry->pos] = tmp; 499 500 rc = asprintf(&newtext, "%s%s%s", ltext, str, entry->text + entry->pos); 501 if (rc < 0) { 502 free(ltext); 503 return ENOMEM; 504 } 505 506 oldtext = entry->text; 507 entry->text = newtext; 508 entry->pos += str_size(str); 509 free(oldtext); 510 free(ltext); 511 512 entry->sel_start = entry->pos; 513 ui_entry_scroll_update(entry, false); 514 ui_entry_paint(entry); 515 516 return EOK; 517 } 518 519 /** Delete character before cursor. 520 * 521 * @param entry Text entry 522 */ 523 void ui_entry_backspace(ui_entry_t *entry) 524 { 525 size_t off; 526 527 /* Do we have a selection? */ 528 if (entry->sel_start != entry->pos) { 529 ui_entry_delete_sel(entry); 530 return; 531 } 532 533 if (entry->pos == 0) 534 return; 535 536 /* Find offset where character before cursor starts */ 537 off = entry->pos; 538 (void) str_decode_reverse(entry->text, &off, 539 str_size(entry->text)); 540 541 memmove(entry->text + off, entry->text + entry->pos, 542 str_size(entry->text + entry->pos) + 1); 543 entry->pos = off; 544 entry->sel_start = off; 545 546 ui_entry_scroll_update(entry, false); 547 ui_entry_paint(entry); 548 } 549 550 /** Delete character after cursor. 551 * 552 * @param entry Text entry 553 */ 554 void ui_entry_delete(ui_entry_t *entry) 555 { 556 size_t off; 557 558 /* Do we have a selection? */ 559 if (entry->sel_start != entry->pos) { 560 ui_entry_delete_sel(entry); 561 return; 562 } 563 564 /* Find offset where character after cursor ends */ 565 off = entry->pos; 566 (void) str_decode(entry->text, &off, 567 str_size(entry->text)); 568 569 memmove(entry->text + entry->pos, entry->text + off, 570 str_size(entry->text + off) + 1); 571 572 ui_entry_scroll_update(entry, false); 573 ui_entry_paint(entry); 574 } 575 576 /** Copy selected text to clipboard. 577 * 578 * @param entry Text entry 579 */ 580 void ui_entry_copy(ui_entry_t *entry) 581 { 582 unsigned off1; 583 unsigned off2; 584 char c; 585 586 off1 = min(entry->pos, entry->sel_start); 587 off2 = max(entry->pos, entry->sel_start); 588 589 c = entry->text[off2]; 590 entry->text[off2] = '\0'; 591 592 (void) clipboard_put_str(entry->text + off1); 593 594 entry->text[off2] = c; 595 } 596 597 /** Cut selected text to clipboard. 598 * 599 * @param entry Text entry 600 */ 601 void ui_entry_cut(ui_entry_t *entry) 602 { 603 ui_entry_copy(entry); 604 ui_entry_delete_sel(entry); 605 } 606 607 /** Paste text from clipboard. 608 * 609 * @param entry Text entry 610 */ 611 void ui_entry_paste(ui_entry_t *entry) 612 { 613 char *str; 614 errno_t rc; 615 616 rc = clipboard_get_str(&str); 617 if (rc != EOK) 618 return; 619 620 ui_entry_insert_str(entry, str); 621 free(str); 622 } 623 624 /** Handle text entry key press without modifiers. 625 * 626 * @param entry Text entry 627 * @param kbd_event Keyboard event 628 * @return @c ui_claimed iff the event is claimed 629 */ 630 ui_evclaim_t ui_entry_key_press_unmod(ui_entry_t *entry, kbd_event_t *event) 631 { 632 assert(event->type == KEY_PRESS); 633 634 switch (event->key) { 635 case KC_BACKSPACE: 636 ui_entry_backspace(entry); 637 break; 638 639 case KC_DELETE: 640 ui_entry_delete(entry); 641 break; 642 643 case KC_ESCAPE: 644 ui_entry_deactivate(entry); 645 break; 646 647 case KC_HOME: 648 ui_entry_seek_start(entry, false); 649 break; 650 651 case KC_END: 652 ui_entry_seek_end(entry, false); 653 break; 654 655 case KC_LEFT: 656 ui_entry_seek_prev_char(entry, false); 657 break; 658 659 case KC_RIGHT: 660 ui_entry_seek_next_char(entry, false); 661 break; 662 663 default: 664 break; 665 } 666 667 return ui_claimed; 668 } 669 670 /** Handle text entry key press with shift modifier. 671 * 672 * @param entry Text entry 673 * @param kbd_event Keyboard event 674 * @return @c ui_claimed iff the event is claimed 675 */ 676 ui_evclaim_t ui_entry_key_press_shift(ui_entry_t *entry, kbd_event_t *event) 677 { 678 assert(event->type == KEY_PRESS); 679 680 switch (event->key) { 681 case KC_HOME: 682 ui_entry_seek_start(entry, true); 683 break; 684 685 case KC_END: 686 ui_entry_seek_end(entry, true); 687 break; 688 689 case KC_LEFT: 690 ui_entry_seek_prev_char(entry, true); 691 break; 692 693 case KC_RIGHT: 694 ui_entry_seek_next_char(entry, true); 695 break; 696 697 default: 698 break; 699 } 700 701 return ui_claimed; 702 } 703 704 /** Handle text entry key press with control modifier. 705 * 706 * @param entry Text entry 707 * @param kbd_event Keyboard event 708 * @return @c ui_claimed iff the event is claimed 709 */ 710 ui_evclaim_t ui_entry_key_press_ctrl(ui_entry_t *entry, kbd_event_t *event) 711 { 712 assert(event->type == KEY_PRESS); 713 714 switch (event->key) { 715 case KC_C: 716 ui_entry_copy(entry); 717 break; 718 case KC_V: 719 ui_entry_paste(entry); 720 break; 721 case KC_X: 722 ui_entry_cut(entry); 723 break; 724 default: 725 break; 726 } 727 728 return ui_claimed; 729 } 730 731 /** Handle text entry keyboard event. 732 * 733 * @param entry Text entry 734 * @param kbd_event Keyboard event 735 * @return @c ui_claimed iff the event is claimed 736 */ 737 ui_evclaim_t ui_entry_kbd_event(ui_entry_t *entry, kbd_event_t *event) 738 { 739 char buf[STR_BOUNDS(1) + 1]; 740 size_t off; 741 errno_t rc; 742 743 if (!entry->active) 744 return ui_unclaimed; 745 746 if (event->type == KEY_PRESS && event->c >= ' ') { 747 off = 0; 748 rc = chr_encode(event->c, buf, &off, sizeof(buf)); 749 if (rc == EOK) { 750 buf[off] = '\0'; 751 (void) ui_entry_insert_str(entry, buf); 752 } 753 } 754 755 /* 756 * Need to keep track if any shift is held for the case 757 * of selecting by shift-click. This could be simplified 758 * if position events were decorated with modifier 759 * state. 760 */ 761 762 if (event->type == KEY_PRESS && event->key == KC_LSHIFT) 763 entry->lshift_held = true; 764 if (event->type == KEY_RELEASE && event->key == KC_LSHIFT) 765 entry->lshift_held = false; 766 if (event->type == KEY_PRESS && event->key == KC_RSHIFT) 767 entry->rshift_held = true; 768 if (event->type == KEY_RELEASE && event->key == KC_RSHIFT) 769 entry->rshift_held = false; 770 771 if (event->type == KEY_PRESS && 772 (event->mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0) 773 return ui_entry_key_press_unmod(entry, event); 774 775 if (event->type == KEY_PRESS && 776 (event->mods & KM_SHIFT) != 0 && 777 (event->mods & (KM_CTRL | KM_ALT)) == 0) 778 return ui_entry_key_press_shift(entry, event); 779 780 if (event->type == KEY_PRESS && 781 (event->mods & KM_CTRL) != 0 && 782 (event->mods & (KM_ALT | KM_SHIFT)) == 0) 783 return ui_entry_key_press_ctrl(entry, event); 784 785 return ui_claimed; 786 } 787 788 /** Handle text entry position event. 789 * 790 * @param entry Text entry 273 791 * @param pos_event Position event 274 792 * @return @c ui_claimed iff the event is claimed 275 793 */ 276 ui_evclaim_t ui_entry_ctl_pos_event(void *arg, pos_event_t *event) 277 { 278 ui_entry_t *entry = (ui_entry_t *) arg; 794 ui_evclaim_t ui_entry_pos_event(ui_entry_t *entry, pos_event_t *event) 795 { 279 796 gfx_coord2_t pos; 797 798 if (entry->read_only) 799 return ui_unclaimed; 280 800 281 801 if (event->type == POS_UPDATE) { … … 283 803 pos.y = event->vpos; 284 804 805 /* Change cursor shape when pointer is entering/leaving */ 285 806 if (gfx_pix_inside_rect(&pos, &entry->rect)) { 286 807 if (!entry->pointer_inside) { … … 296 817 } 297 818 } 819 820 if (entry->held) { 821 /* 822 * Selecting using mouse drag: Change pos, 823 * keep sel_start 824 */ 825 entry->pos = ui_entry_find_pos(entry, &pos); 826 ui_entry_paint(entry); 827 } 828 } 829 830 if (event->type == POS_PRESS) { 831 pos.x = event->hpos; 832 pos.y = event->vpos; 833 834 if (gfx_pix_inside_rect(&pos, &entry->rect)) { 835 /* Clicked inside - activate, set position */ 836 entry->held = true; 837 entry->pos = ui_entry_find_pos(entry, &pos); 838 839 /* Clear selection if no shift key is held */ 840 if (!entry->lshift_held && !entry->rshift_held) 841 entry->sel_start = entry->pos; 842 843 if (entry->active) 844 ui_entry_paint(entry); 845 else 846 ui_entry_activate(entry); 847 848 return ui_claimed; 849 } else { 850 /* Clicked outside - deactivate */ 851 ui_entry_deactivate(entry); 852 } 853 } 854 855 if (event->type == POS_RELEASE) { 856 entry->held = false; 298 857 } 299 858 … … 301 860 } 302 861 862 /** Handle text entry control keyboard event. 863 * 864 * @param arg Argument (ui_entry_t *) 865 * @param kbd_event Keyboard event 866 * @return @c ui_claimed iff the event is claimed 867 */ 868 static ui_evclaim_t ui_entry_ctl_kbd_event(void *arg, kbd_event_t *event) 869 { 870 ui_entry_t *entry = (ui_entry_t *) arg; 871 872 return ui_entry_kbd_event(entry, event); 873 } 874 875 /** Handle text entry control position event. 876 * 877 * @param arg Argument (ui_entry_t *) 878 * @param pos_event Position event 879 * @return @c ui_claimed iff the event is claimed 880 */ 881 static ui_evclaim_t ui_entry_ctl_pos_event(void *arg, pos_event_t *event) 882 { 883 ui_entry_t *entry = (ui_entry_t *) arg; 884 885 return ui_entry_pos_event(entry, event); 886 } 887 888 /** Get text entry geometry. 889 * 890 * @param entry Text entry 891 * @param geom Structure to fill in with computed geometry 892 */ 893 void ui_entry_get_geom(ui_entry_t *entry, ui_entry_geom_t *geom) 894 { 895 gfx_coord_t hpad; 896 gfx_coord_t vpad; 897 ui_resource_t *res; 898 899 res = ui_window_get_res(entry->window); 900 901 if (res->textmode) { 902 hpad = ui_entry_hpad_text; 903 vpad = ui_entry_vpad_text; 904 } else { 905 hpad = ui_entry_hpad; 906 vpad = ui_entry_vpad; 907 } 908 909 if (res->textmode == false) { 910 ui_paint_get_inset_frame_inside(res, &entry->rect, 911 &geom->interior_rect); 912 } else { 913 geom->interior_rect = entry->rect; 914 } 915 916 geom->text_rect.p0.x = geom->interior_rect.p0.x + hpad; 917 geom->text_rect.p0.y = geom->interior_rect.p0.y + vpad; 918 geom->text_rect.p1.x = geom->interior_rect.p1.x - hpad; 919 geom->text_rect.p1.y = geom->interior_rect.p1.y - vpad; 920 921 geom->text_pos.x = geom->interior_rect.p0.x + hpad + 922 entry->scroll_pos; 923 geom->text_pos.y = geom->interior_rect.p0.y + vpad; 924 925 switch (entry->halign) { 926 case gfx_halign_left: 927 case gfx_halign_justify: 928 geom->anchor_x = geom->text_rect.p0.x; 929 break; 930 case gfx_halign_center: 931 geom->anchor_x = (geom->text_rect.p0.x + 932 geom->text_rect.p1.x) / 2; 933 break; 934 case gfx_halign_right: 935 geom->anchor_x = geom->text_rect.p1.x; 936 break; 937 } 938 } 939 940 /** Activate text entry. 941 * 942 * @param entry Text entry 943 */ 944 void ui_entry_activate(ui_entry_t *entry) 945 { 946 ui_resource_t *res; 947 948 res = ui_window_get_res(entry->window); 949 950 if (entry->active) 951 return; 952 953 entry->active = true; 954 (void) ui_entry_paint(entry); 955 956 if (res->textmode) 957 gfx_cursor_set_visible(res->gc, true); 958 } 959 960 /** Move text cursor to the beginning of text. 961 * 962 * @param entry Text entry 963 * @param shift @c true iff shift key is pressed 964 */ 965 void ui_entry_seek_start(ui_entry_t *entry, bool shift) 966 { 967 entry->pos = 0; 968 969 if (!shift) 970 entry->sel_start = entry->pos; 971 972 ui_entry_scroll_update(entry, false); 973 (void) ui_entry_paint(entry); 974 } 975 976 /** Move text cursor to the end of text. 977 * 978 * @param entry Text entry 979 * @param shift @c true iff shift key is pressed 980 */ 981 void ui_entry_seek_end(ui_entry_t *entry, bool shift) 982 { 983 entry->pos = str_size(entry->text); 984 985 if (!shift) 986 entry->sel_start = entry->pos; 987 988 ui_entry_scroll_update(entry, false); 989 (void) ui_entry_paint(entry); 990 } 991 992 /** Move text cursor one character backward. 993 * 994 * @param entry Text entry 995 * @param shift @c true iff shift key is pressed 996 */ 997 void ui_entry_seek_prev_char(ui_entry_t *entry, bool shift) 998 { 999 size_t off; 1000 1001 off = entry->pos; 1002 (void) str_decode_reverse(entry->text, &off, 1003 str_size(entry->text)); 1004 entry->pos = off; 1005 1006 if (!shift) 1007 entry->sel_start = entry->pos; 1008 1009 ui_entry_scroll_update(entry, false); 1010 (void) ui_entry_paint(entry); 1011 } 1012 1013 /** Move text cursor one character forward. 1014 * 1015 * @param entry Text entry 1016 * @param shift @c true iff shift key is pressed 1017 */ 1018 void ui_entry_seek_next_char(ui_entry_t *entry, bool shift) 1019 { 1020 size_t off; 1021 1022 off = entry->pos; 1023 (void) str_decode(entry->text, &off, 1024 str_size(entry->text)); 1025 entry->pos = off; 1026 1027 if (!shift) 1028 entry->sel_start = entry->pos; 1029 1030 ui_entry_scroll_update(entry, false); 1031 (void) ui_entry_paint(entry); 1032 } 1033 1034 /** Deactivate text entry. 1035 * 1036 * @param entry Text entry 1037 */ 1038 void ui_entry_deactivate(ui_entry_t *entry) 1039 { 1040 ui_resource_t *res; 1041 1042 res = ui_window_get_res(entry->window); 1043 1044 if (!entry->active) 1045 return; 1046 1047 entry->active = false; 1048 entry->sel_start = entry->pos; 1049 (void) ui_entry_paint(entry); 1050 1051 if (res->textmode) 1052 gfx_cursor_set_visible(res->gc, false); 1053 } 1054 1055 /** Update text entry scroll position. 1056 * 1057 * @param entry Text entry 1058 * @param realign @c true iff we should left-align short text. 1059 * This should be only used when changing text alignment, 1060 * because left-aligned text entries should not realign 1061 * the text to the left side under normal circumstances. 1062 */ 1063 void ui_entry_scroll_update(ui_entry_t *entry, bool realign) 1064 { 1065 ui_entry_geom_t geom; 1066 gfx_coord_t x; 1067 gfx_coord_t width; 1068 gfx_coord2_t tpos; 1069 gfx_coord2_t anchor; 1070 gfx_text_fmt_t fmt; 1071 ui_resource_t *res; 1072 1073 res = ui_window_get_res(entry->window); 1074 1075 ui_entry_get_geom(entry, &geom); 1076 1077 /* Compute position where cursor is currently displayed at */ 1078 x = geom.text_pos.x + ui_entry_lwidth(entry); 1079 1080 /* Is cursor off to the left? */ 1081 if (x < geom.text_rect.p0.x) { 1082 /* 1083 * Scroll to make cursor visible and put some space between it 1084 * and the left edge of the text rectangle. 1085 */ 1086 entry->scroll_pos += geom.text_rect.p0.x - x + 1087 ui_entry_left_scroll_margin; 1088 1089 /* 1090 * We don't want to scroll further than what's needed 1091 * to reveal the beginning of the text. 1092 */ 1093 if (entry->scroll_pos > 0) 1094 entry->scroll_pos = 0; 1095 } 1096 1097 /* 1098 * Is cursor off to the right? Note that the width of the cursor 1099 * is deliberately not taken into account (i.e. we only care 1100 * about the left edge of the cursor). 1101 */ 1102 if (x > geom.text_rect.p1.x) 1103 entry->scroll_pos -= x - geom.text_rect.p1.x; 1104 1105 width = gfx_text_width(res->font, entry->text); 1106 1107 if (width < geom.text_rect.p1.x - geom.text_rect.p0.x && 1108 (realign || entry->halign != gfx_halign_left)) { 1109 /* Text fits inside entry, so we need to align it */ 1110 anchor.x = geom.anchor_x; 1111 anchor.y = 0; 1112 gfx_text_fmt_init(&fmt); 1113 fmt.halign = entry->halign; 1114 gfx_text_start_pos(res->font, &anchor, &fmt, entry->text, 1115 &tpos); 1116 entry->scroll_pos = tpos.x - geom.text_rect.p0.x; 1117 } else if (geom.text_pos.x + width < geom.text_rect.p1.x && 1118 entry->halign != gfx_halign_left) { 1119 /* Text is long, unused space on the right */ 1120 entry->scroll_pos += geom.text_rect.p1.x - 1121 geom.text_pos.x - width; 1122 } 1123 } 1124 303 1125 /** @} 304 1126 */
Note:
See TracChangeset
for help on using the changeset viewer.