source: mainline/uspace/lib/ui/src/paint.c@ d68239a1

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

Scrollbar needs custom button decorations

Push button now allows setting a 'custom decoration' which means
instead of painting the button text a callback function is invoked
to paint the decoration.

  • Property mode set to 100644
File size: 18.0 KB
RevLine 
[de9992c]1/*
[96c6a00]2 * Copyright (c) 2022 Jiri Svoboda
[de9992c]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 Painting routines
34 */
35
36#include <errno.h>
37#include <gfx/color.h>
38#include <gfx/context.h>
39#include <gfx/render.h>
[ff6e91b]40#include <gfx/text.h>
[96c6a00]41#include <ui/accel.h>
[de9992c]42#include <ui/paint.h>
[ff6e91b]43#include <stdlib.h>
44#include <str.h>
[d70dc1c4]45#include "../private/resource.h"
[de9992c]46
[ff6e91b]47/** Single box characters */
48static ui_box_chars_t single_box_chars = {
49 {
50 { "\u250c", "\u2500", "\u2510" },
51 { "\u2502", " ", "\u2502" },
52 { "\u2514", "\u2500", "\u2518" }
53 }
54};
55
56/** Double box characters */
57static ui_box_chars_t double_box_chars = {
58 {
59 { "\u2554", "\u2550", "\u2557" },
60 { "\u2551", " ", "\u2551" },
61 { "\u255a", "\u2550", "\u255d" }
62 }
63};
64
[81ec7e1]65/** Single horizontal brace characters */
66static ui_brace_chars_t single_hbrace_chars = {
67 .start = "\u251c",
68 .middle = "\u2500",
69 .end = "\u2524"
70};
71
72/** Double horizontal brace characters */
73static ui_brace_chars_t double_hbrace_chars = {
74 .start = "\u2560",
75 .middle = "\u2550",
76 .end = "\u2563"
77};
78
[de9992c]79/** Paint bevel.
80 *
81 * @param gc Graphic context
82 * @param rect Rectangle to paint into
83 * @param tlcolor Top-left color
84 * @param brcolor Bottom-right color
85 * @param thickness Bevel thickness in pixels
86 * @param inside Place to store rectangle of the interior or @c NULL
[d63623f]87 * @return EOK on success or an error code
[de9992c]88 */
89errno_t ui_paint_bevel(gfx_context_t *gc, gfx_rect_t *rect,
90 gfx_color_t *tlcolor, gfx_color_t *brcolor, gfx_coord_t thickness,
91 gfx_rect_t *inside)
92{
93 gfx_rect_t frect;
94 gfx_coord_t i;
95 errno_t rc;
96
97 /* Top left */
98
99 rc = gfx_set_color(gc, tlcolor);
100 if (rc != EOK)
101 goto error;
102
103 for (i = 0; i < thickness; i++) {
104 frect.p0.x = rect->p0.x + i;
105 frect.p0.y = rect->p0.y + i;
106 frect.p1.x = rect->p1.x - i - 1;
107 frect.p1.y = rect->p0.y + i + 1;
108 rc = gfx_fill_rect(gc, &frect);
109 if (rc != EOK)
110 goto error;
111
112 frect.p0.x = rect->p0.x + i;
113 frect.p0.y = rect->p0.y + i + 1;
114 frect.p1.x = rect->p0.x + i + 1;
115 frect.p1.y = rect->p1.y - i - 1;
116 rc = gfx_fill_rect(gc, &frect);
117 if (rc != EOK)
118 goto error;
119 }
120
121 /* Bottom right */
122
123 rc = gfx_set_color(gc, brcolor);
124 if (rc != EOK)
125 goto error;
126
127 for (i = 0; i < thickness; i++) {
128 frect.p0.x = rect->p0.x + i;
129 frect.p0.y = rect->p1.y - i - 1;
130 frect.p1.x = rect->p1.x - i - 1;
131 frect.p1.y = rect->p1.y - i;
132 rc = gfx_fill_rect(gc, &frect);
133 if (rc != EOK)
134 goto error;
135
136 frect.p0.x = rect->p1.x - i - 1;
137 frect.p0.y = rect->p0.y + i;
138 frect.p1.x = rect->p1.x - i;
139 frect.p1.y = rect->p1.y - i;
140 rc = gfx_fill_rect(gc, &frect);
141 if (rc != EOK)
142 goto error;
143 }
144
[d63623f]145 if (inside != NULL)
146 ui_paint_get_bevel_inside(gc, rect, thickness, inside);
[de9992c]147
148 return EOK;
149error:
150 return rc;
151}
152
[d63623f]153/** Get bevel interior rectangle.
154 *
155 * Get the bevel interior rectangle without painting it.
156 *
157 * @param gc Graphic context
158 * @param rect Rectangle to paint into
159 * @param thickness Bevel thickness in pixels
160 * @param inside Place to store rectangle of the interior
161 */
162void ui_paint_get_bevel_inside(gfx_context_t *gc, gfx_rect_t *rect,
163 gfx_coord_t thickness, gfx_rect_t *inside)
164{
165 inside->p0.x = rect->p0.x + thickness;
166 inside->p0.y = rect->p0.y + thickness;
167 inside->p1.x = rect->p1.x - thickness;
168 inside->p1.y = rect->p1.y - thickness;
169}
170
[d70dc1c4]171/** Paint inset frame.
172 *
173 * @param resource UI resource
174 * @param rect Rectangle to paint onto
175 * @param inside Place to store inside rectangle or @c NULL
176 * @return EOK on success or an error code
177 */
178errno_t ui_paint_inset_frame(ui_resource_t *resource, gfx_rect_t *rect,
179 gfx_rect_t *inside)
180{
181 gfx_rect_t frame;
182 errno_t rc;
183
184 rc = ui_paint_bevel(resource->gc, rect,
185 resource->wnd_shadow_color, resource->wnd_highlight_color,
186 1, &frame);
187 if (rc != EOK)
188 goto error;
189
190 rc = ui_paint_bevel(resource->gc, &frame,
191 resource->wnd_frame_sh_color, resource->wnd_frame_hi_color,
192 1, inside);
193 if (rc != EOK)
194 goto error;
195
196 return EOK;
197error:
198 return rc;
199}
200
[d63623f]201/** Get inset frame interior rectangle.
202 *
203 * This allows one to get the interior rectangle without actually painting
204 * the inset frame.
205 *
206 * @param resource UI resource
207 * @param rect Rectangle to paint onto
208 * @param inside Place to store inside rectangle or @c NULL
209 */
210void ui_paint_get_inset_frame_inside(ui_resource_t *resource, gfx_rect_t *rect,
211 gfx_rect_t *inside)
212{
213 ui_paint_get_bevel_inside(resource->gc, rect, 2, inside);
214}
215
[214aefb]216/** Paint outset frame.
217 *
218 * @param resource UI resource
219 * @param rect Rectangle to paint onto
220 * @param inside Place to store inside rectangle or @c NULL
221 * @return EOK on success or an error code
222 */
223errno_t ui_paint_outset_frame(ui_resource_t *resource, gfx_rect_t *rect,
224 gfx_rect_t *inside)
225{
226 gfx_rect_t frame;
227 errno_t rc;
228
229 rc = ui_paint_bevel(resource->gc, rect,
230 resource->wnd_frame_hi_color, resource->wnd_frame_sh_color,
231 1, &frame);
232 if (rc != EOK)
233 goto error;
234
235 rc = ui_paint_bevel(resource->gc, &frame,
236 resource->wnd_highlight_color, resource->wnd_shadow_color,
237 1, inside);
238 if (rc != EOK)
239 goto error;
240
241 return EOK;
242error:
243 return rc;
244}
245
[7020d1f]246/** Paint filled circle vertical scanline.
247 *
248 * @param gc Graphic context
249 * @param center Coordinates of the center of the circle
250 * @param x X-coordinate of the scanline
251 * @param y0 Lowest Y coordinate of the scanline (inclusive)
252 * @param y1 Highest Y coordinate of the scanline (inclusive)
253 * @param part Which part(s) of cicle to paint
254 * @return EOK on success or an error code
255 */
256static errno_t ui_paint_fcircle_line(gfx_context_t *gc, gfx_coord2_t *center,
257 gfx_coord_t x, gfx_coord_t y0, gfx_coord_t y1, ui_fcircle_part_t part)
258{
259 gfx_rect_t rect;
260 gfx_rect_t trect;
261
262 rect.p0.x = x;
263 rect.p0.y = y0;
264 rect.p1.x = x + 1;
265 rect.p1.y = y1;
266
267 /* Clip to upper-left/lower-right half of circle, if required */
268
269 if ((part & ui_fcircle_upleft) == 0) {
270 if (rect.p0.y < -rect.p1.x)
271 rect.p0.y = -rect.p1.x;
272 }
273
274 if ((part & ui_fcircle_lowright) == 0) {
275 if (rect.p1.y > -rect.p1.x)
276 rect.p1.y = -rect.p1.x;
277 }
278
279 /* If coordinates are reversed, there is nothing to do */
280 if (rect.p1.y <= rect.p0.y)
281 return EOK;
282
283 gfx_rect_translate(center, &rect, &trect);
284
285 return gfx_fill_rect(gc, &trect);
286}
287
288/** Paint filled circle scanlines corresponding to octant point.
289 *
290 * Fills the four vertical scanlines lying between the eight reflections
291 * of a circle point.
292 *
293 * @param gc Graphic context
294 * @param center Coordinates of the center
295 * @param r Radius in pixels
296 * @param part Which part(s) of cicle to paint
297 * @return EOK on success or an error code
298 */
299static int ui_paint_fcircle_point(gfx_context_t *gc, gfx_coord2_t *center,
300 gfx_coord2_t *p, ui_fcircle_part_t part)
301{
302 errno_t rc;
303
304 rc = ui_paint_fcircle_line(gc, center, +p->x, -p->y, p->y + 1, part);
305 if (rc != EOK)
306 return rc;
307 rc = ui_paint_fcircle_line(gc, center, -p->x, -p->y, p->y + 1, part);
308 if (rc != EOK)
309 return rc;
310 rc = ui_paint_fcircle_line(gc, center, +p->y, -p->x, p->x + 1, part);
311 if (rc != EOK)
312 return rc;
313 rc = ui_paint_fcircle_line(gc, center, -p->y, -p->x, p->x + 1, part);
314 if (rc != EOK)
315 return rc;
316
317 return EOK;
318}
319
320/** Paint a filled circle.
321 *
322 * @param gc Graphic context
323 * @param center Coordinates of the center
324 * @param r Radius in pixels
325 * @param part Which part(s) of cicle to paint
326 * @return EOK on success or an error code
327 */
328errno_t ui_paint_filled_circle(gfx_context_t *gc, gfx_coord2_t *center,
329 gfx_coord_t r, ui_fcircle_part_t part)
330{
331 gfx_coord2_t p;
332 gfx_coord_t f;
333 errno_t rc;
334
335 /* Run through one octant using circle midpoint algorithm */
336
337 p.x = r;
338 p.y = 0;
339 f = 1 - r;
340
341 rc = ui_paint_fcircle_point(gc, center, &p, part);
342 if (rc != EOK)
343 return rc;
344
345 while (p.x > p.y) {
346 if (f < 0) {
347 f = f + 2 * p.y + 3;
348 } else {
349 f = f + 2 * (p.y - p.x) + 5;
350 --p.x;
351 }
352 ++p.y;
353
354 rc = ui_paint_fcircle_point(gc, center, &p, part);
355 if (rc != EOK)
356 return rc;
357 }
358
359 return EOK;
360}
361
[d68239a1]362/** Paint upward pointing triangle.
363 *
364 * @param gc Graphic context
365 * @param pos Center position
366 * @param n Length of triangle side
367 */
368errno_t ui_paint_up_triangle(gfx_context_t *gc, gfx_coord2_t *pos,
369 gfx_coord_t n)
370{
371 gfx_coord_t i;
372 errno_t rc;
373 gfx_rect_t rect;
374
375 for (i = 0; i < n; i++) {
376 rect.p0.x = pos->x - i;
377 rect.p0.y = pos->y - n / 2 + i;
378 rect.p1.x = pos->x + i + 1;
379 rect.p1.y = pos->y - n / 2 + i + 1;
380 rc = gfx_fill_rect(gc, &rect);
381 if (rc != EOK)
382 return rc;
383 }
384
385 return EOK;
386}
387
388/** Paint downward pointing triangle.
389 *
390 * @param gc Graphic context
391 * @param pos Center position
392 * @param n Length of triangle side
393 */
394errno_t ui_paint_down_triangle(gfx_context_t *gc, gfx_coord2_t *pos,
395 gfx_coord_t n)
396{
397 gfx_coord_t i;
398 errno_t rc;
399 gfx_rect_t rect;
400
401 for (i = 0; i < n; i++) {
402 rect.p0.x = pos->x - i;
403 rect.p0.y = pos->y + n / 2 - i;
404 rect.p1.x = pos->x + i + 1;
405 rect.p1.y = pos->y + n / 2 - i + 1;
406 rc = gfx_fill_rect(gc, &rect);
407 if (rc != EOK)
408 return rc;
409 }
410
411 return EOK;
412}
413
414/** Paint left pointing triangle.
415 *
416 * @param gc Graphic context
417 * @param pos Center position
418 * @param n Length of triangle side
419 */
420errno_t ui_paint_left_triangle(gfx_context_t *gc, gfx_coord2_t *pos,
421 gfx_coord_t n)
422{
423 gfx_coord_t i;
424 errno_t rc;
425 gfx_rect_t rect;
426
427 for (i = 0; i < n; i++) {
428 rect.p0.x = pos->x - n / 2 + i;
429 rect.p0.y = pos->y - i;
430 rect.p1.x = pos->x - n / 2 + i + 1;
431 rect.p1.y = pos->y + i + 1;
432 rc = gfx_fill_rect(gc, &rect);
433 if (rc != EOK)
434 return rc;
435 }
436
437 return EOK;
438}
439
440/** Paint right pointing triangle.
441 *
442 * @param gc Graphic context
443 * @param pos Center position
444 * @param n Length of triangle side
445 */
446errno_t ui_paint_right_triangle(gfx_context_t *gc, gfx_coord2_t *pos,
447 gfx_coord_t n)
448{
449 gfx_coord_t i;
450 errno_t rc;
451 gfx_rect_t rect;
452
453 for (i = 0; i < n; i++) {
454 rect.p0.x = pos->x + n / 2 - i;
455 rect.p0.y = pos->y - i;
456 rect.p1.x = pos->x + n / 2 - i + 1;
457 rect.p1.y = pos->y + i + 1;
458 rc = gfx_fill_rect(gc, &rect);
459 if (rc != EOK)
460 return rc;
461 }
462
463 return EOK;
464}
465
[ff6e91b]466/** Paint a text box.
467 *
468 * @param resource UI resource
469 * @param rect Rectangle inside which to paint the box
470 * @param style Box style
471 * @param color Color
472 * @return EOK on success or an error code
473 */
474errno_t ui_paint_text_box(ui_resource_t *resource, gfx_rect_t *rect,
475 ui_box_style_t style, gfx_color_t *color)
476{
477 errno_t rc;
478 gfx_text_fmt_t fmt;
479 gfx_rect_t srect;
480 gfx_coord2_t pos;
481 gfx_coord2_t dim;
482 size_t bufsz;
483 size_t off;
484 int i;
485 gfx_coord_t y;
486 char *str = NULL;
487 ui_box_chars_t *boxc = NULL;
488
489 gfx_rect_points_sort(rect, &srect);
490 gfx_rect_dims(&srect, &dim);
491
492 /* Is rectangle large enough to hold box? */
493 if (dim.x < 2 || dim.y < 2)
494 return EOK;
495
496 switch (style) {
497 case ui_box_single:
498 boxc = &single_box_chars;
499 break;
500 case ui_box_double:
501 boxc = &double_box_chars;
502 break;
503 }
504
505 if (boxc == NULL)
506 return EINVAL;
507
508 gfx_text_fmt_init(&fmt);
[4583015]509 fmt.font = resource->font;
[ff6e91b]510 fmt.color = color;
511
512 bufsz = str_size(boxc->c[0][0]) +
513 str_size(boxc->c[0][1]) * (dim.x - 2) +
514 str_size(boxc->c[0][2]) + 1;
515
516 str = malloc(bufsz);
517 if (str == NULL)
518 return ENOMEM;
519
520 /* Top edge and corners */
521
522 str_cpy(str, bufsz, boxc->c[0][0]);
523 off = str_size(boxc->c[0][0]);
524
525 for (i = 1; i < dim.x - 1; i++) {
526 str_cpy(str + off, bufsz - off, boxc->c[0][1]);
527 off += str_size(boxc->c[0][1]);
528 }
529
530 str_cpy(str + off, bufsz - off, boxc->c[0][2]);
531 off += str_size(boxc->c[0][2]);
532 str[off] = '\0';
533
534 pos = rect->p0;
[4583015]535 rc = gfx_puttext(&pos, &fmt, str);
[ff6e91b]536 if (rc != EOK)
537 goto error;
538
539 /* Vertical edges */
540 for (y = rect->p0.y + 1; y < rect->p1.y - 1; y++) {
541 pos.y = y;
542
543 pos.x = rect->p0.x;
[4583015]544 rc = gfx_puttext(&pos, &fmt, boxc->c[1][0]);
[ff6e91b]545 if (rc != EOK)
546 goto error;
547
548 pos.x = rect->p1.x - 1;
[4583015]549 rc = gfx_puttext(&pos, &fmt, boxc->c[1][2]);
[ff6e91b]550 if (rc != EOK)
551 goto error;
552 }
553
554 /* Bottom edge and corners */
555
556 str_cpy(str, bufsz, boxc->c[2][0]);
557 off = str_size(boxc->c[2][0]);
558
559 for (i = 1; i < dim.x - 1; i++) {
560 str_cpy(str + off, bufsz - off, boxc->c[2][1]);
561 off += str_size(boxc->c[2][1]);
562 }
563
564 str_cpy(str + off, bufsz - off, boxc->c[2][2]);
565 off += str_size(boxc->c[2][2]);
566 str[off] = '\0';
567
568 pos.x = rect->p0.x;
569 pos.y = rect->p1.y - 1;
[4583015]570 rc = gfx_puttext(&pos, &fmt, str);
[ff6e91b]571 if (rc != EOK)
572 goto error;
573
574 free(str);
575 return EOK;
576error:
577 if (str != NULL)
578 free(str);
579 return rc;
580}
581
[81ec7e1]582/** Paint a text horizontal brace.
583 *
584 * @param resource UI resource
585 * @param rect Rectangle inside which to paint the brace (height should
586 * be 1).
587 * @param style Box style
588 * @param color Color
589 * @return EOK on success or an error code
590 */
591errno_t ui_paint_text_hbrace(ui_resource_t *resource, gfx_rect_t *rect,
592 ui_box_style_t style, gfx_color_t *color)
593{
594 errno_t rc;
595 gfx_text_fmt_t fmt;
596 gfx_rect_t srect;
597 gfx_coord2_t pos;
598 gfx_coord2_t dim;
599 size_t bufsz;
600 size_t off;
601 int i;
602 char *str = NULL;
603 ui_brace_chars_t *hbc = NULL;
604
605 gfx_rect_points_sort(rect, &srect);
606 gfx_rect_dims(&srect, &dim);
607
608 /* Is rectangle large enough to hold brace? */
609 if (dim.x < 2 || dim.y < 1)
610 return EOK;
611
612 switch (style) {
613 case ui_box_single:
614 hbc = &single_hbrace_chars;
615 break;
616 case ui_box_double:
617 hbc = &double_hbrace_chars;
618 break;
619 }
620
621 if (hbc == NULL)
622 return EINVAL;
623
624 gfx_text_fmt_init(&fmt);
[4583015]625 fmt.font = resource->font;
[81ec7e1]626 fmt.color = color;
627
628 bufsz = str_size(hbc->start) +
629 str_size(hbc->middle) * (dim.x - 2) +
630 str_size(hbc->end) + 1;
631
632 str = malloc(bufsz);
633 if (str == NULL)
634 return ENOMEM;
635
636 str_cpy(str, bufsz, hbc->start);
637 off = str_size(hbc->start);
638
639 for (i = 1; i < dim.x - 1; i++) {
640 str_cpy(str + off, bufsz - off, hbc->middle);
641 off += str_size(hbc->middle);
642 }
643
644 str_cpy(str + off, bufsz - off, hbc->end);
645 off += str_size(hbc->end);
646 str[off] = '\0';
647
648 pos = rect->p0;
[4583015]649 rc = gfx_puttext(&pos, &fmt, str);
[81ec7e1]650 if (rc != EOK)
651 goto error;
652
653 free(str);
654 return EOK;
655error:
656 if (str != NULL)
657 free(str);
658 return rc;
659}
660
[0d1d0ea]661/** Fill rectangle with text character.
662 *
663 * @param resource UI resource
664 * @param rect Rectangle
665 * @param color Color
666 * @param gchar Character to fill with
667 * @return EOK on success or an error code
668 */
669errno_t ui_paint_text_rect(ui_resource_t *resource, gfx_rect_t *rect,
670 gfx_color_t *color, const char *gchar)
671{
672 gfx_coord2_t pos;
673 gfx_text_fmt_t fmt;
674 gfx_rect_t srect;
675 gfx_coord_t w, i;
676 char *buf;
677 size_t gcharsz;
678 errno_t rc;
679
680 gfx_rect_points_sort(rect, &srect);
681
682 gfx_text_fmt_init(&fmt);
683 fmt.font = resource->font;
684 fmt.color = color;
685 fmt.halign = gfx_halign_left;
686 fmt.valign = gfx_valign_top;
687
688 w = srect.p1.x - srect.p0.x;
689 if (w == 0)
690 return EOK;
691
692 gcharsz = str_size(gchar);
693
694 buf = malloc(w * gcharsz + 1);
695 if (buf == NULL)
696 return ENOMEM;
697
698 for (i = 0; i < w; i++)
699 str_cpy(buf + i * gcharsz, (w - i) * gcharsz + 1, gchar);
700 buf[w * gcharsz] = '\0';
701
702 pos.x = srect.p0.x;
703 for (pos.y = srect.p0.y; pos.y < srect.p1.y; pos.y++) {
704 rc = gfx_puttext(&pos, &fmt, buf);
705 if (rc != EOK)
706 goto error;
707 }
708
709 free(buf);
710 return EOK;
711error:
712 free(buf);
713 return rc;
714}
715
[ca2680d]716/** Initialize UI text formatting structure.
717 *
718 * UI text formatting structure must always be initialized using this function
719 * first.
720 *
721 * @param fmt UI text formatting structure
722 */
723void ui_text_fmt_init(ui_text_fmt_t *fmt)
724{
725 memset(fmt, 0, sizeof(ui_text_fmt_t));
726}
727
728/** Compute UI text width.
729 *
730 * @param font Font
731 * @param str String
732 * @return Text width
733 */
734gfx_coord_t ui_text_width(gfx_font_t *font, const char *str)
735{
736 gfx_coord_t w;
737 gfx_coord_t tw;
738 const char *sp;
739
740 /* Text including tildes */
741 w = gfx_text_width(font, str);
742 tw = gfx_text_width(font, "~");
743
744 /* Subtract width of singular tildes */
745 sp = str;
746 while (*sp != '\0') {
747 if (*sp == '~' && sp[1] != '~')
748 w -= tw;
749 ++sp;
750 }
751
752 return w;
753}
754
755/** Paint text (with highlighting markup).
756 *
757 * Paint some text with highlighted sections enclosed with tilde characters
758 * ('~'). A literal tilde can be printed with "~~". Text can be highlighted
759 * with an underline or different color, based on display capabilities.
760 *
761 * @param pos Anchor position
762 * @param fmt Text formatting
763 * @param str String containing '~' markup
764 *
765 * @return EOK on success or an error code
766 */
767errno_t ui_paint_text(gfx_coord2_t *pos, ui_text_fmt_t *fmt, const char *str)
768{
769 gfx_coord2_t tpos;
770 gfx_text_fmt_t tfmt;
771 char *buf;
772 char *endptr;
773 const char *sp;
774 errno_t rc;
775
776 /* Break down string into list of (non)highlighted parts */
[96c6a00]777 rc = ui_accel_process(str, &buf, &endptr);
[ca2680d]778 if (rc != EOK)
779 return rc;
780
781 gfx_text_fmt_init(&tfmt);
782 tfmt.font = fmt->font;
783 tfmt.color = fmt->color;
784 tfmt.halign = fmt->halign;
785 tfmt.justify_width = fmt->justify_width;
786 tfmt.valign = fmt->valign;
787 tfmt.underline = false;
788
789 tpos = *pos;
790 sp = buf;
791 while (sp != endptr) {
792 /* Print the current string */
793 rc = gfx_puttext(&tpos, &tfmt, sp);
794 if (rc != EOK)
795 goto error;
796
797 gfx_text_cont(&tpos, &tfmt, sp, &tpos, &tfmt);
798 tfmt.underline = !tfmt.underline;
799 tfmt.color = tfmt.underline ? fmt->hgl_color : fmt->color;
800
801 /* Skip to the next string */
802 while (*sp != '\0')
803 ++sp;
804 ++sp;
805 }
806
807 free(buf);
808 return EOK;
809error:
810 free(buf);
811 return rc;
812}
813
[de9992c]814/** @}
815 */
Note: See TracBrowser for help on using the repository browser.