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

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

Generating button activation event

  • Property mode set to 100644
File size: 10.8 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 Push 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/pbutton.h>
45#include "../private/pbutton.h"
46#include "../private/resource.h"
47
48/** Caption movement when button is pressed down */
49enum {
50 ui_pb_press_dx = 1,
51 ui_pb_press_dy = 1
52};
53
54/** Create new push button.
55 *
56 * @param resource UI resource
57 * @param caption Caption
58 * @param rpbutton Place to store pointer to new push button
59 * @return EOK on success, ENOMEM if out of memory
60 */
61errno_t ui_pbutton_create(ui_resource_t *resource, const char *caption,
62 ui_pbutton_t **rpbutton)
63{
64 ui_pbutton_t *pbutton;
65
66 pbutton = calloc(1, sizeof(ui_pbutton_t));
67 if (pbutton == NULL)
68 return ENOMEM;
69
70 pbutton->caption = str_dup(caption);
71 if (pbutton->caption == NULL) {
72 free(pbutton);
73 return ENOMEM;
74 }
75
76 pbutton->res = resource;
77 *rpbutton = pbutton;
78 return EOK;
79}
80
81/** Destroy push button.
82 *
83 * @param pbutton Push button or @c NULL
84 */
85void ui_pbutton_destroy(ui_pbutton_t *pbutton)
86{
87 if (pbutton == NULL)
88 return;
89
90 free(pbutton);
91}
92
93/** Set push button callbacks.
94 *
95 * @param pbutton Push button
96 * @param cb Push button callbacks
97 * @param arg Callback argument
98 */
99void ui_pbutton_set_cb(ui_pbutton_t *pbutton, ui_pbutton_cb_t *cb, void *arg)
100{
101 pbutton->cb = cb;
102 pbutton->arg = arg;
103}
104
105/** Set button rectangle.
106 *
107 * @param pbutton Button
108 * @param rect New button rectanle
109 */
110void ui_pbutton_set_rect(ui_pbutton_t *pbutton, gfx_rect_t *rect)
111{
112 pbutton->rect = *rect;
113}
114
115/** Set default flag.
116 *
117 * Default button is the one activated by Enter key, it is marked
118 * by thicker frame.
119 *
120 * @param pbutton Button
121 * @param isdefault @c true iff button is default
122 */
123void ui_pbutton_set_default(ui_pbutton_t *pbutton, bool isdefault)
124{
125 pbutton->isdefault = isdefault;
126}
127
128/** Paint outer button frame.
129 *
130 * @param pbutton Push button
131 * @return EOK on success or an error code
132 */
133static errno_t ui_pbutton_paint_frame(ui_pbutton_t *pbutton)
134{
135 gfx_color_t *color = NULL;
136 gfx_rect_t rect;
137 gfx_coord_t thickness;
138 errno_t rc;
139
140 thickness = pbutton->isdefault ? 2 : 1;
141
142 rc = gfx_color_new_rgb_i16(0, 0, 0, &color);
143 if (rc != EOK)
144 goto error;
145
146 rc = gfx_set_color(pbutton->res->gc, color);
147 if (rc != EOK)
148 goto error;
149
150 rect.p0.x = pbutton->rect.p0.x + 1;
151 rect.p0.y = pbutton->rect.p0.y;
152 rect.p1.x = pbutton->rect.p1.x - 1;
153 rect.p1.y = pbutton->rect.p0.y + thickness;
154 rc = gfx_fill_rect(pbutton->res->gc, &rect);
155 if (rc != EOK)
156 goto error;
157
158 rect.p0.x = pbutton->rect.p0.x + 1;
159 rect.p0.y = pbutton->rect.p1.y - thickness;
160 rect.p1.x = pbutton->rect.p1.x - 1;
161 rect.p1.y = pbutton->rect.p1.y;
162 rc = gfx_fill_rect(pbutton->res->gc, &rect);
163 if (rc != EOK)
164 goto error;
165
166 rect.p0.x = pbutton->rect.p0.x;
167 rect.p0.y = pbutton->rect.p0.y + 1;
168 rect.p1.x = pbutton->rect.p0.x + thickness;
169 rect.p1.y = pbutton->rect.p1.y - 1;
170 rc = gfx_fill_rect(pbutton->res->gc, &rect);
171 if (rc != EOK)
172 goto error;
173
174 rect.p0.x = pbutton->rect.p1.x - thickness;
175 rect.p0.y = pbutton->rect.p0.y + 1;
176 rect.p1.x = pbutton->rect.p1.x;
177 rect.p1.y = pbutton->rect.p1.y - 1;
178 rc = gfx_fill_rect(pbutton->res->gc, &rect);
179 if (rc != EOK)
180 goto error;
181
182 gfx_color_delete(color);
183 return EOK;
184error:
185 if (color != NULL)
186 gfx_color_delete(color);
187 return rc;
188}
189
190/** Paint outset button bevel.
191 *
192 * @param pbutton Push button
193 * @return EOK on success or an error code
194 */
195static errno_t ui_pbutton_paint_outset(ui_pbutton_t *pbutton,
196 gfx_rect_t *rect)
197{
198 gfx_color_t *color = NULL;
199 gfx_rect_t frect;
200 gfx_coord_t i;
201 errno_t rc;
202
203 /* Highlight */
204
205 rc = gfx_color_new_rgb_i16(0xffff, 0xffff, 0xffff, &color);
206 if (rc != EOK)
207 goto error;
208
209 rc = gfx_set_color(pbutton->res->gc, color);
210 if (rc != EOK)
211 goto error;
212
213 for (i = 0; i < 2; i++) {
214 frect.p0.x = rect->p0.x + i;
215 frect.p0.y = rect->p0.y + i;
216 frect.p1.x = rect->p1.x - i - 1;
217 frect.p1.y = rect->p0.y + i + 1;
218 rc = gfx_fill_rect(pbutton->res->gc, &frect);
219 if (rc != EOK)
220 goto error;
221
222 frect.p0.x = rect->p0.x + i;
223 frect.p0.y = rect->p0.y + i + 1;
224 frect.p1.x = rect->p0.x + i + 1;
225 frect.p1.y = rect->p1.y - i - 1;
226 rc = gfx_fill_rect(pbutton->res->gc, &frect);
227 if (rc != EOK)
228 goto error;
229 }
230
231 gfx_color_delete(color);
232 color = NULL;
233
234 /* Shadow */
235
236 rc = gfx_color_new_rgb_i16(0x8888, 0x8888, 0x8888, &color);
237 if (rc != EOK)
238 goto error;
239
240 rc = gfx_set_color(pbutton->res->gc, color);
241 if (rc != EOK)
242 goto error;
243
244 for (i = 0; i < 2; i++) {
245 frect.p0.x = rect->p0.x + i;
246 frect.p0.y = rect->p1.y - i - 1;
247 frect.p1.x = rect->p1.x - i - 1;
248 frect.p1.y = rect->p1.y - i;
249 rc = gfx_fill_rect(pbutton->res->gc, &frect);
250 if (rc != EOK)
251 goto error;
252
253 frect.p0.x = rect->p1.x - i - 1;
254 frect.p0.y = rect->p0.y + i;
255 frect.p1.x = rect->p1.x - i;
256 frect.p1.y = rect->p1.y - i;
257 rc = gfx_fill_rect(pbutton->res->gc, &frect);
258 if (rc != EOK)
259 goto error;
260 }
261
262 gfx_color_delete(color);
263
264 return EOK;
265error:
266 if (color != NULL)
267 gfx_color_delete(color);
268 return rc;
269}
270
271/** Paint inset button bevel.
272 *
273 * @param pbutton Push button
274 * @return EOK on success or an error code
275 */
276static errno_t ui_pbutton_paint_inset(ui_pbutton_t *pbutton,
277 gfx_rect_t *rect)
278{
279 gfx_color_t *color = NULL;
280 gfx_rect_t frect;
281 errno_t rc;
282
283 rc = gfx_color_new_rgb_i16(0x8888, 0x8888, 0x8888, &color);
284 if (rc != EOK)
285 goto error;
286
287 rc = gfx_set_color(pbutton->res->gc, color);
288 if (rc != EOK)
289 goto error;
290
291 frect.p0.x = rect->p0.x;
292 frect.p0.y = rect->p0.y;
293 frect.p1.x = rect->p1.x;
294 frect.p1.y = rect->p0.y + 2;
295 rc = gfx_fill_rect(pbutton->res->gc, &frect);
296 if (rc != EOK)
297 goto error;
298
299 frect.p0.x = rect->p0.x;
300 frect.p0.y = rect->p0.y + 2;
301 frect.p1.x = rect->p0.x + 2;
302 frect.p1.y = rect->p1.y;
303 rc = gfx_fill_rect(pbutton->res->gc, &frect);
304 if (rc != EOK)
305 goto error;
306
307 gfx_color_delete(color);
308
309 return EOK;
310error:
311 if (color != NULL)
312 gfx_color_delete(color);
313 return rc;
314}
315
316/** Paint push button.
317 *
318 * @param pbutton Push button
319 * @return EOK on success or an error code
320 */
321errno_t ui_pbutton_paint(ui_pbutton_t *pbutton)
322{
323 gfx_color_t *color = NULL;
324 gfx_coord2_t pos;
325 gfx_text_fmt_t fmt;
326 gfx_rect_t rect;
327 gfx_coord_t thickness;
328 bool depressed;
329 errno_t rc;
330
331 thickness = pbutton->isdefault ? 2 : 1;
332 depressed = pbutton->held && pbutton->inside;
333
334 rect.p0.x = pbutton->rect.p0.x + thickness;
335 rect.p0.y = pbutton->rect.p0.y + thickness;
336 rect.p1.x = pbutton->rect.p1.x - thickness;
337 rect.p1.y = pbutton->rect.p1.y - thickness;
338
339 rc = gfx_color_new_rgb_i16(0xc8c8, 0xc8c8, 0xc8c8, &color);
340 if (rc != EOK)
341 goto error;
342
343 rc = gfx_set_color(pbutton->res->gc, color);
344 if (rc != EOK)
345 goto error;
346
347 rc = gfx_fill_rect(pbutton->res->gc, &rect);
348 if (rc != EOK)
349 goto error;
350
351 gfx_color_delete(color);
352 color = NULL;
353
354 rc = gfx_color_new_rgb_i16(0, 0, 0, &color);
355 if (rc != EOK)
356 goto error;
357
358 rc = gfx_set_color(pbutton->res->gc, color);
359 if (rc != EOK)
360 goto error;
361
362 /* Center of button rectangle */
363 pos.x = (rect.p0.x + rect.p1.x) / 2;
364 pos.y = (rect.p0.y + rect.p1.y) / 2;
365
366 if (depressed) {
367 pos.x += ui_pb_press_dx;
368 pos.y += ui_pb_press_dy;
369 }
370
371 gfx_text_fmt_init(&fmt);
372 fmt.halign = gfx_halign_center;
373 fmt.valign = gfx_valign_center;
374
375 rc = gfx_puttext(pbutton->res->font, &pos, &fmt, pbutton->caption);
376 if (rc != EOK)
377 goto error;
378
379 gfx_color_delete(color);
380 color = NULL;
381
382 rc = ui_pbutton_paint_frame(pbutton);
383 if (rc != EOK)
384 goto error;
385
386 if (depressed) {
387 rc = ui_pbutton_paint_inset(pbutton, &rect);
388 if (rc != EOK)
389 goto error;
390 } else {
391 rc = ui_pbutton_paint_outset(pbutton, &rect);
392 if (rc != EOK)
393 goto error;
394 }
395
396 return EOK;
397error:
398 if (color != NULL)
399 gfx_color_delete(color);
400 return rc;
401}
402
403/** Press down button.
404 *
405 * @param pbutton Push button
406 */
407void ui_pbutton_press(ui_pbutton_t *pbutton)
408{
409 if (pbutton->held)
410 return;
411
412 pbutton->inside = true;
413 pbutton->held = true;
414 (void) ui_pbutton_paint(pbutton);
415}
416
417/** Release button.
418 *
419 * @param pbutton Push button
420 */
421void ui_pbutton_release(ui_pbutton_t *pbutton)
422{
423 if (!pbutton->held)
424 return;
425
426 pbutton->held = false;
427
428 if (pbutton->inside) {
429 (void) ui_pbutton_paint(pbutton);
430 ui_pbutton_clicked(pbutton);
431 }
432}
433
434/** Pointer entered button.
435 *
436 * @param pbutton Push button
437 */
438void ui_pbutton_enter(ui_pbutton_t *pbutton)
439{
440 if (pbutton->inside)
441 return;
442
443 pbutton->inside = true;
444 if (pbutton->held)
445 (void) ui_pbutton_paint(pbutton);
446}
447
448/** Pointer left button.
449 *
450 * @param pbutton Push button
451 */
452void ui_pbutton_leave(ui_pbutton_t *pbutton)
453{
454 if (!pbutton->inside)
455 return;
456
457 pbutton->inside = false;
458 if (pbutton->held)
459 (void) ui_pbutton_paint(pbutton);
460}
461
462/** Button was clicked.
463 *
464 * @param pbutton Push button
465 */
466void ui_pbutton_clicked(ui_pbutton_t *pbutton)
467{
468 if (pbutton->cb != NULL && pbutton->cb->clicked != NULL)
469 pbutton->cb->clicked(pbutton, pbutton->arg);
470}
471
472/** Handle push button position event.
473 *
474 * @param pbutton Push button
475 * @param pos_event Position event
476 */
477void ui_pbutton_pos_event(ui_pbutton_t *pbutton, pos_event_t *event)
478{
479 gfx_coord2_t pos;
480 bool inside;
481
482 pos.x = event->hpos;
483 pos.y = event->vpos;
484
485 inside = gfx_pix_inside_rect(&pos, &pbutton->rect);
486
487 switch (event->type) {
488 case POS_PRESS:
489 if (inside)
490 ui_pbutton_press(pbutton);
491 break;
492 case POS_RELEASE:
493 ui_pbutton_release(pbutton);
494 break;
495 case POS_UPDATE:
496 if (inside && !pbutton->inside) {
497 ui_pbutton_enter(pbutton);
498 } else if (!inside && pbutton->inside) {
499 ui_pbutton_leave(pbutton);
500 }
501 break;
502 }
503}
504
505/** @}
506 */
Note: See TracBrowser for help on using the repository browser.