source: mainline/uspace/lib/ui/src/rbutton.c@ ca2680d

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

Add font to gfx_text_fmt_t

This is quite logical and saves us one argument that we need to pass to
all text formatting functions.

  • Property mode set to 100644
File size: 11.7 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 Radio button
34 */
35
36#include <errno.h>
37#include <gfx/color.h>
38#include <gfx/context.h>
39#include <gfx/render.h>
40#include <gfx/text.h>
41#include <io/pos_event.h>
42#include <stdlib.h>
43#include <str.h>
44#include <ui/control.h>
45#include <ui/paint.h>
46#include <ui/rbutton.h>
47#include "../private/rbutton.h"
48#include "../private/resource.h"
49
50enum {
51 rbutton_oframe_r = 7,
52 rbutton_iframe_r = 6,
53 rbutton_interior_r = 5,
54 rbutton_indicator_r = 3,
55 rbutton_label_margin = 8
56};
57
58static void ui_rbutton_ctl_destroy(void *);
59static errno_t ui_rbutton_ctl_paint(void *);
60static ui_evclaim_t ui_rbutton_ctl_pos_event(void *, pos_event_t *);
61
62/** Radio button control ops */
63ui_control_ops_t ui_rbutton_ops = {
64 .destroy = ui_rbutton_ctl_destroy,
65 .paint = ui_rbutton_ctl_paint,
66 .pos_event = ui_rbutton_ctl_pos_event
67};
68
69/** Create new radio button group.
70 *
71 * @param res UI resource
72 * @param rgroup Place to store pointer to new radio button group
73 * @return EOK on success, ENOMEM if out of memory
74 */
75errno_t ui_rbutton_group_create(ui_resource_t *res, ui_rbutton_group_t **rgroup)
76{
77 ui_rbutton_group_t *group;
78
79 group = calloc(1, sizeof(ui_rbutton_group_t));
80 if (group == NULL)
81 return ENOMEM;
82
83 group->res = res;
84 *rgroup = group;
85 return EOK;
86}
87
88/** Destroy radio button group.
89 *
90 * @param group Radio button group or @c NULL
91 */
92void ui_rbutton_group_destroy(ui_rbutton_group_t *group)
93{
94 if (group == NULL)
95 return;
96
97 free(group);
98}
99
100/** Create new radio button.
101 *
102 * @param group Radio button group
103 * @param caption Caption
104 * @param arg Callback argument
105 * @param rrbutton Place to store pointer to new radio button
106 * @return EOK on success, ENOMEM if out of memory
107 */
108errno_t ui_rbutton_create(ui_rbutton_group_t *group, const char *caption,
109 void *arg, ui_rbutton_t **rrbutton)
110{
111 ui_rbutton_t *rbutton;
112 errno_t rc;
113
114 rbutton = calloc(1, sizeof(ui_rbutton_t));
115 if (rbutton == NULL)
116 return ENOMEM;
117
118 rc = ui_control_new(&ui_rbutton_ops, (void *) rbutton,
119 &rbutton->control);
120 if (rc != EOK) {
121 free(rbutton);
122 return rc;
123 }
124
125 rbutton->caption = str_dup(caption);
126 if (rbutton->caption == NULL) {
127 ui_control_delete(rbutton->control);
128 free(rbutton);
129 return ENOMEM;
130 }
131
132 rbutton->group = group;
133 rbutton->arg = arg;
134
135 if (group->selected == NULL)
136 group->selected = rbutton;
137
138 *rrbutton = rbutton;
139 return EOK;
140}
141
142/** Destroy radio button.
143 *
144 * @param rbutton Radio button or @c NULL
145 */
146void ui_rbutton_destroy(ui_rbutton_t *rbutton)
147{
148 if (rbutton == NULL)
149 return;
150
151 ui_control_delete(rbutton->control);
152 free(rbutton);
153}
154
155/** Get base control from radio button.
156 *
157 * @param rbutton Radio button
158 * @return Control
159 */
160ui_control_t *ui_rbutton_ctl(ui_rbutton_t *rbutton)
161{
162 return rbutton->control;
163}
164
165/** Set radio button group callbacks.
166 *
167 * @param group Radio button group
168 * @param cb Radio button group callbacks
169 * @param arg Callback argument
170 */
171void ui_rbutton_group_set_cb(ui_rbutton_group_t *group,
172 ui_rbutton_group_cb_t *cb, void *arg)
173{
174 group->cb = cb;
175 group->arg = arg;
176}
177
178/** Set button rectangle.
179 *
180 * @param rbutton Button
181 * @param rect New button rectangle
182 */
183void ui_rbutton_set_rect(ui_rbutton_t *rbutton, gfx_rect_t *rect)
184{
185 rbutton->rect = *rect;
186}
187
188/** Paint radio button in graphics mode.
189 *
190 * @param rbutton Radio button
191 * @return EOK on success or an error code
192 */
193errno_t ui_rbutton_paint_gfx(ui_rbutton_t *rbutton)
194{
195 gfx_coord2_t pos;
196 gfx_coord2_t center;
197 gfx_text_fmt_t fmt;
198 bool depressed;
199 errno_t rc;
200
201 center.x = rbutton->rect.p0.x + rbutton_oframe_r;
202 center.y = rbutton->rect.p0.y + rbutton_oframe_r;
203
204 /* Paint radio button frame */
205
206 rc = gfx_set_color(rbutton->group->res->gc,
207 rbutton->group->res->wnd_shadow_color);
208 if (rc != EOK)
209 goto error;
210
211 rc = ui_paint_filled_circle(rbutton->group->res->gc,
212 &center, rbutton_oframe_r, ui_fcircle_upleft);
213 if (rc != EOK)
214 goto error;
215
216 rc = gfx_set_color(rbutton->group->res->gc,
217 rbutton->group->res->wnd_highlight_color);
218 if (rc != EOK)
219 goto error;
220
221 rc = ui_paint_filled_circle(rbutton->group->res->gc,
222 &center, rbutton_oframe_r, ui_fcircle_lowright);
223 if (rc != EOK)
224 goto error;
225
226 rc = gfx_set_color(rbutton->group->res->gc,
227 rbutton->group->res->wnd_frame_sh_color);
228 if (rc != EOK)
229 goto error;
230
231 rc = ui_paint_filled_circle(rbutton->group->res->gc,
232 &center, rbutton_iframe_r, ui_fcircle_upleft);
233 if (rc != EOK)
234 goto error;
235
236 rc = gfx_set_color(rbutton->group->res->gc,
237 rbutton->group->res->wnd_face_color);
238 if (rc != EOK)
239 goto error;
240
241 rc = ui_paint_filled_circle(rbutton->group->res->gc,
242 &center, rbutton_iframe_r, ui_fcircle_lowright);
243 if (rc != EOK)
244 goto error;
245
246 /* Paint radio button interior */
247 depressed = rbutton->held && rbutton->inside;
248
249 rc = gfx_set_color(rbutton->group->res->gc, depressed ?
250 rbutton->group->res->entry_act_bg_color :
251 rbutton->group->res->entry_bg_color);
252 if (rc != EOK)
253 goto error;
254
255 rc = ui_paint_filled_circle(rbutton->group->res->gc,
256 &center, rbutton_interior_r, ui_fcircle_entire);
257 if (rc != EOK)
258 goto error;
259
260 /* Paint active mark */
261
262 if (rbutton->group->selected == rbutton) {
263 rc = gfx_set_color(rbutton->group->res->gc,
264 rbutton->group->res->entry_fg_color);
265 if (rc != EOK)
266 goto error;
267
268 rc = ui_paint_filled_circle(rbutton->group->res->gc,
269 &center, rbutton_indicator_r, ui_fcircle_entire);
270 if (rc != EOK)
271 goto error;
272 }
273
274 /* Paint rbutton label */
275
276 pos.x = center.x + rbutton_oframe_r + rbutton_label_margin;
277 pos.y = center.y;
278
279 gfx_text_fmt_init(&fmt);
280 fmt.font = rbutton->group->res->font;
281 fmt.color = rbutton->group->res->wnd_text_color;
282 fmt.halign = gfx_halign_left;
283 fmt.valign = gfx_valign_center;
284
285 rc = gfx_puttext(&pos, &fmt, rbutton->caption);
286 if (rc != EOK)
287 goto error;
288
289 rc = gfx_update(rbutton->group->res->gc);
290 if (rc != EOK)
291 goto error;
292
293 return EOK;
294error:
295 return rc;
296}
297
298/** Paint radio button in text mode.
299 *
300 * @param rbutton Radio button
301 * @return EOK on success or an error code
302 */
303errno_t ui_rbutton_paint_text(ui_rbutton_t *rbutton)
304{
305 gfx_coord2_t pos;
306 gfx_text_fmt_t fmt;
307 bool depressed;
308 errno_t rc;
309
310 /* Paint radio button */
311
312 depressed = rbutton->held && rbutton->inside;
313
314 pos.x = rbutton->rect.p0.x;
315 pos.y = rbutton->rect.p0.y;
316
317 gfx_text_fmt_init(&fmt);
318 fmt.font = rbutton->group->res->font;
319 fmt.color = depressed ? rbutton->group->res->entry_act_bg_color :
320 rbutton->group->res->wnd_text_color;
321 fmt.halign = gfx_halign_left;
322 fmt.valign = gfx_valign_top;
323
324 rc = gfx_puttext(&pos, &fmt, rbutton->group->selected == rbutton ?
325 "(\u2022)" : "( )");
326 if (rc != EOK)
327 goto error;
328
329 /* Paint radio button label */
330
331 pos.x += 4;
332
333 fmt.color = rbutton->group->res->wnd_text_color;
334
335 rc = gfx_puttext(&pos, &fmt, rbutton->caption);
336 if (rc != EOK)
337 goto error;
338
339 rc = gfx_update(rbutton->group->res->gc);
340 if (rc != EOK)
341 goto error;
342
343 return EOK;
344error:
345 return rc;
346}
347
348/** Paint radio button.
349 *
350 * @param rbutton Radio button
351 * @return EOK on success or an error code
352 */
353errno_t ui_rbutton_paint(ui_rbutton_t *rbutton)
354{
355 if (rbutton->group->res->textmode)
356 return ui_rbutton_paint_text(rbutton);
357 else
358 return ui_rbutton_paint_gfx(rbutton);
359}
360
361/** Press down button.
362 *
363 * @param rbutton Radio button
364 */
365void ui_rbutton_press(ui_rbutton_t *rbutton)
366{
367 if (rbutton->held)
368 return;
369
370 rbutton->inside = true;
371 rbutton->held = true;
372 (void) ui_rbutton_paint(rbutton);
373}
374
375/** Release button.
376 *
377 * @param rbutton Radio button
378 */
379void ui_rbutton_release(ui_rbutton_t *rbutton)
380{
381 if (!rbutton->held)
382 return;
383
384 rbutton->held = false;
385
386 if (rbutton->inside) {
387 /* Activate radio button */
388 ui_rbutton_select(rbutton);
389 }
390}
391
392/** Pointer entered button.
393 *
394 * @param rbutton Radio button
395 */
396void ui_rbutton_enter(ui_rbutton_t *rbutton)
397{
398 if (rbutton->inside)
399 return;
400
401 rbutton->inside = true;
402 if (rbutton->held)
403 (void) ui_rbutton_paint(rbutton);
404}
405
406/** Pointer left button.
407 *
408 * @param rbutton Radio button
409 */
410void ui_rbutton_leave(ui_rbutton_t *rbutton)
411{
412 if (!rbutton->inside)
413 return;
414
415 rbutton->inside = false;
416 if (rbutton->held)
417 (void) ui_rbutton_paint(rbutton);
418}
419
420/** Select radio button.
421 *
422 * @param rbutton Radio button
423 */
424void ui_rbutton_select(ui_rbutton_t *rbutton)
425{
426 ui_rbutton_t *old_selected;
427
428 old_selected = rbutton->group->selected;
429
430 if (old_selected != rbutton) {
431 rbutton->group->selected = rbutton;
432 ui_rbutton_paint(old_selected);
433 }
434
435 /* Repaint and notify */
436 (void) ui_rbutton_paint(rbutton);
437
438 if (old_selected != rbutton)
439 ui_rbutton_selected(rbutton);
440}
441
442/** Notify that button was selected.
443 *
444 * @param rbutton Radio button
445 */
446void ui_rbutton_selected(ui_rbutton_t *rbutton)
447{
448 ui_rbutton_group_t *group = rbutton->group;
449
450 if (group->cb != NULL && group->cb->selected != NULL) {
451 group->cb->selected(group, group->arg, rbutton->arg);
452 }
453}
454
455/** Handle radio button position event.
456 *
457 * @param rbutton Radio button
458 * @param pos_event Position event
459 * @return @c ui_claimed iff the event is claimed
460 */
461ui_evclaim_t ui_rbutton_pos_event(ui_rbutton_t *rbutton, pos_event_t *event)
462{
463 gfx_coord2_t pos;
464 bool inside;
465
466 pos.x = event->hpos;
467 pos.y = event->vpos;
468
469 inside = gfx_pix_inside_rect(&pos, &rbutton->rect);
470
471 switch (event->type) {
472 case POS_PRESS:
473 if (inside) {
474 ui_rbutton_press(rbutton);
475 return ui_claimed;
476 }
477 break;
478 case POS_RELEASE:
479 if (rbutton->held) {
480 ui_rbutton_release(rbutton);
481 return ui_claimed;
482 }
483 break;
484 case POS_UPDATE:
485 if (inside && !rbutton->inside) {
486 ui_rbutton_enter(rbutton);
487 return ui_claimed;
488 } else if (!inside && rbutton->inside) {
489 ui_rbutton_leave(rbutton);
490 }
491 break;
492 case POS_DCLICK:
493 break;
494 }
495
496 return ui_unclaimed;
497}
498
499/** Destroy radio button control.
500 *
501 * @param arg Argument (ui_rbutton_t *)
502 */
503void ui_rbutton_ctl_destroy(void *arg)
504{
505 ui_rbutton_t *rbutton = (ui_rbutton_t *) arg;
506
507 ui_rbutton_destroy(rbutton);
508}
509
510/** Paint radio button control.
511 *
512 * @param arg Argument (ui_rbutton_t *)
513 * @return EOK on success or an error code
514 */
515errno_t ui_rbutton_ctl_paint(void *arg)
516{
517 ui_rbutton_t *rbutton = (ui_rbutton_t *) arg;
518
519 return ui_rbutton_paint(rbutton);
520}
521
522/** Handle radio button control position event.
523 *
524 * @param arg Argument (ui_rbutton_t *)
525 * @param pos_event Position event
526 * @return @c ui_claimed iff the event is claimed
527 */
528ui_evclaim_t ui_rbutton_ctl_pos_event(void *arg, pos_event_t *event)
529{
530 ui_rbutton_t *rbutton = (ui_rbutton_t *) arg;
531
532 return ui_rbutton_pos_event(rbutton, event);
533}
534
535/** @}
536 */
Note: See TracBrowser for help on using the repository browser.