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

serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 65ec18d was 252d03c, checked in by Jiri Svoboda <jiri@…>, 4 years ago

Popping up a message, in text mode as well

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