Changes in uspace/lib/ui/src/entry.c [d0dfbba:03145ee] in mainline
- File:
-
- 1 edited
-
uspace/lib/ui/src/entry.c (modified) (12 diffs)
Legend:
- Unmodified
- Added
- Removed
-
uspace/lib/ui/src/entry.c
rd0dfbba r03145ee 1 1 /* 2 * Copyright (c) 202 6Jiri Svoboda2 * Copyright (c) 2020 Jiri Svoboda 3 3 * All rights reserved. 4 4 * … … 37 37 */ 38 38 39 #include <assert.h>40 #include <clipboard.h>41 39 #include <errno.h> 42 40 #include <gfx/context.h> 43 #include <gfx/cursor.h>44 41 #include <gfx/render.h> 45 42 #include <gfx/text.h> 46 #include <macros.h>47 #include <stdio.h>48 43 #include <stdlib.h> 49 44 #include <str.h> … … 51 46 #include <ui/paint.h> 52 47 #include <ui/entry.h> 53 #include <ui/ui.h>54 #include <ui/window.h>55 48 #include "../private/entry.h" 56 49 #include "../private/resource.h" … … 58 51 static void ui_entry_ctl_destroy(void *); 59 52 static errno_t ui_entry_ctl_paint(void *); 60 static ui_evclaim_t ui_entry_ctl_kbd_event(void *, kbd_event_t *);61 53 static ui_evclaim_t ui_entry_ctl_pos_event(void *, pos_event_t *); 62 54 63 55 enum { 64 56 ui_entry_hpad = 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 57 ui_entry_vpad = 4 74 58 }; 75 59 … … 78 62 .destroy = ui_entry_ctl_destroy, 79 63 .paint = ui_entry_ctl_paint, 80 .kbd_event = ui_entry_ctl_kbd_event,81 64 .pos_event = ui_entry_ctl_pos_event 82 65 }; … … 84 67 /** Create new text entry. 85 68 * 86 * @param window UI window69 * @param resource UI resource 87 70 * @param text Text 88 71 * @param rentry Place to store pointer to new text entry 89 72 * @return EOK on success, ENOMEM if out of memory 90 73 */ 91 errno_t ui_entry_create(ui_ window_t *window, const char *text,74 errno_t ui_entry_create(ui_resource_t *resource, const char *text, 92 75 ui_entry_t **rentry) 93 76 { … … 112 95 } 113 96 114 entry-> window = window;97 entry->res = resource; 115 98 entry->halign = gfx_halign_left; 116 99 *rentry = entry; 117 118 100 return EOK; 119 101 } … … 160 142 { 161 143 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 entry169 * @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;174 144 } 175 145 … … 190 160 free(entry->text); 191 161 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);197 162 198 163 return EOK; 199 164 } 200 165 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) 166 /** Paint text entry. 167 * 168 * @param entry Text entry 214 169 * @return EOK on success or an error code 215 170 */ 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; 171 errno_t ui_entry_paint(ui_entry_t *entry) 172 { 173 gfx_text_fmt_t fmt; 174 gfx_coord2_t pos; 175 gfx_rect_t frame; 176 gfx_rect_t inside; 221 177 errno_t rc; 222 178 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; 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); 184 if (rc != EOK) 185 goto error; 186 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); 190 if (rc != EOK) 191 goto error; 192 193 /* Paint entry background */ 194 195 rc = gfx_set_color(entry->res->gc, entry->res->entry_bg_color); 196 if (rc != EOK) 197 goto error; 198 199 rc = gfx_fill_rect(entry->res->gc, &inside); 200 if (rc != EOK) 201 goto error; 202 203 switch (entry->halign) { 204 case gfx_halign_left: 205 case gfx_halign_justify: 206 pos.x = inside.p0.x + ui_entry_hpad; 207 break; 208 case gfx_halign_center: 209 pos.x = (inside.p0.x + inside.p1.x) / 2; 210 break; 211 case gfx_halign_right: 212 pos.x = inside.p1.x - ui_entry_hpad; 213 break; 228 214 } 229 215 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); 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); 243 227 if (rc != EOK) 244 228 goto error; … … 249 233 } 250 234 251 /** Return width of text before cursor.252 *253 * @param entry Text entry254 * @return Widht of text before cursor255 */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 273 /** Paint text entry.274 *275 * @param entry Text entry276 * @return EOK on success or an error code277 */278 errno_t ui_entry_paint(ui_entry_t *entry)279 {280 ui_resource_t *res;281 ui_entry_geom_t geom;282 gfx_text_fmt_t fmt;283 gfx_coord2_t pos;284 gfx_text_fmt_t cfmt;285 gfx_coord2_t cpos;286 gfx_rect_t inside;287 unsigned off1, off2;288 gfx_rect_t sel;289 char c;290 errno_t rc;291 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);308 if (rc != EOK)309 goto error;310 311 rc = gfx_fill_rect(res->gc, &inside);312 if (rc != EOK)313 goto error;314 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);324 if (rc != EOK)325 goto error;326 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);396 if (rc != EOK)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 entry411 * @param fpos Position for which we need to find text offset412 * @return Corresponding byte offset in entry text413 */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 235 /** Destroy text entry control. 433 236 * … … 451 254 452 255 return ui_entry_paint(entry); 453 }454 455 /** Delete selected text.456 *457 * @param entry Text entry458 */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 entry479 * @param str String480 * @return EOK on success, ENOMEM if out of memory481 */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 entry524 */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 entry555 */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 entry581 */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 entry602 */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 entry612 */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 entry629 * @param kbd_event Keyboard event630 * @return @c ui_claimed iff the event is claimed631 */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 entry674 * @param kbd_event Keyboard event675 * @return @c ui_claimed iff the event is claimed676 */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 entry708 * @param kbd_event Keyboard event709 * @return @c ui_claimed iff the event is claimed710 */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 entry735 * @param kbd_event Keyboard event736 * @return @c ui_claimed iff the event is claimed737 */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 case749 * of selecting by shift-click. This could be simplified750 * if position events were decorated with modifier751 * 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 entry793 * @param pos_event Position event794 * @return @c ui_claimed iff the event is claimed795 */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_start826 */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 else848 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 event868 * @return @c ui_claimed iff the event is claimed869 */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 256 } 876 257 … … 881 262 * @return @c ui_claimed iff the event is claimed 882 263 */ 883 staticui_evclaim_t ui_entry_ctl_pos_event(void *arg, pos_event_t *event)264 ui_evclaim_t ui_entry_ctl_pos_event(void *arg, pos_event_t *event) 884 265 { 885 266 ui_entry_t *entry = (ui_entry_t *) arg; 886 267 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; 926 927 switch (entry->halign) { 928 case gfx_halign_left: 929 case gfx_halign_justify: 930 geom->anchor_x = geom->text_rect.p0.x; 931 break; 932 case gfx_halign_center: 933 geom->anchor_x = (geom->text_rect.p0.x + 934 geom->text_rect.p1.x) / 2; 935 break; 936 case gfx_halign_right: 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 } 268 (void) entry; 269 return ui_unclaimed; 1125 270 } 1126 271
Note:
See TracChangeset
for help on using the changeset viewer.
