Ignore:
File:
1 edited

Legend:

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

    rd0dfbba r03145ee  
    11/*
    2  * Copyright (c) 2026 Jiri Svoboda
     2 * Copyright (c) 2020 Jiri Svoboda
    33 * All rights reserved.
    44 *
     
    3737 */
    3838
    39 #include <assert.h>
    40 #include <clipboard.h>
    4139#include <errno.h>
    4240#include <gfx/context.h>
    43 #include <gfx/cursor.h>
    4441#include <gfx/render.h>
    4542#include <gfx/text.h>
    46 #include <macros.h>
    47 #include <stdio.h>
    4843#include <stdlib.h>
    4944#include <str.h>
     
    5146#include <ui/paint.h>
    5247#include <ui/entry.h>
    53 #include <ui/ui.h>
    54 #include <ui/window.h>
    5548#include "../private/entry.h"
    5649#include "../private/resource.h"
     
    5851static void ui_entry_ctl_destroy(void *);
    5952static errno_t ui_entry_ctl_paint(void *);
    60 static ui_evclaim_t ui_entry_ctl_kbd_event(void *, kbd_event_t *);
    6153static ui_evclaim_t ui_entry_ctl_pos_event(void *, pos_event_t *);
    6254
    6355enum {
    6456        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
    7458};
    7559
     
    7862        .destroy = ui_entry_ctl_destroy,
    7963        .paint = ui_entry_ctl_paint,
    80         .kbd_event = ui_entry_ctl_kbd_event,
    8164        .pos_event = ui_entry_ctl_pos_event
    8265};
     
    8467/** Create new text entry.
    8568 *
    86  * @param window UI window
     69 * @param resource UI resource
    8770 * @param text Text
    8871 * @param rentry Place to store pointer to new text entry
    8972 * @return EOK on success, ENOMEM if out of memory
    9073 */
    91 errno_t ui_entry_create(ui_window_t *window, const char *text,
     74errno_t ui_entry_create(ui_resource_t *resource, const char *text,
    9275    ui_entry_t **rentry)
    9376{
     
    11295        }
    11396
    114         entry->window = window;
     97        entry->res = resource;
    11598        entry->halign = gfx_halign_left;
    11699        *rentry = entry;
    117 
    118100        return EOK;
    119101}
     
    160142{
    161143        entry->halign = halign;
    162         ui_entry_scroll_update(entry, true);
    163         ui_entry_paint(entry);
    164 }
    165 
    166 /** Set text entry read-only flag.
    167  *
    168  * @param entry Text entry
    169  * @param read_only True iff entry is to be read-only.
    170  */
    171 void ui_entry_set_read_only(ui_entry_t *entry, bool read_only)
    172 {
    173         entry->read_only = read_only;
    174144}
    175145
     
    190160        free(entry->text);
    191161        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);
    197162
    198163        return EOK;
    199164}
    200165
    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
    214169 * @return EOK on success or an error code
    215170 */
    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;
     171errno_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;
    221177        errno_t rc;
    222178
    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;
    228214        }
    229215
    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);
    243227        if (rc != EOK)
    244228                goto error;
     
    249233}
    250234
    251 /** Return width of text before cursor.
    252  *
    253  * @param entry Text entry
    254  * @return Widht of text before cursor
    255  */
    256 static gfx_coord_t ui_entry_lwidth(ui_entry_t *entry)
    257 {
    258         ui_resource_t *res;
    259         uint8_t tmp;
    260         gfx_coord_t width;
    261 
    262         res = ui_window_get_res(entry->window);
    263 
    264         tmp = entry->text[entry->pos];
    265 
    266         entry->text[entry->pos] = '\0';
    267         width = gfx_text_width(res->font, entry->text);
    268         entry->text[entry->pos] = tmp;
    269 
    270         return width;
    271 }
    272 
    273 /** Paint text entry.
    274  *
    275  * @param entry Text entry
    276  * @return EOK on success or an error code
    277  */
    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 entry
    411  * @param fpos Position for which we need to find text offset
    412  * @return Corresponding byte offset in entry text
    413  */
    414 size_t ui_entry_find_pos(ui_entry_t *entry, gfx_coord2_t *fpos)
    415 {
    416         ui_resource_t *res;
    417         ui_entry_geom_t geom;
    418         gfx_text_fmt_t fmt;
    419 
    420         res = ui_window_get_res(entry->window);
    421 
    422         ui_entry_get_geom(entry, &geom);
    423 
    424         gfx_text_fmt_init(&fmt);
    425         fmt.font = res->font;
    426         fmt.halign = gfx_halign_left;
    427         fmt.valign = gfx_valign_top;
    428 
    429         return gfx_text_find_pos(&geom.text_pos, &fmt, entry->text, fpos);
    430 }
    431 
    432235/** Destroy text entry control.
    433236 *
     
    451254
    452255        return ui_entry_paint(entry);
    453 }
    454 
    455 /** Delete selected text.
    456  *
    457  * @param entry Text entry
    458  */
    459 void ui_entry_delete_sel(ui_entry_t *entry)
    460 {
    461         size_t off1;
    462         size_t off2;
    463 
    464         off1 = min(entry->sel_start, entry->pos);
    465         off2 = max(entry->sel_start, entry->pos);
    466 
    467         memmove(entry->text + off1, entry->text + off2,
    468             str_size(entry->text + off2) + 1);
    469 
    470         entry->pos = off1;
    471         entry->sel_start = off1;
    472         ui_entry_scroll_update(entry, false);
    473         ui_entry_paint(entry);
    474 }
    475 
    476 /** Insert string at cursor position.
    477  *
    478  * @param entry Text entry
    479  * @param str String
    480  * @return EOK on success, ENOMEM if out of memory
    481  */
    482 errno_t ui_entry_insert_str(ui_entry_t *entry, const char *str)
    483 {
    484         uint8_t tmp;
    485         char *ltext = NULL;
    486         char *newtext;
    487         char *oldtext;
    488         int rc;
    489 
    490         /* Do we have a selection? */
    491         if (entry->sel_start != entry->pos)
    492                 ui_entry_delete_sel(entry);
    493 
    494         tmp = entry->text[entry->pos];
    495         entry->text[entry->pos] = '\0';
    496         ltext = str_dup(entry->text);
    497         if (ltext == NULL)
    498                 return ENOMEM;
    499 
    500         entry->text[entry->pos] = tmp;
    501 
    502         rc = asprintf(&newtext, "%s%s%s", ltext, str, entry->text + entry->pos);
    503         if (rc < 0) {
    504                 free(ltext);
    505                 return ENOMEM;
    506         }
    507 
    508         oldtext = entry->text;
    509         entry->text = newtext;
    510         entry->pos += str_size(str);
    511         free(oldtext);
    512         free(ltext);
    513 
    514         entry->sel_start = entry->pos;
    515         ui_entry_scroll_update(entry, false);
    516         ui_entry_paint(entry);
    517 
    518         return EOK;
    519 }
    520 
    521 /** Delete character before cursor.
    522  *
    523  * @param entry Text entry
    524  */
    525 void ui_entry_backspace(ui_entry_t *entry)
    526 {
    527         size_t off;
    528 
    529         /* Do we have a selection? */
    530         if (entry->sel_start != entry->pos) {
    531                 ui_entry_delete_sel(entry);
    532                 return;
    533         }
    534 
    535         if (entry->pos == 0)
    536                 return;
    537 
    538         /* Find offset where character before cursor starts */
    539         off = entry->pos;
    540         (void) str_decode_reverse(entry->text, &off,
    541             str_size(entry->text));
    542 
    543         memmove(entry->text + off, entry->text + entry->pos,
    544             str_size(entry->text + entry->pos) + 1);
    545         entry->pos = off;
    546         entry->sel_start = off;
    547 
    548         ui_entry_scroll_update(entry, false);
    549         ui_entry_paint(entry);
    550 }
    551 
    552 /** Delete character after cursor.
    553  *
    554  * @param entry Text entry
    555  */
    556 void ui_entry_delete(ui_entry_t *entry)
    557 {
    558         size_t off;
    559 
    560         /* Do we have a selection? */
    561         if (entry->sel_start != entry->pos) {
    562                 ui_entry_delete_sel(entry);
    563                 return;
    564         }
    565 
    566         /* Find offset where character after cursor ends */
    567         off = entry->pos;
    568         (void) str_decode(entry->text, &off,
    569             str_size(entry->text));
    570 
    571         memmove(entry->text + entry->pos, entry->text + off,
    572             str_size(entry->text + off) + 1);
    573 
    574         ui_entry_scroll_update(entry, false);
    575         ui_entry_paint(entry);
    576 }
    577 
    578 /** Copy selected text to clipboard.
    579  *
    580  * @param entry Text entry
    581  */
    582 void ui_entry_copy(ui_entry_t *entry)
    583 {
    584         unsigned off1;
    585         unsigned off2;
    586         char c;
    587 
    588         off1 = min(entry->pos, entry->sel_start);
    589         off2 = max(entry->pos, entry->sel_start);
    590 
    591         c = entry->text[off2];
    592         entry->text[off2] = '\0';
    593 
    594         (void) clipboard_put_str(entry->text + off1);
    595 
    596         entry->text[off2] = c;
    597 }
    598 
    599 /** Cut selected text to clipboard.
    600  *
    601  * @param entry Text entry
    602  */
    603 void ui_entry_cut(ui_entry_t *entry)
    604 {
    605         ui_entry_copy(entry);
    606         ui_entry_delete_sel(entry);
    607 }
    608 
    609 /** Paste text from clipboard.
    610  *
    611  * @param entry Text entry
    612  */
    613 void ui_entry_paste(ui_entry_t *entry)
    614 {
    615         char *str;
    616         errno_t rc;
    617 
    618         rc = clipboard_get_str(&str);
    619         if (rc != EOK)
    620                 return;
    621 
    622         ui_entry_insert_str(entry, str);
    623         free(str);
    624 }
    625 
    626 /** Handle text entry key press without modifiers.
    627  *
    628  * @param entry Text entry
    629  * @param kbd_event Keyboard event
    630  * @return @c ui_claimed iff the event is claimed
    631  */
    632 ui_evclaim_t ui_entry_key_press_unmod(ui_entry_t *entry, kbd_event_t *event)
    633 {
    634         assert(event->type == KEY_PRESS);
    635 
    636         switch (event->key) {
    637         case KC_BACKSPACE:
    638                 ui_entry_backspace(entry);
    639                 break;
    640 
    641         case KC_DELETE:
    642                 ui_entry_delete(entry);
    643                 break;
    644 
    645         case KC_ESCAPE:
    646                 ui_entry_deactivate(entry);
    647                 break;
    648 
    649         case KC_HOME:
    650                 ui_entry_seek_start(entry, false);
    651                 break;
    652 
    653         case KC_END:
    654                 ui_entry_seek_end(entry, false);
    655                 break;
    656 
    657         case KC_LEFT:
    658                 ui_entry_seek_prev_char(entry, false);
    659                 break;
    660 
    661         case KC_RIGHT:
    662                 ui_entry_seek_next_char(entry, false);
    663                 break;
    664 
    665         default:
    666                 break;
    667         }
    668         return ui_claimed;
    669 }
    670 
    671 /** Handle text entry key press with shift modifier.
    672  *
    673  * @param entry Text entry
    674  * @param kbd_event Keyboard event
    675  * @return @c ui_claimed iff the event is claimed
    676  */
    677 ui_evclaim_t ui_entry_key_press_shift(ui_entry_t *entry, kbd_event_t *event)
    678 {
    679         assert(event->type == KEY_PRESS);
    680 
    681         switch (event->key) {
    682         case KC_HOME:
    683                 ui_entry_seek_start(entry, true);
    684                 break;
    685 
    686         case KC_END:
    687                 ui_entry_seek_end(entry, true);
    688                 break;
    689 
    690         case KC_LEFT:
    691                 ui_entry_seek_prev_char(entry, true);
    692                 break;
    693 
    694         case KC_RIGHT:
    695                 ui_entry_seek_next_char(entry, true);
    696                 break;
    697 
    698         default:
    699                 break;
    700         }
    701 
    702         return ui_claimed;
    703 }
    704 
    705 /** Handle text entry key press with control modifier.
    706  *
    707  * @param entry Text entry
    708  * @param kbd_event Keyboard event
    709  * @return @c ui_claimed iff the event is claimed
    710  */
    711 ui_evclaim_t ui_entry_key_press_ctrl(ui_entry_t *entry, kbd_event_t *event)
    712 {
    713         assert(event->type == KEY_PRESS);
    714 
    715         switch (event->key) {
    716         case KC_C:
    717                 ui_entry_copy(entry);
    718                 break;
    719         case KC_V:
    720                 ui_entry_paste(entry);
    721                 break;
    722         case KC_X:
    723                 ui_entry_cut(entry);
    724                 break;
    725         default:
    726                 break;
    727         }
    728 
    729         return ui_claimed;
    730 }
    731 
    732 /** Handle text entry keyboard event.
    733  *
    734  * @param entry Text entry
    735  * @param kbd_event Keyboard event
    736  * @return @c ui_claimed iff the event is claimed
    737  */
    738 ui_evclaim_t ui_entry_kbd_event(ui_entry_t *entry, kbd_event_t *event)
    739 {
    740         char buf[STR_BOUNDS(1) + 1];
    741         size_t off;
    742         errno_t rc;
    743 
    744         if (!entry->active)
    745                 return ui_unclaimed;
    746 
    747         /*
    748          * Need to keep track if any shift is held for the case
    749          * of selecting by shift-click. This could be simplified
    750          * if position events were decorated with modifier
    751          * state.
    752          */
    753 
    754         if (event->type == KEY_PRESS && event->key == KC_LSHIFT)
    755                 entry->lshift_held = true;
    756         if (event->type == KEY_RELEASE && event->key == KC_LSHIFT)
    757                 entry->lshift_held = false;
    758         if (event->type == KEY_PRESS && event->key == KC_RSHIFT)
    759                 entry->rshift_held = true;
    760         if (event->type == KEY_RELEASE && event->key == KC_RSHIFT)
    761                 entry->rshift_held = false;
    762 
    763         if (event->type == KEY_PRESS &&
    764             (event->mods & (KM_CTRL | KM_ALT)) == 0 && event->c >= ' ') {
    765                 off = 0;
    766                 rc = chr_encode(event->c, buf, &off, sizeof(buf));
    767                 if (rc == EOK) {
    768                         buf[off] = '\0';
    769                         (void) ui_entry_insert_str(entry, buf);
    770                 }
    771         }
    772 
    773         if (event->type == KEY_PRESS &&
    774             (event->mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0)
    775                 return ui_entry_key_press_unmod(entry, event);
    776 
    777         if (event->type == KEY_PRESS &&
    778             (event->mods & KM_SHIFT) != 0 &&
    779             (event->mods & (KM_CTRL | KM_ALT)) == 0)
    780                 return ui_entry_key_press_shift(entry, event);
    781 
    782         if (event->type == KEY_PRESS &&
    783             (event->mods & KM_CTRL) != 0 &&
    784             (event->mods & (KM_ALT | KM_SHIFT)) == 0)
    785                 return ui_entry_key_press_ctrl(entry, event);
    786 
    787         return ui_claimed;
    788 }
    789 
    790 /** Handle text entry position event.
    791  *
    792  * @param entry Text entry
    793  * @param pos_event Position event
    794  * @return @c ui_claimed iff the event is claimed
    795  */
    796 ui_evclaim_t ui_entry_pos_event(ui_entry_t *entry, pos_event_t *event)
    797 {
    798         gfx_coord2_t pos;
    799 
    800         if (entry->read_only)
    801                 return ui_unclaimed;
    802 
    803         if (event->type == POS_UPDATE) {
    804                 pos.x = event->hpos;
    805                 pos.y = event->vpos;
    806 
    807                 /* Change cursor shape when pointer is entering/leaving */
    808                 if (gfx_pix_inside_rect(&pos, &entry->rect)) {
    809                         if (!entry->pointer_inside) {
    810                                 ui_window_set_ctl_cursor(entry->window,
    811                                     ui_curs_ibeam);
    812                                 entry->pointer_inside = true;
    813                         }
    814                 } else {
    815                         if (entry->pointer_inside) {
    816                                 ui_window_set_ctl_cursor(entry->window,
    817                                     ui_curs_arrow);
    818                                 entry->pointer_inside = false;
    819                         }
    820                 }
    821 
    822                 if (entry->held) {
    823                         /*
    824                          * Selecting using mouse drag: Change pos,
    825                          * keep sel_start
    826                          */
    827                         entry->pos = ui_entry_find_pos(entry, &pos);
    828                         ui_entry_paint(entry);
    829                 }
    830         }
    831 
    832         if (event->type == POS_PRESS) {
    833                 pos.x = event->hpos;
    834                 pos.y = event->vpos;
    835 
    836                 if (gfx_pix_inside_rect(&pos, &entry->rect)) {
    837                         /* Clicked inside - activate, set position */
    838                         entry->held = true;
    839                         entry->pos = ui_entry_find_pos(entry, &pos);
    840 
    841                         /* Clear selection if no shift key is held */
    842                         if (!entry->lshift_held && !entry->rshift_held)
    843                                 entry->sel_start = entry->pos;
    844 
    845                         if (entry->active)
    846                                 ui_entry_paint(entry);
    847                         else
    848                                 ui_entry_activate(entry);
    849 
    850                         return ui_claimed;
    851                 } else {
    852                         /* Clicked outside - deactivate */
    853                         ui_entry_deactivate(entry);
    854                 }
    855         }
    856 
    857         if (event->type == POS_RELEASE) {
    858                 entry->held = false;
    859         }
    860 
    861         return ui_unclaimed;
    862 }
    863 
    864 /** Handle text entry control keyboard event.
    865  *
    866  * @param arg Argument (ui_entry_t *)
    867  * @param kbd_event Keyboard event
    868  * @return @c ui_claimed iff the event is claimed
    869  */
    870 static ui_evclaim_t ui_entry_ctl_kbd_event(void *arg, kbd_event_t *event)
    871 {
    872         ui_entry_t *entry = (ui_entry_t *) arg;
    873 
    874         return ui_entry_kbd_event(entry, event);
    875256}
    876257
     
    881262 * @return @c ui_claimed iff the event is claimed
    882263 */
    883 static ui_evclaim_t ui_entry_ctl_pos_event(void *arg, pos_event_t *event)
     264ui_evclaim_t ui_entry_ctl_pos_event(void *arg, pos_event_t *event)
    884265{
    885266        ui_entry_t *entry = (ui_entry_t *) arg;
    886267
    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;
    1125270}
    1126271
Note: See TracChangeset for help on using the changeset viewer.