source: mainline/uspace/lib/ui/src/entry.c@ 37d0dd4

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

Basic editable text entry

  • Property mode set to 100644
File size: 10.3 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
39#include <errno.h>
40#include <gfx/context.h>
41#include <gfx/render.h>
42#include <gfx/text.h>
43#include <stdlib.h>
44#include <str.h>
45#include <ui/control.h>
46#include <ui/paint.h>
47#include <ui/entry.h>
[9c7dc8e]48#include <ui/ui.h>
[db3895d]49#include <ui/window.h>
[03145ee]50#include "../private/entry.h"
51#include "../private/resource.h"
52
53static void ui_entry_ctl_destroy(void *);
54static errno_t ui_entry_ctl_paint(void *);
[7481ee19]55static ui_evclaim_t ui_entry_ctl_kbd_event(void *, kbd_event_t *);
[03145ee]56static ui_evclaim_t ui_entry_ctl_pos_event(void *, pos_event_t *);
57
58enum {
59 ui_entry_hpad = 4,
[9c7dc8e]60 ui_entry_vpad = 4,
61 ui_entry_hpad_text = 1,
62 ui_entry_vpad_text = 0
[03145ee]63};
64
65/** Text entry control ops */
66ui_control_ops_t ui_entry_ops = {
67 .destroy = ui_entry_ctl_destroy,
68 .paint = ui_entry_ctl_paint,
[7481ee19]69 .kbd_event = ui_entry_ctl_kbd_event,
[03145ee]70 .pos_event = ui_entry_ctl_pos_event
71};
72
73/** Create new text entry.
74 *
[db3895d]75 * @param window UI window
[03145ee]76 * @param text Text
77 * @param rentry Place to store pointer to new text entry
78 * @return EOK on success, ENOMEM if out of memory
79 */
[db3895d]80errno_t ui_entry_create(ui_window_t *window, const char *text,
[03145ee]81 ui_entry_t **rentry)
82{
83 ui_entry_t *entry;
84 errno_t rc;
85
86 entry = calloc(1, sizeof(ui_entry_t));
87 if (entry == NULL)
88 return ENOMEM;
89
90 rc = ui_control_new(&ui_entry_ops, (void *) entry, &entry->control);
91 if (rc != EOK) {
92 free(entry);
93 return rc;
94 }
95
96 entry->text = str_dup(text);
97 if (entry->text == NULL) {
98 ui_control_delete(entry->control);
99 free(entry);
100 return ENOMEM;
101 }
102
[db3895d]103 entry->window = window;
[03145ee]104 entry->halign = gfx_halign_left;
105 *rentry = entry;
106 return EOK;
107}
108
109/** Destroy text entry.
110 *
111 * @param entry Text entry or @c NULL
112 */
113void ui_entry_destroy(ui_entry_t *entry)
114{
115 if (entry == NULL)
116 return;
117
118 ui_control_delete(entry->control);
119 free(entry);
120}
121
122/** Get base control from text entry.
123 *
124 * @param entry Text entry
125 * @return Control
126 */
127ui_control_t *ui_entry_ctl(ui_entry_t *entry)
128{
129 return entry->control;
130}
131
132/** Set text entry rectangle.
133 *
134 * @param entry Text entry
135 * @param rect New text entry rectangle
136 */
137void ui_entry_set_rect(ui_entry_t *entry, gfx_rect_t *rect)
138{
139 entry->rect = *rect;
140}
141
142/** Set text entry horizontal text alignment.
143 *
144 * @param entry Text entry
145 * @param halign Horizontal alignment
146 */
147void ui_entry_set_halign(ui_entry_t *entry, gfx_halign_t halign)
148{
149 entry->halign = halign;
150}
151
[7481ee19]152/** Set text entry read-only flag.
153 *
154 * @param entry Text entry
155 * @param read_only True iff entry is to be read-only.
156 */
157void ui_entry_set_read_only(ui_entry_t *entry, bool read_only)
158{
159 entry->read_only = read_only;
160}
161
[03145ee]162/** Set entry text.
163 *
164 * @param entry Text entry
165 * @param text New entry text
166 * @return EOK on success, ENOMEM if out of memory
167 */
168errno_t ui_entry_set_text(ui_entry_t *entry, const char *text)
169{
170 char *tcopy;
171
172 tcopy = str_dup(text);
173 if (tcopy == NULL)
174 return ENOMEM;
175
176 free(entry->text);
177 entry->text = tcopy;
178
179 return EOK;
180}
181
182/** Paint text entry.
183 *
184 * @param entry Text entry
185 * @return EOK on success or an error code
186 */
187errno_t ui_entry_paint(ui_entry_t *entry)
188{
[db3895d]189 ui_resource_t *res;
[03145ee]190 gfx_text_fmt_t fmt;
191 gfx_coord2_t pos;
[9c7dc8e]192 gfx_coord_t hpad;
193 gfx_coord_t vpad;
[7481ee19]194 gfx_coord_t width;
[03145ee]195 gfx_rect_t inside;
196 errno_t rc;
197
[db3895d]198 res = ui_window_get_res(entry->window);
199
200 if (res->textmode) {
[9c7dc8e]201 hpad = ui_entry_hpad_text;
202 vpad = ui_entry_vpad_text;
203 } else {
204 hpad = ui_entry_hpad;
205 vpad = ui_entry_vpad;
206 }
207
[db3895d]208 if (res->textmode == false) {
[cd74fa8]209 /* Paint inset frame */
[db3895d]210 rc = ui_paint_inset_frame(res, &entry->rect, &inside);
[cd74fa8]211 if (rc != EOK)
212 goto error;
213 } else {
214 inside = entry->rect;
215 }
[03145ee]216
217 /* Paint entry background */
218
[db3895d]219 rc = gfx_set_color(res->gc, res->entry_bg_color);
[03145ee]220 if (rc != EOK)
221 goto error;
222
[db3895d]223 rc = gfx_fill_rect(res->gc, &inside);
[03145ee]224 if (rc != EOK)
225 goto error;
226
[7481ee19]227 width = gfx_text_width(res->font, entry->text);
228
[03145ee]229 switch (entry->halign) {
230 case gfx_halign_left:
231 case gfx_halign_justify:
[9c7dc8e]232 pos.x = inside.p0.x + hpad;
[03145ee]233 break;
234 case gfx_halign_center:
[7481ee19]235 pos.x = (inside.p0.x + inside.p1.x) / 2 - width / 2;
[03145ee]236 break;
237 case gfx_halign_right:
[7481ee19]238 pos.x = inside.p1.x - hpad - 1 - width;
[03145ee]239 break;
240 }
241
[9c7dc8e]242 pos.y = inside.p0.y + vpad;
[03145ee]243
244 gfx_text_fmt_init(&fmt);
[db3895d]245 fmt.color = res->entry_fg_color;
[7481ee19]246 fmt.halign = gfx_halign_left;
[03145ee]247 fmt.valign = gfx_valign_top;
248
[7481ee19]249 rc = gfx_set_clip_rect(res->gc, &inside);
250 if (rc != EOK)
251 goto error;
252
[db3895d]253 rc = gfx_puttext(res->font, &pos, &fmt, entry->text);
[7481ee19]254 if (rc != EOK) {
255 (void) gfx_set_clip_rect(res->gc, NULL);
256 goto error;
257 }
258
259 if (entry->active) {
260 /* Cursor */
261 pos.x += width;
262
263 rc = gfx_puttext(res->font, &pos, &fmt, "_");
264 if (rc != EOK) {
265 (void) gfx_set_clip_rect(res->gc, NULL);
266 goto error;
267 }
268 }
269
270 rc = gfx_set_clip_rect(res->gc, NULL);
[03145ee]271 if (rc != EOK)
272 goto error;
273
[db3895d]274 rc = gfx_update(res->gc);
[2ab8ab3]275 if (rc != EOK)
276 goto error;
277
[03145ee]278 return EOK;
279error:
280 return rc;
281}
282
283/** Destroy text entry control.
284 *
285 * @param arg Argument (ui_entry_t *)
286 */
287void ui_entry_ctl_destroy(void *arg)
288{
289 ui_entry_t *entry = (ui_entry_t *) arg;
290
291 ui_entry_destroy(entry);
292}
293
294/** Paint text entry control.
295 *
296 * @param arg Argument (ui_entry_t *)
297 * @return EOK on success or an error code
298 */
299errno_t ui_entry_ctl_paint(void *arg)
300{
301 ui_entry_t *entry = (ui_entry_t *) arg;
302
303 return ui_entry_paint(entry);
304}
305
[7481ee19]306/** Insert string at cursor position.
[03145ee]307 *
[7481ee19]308 * @param entry Text entry
309 * @param str String
310 * @return EOK on success, ENOMEM if out of memory
311 */
312errno_t ui_entry_insert_str(ui_entry_t *entry, const char *str)
313{
314 char *newtext;
315 char *oldtext;
316 int rc;
317
318 rc = asprintf(&newtext, "%s%s", entry->text, str);
319 if (rc < 0)
320 return ENOMEM;
321
322 oldtext = entry->text;
323 entry->text = newtext;
324 free(oldtext);
325 ui_entry_paint(entry);
326
327 return EOK;
328}
329
330/** Delete character before cursor.
331 *
332 * @param entry Text entry
333 */
334void ui_entry_backspace(ui_entry_t *entry)
335{
336 size_t off;
337
338 off = str_size(entry->text);
339 (void) str_decode_reverse(entry->text, &off,
340 str_size(entry->text));
341 entry->text[off] = '\0';
342 ui_entry_paint(entry);
343}
344
345/** Handle text entry key press without modifiers.
346 *
347 * @param entry Text entry
348 * @param kbd_event Keyboard event
349 * @return @c ui_claimed iff the event is claimed
350 */
351ui_evclaim_t ui_entry_key_press_unmod(ui_entry_t *entry, kbd_event_t *event)
352{
353 assert(event->type == KEY_PRESS);
354
355 if (event->key == KC_BACKSPACE)
356 ui_entry_backspace(entry);
357
358 if (event->key == KC_ESCAPE) {
359 entry->active = false;
360 (void) ui_entry_paint(entry);
361 }
362
363 return ui_claimed;
364}
365
366/** Handle text entry keyboard event.
367 *
368 * @param entry Text entry
369 * @param kbd_event Keyboard event
370 * @return @c ui_claimed iff the event is claimed
371 */
372ui_evclaim_t ui_entry_kbd_event(ui_entry_t *entry, kbd_event_t *event)
373{
374 char buf[STR_BOUNDS(1) + 1];
375 size_t off;
376 errno_t rc;
377
378 if (!entry->active)
379 return ui_unclaimed;
380
381 if (event->type == KEY_PRESS && event->c >= ' ') {
382 off = 0;
383 rc = chr_encode(event->c, buf, &off, sizeof(buf));
384 if (rc == EOK) {
385 buf[off] = '\0';
386 (void) ui_entry_insert_str(entry, buf);
387 }
388 }
389
390 if (event->type == KEY_PRESS &&
391 (event->mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0)
392 return ui_entry_key_press_unmod(entry, event);
393
394 return ui_claimed;
395}
396
397/** Handle text entry position event.
398 *
399 * @param entry Text entry
[03145ee]400 * @param pos_event Position event
401 * @return @c ui_claimed iff the event is claimed
402 */
[7481ee19]403ui_evclaim_t ui_entry_pos_event(ui_entry_t *entry, pos_event_t *event)
[03145ee]404{
[db3895d]405 gfx_coord2_t pos;
406
[7481ee19]407 if (entry->read_only)
408 return ui_unclaimed;
409
[db3895d]410 if (event->type == POS_UPDATE) {
411 pos.x = event->hpos;
412 pos.y = event->vpos;
413
414 if (gfx_pix_inside_rect(&pos, &entry->rect)) {
415 if (!entry->pointer_inside) {
416 ui_window_set_ctl_cursor(entry->window,
417 ui_curs_ibeam);
418 entry->pointer_inside = true;
419 }
420 } else {
421 if (entry->pointer_inside) {
422 ui_window_set_ctl_cursor(entry->window,
423 ui_curs_arrow);
424 entry->pointer_inside = false;
425 }
426 }
427 }
[03145ee]428
[7481ee19]429 if (event->type == POS_PRESS) {
430 pos.x = event->hpos;
431 pos.y = event->vpos;
432
433 if (gfx_pix_inside_rect(&pos, &entry->rect)) {
434 if (!entry->active) {
435 entry->active = true;
436 (void) ui_entry_paint(entry);
437 }
438
439 return ui_claimed;
440 } else {
441 if (entry->active) {
442 entry->active = false;
443 (void) ui_entry_paint(entry);
444 }
445 }
446 }
447
[03145ee]448 return ui_unclaimed;
449}
450
[7481ee19]451/** Handle text entry control keyboard event.
452 *
453 * @param arg Argument (ui_entry_t *)
454 * @param kbd_event Keyboard event
455 * @return @c ui_claimed iff the event is claimed
456 */
457static ui_evclaim_t ui_entry_ctl_kbd_event(void *arg, kbd_event_t *event)
458{
459 ui_entry_t *entry = (ui_entry_t *) arg;
460
461 return ui_entry_kbd_event(entry, event);
462}
463
464/** Handle text entry control position event.
465 *
466 * @param arg Argument (ui_entry_t *)
467 * @param pos_event Position event
468 * @return @c ui_claimed iff the event is claimed
469 */
470static ui_evclaim_t ui_entry_ctl_pos_event(void *arg, pos_event_t *event)
471{
472 ui_entry_t *entry = (ui_entry_t *) arg;
473
474 return ui_entry_pos_event(entry, event);
475}
476
[03145ee]477/** @}
478 */
Note: See TracBrowser for help on using the repository browser.