Ignore:
File:
1 edited

Legend:

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

    r6df564c rdb3895d  
    3737 */
    3838
    39 #include <clipboard.h>
    4039#include <errno.h>
    4140#include <gfx/context.h>
    42 #include <gfx/cursor.h>
    4341#include <gfx/render.h>
    4442#include <gfx/text.h>
    45 #include <macros.h>
    4643#include <stdlib.h>
    4744#include <str.h>
     
    5653static void ui_entry_ctl_destroy(void *);
    5754static errno_t ui_entry_ctl_paint(void *);
    58 static ui_evclaim_t ui_entry_ctl_kbd_event(void *, kbd_event_t *);
    5955static ui_evclaim_t ui_entry_ctl_pos_event(void *, pos_event_t *);
    6056
     
    6359        ui_entry_vpad = 4,
    6460        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
     61        ui_entry_vpad_text = 0
    7262};
    7363
     
    7666        .destroy = ui_entry_ctl_destroy,
    7767        .paint = ui_entry_ctl_paint,
    78         .kbd_event = ui_entry_ctl_kbd_event,
    7968        .pos_event = ui_entry_ctl_pos_event
    8069};
     
    113102        entry->halign = gfx_halign_left;
    114103        *rentry = entry;
    115 
    116104        return EOK;
    117105}
     
    158146{
    159147        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;
    172148}
    173149
     
    188164        free(entry->text);
    189165        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);
    195166
    196167        return EOK;
    197168}
    198169
    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)
     170/** Paint text entry.
     171 *
     172 * @param entry Text entry
    212173 * @return EOK on success or an error code
    213174 */
    214 static errno_t ui_entry_paint_cursor(ui_entry_t *entry, gfx_coord2_t *pos)
     175errno_t ui_entry_paint(ui_entry_t *entry)
    215176{
    216177        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 
    271 /** Paint text entry.
    272  *
    273  * @param entry Text entry
    274  * @return EOK on success or an error code
    275  */
    276 errno_t ui_entry_paint(ui_entry_t *entry)
    277 {
    278         ui_resource_t *res;
    279         ui_entry_geom_t geom;
    280178        gfx_text_fmt_t fmt;
    281179        gfx_coord2_t pos;
    282         gfx_text_fmt_t cfmt;
    283         gfx_coord2_t cpos;
     180        gfx_coord_t hpad;
     181        gfx_coord_t vpad;
    284182        gfx_rect_t inside;
    285         unsigned off1, off2;
    286         gfx_rect_t sel;
    287         char c;
    288183        errno_t rc;
    289184
    290185        res = ui_window_get_res(entry->window);
    291186
    292         ui_entry_get_geom(entry, &geom);
     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        }
    293194
    294195        if (res->textmode == false) {
     
    311212                goto error;
    312213
    313         pos = geom.text_pos;
     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;
    314228
    315229        gfx_text_fmt_init(&fmt);
    316230        fmt.color = res->entry_fg_color;
    317         fmt.halign = gfx_halign_left;
     231        fmt.halign = entry->halign;
    318232        fmt.valign = gfx_valign_top;
    319233
    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 
    331234        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);
    394235        if (rc != EOK)
    395236                goto error;
     
    404245}
    405246
    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 
    430247/** Destroy text entry control.
    431248 *
     
    451268}
    452269
    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
     270/** Handle text entry control position event.
     271 *
     272 * @param arg Argument (ui_entry_t *)
    791273 * @param pos_event Position event
    792274 * @return @c ui_claimed iff the event is claimed
    793275 */
    794 ui_evclaim_t ui_entry_pos_event(ui_entry_t *entry, pos_event_t *event)
    795 {
     276ui_evclaim_t ui_entry_ctl_pos_event(void *arg, pos_event_t *event)
     277{
     278        ui_entry_t *entry = (ui_entry_t *) arg;
    796279        gfx_coord2_t pos;
    797 
    798         if (entry->read_only)
    799                 return ui_unclaimed;
    800280
    801281        if (event->type == POS_UPDATE) {
     
    803283                pos.y = event->vpos;
    804284
    805                 /* Change cursor shape when pointer is entering/leaving */
    806285                if (gfx_pix_inside_rect(&pos, &entry->rect)) {
    807286                        if (!entry->pointer_inside) {
     
    817296                        }
    818297                }
    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;
    857298        }
    858299
     
    860301}
    861302
    862 /** Handle text entry control keyboard event.
    863  *
    864  * @param arg Argument (ui_entry_t *)
    865  * @param kbd_event Keyboard event
    866  * @return @c ui_claimed iff the event is claimed
    867  */
    868 static ui_evclaim_t ui_entry_ctl_kbd_event(void *arg, kbd_event_t *event)
    869 {
    870         ui_entry_t *entry = (ui_entry_t *) arg;
    871 
    872         return ui_entry_kbd_event(entry, event);
    873 }
    874 
    875 /** Handle text entry control position event.
    876  *
    877  * @param arg Argument (ui_entry_t *)
    878  * @param pos_event Position event
    879  * @return @c ui_claimed iff the event is claimed
    880  */
    881 static ui_evclaim_t ui_entry_ctl_pos_event(void *arg, pos_event_t *event)
    882 {
    883         ui_entry_t *entry = (ui_entry_t *) arg;
    884 
    885         return ui_entry_pos_event(entry, event);
    886 }
    887 
    888 /** Get text entry geometry.
    889  *
    890  * @param entry Text entry
    891  * @param geom Structure to fill in with computed geometry
    892  */
    893 void ui_entry_get_geom(ui_entry_t *entry, ui_entry_geom_t *geom)
    894 {
    895         gfx_coord_t hpad;
    896         gfx_coord_t vpad;
    897         ui_resource_t *res;
    898 
    899         res = ui_window_get_res(entry->window);
    900 
    901         if (res->textmode) {
    902                 hpad = ui_entry_hpad_text;
    903                 vpad = ui_entry_vpad_text;
    904         } else {
    905                 hpad = ui_entry_hpad;
    906                 vpad = ui_entry_vpad;
    907         }
    908 
    909         if (res->textmode == false) {
    910                 ui_paint_get_inset_frame_inside(res, &entry->rect,
    911                     &geom->interior_rect);
    912         } else {
    913                 geom->interior_rect = entry->rect;
    914         }
    915 
    916         geom->text_rect.p0.x = geom->interior_rect.p0.x + hpad;
    917         geom->text_rect.p0.y = geom->interior_rect.p0.y + vpad;
    918         geom->text_rect.p1.x = geom->interior_rect.p1.x - hpad;
    919         geom->text_rect.p1.y = geom->interior_rect.p1.y - vpad;
    920 
    921         geom->text_pos.x = geom->interior_rect.p0.x + hpad +
    922             entry->scroll_pos;
    923         geom->text_pos.y = geom->interior_rect.p0.y + vpad;
    924 
    925         switch (entry->halign) {
    926         case gfx_halign_left:
    927         case gfx_halign_justify:
    928                 geom->anchor_x = geom->text_rect.p0.x;
    929                 break;
    930         case gfx_halign_center:
    931                 geom->anchor_x = (geom->text_rect.p0.x +
    932                     geom->text_rect.p1.x) / 2;
    933                 break;
    934         case gfx_halign_right:
    935                 geom->anchor_x = geom->text_rect.p1.x;
    936                 break;
    937         }
    938 }
    939 
    940 /** Activate text entry.
    941  *
    942  * @param entry Text entry
    943  */
    944 void ui_entry_activate(ui_entry_t *entry)
    945 {
    946         ui_resource_t *res;
    947 
    948         res = ui_window_get_res(entry->window);
    949 
    950         if (entry->active)
    951                 return;
    952 
    953         entry->active = true;
    954         (void) ui_entry_paint(entry);
    955 
    956         if (res->textmode)
    957                 gfx_cursor_set_visible(res->gc, true);
    958 }
    959 
    960 /** Move text cursor to the beginning of text.
    961  *
    962  * @param entry Text entry
    963  * @param shift @c true iff shift key is pressed
    964  */
    965 void ui_entry_seek_start(ui_entry_t *entry, bool shift)
    966 {
    967         entry->pos = 0;
    968 
    969         if (!shift)
    970                 entry->sel_start = entry->pos;
    971 
    972         ui_entry_scroll_update(entry, false);
    973         (void) ui_entry_paint(entry);
    974 }
    975 
    976 /** Move text cursor to the end of text.
    977  *
    978  * @param entry Text entry
    979  * @param shift @c true iff shift key is pressed
    980  */
    981 void ui_entry_seek_end(ui_entry_t *entry, bool shift)
    982 {
    983         entry->pos = str_size(entry->text);
    984 
    985         if (!shift)
    986                 entry->sel_start = entry->pos;
    987 
    988         ui_entry_scroll_update(entry, false);
    989         (void) ui_entry_paint(entry);
    990 }
    991 
    992 /** Move text cursor one character backward.
    993  *
    994  * @param entry Text entry
    995  * @param shift @c true iff shift key is pressed
    996  */
    997 void ui_entry_seek_prev_char(ui_entry_t *entry, bool shift)
    998 {
    999         size_t off;
    1000 
    1001         off = entry->pos;
    1002         (void) str_decode_reverse(entry->text, &off,
    1003             str_size(entry->text));
    1004         entry->pos = off;
    1005 
    1006         if (!shift)
    1007                 entry->sel_start = entry->pos;
    1008 
    1009         ui_entry_scroll_update(entry, false);
    1010         (void) ui_entry_paint(entry);
    1011 }
    1012 
    1013 /** Move text cursor one character forward.
    1014  *
    1015  * @param entry Text entry
    1016  * @param shift @c true iff shift key is pressed
    1017  */
    1018 void ui_entry_seek_next_char(ui_entry_t *entry, bool shift)
    1019 {
    1020         size_t off;
    1021 
    1022         off = entry->pos;
    1023         (void) str_decode(entry->text, &off,
    1024             str_size(entry->text));
    1025         entry->pos = off;
    1026 
    1027         if (!shift)
    1028                 entry->sel_start = entry->pos;
    1029 
    1030         ui_entry_scroll_update(entry, false);
    1031         (void) ui_entry_paint(entry);
    1032 }
    1033 
    1034 /** Deactivate text entry.
    1035  *
    1036  * @param entry Text entry
    1037  */
    1038 void ui_entry_deactivate(ui_entry_t *entry)
    1039 {
    1040         ui_resource_t *res;
    1041 
    1042         res = ui_window_get_res(entry->window);
    1043 
    1044         if (!entry->active)
    1045                 return;
    1046 
    1047         entry->active = false;
    1048         entry->sel_start = entry->pos;
    1049         (void) ui_entry_paint(entry);
    1050 
    1051         if (res->textmode)
    1052                 gfx_cursor_set_visible(res->gc, false);
    1053 }
    1054 
    1055 /** Update text entry scroll position.
    1056  *
    1057  * @param entry Text entry
    1058  * @param realign @c true iff we should left-align short text.
    1059  *                This should be only used when changing text alignment,
    1060  *                because left-aligned text entries should not realign
    1061  *                the text to the left side under normal circumstances.
    1062  */
    1063 void ui_entry_scroll_update(ui_entry_t *entry, bool realign)
    1064 {
    1065         ui_entry_geom_t geom;
    1066         gfx_coord_t x;
    1067         gfx_coord_t width;
    1068         gfx_coord2_t tpos;
    1069         gfx_coord2_t anchor;
    1070         gfx_text_fmt_t fmt;
    1071         ui_resource_t *res;
    1072 
    1073         res = ui_window_get_res(entry->window);
    1074 
    1075         ui_entry_get_geom(entry, &geom);
    1076 
    1077         /* Compute position where cursor is currently displayed at */
    1078         x = geom.text_pos.x + ui_entry_lwidth(entry);
    1079 
    1080         /* Is cursor off to the left? */
    1081         if (x < geom.text_rect.p0.x) {
    1082                 /*
    1083                  * Scroll to make cursor visible and put some space between it
    1084                  * and the left edge of the text rectangle.
    1085                  */
    1086                 entry->scroll_pos += geom.text_rect.p0.x - x +
    1087                     ui_entry_left_scroll_margin;
    1088 
    1089                 /*
    1090                  * We don't want to scroll further than what's needed
    1091                  * to reveal the beginning of the text.
    1092                  */
    1093                 if (entry->scroll_pos > 0)
    1094                         entry->scroll_pos = 0;
    1095         }
    1096 
    1097         /*
    1098          * Is cursor off to the right? Note that the width of the cursor
    1099          * is deliberately not taken into account (i.e. we only care
    1100          * about the left edge of the cursor).
    1101          */
    1102         if (x > geom.text_rect.p1.x)
    1103                 entry->scroll_pos -= x - geom.text_rect.p1.x;
    1104 
    1105         width = gfx_text_width(res->font, entry->text);
    1106 
    1107         if (width < geom.text_rect.p1.x - geom.text_rect.p0.x &&
    1108             (realign || entry->halign != gfx_halign_left)) {
    1109                 /* Text fits inside entry, so we need to align it */
    1110                 anchor.x = geom.anchor_x;
    1111                 anchor.y = 0;
    1112                 gfx_text_fmt_init(&fmt);
    1113                 fmt.halign = entry->halign;
    1114                 gfx_text_start_pos(res->font, &anchor, &fmt, entry->text,
    1115                     &tpos);
    1116                 entry->scroll_pos = tpos.x - geom.text_rect.p0.x;
    1117         } else if (geom.text_pos.x + width < geom.text_rect.p1.x &&
    1118             entry->halign != gfx_halign_left) {
    1119                 /* Text is long, unused space on the right */
    1120                 entry->scroll_pos += geom.text_rect.p1.x -
    1121                     geom.text_pos.x - width;
    1122         }
    1123 }
    1124 
    1125303/** @}
    1126304 */
Note: See TracChangeset for help on using the changeset viewer.