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

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

Add missing docblock

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