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
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};
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,
69 .kbd_event = ui_entry_ctl_kbd_event,
70 .pos_event = ui_entry_ctl_pos_event
71};
72
73/** Create new text entry.
74 *
75 * @param window UI window
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 */
80errno_t ui_entry_create(ui_window_t *window, const char *text,
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
103 entry->window = window;
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
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
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{
189 ui_resource_t *res;
190 gfx_text_fmt_t fmt;
191 gfx_coord2_t pos;
192 gfx_coord_t hpad;
193 gfx_coord_t vpad;
194 gfx_coord_t width;
195 gfx_rect_t inside;
196 errno_t rc;
197
198 res = ui_window_get_res(entry->window);
199
200 if (res->textmode) {
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
208 if (res->textmode == false) {
209 /* Paint inset frame */
210 rc = ui_paint_inset_frame(res, &entry->rect, &inside);
211 if (rc != EOK)
212 goto error;
213 } else {
214 inside = entry->rect;
215 }
216
217 /* Paint entry background */
218
219 rc = gfx_set_color(res->gc, res->entry_bg_color);
220 if (rc != EOK)
221 goto error;
222
223 rc = gfx_fill_rect(res->gc, &inside);
224 if (rc != EOK)
225 goto error;
226
227 width = gfx_text_width(res->font, entry->text);
228
229 switch (entry->halign) {
230 case gfx_halign_left:
231 case gfx_halign_justify:
232 pos.x = inside.p0.x + hpad;
233 break;
234 case gfx_halign_center:
235 pos.x = (inside.p0.x + inside.p1.x) / 2 - width / 2;
236 break;
237 case gfx_halign_right:
238 pos.x = inside.p1.x - hpad - 1 - width;
239 break;
240 }
241
242 pos.y = inside.p0.y + vpad;
243
244 gfx_text_fmt_init(&fmt);
245 fmt.color = res->entry_fg_color;
246 fmt.halign = gfx_halign_left;
247 fmt.valign = gfx_valign_top;
248
249 rc = gfx_set_clip_rect(res->gc, &inside);
250 if (rc != EOK)
251 goto error;
252
253 rc = gfx_puttext(res->font, &pos, &fmt, entry->text);
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);
271 if (rc != EOK)
272 goto error;
273
274 rc = gfx_update(res->gc);
275 if (rc != EOK)
276 goto error;
277
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
306/** Insert string at cursor position.
307 *
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
400 * @param pos_event Position event
401 * @return @c ui_claimed iff the event is claimed
402 */
403ui_evclaim_t ui_entry_pos_event(ui_entry_t *entry, pos_event_t *event)
404{
405 gfx_coord2_t pos;
406
407 if (entry->read_only)
408 return ui_unclaimed;
409
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 }
428
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
448 return ui_unclaimed;
449}
450
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
477/** @}
478 */
Note: See TracBrowser for help on using the repository browser.