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

Last change on this file was 211fd68, checked in by Jiri Svoboda <jiri@…>, 16 months ago

Add text mode support to Barber

  • Property mode set to 100644
File size: 32.0 KB
Line 
1/*
2 * Copyright (c) 2024 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 Window decoration
34 */
35
36#include <assert.h>
37#include <errno.h>
38#include <gfx/color.h>
39#include <gfx/context.h>
40#include <gfx/render.h>
41#include <gfx/text.h>
42#include <io/pos_event.h>
43#include <stdlib.h>
44#include <str.h>
45#include <ui/paint.h>
46#include <ui/pbutton.h>
47#include <ui/ui.h>
48#include <ui/wdecor.h>
49#include "../private/resource.h"
50#include "../private/wdecor.h"
51
52static void ui_wdecor_btn_min_clicked(ui_pbutton_t *, void *);
53static errno_t ui_wdecor_btn_min_paint(ui_pbutton_t *, void *,
54 gfx_coord2_t *);
55
56static void ui_wdecor_btn_max_clicked(ui_pbutton_t *, void *);
57static errno_t ui_wdecor_btn_max_paint(ui_pbutton_t *, void *,
58 gfx_coord2_t *);
59
60static void ui_wdecor_btn_close_clicked(ui_pbutton_t *, void *);
61static errno_t ui_wdecor_btn_close_paint(ui_pbutton_t *, void *,
62 gfx_coord2_t *);
63
64static ui_pbutton_cb_t ui_wdecor_btn_min_cb = {
65 .clicked = ui_wdecor_btn_min_clicked
66};
67
68static ui_pbutton_decor_ops_t ui_wdecor_btn_min_decor_ops = {
69 .paint = ui_wdecor_btn_min_paint
70};
71
72static ui_pbutton_cb_t ui_wdecor_btn_max_cb = {
73 .clicked = ui_wdecor_btn_max_clicked
74};
75
76static ui_pbutton_decor_ops_t ui_wdecor_btn_max_decor_ops = {
77 .paint = ui_wdecor_btn_max_paint
78};
79
80static ui_pbutton_cb_t ui_wdecor_btn_close_cb = {
81 .clicked = ui_wdecor_btn_close_clicked
82};
83
84static ui_pbutton_decor_ops_t ui_wdecor_btn_close_decor_ops = {
85 .paint = ui_wdecor_btn_close_paint
86};
87
88enum {
89 /** Width of corner drag area */
90 wdecor_corner_w = 24,
91 /** Height of corner drag area */
92 wdecor_corner_h = 24,
93 /** Window resizing edge witdth */
94 wdecor_edge_w = 4,
95 /** Window resizing edge height */
96 wdecor_edge_h = 4,
97 /** Window resizing edge witdth */
98 wdecor_edge_w_text = 1,
99 /** Window resizing edge height */
100 wdecor_edge_h_text = 1,
101 /** Title bar height */
102 wdecor_tbar_h = 22,
103 /** Window width */
104 wdecor_frame_w = 4,
105 /** Window frame width in text mode */
106 wdecor_frame_w_text = 1,
107 /** Window caption horizontal margin */
108 wdecor_cap_hmargin = 4,
109 /** Window caption horizontal margin in text mode */
110 wdecor_cap_hmargin_text = 1,
111 /** System menu handle width */
112 wdecor_sysmenu_hdl_w = 20,
113 /** System menu handle height */
114 wdecor_sysmenu_hdl_h = 20,
115 /** System menu handle width in text mode */
116 wdecor_sysmenu_hdl_w_text = 3,
117 /** System menu handle height in text mode */
118 wdecor_sysmenu_hdl_h_text = 1,
119 /** Close button cross leg length */
120 wdecor_close_cross_n = 5,
121 /** Close button cross pen width */
122 wdecor_close_cross_w = 2,
123 /** Close button cross pen height */
124 wdecor_close_cross_h = 1,
125 /** Minimize icon width */
126 wdecor_min_w = 10,
127 /** Minimize icon height */
128 wdecor_min_h = 10,
129 /** Maximize icon width */
130 wdecor_max_w = 10,
131 /** Maximize icon height */
132 wdecor_max_h = 10,
133 /** Unmaximize icon window width */
134 wdecor_unmax_w = 8,
135 /** Unmaximize icon window height */
136 wdecor_unmax_h = 8,
137 /** Unmaximize icon window horizontal distance */
138 wdecor_unmax_dw = 4,
139 /** Unmaximize icon window vertical distance */
140 wdecor_unmax_dh = 4
141};
142
143/** Create new window decoration.
144 *
145 * @param resource UI resource
146 * @param caption Window caption
147 * @param style Style
148 * @param rwdecor Place to store pointer to new window decoration
149 * @return EOK on success, ENOMEM if out of memory
150 */
151errno_t ui_wdecor_create(ui_resource_t *resource, const char *caption,
152 ui_wdecor_style_t style, ui_wdecor_t **rwdecor)
153{
154 ui_wdecor_t *wdecor;
155 errno_t rc;
156
157 wdecor = calloc(1, sizeof(ui_wdecor_t));
158 if (wdecor == NULL)
159 return ENOMEM;
160
161 wdecor->caption = str_dup(caption);
162 if (wdecor->caption == NULL) {
163 free(wdecor);
164 return ENOMEM;
165 }
166
167 if ((style & ui_wds_minimize_btn) != 0) {
168 rc = ui_pbutton_create(resource, "_", &wdecor->btn_min);
169 if (rc != EOK) {
170 ui_wdecor_destroy(wdecor);
171 return rc;
172 }
173
174 ui_pbutton_set_cb(wdecor->btn_min, &ui_wdecor_btn_min_cb,
175 (void *)wdecor);
176
177 ui_pbutton_set_decor_ops(wdecor->btn_min,
178 &ui_wdecor_btn_min_decor_ops, (void *)wdecor);
179 }
180
181 if ((style & ui_wds_maximize_btn) != 0) {
182 rc = ui_pbutton_create(resource, "^", &wdecor->btn_max);
183 if (rc != EOK) {
184 ui_wdecor_destroy(wdecor);
185 return rc;
186 }
187
188 ui_pbutton_set_cb(wdecor->btn_max, &ui_wdecor_btn_max_cb,
189 (void *)wdecor);
190
191 ui_pbutton_set_decor_ops(wdecor->btn_max,
192 &ui_wdecor_btn_max_decor_ops, (void *)wdecor);
193 }
194
195 if ((style & ui_wds_close_btn) != 0) {
196 rc = ui_pbutton_create(resource, "X", &wdecor->btn_close);
197 if (rc != EOK) {
198 ui_wdecor_destroy(wdecor);
199 return rc;
200 }
201
202 ui_pbutton_set_cb(wdecor->btn_close, &ui_wdecor_btn_close_cb,
203 (void *)wdecor);
204
205 ui_pbutton_set_decor_ops(wdecor->btn_close,
206 &ui_wdecor_btn_close_decor_ops, (void *)wdecor);
207 }
208
209 wdecor->res = resource;
210 wdecor->active = true;
211 wdecor->style = style;
212 *rwdecor = wdecor;
213 return EOK;
214}
215
216/** Destroy window decoration.
217 *
218 * @param wdecor Window decoration or @c NULL
219 */
220void ui_wdecor_destroy(ui_wdecor_t *wdecor)
221{
222 if (wdecor == NULL)
223 return;
224
225 ui_pbutton_destroy(wdecor->btn_min);
226 ui_pbutton_destroy(wdecor->btn_max);
227 ui_pbutton_destroy(wdecor->btn_close);
228 free(wdecor->caption);
229 free(wdecor);
230}
231
232/** Set window decoration callbacks.
233 *
234 * @param wdecor Window decoration
235 * @param cb Window decoration callbacks
236 * @param arg Callback argument
237 */
238void ui_wdecor_set_cb(ui_wdecor_t *wdecor, ui_wdecor_cb_t *cb, void *arg)
239{
240 wdecor->cb = cb;
241 wdecor->arg = arg;
242}
243
244/** Set window decoration rectangle.
245 *
246 * @param wdecor Window decoration
247 * @param rect New window decoration rectangle
248 */
249void ui_wdecor_set_rect(ui_wdecor_t *wdecor, gfx_rect_t *rect)
250{
251 ui_wdecor_geom_t geom;
252
253 wdecor->rect = *rect;
254
255 ui_wdecor_get_geom(wdecor, &geom);
256
257 if (wdecor->btn_min != NULL)
258 ui_pbutton_set_rect(wdecor->btn_min, &geom.btn_min_rect);
259 if (wdecor->btn_max != NULL)
260 ui_pbutton_set_rect(wdecor->btn_max, &geom.btn_max_rect);
261 if (wdecor->btn_close != NULL)
262 ui_pbutton_set_rect(wdecor->btn_close, &geom.btn_close_rect);
263}
264
265/** Set active flag.
266 *
267 * Active window is the one receiving keyboard events.
268 *
269 * @param wdecor Window decoration
270 * @param active @c true iff window is active
271 */
272void ui_wdecor_set_active(ui_wdecor_t *wdecor, bool active)
273{
274 wdecor->active = active;
275}
276
277/** Set maximized flag.
278 *
279 * Active window is the one receiving keyboard events.
280 *
281 * @param wdecor Window decoration
282 * @param maximized @c true iff window is maximized
283 */
284void ui_wdecor_set_maximized(ui_wdecor_t *wdecor, bool maximized)
285{
286 wdecor->maximized = maximized;
287}
288
289/** Change caption.
290 *
291 * @param wdecor Window decoration
292 * @param caption New caption
293 *
294 * @return EOK on success or an error code
295 */
296errno_t ui_wdecor_set_caption(ui_wdecor_t *wdecor, const char *caption)
297{
298 char *cdup;
299
300 cdup = str_dup(caption);
301 if (cdup == NULL)
302 return ENOMEM;
303
304 free(wdecor->caption);
305 wdecor->caption = cdup;
306
307 ui_wdecor_paint(wdecor);
308 return EOK;
309}
310
311/** Paint system menu handle in graphics mode.
312 *
313 * @param wdecor Window decoration
314 * @param rect System menu handle rectangle
315 * @return EOK on success or an error code
316 */
317errno_t ui_wdecor_sysmenu_hdl_paint_gfx(ui_wdecor_t *wdecor, gfx_rect_t *rect)
318{
319 errno_t rc;
320 gfx_rect_t mrect;
321 gfx_rect_t inside;
322 gfx_coord2_t center;
323
324 rc = gfx_set_color(wdecor->res->gc, wdecor->sysmenu_hdl_active ?
325 wdecor->res->btn_shadow_color : wdecor->res->btn_face_color);
326 if (rc != EOK)
327 return rc;
328
329 rc = gfx_fill_rect(wdecor->res->gc, rect);
330 if (rc != EOK)
331 return rc;
332
333 center.x = (rect->p0.x + rect->p1.x) / 2;
334 center.y = (rect->p0.y + rect->p1.y) / 2;
335 mrect.p0.x = center.x - 7;
336 mrect.p0.y = center.y - 1;
337 mrect.p1.x = center.x + 7;
338 mrect.p1.y = center.y + 1 + 1;
339
340 /* XXX Not really a bevel, just a frame */
341 rc = ui_paint_bevel(wdecor->res->gc, &mrect,
342 wdecor->res->btn_text_color, wdecor->res->btn_text_color, 1,
343 &inside);
344 if (rc != EOK)
345 return rc;
346
347 rc = gfx_set_color(wdecor->res->gc, wdecor->res->btn_highlight_color);
348 if (rc != EOK)
349 return rc;
350
351 rc = gfx_fill_rect(wdecor->res->gc, &inside);
352 if (rc != EOK)
353 return rc;
354
355 return EOK;
356}
357
358/** Paint system menu handle in text mode.
359 *
360 * @param wdecor Window decoration
361 * @param rect System menu handle rectangle
362 * @return EOK on success or an error code
363 */
364errno_t ui_wdecor_sysmenu_hdl_paint_text(ui_wdecor_t *wdecor, gfx_rect_t *rect)
365{
366 errno_t rc;
367 gfx_text_fmt_t fmt;
368
369 rc = gfx_set_color(wdecor->res->gc, wdecor->sysmenu_hdl_active ?
370 wdecor->res->btn_shadow_color : wdecor->res->btn_face_color);
371
372 gfx_text_fmt_init(&fmt);
373 fmt.font = wdecor->res->font;
374 fmt.color = wdecor->sysmenu_hdl_active ?
375 wdecor->res->wnd_sel_text_color :
376 wdecor->res->tbar_act_text_color;
377 fmt.halign = gfx_halign_left;
378 fmt.valign = gfx_valign_top;
379
380 rc = gfx_puttext(&rect->p0, &fmt, "[\u2261]");
381 if (rc != EOK)
382 return rc;
383
384 return EOK;
385}
386
387/** Paint system menu handle.
388 *
389 * @param wdecor Window decoration
390 * @param rect System menu handle rectangle
391 * @return EOK on success or an error code
392 */
393errno_t ui_wdecor_sysmenu_hdl_paint(ui_wdecor_t *wdecor, gfx_rect_t *rect)
394{
395 errno_t rc;
396
397 if (wdecor->res->textmode)
398 rc = ui_wdecor_sysmenu_hdl_paint_text(wdecor, rect);
399 else
400 rc = ui_wdecor_sysmenu_hdl_paint_gfx(wdecor, rect);
401
402 return rc;
403}
404
405/** Set system menu handle active flag.
406 *
407 * @param wdecor Window decoration
408 * @param active @c true iff handle should be active
409 */
410void ui_wdecor_sysmenu_hdl_set_active(ui_wdecor_t *wdecor, bool active)
411{
412 ui_wdecor_geom_t geom;
413
414 wdecor->sysmenu_hdl_active = active;
415
416 ui_wdecor_get_geom(wdecor, &geom);
417 (void) ui_wdecor_sysmenu_hdl_paint(wdecor, &geom.sysmenu_hdl_rect);
418 (void) gfx_update(wdecor->res->gc);
419}
420
421/** Paint window decoration.
422 *
423 * @param wdecor Window decoration
424 * @return EOK on success or an error code
425 */
426errno_t ui_wdecor_paint(ui_wdecor_t *wdecor)
427{
428 errno_t rc;
429 gfx_rect_t rect;
430 gfx_rect_t trect;
431 gfx_rect_t text_rect;
432 gfx_text_fmt_t fmt;
433 gfx_coord2_t pos;
434 ui_wdecor_geom_t geom;
435
436 rect = wdecor->rect;
437 ui_wdecor_get_geom(wdecor, &geom);
438
439 if ((wdecor->style & ui_wds_frame) != 0) {
440
441 if (wdecor->res->textmode != false) {
442 rc = ui_paint_text_box(wdecor->res, &rect,
443 ui_box_double, wdecor->res->wnd_face_color);
444 if (rc != EOK)
445 return rc;
446 } else {
447 rc = ui_paint_outset_frame(wdecor->res, &rect,
448 &rect);
449 if (rc != EOK)
450 return rc;
451
452 rc = ui_paint_bevel(wdecor->res->gc, &rect,
453 wdecor->res->wnd_face_color,
454 wdecor->res->wnd_face_color, 2, &rect);
455 if (rc != EOK)
456 return rc;
457 }
458 }
459
460 if ((wdecor->style & ui_wds_titlebar) != 0) {
461 trect = geom.title_bar_rect;
462
463 if (wdecor->res->textmode == false) {
464 rc = ui_paint_bevel(wdecor->res->gc, &trect,
465 wdecor->res->wnd_shadow_color,
466 wdecor->res->wnd_highlight_color, 1, &trect);
467 if (rc != EOK)
468 return rc;
469
470 rc = gfx_set_color(wdecor->res->gc, wdecor->active ?
471 wdecor->res->tbar_act_bg_color :
472 wdecor->res->tbar_inact_bg_color);
473 if (rc != EOK)
474 return rc;
475
476 rc = gfx_fill_rect(wdecor->res->gc, &trect);
477 if (rc != EOK)
478 return rc;
479 }
480
481 gfx_text_fmt_init(&fmt);
482 fmt.font = wdecor->res->font;
483 fmt.color = wdecor->active ?
484 wdecor->res->tbar_act_text_color :
485 wdecor->res->tbar_inact_text_color;
486 fmt.halign = gfx_halign_center;
487 fmt.valign = gfx_valign_center;
488 fmt.abbreviate = true;
489 fmt.width = geom.caption_rect.p1.x -
490 geom.caption_rect.p0.x;
491
492 pos.x = (geom.caption_rect.p0.x + geom.caption_rect.p1.x) / 2;
493 pos.y = (geom.caption_rect.p0.y + geom.caption_rect.p1.y) / 2;
494
495 if (wdecor->res->textmode) {
496 /* Make space around caption text */
497 gfx_text_rect(&pos, &fmt, wdecor->caption, &text_rect);
498
499 /* Only make space if caption is non-empty */
500 if (text_rect.p0.x < text_rect.p1.x) {
501 text_rect.p0.x -= 1;
502 text_rect.p1.x += 1;
503 }
504
505 rc = gfx_set_color(wdecor->res->gc, wdecor->active ?
506 wdecor->res->tbar_act_bg_color :
507 wdecor->res->tbar_inact_bg_color);
508 if (rc != EOK)
509 return rc;
510
511 rc = gfx_fill_rect(wdecor->res->gc, &text_rect);
512 if (rc != EOK)
513 return rc;
514 }
515
516 rc = gfx_puttext(&pos, &fmt, wdecor->caption);
517 if (rc != EOK)
518 return rc;
519
520 if ((wdecor->style & ui_wds_sysmenu_hdl) != 0) {
521 rc = ui_wdecor_sysmenu_hdl_paint(wdecor,
522 &geom.sysmenu_hdl_rect);
523 if (rc != EOK)
524 return rc;
525 }
526
527 if (wdecor->btn_min != NULL) {
528 rc = ui_pbutton_paint(wdecor->btn_min);
529 if (rc != EOK)
530 return rc;
531 }
532
533 if (wdecor->btn_max != NULL) {
534 rc = ui_pbutton_paint(wdecor->btn_max);
535 if (rc != EOK)
536 return rc;
537 }
538
539 if (wdecor->btn_close != NULL) {
540 rc = ui_pbutton_paint(wdecor->btn_close);
541 if (rc != EOK)
542 return rc;
543 }
544 }
545
546 rc = gfx_update(wdecor->res->gc);
547 if (rc != EOK)
548 return rc;
549
550 return EOK;
551}
552
553/** Send decoration sysmenu open event.
554 *
555 * @param wdecor Window decoration
556 * @param idev_id Input device ID
557 */
558void ui_wdecor_sysmenu_open(ui_wdecor_t *wdecor, sysarg_t idev_id)
559{
560 if (wdecor->cb != NULL && wdecor->cb->sysmenu_open != NULL)
561 wdecor->cb->sysmenu_open(wdecor, wdecor->arg, idev_id);
562}
563
564/** Send decoration sysmenu left event.
565 *
566 * @param wdecor Window decoration
567 * @param idev_id Input device ID
568 */
569void ui_wdecor_sysmenu_left(ui_wdecor_t *wdecor, sysarg_t idev_id)
570{
571 if (wdecor->cb != NULL && wdecor->cb->sysmenu_left != NULL)
572 wdecor->cb->sysmenu_left(wdecor, wdecor->arg, idev_id);
573}
574
575/** Send decoration sysmenu right event.
576 *
577 * @param wdecor Window decoration
578 * @param idev_id Input device ID
579 */
580void ui_wdecor_sysmenu_right(ui_wdecor_t *wdecor, sysarg_t idev_id)
581{
582 if (wdecor->cb != NULL && wdecor->cb->sysmenu_right != NULL)
583 wdecor->cb->sysmenu_right(wdecor, wdecor->arg, idev_id);
584}
585
586/** Send decoration sysmenu accelerator event.
587 *
588 * @param wdecor Window decoration
589 * @param c Accelerator character
590 * @param idev_id Input device ID
591 */
592void ui_wdecor_sysmenu_accel(ui_wdecor_t *wdecor, char32_t c, sysarg_t idev_id)
593{
594 if (wdecor->cb != NULL && wdecor->cb->sysmenu_right != NULL)
595 wdecor->cb->sysmenu_accel(wdecor, wdecor->arg, c, idev_id);
596}
597
598/** Send decoration minimize event.
599 *
600 * @param wdecor Window decoration
601 */
602void ui_wdecor_minimize(ui_wdecor_t *wdecor)
603{
604 if (wdecor->cb != NULL && wdecor->cb->minimize != NULL)
605 wdecor->cb->minimize(wdecor, wdecor->arg);
606}
607
608/** Send decoration maximize event.
609 *
610 * @param wdecor Window decoration
611 */
612void ui_wdecor_maximize(ui_wdecor_t *wdecor)
613{
614 if (wdecor->cb != NULL && wdecor->cb->maximize != NULL)
615 wdecor->cb->maximize(wdecor, wdecor->arg);
616}
617
618/** Send decoration unmaximize event.
619 *
620 * @param wdecor Window decoration
621 */
622void ui_wdecor_unmaximize(ui_wdecor_t *wdecor)
623{
624 if (wdecor->cb != NULL && wdecor->cb->unmaximize != NULL)
625 wdecor->cb->unmaximize(wdecor, wdecor->arg);
626}
627
628/** Send decoration close event.
629 *
630 * @param wdecor Window decoration
631 */
632void ui_wdecor_close(ui_wdecor_t *wdecor)
633{
634 if (wdecor->cb != NULL && wdecor->cb->close != NULL)
635 wdecor->cb->close(wdecor, wdecor->arg);
636}
637
638/** Send decoration move event.
639 *
640 * @param wdecor Window decoration
641 * @param pos Position where the title bar was pressed
642 * @param pos_id Positioning device ID
643 */
644void ui_wdecor_move(ui_wdecor_t *wdecor, gfx_coord2_t *pos, sysarg_t pos_id)
645{
646 if (wdecor->cb != NULL && wdecor->cb->move != NULL)
647 wdecor->cb->move(wdecor, wdecor->arg, pos, pos_id);
648}
649
650/** Send decoration resize event.
651 *
652 * @param wdecor Window decoration
653 * @param rsztype Resize type
654 * @param pos Position where the button was pressed
655 * @param pos_id Positioning device ID
656 */
657void ui_wdecor_resize(ui_wdecor_t *wdecor, ui_wdecor_rsztype_t rsztype,
658 gfx_coord2_t *pos, sysarg_t pos_id)
659{
660 if (wdecor->cb != NULL && wdecor->cb->resize != NULL)
661 wdecor->cb->resize(wdecor, wdecor->arg, rsztype, pos, pos_id);
662}
663
664/** Send cursor change event.
665 *
666 * @param wdecor Window decoration
667 * @param cursor Cursor
668 */
669void ui_wdecor_set_cursor(ui_wdecor_t *wdecor, ui_stock_cursor_t cursor)
670{
671 if (wdecor->cb != NULL && wdecor->cb->set_cursor != NULL)
672 wdecor->cb->set_cursor(wdecor, wdecor->arg, cursor);
673}
674
675/** Get window decoration geometry.
676 *
677 * @param wdecor Window decoration
678 * @param geom Structure to fill in with computed geometry
679 */
680void ui_wdecor_get_geom(ui_wdecor_t *wdecor, ui_wdecor_geom_t *geom)
681{
682 gfx_coord_t frame_w;
683 gfx_coord_t btn_x;
684 gfx_coord_t btn_y;
685 gfx_coord_t cap_hmargin;
686 gfx_coord_t cap_x;
687 gfx_coord_t hdl_x_off;
688 gfx_coord_t hdl_y_off;
689 gfx_coord_t hdl_w;
690 gfx_coord_t hdl_h;
691
692 /* Does window have a frame? */
693 if ((wdecor->style & ui_wds_frame) != 0) {
694 frame_w = wdecor->res->textmode ?
695 wdecor_frame_w_text : wdecor_frame_w;
696
697 geom->interior_rect.p0.x = wdecor->rect.p0.x + frame_w;
698 geom->interior_rect.p0.y = wdecor->rect.p0.y + frame_w;
699 geom->interior_rect.p1.x = wdecor->rect.p1.x - frame_w;
700 geom->interior_rect.p1.y = wdecor->rect.p1.y - frame_w;
701 } else {
702 geom->interior_rect = wdecor->rect;
703 }
704
705 /* Does window have a title bar? */
706 if ((wdecor->style & ui_wds_titlebar) != 0) {
707 if (wdecor->res->textmode) {
708 geom->title_bar_rect.p0 = wdecor->rect.p0;
709 geom->title_bar_rect.p1.x = wdecor->rect.p1.x;
710 geom->title_bar_rect.p1.y = wdecor->rect.p0.y + 1;
711
712 btn_x = geom->title_bar_rect.p1.x - 1;
713 btn_y = geom->title_bar_rect.p0.y;
714 } else {
715 geom->title_bar_rect.p0 = geom->interior_rect.p0;
716 geom->title_bar_rect.p1.x = geom->interior_rect.p1.x;
717 geom->title_bar_rect.p1.y = geom->interior_rect.p0.y +
718 wdecor_tbar_h;
719
720 btn_x = geom->title_bar_rect.p1.x - 1;
721 btn_y = geom->title_bar_rect.p0.y + 1;
722 }
723
724 geom->app_area_rect.p0.x = geom->interior_rect.p0.x;
725 geom->app_area_rect.p0.y = geom->title_bar_rect.p1.y;
726 geom->app_area_rect.p1 = geom->interior_rect.p1;
727
728 } else {
729 geom->title_bar_rect.p0.x = 0;
730 geom->title_bar_rect.p0.y = 0;
731 geom->title_bar_rect.p1.x = 0;
732 geom->title_bar_rect.p1.y = 0;
733
734 geom->app_area_rect = geom->interior_rect;
735 btn_x = 0;
736 btn_y = 0;
737 }
738
739 /* Does window have a sysmenu handle? */
740 if ((wdecor->style & ui_wds_sysmenu_hdl) != 0) {
741 if (wdecor->res->textmode) {
742 hdl_x_off = 2;
743 hdl_y_off = 0;
744 hdl_w = wdecor_sysmenu_hdl_w_text;
745 hdl_h = wdecor_sysmenu_hdl_h_text;
746 } else {
747 hdl_x_off = 1;
748 hdl_y_off = 1;
749 hdl_w = wdecor_sysmenu_hdl_w;
750 hdl_h = wdecor_sysmenu_hdl_h;
751 }
752
753 geom->sysmenu_hdl_rect.p0.x = geom->title_bar_rect.p0.x +
754 hdl_x_off;
755 geom->sysmenu_hdl_rect.p0.y = geom->title_bar_rect.p0.y +
756 hdl_y_off;
757 geom->sysmenu_hdl_rect.p1.x = geom->sysmenu_hdl_rect.p0.x +
758 hdl_w;
759 geom->sysmenu_hdl_rect.p1.y = geom->sysmenu_hdl_rect.p0.y +
760 hdl_h;
761 cap_x = hdl_w;
762 } else {
763 geom->sysmenu_hdl_rect.p0.x = 0;
764 geom->sysmenu_hdl_rect.p0.y = 0;
765 geom->sysmenu_hdl_rect.p1.x = 0;
766 geom->sysmenu_hdl_rect.p1.y = 0;
767 cap_x = 0;
768 }
769
770 /* Does window have a close button? */
771 if ((wdecor->style & ui_wds_close_btn) != 0) {
772 if (wdecor->res->textmode == false) {
773 geom->btn_close_rect.p0.x = btn_x - 20;
774 geom->btn_close_rect.p0.y = btn_y;
775 geom->btn_close_rect.p1.x = btn_x;
776 geom->btn_close_rect.p1.y = btn_y + 20;
777
778 btn_x -= 20;
779 } else {
780 geom->btn_close_rect.p0.x = btn_x - 3;
781 geom->btn_close_rect.p0.y = btn_y;
782 geom->btn_close_rect.p1.x = btn_x;
783 geom->btn_close_rect.p1.y = btn_y + 1;
784
785 btn_x -= 3;
786 }
787 } else {
788 geom->btn_close_rect.p0.x = 0;
789 geom->btn_close_rect.p0.y = 0;
790 geom->btn_close_rect.p1.x = 0;
791 geom->btn_close_rect.p1.y = 0;
792 }
793
794 /* Does window have a (un)maximize button? */
795 if ((wdecor->style & ui_wds_maximize_btn) != 0) {
796 if (wdecor->res->textmode == false) {
797 geom->btn_max_rect.p0.x = btn_x - 20;
798 geom->btn_max_rect.p0.y = btn_y;
799 geom->btn_max_rect.p1.x = btn_x;
800 geom->btn_max_rect.p1.y = btn_y + 20;
801
802 btn_x -= 20;
803 } else {
804 geom->btn_max_rect.p0.x = btn_x - 3;
805 geom->btn_max_rect.p0.y = btn_y;
806 geom->btn_max_rect.p1.x = btn_x;
807 geom->btn_max_rect.p1.y = btn_y + 1;
808
809 btn_x -= 3;
810 }
811 } else {
812 geom->btn_max_rect.p0.x = 0;
813 geom->btn_max_rect.p0.y = 0;
814 geom->btn_max_rect.p1.x = 0;
815 geom->btn_max_rect.p1.y = 0;
816 }
817
818 /* Does window have a minimize button? */
819 if ((wdecor->style & ui_wds_minimize_btn) != 0) {
820 if (wdecor->res->textmode == false) {
821 geom->btn_min_rect.p0.x = btn_x - 20;
822 geom->btn_min_rect.p0.y = btn_y;
823 geom->btn_min_rect.p1.x = btn_x;
824 geom->btn_min_rect.p1.y = btn_y + 20;
825
826 btn_x -= 20;
827 } else {
828 geom->btn_min_rect.p0.x = btn_x - 3;
829 geom->btn_min_rect.p0.y = btn_y;
830 geom->btn_min_rect.p1.x = btn_x;
831 geom->btn_min_rect.p1.y = btn_y + 1;
832
833 btn_x -= 3;
834 }
835 } else {
836 geom->btn_min_rect.p0.x = 0;
837 geom->btn_min_rect.p0.y = 0;
838 geom->btn_min_rect.p1.x = 0;
839 geom->btn_min_rect.p1.y = 0;
840 }
841
842 if ((wdecor->style & ui_wds_titlebar) != 0) {
843 if (wdecor->res->textmode == false)
844 cap_hmargin = wdecor_cap_hmargin;
845 else
846 cap_hmargin = wdecor_cap_hmargin_text;
847
848 geom->caption_rect.p0.x = geom->title_bar_rect.p0.x +
849 cap_hmargin + cap_x;
850 geom->caption_rect.p0.y = geom->title_bar_rect.p0.y;
851 geom->caption_rect.p1.x = btn_x - cap_hmargin;
852 geom->caption_rect.p1.y = geom->title_bar_rect.p1.y;
853 } else {
854 geom->caption_rect.p0.x = 0;
855 geom->caption_rect.p0.y = 0;
856 geom->caption_rect.p1.x = 0;
857 geom->caption_rect.p1.y = 0;
858 }
859}
860
861/** Get outer rectangle from application area rectangle.
862 *
863 * Note that this needs to work just based on a UI, without having an actual
864 * window decoration, since we need it in order to create the window
865 * and its decoration.
866 *
867 * @param ui UI
868 * @param style Decoration style
869 * @param app Application area rectangle
870 * @param rect Place to store (outer) window decoration rectangle
871 */
872void ui_wdecor_rect_from_app(ui_t *ui, ui_wdecor_style_t style,
873 gfx_rect_t *app, gfx_rect_t *rect)
874{
875 bool textmode;
876 gfx_coord_t edge_w, edge_h;
877 *rect = *app;
878
879 textmode = ui_is_textmode(ui);
880 if (textmode) {
881 edge_w = wdecor_edge_w_text;
882 edge_h = wdecor_edge_h_text;
883 } else {
884 edge_w = wdecor_edge_w;
885 edge_h = wdecor_edge_h;
886 }
887
888 if ((style & ui_wds_frame) != 0) {
889 rect->p0.x -= edge_w;
890 rect->p0.y -= edge_h;
891 rect->p1.x += edge_w;
892 rect->p1.y += edge_h;
893 }
894
895 if ((style & ui_wds_titlebar) != 0 && !textmode)
896 rect->p0.y -= wdecor_tbar_h;
897}
898
899/** Application area rectangle from window rectangle.
900 *
901 * Note that this needs to work just based on a UI, without having an actual
902 * window decoration, since we need it in process of resizing the window,
903 * before it is actually resized.
904 *
905 * @param style Decoration style
906 * @param rect Window decoration rectangle
907 * @param app Place to store application area rectangle
908 */
909void ui_wdecor_app_from_rect(ui_wdecor_style_t style, gfx_rect_t *rect,
910 gfx_rect_t *app)
911{
912 *app = *rect;
913
914 if ((style & ui_wds_frame) != 0) {
915 app->p0.x += wdecor_edge_w;
916 app->p0.y += wdecor_edge_h;
917 app->p1.x -= wdecor_edge_w;
918 app->p1.y -= wdecor_edge_h;
919 }
920
921 if ((style & ui_wds_titlebar) != 0)
922 app->p0.y += 22;
923}
924
925/** Get resize type for pointer at the specified position.
926 *
927 * @param wdecor Window decoration
928 * @param pos Pointer position
929 * @return Resize type
930 */
931ui_wdecor_rsztype_t ui_wdecor_get_rsztype(ui_wdecor_t *wdecor,
932 gfx_coord2_t *pos)
933{
934 bool eleft, eright;
935 bool etop, ebottom;
936 bool edge;
937 bool cleft, cright;
938 bool ctop, cbottom;
939
940 /* Window not resizable? */
941 if ((wdecor->style & ui_wds_resizable) == 0)
942 return ui_wr_none;
943
944 /* Window is maximized? */
945 if (wdecor->maximized)
946 return ui_wr_none;
947
948 /* Position not inside window? */
949 if (!gfx_pix_inside_rect(pos, &wdecor->rect))
950 return ui_wr_none;
951
952 /* Position is within edge width from the outside */
953 eleft = (pos->x < wdecor->rect.p0.x + wdecor_edge_w);
954 eright = (pos->x >= wdecor->rect.p1.x - wdecor_edge_w);
955 etop = (pos->y < wdecor->rect.p0.y + wdecor_edge_h);
956 ebottom = (pos->y >= wdecor->rect.p1.y - wdecor_edge_h);
957
958 /* Position is on one of the four edges */
959 edge = eleft || eright || etop || ebottom;
960
961 /* Position is within resize-corner distance from the outside */
962 cleft = (pos->x < wdecor->rect.p0.x + wdecor_corner_w);
963 cright = (pos->x >= wdecor->rect.p1.x - wdecor_corner_w);
964 ctop = (pos->y < wdecor->rect.p0.y + wdecor_corner_h);
965 cbottom = (pos->y >= wdecor->rect.p1.y - wdecor_corner_h);
966
967 /* Top-left corner */
968 if (edge && cleft && ctop)
969 return ui_wr_top_left;
970
971 /* Top-right corner */
972 if (edge && cright && ctop)
973 return ui_wr_top_right;
974
975 /* Bottom-left corner */
976 if (edge && cleft && cbottom)
977 return ui_wr_bottom_left;
978
979 /* Bottom-right corner */
980 if (edge && cright && cbottom)
981 return ui_wr_bottom_right;
982
983 /* Left edge */
984 if (eleft)
985 return ui_wr_left;
986
987 /* Right edge */
988 if (eright)
989 return ui_wr_right;
990
991 /* Top edge */
992 if (etop)
993 return ui_wr_top;
994
995 /* Bottom edge */
996 if (ebottom)
997 return ui_wr_bottom;
998
999 return ui_wr_none;
1000}
1001
1002/** Get stock cursor to use for the specified window resize type.
1003 *
1004 * The resize type must be valid, otherwise behavior is undefined.
1005 *
1006 * @param rsztype Resize type
1007 * @return Cursor to use for this resize type
1008 */
1009ui_stock_cursor_t ui_wdecor_cursor_from_rsztype(ui_wdecor_rsztype_t rsztype)
1010{
1011 switch (rsztype) {
1012 case ui_wr_none:
1013 return ui_curs_arrow;
1014
1015 case ui_wr_top:
1016 case ui_wr_bottom:
1017 return ui_curs_size_ud;
1018
1019 case ui_wr_left:
1020 case ui_wr_right:
1021 return ui_curs_size_lr;
1022
1023 case ui_wr_top_left:
1024 case ui_wr_bottom_right:
1025 return ui_curs_size_uldr;
1026
1027 case ui_wr_top_right:
1028 case ui_wr_bottom_left:
1029 return ui_curs_size_urdl;
1030
1031 default:
1032 assert(false);
1033 return ui_curs_arrow;
1034 }
1035}
1036
1037/** Handle window decoration keyboard event.
1038 *
1039 * @param wdecor Window decoration
1040 * @param kbd_event Keyboard event
1041 * @return @c ui_claimed iff event was claimed
1042 */
1043ui_evclaim_t ui_wdecor_kbd_event(ui_wdecor_t *wdecor, kbd_event_t *event)
1044{
1045 if (event->type == KEY_PRESS && (event->mods & (KM_CTRL | KM_ALT |
1046 KM_SHIFT)) == 0) {
1047 if (event->key == KC_F10) {
1048 ui_wdecor_sysmenu_hdl_set_active(wdecor, true);
1049 ui_wdecor_sysmenu_open(wdecor, event->kbd_id);
1050 return ui_claimed;
1051 }
1052 }
1053
1054 /* System menu handle events (if active) */
1055 if (event->type == KEY_PRESS && (event->mods & (KM_CTRL | KM_ALT |
1056 KM_SHIFT)) == 0 && wdecor->sysmenu_hdl_active) {
1057 switch (event->key) {
1058 case KC_ESCAPE:
1059 ui_wdecor_sysmenu_hdl_set_active(wdecor, false);
1060 return ui_claimed;
1061 case KC_LEFT:
1062 ui_wdecor_sysmenu_left(wdecor, event->kbd_id);
1063 return ui_claimed;
1064 case KC_RIGHT:
1065 ui_wdecor_sysmenu_right(wdecor, event->kbd_id);
1066 return ui_claimed;
1067 case KC_DOWN:
1068 ui_wdecor_sysmenu_open(wdecor, event->kbd_id);
1069 return ui_claimed;
1070 default:
1071 break;
1072 }
1073
1074 if (event->c != '\0') {
1075 /* Could be an accelerator key */
1076 ui_wdecor_sysmenu_accel(wdecor, event->c,
1077 event->kbd_id);
1078 }
1079 }
1080
1081 return ui_unclaimed;
1082}
1083
1084/** Handle window frame position event.
1085 *
1086 * @param wdecor Window decoration
1087 * @param pos_event Position event
1088 */
1089void ui_wdecor_frame_pos_event(ui_wdecor_t *wdecor, pos_event_t *event)
1090{
1091 gfx_coord2_t pos;
1092 sysarg_t pos_id;
1093 ui_wdecor_rsztype_t rsztype;
1094 ui_stock_cursor_t cursor;
1095
1096 pos.x = event->hpos;
1097 pos.y = event->vpos;
1098 pos_id = event->pos_id;
1099
1100 /* Set appropriate resizing cursor, or set arrow cursor */
1101
1102 rsztype = ui_wdecor_get_rsztype(wdecor, &pos);
1103 cursor = ui_wdecor_cursor_from_rsztype(rsztype);
1104
1105 ui_wdecor_set_cursor(wdecor, cursor);
1106
1107 /* Press on window border? */
1108 if (rsztype != ui_wr_none && event->type == POS_PRESS)
1109 ui_wdecor_resize(wdecor, rsztype, &pos, pos_id);
1110}
1111
1112/** Handle window decoration position event.
1113 *
1114 * @param wdecor Window decoration
1115 * @param pos_event Position event
1116 * @return @c ui_claimed iff event was claimed
1117 */
1118ui_evclaim_t ui_wdecor_pos_event(ui_wdecor_t *wdecor, pos_event_t *event)
1119{
1120 gfx_coord2_t pos;
1121 sysarg_t pos_id;
1122 ui_wdecor_geom_t geom;
1123 ui_evclaim_t claim;
1124
1125 pos.x = event->hpos;
1126 pos.y = event->vpos;
1127 pos_id = event->pos_id;
1128
1129 ui_wdecor_get_geom(wdecor, &geom);
1130
1131 if ((wdecor->style & ui_wds_titlebar) != 0 &&
1132 (wdecor->style & ui_wds_sysmenu_hdl) != 0) {
1133 if (event->type == POS_PRESS &&
1134 gfx_pix_inside_rect(&pos, &geom.sysmenu_hdl_rect)) {
1135 ui_wdecor_sysmenu_hdl_set_active(wdecor, true);
1136 ui_wdecor_sysmenu_open(wdecor, event->pos_id);
1137 return ui_claimed;
1138 }
1139 }
1140
1141 if (wdecor->btn_min != NULL) {
1142 claim = ui_pbutton_pos_event(wdecor->btn_min, event);
1143 if (claim == ui_claimed)
1144 return ui_claimed;
1145 }
1146
1147 if (wdecor->btn_max != NULL) {
1148 claim = ui_pbutton_pos_event(wdecor->btn_max, event);
1149 if (claim == ui_claimed)
1150 return ui_claimed;
1151 }
1152
1153 if (wdecor->btn_close != NULL) {
1154 claim = ui_pbutton_pos_event(wdecor->btn_close, event);
1155 if (claim == ui_claimed)
1156 return ui_claimed;
1157 }
1158
1159 ui_wdecor_frame_pos_event(wdecor, event);
1160
1161 if ((wdecor->style & ui_wds_titlebar) != 0 && !wdecor->maximized) {
1162 if (event->type == POS_PRESS &&
1163 gfx_pix_inside_rect(&pos, &geom.title_bar_rect)) {
1164 ui_wdecor_move(wdecor, &pos, pos_id);
1165 return ui_claimed;
1166 }
1167 }
1168
1169 return ui_unclaimed;
1170}
1171
1172/** Window decoration minimize button was clicked.
1173 *
1174 * @param pbutton Minimize button
1175 * @param arg Argument (ui_wdecor_t)
1176 */
1177static void ui_wdecor_btn_min_clicked(ui_pbutton_t *pbutton, void *arg)
1178{
1179 ui_wdecor_t *wdecor = (ui_wdecor_t *) arg;
1180
1181 (void) pbutton;
1182
1183 ui_wdecor_minimize(wdecor);
1184}
1185
1186/** Window decoration (un)maximize button was clicked.
1187 *
1188 * @param pbutton (Un)maximize button
1189 * @param arg Argument (ui_wdecor_t)
1190 */
1191static void ui_wdecor_btn_max_clicked(ui_pbutton_t *pbutton, void *arg)
1192{
1193 ui_wdecor_t *wdecor = (ui_wdecor_t *) arg;
1194
1195 (void) pbutton;
1196
1197 if (wdecor->maximized)
1198 ui_wdecor_unmaximize(wdecor);
1199 else
1200 ui_wdecor_maximize(wdecor);
1201}
1202
1203/** Paint minimize button decoration.
1204 *
1205 * @param pbutton Push button
1206 * @param arg Argument (ui_wdecor_t *)
1207 * @param pos Center position
1208 */
1209static errno_t ui_wdecor_btn_min_paint(ui_pbutton_t *pbutton,
1210 void *arg, gfx_coord2_t *pos)
1211{
1212 ui_wdecor_t *wdecor = (ui_wdecor_t *)arg;
1213 errno_t rc;
1214
1215 rc = ui_paint_minicon(wdecor->res, pos, wdecor_min_w,
1216 wdecor_min_h);
1217
1218 return rc;
1219}
1220
1221/** Paint (un)maximize button decoration.
1222 *
1223 * @param pbutton Push button
1224 * @param arg Argument (ui_wdecor_t *)
1225 * @param pos Center position
1226 */
1227static errno_t ui_wdecor_btn_max_paint(ui_pbutton_t *pbutton,
1228 void *arg, gfx_coord2_t *pos)
1229{
1230 ui_wdecor_t *wdecor = (ui_wdecor_t *)arg;
1231 errno_t rc;
1232
1233 if (wdecor->maximized) {
1234 rc = ui_paint_unmaxicon(wdecor->res, pos, wdecor_unmax_w,
1235 wdecor_unmax_h, wdecor_unmax_dw, wdecor_unmax_dh);
1236 } else {
1237 rc = ui_paint_maxicon(wdecor->res, pos, wdecor_max_w,
1238 wdecor_max_h);
1239 }
1240
1241 return rc;
1242}
1243
1244/** Window decoration close button was clicked.
1245 *
1246 * @param pbutton Close button
1247 * @param arg Argument (ui_wdecor_t)
1248 */
1249static void ui_wdecor_btn_close_clicked(ui_pbutton_t *pbutton, void *arg)
1250{
1251 ui_wdecor_t *wdecor = (ui_wdecor_t *) arg;
1252
1253 (void) pbutton;
1254 ui_wdecor_close(wdecor);
1255}
1256
1257/** Paint close button decoration.
1258 *
1259 * @param pbutton Push button
1260 * @param arg Argument (ui_wdecor_t *)
1261 * @param pos Center position
1262 */
1263static errno_t ui_wdecor_btn_close_paint(ui_pbutton_t *pbutton,
1264 void *arg, gfx_coord2_t *pos)
1265{
1266 ui_wdecor_t *wdecor = (ui_wdecor_t *)arg;
1267 gfx_coord2_t p;
1268 errno_t rc;
1269
1270 rc = gfx_set_color(wdecor->res->gc, wdecor->res->btn_text_color);
1271 if (rc != EOK)
1272 return rc;
1273
1274 p.x = pos->x - 1;
1275 p.y = pos->y - 1;
1276 return ui_paint_cross(wdecor->res->gc, &p, wdecor_close_cross_n,
1277 wdecor_close_cross_w, wdecor_close_cross_h);
1278}
1279
1280/** @}
1281 */
Note: See TracBrowser for help on using the repository browser.