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

Last change on this file since c6f23a7 was d7f7a4a, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 4 years ago

Replace some license headers with SPDX identifier

Headers are replaced using tools/transorm-copyright.sh only
when it can be matched verbatim with the license header used
throughout most of the codebase.

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