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

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

Resize application GC when resizing window (and app GC is being used)

  • Property mode set to 100644
File size: 14.1 KB
Line 
1/*
2 * Copyright (c) 2020 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 return EOK;
239}
240
241/** Send decoration close event.
242 *
243 * @param wdecor Window decoration
244 */
245void ui_wdecor_close(ui_wdecor_t *wdecor)
246{
247 if (wdecor->cb != NULL && wdecor->cb->close != NULL)
248 wdecor->cb->close(wdecor, wdecor->arg);
249}
250
251/** Send decoration move event.
252 *
253 * @param wdecor Window decoration
254 * @param pos Position where the title bar was pressed
255 */
256void ui_wdecor_move(ui_wdecor_t *wdecor, gfx_coord2_t *pos)
257{
258 if (wdecor->cb != NULL && wdecor->cb->move != NULL)
259 wdecor->cb->move(wdecor, wdecor->arg, pos);
260}
261
262/** Send decoration resize event.
263 *
264 * @param wdecor Window decoration
265 * @param rsztype Resize type
266 * @param pos Position where the button was pressed
267 */
268void ui_wdecor_resize(ui_wdecor_t *wdecor, ui_wdecor_rsztype_t rsztype,
269 gfx_coord2_t *pos)
270{
271 if (wdecor->cb != NULL && wdecor->cb->resize != NULL)
272 wdecor->cb->resize(wdecor, wdecor->arg, rsztype, pos);
273}
274
275/** Send cursor change event.
276 *
277 * @param wdecor Window decoration
278 * @param cursor Cursor
279 */
280void ui_wdecor_set_cursor(ui_wdecor_t *wdecor, ui_stock_cursor_t cursor)
281{
282 if (wdecor->cb != NULL && wdecor->cb->set_cursor != NULL)
283 wdecor->cb->set_cursor(wdecor, wdecor->arg, cursor);
284}
285
286/** Get window decoration geometry.
287 *
288 * @param wdecor Window decoration
289 * @param geom Structure to fill in with computed geometry
290 */
291void ui_wdecor_get_geom(ui_wdecor_t *wdecor, ui_wdecor_geom_t *geom)
292{
293 /* Does window have a frame? */
294 if ((wdecor->style & ui_wds_frame) != 0) {
295 geom->interior_rect.p0.x = wdecor->rect.p0.x + 4;
296 geom->interior_rect.p0.y = wdecor->rect.p0.y + 4;
297 geom->interior_rect.p1.x = wdecor->rect.p1.x - 4;
298 geom->interior_rect.p1.y = wdecor->rect.p1.y - 4;
299 } else {
300 geom->interior_rect = wdecor->rect;
301 }
302
303 /* Does window have a title bar? */
304 if ((wdecor->style & ui_wds_titlebar) != 0) {
305 geom->title_bar_rect.p0 = geom->interior_rect.p0;
306 geom->title_bar_rect.p1.x = geom->interior_rect.p1.x;
307 geom->title_bar_rect.p1.y = geom->interior_rect.p0.y + 22;
308
309 geom->app_area_rect.p0.x = geom->interior_rect.p0.x;
310 geom->app_area_rect.p0.y = geom->title_bar_rect.p1.y;
311 geom->app_area_rect.p1 = geom->interior_rect.p1;
312 } else {
313 geom->title_bar_rect.p0.x = 0;
314 geom->title_bar_rect.p0.y = 0;
315 geom->title_bar_rect.p1.x = 0;
316 geom->title_bar_rect.p1.y = 0;
317
318 geom->app_area_rect = geom->interior_rect;
319 }
320
321 /* Does window have a close button? */
322 if ((wdecor->style & ui_wds_close_btn) != 0) {
323 geom->btn_close_rect.p0.x = geom->title_bar_rect.p1.x - 1 - 20;
324 geom->btn_close_rect.p0.y = geom->title_bar_rect.p0.y + 1;
325 geom->btn_close_rect.p1.x = geom->title_bar_rect.p1.x - 1;
326 geom->btn_close_rect.p1.y = geom->title_bar_rect.p0.y + 1 + 20;
327 } else {
328 geom->btn_close_rect.p0.x = 0;
329 geom->btn_close_rect.p0.y = 0;
330 geom->btn_close_rect.p1.x = 0;
331 geom->btn_close_rect.p1.y = 0;
332 }
333}
334
335/** Get outer rectangle from application area rectangle.
336 *
337 * Note that this needs to work just based on a UI, without having an actual
338 * window decoration, since we need it in order to create the window
339 * and its decoration.
340 *
341 * @param style Decoration style
342 * @param app Application area rectangle
343 * @param rect Place to store (outer) window decoration rectangle
344 */
345void ui_wdecor_rect_from_app(ui_wdecor_style_t style, gfx_rect_t *app,
346 gfx_rect_t *rect)
347{
348 *rect = *app;
349
350 if ((style & ui_wds_frame) != 0) {
351 rect->p0.x -= wdecor_edge_w;
352 rect->p0.y -= wdecor_edge_h;
353 rect->p1.x += wdecor_edge_w;
354 rect->p1.y += wdecor_edge_h;
355 }
356
357 if ((style & ui_wds_titlebar) != 0)
358 rect->p0.y -= 22;
359}
360
361/** Application area rectangle from window rectangle.
362 *
363 * Note that this needs to work just based on a UI, without having an actual
364 * window decoration, since we need it in process of resizing the window,
365 * before it is actually resized.
366 *
367 * @param style Decoration style
368 * @param rect Window decoration rectangle
369 * @param app Place to store application area rectangle
370 */
371void ui_wdecor_app_from_rect(ui_wdecor_style_t style, gfx_rect_t *rect,
372 gfx_rect_t *app)
373{
374 *app = *rect;
375
376 if ((style & ui_wds_frame) != 0) {
377 app->p0.x += wdecor_edge_w;
378 app->p0.y += wdecor_edge_h;
379 app->p1.x -= wdecor_edge_w;
380 app->p1.y -= wdecor_edge_h;
381 }
382
383 if ((style & ui_wds_titlebar) != 0)
384 app->p0.y += 22;
385}
386
387/** Get resize type for pointer at the specified position.
388 *
389 * @param wdecor Window decoration
390 * @param pos Pointer position
391 * @return Resize type
392 */
393ui_wdecor_rsztype_t ui_wdecor_get_rsztype(ui_wdecor_t *wdecor,
394 gfx_coord2_t *pos)
395{
396 bool eleft, eright;
397 bool etop, ebottom;
398 bool edge;
399 bool cleft, cright;
400 bool ctop, cbottom;
401
402 /* Window not resizable? */
403 if ((wdecor->style & ui_wds_resizable) == 0)
404 return ui_wr_none;
405
406 /* Position not inside window? */
407 if (!gfx_pix_inside_rect(pos, &wdecor->rect))
408 return ui_wr_none;
409
410 /* Position is within edge width from the outside */
411 eleft = (pos->x < wdecor->rect.p0.x + wdecor_edge_w);
412 eright = (pos->x >= wdecor->rect.p1.x - wdecor_edge_w);
413 etop = (pos->y < wdecor->rect.p0.y + wdecor_edge_h);
414 ebottom = (pos->y >= wdecor->rect.p1.y - wdecor_edge_h);
415
416 /* Position is on one of the four edges */
417 edge = eleft || eright || etop || ebottom;
418
419 /* Position is within resize-corner distance from the outside */
420 cleft = (pos->x < wdecor->rect.p0.x + wdecor_corner_w);
421 cright = (pos->x >= wdecor->rect.p1.x - wdecor_corner_w);
422 ctop = (pos->y < wdecor->rect.p0.y + wdecor_corner_h);
423 cbottom = (pos->y >= wdecor->rect.p1.y - wdecor_corner_h);
424
425 /* Top-left corner */
426 if (edge && cleft && ctop)
427 return ui_wr_top_left;
428
429 /* Top-right corner */
430 if (edge && cright && ctop)
431 return ui_wr_top_right;
432
433 /* Bottom-left corner */
434 if (edge && cleft && cbottom)
435 return ui_wr_bottom_left;
436
437 /* Bottom-right corner */
438 if (edge && cright && cbottom)
439 return ui_wr_bottom_right;
440
441 /* Left edge */
442 if (eleft)
443 return ui_wr_left;
444
445 /* Right edge */
446 if (eright)
447 return ui_wr_right;
448
449 /* Top edge */
450 if (etop)
451 return ui_wr_top;
452
453 /* Bottom edge */
454 if (ebottom)
455 return ui_wr_bottom;
456
457 return ui_wr_none;
458}
459
460/** Get stock cursor to use for the specified window resize type.
461 *
462 * The resize type must be valid, otherwise behavior is undefined.
463 *
464 * @param rsztype Resize type
465 * @return Cursor to use for this resize type
466 */
467ui_stock_cursor_t ui_wdecor_cursor_from_rsztype(ui_wdecor_rsztype_t rsztype)
468{
469 switch (rsztype) {
470 case ui_wr_none:
471 return ui_curs_arrow;
472
473 case ui_wr_top:
474 case ui_wr_bottom:
475 return ui_curs_size_ud;
476
477 case ui_wr_left:
478 case ui_wr_right:
479 return ui_curs_size_lr;
480
481 case ui_wr_top_left:
482 case ui_wr_bottom_right:
483 return ui_curs_size_uldr;
484
485 case ui_wr_top_right:
486 case ui_wr_bottom_left:
487 return ui_curs_size_urdl;
488
489 default:
490 assert(false);
491 return ui_curs_arrow;
492 }
493}
494
495/** Handle window frame position event.
496 *
497 * @param wdecor Window decoration
498 * @param pos_event Position event
499 */
500void ui_wdecor_frame_pos_event(ui_wdecor_t *wdecor, pos_event_t *event)
501{
502 gfx_coord2_t pos;
503 ui_wdecor_rsztype_t rsztype;
504 ui_stock_cursor_t cursor;
505
506 pos.x = event->hpos;
507 pos.y = event->vpos;
508
509 /* Set appropriate resizing cursor, or set arrow cursor */
510
511 rsztype = ui_wdecor_get_rsztype(wdecor, &pos);
512 cursor = ui_wdecor_cursor_from_rsztype(rsztype);
513
514 ui_wdecor_set_cursor(wdecor, cursor);
515
516 /* Press on window border? */
517 if (rsztype != ui_wr_none && event->type == POS_PRESS)
518 ui_wdecor_resize(wdecor, rsztype, &pos);
519}
520
521/** Handle window decoration position event.
522 *
523 * @param wdecor Window decoration
524 * @param pos_event Position event
525 */
526void ui_wdecor_pos_event(ui_wdecor_t *wdecor, pos_event_t *event)
527{
528 gfx_coord2_t pos;
529 ui_wdecor_geom_t geom;
530 ui_evclaim_t claim;
531
532 pos.x = event->hpos;
533 pos.y = event->vpos;
534
535 ui_wdecor_get_geom(wdecor, &geom);
536
537 if ((wdecor->style & ui_wds_close_btn) != 0) {
538 claim = ui_pbutton_pos_event(wdecor->btn_close, event);
539 if (claim == ui_claimed)
540 return;
541 }
542
543 ui_wdecor_frame_pos_event(wdecor, event);
544
545 if ((wdecor->style & ui_wds_titlebar) != 0) {
546 if (event->type == POS_PRESS &&
547 gfx_pix_inside_rect(&pos, &geom.title_bar_rect))
548 ui_wdecor_move(wdecor, &pos);
549 }
550}
551
552/** Window decoration close button was clicked.
553 *
554 * @param pbutton Close button
555 * @param arg Argument (ui_wdecor_t)
556 */
557static void ui_wdecor_btn_clicked(ui_pbutton_t *pbutton, void *arg)
558{
559 ui_wdecor_t *wdecor = (ui_wdecor_t *) arg;
560
561 (void) pbutton;
562 ui_wdecor_close(wdecor);
563}
564
565/** @}
566 */
Note: See TracBrowser for help on using the repository browser.