source: mainline/uspace/lib/ui/src/entry.c@ 9eb8d12

serial ticket/834-toolchain-update topic/fix-logger-deadlock topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 9eb8d12 was 9eb8d12, checked in by Jiri Svoboda <jiri@…>, 5 years ago

Entry text selection (using keyboard)

Text can be selected with movement keys while holding down Shift.
Selection can be deleted by pressing Backspace, Delete or typing
in replacement text.

  • Property mode set to 100644
File size: 18.9 KB
Line 
1/*
2 * Copyright (c) 2021 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libui
30 * @{
31 */
32/**
33 * @file Text entry.
34 *
35 * Currentry text entry is always read-only. It differs from label mostly
36 * by its looks.
37 */
38
39#include <errno.h>
40#include <gfx/context.h>
41#include <gfx/cursor.h>
42#include <gfx/render.h>
43#include <gfx/text.h>
44#include <macros.h>
45#include <stdlib.h>
46#include <str.h>
47#include <ui/control.h>
48#include <ui/paint.h>
49#include <ui/entry.h>
50#include <ui/ui.h>
51#include <ui/window.h>
52#include "../private/entry.h"
53#include "../private/resource.h"
54
55static void ui_entry_ctl_destroy(void *);
56static errno_t ui_entry_ctl_paint(void *);
57static ui_evclaim_t ui_entry_ctl_kbd_event(void *, kbd_event_t *);
58static ui_evclaim_t ui_entry_ctl_pos_event(void *, pos_event_t *);
59
60enum {
61 ui_entry_hpad = 4,
62 ui_entry_vpad = 4,
63 ui_entry_hpad_text = 1,
64 ui_entry_vpad_text = 0,
65 ui_entry_cursor_overshoot = 1,
66 ui_entry_cursor_width = 2,
67 ui_entry_sel_hpad = 0,
68 ui_entry_sel_vpad = 2
69};
70
71/** Text entry control ops */
72ui_control_ops_t ui_entry_ops = {
73 .destroy = ui_entry_ctl_destroy,
74 .paint = ui_entry_ctl_paint,
75 .kbd_event = ui_entry_ctl_kbd_event,
76 .pos_event = ui_entry_ctl_pos_event
77};
78
79/** Create new text entry.
80 *
81 * @param window UI window
82 * @param text Text
83 * @param rentry Place to store pointer to new text entry
84 * @return EOK on success, ENOMEM if out of memory
85 */
86errno_t ui_entry_create(ui_window_t *window, const char *text,
87 ui_entry_t **rentry)
88{
89 ui_entry_t *entry;
90 errno_t rc;
91
92 entry = calloc(1, sizeof(ui_entry_t));
93 if (entry == NULL)
94 return ENOMEM;
95
96 rc = ui_control_new(&ui_entry_ops, (void *) entry, &entry->control);
97 if (rc != EOK) {
98 free(entry);
99 return rc;
100 }
101
102 entry->text = str_dup(text);
103 if (entry->text == NULL) {
104 ui_control_delete(entry->control);
105 free(entry);
106 return ENOMEM;
107 }
108
109 entry->window = window;
110 entry->halign = gfx_halign_left;
111 *rentry = entry;
112
113 return EOK;
114}
115
116/** Destroy text entry.
117 *
118 * @param entry Text entry or @c NULL
119 */
120void ui_entry_destroy(ui_entry_t *entry)
121{
122 if (entry == NULL)
123 return;
124
125 ui_control_delete(entry->control);
126 free(entry);
127}
128
129/** Get base control from text entry.
130 *
131 * @param entry Text entry
132 * @return Control
133 */
134ui_control_t *ui_entry_ctl(ui_entry_t *entry)
135{
136 return entry->control;
137}
138
139/** Set text entry rectangle.
140 *
141 * @param entry Text entry
142 * @param rect New text entry rectangle
143 */
144void ui_entry_set_rect(ui_entry_t *entry, gfx_rect_t *rect)
145{
146 entry->rect = *rect;
147}
148
149/** Set text entry horizontal text alignment.
150 *
151 * @param entry Text entry
152 * @param halign Horizontal alignment
153 */
154void ui_entry_set_halign(ui_entry_t *entry, gfx_halign_t halign)
155{
156 entry->halign = halign;
157}
158
159/** Set text entry read-only flag.
160 *
161 * @param entry Text entry
162 * @param read_only True iff entry is to be read-only.
163 */
164void ui_entry_set_read_only(ui_entry_t *entry, bool read_only)
165{
166 entry->read_only = read_only;
167}
168
169/** Set entry text.
170 *
171 * @param entry Text entry
172 * @param text New entry text
173 * @return EOK on success, ENOMEM if out of memory
174 */
175errno_t ui_entry_set_text(ui_entry_t *entry, const char *text)
176{
177 char *tcopy;
178
179 tcopy = str_dup(text);
180 if (tcopy == NULL)
181 return ENOMEM;
182
183 free(entry->text);
184 entry->text = tcopy;
185 entry->pos = str_size(text);
186 entry->sel_start = entry->pos;
187
188 return EOK;
189}
190
191/** Paint cursor.
192 *
193 * @param entry Text entry
194 * @param pos Cursor position (top-left corner of next character)
195 * @return EOK on success or an error code
196 */
197static errno_t ui_entry_paint_cursor(ui_entry_t *entry, gfx_coord2_t *pos)
198{
199 ui_resource_t *res;
200 gfx_rect_t rect;
201 gfx_font_metrics_t metrics;
202 errno_t rc;
203
204 res = ui_window_get_res(entry->window);
205
206 if (res->textmode) {
207 rc = gfx_cursor_set_pos(res->gc, pos);
208 return rc;
209 }
210
211 gfx_font_get_metrics(res->font, &metrics);
212
213 rect.p0.x = pos->x;
214 rect.p0.y = pos->y - ui_entry_cursor_overshoot;
215 rect.p1.x = pos->x + ui_entry_cursor_width;
216 rect.p1.y = pos->y + metrics.ascent + metrics.descent + 1 +
217 ui_entry_cursor_overshoot;
218
219 rc = gfx_set_color(res->gc, res->entry_fg_color);
220 if (rc != EOK)
221 goto error;
222
223 rc = gfx_fill_rect(res->gc, &rect);
224 if (rc != EOK)
225 goto error;
226
227 return EOK;
228error:
229 return rc;
230}
231
232/** Return width of text before cursor.
233 *
234 * @param entry Text entry
235 * @return Widht of text before cursor
236 */
237static gfx_coord_t ui_entry_lwidth(ui_entry_t *entry)
238{
239 ui_resource_t *res;
240 uint8_t tmp;
241 gfx_coord_t width;
242
243 res = ui_window_get_res(entry->window);
244
245 tmp = entry->text[entry->pos];
246
247 entry->text[entry->pos] = '\0';
248 width = gfx_text_width(res->font, entry->text);
249 entry->text[entry->pos] = tmp;
250
251 return width;
252}
253
254/** Paint text entry.
255 *
256 * @param entry Text entry
257 * @return EOK on success or an error code
258 */
259errno_t ui_entry_paint(ui_entry_t *entry)
260{
261 ui_resource_t *res;
262 ui_entry_geom_t geom;
263 gfx_text_fmt_t fmt;
264 gfx_coord2_t pos;
265 gfx_text_fmt_t cfmt;
266 gfx_coord2_t cpos;
267 gfx_rect_t inside;
268 unsigned off1, off2;
269 gfx_rect_t sel;
270 char c;
271 errno_t rc;
272
273 res = ui_window_get_res(entry->window);
274
275 ui_entry_get_geom(entry, &geom);
276
277 if (res->textmode == false) {
278 /* Paint inset frame */
279 rc = ui_paint_inset_frame(res, &entry->rect, &inside);
280 if (rc != EOK)
281 goto error;
282 } else {
283 inside = entry->rect;
284 }
285
286 /* Paint entry background */
287
288 rc = gfx_set_color(res->gc, res->entry_bg_color);
289 if (rc != EOK)
290 goto error;
291
292 rc = gfx_fill_rect(res->gc, &inside);
293 if (rc != EOK)
294 goto error;
295
296 pos = geom.text_pos;
297
298 gfx_text_fmt_init(&fmt);
299 fmt.color = res->entry_fg_color;
300 fmt.halign = gfx_halign_left;
301 fmt.valign = gfx_valign_top;
302
303 rc = gfx_set_clip_rect(res->gc, &inside);
304 if (rc != EOK)
305 goto error;
306
307 off1 = min(entry->pos, entry->sel_start);
308 off2 = max(entry->pos, entry->sel_start);
309
310 /* Render initial segment before start of selection */
311 c = entry->text[off1];
312 entry->text[off1] = '\0';
313
314 rc = gfx_puttext(res->font, &pos, &fmt, entry->text);
315 if (rc != EOK) {
316 (void) gfx_set_clip_rect(res->gc, NULL);
317 goto error;
318 }
319
320 gfx_text_cont(res->font, &pos, &fmt, entry->text, &cpos, &cfmt);
321 entry->text[off1] = c;
322
323 /* Render selected text */
324
325 if (off1 != off2) {
326 c = entry->text[off2];
327 entry->text[off2] = '\0';
328 cfmt.color = res->entry_bg_color;
329
330 gfx_text_rect(res->font, &cpos, &cfmt, entry->text + off1, &sel);
331 sel.p0.x -= ui_entry_sel_hpad;
332 sel.p0.y -= ui_entry_sel_vpad;
333 sel.p1.x += ui_entry_sel_hpad;
334 sel.p1.y += ui_entry_sel_vpad;
335
336 rc = gfx_set_color(res->gc, res->entry_fg_color);
337 if (rc != EOK)
338 goto error;
339
340 rc = gfx_fill_rect(res->gc, &sel);
341 if (rc != EOK)
342 goto error;
343
344 rc = gfx_puttext(res->font, &cpos, &cfmt, entry->text + off1);
345 if (rc != EOK) {
346 (void) gfx_set_clip_rect(res->gc, NULL);
347 goto error;
348 }
349
350 gfx_text_cont(res->font, &cpos, &cfmt, entry->text + off1,
351 &cpos, &cfmt);
352
353 entry->text[off2] = c;
354 }
355
356 /* Render trailing, non-selected text */
357 cfmt.color = res->entry_fg_color;
358
359 rc = gfx_puttext(res->font, &cpos, &cfmt, entry->text + off2);
360 if (rc != EOK) {
361 (void) gfx_set_clip_rect(res->gc, NULL);
362 goto error;
363 }
364
365 if (entry->active) {
366 /* Cursor */
367 pos.x += ui_entry_lwidth(entry);
368
369 rc = ui_entry_paint_cursor(entry, &pos);
370 if (rc != EOK) {
371 (void) gfx_set_clip_rect(res->gc, NULL);
372 goto error;
373 }
374 }
375
376 rc = gfx_set_clip_rect(res->gc, NULL);
377 if (rc != EOK)
378 goto error;
379
380 rc = gfx_update(res->gc);
381 if (rc != EOK)
382 goto error;
383
384 return EOK;
385error:
386 return rc;
387}
388
389/** Find position in text entry.
390 *
391 * @param entry Text entry
392 * @param fpos Position for which we need to find text offset
393 * @return Corresponding byte offset in entry text
394 */
395size_t ui_entry_find_pos(ui_entry_t *entry, gfx_coord2_t *fpos)
396{
397 ui_resource_t *res;
398 ui_entry_geom_t geom;
399 gfx_text_fmt_t fmt;
400
401 res = ui_window_get_res(entry->window);
402
403 ui_entry_get_geom(entry, &geom);
404
405 gfx_text_fmt_init(&fmt);
406 fmt.halign = gfx_halign_left;
407 fmt.valign = gfx_valign_top;
408
409 return gfx_text_find_pos(res->font, &geom.text_pos, &fmt,
410 entry->text, fpos);
411}
412
413/** Destroy text entry control.
414 *
415 * @param arg Argument (ui_entry_t *)
416 */
417void ui_entry_ctl_destroy(void *arg)
418{
419 ui_entry_t *entry = (ui_entry_t *) arg;
420
421 ui_entry_destroy(entry);
422}
423
424/** Paint text entry control.
425 *
426 * @param arg Argument (ui_entry_t *)
427 * @return EOK on success or an error code
428 */
429errno_t ui_entry_ctl_paint(void *arg)
430{
431 ui_entry_t *entry = (ui_entry_t *) arg;
432
433 return ui_entry_paint(entry);
434}
435
436/** Delete selected text.
437 *
438 * @param entry Text entry
439 */
440void ui_entry_delete_sel(ui_entry_t *entry)
441{
442 size_t off1;
443 size_t off2;
444
445 off1 = min(entry->sel_start, entry->pos);
446 off2 = max(entry->sel_start, entry->pos);
447
448 memmove(entry->text + off1, entry->text + off2,
449 str_size(entry->text + off2) + 1);
450
451 entry->pos = off1;
452 entry->sel_start = off1;
453 ui_entry_paint(entry);
454}
455
456/** Insert string at cursor position.
457 *
458 * @param entry Text entry
459 * @param str String
460 * @return EOK on success, ENOMEM if out of memory
461 */
462errno_t ui_entry_insert_str(ui_entry_t *entry, const char *str)
463{
464 uint8_t tmp;
465 char *ltext = NULL;
466 char *newtext;
467 char *oldtext;
468 int rc;
469
470 /* Do we have a selection? */
471 if (entry->sel_start != entry->pos)
472 ui_entry_delete_sel(entry);
473
474 tmp = entry->text[entry->pos];
475 entry->text[entry->pos] = '\0';
476 ltext = str_dup(entry->text);
477 if (ltext == NULL)
478 return ENOMEM;
479
480 entry->text[entry->pos] = tmp;
481
482 rc = asprintf(&newtext, "%s%s%s", ltext, str, entry->text + entry->pos);
483 if (rc < 0) {
484 free(ltext);
485 return ENOMEM;
486 }
487
488 oldtext = entry->text;
489 entry->text = newtext;
490 entry->pos += str_size(str);
491 free(oldtext);
492 free(ltext);
493
494 entry->sel_start = entry->pos;
495 ui_entry_paint(entry);
496
497 return EOK;
498}
499
500/** Delete character before cursor.
501 *
502 * @param entry Text entry
503 */
504void ui_entry_backspace(ui_entry_t *entry)
505{
506 size_t off;
507
508 /* Do we have a selection? */
509 if (entry->sel_start != entry->pos) {
510 ui_entry_delete_sel(entry);
511 return;
512 }
513
514 if (entry->pos == 0)
515 return;
516
517 /* Find offset where character before cursor starts */
518 off = entry->pos;
519 (void) str_decode_reverse(entry->text, &off,
520 str_size(entry->text));
521
522 memmove(entry->text + off, entry->text + entry->pos,
523 str_size(entry->text + entry->pos) + 1);
524 entry->pos = off;
525 entry->sel_start = off;
526
527 ui_entry_paint(entry);
528}
529
530/** Delete character after cursor.
531 *
532 * @param entry Text entry
533 */
534void ui_entry_delete(ui_entry_t *entry)
535{
536 size_t off;
537
538 /* Do we have a selection? */
539 if (entry->sel_start != entry->pos) {
540 ui_entry_delete_sel(entry);
541 return;
542 }
543
544 /* Find offset where character after cursor ends */
545 off = entry->pos;
546 (void) str_decode(entry->text, &off,
547 str_size(entry->text));
548
549 memmove(entry->text + entry->pos, entry->text + off,
550 str_size(entry->text + off) + 1);
551
552 ui_entry_paint(entry);
553}
554
555/** Handle text entry key press without modifiers.
556 *
557 * @param entry Text entry
558 * @param kbd_event Keyboard event
559 * @return @c ui_claimed iff the event is claimed
560 */
561ui_evclaim_t ui_entry_key_press_unmod(ui_entry_t *entry, kbd_event_t *event)
562{
563 assert(event->type == KEY_PRESS);
564
565 switch (event->key) {
566 case KC_BACKSPACE:
567 ui_entry_backspace(entry);
568 break;
569
570 case KC_DELETE:
571 ui_entry_delete(entry);
572 break;
573
574 case KC_ESCAPE:
575 ui_entry_deactivate(entry);
576 break;
577
578 case KC_HOME:
579 ui_entry_seek_start(entry, false);
580 break;
581
582 case KC_END:
583 ui_entry_seek_end(entry, false);
584 break;
585
586 case KC_LEFT:
587 ui_entry_seek_prev_char(entry, false);
588 break;
589
590 case KC_RIGHT:
591 ui_entry_seek_next_char(entry, false);
592 break;
593
594 default:
595 break;
596 }
597
598 return ui_claimed;
599}
600
601/** Handle text entry key press with shift modifier.
602 *
603 * @param entry Text entry
604 * @param kbd_event Keyboard event
605 * @return @c ui_claimed iff the event is claimed
606 */
607ui_evclaim_t ui_entry_key_press_shift(ui_entry_t *entry, kbd_event_t *event)
608{
609 assert(event->type == KEY_PRESS);
610
611 switch (event->key) {
612 case KC_HOME:
613 ui_entry_seek_start(entry, true);
614 break;
615
616 case KC_END:
617 ui_entry_seek_end(entry, true);
618 break;
619
620 case KC_LEFT:
621 ui_entry_seek_prev_char(entry, true);
622 break;
623
624 case KC_RIGHT:
625 ui_entry_seek_next_char(entry, true);
626 break;
627
628 default:
629 break;
630 }
631
632 return ui_claimed;
633}
634
635/** Handle text entry keyboard event.
636 *
637 * @param entry Text entry
638 * @param kbd_event Keyboard event
639 * @return @c ui_claimed iff the event is claimed
640 */
641ui_evclaim_t ui_entry_kbd_event(ui_entry_t *entry, kbd_event_t *event)
642{
643 char buf[STR_BOUNDS(1) + 1];
644 size_t off;
645 errno_t rc;
646
647 if (!entry->active)
648 return ui_unclaimed;
649
650 if (event->type == KEY_PRESS && event->c >= ' ') {
651 off = 0;
652 rc = chr_encode(event->c, buf, &off, sizeof(buf));
653 if (rc == EOK) {
654 buf[off] = '\0';
655 (void) ui_entry_insert_str(entry, buf);
656 }
657 }
658
659 if (event->type == KEY_PRESS &&
660 (event->mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0)
661 return ui_entry_key_press_unmod(entry, event);
662
663 if (event->type == KEY_PRESS &&
664 (event->mods & KM_SHIFT) != 0 &&
665 (event->mods & (KM_CTRL | KM_ALT)) == 0)
666 return ui_entry_key_press_shift(entry, event);
667
668 return ui_claimed;
669}
670
671/** Handle text entry position event.
672 *
673 * @param entry Text entry
674 * @param pos_event Position event
675 * @return @c ui_claimed iff the event is claimed
676 */
677ui_evclaim_t ui_entry_pos_event(ui_entry_t *entry, pos_event_t *event)
678{
679 gfx_coord2_t pos;
680
681 if (entry->read_only)
682 return ui_unclaimed;
683
684 if (event->type == POS_UPDATE) {
685 pos.x = event->hpos;
686 pos.y = event->vpos;
687
688 if (gfx_pix_inside_rect(&pos, &entry->rect)) {
689 if (!entry->pointer_inside) {
690 ui_window_set_ctl_cursor(entry->window,
691 ui_curs_ibeam);
692 entry->pointer_inside = true;
693 }
694 } else {
695 if (entry->pointer_inside) {
696 ui_window_set_ctl_cursor(entry->window,
697 ui_curs_arrow);
698 entry->pointer_inside = false;
699 }
700 }
701 }
702
703 if (event->type == POS_PRESS) {
704 pos.x = event->hpos;
705 pos.y = event->vpos;
706
707 if (gfx_pix_inside_rect(&pos, &entry->rect)) {
708 entry->pos = ui_entry_find_pos(entry, &pos);
709 entry->sel_start = entry->pos;
710 if (entry->active)
711 ui_entry_paint(entry);
712 else
713 ui_entry_activate(entry);
714
715 return ui_claimed;
716 } else {
717 ui_entry_deactivate(entry);
718 }
719 }
720
721 return ui_unclaimed;
722}
723
724/** Handle text entry control keyboard event.
725 *
726 * @param arg Argument (ui_entry_t *)
727 * @param kbd_event Keyboard event
728 * @return @c ui_claimed iff the event is claimed
729 */
730static ui_evclaim_t ui_entry_ctl_kbd_event(void *arg, kbd_event_t *event)
731{
732 ui_entry_t *entry = (ui_entry_t *) arg;
733
734 return ui_entry_kbd_event(entry, event);
735}
736
737/** Handle text entry control position event.
738 *
739 * @param arg Argument (ui_entry_t *)
740 * @param pos_event Position event
741 * @return @c ui_claimed iff the event is claimed
742 */
743static ui_evclaim_t ui_entry_ctl_pos_event(void *arg, pos_event_t *event)
744{
745 ui_entry_t *entry = (ui_entry_t *) arg;
746
747 return ui_entry_pos_event(entry, event);
748}
749
750/** Get text entry geometry.
751 *
752 * @param entry Text entry
753 * @param geom Structure to fill in with computed geometry
754 */
755void ui_entry_get_geom(ui_entry_t *entry, ui_entry_geom_t *geom)
756{
757 gfx_coord_t hpad;
758 gfx_coord_t vpad;
759 gfx_coord_t width;
760 ui_resource_t *res;
761
762 res = ui_window_get_res(entry->window);
763
764 if (res->textmode) {
765 hpad = ui_entry_hpad_text;
766 vpad = ui_entry_vpad_text;
767 } else {
768 hpad = ui_entry_hpad;
769 vpad = ui_entry_vpad;
770 }
771
772 if (res->textmode == false) {
773 ui_paint_get_inset_frame_inside(res, &entry->rect,
774 &geom->interior_rect);
775 } else {
776 geom->interior_rect = entry->rect;
777 }
778
779 width = gfx_text_width(res->font, entry->text);
780
781 switch (entry->halign) {
782 case gfx_halign_left:
783 case gfx_halign_justify:
784 geom->text_pos.x = geom->interior_rect.p0.x + hpad;
785 break;
786 case gfx_halign_center:
787 geom->text_pos.x = (geom->interior_rect.p0.x +
788 geom->interior_rect.p1.x) / 2 - width / 2;
789 break;
790 case gfx_halign_right:
791 geom->text_pos.x = geom->interior_rect.p1.x - hpad - 1 - width;
792 break;
793 }
794
795 geom->text_pos.y = geom->interior_rect.p0.y + vpad;
796}
797
798/** Activate text entry.
799 *
800 * @param entry Text entry
801 */
802void ui_entry_activate(ui_entry_t *entry)
803{
804 ui_resource_t *res;
805
806 res = ui_window_get_res(entry->window);
807
808 if (entry->active)
809 return;
810
811 entry->active = true;
812 (void) ui_entry_paint(entry);
813
814 if (res->textmode)
815 gfx_cursor_set_visible(res->gc, true);
816}
817
818/** Move text cursor to the beginning of text.
819 *
820 * @param entry Text entry
821 * @param shift @c true iff shift key is pressed
822 */
823void ui_entry_seek_start(ui_entry_t *entry, bool shift)
824{
825 entry->pos = 0;
826
827 if (!shift)
828 entry->sel_start = entry->pos;
829 (void) ui_entry_paint(entry);
830}
831
832/** Move text cursor to the end of text.
833 *
834 * @param entry Text entry
835 * @param shift @c true iff shift key is pressed
836 */
837void ui_entry_seek_end(ui_entry_t *entry, bool shift)
838{
839 entry->pos = str_size(entry->text);
840
841 if (!shift)
842 entry->sel_start = entry->pos;
843 (void) ui_entry_paint(entry);
844}
845
846/** Move text cursor one character backward.
847 *
848 * @param entry Text entry
849 * @param shift @c true iff shift key is pressed
850 */
851void ui_entry_seek_prev_char(ui_entry_t *entry, bool shift)
852{
853 size_t off;
854
855 off = entry->pos;
856 (void) str_decode_reverse(entry->text, &off,
857 str_size(entry->text));
858 entry->pos = off;
859
860 if (!shift)
861 entry->sel_start = entry->pos;
862 (void) ui_entry_paint(entry);
863}
864
865/** Move text cursor one character forward.
866 *
867 * @param entry Text entry
868 * @param shift @c true iff shift key is pressed
869 */
870void ui_entry_seek_next_char(ui_entry_t *entry, bool shift)
871{
872 size_t off;
873
874 off = entry->pos;
875 (void) str_decode(entry->text, &off,
876 str_size(entry->text));
877 entry->pos = off;
878
879 if (!shift)
880 entry->sel_start = entry->pos;
881 (void) ui_entry_paint(entry);
882}
883
884/** Deactivate text entry.
885 *
886 * @param entry Text entry
887 */
888void ui_entry_deactivate(ui_entry_t *entry)
889{
890 ui_resource_t *res;
891
892 res = ui_window_get_res(entry->window);
893
894 if (!entry->active)
895 return;
896
897 entry->active = false;
898 entry->sel_start = entry->pos;
899 (void) ui_entry_paint(entry);
900
901 if (res->textmode)
902 gfx_cursor_set_visible(res->gc, false);
903}
904
905/** @}
906 */
Note: See TracBrowser for help on using the repository browser.