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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since f03d1308 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: 8.7 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/paint.h>
45#include <ui/pbutton.h>
46#include "../private/pbutton.h"
47#include "../private/resource.h"
48
49/** Caption movement when button is pressed down */
50enum {
51 ui_pb_press_dx = 1,
52 ui_pb_press_dy = 1
53};
54
55/** Create new push button.
56 *
57 * @param resource UI resource
58 * @param caption Caption
59 * @param rpbutton Place to store pointer to new push button
60 * @return EOK on success, ENOMEM if out of memory
61 */
62errno_t ui_pbutton_create(ui_resource_t *resource, const char *caption,
63 ui_pbutton_t **rpbutton)
64{
65 ui_pbutton_t *pbutton;
66
67 pbutton = calloc(1, sizeof(ui_pbutton_t));
68 if (pbutton == NULL)
69 return ENOMEM;
70
71 pbutton->caption = str_dup(caption);
72 if (pbutton->caption == NULL) {
73 free(pbutton);
74 return ENOMEM;
75 }
76
77 pbutton->res = resource;
78 *rpbutton = pbutton;
79 return EOK;
80}
81
82/** Destroy push button.
83 *
84 * @param pbutton Push button or @c NULL
85 */
86void ui_pbutton_destroy(ui_pbutton_t *pbutton)
87{
88 if (pbutton == NULL)
89 return;
90
91 free(pbutton);
92}
93
94/** Set push button callbacks.
95 *
96 * @param pbutton Push button
97 * @param cb Push button callbacks
98 * @param arg Callback argument
99 */
100void ui_pbutton_set_cb(ui_pbutton_t *pbutton, ui_pbutton_cb_t *cb, void *arg)
101{
102 pbutton->cb = cb;
103 pbutton->arg = arg;
104}
105
106/** Set button rectangle.
107 *
108 * @param pbutton Button
109 * @param rect New button rectangle
110 */
111void ui_pbutton_set_rect(ui_pbutton_t *pbutton, gfx_rect_t *rect)
112{
113 pbutton->rect = *rect;
114}
115
116/** Set default flag.
117 *
118 * Default button is the one activated by Enter key, it is marked
119 * by thicker frame.
120 *
121 * @param pbutton Button
122 * @param isdefault @c true iff button is default
123 */
124void ui_pbutton_set_default(ui_pbutton_t *pbutton, bool isdefault)
125{
126 pbutton->isdefault = isdefault;
127}
128
129/** Paint outer button frame.
130 *
131 * @param pbutton Push button
132 * @return EOK on success or an error code
133 */
134static errno_t ui_pbutton_paint_frame(ui_pbutton_t *pbutton)
135{
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_set_color(pbutton->res->gc, pbutton->res->btn_frame_color);
143 if (rc != EOK)
144 goto error;
145
146 rect.p0.x = pbutton->rect.p0.x + 1;
147 rect.p0.y = pbutton->rect.p0.y;
148 rect.p1.x = pbutton->rect.p1.x - 1;
149 rect.p1.y = pbutton->rect.p0.y + thickness;
150 rc = gfx_fill_rect(pbutton->res->gc, &rect);
151 if (rc != EOK)
152 goto error;
153
154 rect.p0.x = pbutton->rect.p0.x + 1;
155 rect.p0.y = pbutton->rect.p1.y - thickness;
156 rect.p1.x = pbutton->rect.p1.x - 1;
157 rect.p1.y = pbutton->rect.p1.y;
158 rc = gfx_fill_rect(pbutton->res->gc, &rect);
159 if (rc != EOK)
160 goto error;
161
162 rect.p0.x = pbutton->rect.p0.x;
163 rect.p0.y = pbutton->rect.p0.y + 1;
164 rect.p1.x = pbutton->rect.p0.x + thickness;
165 rect.p1.y = pbutton->rect.p1.y - 1;
166 rc = gfx_fill_rect(pbutton->res->gc, &rect);
167 if (rc != EOK)
168 goto error;
169
170 rect.p0.x = pbutton->rect.p1.x - thickness;
171 rect.p0.y = pbutton->rect.p0.y + 1;
172 rect.p1.x = pbutton->rect.p1.x;
173 rect.p1.y = pbutton->rect.p1.y - 1;
174 rc = gfx_fill_rect(pbutton->res->gc, &rect);
175 if (rc != EOK)
176 goto error;
177
178 return EOK;
179error:
180 return rc;
181}
182
183/** Paint outset button bevel.
184 *
185 * @param pbutton Push button
186 * @return EOK on success or an error code
187 */
188static errno_t ui_pbutton_paint_outset(ui_pbutton_t *pbutton,
189 gfx_rect_t *rect)
190{
191 return ui_paint_bevel(pbutton->res->gc, rect,
192 pbutton->res->btn_highlight_color,
193 pbutton->res->btn_shadow_color, 2, NULL);
194}
195
196/** Paint inset button bevel.
197 *
198 * @param pbutton Push button
199 * @return EOK on success or an error code
200 */
201static errno_t ui_pbutton_paint_inset(ui_pbutton_t *pbutton,
202 gfx_rect_t *rect)
203{
204 return ui_paint_bevel(pbutton->res->gc, rect,
205 pbutton->res->btn_shadow_color,
206 pbutton->res->btn_face_color, 2, NULL);
207}
208
209/** Paint push button.
210 *
211 * @param pbutton Push button
212 * @return EOK on success or an error code
213 */
214errno_t ui_pbutton_paint(ui_pbutton_t *pbutton)
215{
216 gfx_coord2_t pos;
217 gfx_text_fmt_t fmt;
218 gfx_rect_t rect;
219 gfx_coord_t thickness;
220 bool depressed;
221 errno_t rc;
222
223 thickness = pbutton->isdefault ? 2 : 1;
224 depressed = pbutton->held && pbutton->inside;
225
226 rect.p0.x = pbutton->rect.p0.x + thickness;
227 rect.p0.y = pbutton->rect.p0.y + thickness;
228 rect.p1.x = pbutton->rect.p1.x - thickness;
229 rect.p1.y = pbutton->rect.p1.y - thickness;
230
231 rc = gfx_set_color(pbutton->res->gc, pbutton->res->btn_face_color);
232 if (rc != EOK)
233 goto error;
234
235 rc = gfx_fill_rect(pbutton->res->gc, &rect);
236 if (rc != EOK)
237 goto error;
238
239 rc = gfx_set_color(pbutton->res->gc, pbutton->res->btn_text_color);
240 if (rc != EOK)
241 goto error;
242
243 /* Center of button rectangle */
244 pos.x = (rect.p0.x + rect.p1.x) / 2;
245 pos.y = (rect.p0.y + rect.p1.y) / 2;
246
247 if (depressed) {
248 pos.x += ui_pb_press_dx;
249 pos.y += ui_pb_press_dy;
250 }
251
252 gfx_text_fmt_init(&fmt);
253 fmt.halign = gfx_halign_center;
254 fmt.valign = gfx_valign_center;
255
256 rc = gfx_puttext(pbutton->res->font, &pos, &fmt, pbutton->caption);
257 if (rc != EOK)
258 goto error;
259
260 rc = ui_pbutton_paint_frame(pbutton);
261 if (rc != EOK)
262 goto error;
263
264 if (depressed) {
265 rc = ui_pbutton_paint_inset(pbutton, &rect);
266 if (rc != EOK)
267 goto error;
268 } else {
269 rc = ui_pbutton_paint_outset(pbutton, &rect);
270 if (rc != EOK)
271 goto error;
272 }
273
274 return EOK;
275error:
276 return rc;
277}
278
279/** Press down button.
280 *
281 * @param pbutton Push button
282 */
283void ui_pbutton_press(ui_pbutton_t *pbutton)
284{
285 if (pbutton->held)
286 return;
287
288 pbutton->inside = true;
289 pbutton->held = true;
290 (void) ui_pbutton_paint(pbutton);
291}
292
293/** Release button.
294 *
295 * @param pbutton Push button
296 */
297void ui_pbutton_release(ui_pbutton_t *pbutton)
298{
299 if (!pbutton->held)
300 return;
301
302 pbutton->held = false;
303
304 if (pbutton->inside) {
305 (void) ui_pbutton_paint(pbutton);
306 ui_pbutton_clicked(pbutton);
307 }
308}
309
310/** Pointer entered button.
311 *
312 * @param pbutton Push button
313 */
314void ui_pbutton_enter(ui_pbutton_t *pbutton)
315{
316 if (pbutton->inside)
317 return;
318
319 pbutton->inside = true;
320 if (pbutton->held)
321 (void) ui_pbutton_paint(pbutton);
322}
323
324/** Pointer left button.
325 *
326 * @param pbutton Push button
327 */
328void ui_pbutton_leave(ui_pbutton_t *pbutton)
329{
330 if (!pbutton->inside)
331 return;
332
333 pbutton->inside = false;
334 if (pbutton->held)
335 (void) ui_pbutton_paint(pbutton);
336}
337
338/** Button was clicked.
339 *
340 * @param pbutton Push button
341 */
342void ui_pbutton_clicked(ui_pbutton_t *pbutton)
343{
344 if (pbutton->cb != NULL && pbutton->cb->clicked != NULL)
345 pbutton->cb->clicked(pbutton, pbutton->arg);
346}
347
348/** Handle push button position event.
349 *
350 * @param pbutton Push button
351 * @param pos_event Position event
352 * @return @c ui_claimed iff the event is claimed
353 */
354ui_evclaim_t ui_pbutton_pos_event(ui_pbutton_t *pbutton, pos_event_t *event)
355{
356 gfx_coord2_t pos;
357 bool inside;
358
359 pos.x = event->hpos;
360 pos.y = event->vpos;
361
362 inside = gfx_pix_inside_rect(&pos, &pbutton->rect);
363
364 switch (event->type) {
365 case POS_PRESS:
366 if (inside) {
367 ui_pbutton_press(pbutton);
368 return ui_claimed;
369 }
370 break;
371 case POS_RELEASE:
372 if (pbutton->held) {
373 ui_pbutton_release(pbutton);
374 return ui_claimed;
375 }
376 break;
377 case POS_UPDATE:
378 if (inside && !pbutton->inside) {
379 ui_pbutton_enter(pbutton);
380 return ui_claimed;
381 } else if (!inside && pbutton->inside) {
382 ui_pbutton_leave(pbutton);
383 }
384 break;
385 }
386
387 return ui_unclaimed;
388}
389
390/** @}
391 */
Note: See TracBrowser for help on using the repository browser.