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

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

Draw text cursor as a vertical bar

  • Property mode set to 100644
File size: 11.0 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
185static errno_t ui_entry_paint_cursor(ui_entry_t *entry, gfx_coord2_t *pos)
186{
187 ui_resource_t *res;
188 gfx_rect_t rect;
189 gfx_font_metrics_t metrics;
190 gfx_coord_t w;
191 errno_t rc;
192
193 res = ui_window_get_res(entry->window);
194
195 gfx_font_get_metrics(res->font, &metrics);
196
197 w = res->textmode ? ui_entry_cursor_width_text :
198 ui_entry_cursor_width;
199
200 rect.p0.x = pos->x;
201 rect.p0.y = pos->y - ui_entry_cursor_overshoot;
202 rect.p1.x = pos->x + w;
203 rect.p1.y = pos->y + metrics.ascent + metrics.descent + 1 +
204 ui_entry_cursor_overshoot;
205
206 rc = gfx_set_color(res->gc, res->entry_fg_color);
207 if (rc != EOK)
208 goto error;
209
210 rc = gfx_fill_rect(res->gc, &rect);
211 if (rc != EOK)
212 goto error;
213
214 return EOK;
215error:
216 return rc;
217}
218
219/** Paint text entry.
220 *
221 * @param entry Text entry
222 * @return EOK on success or an error code
223 */
224errno_t ui_entry_paint(ui_entry_t *entry)
225{
226 ui_resource_t *res;
227 gfx_text_fmt_t fmt;
228 gfx_coord2_t pos;
229 gfx_coord_t hpad;
230 gfx_coord_t vpad;
231 gfx_coord_t width;
232 gfx_rect_t inside;
233 errno_t rc;
234
235 res = ui_window_get_res(entry->window);
236
237 if (res->textmode) {
238 hpad = ui_entry_hpad_text;
239 vpad = ui_entry_vpad_text;
240 } else {
241 hpad = ui_entry_hpad;
242 vpad = ui_entry_vpad;
243 }
244
245 if (res->textmode == false) {
246 /* Paint inset frame */
247 rc = ui_paint_inset_frame(res, &entry->rect, &inside);
248 if (rc != EOK)
249 goto error;
250 } else {
251 inside = entry->rect;
252 }
253
254 /* Paint entry background */
255
256 rc = gfx_set_color(res->gc, res->entry_bg_color);
257 if (rc != EOK)
258 goto error;
259
260 rc = gfx_fill_rect(res->gc, &inside);
261 if (rc != EOK)
262 goto error;
263
264 width = gfx_text_width(res->font, entry->text);
265
266 switch (entry->halign) {
267 case gfx_halign_left:
268 case gfx_halign_justify:
269 pos.x = inside.p0.x + hpad;
270 break;
271 case gfx_halign_center:
272 pos.x = (inside.p0.x + inside.p1.x) / 2 - width / 2;
273 break;
274 case gfx_halign_right:
275 pos.x = inside.p1.x - hpad - 1 - width;
276 break;
277 }
278
279 pos.y = inside.p0.y + vpad;
280
281 gfx_text_fmt_init(&fmt);
282 fmt.color = res->entry_fg_color;
283 fmt.halign = gfx_halign_left;
284 fmt.valign = gfx_valign_top;
285
286 rc = gfx_set_clip_rect(res->gc, &inside);
287 if (rc != EOK)
288 goto error;
289
290 rc = gfx_puttext(res->font, &pos, &fmt, entry->text);
291 if (rc != EOK) {
292 (void) gfx_set_clip_rect(res->gc, NULL);
293 goto error;
294 }
295
296 if (entry->active) {
297 /* Cursor */
298 pos.x += width;
299
300 rc = ui_entry_paint_cursor(entry, &pos);
301 if (rc != EOK) {
302 (void) gfx_set_clip_rect(res->gc, NULL);
303 goto error;
304 }
305 }
306
307 rc = gfx_set_clip_rect(res->gc, NULL);
308 if (rc != EOK)
309 goto error;
310
311 rc = gfx_update(res->gc);
312 if (rc != EOK)
313 goto error;
314
315 return EOK;
316error:
317 return rc;
318}
319
320/** Destroy text entry control.
321 *
322 * @param arg Argument (ui_entry_t *)
323 */
324void ui_entry_ctl_destroy(void *arg)
325{
326 ui_entry_t *entry = (ui_entry_t *) arg;
327
328 ui_entry_destroy(entry);
329}
330
331/** Paint text entry control.
332 *
333 * @param arg Argument (ui_entry_t *)
334 * @return EOK on success or an error code
335 */
336errno_t ui_entry_ctl_paint(void *arg)
337{
338 ui_entry_t *entry = (ui_entry_t *) arg;
339
340 return ui_entry_paint(entry);
341}
342
343/** Insert string at cursor position.
344 *
345 * @param entry Text entry
346 * @param str String
347 * @return EOK on success, ENOMEM if out of memory
348 */
349errno_t ui_entry_insert_str(ui_entry_t *entry, const char *str)
350{
351 char *newtext;
352 char *oldtext;
353 int rc;
354
355 rc = asprintf(&newtext, "%s%s", entry->text, str);
356 if (rc < 0)
357 return ENOMEM;
358
359 oldtext = entry->text;
360 entry->text = newtext;
361 free(oldtext);
362 ui_entry_paint(entry);
363
364 return EOK;
365}
366
367/** Delete character before cursor.
368 *
369 * @param entry Text entry
370 */
371void ui_entry_backspace(ui_entry_t *entry)
372{
373 size_t off;
374
375 off = str_size(entry->text);
376 (void) str_decode_reverse(entry->text, &off,
377 str_size(entry->text));
378 entry->text[off] = '\0';
379 ui_entry_paint(entry);
380}
381
382/** Handle text entry key press without modifiers.
383 *
384 * @param entry Text entry
385 * @param kbd_event Keyboard event
386 * @return @c ui_claimed iff the event is claimed
387 */
388ui_evclaim_t ui_entry_key_press_unmod(ui_entry_t *entry, kbd_event_t *event)
389{
390 assert(event->type == KEY_PRESS);
391
392 if (event->key == KC_BACKSPACE)
393 ui_entry_backspace(entry);
394
395 if (event->key == KC_ESCAPE) {
396 entry->active = false;
397 (void) ui_entry_paint(entry);
398 }
399
400 return ui_claimed;
401}
402
403/** Handle text entry keyboard event.
404 *
405 * @param entry Text entry
406 * @param kbd_event Keyboard event
407 * @return @c ui_claimed iff the event is claimed
408 */
409ui_evclaim_t ui_entry_kbd_event(ui_entry_t *entry, kbd_event_t *event)
410{
411 char buf[STR_BOUNDS(1) + 1];
412 size_t off;
413 errno_t rc;
414
415 if (!entry->active)
416 return ui_unclaimed;
417
418 if (event->type == KEY_PRESS && event->c >= ' ') {
419 off = 0;
420 rc = chr_encode(event->c, buf, &off, sizeof(buf));
421 if (rc == EOK) {
422 buf[off] = '\0';
423 (void) ui_entry_insert_str(entry, buf);
424 }
425 }
426
427 if (event->type == KEY_PRESS &&
428 (event->mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0)
429 return ui_entry_key_press_unmod(entry, event);
430
431 return ui_claimed;
432}
433
434/** Handle text entry position event.
435 *
436 * @param entry Text entry
437 * @param pos_event Position event
438 * @return @c ui_claimed iff the event is claimed
439 */
440ui_evclaim_t ui_entry_pos_event(ui_entry_t *entry, pos_event_t *event)
441{
442 gfx_coord2_t pos;
443
444 if (entry->read_only)
445 return ui_unclaimed;
446
447 if (event->type == POS_UPDATE) {
448 pos.x = event->hpos;
449 pos.y = event->vpos;
450
451 if (gfx_pix_inside_rect(&pos, &entry->rect)) {
452 if (!entry->pointer_inside) {
453 ui_window_set_ctl_cursor(entry->window,
454 ui_curs_ibeam);
455 entry->pointer_inside = true;
456 }
457 } else {
458 if (entry->pointer_inside) {
459 ui_window_set_ctl_cursor(entry->window,
460 ui_curs_arrow);
461 entry->pointer_inside = false;
462 }
463 }
464 }
465
466 if (event->type == POS_PRESS) {
467 pos.x = event->hpos;
468 pos.y = event->vpos;
469
470 if (gfx_pix_inside_rect(&pos, &entry->rect)) {
471 if (!entry->active) {
472 entry->active = true;
473 (void) ui_entry_paint(entry);
474 }
475
476 return ui_claimed;
477 } else {
478 if (entry->active) {
479 entry->active = false;
480 (void) ui_entry_paint(entry);
481 }
482 }
483 }
484
485 return ui_unclaimed;
486}
487
488/** Handle text entry control keyboard event.
489 *
490 * @param arg Argument (ui_entry_t *)
491 * @param kbd_event Keyboard event
492 * @return @c ui_claimed iff the event is claimed
493 */
494static ui_evclaim_t ui_entry_ctl_kbd_event(void *arg, kbd_event_t *event)
495{
496 ui_entry_t *entry = (ui_entry_t *) arg;
497
498 return ui_entry_kbd_event(entry, event);
499}
500
501/** Handle text entry control position event.
502 *
503 * @param arg Argument (ui_entry_t *)
504 * @param pos_event Position event
505 * @return @c ui_claimed iff the event is claimed
506 */
507static ui_evclaim_t ui_entry_ctl_pos_event(void *arg, pos_event_t *event)
508{
509 ui_entry_t *entry = (ui_entry_t *) arg;
510
511 return ui_entry_pos_event(entry, event);
512}
513
514/** @}
515 */
Note: See TracBrowser for help on using the repository browser.