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

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

Client-side UI rendering

It is possible to turn on and off and if turned on, one can also
enable or disable window double buffering (currently both options
are build-time).

  • Property mode set to 100644
File size: 14.2 KB
Line 
1/*
2 * Copyright (c) 2021 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_clicked(ui_pbutton_t *, void *);
52
53static ui_pbutton_cb_t ui_wdecor_btn_close_cb = {
54 .clicked = ui_wdecor_btn_clicked
55};
56
57enum {
58 wdecor_corner_w = 24,
59 wdecor_corner_h = 24,
60 wdecor_edge_w = 4,
61 wdecor_edge_h = 4
62};
63
64/** Create new window decoration.
65 *
66 * @param resource UI resource
67 * @param caption Window caption
68 * @param style Style
69 * @param rwdecor Place to store pointer to new window decoration
70 * @return EOK on success, ENOMEM if out of memory
71 */
72errno_t ui_wdecor_create(ui_resource_t *resource, const char *caption,
73 ui_wdecor_style_t style, ui_wdecor_t **rwdecor)
74{
75 ui_wdecor_t *wdecor;
76 errno_t rc;
77
78 wdecor = calloc(1, sizeof(ui_wdecor_t));
79 if (wdecor == NULL)
80 return ENOMEM;
81
82 wdecor->caption = str_dup(caption);
83 if (wdecor->caption == NULL) {
84 free(wdecor);
85 return ENOMEM;
86 }
87
88 rc = ui_pbutton_create(resource, "X", &wdecor->btn_close);
89 if (rc != EOK) {
90 free(wdecor->caption);
91 free(wdecor);
92 return rc;
93 }
94
95 ui_pbutton_set_cb(wdecor->btn_close, &ui_wdecor_btn_close_cb,
96 (void *)wdecor);
97
98 wdecor->res = resource;
99 wdecor->active = true;
100 wdecor->style = style;
101 *rwdecor = wdecor;
102 return EOK;
103}
104
105/** Destroy window decoration.
106 *
107 * @param wdecor Window decoration or @c NULL
108 */
109void ui_wdecor_destroy(ui_wdecor_t *wdecor)
110{
111 if (wdecor == NULL)
112 return;
113
114 ui_pbutton_destroy(wdecor->btn_close);
115 free(wdecor->caption);
116 free(wdecor);
117}
118
119/** Set window decoration callbacks.
120 *
121 * @param wdecor Window decoration
122 * @param cb Window decoration callbacks
123 * @param arg Callback argument
124 */
125void ui_wdecor_set_cb(ui_wdecor_t *wdecor, ui_wdecor_cb_t *cb, void *arg)
126{
127 wdecor->cb = cb;
128 wdecor->arg = arg;
129}
130
131/** Set window decoration rectangle.
132 *
133 * @param wdecor Window decoration
134 * @param rect New window decoration rectangle
135 */
136void ui_wdecor_set_rect(ui_wdecor_t *wdecor, gfx_rect_t *rect)
137{
138 ui_wdecor_geom_t geom;
139
140 wdecor->rect = *rect;
141
142 ui_wdecor_get_geom(wdecor, &geom);
143 ui_pbutton_set_rect(wdecor->btn_close, &geom.btn_close_rect);
144}
145
146/** Set active flag.
147 *
148 * Active window is the one receiving keyboard events.
149 *
150 * @param wdecor Window decoration
151 * @param active @c true iff window is active
152 */
153void ui_wdecor_set_active(ui_wdecor_t *wdecor, bool active)
154{
155 wdecor->active = active;
156}
157
158/** Paint window decoration.
159 *
160 * @param wdecor Window decoration
161 * @return EOK on success or an error code
162 */
163errno_t ui_wdecor_paint(ui_wdecor_t *wdecor)
164{
165 errno_t rc;
166 gfx_rect_t rect;
167 gfx_rect_t trect;
168 gfx_text_fmt_t fmt;
169 gfx_coord2_t pos;
170 ui_wdecor_geom_t geom;
171
172 rect = wdecor->rect;
173 ui_wdecor_get_geom(wdecor, &geom);
174
175 if ((wdecor->style & ui_wds_frame) != 0) {
176 rc = ui_paint_bevel(wdecor->res->gc, &rect,
177 wdecor->res->wnd_frame_hi_color,
178 wdecor->res->wnd_frame_sh_color, 1, &rect);
179 if (rc != EOK)
180 return rc;
181
182 rc = ui_paint_bevel(wdecor->res->gc, &rect,
183 wdecor->res->wnd_highlight_color,
184 wdecor->res->wnd_shadow_color, 1, &rect);
185 if (rc != EOK)
186 return rc;
187
188 rc = ui_paint_bevel(wdecor->res->gc, &rect,
189 wdecor->res->wnd_face_color,
190 wdecor->res->wnd_face_color, 2, &rect);
191 if (rc != EOK)
192 return rc;
193 }
194
195 if ((wdecor->style & ui_wds_titlebar) != 0) {
196 trect = geom.title_bar_rect;
197
198 rc = ui_paint_bevel(wdecor->res->gc, &trect,
199 wdecor->res->wnd_shadow_color,
200 wdecor->res->wnd_highlight_color, 1, &trect);
201 if (rc != EOK)
202 return rc;
203
204 rc = gfx_set_color(wdecor->res->gc, wdecor->active ?
205 wdecor->res->tbar_act_bg_color :
206 wdecor->res->tbar_inact_bg_color);
207 if (rc != EOK)
208 return rc;
209
210 rc = gfx_fill_rect(wdecor->res->gc, &trect);
211 if (rc != EOK)
212 return rc;
213
214 gfx_text_fmt_init(&fmt);
215 fmt.halign = gfx_halign_center;
216 fmt.valign = gfx_valign_center;
217
218 pos.x = (trect.p0.x + trect.p1.x) / 2;
219 pos.y = (trect.p0.y + trect.p1.y) / 2;
220
221 rc = gfx_set_color(wdecor->res->gc, wdecor->active ?
222 wdecor->res->tbar_act_text_color :
223 wdecor->res->tbar_inact_text_color);
224 if (rc != EOK)
225 return rc;
226
227 rc = gfx_puttext(wdecor->res->font, &pos, &fmt, wdecor->caption);
228 if (rc != EOK)
229 return rc;
230
231 if ((wdecor->style & ui_wds_close_btn) != 0) {
232 rc = ui_pbutton_paint(wdecor->btn_close);
233 if (rc != EOK)
234 return rc;
235 }
236 }
237
238 rc = gfx_update(wdecor->res->gc);
239 if (rc != EOK)
240 return rc;
241
242 return EOK;
243}
244
245/** Send decoration close event.
246 *
247 * @param wdecor Window decoration
248 */
249void ui_wdecor_close(ui_wdecor_t *wdecor)
250{
251 if (wdecor->cb != NULL && wdecor->cb->close != NULL)
252 wdecor->cb->close(wdecor, wdecor->arg);
253}
254
255/** Send decoration move event.
256 *
257 * @param wdecor Window decoration
258 * @param pos Position where the title bar was pressed
259 */
260void ui_wdecor_move(ui_wdecor_t *wdecor, gfx_coord2_t *pos)
261{
262 if (wdecor->cb != NULL && wdecor->cb->move != NULL)
263 wdecor->cb->move(wdecor, wdecor->arg, pos);
264}
265
266/** Send decoration resize event.
267 *
268 * @param wdecor Window decoration
269 * @param rsztype Resize type
270 * @param pos Position where the button was pressed
271 */
272void ui_wdecor_resize(ui_wdecor_t *wdecor, ui_wdecor_rsztype_t rsztype,
273 gfx_coord2_t *pos)
274{
275 if (wdecor->cb != NULL && wdecor->cb->resize != NULL)
276 wdecor->cb->resize(wdecor, wdecor->arg, rsztype, pos);
277}
278
279/** Send cursor change event.
280 *
281 * @param wdecor Window decoration
282 * @param cursor Cursor
283 */
284void ui_wdecor_set_cursor(ui_wdecor_t *wdecor, ui_stock_cursor_t cursor)
285{
286 if (wdecor->cb != NULL && wdecor->cb->set_cursor != NULL)
287 wdecor->cb->set_cursor(wdecor, wdecor->arg, cursor);
288}
289
290/** Get window decoration geometry.
291 *
292 * @param wdecor Window decoration
293 * @param geom Structure to fill in with computed geometry
294 */
295void ui_wdecor_get_geom(ui_wdecor_t *wdecor, ui_wdecor_geom_t *geom)
296{
297 /* Does window have a frame? */
298 if ((wdecor->style & ui_wds_frame) != 0) {
299 geom->interior_rect.p0.x = wdecor->rect.p0.x + 4;
300 geom->interior_rect.p0.y = wdecor->rect.p0.y + 4;
301 geom->interior_rect.p1.x = wdecor->rect.p1.x - 4;
302 geom->interior_rect.p1.y = wdecor->rect.p1.y - 4;
303 } else {
304 geom->interior_rect = wdecor->rect;
305 }
306
307 /* Does window have a title bar? */
308 if ((wdecor->style & ui_wds_titlebar) != 0) {
309 geom->title_bar_rect.p0 = geom->interior_rect.p0;
310 geom->title_bar_rect.p1.x = geom->interior_rect.p1.x;
311 geom->title_bar_rect.p1.y = geom->interior_rect.p0.y + 22;
312
313 geom->app_area_rect.p0.x = geom->interior_rect.p0.x;
314 geom->app_area_rect.p0.y = geom->title_bar_rect.p1.y;
315 geom->app_area_rect.p1 = geom->interior_rect.p1;
316 } else {
317 geom->title_bar_rect.p0.x = 0;
318 geom->title_bar_rect.p0.y = 0;
319 geom->title_bar_rect.p1.x = 0;
320 geom->title_bar_rect.p1.y = 0;
321
322 geom->app_area_rect = geom->interior_rect;
323 }
324
325 /* Does window have a close button? */
326 if ((wdecor->style & ui_wds_close_btn) != 0) {
327 geom->btn_close_rect.p0.x = geom->title_bar_rect.p1.x - 1 - 20;
328 geom->btn_close_rect.p0.y = geom->title_bar_rect.p0.y + 1;
329 geom->btn_close_rect.p1.x = geom->title_bar_rect.p1.x - 1;
330 geom->btn_close_rect.p1.y = geom->title_bar_rect.p0.y + 1 + 20;
331 } else {
332 geom->btn_close_rect.p0.x = 0;
333 geom->btn_close_rect.p0.y = 0;
334 geom->btn_close_rect.p1.x = 0;
335 geom->btn_close_rect.p1.y = 0;
336 }
337}
338
339/** Get outer rectangle from application area rectangle.
340 *
341 * Note that this needs to work just based on a UI, without having an actual
342 * window decoration, since we need it in order to create the window
343 * and its decoration.
344 *
345 * @param style Decoration style
346 * @param app Application area rectangle
347 * @param rect Place to store (outer) window decoration rectangle
348 */
349void ui_wdecor_rect_from_app(ui_wdecor_style_t style, gfx_rect_t *app,
350 gfx_rect_t *rect)
351{
352 *rect = *app;
353
354 if ((style & ui_wds_frame) != 0) {
355 rect->p0.x -= wdecor_edge_w;
356 rect->p0.y -= wdecor_edge_h;
357 rect->p1.x += wdecor_edge_w;
358 rect->p1.y += wdecor_edge_h;
359 }
360
361 if ((style & ui_wds_titlebar) != 0)
362 rect->p0.y -= 22;
363}
364
365/** Application area rectangle from window rectangle.
366 *
367 * Note that this needs to work just based on a UI, without having an actual
368 * window decoration, since we need it in process of resizing the window,
369 * before it is actually resized.
370 *
371 * @param style Decoration style
372 * @param rect Window decoration rectangle
373 * @param app Place to store application area rectangle
374 */
375void ui_wdecor_app_from_rect(ui_wdecor_style_t style, gfx_rect_t *rect,
376 gfx_rect_t *app)
377{
378 *app = *rect;
379
380 if ((style & ui_wds_frame) != 0) {
381 app->p0.x += wdecor_edge_w;
382 app->p0.y += wdecor_edge_h;
383 app->p1.x -= wdecor_edge_w;
384 app->p1.y -= wdecor_edge_h;
385 }
386
387 if ((style & ui_wds_titlebar) != 0)
388 app->p0.y += 22;
389}
390
391/** Get resize type for pointer at the specified position.
392 *
393 * @param wdecor Window decoration
394 * @param pos Pointer position
395 * @return Resize type
396 */
397ui_wdecor_rsztype_t ui_wdecor_get_rsztype(ui_wdecor_t *wdecor,
398 gfx_coord2_t *pos)
399{
400 bool eleft, eright;
401 bool etop, ebottom;
402 bool edge;
403 bool cleft, cright;
404 bool ctop, cbottom;
405
406 /* Window not resizable? */
407 if ((wdecor->style & ui_wds_resizable) == 0)
408 return ui_wr_none;
409
410 /* Position not inside window? */
411 if (!gfx_pix_inside_rect(pos, &wdecor->rect))
412 return ui_wr_none;
413
414 /* Position is within edge width from the outside */
415 eleft = (pos->x < wdecor->rect.p0.x + wdecor_edge_w);
416 eright = (pos->x >= wdecor->rect.p1.x - wdecor_edge_w);
417 etop = (pos->y < wdecor->rect.p0.y + wdecor_edge_h);
418 ebottom = (pos->y >= wdecor->rect.p1.y - wdecor_edge_h);
419
420 /* Position is on one of the four edges */
421 edge = eleft || eright || etop || ebottom;
422
423 /* Position is within resize-corner distance from the outside */
424 cleft = (pos->x < wdecor->rect.p0.x + wdecor_corner_w);
425 cright = (pos->x >= wdecor->rect.p1.x - wdecor_corner_w);
426 ctop = (pos->y < wdecor->rect.p0.y + wdecor_corner_h);
427 cbottom = (pos->y >= wdecor->rect.p1.y - wdecor_corner_h);
428
429 /* Top-left corner */
430 if (edge && cleft && ctop)
431 return ui_wr_top_left;
432
433 /* Top-right corner */
434 if (edge && cright && ctop)
435 return ui_wr_top_right;
436
437 /* Bottom-left corner */
438 if (edge && cleft && cbottom)
439 return ui_wr_bottom_left;
440
441 /* Bottom-right corner */
442 if (edge && cright && cbottom)
443 return ui_wr_bottom_right;
444
445 /* Left edge */
446 if (eleft)
447 return ui_wr_left;
448
449 /* Right edge */
450 if (eright)
451 return ui_wr_right;
452
453 /* Top edge */
454 if (etop)
455 return ui_wr_top;
456
457 /* Bottom edge */
458 if (ebottom)
459 return ui_wr_bottom;
460
461 return ui_wr_none;
462}
463
464/** Get stock cursor to use for the specified window resize type.
465 *
466 * The resize type must be valid, otherwise behavior is undefined.
467 *
468 * @param rsztype Resize type
469 * @return Cursor to use for this resize type
470 */
471ui_stock_cursor_t ui_wdecor_cursor_from_rsztype(ui_wdecor_rsztype_t rsztype)
472{
473 switch (rsztype) {
474 case ui_wr_none:
475 return ui_curs_arrow;
476
477 case ui_wr_top:
478 case ui_wr_bottom:
479 return ui_curs_size_ud;
480
481 case ui_wr_left:
482 case ui_wr_right:
483 return ui_curs_size_lr;
484
485 case ui_wr_top_left:
486 case ui_wr_bottom_right:
487 return ui_curs_size_uldr;
488
489 case ui_wr_top_right:
490 case ui_wr_bottom_left:
491 return ui_curs_size_urdl;
492
493 default:
494 assert(false);
495 return ui_curs_arrow;
496 }
497}
498
499/** Handle window frame position event.
500 *
501 * @param wdecor Window decoration
502 * @param pos_event Position event
503 */
504void ui_wdecor_frame_pos_event(ui_wdecor_t *wdecor, pos_event_t *event)
505{
506 gfx_coord2_t pos;
507 ui_wdecor_rsztype_t rsztype;
508 ui_stock_cursor_t cursor;
509
510 pos.x = event->hpos;
511 pos.y = event->vpos;
512
513 /* Set appropriate resizing cursor, or set arrow cursor */
514
515 rsztype = ui_wdecor_get_rsztype(wdecor, &pos);
516 cursor = ui_wdecor_cursor_from_rsztype(rsztype);
517
518 ui_wdecor_set_cursor(wdecor, cursor);
519
520 /* Press on window border? */
521 if (rsztype != ui_wr_none && event->type == POS_PRESS)
522 ui_wdecor_resize(wdecor, rsztype, &pos);
523}
524
525/** Handle window decoration position event.
526 *
527 * @param wdecor Window decoration
528 * @param pos_event Position event
529 */
530void ui_wdecor_pos_event(ui_wdecor_t *wdecor, pos_event_t *event)
531{
532 gfx_coord2_t pos;
533 ui_wdecor_geom_t geom;
534 ui_evclaim_t claim;
535
536 pos.x = event->hpos;
537 pos.y = event->vpos;
538
539 ui_wdecor_get_geom(wdecor, &geom);
540
541 if ((wdecor->style & ui_wds_close_btn) != 0) {
542 claim = ui_pbutton_pos_event(wdecor->btn_close, event);
543 if (claim == ui_claimed)
544 return;
545 }
546
547 ui_wdecor_frame_pos_event(wdecor, event);
548
549 if ((wdecor->style & ui_wds_titlebar) != 0) {
550 if (event->type == POS_PRESS &&
551 gfx_pix_inside_rect(&pos, &geom.title_bar_rect))
552 ui_wdecor_move(wdecor, &pos);
553 }
554}
555
556/** Window decoration close button was clicked.
557 *
558 * @param pbutton Close button
559 * @param arg Argument (ui_wdecor_t)
560 */
561static void ui_wdecor_btn_clicked(ui_pbutton_t *pbutton, void *arg)
562{
563 ui_wdecor_t *wdecor = (ui_wdecor_t *) arg;
564
565 (void) pbutton;
566 ui_wdecor_close(wdecor);
567}
568
569/** @}
570 */
Note: See TracBrowser for help on using the repository browser.