source: mainline/uspace/lib/ui/src/pbutton.c@ 3a6d44b7

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

Update window button when window caption changes

  • Property mode set to 100644
File size: 14.3 KB
Line 
1/*
2 * Copyright (c) 2022 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 Push button
34 *
35 * Push button either uses text as decoration, or it can use a caller-provided
36 * function to paint the decoration.
37 */
38
39#include <errno.h>
40#include <gfx/color.h>
41#include <gfx/context.h>
42#include <gfx/render.h>
43#include <gfx/text.h>
44#include <io/pos_event.h>
45#include <stdlib.h>
46#include <str.h>
47#include <ui/control.h>
48#include <ui/paint.h>
49#include <ui/pbutton.h>
50#include "../private/pbutton.h"
51#include "../private/resource.h"
52
53/** Caption movement when button is pressed down */
54enum {
55 ui_pb_press_dx = 1,
56 ui_pb_press_dy = 1
57};
58
59static void ui_pbutton_ctl_destroy(void *);
60static errno_t ui_pbutton_ctl_paint(void *);
61static ui_evclaim_t ui_pbutton_ctl_pos_event(void *, pos_event_t *);
62
63/** Push button control ops */
64ui_control_ops_t ui_pbutton_ops = {
65 .destroy = ui_pbutton_ctl_destroy,
66 .paint = ui_pbutton_ctl_paint,
67 .pos_event = ui_pbutton_ctl_pos_event
68};
69
70/** Create new push button.
71 *
72 * @param resource UI resource
73 * @param caption Caption
74 * @param rpbutton Place to store pointer to new push button
75 * @return EOK on success, ENOMEM if out of memory
76 */
77errno_t ui_pbutton_create(ui_resource_t *resource, const char *caption,
78 ui_pbutton_t **rpbutton)
79{
80 ui_pbutton_t *pbutton;
81 errno_t rc;
82
83 pbutton = calloc(1, sizeof(ui_pbutton_t));
84 if (pbutton == NULL)
85 return ENOMEM;
86
87 rc = ui_control_new(&ui_pbutton_ops, (void *) pbutton,
88 &pbutton->control);
89 if (rc != EOK) {
90 free(pbutton);
91 return rc;
92 }
93
94 pbutton->caption = str_dup(caption);
95 if (pbutton->caption == NULL) {
96 ui_control_delete(pbutton->control);
97 free(pbutton);
98 return ENOMEM;
99 }
100
101 pbutton->res = resource;
102 *rpbutton = pbutton;
103 return EOK;
104}
105
106/** Destroy push button.
107 *
108 * @param pbutton Push button or @c NULL
109 */
110void ui_pbutton_destroy(ui_pbutton_t *pbutton)
111{
112 if (pbutton == NULL)
113 return;
114
115 ui_control_delete(pbutton->control);
116 free(pbutton->caption);
117 free(pbutton);
118}
119
120/** Get base control from push button.
121 *
122 * @param pbutton Push button
123 * @return Control
124 */
125ui_control_t *ui_pbutton_ctl(ui_pbutton_t *pbutton)
126{
127 return pbutton->control;
128}
129
130/** Set push button callbacks.
131 *
132 * @param pbutton Push button
133 * @param cb Push button callbacks
134 * @param arg Callback argument
135 */
136void ui_pbutton_set_cb(ui_pbutton_t *pbutton, ui_pbutton_cb_t *cb, void *arg)
137{
138 pbutton->cb = cb;
139 pbutton->arg = arg;
140}
141
142/** Set push button decoration ops.
143 *
144 * @param pbutton Push button
145 * @param ops Push button decoration callbacks
146 * @param arg Decoration ops argument
147 */
148void ui_pbutton_set_decor_ops(ui_pbutton_t *pbutton,
149 ui_pbutton_decor_ops_t *ops, void *arg)
150{
151 pbutton->decor_ops = ops;
152 pbutton->decor_arg = arg;
153}
154
155/** Set push button flag.s
156 *
157 * @param pbutton Push button
158 * @param flags Flags
159 */
160void ui_pbutton_set_flags(ui_pbutton_t *pbutton, ui_pbutton_flags_t flags)
161{
162 pbutton->flags = flags;
163}
164
165/** Set button rectangle.
166 *
167 * @param pbutton Button
168 * @param rect New button rectangle
169 */
170void ui_pbutton_set_rect(ui_pbutton_t *pbutton, gfx_rect_t *rect)
171{
172 pbutton->rect = *rect;
173}
174
175/** Set default flag.
176 *
177 * Default button is the one activated by Enter key, it is marked
178 * by thicker frame.
179 *
180 * @param pbutton Button
181 * @param isdefault @c true iff button is default
182 */
183void ui_pbutton_set_default(ui_pbutton_t *pbutton, bool isdefault)
184{
185 pbutton->isdefault = isdefault;
186}
187
188/** Set push button caption.
189 *
190 * @param pbutton Button
191 * @param caption New caption
192 * @return EOK on success, ENOMEM if out of memory
193 */
194errno_t ui_pbutton_set_caption(ui_pbutton_t *pbutton, const char *caption)
195{
196 char *dcaption;
197
198 dcaption = str_dup(caption);
199 if (dcaption == NULL)
200 return ENOMEM;
201
202 free(pbutton->caption);
203 pbutton->caption = dcaption;
204 return EOK;
205}
206
207/** Paint outer button frame.
208 *
209 * @param pbutton Push button
210 * @return EOK on success or an error code
211 */
212static errno_t ui_pbutton_paint_frame(ui_pbutton_t *pbutton)
213{
214 gfx_rect_t rect;
215 gfx_coord_t thickness;
216 errno_t rc;
217
218 thickness = pbutton->isdefault ? 2 : 1;
219
220 rc = gfx_set_color(pbutton->res->gc, pbutton->res->btn_frame_color);
221 if (rc != EOK)
222 goto error;
223
224 rect.p0.x = pbutton->rect.p0.x + 1;
225 rect.p0.y = pbutton->rect.p0.y;
226 rect.p1.x = pbutton->rect.p1.x - 1;
227 rect.p1.y = pbutton->rect.p0.y + thickness;
228 rc = gfx_fill_rect(pbutton->res->gc, &rect);
229 if (rc != EOK)
230 goto error;
231
232 rect.p0.x = pbutton->rect.p0.x + 1;
233 rect.p0.y = pbutton->rect.p1.y - thickness;
234 rect.p1.x = pbutton->rect.p1.x - 1;
235 rect.p1.y = pbutton->rect.p1.y;
236 rc = gfx_fill_rect(pbutton->res->gc, &rect);
237 if (rc != EOK)
238 goto error;
239
240 rect.p0.x = pbutton->rect.p0.x;
241 rect.p0.y = pbutton->rect.p0.y + 1;
242 rect.p1.x = pbutton->rect.p0.x + thickness;
243 rect.p1.y = pbutton->rect.p1.y - 1;
244 rc = gfx_fill_rect(pbutton->res->gc, &rect);
245 if (rc != EOK)
246 goto error;
247
248 rect.p0.x = pbutton->rect.p1.x - thickness;
249 rect.p0.y = pbutton->rect.p0.y + 1;
250 rect.p1.x = pbutton->rect.p1.x;
251 rect.p1.y = pbutton->rect.p1.y - 1;
252 rc = gfx_fill_rect(pbutton->res->gc, &rect);
253 if (rc != EOK)
254 goto error;
255
256 return EOK;
257error:
258 return rc;
259}
260
261/** Paint outset button bevel.
262 *
263 * @param pbutton Push button
264 * @return EOK on success or an error code
265 */
266static errno_t ui_pbutton_paint_outset(ui_pbutton_t *pbutton,
267 gfx_rect_t *rect)
268{
269 return ui_paint_bevel(pbutton->res->gc, rect,
270 pbutton->res->btn_highlight_color,
271 pbutton->res->btn_shadow_color, 2, NULL);
272}
273
274/** Paint inset button bevel.
275 *
276 * @param pbutton Push button
277 * @return EOK on success or an error code
278 */
279static errno_t ui_pbutton_paint_inset(ui_pbutton_t *pbutton,
280 gfx_rect_t *rect)
281{
282 return ui_paint_bevel(pbutton->res->gc, rect,
283 pbutton->res->btn_shadow_color,
284 pbutton->res->btn_face_color, 2, NULL);
285}
286
287/** Paint button text shadow.
288 *
289 * @param pbutton Push button
290 * @return EOK on success or an error code
291 */
292static errno_t ui_pbutton_paint_text_shadow(ui_pbutton_t *pbutton)
293{
294 gfx_rect_t rect;
295 errno_t rc;
296
297 rect.p0.x = pbutton->rect.p0.x + 1;
298 rect.p0.y = pbutton->rect.p0.y + 1;
299 rect.p1.x = pbutton->rect.p1.x;
300 rect.p1.y = pbutton->rect.p1.y;
301
302 rc = gfx_set_color(pbutton->res->gc, pbutton->res->btn_shadow_color);
303 if (rc != EOK)
304 goto error;
305
306 rc = gfx_fill_rect(pbutton->res->gc, &rect);
307 if (rc != EOK)
308 goto error;
309
310 return EOK;
311error:
312 return rc;
313}
314
315/** Paint push button in graphic mode.
316 *
317 * @param pbutton Push button
318 * @return EOK on success or an error code
319 */
320static errno_t ui_pbutton_paint_gfx(ui_pbutton_t *pbutton)
321{
322 gfx_coord2_t pos;
323 gfx_text_fmt_t fmt;
324 gfx_rect_t rect;
325 gfx_coord_t thickness;
326 bool depressed;
327 errno_t rc;
328
329 thickness = pbutton->isdefault ? 2 : 1;
330 depressed = pbutton->held && pbutton->inside;
331
332 rect.p0.x = pbutton->rect.p0.x + thickness;
333 rect.p0.y = pbutton->rect.p0.y + thickness;
334 rect.p1.x = pbutton->rect.p1.x - thickness;
335 rect.p1.y = pbutton->rect.p1.y - thickness;
336
337 rc = gfx_set_color(pbutton->res->gc, pbutton->res->btn_face_color);
338 if (rc != EOK)
339 goto error;
340
341 rc = gfx_fill_rect(pbutton->res->gc, &rect);
342 if (rc != EOK)
343 goto error;
344
345 /* Center of button rectangle */
346 pos.x = (rect.p0.x + rect.p1.x) / 2;
347 pos.y = (rect.p0.y + rect.p1.y) / 2;
348
349 if (depressed) {
350 pos.x += ui_pb_press_dx;
351 pos.y += ui_pb_press_dy;
352 }
353
354 if (pbutton->decor_ops != NULL && pbutton->decor_ops->paint != NULL) {
355 /* Custom decoration */
356 rc = pbutton->decor_ops->paint(pbutton, pbutton->decor_arg,
357 &pos);
358 if (rc != EOK)
359 goto error;
360 } else {
361 /* Text decoration */
362 gfx_text_fmt_init(&fmt);
363 fmt.font = pbutton->res->font;
364 fmt.color = pbutton->res->btn_text_color;
365 fmt.halign = gfx_halign_center;
366 fmt.valign = gfx_valign_center;
367
368 rc = gfx_puttext(&pos, &fmt, pbutton->caption);
369 if (rc != EOK)
370 goto error;
371 }
372
373 rc = ui_pbutton_paint_frame(pbutton);
374 if (rc != EOK)
375 goto error;
376
377 if (depressed) {
378 rc = ui_pbutton_paint_inset(pbutton, &rect);
379 if (rc != EOK)
380 goto error;
381 } else {
382 rc = ui_pbutton_paint_outset(pbutton, &rect);
383 if (rc != EOK)
384 goto error;
385 }
386
387 rc = gfx_update(pbutton->res->gc);
388 if (rc != EOK)
389 goto error;
390
391 return EOK;
392error:
393 return rc;
394}
395
396/** Paint push button in text mode.
397 *
398 * @param pbutton Push button
399 * @return EOK on success or an error code
400 */
401static errno_t ui_pbutton_paint_text(ui_pbutton_t *pbutton)
402{
403 gfx_coord2_t pos;
404 gfx_text_fmt_t fmt;
405 gfx_rect_t rect;
406 bool depressed;
407 errno_t rc;
408
409 if ((pbutton->flags & ui_pbf_no_text_depress) == 0)
410 depressed = pbutton->held && pbutton->inside;
411 else
412 depressed = false;
413
414 rc = gfx_set_color(pbutton->res->gc, pbutton->res->wnd_face_color);
415 if (rc != EOK)
416 goto error;
417
418 rc = gfx_fill_rect(pbutton->res->gc, &pbutton->rect);
419 if (rc != EOK)
420 goto error;
421
422 rect.p0.x = pbutton->rect.p0.x + (depressed ? 1 : 0);
423 rect.p0.y = pbutton->rect.p0.y;
424 rect.p1.x = pbutton->rect.p1.x - 1 + (depressed ? 1 : 0);
425 rect.p1.y = pbutton->rect.p0.y + 1;
426
427 rc = gfx_set_color(pbutton->res->gc, pbutton->res->btn_face_color);
428 if (rc != EOK)
429 goto error;
430
431 rc = gfx_fill_rect(pbutton->res->gc, &rect);
432 if (rc != EOK)
433 goto error;
434
435 /* Center of button rectangle */
436 pos.x = (rect.p0.x + rect.p1.x) / 2;
437 pos.y = (rect.p0.y + rect.p1.y) / 2;
438
439 gfx_text_fmt_init(&fmt);
440 fmt.font = pbutton->res->font;
441 fmt.color = pbutton->res->btn_text_color;
442 fmt.halign = gfx_halign_center;
443 fmt.valign = gfx_valign_center;
444
445 rc = gfx_puttext(&pos, &fmt, pbutton->caption);
446 if (rc != EOK)
447 goto error;
448
449 if (!depressed) {
450 rc = ui_pbutton_paint_text_shadow(pbutton);
451 if (rc != EOK)
452 goto error;
453 }
454
455 rc = gfx_update(pbutton->res->gc);
456 if (rc != EOK)
457 goto error;
458
459 return EOK;
460error:
461 return rc;
462}
463
464/** Paint push button.
465 *
466 * @param pbutton Push button
467 * @return EOK on success or an error code
468 */
469errno_t ui_pbutton_paint(ui_pbutton_t *pbutton)
470{
471 if (pbutton->res->textmode)
472 return ui_pbutton_paint_text(pbutton);
473 else
474 return ui_pbutton_paint_gfx(pbutton);
475}
476
477/** Press down button.
478 *
479 * @param pbutton Push button
480 */
481void ui_pbutton_press(ui_pbutton_t *pbutton)
482{
483 if (pbutton->held)
484 return;
485
486 pbutton->inside = true;
487 pbutton->held = true;
488 (void) ui_pbutton_paint(pbutton);
489 ui_pbutton_down(pbutton);
490}
491
492/** Release button.
493 *
494 * @param pbutton Push button
495 */
496void ui_pbutton_release(ui_pbutton_t *pbutton)
497{
498 if (!pbutton->held)
499 return;
500
501 pbutton->held = false;
502 ui_pbutton_up(pbutton);
503
504 if (pbutton->inside) {
505 (void) ui_pbutton_paint(pbutton);
506 ui_pbutton_clicked(pbutton);
507 }
508}
509
510/** Pointer entered button.
511 *
512 * @param pbutton Push button
513 */
514void ui_pbutton_enter(ui_pbutton_t *pbutton)
515{
516 if (pbutton->inside)
517 return;
518
519 pbutton->inside = true;
520 if (pbutton->held)
521 (void) ui_pbutton_paint(pbutton);
522}
523
524/** Pointer left button.
525 *
526 * @param pbutton Push button
527 */
528void ui_pbutton_leave(ui_pbutton_t *pbutton)
529{
530 if (!pbutton->inside)
531 return;
532
533 pbutton->inside = false;
534 if (pbutton->held)
535 (void) ui_pbutton_paint(pbutton);
536}
537
538/** Send button clicked event.
539 *
540 * @param pbutton Push button
541 */
542void ui_pbutton_clicked(ui_pbutton_t *pbutton)
543{
544 if (pbutton->cb != NULL && pbutton->cb->clicked != NULL)
545 pbutton->cb->clicked(pbutton, pbutton->arg);
546}
547
548/** Send button down event.
549 *
550 * @param pbutton Push button
551 */
552void ui_pbutton_down(ui_pbutton_t *pbutton)
553{
554 if (pbutton->cb != NULL && pbutton->cb->down != NULL)
555 pbutton->cb->down(pbutton, pbutton->arg);
556}
557
558/** Send button up event.
559 *
560 * @param pbutton Push button
561 */
562void ui_pbutton_up(ui_pbutton_t *pbutton)
563{
564 if (pbutton->cb != NULL && pbutton->cb->up != NULL)
565 pbutton->cb->up(pbutton, pbutton->arg);
566}
567
568/** Handle push button position event.
569 *
570 * @param pbutton Push button
571 * @param pos_event Position event
572 * @return @c ui_claimed iff the event is claimed
573 */
574ui_evclaim_t ui_pbutton_pos_event(ui_pbutton_t *pbutton, pos_event_t *event)
575{
576 gfx_coord2_t pos;
577 bool inside;
578
579 pos.x = event->hpos;
580 pos.y = event->vpos;
581
582 inside = gfx_pix_inside_rect(&pos, &pbutton->rect);
583
584 switch (event->type) {
585 case POS_PRESS:
586 if (inside) {
587 ui_pbutton_press(pbutton);
588 return ui_claimed;
589 }
590 break;
591 case POS_RELEASE:
592 if (pbutton->held) {
593 ui_pbutton_release(pbutton);
594 return ui_claimed;
595 }
596 break;
597 case POS_UPDATE:
598 if (inside && !pbutton->inside) {
599 ui_pbutton_enter(pbutton);
600 return ui_claimed;
601 } else if (!inside && pbutton->inside) {
602 ui_pbutton_leave(pbutton);
603 }
604 break;
605 case POS_DCLICK:
606 break;
607 }
608
609 return ui_unclaimed;
610}
611
612/** Destroy push button control.
613 *
614 * @param arg Argument (ui_pbutton_t *)
615 */
616void ui_pbutton_ctl_destroy(void *arg)
617{
618 ui_pbutton_t *pbutton = (ui_pbutton_t *) arg;
619
620 ui_pbutton_destroy(pbutton);
621}
622
623/** Paint push button control.
624 *
625 * @param arg Argument (ui_pbutton_t *)
626 * @return EOK on success or an error code
627 */
628errno_t ui_pbutton_ctl_paint(void *arg)
629{
630 ui_pbutton_t *pbutton = (ui_pbutton_t *) arg;
631
632 return ui_pbutton_paint(pbutton);
633}
634
635/** Handle push button control position event.
636 *
637 * @param arg Argument (ui_pbutton_t *)
638 * @param pos_event Position event
639 * @return @c ui_claimed iff the event is claimed
640 */
641ui_evclaim_t ui_pbutton_ctl_pos_event(void *arg, pos_event_t *event)
642{
643 ui_pbutton_t *pbutton = (ui_pbutton_t *) arg;
644
645 return ui_pbutton_pos_event(pbutton, event);
646}
647
648/** @}
649 */
Note: See TracBrowser for help on using the repository browser.