Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/ui/src/entry.c

    rdb3895d r6df564c  
    3737 */
    3838
     39#include <clipboard.h>
    3940#include <errno.h>
    4041#include <gfx/context.h>
     42#include <gfx/cursor.h>
    4143#include <gfx/render.h>
    4244#include <gfx/text.h>
     45#include <macros.h>
    4346#include <stdlib.h>
    4447#include <str.h>
     
    5356static void ui_entry_ctl_destroy(void *);
    5457static errno_t ui_entry_ctl_paint(void *);
     58static ui_evclaim_t ui_entry_ctl_kbd_event(void *, kbd_event_t *);
    5559static ui_evclaim_t ui_entry_ctl_pos_event(void *, pos_event_t *);
    5660
     
    5963        ui_entry_vpad = 4,
    6064        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
    6272};
    6373
     
    6676        .destroy = ui_entry_ctl_destroy,
    6777        .paint = ui_entry_ctl_paint,
     78        .kbd_event = ui_entry_ctl_kbd_event,
    6879        .pos_event = ui_entry_ctl_pos_event
    6980};
     
    102113        entry->halign = gfx_halign_left;
    103114        *rentry = entry;
     115
    104116        return EOK;
    105117}
     
    146158{
    147159        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 */
     169void ui_entry_set_read_only(ui_entry_t *entry, bool read_only)
     170{
     171        entry->read_only = read_only;
    148172}
    149173
     
    164188        free(entry->text);
    165189        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);
    166195
    167196        return EOK;
    168197}
    169198
     199/** Get entry text.
     200 *
     201 * @return Pointer to entry text.
     202 */
     203const 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 */
     214static 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;
     245error:
     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 */
     254static 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
    170271/** Paint text entry.
    171272 *
     
    176277{
    177278        ui_resource_t *res;
     279        ui_entry_geom_t geom;
    178280        gfx_text_fmt_t fmt;
    179281        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;
    182284        gfx_rect_t inside;
     285        unsigned off1, off2;
     286        gfx_rect_t sel;
     287        char c;
    183288        errno_t rc;
    184289
    185290        res = ui_window_get_res(entry->window);
    186291
    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);
    194293
    195294        if (res->textmode == false) {
     
    212311                goto error;
    213312
    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;
    228314
    229315        gfx_text_fmt_init(&fmt);
    230316        fmt.color = res->entry_fg_color;
    231         fmt.halign = entry->halign;
     317        fmt.halign = gfx_halign_left;
    232318        fmt.valign = gfx_valign_top;
    233319
     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
    234331        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);
    235394        if (rc != EOK)
    236395                goto error;
     
    245404}
    246405
     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 */
     412size_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
    247430/** Destroy text entry control.
    248431 *
     
    268451}
    269452
    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 */
     457void 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 */
     480errno_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 */
     523void 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 */
     554void 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 */
     580void 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 */
     601void 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 */
     611void 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 */
     630ui_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 */
     676ui_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 */
     710ui_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 */
     737ui_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
    273791 * @param pos_event Position event
    274792 * @return @c ui_claimed iff the event is claimed
    275793 */
    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;
     794ui_evclaim_t ui_entry_pos_event(ui_entry_t *entry, pos_event_t *event)
     795{
    279796        gfx_coord2_t pos;
     797
     798        if (entry->read_only)
     799                return ui_unclaimed;
    280800
    281801        if (event->type == POS_UPDATE) {
     
    283803                pos.y = event->vpos;
    284804
     805                /* Change cursor shape when pointer is entering/leaving */
    285806                if (gfx_pix_inside_rect(&pos, &entry->rect)) {
    286807                        if (!entry->pointer_inside) {
     
    296817                        }
    297818                }
     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;
    298857        }
    299858
     
    301860}
    302861
     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 */
     868static 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 */
     881static 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 */
     893void 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 */
     944void 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 */
     965void 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 */
     981void 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 */
     997void 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 */
     1018void 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 */
     1038void 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 */
     1063void 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
    3031125/** @}
    3041126 */
Note: See TracChangeset for help on using the changeset viewer.