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