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

Last change on this file was 1eaead4, checked in by Jiri Svoboda <jiri@…>, 2 years ago

Tab set control

This allows to expand the space available in a dialog window
using stacking, with individual tabs that can be activated
by clicking the handle.

  • Property mode set to 100644
File size: 22.2 KB
Line 
1/*
2 * Copyright (c) 2023 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 Painting routines
34 */
35
36#include <errno.h>
37#include <gfx/color.h>
38#include <gfx/context.h>
39#include <gfx/render.h>
40#include <gfx/text.h>
41#include <ui/accel.h>
42#include <ui/paint.h>
43#include <stdlib.h>
44#include <str.h>
45#include "../private/resource.h"
46
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
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
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
87 * @return EOK on success or an error code
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
145 if (inside != NULL)
146 ui_paint_get_bevel_inside(gc, rect, thickness, inside);
147
148 return EOK;
149error:
150 return rc;
151}
152
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
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
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
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
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
362/** Paint upward pointing triangle.
363 *
364 * @param gc Graphic context
365 * @param pos Center position
366 * @param n Length of triangle side
367 * @return EOK on success or an error code
368 */
369errno_t ui_paint_up_triangle(gfx_context_t *gc, gfx_coord2_t *pos,
370 gfx_coord_t n)
371{
372 gfx_coord_t i;
373 errno_t rc;
374 gfx_rect_t rect;
375
376 for (i = 0; i < n; i++) {
377 rect.p0.x = pos->x - i;
378 rect.p0.y = pos->y - n / 2 + i;
379 rect.p1.x = pos->x + i + 1;
380 rect.p1.y = pos->y - n / 2 + i + 1;
381 rc = gfx_fill_rect(gc, &rect);
382 if (rc != EOK)
383 return rc;
384 }
385
386 return EOK;
387}
388
389/** Paint downward pointing triangle.
390 *
391 * @param gc Graphic context
392 * @param pos Center position
393 * @param n Length of triangle side
394 * @return EOK on success or an error code
395 */
396errno_t ui_paint_down_triangle(gfx_context_t *gc, gfx_coord2_t *pos,
397 gfx_coord_t n)
398{
399 gfx_coord_t i;
400 errno_t rc;
401 gfx_rect_t rect;
402
403 for (i = 0; i < n; i++) {
404 rect.p0.x = pos->x - i;
405 rect.p0.y = pos->y + n / 2 - i;
406 rect.p1.x = pos->x + i + 1;
407 rect.p1.y = pos->y + n / 2 - i + 1;
408 rc = gfx_fill_rect(gc, &rect);
409 if (rc != EOK)
410 return rc;
411 }
412
413 return EOK;
414}
415
416/** Paint left pointing triangle.
417 *
418 * @param gc Graphic context
419 * @param pos Center position
420 * @param n Length of triangle side
421 * @return EOK on success or an error code
422 */
423errno_t ui_paint_left_triangle(gfx_context_t *gc, gfx_coord2_t *pos,
424 gfx_coord_t n)
425{
426 gfx_coord_t i;
427 errno_t rc;
428 gfx_rect_t rect;
429
430 for (i = 0; i < n; i++) {
431 rect.p0.x = pos->x - n / 2 + i;
432 rect.p0.y = pos->y - i;
433 rect.p1.x = pos->x - n / 2 + i + 1;
434 rect.p1.y = pos->y + i + 1;
435 rc = gfx_fill_rect(gc, &rect);
436 if (rc != EOK)
437 return rc;
438 }
439
440 return EOK;
441}
442
443/** Paint right pointing triangle.
444 *
445 * @param gc Graphic context
446 * @param pos Center position
447 * @param n Length of triangle side
448 * @return EOK on success or an error code
449 */
450errno_t ui_paint_right_triangle(gfx_context_t *gc, gfx_coord2_t *pos,
451 gfx_coord_t n)
452{
453 gfx_coord_t i;
454 errno_t rc;
455 gfx_rect_t rect;
456
457 for (i = 0; i < n; i++) {
458 rect.p0.x = pos->x + n / 2 - i;
459 rect.p0.y = pos->y - i;
460 rect.p1.x = pos->x + n / 2 - i + 1;
461 rect.p1.y = pos->y + i + 1;
462 rc = gfx_fill_rect(gc, &rect);
463 if (rc != EOK)
464 return rc;
465 }
466
467 return EOK;
468}
469
470/** Paint diagonal cross (X).
471 *
472 * @param gc Graphic context
473 * @param pos Center position
474 * @param n Length of each leg
475 * @param w Pen width
476 * @param h Pen height
477 * @return EOK on success or an error code
478 */
479errno_t ui_paint_cross(gfx_context_t *gc, gfx_coord2_t *pos,
480 gfx_coord_t n, gfx_coord_t w, gfx_coord_t h)
481{
482 gfx_coord_t i;
483 gfx_rect_t rect;
484 errno_t rc;
485
486 rect.p0.x = pos->x;
487 rect.p0.y = pos->y;
488 rect.p1.x = pos->x + w;
489 rect.p1.y = pos->y + h;
490 rc = gfx_fill_rect(gc, &rect);
491 if (rc != EOK)
492 return rc;
493
494 for (i = 1; i < n; i++) {
495 rect.p0.x = pos->x - i;
496 rect.p0.y = pos->y - i;
497 rect.p1.x = pos->x - i + w;
498 rect.p1.y = pos->y - i + h;
499 rc = gfx_fill_rect(gc, &rect);
500 if (rc != EOK)
501 return rc;
502
503 rect.p0.x = pos->x - i;
504 rect.p0.y = pos->y + i;
505 rect.p1.x = pos->x - i + w;
506 rect.p1.y = pos->y + i + h;
507 rc = gfx_fill_rect(gc, &rect);
508 if (rc != EOK)
509 return rc;
510
511 rect.p0.x = pos->x + i;
512 rect.p0.y = pos->y - i;
513 rect.p1.x = pos->x + i + w;
514 rect.p1.y = pos->y - i + h;
515 rc = gfx_fill_rect(gc, &rect);
516 if (rc != EOK)
517 return rc;
518
519 rect.p0.x = pos->x + i;
520 rect.p0.y = pos->y + i;
521 rect.p1.x = pos->x + i + w;
522 rect.p1.y = pos->y + i + h;
523 rc = gfx_fill_rect(gc, &rect);
524 if (rc != EOK)
525 return rc;
526 }
527
528 return EOK;
529}
530
531/** Paint minimize icon.
532 *
533 * @param resource UI resource
534 * @param pos Center position
535 * @param w Icon width
536 * @param h Icon height
537 * @return EOK on success or an error code
538 */
539errno_t ui_paint_minicon(ui_resource_t *resource, gfx_coord2_t *pos,
540 gfx_coord_t w, gfx_coord_t h)
541{
542 gfx_rect_t rect;
543 errno_t rc;
544
545 rc = gfx_set_color(resource->gc, resource->btn_text_color);
546 if (rc != EOK)
547 return rc;
548
549 rect.p0.x = pos->x - w / 2;
550 rect.p0.y = pos->y + h / 2 - 2;
551 rect.p1.x = rect.p0.x + w;
552 rect.p1.y = rect.p0.y + 2;
553 rc = gfx_fill_rect(resource->gc, &rect);
554 if (rc != EOK)
555 return rc;
556
557 return EOK;
558}
559
560/** Paint maximize icon.
561 *
562 * @param resource UI resource
563 * @param pos Center position
564 * @param w Icon width
565 * @param h Icon height
566 * @return EOK on success or an error code
567 */
568errno_t ui_paint_maxicon(ui_resource_t *resource, gfx_coord2_t *pos,
569 gfx_coord_t w, gfx_coord_t h)
570{
571 gfx_rect_t rect;
572 errno_t rc;
573
574 rc = gfx_set_color(resource->gc, resource->btn_text_color);
575 if (rc != EOK)
576 return rc;
577
578 rect.p0.x = pos->x - w / 2;
579 rect.p0.y = pos->y - h / 2;
580 rect.p1.x = rect.p0.x + w;
581 rect.p1.y = rect.p0.y + h;
582 rc = gfx_fill_rect(resource->gc, &rect);
583 if (rc != EOK)
584 return rc;
585
586 rc = gfx_set_color(resource->gc, resource->btn_face_color);
587 if (rc != EOK)
588 return rc;
589
590 rect.p0.x += 1;
591 rect.p0.y += 2;
592 rect.p1.x -= 1;
593 rect.p1.y -= 1;
594 rc = gfx_fill_rect(resource->gc, &rect);
595 if (rc != EOK)
596 return rc;
597
598 return EOK;
599}
600
601/** Paint unmaximize icon.
602 *
603 * Unmaximize icon consists of two overlapping window icons.
604 *
605 * @param resource UI resource
606 * @param pos Center position
607 * @param w Window icon width
608 * @param h Window icon height
609 * @param dw Horizontal distance between window icon centers
610 * @param dh Vertical distance between window icon centers
611 * @return EOK on success or an error code
612 */
613errno_t ui_paint_unmaxicon(ui_resource_t *resource, gfx_coord2_t *pos,
614 gfx_coord_t w, gfx_coord_t h, gfx_coord_t dw, gfx_coord_t dh)
615{
616 gfx_coord2_t p;
617 errno_t rc;
618
619 p.x = pos->x + dw / 2;
620 p.y = pos->y - dh / 2;
621 rc = ui_paint_maxicon(resource, &p, w, h);
622 if (rc != EOK)
623 return rc;
624
625 p.x = pos->x - dw / 2;
626 p.y = pos->y + dh / 2;
627 rc = ui_paint_maxicon(resource, &p, w, h);
628 if (rc != EOK)
629 return rc;
630
631 return EOK;
632}
633
634/** Paint a custom text box.
635 *
636 * Paint a text box using user-provided box chars.
637 *
638 * @param resource UI resource
639 * @param rect Rectangle inside which to paint the box
640 * @param style Box style
641 * @param boxc Box characters
642 * @param color Color
643 * @return EOK on success or an error code
644 */
645errno_t ui_paint_text_box_custom(ui_resource_t *resource, gfx_rect_t *rect,
646 ui_box_chars_t *boxc, gfx_color_t *color)
647{
648 errno_t rc;
649 gfx_text_fmt_t fmt;
650 gfx_rect_t srect;
651 gfx_coord2_t pos;
652 gfx_coord2_t dim;
653 size_t bufsz;
654 size_t off;
655 int i;
656 gfx_coord_t y;
657 char *str = NULL;
658
659 gfx_rect_points_sort(rect, &srect);
660 gfx_rect_dims(&srect, &dim);
661
662 /* Is rectangle large enough to hold box? */
663 if (dim.x < 2 || dim.y < 2)
664 return EOK;
665
666 gfx_text_fmt_init(&fmt);
667 fmt.font = resource->font;
668 fmt.color = color;
669
670 bufsz = str_size(boxc->c[0][0]) +
671 str_size(boxc->c[0][1]) * (dim.x - 2) +
672 str_size(boxc->c[0][2]) + 1;
673
674 str = malloc(bufsz);
675 if (str == NULL)
676 return ENOMEM;
677
678 /* Top edge and corners */
679
680 str_cpy(str, bufsz, boxc->c[0][0]);
681 off = str_size(boxc->c[0][0]);
682
683 for (i = 1; i < dim.x - 1; i++) {
684 str_cpy(str + off, bufsz - off, boxc->c[0][1]);
685 off += str_size(boxc->c[0][1]);
686 }
687
688 str_cpy(str + off, bufsz - off, boxc->c[0][2]);
689 off += str_size(boxc->c[0][2]);
690 str[off] = '\0';
691
692 pos = rect->p0;
693 rc = gfx_puttext(&pos, &fmt, str);
694 if (rc != EOK)
695 goto error;
696
697 /* Vertical edges */
698 for (y = rect->p0.y + 1; y < rect->p1.y - 1; y++) {
699 pos.y = y;
700
701 pos.x = rect->p0.x;
702 rc = gfx_puttext(&pos, &fmt, boxc->c[1][0]);
703 if (rc != EOK)
704 goto error;
705
706 pos.x = rect->p1.x - 1;
707 rc = gfx_puttext(&pos, &fmt, boxc->c[1][2]);
708 if (rc != EOK)
709 goto error;
710 }
711
712 /* Bottom edge and corners */
713
714 str_cpy(str, bufsz, boxc->c[2][0]);
715 off = str_size(boxc->c[2][0]);
716
717 for (i = 1; i < dim.x - 1; i++) {
718 str_cpy(str + off, bufsz - off, boxc->c[2][1]);
719 off += str_size(boxc->c[2][1]);
720 }
721
722 str_cpy(str + off, bufsz - off, boxc->c[2][2]);
723 off += str_size(boxc->c[2][2]);
724 str[off] = '\0';
725
726 pos.x = rect->p0.x;
727 pos.y = rect->p1.y - 1;
728 rc = gfx_puttext(&pos, &fmt, str);
729 if (rc != EOK)
730 goto error;
731
732 free(str);
733 return EOK;
734error:
735 if (str != NULL)
736 free(str);
737 return rc;
738}
739
740/** Paint a text box.
741 *
742 * Paint a text box with the specified style.
743 *
744 * @param resource UI resource
745 * @param rect Rectangle inside which to paint the box
746 * @param style Box style
747 * @param color Color
748 * @return EOK on success or an error code
749 */
750errno_t ui_paint_text_box(ui_resource_t *resource, gfx_rect_t *rect,
751 ui_box_style_t style, gfx_color_t *color)
752{
753 ui_box_chars_t *boxc = NULL;
754
755 switch (style) {
756 case ui_box_single:
757 boxc = &single_box_chars;
758 break;
759 case ui_box_double:
760 boxc = &double_box_chars;
761 break;
762 }
763
764 if (boxc == NULL)
765 return EINVAL;
766
767 return ui_paint_text_box_custom(resource, rect, boxc, color);
768}
769
770/** Paint a text horizontal brace.
771 *
772 * @param resource UI resource
773 * @param rect Rectangle inside which to paint the brace (height should
774 * be 1).
775 * @param style Box style
776 * @param color Color
777 * @return EOK on success or an error code
778 */
779errno_t ui_paint_text_hbrace(ui_resource_t *resource, gfx_rect_t *rect,
780 ui_box_style_t style, gfx_color_t *color)
781{
782 errno_t rc;
783 gfx_text_fmt_t fmt;
784 gfx_rect_t srect;
785 gfx_coord2_t pos;
786 gfx_coord2_t dim;
787 size_t bufsz;
788 size_t off;
789 int i;
790 char *str = NULL;
791 ui_brace_chars_t *hbc = NULL;
792
793 gfx_rect_points_sort(rect, &srect);
794 gfx_rect_dims(&srect, &dim);
795
796 /* Is rectangle large enough to hold brace? */
797 if (dim.x < 2 || dim.y < 1)
798 return EOK;
799
800 switch (style) {
801 case ui_box_single:
802 hbc = &single_hbrace_chars;
803 break;
804 case ui_box_double:
805 hbc = &double_hbrace_chars;
806 break;
807 }
808
809 if (hbc == NULL)
810 return EINVAL;
811
812 gfx_text_fmt_init(&fmt);
813 fmt.font = resource->font;
814 fmt.color = color;
815
816 bufsz = str_size(hbc->start) +
817 str_size(hbc->middle) * (dim.x - 2) +
818 str_size(hbc->end) + 1;
819
820 str = malloc(bufsz);
821 if (str == NULL)
822 return ENOMEM;
823
824 str_cpy(str, bufsz, hbc->start);
825 off = str_size(hbc->start);
826
827 for (i = 1; i < dim.x - 1; i++) {
828 str_cpy(str + off, bufsz - off, hbc->middle);
829 off += str_size(hbc->middle);
830 }
831
832 str_cpy(str + off, bufsz - off, hbc->end);
833 off += str_size(hbc->end);
834 str[off] = '\0';
835
836 pos = rect->p0;
837 rc = gfx_puttext(&pos, &fmt, str);
838 if (rc != EOK)
839 goto error;
840
841 free(str);
842 return EOK;
843error:
844 if (str != NULL)
845 free(str);
846 return rc;
847}
848
849/** Fill rectangle with text character.
850 *
851 * @param resource UI resource
852 * @param rect Rectangle
853 * @param color Color
854 * @param gchar Character to fill with
855 * @return EOK on success or an error code
856 */
857errno_t ui_paint_text_rect(ui_resource_t *resource, gfx_rect_t *rect,
858 gfx_color_t *color, const char *gchar)
859{
860 gfx_coord2_t pos;
861 gfx_text_fmt_t fmt;
862 gfx_rect_t srect;
863 gfx_coord_t w, i;
864 char *buf;
865 size_t gcharsz;
866 errno_t rc;
867
868 gfx_rect_points_sort(rect, &srect);
869
870 gfx_text_fmt_init(&fmt);
871 fmt.font = resource->font;
872 fmt.color = color;
873 fmt.halign = gfx_halign_left;
874 fmt.valign = gfx_valign_top;
875
876 w = srect.p1.x - srect.p0.x;
877 if (w == 0)
878 return EOK;
879
880 gcharsz = str_size(gchar);
881
882 buf = malloc(w * gcharsz + 1);
883 if (buf == NULL)
884 return ENOMEM;
885
886 for (i = 0; i < w; i++)
887 str_cpy(buf + i * gcharsz, (w - i) * gcharsz + 1, gchar);
888 buf[w * gcharsz] = '\0';
889
890 pos.x = srect.p0.x;
891 for (pos.y = srect.p0.y; pos.y < srect.p1.y; pos.y++) {
892 rc = gfx_puttext(&pos, &fmt, buf);
893 if (rc != EOK)
894 goto error;
895 }
896
897 free(buf);
898 return EOK;
899error:
900 free(buf);
901 return rc;
902}
903
904/** Initialize UI text formatting structure.
905 *
906 * UI text formatting structure must always be initialized using this function
907 * first.
908 *
909 * @param fmt UI text formatting structure
910 */
911void ui_text_fmt_init(ui_text_fmt_t *fmt)
912{
913 memset(fmt, 0, sizeof(ui_text_fmt_t));
914}
915
916/** Compute UI text width.
917 *
918 * @param font Font
919 * @param str String
920 * @return Text width
921 */
922gfx_coord_t ui_text_width(gfx_font_t *font, const char *str)
923{
924 gfx_coord_t w;
925 gfx_coord_t tw;
926 const char *sp;
927
928 /* Text including tildes */
929 w = gfx_text_width(font, str);
930 tw = gfx_text_width(font, "~");
931
932 /* Subtract width of singular tildes */
933 sp = str;
934 while (*sp != '\0') {
935 if (*sp == '~' && sp[1] != '~')
936 w -= tw;
937 ++sp;
938 }
939
940 return w;
941}
942
943/** Paint text (with highlighting markup).
944 *
945 * Paint some text with highlighted sections enclosed with tilde characters
946 * ('~'). A literal tilde can be printed with "~~". Text can be highlighted
947 * with an underline or different color, based on display capabilities.
948 *
949 * @param pos Anchor position
950 * @param fmt Text formatting
951 * @param str String containing '~' markup
952 *
953 * @return EOK on success or an error code
954 */
955errno_t ui_paint_text(gfx_coord2_t *pos, ui_text_fmt_t *fmt, const char *str)
956{
957 gfx_coord2_t tpos;
958 gfx_text_fmt_t tfmt;
959 char *buf;
960 char *endptr;
961 const char *sp;
962 errno_t rc;
963
964 /* Break down string into list of (non)highlighted parts */
965 rc = ui_accel_process(str, &buf, &endptr);
966 if (rc != EOK)
967 return rc;
968
969 gfx_text_fmt_init(&tfmt);
970 tfmt.font = fmt->font;
971 tfmt.color = fmt->color;
972 tfmt.halign = fmt->halign;
973 tfmt.width = fmt->width;
974 tfmt.valign = fmt->valign;
975 tfmt.underline = false;
976
977 tpos = *pos;
978 sp = buf;
979 while (sp != endptr) {
980 /* Print the current string */
981 rc = gfx_puttext(&tpos, &tfmt, sp);
982 if (rc != EOK)
983 goto error;
984
985 gfx_text_cont(&tpos, &tfmt, sp, &tpos, &tfmt);
986 tfmt.underline = !tfmt.underline;
987 tfmt.color = tfmt.underline ? fmt->hgl_color : fmt->color;
988
989 /* Skip to the next string */
990 while (*sp != '\0')
991 ++sp;
992 ++sp;
993 }
994
995 free(buf);
996 return EOK;
997error:
998 free(buf);
999 return rc;
1000}
1001
1002/** @}
1003 */
Note: See TracBrowser for help on using the repository browser.