source: mainline/uspace/lib/ui/test/pbutton.c@ f7a90df

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

Improve close button event routing

Button event processing routine is the best place to determine whether
the event goes to the button or not. It can also 'grab' button release
events simply by claiming them.

  • Property mode set to 100644
File size: 13.5 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#include <gfx/context.h>
30#include <gfx/coord.h>
31#include <mem.h>
32#include <pcut/pcut.h>
33#include <stdbool.h>
34#include <ui/pbutton.h>
35#include <ui/resource.h>
36#include "../private/pbutton.h"
37
38PCUT_INIT;
39
40PCUT_TEST_SUITE(pbutton);
41
42static errno_t testgc_set_color(void *, gfx_color_t *);
43static errno_t testgc_fill_rect(void *, gfx_rect_t *);
44static errno_t testgc_bitmap_create(void *, gfx_bitmap_params_t *,
45 gfx_bitmap_alloc_t *, void **);
46static errno_t testgc_bitmap_destroy(void *);
47static errno_t testgc_bitmap_render(void *, gfx_rect_t *, gfx_coord2_t *);
48static errno_t testgc_bitmap_get_alloc(void *, gfx_bitmap_alloc_t *);
49
50static gfx_context_ops_t ops = {
51 .set_color = testgc_set_color,
52 .fill_rect = testgc_fill_rect,
53 .bitmap_create = testgc_bitmap_create,
54 .bitmap_destroy = testgc_bitmap_destroy,
55 .bitmap_render = testgc_bitmap_render,
56 .bitmap_get_alloc = testgc_bitmap_get_alloc
57};
58
59static void test_pbutton_clicked(ui_pbutton_t *, void *);
60
61static ui_pbutton_cb_t test_pbutton_cb = {
62 .clicked = test_pbutton_clicked
63};
64
65static ui_pbutton_cb_t dummy_pbutton_cb = {
66};
67
68typedef struct {
69 bool bm_created;
70 bool bm_destroyed;
71 gfx_bitmap_params_t bm_params;
72 void *bm_pixels;
73 gfx_rect_t bm_srect;
74 gfx_coord2_t bm_offs;
75 bool bm_rendered;
76 bool bm_got_alloc;
77} test_gc_t;
78
79typedef struct {
80 test_gc_t *tgc;
81 gfx_bitmap_alloc_t alloc;
82 bool myalloc;
83} testgc_bitmap_t;
84
85typedef struct {
86 bool clicked;
87} test_cb_resp_t;
88
89/** Create and destroy button */
90PCUT_TEST(create_destroy)
91{
92 ui_pbutton_t *pbutton = NULL;
93 errno_t rc;
94
95 rc = ui_pbutton_create(NULL, "Hello", &pbutton);
96 PCUT_ASSERT_ERRNO_VAL(EOK, rc);
97 PCUT_ASSERT_NOT_NULL(pbutton);
98
99 ui_pbutton_destroy(pbutton);
100}
101
102/** ui_pbutton_destroy() can take NULL argument (no-op) */
103PCUT_TEST(destroy_null)
104{
105 ui_pbutton_destroy(NULL);
106}
107
108/** Set button rectangle sets internal field */
109PCUT_TEST(set_rect)
110{
111 ui_pbutton_t *pbutton;
112 gfx_rect_t rect;
113 errno_t rc;
114
115 rc = ui_pbutton_create(NULL, "Hello", &pbutton);
116 PCUT_ASSERT_ERRNO_VAL(EOK, rc);
117
118 rect.p0.x = 1;
119 rect.p0.y = 2;
120 rect.p1.x = 3;
121 rect.p1.y = 4;
122
123 ui_pbutton_set_rect(pbutton, &rect);
124 PCUT_ASSERT_INT_EQUALS(rect.p0.x, pbutton->rect.p0.x);
125 PCUT_ASSERT_INT_EQUALS(rect.p0.y, pbutton->rect.p0.y);
126 PCUT_ASSERT_INT_EQUALS(rect.p1.x, pbutton->rect.p1.x);
127 PCUT_ASSERT_INT_EQUALS(rect.p1.y, pbutton->rect.p1.y);
128
129 ui_pbutton_destroy(pbutton);
130}
131
132/** Set default flag sets internal field */
133PCUT_TEST(set_default)
134{
135 ui_pbutton_t *pbutton;
136 errno_t rc;
137
138 rc = ui_pbutton_create(NULL, "Hello", &pbutton);
139 PCUT_ASSERT_ERRNO_VAL(EOK, rc);
140
141 ui_pbutton_set_default(pbutton, true);
142 PCUT_ASSERT_TRUE(pbutton->isdefault);
143
144 ui_pbutton_set_default(pbutton, false);
145 PCUT_ASSERT_FALSE(pbutton->isdefault);
146
147 ui_pbutton_destroy(pbutton);
148}
149
150/** Paint button */
151PCUT_TEST(paint)
152{
153 errno_t rc;
154 gfx_context_t *gc = NULL;
155 test_gc_t tgc;
156 ui_resource_t *resource = NULL;
157 ui_pbutton_t *pbutton;
158
159 memset(&tgc, 0, sizeof(tgc));
160 rc = gfx_context_new(&ops, &tgc, &gc);
161 PCUT_ASSERT_ERRNO_VAL(EOK, rc);
162
163 rc = ui_resource_create(gc, &resource);
164 PCUT_ASSERT_ERRNO_VAL(EOK, rc);
165 PCUT_ASSERT_NOT_NULL(resource);
166
167 rc = ui_pbutton_create(resource, "Hello", &pbutton);
168 PCUT_ASSERT_ERRNO_VAL(EOK, rc);
169
170 rc = ui_pbutton_paint(pbutton);
171 PCUT_ASSERT_ERRNO_VAL(EOK, rc);
172
173 ui_pbutton_destroy(pbutton);
174 ui_resource_destroy(resource);
175
176 rc = gfx_context_delete(gc);
177 PCUT_ASSERT_ERRNO_VAL(EOK, rc);
178}
179
180/** Test ui_pbutton_clicked() */
181PCUT_TEST(clicked)
182{
183 errno_t rc;
184 ui_pbutton_t *pbutton;
185 test_cb_resp_t resp;
186
187 rc = ui_pbutton_create(NULL, "Hello", &pbutton);
188 PCUT_ASSERT_ERRNO_VAL(EOK, rc);
189
190 /* Clicked with no callbacks set */
191 ui_pbutton_clicked(pbutton);
192
193 /* Clicked with callback not implementing clicked */
194 ui_pbutton_set_cb(pbutton, &dummy_pbutton_cb, NULL);
195 ui_pbutton_clicked(pbutton);
196
197 /* Clicked with real callback set */
198 resp.clicked = false;
199 ui_pbutton_set_cb(pbutton, &test_pbutton_cb, &resp);
200 ui_pbutton_clicked(pbutton);
201 PCUT_ASSERT_TRUE(resp.clicked);
202
203 ui_pbutton_destroy(pbutton);
204}
205
206/** Press and release button */
207PCUT_TEST(press_release)
208{
209 errno_t rc;
210 gfx_context_t *gc = NULL;
211 test_gc_t tgc;
212 ui_resource_t *resource = NULL;
213 ui_pbutton_t *pbutton;
214 test_cb_resp_t resp;
215
216 memset(&tgc, 0, sizeof(tgc));
217 rc = gfx_context_new(&ops, &tgc, &gc);
218 PCUT_ASSERT_ERRNO_VAL(EOK, rc);
219
220 rc = ui_resource_create(gc, &resource);
221 PCUT_ASSERT_ERRNO_VAL(EOK, rc);
222 PCUT_ASSERT_NOT_NULL(resource);
223
224 rc = ui_pbutton_create(resource, "Hello", &pbutton);
225 PCUT_ASSERT_ERRNO_VAL(EOK, rc);
226
227 resp.clicked = false;
228 ui_pbutton_set_cb(pbutton, &test_pbutton_cb, &resp);
229
230 PCUT_ASSERT_FALSE(pbutton->held);
231 PCUT_ASSERT_FALSE(pbutton->inside);
232
233 ui_pbutton_press(pbutton);
234 PCUT_ASSERT_TRUE(pbutton->held);
235 PCUT_ASSERT_TRUE(pbutton->inside);
236 PCUT_ASSERT_FALSE(resp.clicked);
237
238 ui_pbutton_release(pbutton);
239 PCUT_ASSERT_FALSE(pbutton->held);
240 PCUT_ASSERT_TRUE(pbutton->inside);
241 PCUT_ASSERT_TRUE(resp.clicked);
242
243 ui_pbutton_destroy(pbutton);
244 ui_resource_destroy(resource);
245
246 rc = gfx_context_delete(gc);
247 PCUT_ASSERT_ERRNO_VAL(EOK, rc);
248}
249
250/** Press, leave and release button */
251PCUT_TEST(press_leave_release)
252{
253 errno_t rc;
254 gfx_context_t *gc = NULL;
255 test_gc_t tgc;
256 ui_resource_t *resource = NULL;
257 ui_pbutton_t *pbutton;
258 test_cb_resp_t resp;
259
260 memset(&tgc, 0, sizeof(tgc));
261 rc = gfx_context_new(&ops, &tgc, &gc);
262 PCUT_ASSERT_ERRNO_VAL(EOK, rc);
263
264 rc = ui_resource_create(gc, &resource);
265 PCUT_ASSERT_ERRNO_VAL(EOK, rc);
266 PCUT_ASSERT_NOT_NULL(resource);
267
268 rc = ui_pbutton_create(resource, "Hello", &pbutton);
269 PCUT_ASSERT_ERRNO_VAL(EOK, rc);
270
271 resp.clicked = false;
272 ui_pbutton_set_cb(pbutton, &test_pbutton_cb, &resp);
273
274 PCUT_ASSERT_FALSE(pbutton->held);
275 PCUT_ASSERT_FALSE(pbutton->inside);
276
277 ui_pbutton_press(pbutton);
278 PCUT_ASSERT_TRUE(pbutton->held);
279 PCUT_ASSERT_TRUE(pbutton->inside);
280 PCUT_ASSERT_FALSE(resp.clicked);
281
282 ui_pbutton_leave(pbutton);
283 PCUT_ASSERT_TRUE(pbutton->held);
284 PCUT_ASSERT_FALSE(pbutton->inside);
285 PCUT_ASSERT_FALSE(resp.clicked);
286
287 ui_pbutton_release(pbutton);
288 PCUT_ASSERT_FALSE(pbutton->held);
289 PCUT_ASSERT_FALSE(pbutton->inside);
290 PCUT_ASSERT_FALSE(resp.clicked);
291
292 ui_pbutton_destroy(pbutton);
293 ui_resource_destroy(resource);
294
295 rc = gfx_context_delete(gc);
296 PCUT_ASSERT_ERRNO_VAL(EOK, rc);
297}
298
299/** Press, leave, enter and release button */
300PCUT_TEST(press_leave_enter_release)
301{
302 errno_t rc;
303 gfx_context_t *gc = NULL;
304 test_gc_t tgc;
305 ui_resource_t *resource = NULL;
306 ui_pbutton_t *pbutton;
307 test_cb_resp_t resp;
308
309 memset(&tgc, 0, sizeof(tgc));
310 rc = gfx_context_new(&ops, &tgc, &gc);
311 PCUT_ASSERT_ERRNO_VAL(EOK, rc);
312
313 rc = ui_resource_create(gc, &resource);
314 PCUT_ASSERT_ERRNO_VAL(EOK, rc);
315 PCUT_ASSERT_NOT_NULL(resource);
316
317 rc = ui_pbutton_create(resource, "Hello", &pbutton);
318 PCUT_ASSERT_ERRNO_VAL(EOK, rc);
319
320 resp.clicked = false;
321 ui_pbutton_set_cb(pbutton, &test_pbutton_cb, &resp);
322
323 PCUT_ASSERT_FALSE(pbutton->held);
324 PCUT_ASSERT_FALSE(pbutton->inside);
325
326 ui_pbutton_press(pbutton);
327 PCUT_ASSERT_TRUE(pbutton->held);
328 PCUT_ASSERT_TRUE(pbutton->inside);
329 PCUT_ASSERT_FALSE(resp.clicked);
330
331 ui_pbutton_leave(pbutton);
332 PCUT_ASSERT_TRUE(pbutton->held);
333 PCUT_ASSERT_FALSE(pbutton->inside);
334 PCUT_ASSERT_FALSE(resp.clicked);
335
336 ui_pbutton_enter(pbutton);
337 PCUT_ASSERT_TRUE(pbutton->held);
338 PCUT_ASSERT_TRUE(pbutton->inside);
339 PCUT_ASSERT_FALSE(resp.clicked);
340
341 ui_pbutton_release(pbutton);
342 PCUT_ASSERT_FALSE(pbutton->held);
343 PCUT_ASSERT_TRUE(pbutton->inside);
344 PCUT_ASSERT_TRUE(resp.clicked);
345
346 ui_pbutton_destroy(pbutton);
347 ui_resource_destroy(resource);
348
349 rc = gfx_context_delete(gc);
350 PCUT_ASSERT_ERRNO_VAL(EOK, rc);
351}
352
353/** ui_pos_event() correctly translates POS_PRESS/POS_RELEASE */
354PCUT_TEST(pos_event_press_release)
355{
356 errno_t rc;
357 gfx_context_t *gc = NULL;
358 test_gc_t tgc;
359 ui_resource_t *resource = NULL;
360 ui_pbutton_t *pbutton;
361 ui_evclaim_t claim;
362 pos_event_t event;
363 gfx_rect_t rect;
364
365 memset(&tgc, 0, sizeof(tgc));
366 rc = gfx_context_new(&ops, &tgc, &gc);
367 PCUT_ASSERT_ERRNO_VAL(EOK, rc);
368
369 rc = ui_resource_create(gc, &resource);
370 PCUT_ASSERT_ERRNO_VAL(EOK, rc);
371 PCUT_ASSERT_NOT_NULL(resource);
372
373 rc = ui_pbutton_create(resource, "Hello", &pbutton);
374 PCUT_ASSERT_ERRNO_VAL(EOK, rc);
375
376 PCUT_ASSERT_FALSE(pbutton->held);
377
378 rect.p0.x = 10;
379 rect.p0.y = 20;
380 rect.p1.x = 30;
381 rect.p1.y = 40;
382 ui_pbutton_set_rect(pbutton, &rect);
383
384 /* Press outside is not claimed and does nothing */
385 event.type = POS_PRESS;
386 event.hpos = 9;
387 event.vpos = 20;
388 claim = ui_pbutton_pos_event(pbutton, &event);
389 PCUT_ASSERT_FALSE(pbutton->held);
390 PCUT_ASSERT_EQUALS(ui_unclaimed, claim);
391
392 /* Press inside is claimed and depresses button */
393 event.type = POS_PRESS;
394 event.hpos = 10;
395 event.vpos = 20;
396 claim = ui_pbutton_pos_event(pbutton, &event);
397 PCUT_ASSERT_TRUE(pbutton->held);
398 PCUT_ASSERT_EQUALS(ui_claimed, claim);
399
400 /* Release outside (or anywhere) is claimed and relases button */
401 event.type = POS_RELEASE;
402 event.hpos = 9;
403 event.vpos = 20;
404 claim = ui_pbutton_pos_event(pbutton, &event);
405 PCUT_ASSERT_FALSE(pbutton->held);
406 PCUT_ASSERT_EQUALS(ui_claimed, claim);
407
408 ui_pbutton_destroy(pbutton);
409 ui_resource_destroy(resource);
410
411 rc = gfx_context_delete(gc);
412 PCUT_ASSERT_ERRNO_VAL(EOK, rc);
413}
414
415/** ui_pos_event() correctly translates POS_UPDATE to enter/leave */
416PCUT_TEST(pos_event_enter_leave)
417{
418 errno_t rc;
419 gfx_context_t *gc = NULL;
420 test_gc_t tgc;
421 ui_resource_t *resource = NULL;
422 ui_pbutton_t *pbutton;
423 pos_event_t event;
424 gfx_rect_t rect;
425
426 memset(&tgc, 0, sizeof(tgc));
427 rc = gfx_context_new(&ops, &tgc, &gc);
428 PCUT_ASSERT_ERRNO_VAL(EOK, rc);
429
430 rc = ui_resource_create(gc, &resource);
431 PCUT_ASSERT_ERRNO_VAL(EOK, rc);
432 PCUT_ASSERT_NOT_NULL(resource);
433
434 rc = ui_pbutton_create(resource, "Hello", &pbutton);
435 PCUT_ASSERT_ERRNO_VAL(EOK, rc);
436
437 PCUT_ASSERT_FALSE(pbutton->inside);
438
439 rect.p0.x = 10;
440 rect.p0.y = 20;
441 rect.p1.x = 30;
442 rect.p1.y = 40;
443 ui_pbutton_set_rect(pbutton, &rect);
444
445 /* Moving outside does nothing */
446 event.type = POS_UPDATE;
447 event.hpos = 9;
448 event.vpos = 20;
449 ui_pbutton_pos_event(pbutton, &event);
450 PCUT_ASSERT_FALSE(pbutton->inside);
451
452 /* Moving inside sets inside flag */
453 event.type = POS_UPDATE;
454 event.hpos = 10;
455 event.vpos = 20;
456 ui_pbutton_pos_event(pbutton, &event);
457 PCUT_ASSERT_TRUE(pbutton->inside);
458
459 /* Moving outside clears inside flag */
460 event.type = POS_UPDATE;
461 event.hpos = 9;
462 event.vpos = 20;
463 ui_pbutton_pos_event(pbutton, &event);
464 PCUT_ASSERT_FALSE(pbutton->inside);
465
466 ui_pbutton_destroy(pbutton);
467 ui_resource_destroy(resource);
468
469 rc = gfx_context_delete(gc);
470 PCUT_ASSERT_ERRNO_VAL(EOK, rc);
471}
472
473static errno_t testgc_set_color(void *arg, gfx_color_t *color)
474{
475 (void) arg;
476 (void) color;
477 return EOK;
478}
479
480static errno_t testgc_fill_rect(void *arg, gfx_rect_t *rect)
481{
482 (void) arg;
483 (void) rect;
484 return EOK;
485}
486
487static errno_t testgc_bitmap_create(void *arg, gfx_bitmap_params_t *params,
488 gfx_bitmap_alloc_t *alloc, void **rbm)
489{
490 test_gc_t *tgc = (test_gc_t *) arg;
491 testgc_bitmap_t *tbm;
492
493 tbm = calloc(1, sizeof(testgc_bitmap_t));
494 if (tbm == NULL)
495 return ENOMEM;
496
497 if (alloc == NULL) {
498 tbm->alloc.pitch = (params->rect.p1.x - params->rect.p0.x) *
499 sizeof(uint32_t);
500 tbm->alloc.off0 = 0;
501 tbm->alloc.pixels = calloc(sizeof(uint32_t),
502 (params->rect.p1.x - params->rect.p0.x) *
503 (params->rect.p1.y - params->rect.p0.y));
504 tbm->myalloc = true;
505 if (tbm->alloc.pixels == NULL) {
506 free(tbm);
507 return ENOMEM;
508 }
509 } else {
510 tbm->alloc = *alloc;
511 }
512
513 tbm->tgc = tgc;
514 tgc->bm_created = true;
515 tgc->bm_params = *params;
516 tgc->bm_pixels = tbm->alloc.pixels;
517 *rbm = (void *)tbm;
518 return EOK;
519}
520
521static errno_t testgc_bitmap_destroy(void *bm)
522{
523 testgc_bitmap_t *tbm = (testgc_bitmap_t *)bm;
524 if (tbm->myalloc)
525 free(tbm->alloc.pixels);
526 tbm->tgc->bm_destroyed = true;
527 free(tbm);
528 return EOK;
529}
530
531static errno_t testgc_bitmap_render(void *bm, gfx_rect_t *srect,
532 gfx_coord2_t *offs)
533{
534 testgc_bitmap_t *tbm = (testgc_bitmap_t *)bm;
535 tbm->tgc->bm_rendered = true;
536 tbm->tgc->bm_srect = *srect;
537 tbm->tgc->bm_offs = *offs;
538 return EOK;
539}
540
541static errno_t testgc_bitmap_get_alloc(void *bm, gfx_bitmap_alloc_t *alloc)
542{
543 testgc_bitmap_t *tbm = (testgc_bitmap_t *)bm;
544 *alloc = tbm->alloc;
545 tbm->tgc->bm_got_alloc = true;
546 return EOK;
547}
548
549static void test_pbutton_clicked(ui_pbutton_t *pbutton, void *arg)
550{
551 test_cb_resp_t *resp = (test_cb_resp_t *) arg;
552
553 resp->clicked = true;
554}
555
556PCUT_EXPORT(pbutton);
Note: See TracBrowser for help on using the repository browser.