source: mainline/uspace/lib/ui/src/entry.c@ 282c86d

serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 282c86d was 282c86d, checked in by Jiri Svoboda <jiri@…>, 4 years ago

Selecting entry text by dragging the mouse

  • Property mode set to 100644
File size: 20.7 KB
RevLine 
[03145ee]1/*
[2ab8ab3]2 * Copyright (c) 2021 Jiri Svoboda
[03145ee]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
[c9722c1]39#include <clipboard.h>
[03145ee]40#include <errno.h>
41#include <gfx/context.h>
[bb14312]42#include <gfx/cursor.h>
[03145ee]43#include <gfx/render.h>
44#include <gfx/text.h>
[9eb8d12]45#include <macros.h>
[03145ee]46#include <stdlib.h>
47#include <str.h>
48#include <ui/control.h>
49#include <ui/paint.h>
50#include <ui/entry.h>
[9c7dc8e]51#include <ui/ui.h>
[db3895d]52#include <ui/window.h>
[03145ee]53#include "../private/entry.h"
54#include "../private/resource.h"
55
56static void ui_entry_ctl_destroy(void *);
57static errno_t ui_entry_ctl_paint(void *);
[7481ee19]58static ui_evclaim_t ui_entry_ctl_kbd_event(void *, kbd_event_t *);
[03145ee]59static ui_evclaim_t ui_entry_ctl_pos_event(void *, pos_event_t *);
60
61enum {
62 ui_entry_hpad = 4,
[9c7dc8e]63 ui_entry_vpad = 4,
64 ui_entry_hpad_text = 1,
[65ec18d]65 ui_entry_vpad_text = 0,
66 ui_entry_cursor_overshoot = 1,
[9eb8d12]67 ui_entry_cursor_width = 2,
68 ui_entry_sel_hpad = 0,
69 ui_entry_sel_vpad = 2
[03145ee]70};
71
72/** Text entry control ops */
73ui_control_ops_t ui_entry_ops = {
74 .destroy = ui_entry_ctl_destroy,
75 .paint = ui_entry_ctl_paint,
[7481ee19]76 .kbd_event = ui_entry_ctl_kbd_event,
[03145ee]77 .pos_event = ui_entry_ctl_pos_event
78};
79
80/** Create new text entry.
81 *
[db3895d]82 * @param window UI window
[03145ee]83 * @param text Text
84 * @param rentry Place to store pointer to new text entry
85 * @return EOK on success, ENOMEM if out of memory
86 */
[db3895d]87errno_t ui_entry_create(ui_window_t *window, const char *text,
[03145ee]88 ui_entry_t **rentry)
89{
90 ui_entry_t *entry;
91 errno_t rc;
92
93 entry = calloc(1, sizeof(ui_entry_t));
94 if (entry == NULL)
95 return ENOMEM;
96
97 rc = ui_control_new(&ui_entry_ops, (void *) entry, &entry->control);
98 if (rc != EOK) {
99 free(entry);
100 return rc;
101 }
102
103 entry->text = str_dup(text);
104 if (entry->text == NULL) {
105 ui_control_delete(entry->control);
106 free(entry);
107 return ENOMEM;
108 }
109
[db3895d]110 entry->window = window;
[03145ee]111 entry->halign = gfx_halign_left;
112 *rentry = entry;
[bb14312]113
[03145ee]114 return EOK;
115}
116
117/** Destroy text entry.
118 *
119 * @param entry Text entry or @c NULL
120 */
121void ui_entry_destroy(ui_entry_t *entry)
122{
123 if (entry == NULL)
124 return;
125
126 ui_control_delete(entry->control);
127 free(entry);
128}
129
130/** Get base control from text entry.
131 *
132 * @param entry Text entry
133 * @return Control
134 */
135ui_control_t *ui_entry_ctl(ui_entry_t *entry)
136{
137 return entry->control;
138}
139
140/** Set text entry rectangle.
141 *
142 * @param entry Text entry
143 * @param rect New text entry rectangle
144 */
145void ui_entry_set_rect(ui_entry_t *entry, gfx_rect_t *rect)
146{
147 entry->rect = *rect;
148}
149
150/** Set text entry horizontal text alignment.
151 *
152 * @param entry Text entry
153 * @param halign Horizontal alignment
154 */
155void ui_entry_set_halign(ui_entry_t *entry, gfx_halign_t halign)
156{
157 entry->halign = halign;
158}
159
[7481ee19]160/** Set text entry read-only flag.
161 *
162 * @param entry Text entry
163 * @param read_only True iff entry is to be read-only.
164 */
165void ui_entry_set_read_only(ui_entry_t *entry, bool read_only)
166{
167 entry->read_only = read_only;
168}
169
[03145ee]170/** Set entry text.
171 *
172 * @param entry Text entry
173 * @param text New entry text
174 * @return EOK on success, ENOMEM if out of memory
175 */
176errno_t ui_entry_set_text(ui_entry_t *entry, const char *text)
177{
178 char *tcopy;
179
180 tcopy = str_dup(text);
181 if (tcopy == NULL)
182 return ENOMEM;
183
184 free(entry->text);
185 entry->text = tcopy;
[61bf9dd9]186 entry->pos = str_size(text);
[9eb8d12]187 entry->sel_start = entry->pos;
[03145ee]188
189 return EOK;
190}
191
[f5819ca1]192/** Paint cursor.
193 *
194 * @param entry Text entry
195 * @param pos Cursor position (top-left corner of next character)
196 * @return EOK on success or an error code
197 */
[65ec18d]198static errno_t ui_entry_paint_cursor(ui_entry_t *entry, gfx_coord2_t *pos)
199{
200 ui_resource_t *res;
201 gfx_rect_t rect;
202 gfx_font_metrics_t metrics;
203 errno_t rc;
204
205 res = ui_window_get_res(entry->window);
206
[bb14312]207 if (res->textmode) {
208 rc = gfx_cursor_set_pos(res->gc, pos);
209 return rc;
210 }
[65ec18d]211
[bb14312]212 gfx_font_get_metrics(res->font, &metrics);
[65ec18d]213
214 rect.p0.x = pos->x;
215 rect.p0.y = pos->y - ui_entry_cursor_overshoot;
[bb14312]216 rect.p1.x = pos->x + ui_entry_cursor_width;
[65ec18d]217 rect.p1.y = pos->y + metrics.ascent + metrics.descent + 1 +
218 ui_entry_cursor_overshoot;
219
220 rc = gfx_set_color(res->gc, res->entry_fg_color);
221 if (rc != EOK)
222 goto error;
223
224 rc = gfx_fill_rect(res->gc, &rect);
225 if (rc != EOK)
226 goto error;
227
228 return EOK;
229error:
230 return rc;
231}
232
[61bf9dd9]233/** Return width of text before cursor.
234 *
235 * @param entry Text entry
236 * @return Widht of text before cursor
237 */
238static gfx_coord_t ui_entry_lwidth(ui_entry_t *entry)
239{
240 ui_resource_t *res;
241 uint8_t tmp;
242 gfx_coord_t width;
243
244 res = ui_window_get_res(entry->window);
245
246 tmp = entry->text[entry->pos];
247
248 entry->text[entry->pos] = '\0';
249 width = gfx_text_width(res->font, entry->text);
250 entry->text[entry->pos] = tmp;
251
252 return width;
253}
254
[03145ee]255/** Paint text entry.
256 *
257 * @param entry Text entry
258 * @return EOK on success or an error code
259 */
260errno_t ui_entry_paint(ui_entry_t *entry)
261{
[db3895d]262 ui_resource_t *res;
[d63623f]263 ui_entry_geom_t geom;
[03145ee]264 gfx_text_fmt_t fmt;
265 gfx_coord2_t pos;
[9eb8d12]266 gfx_text_fmt_t cfmt;
267 gfx_coord2_t cpos;
[03145ee]268 gfx_rect_t inside;
[9eb8d12]269 unsigned off1, off2;
270 gfx_rect_t sel;
271 char c;
[03145ee]272 errno_t rc;
273
[db3895d]274 res = ui_window_get_res(entry->window);
275
[d63623f]276 ui_entry_get_geom(entry, &geom);
[9c7dc8e]277
[db3895d]278 if (res->textmode == false) {
[cd74fa8]279 /* Paint inset frame */
[db3895d]280 rc = ui_paint_inset_frame(res, &entry->rect, &inside);
[cd74fa8]281 if (rc != EOK)
282 goto error;
283 } else {
284 inside = entry->rect;
285 }
[03145ee]286
287 /* Paint entry background */
288
[db3895d]289 rc = gfx_set_color(res->gc, res->entry_bg_color);
[03145ee]290 if (rc != EOK)
291 goto error;
292
[db3895d]293 rc = gfx_fill_rect(res->gc, &inside);
[03145ee]294 if (rc != EOK)
295 goto error;
296
[d63623f]297 pos = geom.text_pos;
[03145ee]298
299 gfx_text_fmt_init(&fmt);
[db3895d]300 fmt.color = res->entry_fg_color;
[7481ee19]301 fmt.halign = gfx_halign_left;
[03145ee]302 fmt.valign = gfx_valign_top;
303
[7481ee19]304 rc = gfx_set_clip_rect(res->gc, &inside);
305 if (rc != EOK)
306 goto error;
307
[9eb8d12]308 off1 = min(entry->pos, entry->sel_start);
309 off2 = max(entry->pos, entry->sel_start);
310
311 /* Render initial segment before start of selection */
312 c = entry->text[off1];
313 entry->text[off1] = '\0';
314
[db3895d]315 rc = gfx_puttext(res->font, &pos, &fmt, entry->text);
[7481ee19]316 if (rc != EOK) {
317 (void) gfx_set_clip_rect(res->gc, NULL);
318 goto error;
319 }
320
[9eb8d12]321 gfx_text_cont(res->font, &pos, &fmt, entry->text, &cpos, &cfmt);
322 entry->text[off1] = c;
323
324 /* Render selected text */
325
326 if (off1 != off2) {
327 c = entry->text[off2];
328 entry->text[off2] = '\0';
329 cfmt.color = res->entry_bg_color;
330
331 gfx_text_rect(res->font, &cpos, &cfmt, entry->text + off1, &sel);
332 sel.p0.x -= ui_entry_sel_hpad;
333 sel.p0.y -= ui_entry_sel_vpad;
334 sel.p1.x += ui_entry_sel_hpad;
335 sel.p1.y += ui_entry_sel_vpad;
336
337 rc = gfx_set_color(res->gc, res->entry_fg_color);
338 if (rc != EOK)
339 goto error;
340
341 rc = gfx_fill_rect(res->gc, &sel);
342 if (rc != EOK)
343 goto error;
344
345 rc = gfx_puttext(res->font, &cpos, &cfmt, entry->text + off1);
346 if (rc != EOK) {
347 (void) gfx_set_clip_rect(res->gc, NULL);
348 goto error;
349 }
350
351 gfx_text_cont(res->font, &cpos, &cfmt, entry->text + off1,
352 &cpos, &cfmt);
353
354 entry->text[off2] = c;
355 }
356
357 /* Render trailing, non-selected text */
358 cfmt.color = res->entry_fg_color;
359
360 rc = gfx_puttext(res->font, &cpos, &cfmt, entry->text + off2);
361 if (rc != EOK) {
362 (void) gfx_set_clip_rect(res->gc, NULL);
363 goto error;
364 }
365
[7481ee19]366 if (entry->active) {
367 /* Cursor */
[61bf9dd9]368 pos.x += ui_entry_lwidth(entry);
[7481ee19]369
[65ec18d]370 rc = ui_entry_paint_cursor(entry, &pos);
[7481ee19]371 if (rc != EOK) {
372 (void) gfx_set_clip_rect(res->gc, NULL);
373 goto error;
374 }
375 }
376
377 rc = gfx_set_clip_rect(res->gc, NULL);
[03145ee]378 if (rc != EOK)
379 goto error;
380
[db3895d]381 rc = gfx_update(res->gc);
[2ab8ab3]382 if (rc != EOK)
383 goto error;
384
[03145ee]385 return EOK;
386error:
387 return rc;
388}
389
[d63623f]390/** Find position in text entry.
391 *
392 * @param entry Text entry
393 * @param fpos Position for which we need to find text offset
394 * @return Corresponding byte offset in entry text
395 */
396size_t ui_entry_find_pos(ui_entry_t *entry, gfx_coord2_t *fpos)
397{
398 ui_resource_t *res;
399 ui_entry_geom_t geom;
400 gfx_text_fmt_t fmt;
401
402 res = ui_window_get_res(entry->window);
403
404 ui_entry_get_geom(entry, &geom);
405
406 gfx_text_fmt_init(&fmt);
407 fmt.halign = gfx_halign_left;
408 fmt.valign = gfx_valign_top;
409
410 return gfx_text_find_pos(res->font, &geom.text_pos, &fmt,
411 entry->text, fpos);
412}
413
[03145ee]414/** Destroy text entry control.
415 *
416 * @param arg Argument (ui_entry_t *)
417 */
418void ui_entry_ctl_destroy(void *arg)
419{
420 ui_entry_t *entry = (ui_entry_t *) arg;
421
422 ui_entry_destroy(entry);
423}
424
425/** Paint text entry control.
426 *
427 * @param arg Argument (ui_entry_t *)
428 * @return EOK on success or an error code
429 */
430errno_t ui_entry_ctl_paint(void *arg)
431{
432 ui_entry_t *entry = (ui_entry_t *) arg;
433
434 return ui_entry_paint(entry);
435}
436
[9eb8d12]437/** Delete selected text.
438 *
439 * @param entry Text entry
440 */
441void ui_entry_delete_sel(ui_entry_t *entry)
442{
443 size_t off1;
444 size_t off2;
445
446 off1 = min(entry->sel_start, entry->pos);
447 off2 = max(entry->sel_start, entry->pos);
448
449 memmove(entry->text + off1, entry->text + off2,
450 str_size(entry->text + off2) + 1);
451
452 entry->pos = off1;
453 entry->sel_start = off1;
454 ui_entry_paint(entry);
455}
456
[7481ee19]457/** Insert string at cursor position.
[03145ee]458 *
[7481ee19]459 * @param entry Text entry
460 * @param str String
461 * @return EOK on success, ENOMEM if out of memory
462 */
463errno_t ui_entry_insert_str(ui_entry_t *entry, const char *str)
464{
[61bf9dd9]465 uint8_t tmp;
466 char *ltext = NULL;
[7481ee19]467 char *newtext;
468 char *oldtext;
469 int rc;
470
[9eb8d12]471 /* Do we have a selection? */
472 if (entry->sel_start != entry->pos)
473 ui_entry_delete_sel(entry);
474
[61bf9dd9]475 tmp = entry->text[entry->pos];
476 entry->text[entry->pos] = '\0';
477 ltext = str_dup(entry->text);
478 if (ltext == NULL)
[7481ee19]479 return ENOMEM;
480
[61bf9dd9]481 entry->text[entry->pos] = tmp;
482
483 rc = asprintf(&newtext, "%s%s%s", ltext, str, entry->text + entry->pos);
484 if (rc < 0) {
485 free(ltext);
486 return ENOMEM;
487 }
488
[7481ee19]489 oldtext = entry->text;
490 entry->text = newtext;
[61bf9dd9]491 entry->pos += str_size(str);
[7481ee19]492 free(oldtext);
[61bf9dd9]493 free(ltext);
494
[9eb8d12]495 entry->sel_start = entry->pos;
[7481ee19]496 ui_entry_paint(entry);
497
498 return EOK;
499}
500
501/** Delete character before cursor.
502 *
503 * @param entry Text entry
504 */
505void ui_entry_backspace(ui_entry_t *entry)
506{
507 size_t off;
508
[9eb8d12]509 /* Do we have a selection? */
510 if (entry->sel_start != entry->pos) {
511 ui_entry_delete_sel(entry);
512 return;
513 }
514
[61bf9dd9]515 if (entry->pos == 0)
516 return;
517
518 /* Find offset where character before cursor starts */
519 off = entry->pos;
[7481ee19]520 (void) str_decode_reverse(entry->text, &off,
521 str_size(entry->text));
[61bf9dd9]522
523 memmove(entry->text + off, entry->text + entry->pos,
524 str_size(entry->text + entry->pos) + 1);
525 entry->pos = off;
[9eb8d12]526 entry->sel_start = off;
[61bf9dd9]527
528 ui_entry_paint(entry);
529}
530
531/** Delete character after cursor.
532 *
533 * @param entry Text entry
534 */
535void ui_entry_delete(ui_entry_t *entry)
536{
537 size_t off;
538
[9eb8d12]539 /* Do we have a selection? */
540 if (entry->sel_start != entry->pos) {
541 ui_entry_delete_sel(entry);
542 return;
543 }
544
545 /* Find offset where character after cursor ends */
[61bf9dd9]546 off = entry->pos;
547 (void) str_decode(entry->text, &off,
548 str_size(entry->text));
549
550 memmove(entry->text + entry->pos, entry->text + off,
551 str_size(entry->text + off) + 1);
552
[7481ee19]553 ui_entry_paint(entry);
554}
555
[c9722c1]556/** Copy selected text to clipboard.
557 *
558 * @param entry Text entry
559 */
560void ui_entry_copy(ui_entry_t *entry)
561{
562 unsigned off1;
563 unsigned off2;
564 char c;
565
566 off1 = min(entry->pos, entry->sel_start);
567 off2 = max(entry->pos, entry->sel_start);
568
569 c = entry->text[off2];
570 entry->text[off2] = '\0';
571
572 (void) clipboard_put_str(entry->text + off1);
573
574 entry->text[off2] = c;
575}
576
577/** Cut selected text to clipboard.
578 *
579 * @param entry Text entry
580 */
581void ui_entry_cut(ui_entry_t *entry)
582{
583 ui_entry_copy(entry);
584 ui_entry_delete_sel(entry);
585}
586
587/** Paste text from clipboard.
588 *
589 * @param entry Text entry
590 */
591void ui_entry_paste(ui_entry_t *entry)
592{
593 char *str;
594 errno_t rc;
595
596 rc = clipboard_get_str(&str);
597 if (rc != EOK)
598 return;
599
600 ui_entry_insert_str(entry, str);
601 free(str);
602}
603
[7481ee19]604/** Handle text entry key press without modifiers.
605 *
606 * @param entry Text entry
607 * @param kbd_event Keyboard event
608 * @return @c ui_claimed iff the event is claimed
609 */
610ui_evclaim_t ui_entry_key_press_unmod(ui_entry_t *entry, kbd_event_t *event)
611{
612 assert(event->type == KEY_PRESS);
613
[61bf9dd9]614 switch (event->key) {
615 case KC_BACKSPACE:
[7481ee19]616 ui_entry_backspace(entry);
[61bf9dd9]617 break;
618
619 case KC_DELETE:
620 ui_entry_delete(entry);
621 break;
[7481ee19]622
[61bf9dd9]623 case KC_ESCAPE:
[bb14312]624 ui_entry_deactivate(entry);
[61bf9dd9]625 break;
626
627 case KC_HOME:
[9eb8d12]628 ui_entry_seek_start(entry, false);
629 break;
630
631 case KC_END:
632 ui_entry_seek_end(entry, false);
633 break;
634
635 case KC_LEFT:
636 ui_entry_seek_prev_char(entry, false);
637 break;
638
639 case KC_RIGHT:
640 ui_entry_seek_next_char(entry, false);
641 break;
642
643 default:
644 break;
645 }
646
647 return ui_claimed;
648}
649
650/** Handle text entry key press with shift modifier.
651 *
652 * @param entry Text entry
653 * @param kbd_event Keyboard event
654 * @return @c ui_claimed iff the event is claimed
655 */
656ui_evclaim_t ui_entry_key_press_shift(ui_entry_t *entry, kbd_event_t *event)
657{
658 assert(event->type == KEY_PRESS);
659
660 switch (event->key) {
661 case KC_HOME:
662 ui_entry_seek_start(entry, true);
[61bf9dd9]663 break;
664
665 case KC_END:
[9eb8d12]666 ui_entry_seek_end(entry, true);
[61bf9dd9]667 break;
668
669 case KC_LEFT:
[9eb8d12]670 ui_entry_seek_prev_char(entry, true);
[61bf9dd9]671 break;
672
673 case KC_RIGHT:
[9eb8d12]674 ui_entry_seek_next_char(entry, true);
[61bf9dd9]675 break;
676
677 default:
678 break;
679 }
[7481ee19]680
681 return ui_claimed;
682}
683
[c9722c1]684/** Handle text entry key press with control modifier.
685 *
686 * @param entry Text entry
687 * @param kbd_event Keyboard event
688 * @return @c ui_claimed iff the event is claimed
689 */
690ui_evclaim_t ui_entry_key_press_ctrl(ui_entry_t *entry, kbd_event_t *event)
691{
692 assert(event->type == KEY_PRESS);
693
694 switch (event->key) {
695 case KC_C:
696 ui_entry_copy(entry);
697 break;
698 case KC_V:
699 ui_entry_paste(entry);
700 break;
701 case KC_X:
702 ui_entry_cut(entry);
703 break;
704 default:
705 break;
706 }
707
708 return ui_claimed;
709}
710
[7481ee19]711/** Handle text entry keyboard event.
712 *
713 * @param entry Text entry
714 * @param kbd_event Keyboard event
715 * @return @c ui_claimed iff the event is claimed
716 */
717ui_evclaim_t ui_entry_kbd_event(ui_entry_t *entry, kbd_event_t *event)
718{
719 char buf[STR_BOUNDS(1) + 1];
720 size_t off;
721 errno_t rc;
722
723 if (!entry->active)
724 return ui_unclaimed;
725
726 if (event->type == KEY_PRESS && event->c >= ' ') {
727 off = 0;
728 rc = chr_encode(event->c, buf, &off, sizeof(buf));
729 if (rc == EOK) {
730 buf[off] = '\0';
731 (void) ui_entry_insert_str(entry, buf);
732 }
733 }
734
735 if (event->type == KEY_PRESS &&
736 (event->mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0)
737 return ui_entry_key_press_unmod(entry, event);
738
[9eb8d12]739 if (event->type == KEY_PRESS &&
740 (event->mods & KM_SHIFT) != 0 &&
741 (event->mods & (KM_CTRL | KM_ALT)) == 0)
742 return ui_entry_key_press_shift(entry, event);
743
[c9722c1]744 if (event->type == KEY_PRESS &&
745 (event->mods & KM_CTRL) != 0 &&
746 (event->mods & (KM_ALT | KM_SHIFT)) == 0)
747 return ui_entry_key_press_ctrl(entry, event);
748
[7481ee19]749 return ui_claimed;
750}
751
752/** Handle text entry position event.
753 *
754 * @param entry Text entry
[03145ee]755 * @param pos_event Position event
756 * @return @c ui_claimed iff the event is claimed
757 */
[7481ee19]758ui_evclaim_t ui_entry_pos_event(ui_entry_t *entry, pos_event_t *event)
[03145ee]759{
[db3895d]760 gfx_coord2_t pos;
761
[7481ee19]762 if (entry->read_only)
763 return ui_unclaimed;
764
[db3895d]765 if (event->type == POS_UPDATE) {
766 pos.x = event->hpos;
767 pos.y = event->vpos;
768
[282c86d]769 /* Change cursor shape when pointer is entering/leaving */
[db3895d]770 if (gfx_pix_inside_rect(&pos, &entry->rect)) {
771 if (!entry->pointer_inside) {
772 ui_window_set_ctl_cursor(entry->window,
773 ui_curs_ibeam);
774 entry->pointer_inside = true;
775 }
776 } else {
777 if (entry->pointer_inside) {
778 ui_window_set_ctl_cursor(entry->window,
779 ui_curs_arrow);
780 entry->pointer_inside = false;
781 }
782 }
[282c86d]783
784 if (entry->held) {
785 /*
786 * Selecting using mouse drag: Change pos,
787 * keep sel_start
788 */
789 entry->pos = ui_entry_find_pos(entry, &pos);
790 ui_entry_paint(entry);
791 }
[db3895d]792 }
[03145ee]793
[7481ee19]794 if (event->type == POS_PRESS) {
795 pos.x = event->hpos;
796 pos.y = event->vpos;
797
798 if (gfx_pix_inside_rect(&pos, &entry->rect)) {
[282c86d]799 /* Clicked inside - activate, set position */
800 entry->held = true;
[d63623f]801 entry->pos = ui_entry_find_pos(entry, &pos);
[9eb8d12]802 entry->sel_start = entry->pos;
[d63623f]803 if (entry->active)
804 ui_entry_paint(entry);
805 else
806 ui_entry_activate(entry);
[7481ee19]807
808 return ui_claimed;
809 } else {
[282c86d]810 /* Clicked outside - deactivate */
[bb14312]811 ui_entry_deactivate(entry);
[7481ee19]812 }
813 }
814
[282c86d]815 if (event->type == POS_RELEASE) {
816 entry->held = false;
817 }
818
[03145ee]819 return ui_unclaimed;
820}
821
[7481ee19]822/** Handle text entry control keyboard event.
823 *
824 * @param arg Argument (ui_entry_t *)
825 * @param kbd_event Keyboard event
826 * @return @c ui_claimed iff the event is claimed
827 */
828static ui_evclaim_t ui_entry_ctl_kbd_event(void *arg, kbd_event_t *event)
829{
830 ui_entry_t *entry = (ui_entry_t *) arg;
831
832 return ui_entry_kbd_event(entry, event);
833}
834
835/** Handle text entry control position event.
836 *
837 * @param arg Argument (ui_entry_t *)
838 * @param pos_event Position event
839 * @return @c ui_claimed iff the event is claimed
840 */
841static ui_evclaim_t ui_entry_ctl_pos_event(void *arg, pos_event_t *event)
842{
843 ui_entry_t *entry = (ui_entry_t *) arg;
844
845 return ui_entry_pos_event(entry, event);
846}
847
[d63623f]848/** Get text entry geometry.
849 *
850 * @param entry Text entry
851 * @param geom Structure to fill in with computed geometry
852 */
853void ui_entry_get_geom(ui_entry_t *entry, ui_entry_geom_t *geom)
854{
855 gfx_coord_t hpad;
856 gfx_coord_t vpad;
857 gfx_coord_t width;
858 ui_resource_t *res;
859
860 res = ui_window_get_res(entry->window);
861
862 if (res->textmode) {
863 hpad = ui_entry_hpad_text;
864 vpad = ui_entry_vpad_text;
865 } else {
866 hpad = ui_entry_hpad;
867 vpad = ui_entry_vpad;
868 }
869
870 if (res->textmode == false) {
871 ui_paint_get_inset_frame_inside(res, &entry->rect,
872 &geom->interior_rect);
873 } else {
874 geom->interior_rect = entry->rect;
875 }
876
877 width = gfx_text_width(res->font, entry->text);
878
879 switch (entry->halign) {
880 case gfx_halign_left:
881 case gfx_halign_justify:
882 geom->text_pos.x = geom->interior_rect.p0.x + hpad;
883 break;
884 case gfx_halign_center:
885 geom->text_pos.x = (geom->interior_rect.p0.x +
886 geom->interior_rect.p1.x) / 2 - width / 2;
887 break;
888 case gfx_halign_right:
889 geom->text_pos.x = geom->interior_rect.p1.x - hpad - 1 - width;
890 break;
891 }
892
893 geom->text_pos.y = geom->interior_rect.p0.y + vpad;
894}
895
[bb14312]896/** Activate text entry.
897 *
898 * @param entry Text entry
899 */
900void ui_entry_activate(ui_entry_t *entry)
901{
902 ui_resource_t *res;
903
904 res = ui_window_get_res(entry->window);
905
906 if (entry->active)
907 return;
908
909 entry->active = true;
910 (void) ui_entry_paint(entry);
911
912 if (res->textmode)
913 gfx_cursor_set_visible(res->gc, true);
914}
915
[61bf9dd9]916/** Move text cursor to the beginning of text.
917 *
918 * @param entry Text entry
[9eb8d12]919 * @param shift @c true iff shift key is pressed
[61bf9dd9]920 */
[9eb8d12]921void ui_entry_seek_start(ui_entry_t *entry, bool shift)
[61bf9dd9]922{
923 entry->pos = 0;
[9eb8d12]924
925 if (!shift)
926 entry->sel_start = entry->pos;
[61bf9dd9]927 (void) ui_entry_paint(entry);
928}
929
930/** Move text cursor to the end of text.
931 *
932 * @param entry Text entry
[9eb8d12]933 * @param shift @c true iff shift key is pressed
[61bf9dd9]934 */
[9eb8d12]935void ui_entry_seek_end(ui_entry_t *entry, bool shift)
[61bf9dd9]936{
937 entry->pos = str_size(entry->text);
[9eb8d12]938
939 if (!shift)
940 entry->sel_start = entry->pos;
[61bf9dd9]941 (void) ui_entry_paint(entry);
942}
943
944/** Move text cursor one character backward.
945 *
946 * @param entry Text entry
[9eb8d12]947 * @param shift @c true iff shift key is pressed
[61bf9dd9]948 */
[9eb8d12]949void ui_entry_seek_prev_char(ui_entry_t *entry, bool shift)
[61bf9dd9]950{
951 size_t off;
952
953 off = entry->pos;
954 (void) str_decode_reverse(entry->text, &off,
955 str_size(entry->text));
956 entry->pos = off;
[9eb8d12]957
958 if (!shift)
959 entry->sel_start = entry->pos;
[61bf9dd9]960 (void) ui_entry_paint(entry);
961}
962
963/** Move text cursor one character forward.
964 *
965 * @param entry Text entry
[9eb8d12]966 * @param shift @c true iff shift key is pressed
[61bf9dd9]967 */
[9eb8d12]968void ui_entry_seek_next_char(ui_entry_t *entry, bool shift)
[61bf9dd9]969{
970 size_t off;
971
972 off = entry->pos;
973 (void) str_decode(entry->text, &off,
974 str_size(entry->text));
975 entry->pos = off;
[9eb8d12]976
977 if (!shift)
978 entry->sel_start = entry->pos;
[61bf9dd9]979 (void) ui_entry_paint(entry);
980}
981
[bb14312]982/** Deactivate text entry.
983 *
984 * @param entry Text entry
985 */
986void ui_entry_deactivate(ui_entry_t *entry)
987{
988 ui_resource_t *res;
989
990 res = ui_window_get_res(entry->window);
991
992 if (!entry->active)
993 return;
994
995 entry->active = false;
[9eb8d12]996 entry->sel_start = entry->pos;
[bb14312]997 (void) ui_entry_paint(entry);
998
999 if (res->textmode)
1000 gfx_cursor_set_visible(res->gc, false);
1001}
1002
[03145ee]1003/** @}
1004 */
Note: See TracBrowser for help on using the repository browser.