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

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

Maximizing/unmaximizing a window

  • Property mode set to 100644
File size: 21.1 KB
Line 
1/*
2 * Copyright (c) 2022 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 maximize 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_maxicon(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;
551 rect.p1.x = rect.p0.x + w;
552 rect.p1.y = rect.p0.y + h;
553 rc = gfx_fill_rect(resource->gc, &rect);
554 if (rc != EOK)
555 return rc;
556
557 rc = gfx_set_color(resource->gc, resource->btn_face_color);
558 if (rc != EOK)
559 return rc;
560
561 rect.p0.x += 1;
562 rect.p0.y += 2;
563 rect.p1.x -= 1;
564 rect.p1.y -= 1;
565 rc = gfx_fill_rect(resource->gc, &rect);
566 if (rc != EOK)
567 return rc;
568
569 return EOK;
570}
571
572/** Paint unmaximize icon.
573 *
574 * Unmaximize icon consists of two overlapping window icons.
575 *
576 * @param resource UI resource
577 * @param pos Center position
578 * @param w Window icon width
579 * @param h Window icon height
580 * @param dw Horizontal distance between window icon centers
581 * @param dh Vertical distance between window icon centers
582 * @return EOK on success or an error code
583 */
584errno_t ui_paint_unmaxicon(ui_resource_t *resource, gfx_coord2_t *pos,
585 gfx_coord_t w, gfx_coord_t h, gfx_coord_t dw, gfx_coord_t dh)
586{
587 gfx_coord2_t p;
588 errno_t rc;
589
590 p.x = pos->x + dw / 2;
591 p.y = pos->y - dh / 2;
592 rc = ui_paint_maxicon(resource, &p, w, h);
593 if (rc != EOK)
594 return rc;
595
596 p.x = pos->x - dw / 2;
597 p.y = pos->y + dh / 2;
598 rc = ui_paint_maxicon(resource, &p, w, h);
599 if (rc != EOK)
600 return rc;
601
602 return EOK;
603}
604
605/** Paint a text box.
606 *
607 * @param resource UI resource
608 * @param rect Rectangle inside which to paint the box
609 * @param style Box style
610 * @param color Color
611 * @return EOK on success or an error code
612 */
613errno_t ui_paint_text_box(ui_resource_t *resource, gfx_rect_t *rect,
614 ui_box_style_t style, gfx_color_t *color)
615{
616 errno_t rc;
617 gfx_text_fmt_t fmt;
618 gfx_rect_t srect;
619 gfx_coord2_t pos;
620 gfx_coord2_t dim;
621 size_t bufsz;
622 size_t off;
623 int i;
624 gfx_coord_t y;
625 char *str = NULL;
626 ui_box_chars_t *boxc = NULL;
627
628 gfx_rect_points_sort(rect, &srect);
629 gfx_rect_dims(&srect, &dim);
630
631 /* Is rectangle large enough to hold box? */
632 if (dim.x < 2 || dim.y < 2)
633 return EOK;
634
635 switch (style) {
636 case ui_box_single:
637 boxc = &single_box_chars;
638 break;
639 case ui_box_double:
640 boxc = &double_box_chars;
641 break;
642 }
643
644 if (boxc == NULL)
645 return EINVAL;
646
647 gfx_text_fmt_init(&fmt);
648 fmt.font = resource->font;
649 fmt.color = color;
650
651 bufsz = str_size(boxc->c[0][0]) +
652 str_size(boxc->c[0][1]) * (dim.x - 2) +
653 str_size(boxc->c[0][2]) + 1;
654
655 str = malloc(bufsz);
656 if (str == NULL)
657 return ENOMEM;
658
659 /* Top edge and corners */
660
661 str_cpy(str, bufsz, boxc->c[0][0]);
662 off = str_size(boxc->c[0][0]);
663
664 for (i = 1; i < dim.x - 1; i++) {
665 str_cpy(str + off, bufsz - off, boxc->c[0][1]);
666 off += str_size(boxc->c[0][1]);
667 }
668
669 str_cpy(str + off, bufsz - off, boxc->c[0][2]);
670 off += str_size(boxc->c[0][2]);
671 str[off] = '\0';
672
673 pos = rect->p0;
674 rc = gfx_puttext(&pos, &fmt, str);
675 if (rc != EOK)
676 goto error;
677
678 /* Vertical edges */
679 for (y = rect->p0.y + 1; y < rect->p1.y - 1; y++) {
680 pos.y = y;
681
682 pos.x = rect->p0.x;
683 rc = gfx_puttext(&pos, &fmt, boxc->c[1][0]);
684 if (rc != EOK)
685 goto error;
686
687 pos.x = rect->p1.x - 1;
688 rc = gfx_puttext(&pos, &fmt, boxc->c[1][2]);
689 if (rc != EOK)
690 goto error;
691 }
692
693 /* Bottom edge and corners */
694
695 str_cpy(str, bufsz, boxc->c[2][0]);
696 off = str_size(boxc->c[2][0]);
697
698 for (i = 1; i < dim.x - 1; i++) {
699 str_cpy(str + off, bufsz - off, boxc->c[2][1]);
700 off += str_size(boxc->c[2][1]);
701 }
702
703 str_cpy(str + off, bufsz - off, boxc->c[2][2]);
704 off += str_size(boxc->c[2][2]);
705 str[off] = '\0';
706
707 pos.x = rect->p0.x;
708 pos.y = rect->p1.y - 1;
709 rc = gfx_puttext(&pos, &fmt, str);
710 if (rc != EOK)
711 goto error;
712
713 free(str);
714 return EOK;
715error:
716 if (str != NULL)
717 free(str);
718 return rc;
719}
720
721/** Paint a text horizontal brace.
722 *
723 * @param resource UI resource
724 * @param rect Rectangle inside which to paint the brace (height should
725 * be 1).
726 * @param style Box style
727 * @param color Color
728 * @return EOK on success or an error code
729 */
730errno_t ui_paint_text_hbrace(ui_resource_t *resource, gfx_rect_t *rect,
731 ui_box_style_t style, gfx_color_t *color)
732{
733 errno_t rc;
734 gfx_text_fmt_t fmt;
735 gfx_rect_t srect;
736 gfx_coord2_t pos;
737 gfx_coord2_t dim;
738 size_t bufsz;
739 size_t off;
740 int i;
741 char *str = NULL;
742 ui_brace_chars_t *hbc = NULL;
743
744 gfx_rect_points_sort(rect, &srect);
745 gfx_rect_dims(&srect, &dim);
746
747 /* Is rectangle large enough to hold brace? */
748 if (dim.x < 2 || dim.y < 1)
749 return EOK;
750
751 switch (style) {
752 case ui_box_single:
753 hbc = &single_hbrace_chars;
754 break;
755 case ui_box_double:
756 hbc = &double_hbrace_chars;
757 break;
758 }
759
760 if (hbc == NULL)
761 return EINVAL;
762
763 gfx_text_fmt_init(&fmt);
764 fmt.font = resource->font;
765 fmt.color = color;
766
767 bufsz = str_size(hbc->start) +
768 str_size(hbc->middle) * (dim.x - 2) +
769 str_size(hbc->end) + 1;
770
771 str = malloc(bufsz);
772 if (str == NULL)
773 return ENOMEM;
774
775 str_cpy(str, bufsz, hbc->start);
776 off = str_size(hbc->start);
777
778 for (i = 1; i < dim.x - 1; i++) {
779 str_cpy(str + off, bufsz - off, hbc->middle);
780 off += str_size(hbc->middle);
781 }
782
783 str_cpy(str + off, bufsz - off, hbc->end);
784 off += str_size(hbc->end);
785 str[off] = '\0';
786
787 pos = rect->p0;
788 rc = gfx_puttext(&pos, &fmt, str);
789 if (rc != EOK)
790 goto error;
791
792 free(str);
793 return EOK;
794error:
795 if (str != NULL)
796 free(str);
797 return rc;
798}
799
800/** Fill rectangle with text character.
801 *
802 * @param resource UI resource
803 * @param rect Rectangle
804 * @param color Color
805 * @param gchar Character to fill with
806 * @return EOK on success or an error code
807 */
808errno_t ui_paint_text_rect(ui_resource_t *resource, gfx_rect_t *rect,
809 gfx_color_t *color, const char *gchar)
810{
811 gfx_coord2_t pos;
812 gfx_text_fmt_t fmt;
813 gfx_rect_t srect;
814 gfx_coord_t w, i;
815 char *buf;
816 size_t gcharsz;
817 errno_t rc;
818
819 gfx_rect_points_sort(rect, &srect);
820
821 gfx_text_fmt_init(&fmt);
822 fmt.font = resource->font;
823 fmt.color = color;
824 fmt.halign = gfx_halign_left;
825 fmt.valign = gfx_valign_top;
826
827 w = srect.p1.x - srect.p0.x;
828 if (w == 0)
829 return EOK;
830
831 gcharsz = str_size(gchar);
832
833 buf = malloc(w * gcharsz + 1);
834 if (buf == NULL)
835 return ENOMEM;
836
837 for (i = 0; i < w; i++)
838 str_cpy(buf + i * gcharsz, (w - i) * gcharsz + 1, gchar);
839 buf[w * gcharsz] = '\0';
840
841 pos.x = srect.p0.x;
842 for (pos.y = srect.p0.y; pos.y < srect.p1.y; pos.y++) {
843 rc = gfx_puttext(&pos, &fmt, buf);
844 if (rc != EOK)
845 goto error;
846 }
847
848 free(buf);
849 return EOK;
850error:
851 free(buf);
852 return rc;
853}
854
855/** Initialize UI text formatting structure.
856 *
857 * UI text formatting structure must always be initialized using this function
858 * first.
859 *
860 * @param fmt UI text formatting structure
861 */
862void ui_text_fmt_init(ui_text_fmt_t *fmt)
863{
864 memset(fmt, 0, sizeof(ui_text_fmt_t));
865}
866
867/** Compute UI text width.
868 *
869 * @param font Font
870 * @param str String
871 * @return Text width
872 */
873gfx_coord_t ui_text_width(gfx_font_t *font, const char *str)
874{
875 gfx_coord_t w;
876 gfx_coord_t tw;
877 const char *sp;
878
879 /* Text including tildes */
880 w = gfx_text_width(font, str);
881 tw = gfx_text_width(font, "~");
882
883 /* Subtract width of singular tildes */
884 sp = str;
885 while (*sp != '\0') {
886 if (*sp == '~' && sp[1] != '~')
887 w -= tw;
888 ++sp;
889 }
890
891 return w;
892}
893
894/** Paint text (with highlighting markup).
895 *
896 * Paint some text with highlighted sections enclosed with tilde characters
897 * ('~'). A literal tilde can be printed with "~~". Text can be highlighted
898 * with an underline or different color, based on display capabilities.
899 *
900 * @param pos Anchor position
901 * @param fmt Text formatting
902 * @param str String containing '~' markup
903 *
904 * @return EOK on success or an error code
905 */
906errno_t ui_paint_text(gfx_coord2_t *pos, ui_text_fmt_t *fmt, const char *str)
907{
908 gfx_coord2_t tpos;
909 gfx_text_fmt_t tfmt;
910 char *buf;
911 char *endptr;
912 const char *sp;
913 errno_t rc;
914
915 /* Break down string into list of (non)highlighted parts */
916 rc = ui_accel_process(str, &buf, &endptr);
917 if (rc != EOK)
918 return rc;
919
920 gfx_text_fmt_init(&tfmt);
921 tfmt.font = fmt->font;
922 tfmt.color = fmt->color;
923 tfmt.halign = fmt->halign;
924 tfmt.justify_width = fmt->justify_width;
925 tfmt.valign = fmt->valign;
926 tfmt.underline = false;
927
928 tpos = *pos;
929 sp = buf;
930 while (sp != endptr) {
931 /* Print the current string */
932 rc = gfx_puttext(&tpos, &tfmt, sp);
933 if (rc != EOK)
934 goto error;
935
936 gfx_text_cont(&tpos, &tfmt, sp, &tpos, &tfmt);
937 tfmt.underline = !tfmt.underline;
938 tfmt.color = tfmt.underline ? fmt->hgl_color : fmt->color;
939
940 /* Skip to the next string */
941 while (*sp != '\0')
942 ++sp;
943 ++sp;
944 }
945
946 free(buf);
947 return EOK;
948error:
949 free(buf);
950 return rc;
951}
952
953/** @}
954 */
Note: See TracBrowser for help on using the repository browser.