Changes in uspace/lib/ui/src/entry.c [03145ee:5e109e1] in mainline
- File:
-
- 1 edited
-
uspace/lib/ui/src/entry.c (modified) (10 diffs)
Legend:
- Unmodified
- Added
- Removed
-
uspace/lib/ui/src/entry.c
r03145ee r5e109e1 1 1 /* 2 * Copyright (c) 202 0Jiri Svoboda2 * Copyright (c) 2021 Jiri Svoboda 3 3 * All rights reserved. 4 4 * … … 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> … … 46 49 #include <ui/paint.h> 47 50 #include <ui/entry.h> 51 #include <ui/ui.h> 52 #include <ui/window.h> 48 53 #include "../private/entry.h" 49 54 #include "../private/resource.h" … … 51 56 static void ui_entry_ctl_destroy(void *); 52 57 static errno_t ui_entry_ctl_paint(void *); 58 static ui_evclaim_t ui_entry_ctl_kbd_event(void *, kbd_event_t *); 53 59 static ui_evclaim_t ui_entry_ctl_pos_event(void *, pos_event_t *); 54 60 55 61 enum { 56 62 ui_entry_hpad = 4, 57 ui_entry_vpad = 4 63 ui_entry_vpad = 4, 64 ui_entry_hpad_text = 1, 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 58 72 }; 59 73 … … 62 76 .destroy = ui_entry_ctl_destroy, 63 77 .paint = ui_entry_ctl_paint, 78 .kbd_event = ui_entry_ctl_kbd_event, 64 79 .pos_event = ui_entry_ctl_pos_event 65 80 }; … … 67 82 /** Create new text entry. 68 83 * 69 * @param resource UI resource84 * @param window UI window 70 85 * @param text Text 71 86 * @param rentry Place to store pointer to new text entry 72 87 * @return EOK on success, ENOMEM if out of memory 73 88 */ 74 errno_t ui_entry_create(ui_ resource_t *resource, const char *text,89 errno_t ui_entry_create(ui_window_t *window, const char *text, 75 90 ui_entry_t **rentry) 76 91 { … … 95 110 } 96 111 97 entry-> res = resource;112 entry->window = window; 98 113 entry->halign = gfx_halign_left; 99 114 *rentry = entry; 115 100 116 return EOK; 101 117 } … … 142 158 { 143 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; 144 172 } 145 173 … … 160 188 free(entry->text); 161 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); 162 195 163 196 return EOK; 164 197 } 165 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 166 271 /** Paint text entry. 167 272 * … … 171 276 errno_t ui_entry_paint(ui_entry_t *entry) 172 277 { 278 ui_resource_t *res; 279 ui_entry_geom_t geom; 173 280 gfx_text_fmt_t fmt; 174 281 gfx_coord2_t pos; 175 gfx_rect_t frame; 282 gfx_text_fmt_t cfmt; 283 gfx_coord2_t cpos; 176 284 gfx_rect_t inside; 285 unsigned off1, off2; 286 gfx_rect_t sel; 287 char c; 177 288 errno_t rc; 178 289 179 /* Paint inset frame */ 180 181 rc = ui_paint_bevel(entry->res->gc, &entry->rect, 182 entry->res->wnd_shadow_color, entry->res->wnd_highlight_color, 183 1, &frame); 290 res = ui_window_get_res(entry->window); 291 292 ui_entry_get_geom(entry, &geom); 293 294 if (res->textmode == false) { 295 /* Paint inset frame */ 296 rc = ui_paint_inset_frame(res, &entry->rect, &inside); 297 if (rc != EOK) 298 goto error; 299 } else { 300 inside = entry->rect; 301 } 302 303 /* Paint entry background */ 304 305 rc = gfx_set_color(res->gc, res->entry_bg_color); 184 306 if (rc != EOK) 185 307 goto error; 186 308 187 rc = ui_paint_bevel(entry->res->gc, &frame, 188 entry->res->wnd_frame_sh_color, entry->res->wnd_frame_hi_color, 189 1, &inside); 309 rc = gfx_fill_rect(res->gc, &inside); 190 310 if (rc != EOK) 191 311 goto error; 192 312 193 /* Paint entry background */ 194 195 rc = gfx_set_color(entry->res->gc, entry->res->entry_bg_color); 313 pos = geom.text_pos; 314 315 gfx_text_fmt_init(&fmt); 316 fmt.color = res->entry_fg_color; 317 fmt.halign = gfx_halign_left; 318 fmt.valign = gfx_valign_top; 319 320 rc = gfx_set_clip_rect(res->gc, &inside); 196 321 if (rc != EOK) 197 322 goto error; 198 323 199 rc = gfx_fill_rect(entry->res->gc, &inside); 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 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_bg_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_fg_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); 200 394 if (rc != EOK) 201 395 goto error; 396 397 rc = gfx_update(res->gc); 398 if (rc != EOK) 399 goto error; 400 401 return EOK; 402 error: 403 return rc; 404 } 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 430 /** Destroy text entry control. 431 * 432 * @param arg Argument (ui_entry_t *) 433 */ 434 void ui_entry_ctl_destroy(void *arg) 435 { 436 ui_entry_t *entry = (ui_entry_t *) arg; 437 438 ui_entry_destroy(entry); 439 } 440 441 /** Paint text entry control. 442 * 443 * @param arg Argument (ui_entry_t *) 444 * @return EOK on success or an error code 445 */ 446 errno_t ui_entry_ctl_paint(void *arg) 447 { 448 ui_entry_t *entry = (ui_entry_t *) arg; 449 450 return ui_entry_paint(entry); 451 } 452 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 791 * @param pos_event Position event 792 * @return @c ui_claimed iff the event is claimed 793 */ 794 ui_evclaim_t ui_entry_pos_event(ui_entry_t *entry, pos_event_t *event) 795 { 796 gfx_coord2_t pos; 797 798 if (entry->read_only) 799 return ui_unclaimed; 800 801 if (event->type == POS_UPDATE) { 802 pos.x = event->hpos; 803 pos.y = event->vpos; 804 805 /* Change cursor shape when pointer is entering/leaving */ 806 if (gfx_pix_inside_rect(&pos, &entry->rect)) { 807 if (!entry->pointer_inside) { 808 ui_window_set_ctl_cursor(entry->window, 809 ui_curs_ibeam); 810 entry->pointer_inside = true; 811 } 812 } else { 813 if (entry->pointer_inside) { 814 ui_window_set_ctl_cursor(entry->window, 815 ui_curs_arrow); 816 entry->pointer_inside = false; 817 } 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; 857 } 858 859 return ui_unclaimed; 860 } 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; 202 924 203 925 switch (entry->halign) { 204 926 case gfx_halign_left: 205 927 case gfx_halign_justify: 206 pos.x = inside.p0.x + ui_entry_hpad;928 geom->anchor_x = geom->text_rect.p0.x; 207 929 break; 208 930 case gfx_halign_center: 209 pos.x = (inside.p0.x + inside.p1.x) / 2; 931 geom->anchor_x = (geom->text_rect.p0.x + 932 geom->text_rect.p1.x) / 2; 210 933 break; 211 934 case gfx_halign_right: 212 pos.x = inside.p1.x - ui_entry_hpad; 213 break; 214 } 215 216 pos.y = inside.p0.y + ui_entry_vpad; 217 218 gfx_text_fmt_init(&fmt); 219 fmt.halign = entry->halign; 220 fmt.valign = gfx_valign_top; 221 222 rc = gfx_set_color(entry->res->gc, entry->res->entry_fg_color); 223 if (rc != EOK) 224 goto error; 225 226 rc = gfx_puttext(entry->res->font, &pos, &fmt, entry->text); 227 if (rc != EOK) 228 goto error; 229 230 return EOK; 231 error: 232 return rc; 233 } 234 235 /** Destroy text entry control. 236 * 237 * @param arg Argument (ui_entry_t *) 238 */ 239 void ui_entry_ctl_destroy(void *arg) 240 { 241 ui_entry_t *entry = (ui_entry_t *) arg; 242 243 ui_entry_destroy(entry); 244 } 245 246 /** Paint text entry control. 247 * 248 * @param arg Argument (ui_entry_t *) 249 * @return EOK on success or an error code 250 */ 251 errno_t ui_entry_ctl_paint(void *arg) 252 { 253 ui_entry_t *entry = (ui_entry_t *) arg; 254 255 return ui_entry_paint(entry); 256 } 257 258 /** Handle text entry control position event. 259 * 260 * @param arg Argument (ui_entry_t *) 261 * @param pos_event Position event 262 * @return @c ui_claimed iff the event is claimed 263 */ 264 ui_evclaim_t ui_entry_ctl_pos_event(void *arg, pos_event_t *event) 265 { 266 ui_entry_t *entry = (ui_entry_t *) arg; 267 268 (void) entry; 269 return ui_unclaimed; 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 } 270 1123 } 271 1124
Note:
See TracChangeset
for help on using the changeset viewer.
