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

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

Rename startmenu library to tbarcfg

There may be other aspects of task bar that will need configuring
so let's widen the scope of the library a bit.

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