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

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

System menu (WIP)

The system menu provides browsable, keyboard-accessible access to window
management functions (such as closing, minimizing, maximizing, resizing,
moving).

  • Property mode set to 100644
File size: 25.3 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 /** Close button cross leg length */
107 wdecor_close_cross_n = 5,
108 /** Close button cross pen width */
109 wdecor_close_cross_w = 2,
110 /** Close button cross pen height */
111 wdecor_close_cross_h = 1,
112 /** Minimize icon width */
113 wdecor_min_w = 10,
114 /** Minimize icon height */
115 wdecor_min_h = 10,
116 /** Maximize icon width */
117 wdecor_max_w = 10,
118 /** Maximize icon height */
119 wdecor_max_h = 10,
120 /** Unmaximize icon window width */
121 wdecor_unmax_w = 8,
122 /** Unmaximize icon window height */
123 wdecor_unmax_h = 8,
124 /** Unmaximize icon window horizontal distance */
125 wdecor_unmax_dw = 4,
126 /** Unmaximize icon window vertical distance */
127 wdecor_unmax_dh = 4
128};
129
130/** Create new window decoration.
131 *
132 * @param resource UI resource
133 * @param caption Window caption
134 * @param style Style
135 * @param rwdecor Place to store pointer to new window decoration
136 * @return EOK on success, ENOMEM if out of memory
137 */
138errno_t ui_wdecor_create(ui_resource_t *resource, const char *caption,
139 ui_wdecor_style_t style, ui_wdecor_t **rwdecor)
140{
141 ui_wdecor_t *wdecor;
142 errno_t rc;
143
144 wdecor = calloc(1, sizeof(ui_wdecor_t));
145 if (wdecor == NULL)
146 return ENOMEM;
147
148 wdecor->caption = str_dup(caption);
149 if (wdecor->caption == NULL) {
150 free(wdecor);
151 return ENOMEM;
152 }
153
154 if ((style & ui_wds_minimize_btn) != 0) {
155 rc = ui_pbutton_create(resource, "_", &wdecor->btn_min);
156 if (rc != EOK) {
157 ui_wdecor_destroy(wdecor);
158 return rc;
159 }
160
161 ui_pbutton_set_cb(wdecor->btn_min, &ui_wdecor_btn_min_cb,
162 (void *)wdecor);
163
164 ui_pbutton_set_decor_ops(wdecor->btn_min,
165 &ui_wdecor_btn_min_decor_ops, (void *)wdecor);
166 }
167
168 if ((style & ui_wds_maximize_btn) != 0) {
169 rc = ui_pbutton_create(resource, "^", &wdecor->btn_max);
170 if (rc != EOK) {
171 ui_wdecor_destroy(wdecor);
172 return rc;
173 }
174
175 ui_pbutton_set_cb(wdecor->btn_max, &ui_wdecor_btn_max_cb,
176 (void *)wdecor);
177
178 ui_pbutton_set_decor_ops(wdecor->btn_max,
179 &ui_wdecor_btn_max_decor_ops, (void *)wdecor);
180 }
181
182 if ((style & ui_wds_close_btn) != 0) {
183 rc = ui_pbutton_create(resource, "X", &wdecor->btn_close);
184 if (rc != EOK) {
185 ui_wdecor_destroy(wdecor);
186 return rc;
187 }
188
189 ui_pbutton_set_cb(wdecor->btn_close, &ui_wdecor_btn_close_cb,
190 (void *)wdecor);
191
192 ui_pbutton_set_decor_ops(wdecor->btn_close,
193 &ui_wdecor_btn_close_decor_ops, (void *)wdecor);
194 }
195
196 wdecor->res = resource;
197 wdecor->active = true;
198 wdecor->style = style;
199 *rwdecor = wdecor;
200 return EOK;
201}
202
203/** Destroy window decoration.
204 *
205 * @param wdecor Window decoration or @c NULL
206 */
207void ui_wdecor_destroy(ui_wdecor_t *wdecor)
208{
209 if (wdecor == NULL)
210 return;
211
212 ui_pbutton_destroy(wdecor->btn_min);
213 ui_pbutton_destroy(wdecor->btn_max);
214 ui_pbutton_destroy(wdecor->btn_close);
215 free(wdecor->caption);
216 free(wdecor);
217}
218
219/** Set window decoration callbacks.
220 *
221 * @param wdecor Window decoration
222 * @param cb Window decoration callbacks
223 * @param arg Callback argument
224 */
225void ui_wdecor_set_cb(ui_wdecor_t *wdecor, ui_wdecor_cb_t *cb, void *arg)
226{
227 wdecor->cb = cb;
228 wdecor->arg = arg;
229}
230
231/** Set window decoration rectangle.
232 *
233 * @param wdecor Window decoration
234 * @param rect New window decoration rectangle
235 */
236void ui_wdecor_set_rect(ui_wdecor_t *wdecor, gfx_rect_t *rect)
237{
238 ui_wdecor_geom_t geom;
239
240 wdecor->rect = *rect;
241
242 ui_wdecor_get_geom(wdecor, &geom);
243
244 if (wdecor->btn_min != NULL)
245 ui_pbutton_set_rect(wdecor->btn_min, &geom.btn_min_rect);
246 if (wdecor->btn_max != NULL)
247 ui_pbutton_set_rect(wdecor->btn_max, &geom.btn_max_rect);
248 if (wdecor->btn_close != NULL)
249 ui_pbutton_set_rect(wdecor->btn_close, &geom.btn_close_rect);
250}
251
252/** Set active flag.
253 *
254 * Active window is the one receiving keyboard events.
255 *
256 * @param wdecor Window decoration
257 * @param active @c true iff window is active
258 */
259void ui_wdecor_set_active(ui_wdecor_t *wdecor, bool active)
260{
261 wdecor->active = active;
262}
263
264/** Set maximized flag.
265 *
266 * Active window is the one receiving keyboard events.
267 *
268 * @param wdecor Window decoration
269 * @param maximized @c true iff window is maximized
270 */
271void ui_wdecor_set_maximized(ui_wdecor_t *wdecor, bool maximized)
272{
273 wdecor->maximized = maximized;
274}
275
276/** Change caption.
277 *
278 * @param wdecor Window decoration
279 * @param caption New caption
280 *
281 * @return EOK on success or an error code
282 */
283errno_t ui_wdecor_set_caption(ui_wdecor_t *wdecor, const char *caption)
284{
285 char *cdup;
286
287 cdup = str_dup(caption);
288 if (cdup == NULL)
289 return ENOMEM;
290
291 free(wdecor->caption);
292 wdecor->caption = cdup;
293
294 ui_wdecor_paint(wdecor);
295 return EOK;
296}
297
298/** Paint window decoration.
299 *
300 * @param wdecor Window decoration
301 * @return EOK on success or an error code
302 */
303errno_t ui_wdecor_paint(ui_wdecor_t *wdecor)
304{
305 errno_t rc;
306 gfx_rect_t rect;
307 gfx_rect_t trect;
308 gfx_rect_t text_rect;
309 gfx_text_fmt_t fmt;
310 gfx_coord2_t pos;
311 ui_wdecor_geom_t geom;
312
313 rect = wdecor->rect;
314 ui_wdecor_get_geom(wdecor, &geom);
315
316 if ((wdecor->style & ui_wds_frame) != 0) {
317
318 if (wdecor->res->textmode != false) {
319 rc = ui_paint_text_box(wdecor->res, &rect,
320 ui_box_double, wdecor->res->wnd_face_color);
321 if (rc != EOK)
322 return rc;
323 } else {
324 rc = ui_paint_outset_frame(wdecor->res, &rect,
325 &rect);
326 if (rc != EOK)
327 return rc;
328
329 rc = ui_paint_bevel(wdecor->res->gc, &rect,
330 wdecor->res->wnd_face_color,
331 wdecor->res->wnd_face_color, 2, &rect);
332 if (rc != EOK)
333 return rc;
334 }
335 }
336
337 if ((wdecor->style & ui_wds_titlebar) != 0) {
338 trect = geom.title_bar_rect;
339
340 if (wdecor->res->textmode == false) {
341 rc = ui_paint_bevel(wdecor->res->gc, &trect,
342 wdecor->res->wnd_shadow_color,
343 wdecor->res->wnd_highlight_color, 1, &trect);
344 if (rc != EOK)
345 return rc;
346
347 rc = gfx_set_color(wdecor->res->gc, wdecor->active ?
348 wdecor->res->tbar_act_bg_color :
349 wdecor->res->tbar_inact_bg_color);
350 if (rc != EOK)
351 return rc;
352
353 rc = gfx_fill_rect(wdecor->res->gc, &trect);
354 if (rc != EOK)
355 return rc;
356 }
357
358 gfx_text_fmt_init(&fmt);
359 fmt.font = wdecor->res->font;
360 fmt.color = wdecor->active ?
361 wdecor->res->tbar_act_text_color :
362 wdecor->res->tbar_inact_text_color;
363 fmt.halign = gfx_halign_center;
364 fmt.valign = gfx_valign_center;
365 fmt.abbreviate = true;
366 fmt.width = geom.caption_rect.p1.x -
367 geom.caption_rect.p0.x;
368
369 pos.x = (geom.caption_rect.p0.x + geom.caption_rect.p1.x) / 2;
370 pos.y = (geom.caption_rect.p0.y + geom.caption_rect.p1.y) / 2;
371
372 if (wdecor->res->textmode) {
373 /* Make space around caption text */
374 gfx_text_rect(&pos, &fmt, wdecor->caption, &text_rect);
375
376 /* Only make space if caption is non-empty */
377 if (text_rect.p0.x < text_rect.p1.x) {
378 text_rect.p0.x -= 1;
379 text_rect.p1.x += 1;
380 }
381
382 rc = gfx_set_color(wdecor->res->gc, wdecor->active ?
383 wdecor->res->tbar_act_bg_color :
384 wdecor->res->tbar_inact_bg_color);
385 if (rc != EOK)
386 return rc;
387
388 rc = gfx_fill_rect(wdecor->res->gc, &text_rect);
389 if (rc != EOK)
390 return rc;
391 }
392
393 rc = gfx_puttext(&pos, &fmt, wdecor->caption);
394 if (rc != EOK)
395 return rc;
396
397 if (wdecor->btn_min != NULL) {
398 rc = ui_pbutton_paint(wdecor->btn_min);
399 if (rc != EOK)
400 return rc;
401 }
402
403 if (wdecor->btn_max != NULL) {
404 rc = ui_pbutton_paint(wdecor->btn_max);
405 if (rc != EOK)
406 return rc;
407 }
408
409 if (wdecor->btn_close != NULL) {
410 rc = ui_pbutton_paint(wdecor->btn_close);
411 if (rc != EOK)
412 return rc;
413 }
414 }
415
416 rc = gfx_update(wdecor->res->gc);
417 if (rc != EOK)
418 return rc;
419
420 return EOK;
421}
422
423/** Send decoration sysmenu event.
424 *
425 * @param wdecor Window decoration
426 * @param idev_id Input device ID
427 */
428void ui_wdecor_sysmenu(ui_wdecor_t *wdecor, sysarg_t idev_id)
429{
430 if (wdecor->cb != NULL && wdecor->cb->sysmenu != NULL)
431 wdecor->cb->sysmenu(wdecor, wdecor->arg, idev_id);
432}
433
434/** Send decoration minimize event.
435 *
436 * @param wdecor Window decoration
437 */
438void ui_wdecor_minimize(ui_wdecor_t *wdecor)
439{
440 if (wdecor->cb != NULL && wdecor->cb->minimize != NULL)
441 wdecor->cb->minimize(wdecor, wdecor->arg);
442}
443
444/** Send decoration maximize event.
445 *
446 * @param wdecor Window decoration
447 */
448void ui_wdecor_maximize(ui_wdecor_t *wdecor)
449{
450 if (wdecor->cb != NULL && wdecor->cb->maximize != NULL)
451 wdecor->cb->maximize(wdecor, wdecor->arg);
452}
453
454/** Send decoration unmaximize event.
455 *
456 * @param wdecor Window decoration
457 */
458void ui_wdecor_unmaximize(ui_wdecor_t *wdecor)
459{
460 if (wdecor->cb != NULL && wdecor->cb->unmaximize != NULL)
461 wdecor->cb->unmaximize(wdecor, wdecor->arg);
462}
463
464/** Send decoration close event.
465 *
466 * @param wdecor Window decoration
467 */
468void ui_wdecor_close(ui_wdecor_t *wdecor)
469{
470 if (wdecor->cb != NULL && wdecor->cb->close != NULL)
471 wdecor->cb->close(wdecor, wdecor->arg);
472}
473
474/** Send decoration move event.
475 *
476 * @param wdecor Window decoration
477 * @param pos Position where the title bar was pressed
478 * @param pos_id Positioning device ID
479 */
480void ui_wdecor_move(ui_wdecor_t *wdecor, gfx_coord2_t *pos, sysarg_t pos_id)
481{
482 if (wdecor->cb != NULL && wdecor->cb->move != NULL)
483 wdecor->cb->move(wdecor, wdecor->arg, pos, pos_id);
484}
485
486/** Send decoration resize event.
487 *
488 * @param wdecor Window decoration
489 * @param rsztype Resize type
490 * @param pos Position where the button was pressed
491 * @param pos_id Positioning device ID
492 */
493void ui_wdecor_resize(ui_wdecor_t *wdecor, ui_wdecor_rsztype_t rsztype,
494 gfx_coord2_t *pos, sysarg_t pos_id)
495{
496 if (wdecor->cb != NULL && wdecor->cb->resize != NULL)
497 wdecor->cb->resize(wdecor, wdecor->arg, rsztype, pos, pos_id);
498}
499
500/** Send cursor change event.
501 *
502 * @param wdecor Window decoration
503 * @param cursor Cursor
504 */
505void ui_wdecor_set_cursor(ui_wdecor_t *wdecor, ui_stock_cursor_t cursor)
506{
507 if (wdecor->cb != NULL && wdecor->cb->set_cursor != NULL)
508 wdecor->cb->set_cursor(wdecor, wdecor->arg, cursor);
509}
510
511/** Get window decoration geometry.
512 *
513 * @param wdecor Window decoration
514 * @param geom Structure to fill in with computed geometry
515 */
516void ui_wdecor_get_geom(ui_wdecor_t *wdecor, ui_wdecor_geom_t *geom)
517{
518 gfx_coord_t frame_w;
519 gfx_coord_t btn_x;
520 gfx_coord_t btn_y;
521 gfx_coord_t cap_hmargin;
522
523 /* Does window have a frame? */
524 if ((wdecor->style & ui_wds_frame) != 0) {
525 frame_w = wdecor->res->textmode ?
526 wdecor_frame_w_text : wdecor_frame_w;
527
528 geom->interior_rect.p0.x = wdecor->rect.p0.x + frame_w;
529 geom->interior_rect.p0.y = wdecor->rect.p0.y + frame_w;
530 geom->interior_rect.p1.x = wdecor->rect.p1.x - frame_w;
531 geom->interior_rect.p1.y = wdecor->rect.p1.y - frame_w;
532 } else {
533 geom->interior_rect = wdecor->rect;
534 }
535
536 /* Does window have a title bar? */
537 if ((wdecor->style & ui_wds_titlebar) != 0) {
538 if (wdecor->res->textmode) {
539 geom->title_bar_rect.p0 = wdecor->rect.p0;
540 geom->title_bar_rect.p1.x = wdecor->rect.p1.x;
541 geom->title_bar_rect.p1.y = wdecor->rect.p0.y + 1;
542
543 btn_x = geom->title_bar_rect.p1.x - 1;
544 btn_y = geom->title_bar_rect.p0.y;
545 } else {
546 geom->title_bar_rect.p0 = geom->interior_rect.p0;
547 geom->title_bar_rect.p1.x = geom->interior_rect.p1.x;
548 geom->title_bar_rect.p1.y = geom->interior_rect.p0.y +
549 wdecor_tbar_h;
550
551 btn_x = geom->title_bar_rect.p1.x - 1;
552 btn_y = geom->title_bar_rect.p0.y + 1;
553 }
554
555 geom->app_area_rect.p0.x = geom->interior_rect.p0.x;
556 geom->app_area_rect.p0.y = geom->title_bar_rect.p1.y;
557 geom->app_area_rect.p1 = geom->interior_rect.p1;
558
559 } else {
560 geom->title_bar_rect.p0.x = 0;
561 geom->title_bar_rect.p0.y = 0;
562 geom->title_bar_rect.p1.x = 0;
563 geom->title_bar_rect.p1.y = 0;
564
565 geom->app_area_rect = geom->interior_rect;
566 btn_x = 0;
567 btn_y = 0;
568 }
569
570 /* Does window have a close button? */
571 if ((wdecor->style & ui_wds_close_btn) != 0) {
572 if (wdecor->res->textmode == false) {
573 geom->btn_close_rect.p0.x = btn_x - 20;
574 geom->btn_close_rect.p0.y = btn_y;
575 geom->btn_close_rect.p1.x = btn_x;
576 geom->btn_close_rect.p1.y = btn_y + 20;
577
578 btn_x -= 20;
579 } else {
580 geom->btn_close_rect.p0.x = btn_x - 3;
581 geom->btn_close_rect.p0.y = btn_y;
582 geom->btn_close_rect.p1.x = btn_x;
583 geom->btn_close_rect.p1.y = btn_y + 1;
584
585 btn_x -= 3;
586 }
587 } else {
588 geom->btn_close_rect.p0.x = 0;
589 geom->btn_close_rect.p0.y = 0;
590 geom->btn_close_rect.p1.x = 0;
591 geom->btn_close_rect.p1.y = 0;
592 }
593
594 /* Does window have a (un)maximize button? */
595 if ((wdecor->style & ui_wds_maximize_btn) != 0) {
596 if (wdecor->res->textmode == false) {
597 geom->btn_max_rect.p0.x = btn_x - 20;
598 geom->btn_max_rect.p0.y = btn_y;
599 geom->btn_max_rect.p1.x = btn_x;
600 geom->btn_max_rect.p1.y = btn_y + 20;
601
602 btn_x -= 20;
603 } else {
604 geom->btn_max_rect.p0.x = btn_x - 3;
605 geom->btn_max_rect.p0.y = btn_y;
606 geom->btn_max_rect.p1.x = btn_x;
607 geom->btn_max_rect.p1.y = btn_y + 1;
608
609 btn_x -= 3;
610 }
611 } else {
612 geom->btn_max_rect.p0.x = 0;
613 geom->btn_max_rect.p0.y = 0;
614 geom->btn_max_rect.p1.x = 0;
615 geom->btn_max_rect.p1.y = 0;
616 }
617
618 /* Does window have a minimize button? */
619 if ((wdecor->style & ui_wds_minimize_btn) != 0) {
620 if (wdecor->res->textmode == false) {
621 geom->btn_min_rect.p0.x = btn_x - 20;
622 geom->btn_min_rect.p0.y = btn_y;
623 geom->btn_min_rect.p1.x = btn_x;
624 geom->btn_min_rect.p1.y = btn_y + 20;
625
626 btn_x -= 20;
627 } else {
628 geom->btn_min_rect.p0.x = btn_x - 3;
629 geom->btn_min_rect.p0.y = btn_y;
630 geom->btn_min_rect.p1.x = btn_x;
631 geom->btn_min_rect.p1.y = btn_y + 1;
632
633 btn_x -= 3;
634 }
635 } else {
636 geom->btn_min_rect.p0.x = 0;
637 geom->btn_min_rect.p0.y = 0;
638 geom->btn_min_rect.p1.x = 0;
639 geom->btn_min_rect.p1.y = 0;
640 }
641
642 if (wdecor->res->textmode == false)
643 cap_hmargin = wdecor_cap_hmargin;
644 else
645 cap_hmargin = wdecor_cap_hmargin_text;
646
647 geom->caption_rect.p0.x = geom->title_bar_rect.p0.x +
648 cap_hmargin;
649 geom->caption_rect.p0.y = geom->title_bar_rect.p0.y;
650 geom->caption_rect.p1.x = btn_x - cap_hmargin;
651 geom->caption_rect.p1.y = geom->title_bar_rect.p1.y;
652}
653
654/** Get outer rectangle from application area rectangle.
655 *
656 * Note that this needs to work just based on a UI, without having an actual
657 * window decoration, since we need it in order to create the window
658 * and its decoration.
659 *
660 * @param style Decoration style
661 * @param app Application area rectangle
662 * @param rect Place to store (outer) window decoration rectangle
663 */
664void ui_wdecor_rect_from_app(ui_wdecor_style_t style, gfx_rect_t *app,
665 gfx_rect_t *rect)
666{
667 *rect = *app;
668
669 if ((style & ui_wds_frame) != 0) {
670 rect->p0.x -= wdecor_edge_w;
671 rect->p0.y -= wdecor_edge_h;
672 rect->p1.x += wdecor_edge_w;
673 rect->p1.y += wdecor_edge_h;
674 }
675
676 if ((style & ui_wds_titlebar) != 0)
677 rect->p0.y -= 22;
678}
679
680/** Application area rectangle from window rectangle.
681 *
682 * Note that this needs to work just based on a UI, without having an actual
683 * window decoration, since we need it in process of resizing the window,
684 * before it is actually resized.
685 *
686 * @param style Decoration style
687 * @param rect Window decoration rectangle
688 * @param app Place to store application area rectangle
689 */
690void ui_wdecor_app_from_rect(ui_wdecor_style_t style, gfx_rect_t *rect,
691 gfx_rect_t *app)
692{
693 *app = *rect;
694
695 if ((style & ui_wds_frame) != 0) {
696 app->p0.x += wdecor_edge_w;
697 app->p0.y += wdecor_edge_h;
698 app->p1.x -= wdecor_edge_w;
699 app->p1.y -= wdecor_edge_h;
700 }
701
702 if ((style & ui_wds_titlebar) != 0)
703 app->p0.y += 22;
704}
705
706/** Get resize type for pointer at the specified position.
707 *
708 * @param wdecor Window decoration
709 * @param pos Pointer position
710 * @return Resize type
711 */
712ui_wdecor_rsztype_t ui_wdecor_get_rsztype(ui_wdecor_t *wdecor,
713 gfx_coord2_t *pos)
714{
715 bool eleft, eright;
716 bool etop, ebottom;
717 bool edge;
718 bool cleft, cright;
719 bool ctop, cbottom;
720
721 /* Window not resizable? */
722 if ((wdecor->style & ui_wds_resizable) == 0)
723 return ui_wr_none;
724
725 /* Window is maximized? */
726 if (wdecor->maximized)
727 return ui_wr_none;
728
729 /* Position not inside window? */
730 if (!gfx_pix_inside_rect(pos, &wdecor->rect))
731 return ui_wr_none;
732
733 /* Position is within edge width from the outside */
734 eleft = (pos->x < wdecor->rect.p0.x + wdecor_edge_w);
735 eright = (pos->x >= wdecor->rect.p1.x - wdecor_edge_w);
736 etop = (pos->y < wdecor->rect.p0.y + wdecor_edge_h);
737 ebottom = (pos->y >= wdecor->rect.p1.y - wdecor_edge_h);
738
739 /* Position is on one of the four edges */
740 edge = eleft || eright || etop || ebottom;
741
742 /* Position is within resize-corner distance from the outside */
743 cleft = (pos->x < wdecor->rect.p0.x + wdecor_corner_w);
744 cright = (pos->x >= wdecor->rect.p1.x - wdecor_corner_w);
745 ctop = (pos->y < wdecor->rect.p0.y + wdecor_corner_h);
746 cbottom = (pos->y >= wdecor->rect.p1.y - wdecor_corner_h);
747
748 /* Top-left corner */
749 if (edge && cleft && ctop)
750 return ui_wr_top_left;
751
752 /* Top-right corner */
753 if (edge && cright && ctop)
754 return ui_wr_top_right;
755
756 /* Bottom-left corner */
757 if (edge && cleft && cbottom)
758 return ui_wr_bottom_left;
759
760 /* Bottom-right corner */
761 if (edge && cright && cbottom)
762 return ui_wr_bottom_right;
763
764 /* Left edge */
765 if (eleft)
766 return ui_wr_left;
767
768 /* Right edge */
769 if (eright)
770 return ui_wr_right;
771
772 /* Top edge */
773 if (etop)
774 return ui_wr_top;
775
776 /* Bottom edge */
777 if (ebottom)
778 return ui_wr_bottom;
779
780 return ui_wr_none;
781}
782
783/** Get stock cursor to use for the specified window resize type.
784 *
785 * The resize type must be valid, otherwise behavior is undefined.
786 *
787 * @param rsztype Resize type
788 * @return Cursor to use for this resize type
789 */
790ui_stock_cursor_t ui_wdecor_cursor_from_rsztype(ui_wdecor_rsztype_t rsztype)
791{
792 switch (rsztype) {
793 case ui_wr_none:
794 return ui_curs_arrow;
795
796 case ui_wr_top:
797 case ui_wr_bottom:
798 return ui_curs_size_ud;
799
800 case ui_wr_left:
801 case ui_wr_right:
802 return ui_curs_size_lr;
803
804 case ui_wr_top_left:
805 case ui_wr_bottom_right:
806 return ui_curs_size_uldr;
807
808 case ui_wr_top_right:
809 case ui_wr_bottom_left:
810 return ui_curs_size_urdl;
811
812 default:
813 assert(false);
814 return ui_curs_arrow;
815 }
816}
817
818/** Handle window decoration keyboard event.
819 *
820 * @param wdecor Window decoration
821 * @param kbd_event Keyboard event
822 * @return @c ui_claimed iff event was claimed
823 */
824ui_evclaim_t ui_wdecor_kbd_event(ui_wdecor_t *wdecor, kbd_event_t *event)
825{
826 (void)wdecor;
827
828 if (event->type == KEY_PRESS && (event->mods & (KM_CTRL | KM_ALT |
829 KM_SHIFT)) == 0) {
830 if (event->key == KC_F9)
831 ui_wdecor_sysmenu(wdecor, event->kbd_id);
832 }
833
834 return ui_unclaimed;
835}
836
837/** Handle window frame position event.
838 *
839 * @param wdecor Window decoration
840 * @param pos_event Position event
841 */
842void ui_wdecor_frame_pos_event(ui_wdecor_t *wdecor, pos_event_t *event)
843{
844 gfx_coord2_t pos;
845 sysarg_t pos_id;
846 ui_wdecor_rsztype_t rsztype;
847 ui_stock_cursor_t cursor;
848
849 pos.x = event->hpos;
850 pos.y = event->vpos;
851 pos_id = event->pos_id;
852
853 /* Set appropriate resizing cursor, or set arrow cursor */
854
855 rsztype = ui_wdecor_get_rsztype(wdecor, &pos);
856 cursor = ui_wdecor_cursor_from_rsztype(rsztype);
857
858 ui_wdecor_set_cursor(wdecor, cursor);
859
860 /* Press on window border? */
861 if (rsztype != ui_wr_none && event->type == POS_PRESS)
862 ui_wdecor_resize(wdecor, rsztype, &pos, pos_id);
863}
864
865/** Handle window decoration position event.
866 *
867 * @param wdecor Window decoration
868 * @param pos_event Position event
869 * @return @c ui_claimed iff event was claimed
870 */
871ui_evclaim_t ui_wdecor_pos_event(ui_wdecor_t *wdecor, pos_event_t *event)
872{
873 gfx_coord2_t pos;
874 sysarg_t pos_id;
875 ui_wdecor_geom_t geom;
876 ui_evclaim_t claim;
877
878 pos.x = event->hpos;
879 pos.y = event->vpos;
880 pos_id = event->pos_id;
881
882 ui_wdecor_get_geom(wdecor, &geom);
883
884 if (wdecor->btn_min != NULL) {
885 claim = ui_pbutton_pos_event(wdecor->btn_min, event);
886 if (claim == ui_claimed)
887 return ui_claimed;
888 }
889
890 if (wdecor->btn_max != NULL) {
891 claim = ui_pbutton_pos_event(wdecor->btn_max, event);
892 if (claim == ui_claimed)
893 return ui_claimed;
894 }
895
896 if (wdecor->btn_close != NULL) {
897 claim = ui_pbutton_pos_event(wdecor->btn_close, event);
898 if (claim == ui_claimed)
899 return ui_claimed;
900 }
901
902 ui_wdecor_frame_pos_event(wdecor, event);
903
904 if ((wdecor->style & ui_wds_titlebar) != 0 && !wdecor->maximized) {
905 if (event->type == POS_PRESS &&
906 gfx_pix_inside_rect(&pos, &geom.title_bar_rect)) {
907 ui_wdecor_move(wdecor, &pos, pos_id);
908 return ui_claimed;
909 }
910 }
911
912 return ui_unclaimed;
913}
914
915/** Window decoration minimize button was clicked.
916 *
917 * @param pbutton Minimize button
918 * @param arg Argument (ui_wdecor_t)
919 */
920static void ui_wdecor_btn_min_clicked(ui_pbutton_t *pbutton, void *arg)
921{
922 ui_wdecor_t *wdecor = (ui_wdecor_t *) arg;
923
924 (void) pbutton;
925
926 ui_wdecor_minimize(wdecor);
927}
928
929/** Window decoration (un)maximize button was clicked.
930 *
931 * @param pbutton (Un)maximize button
932 * @param arg Argument (ui_wdecor_t)
933 */
934static void ui_wdecor_btn_max_clicked(ui_pbutton_t *pbutton, void *arg)
935{
936 ui_wdecor_t *wdecor = (ui_wdecor_t *) arg;
937
938 (void) pbutton;
939
940 if (wdecor->maximized)
941 ui_wdecor_unmaximize(wdecor);
942 else
943 ui_wdecor_maximize(wdecor);
944}
945
946/** Paint minimize button decoration.
947 *
948 * @param pbutton Push button
949 * @param arg Argument (ui_wdecor_t *)
950 * @param pos Center position
951 */
952static errno_t ui_wdecor_btn_min_paint(ui_pbutton_t *pbutton,
953 void *arg, gfx_coord2_t *pos)
954{
955 ui_wdecor_t *wdecor = (ui_wdecor_t *)arg;
956 errno_t rc;
957
958 rc = ui_paint_minicon(wdecor->res, pos, wdecor_min_w,
959 wdecor_min_h);
960
961 return rc;
962}
963
964/** Paint (un)maximize button decoration.
965 *
966 * @param pbutton Push button
967 * @param arg Argument (ui_wdecor_t *)
968 * @param pos Center position
969 */
970static errno_t ui_wdecor_btn_max_paint(ui_pbutton_t *pbutton,
971 void *arg, gfx_coord2_t *pos)
972{
973 ui_wdecor_t *wdecor = (ui_wdecor_t *)arg;
974 errno_t rc;
975
976 if (wdecor->maximized) {
977 rc = ui_paint_unmaxicon(wdecor->res, pos, wdecor_unmax_w,
978 wdecor_unmax_h, wdecor_unmax_dw, wdecor_unmax_dh);
979 } else {
980 rc = ui_paint_maxicon(wdecor->res, pos, wdecor_max_w,
981 wdecor_max_h);
982 }
983
984 return rc;
985}
986
987/** Window decoration close button was clicked.
988 *
989 * @param pbutton Close button
990 * @param arg Argument (ui_wdecor_t)
991 */
992static void ui_wdecor_btn_close_clicked(ui_pbutton_t *pbutton, void *arg)
993{
994 ui_wdecor_t *wdecor = (ui_wdecor_t *) arg;
995
996 (void) pbutton;
997 ui_wdecor_close(wdecor);
998}
999
1000/** Paint close button decoration.
1001 *
1002 * @param pbutton Push button
1003 * @param arg Argument (ui_wdecor_t *)
1004 * @param pos Center position
1005 */
1006static errno_t ui_wdecor_btn_close_paint(ui_pbutton_t *pbutton,
1007 void *arg, gfx_coord2_t *pos)
1008{
1009 ui_wdecor_t *wdecor = (ui_wdecor_t *)arg;
1010 gfx_coord2_t p;
1011 errno_t rc;
1012
1013 rc = gfx_set_color(wdecor->res->gc, wdecor->res->btn_text_color);
1014 if (rc != EOK)
1015 return rc;
1016
1017 p.x = pos->x - 1;
1018 p.y = pos->y - 1;
1019 return ui_paint_cross(wdecor->res->gc, &p, wdecor_close_cross_n,
1020 wdecor_close_cross_w, wdecor_close_cross_h);
1021}
1022
1023/** @}
1024 */
Note: See TracBrowser for help on using the repository browser.