source: mainline/uspace/lib/ui/src/slider.c@ ef734b7

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

Slider UI control

  • Property mode set to 100644
File size: 10.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 Slider
34 */
35
36#include <errno.h>
37#include <gfx/color.h>
38#include <gfx/context.h>
39#include <gfx/render.h>
40#include <io/pos_event.h>
41#include <stdlib.h>
42#include <str.h>
43#include <ui/control.h>
44#include <ui/paint.h>
45#include <ui/slider.h>
46#include "../private/resource.h"
47#include "../private/slider.h"
48
49/*
50 * The kind reader will appreciate that slider button dimensions 23:15
51 * are chosen such that, after subtracting the frame width (2 times 1),
52 * we get 21:13, which is a good approximation of the golden ratio.
53 */
54enum {
55 /** Slider button width */
56 ui_slider_btn_w = 15,
57 /** Slider button height */
58 ui_slider_btn_h = 23,
59 /** Slider button frame thickness */
60 ui_slider_btn_frame_thickness = 1,
61 /** Slider button bevel width */
62 ui_slider_btn_bevel_width = 2,
63 /** Slider groove width (total) */
64 ui_slider_groove_width = 4,
65 /** Amount by which slider groove bevel extendeds on each side
66 * beyond nominal groove length.
67 */
68 ui_slider_groove_margin = 2
69};
70
71static void ui_slider_ctl_destroy(void *);
72static errno_t ui_slider_ctl_paint(void *);
73static ui_evclaim_t ui_slider_ctl_pos_event(void *, pos_event_t *);
74
75/** Slider control ops */
76ui_control_ops_t ui_slider_ops = {
77 .destroy = ui_slider_ctl_destroy,
78 .paint = ui_slider_ctl_paint,
79 .pos_event = ui_slider_ctl_pos_event
80};
81
82/** Create new slider.
83 *
84 * @param resource UI resource
85 * @param caption Caption
86 * @param rslider Place to store pointer to new slider
87 * @return EOK on success, ENOMEM if out of memory
88 */
89errno_t ui_slider_create(ui_resource_t *resource, const char *caption,
90 ui_slider_t **rslider)
91{
92 ui_slider_t *slider;
93 errno_t rc;
94
95 slider = calloc(1, sizeof(ui_slider_t));
96 if (slider == NULL)
97 return ENOMEM;
98
99 rc = ui_control_new(&ui_slider_ops, (void *) slider,
100 &slider->control);
101 if (rc != EOK) {
102 free(slider);
103 return rc;
104 }
105
106 slider->res = resource;
107 *rslider = slider;
108 return EOK;
109}
110
111/** Destroy slider.
112 *
113 * @param slider Slider or @c NULL
114 */
115void ui_slider_destroy(ui_slider_t *slider)
116{
117 if (slider == NULL)
118 return;
119
120 ui_control_delete(slider->control);
121 free(slider);
122}
123
124/** Get base control from slider.
125 *
126 * @param slider Slider
127 * @return Control
128 */
129ui_control_t *ui_slider_ctl(ui_slider_t *slider)
130{
131 return slider->control;
132}
133
134/** Set slider callbacks.
135 *
136 * @param slider Slider
137 * @param cb Slider callbacks
138 * @param arg Callback argument
139 */
140void ui_slider_set_cb(ui_slider_t *slider, ui_slider_cb_t *cb, void *arg)
141{
142 slider->cb = cb;
143 slider->arg = arg;
144}
145
146/** Set slider rectangle.
147 *
148 * @param slider Slider
149 * @param rect New slider rectangle
150 */
151void ui_slider_set_rect(ui_slider_t *slider, gfx_rect_t *rect)
152{
153 slider->rect = *rect;
154}
155
156/** Paint outer slider frame.
157 *
158 * @param slider Slider
159 * @return EOK on success or an error code
160 */
161static errno_t ui_slider_paint_frame(ui_resource_t *res, gfx_rect_t *rect,
162 gfx_coord_t thickness, gfx_rect_t *inside)
163{
164 gfx_rect_t drect;
165 errno_t rc;
166
167 rc = gfx_set_color(res->gc, res->btn_frame_color);
168 if (rc != EOK)
169 goto error;
170
171 drect.p0.x = rect->p0.x + 1;
172 drect.p0.y = rect->p0.y;
173 drect.p1.x = rect->p1.x - 1;
174 drect.p1.y = rect->p0.y + thickness;
175 rc = gfx_fill_rect(res->gc, &drect);
176 if (rc != EOK)
177 goto error;
178
179 drect.p0.x = rect->p0.x + 1;
180 drect.p0.y = rect->p1.y - thickness;
181 drect.p1.x = rect->p1.x - 1;
182 drect.p1.y = rect->p1.y;
183 rc = gfx_fill_rect(res->gc, &drect);
184 if (rc != EOK)
185 goto error;
186
187 drect.p0.x = rect->p0.x;
188 drect.p0.y = rect->p0.y + 1;
189 drect.p1.x = rect->p0.x + thickness;
190 drect.p1.y = rect->p1.y - 1;
191 rc = gfx_fill_rect(res->gc, &drect);
192 if (rc != EOK)
193 goto error;
194
195 drect.p0.x = rect->p1.x - thickness;
196 drect.p0.y = rect->p0.y + 1;
197 drect.p1.x = rect->p1.x;
198 drect.p1.y = rect->p1.y - 1;
199 rc = gfx_fill_rect(res->gc, &drect);
200 if (rc != EOK)
201 goto error;
202
203 if (inside != NULL) {
204 inside->p0.x = rect->p0.x + thickness;
205 inside->p0.y = rect->p0.y + thickness;
206 inside->p1.x = rect->p1.x - thickness;
207 inside->p1.y = rect->p1.y - thickness;
208 }
209
210 return EOK;
211error:
212 return rc;
213}
214
215/** Paint outset slider bevel.
216 *
217 * @param slider Slider
218 * @return EOK on success or an error code
219 */
220static errno_t ui_slider_paint_outset(ui_slider_t *slider,
221 gfx_rect_t *rect, gfx_rect_t *inside)
222{
223 return ui_paint_bevel(slider->res->gc, rect,
224 slider->res->btn_highlight_color,
225 slider->res->btn_shadow_color, ui_slider_btn_bevel_width, inside);
226}
227
228/** Determine slider button rectagle.
229 *
230 * @param slider Slider
231 */
232static void ui_slider_btn_rect(ui_slider_t *slider, gfx_rect_t *rect)
233{
234 gfx_coord2_t pos;
235
236 pos.x = slider->rect.p0.x + slider->pos;
237 pos.y = slider->rect.p0.y;
238
239 rect->p0.x = pos.x;
240 rect->p0.y = pos.y;
241 rect->p1.x = pos.x + ui_slider_btn_w;
242 rect->p1.y = pos.y + ui_slider_btn_h;
243}
244
245/** Determine slider length.
246 *
247 * @param slider Slider
248 * @return Slider length in pixels
249 */
250gfx_coord_t ui_slider_length(ui_slider_t *slider)
251{
252 gfx_coord2_t dims;
253
254 gfx_rect_dims(&slider->rect, &dims);
255 return dims.x - ui_slider_btn_w;
256}
257
258/** Paint slider.
259 *
260 * @param slider Slider
261 * @return EOK on success or an error code
262 */
263errno_t ui_slider_paint(ui_slider_t *slider)
264{
265 gfx_coord2_t pos;
266 gfx_coord_t length;
267 gfx_rect_t rect;
268 gfx_rect_t brect;
269 gfx_rect_t irect;
270 errno_t rc;
271
272 /* Paint slider groove */
273
274 pos = slider->rect.p0;
275 length = ui_slider_length(slider);
276
277 rect.p0.x = pos.x + ui_slider_btn_w / 2 - ui_slider_groove_margin;
278 rect.p0.y = pos.y + ui_slider_btn_h / 2 - ui_slider_groove_width / 2;
279 rect.p1.x = pos.x + ui_slider_btn_w / 2 + length +
280 ui_slider_groove_margin;
281 rect.p1.y = pos.y + ui_slider_btn_h / 2 + ui_slider_groove_width / 2;
282
283 rc = ui_paint_inset_frame(slider->res, &rect, NULL);
284 if (rc != EOK)
285 goto error;
286
287 /* Paint slider button */
288
289 ui_slider_btn_rect(slider, &rect);
290
291 rc = ui_slider_paint_frame(slider->res, &rect,
292 ui_slider_btn_frame_thickness, &brect);
293 if (rc != EOK)
294 goto error;
295
296 rc = ui_slider_paint_outset(slider, &brect, &irect);
297 if (rc != EOK)
298 goto error;
299
300 rc = gfx_set_color(slider->res->gc, slider->res->btn_face_color);
301 if (rc != EOK)
302 goto error;
303
304 rc = gfx_fill_rect(slider->res->gc, &irect);
305 if (rc != EOK)
306 goto error;
307
308 return EOK;
309error:
310 return rc;
311}
312
313/** Clear slider button.
314 *
315 * @param slider Slider
316 * @return EOK on success or an error code
317 */
318errno_t ui_slider_btn_clear(ui_slider_t *slider)
319{
320 gfx_rect_t rect;
321 errno_t rc;
322
323 ui_slider_btn_rect(slider, &rect);
324
325 rc = gfx_set_color(slider->res->gc, slider->res->wnd_face_color);
326 if (rc != EOK)
327 goto error;
328
329 rc = gfx_fill_rect(slider->res->gc, &rect);
330 if (rc != EOK)
331 goto error;
332
333 return EOK;
334error:
335 return rc;
336}
337
338/** Press down slider.
339 *
340 * @param slider Slider
341 * @param pos Pointer position
342 */
343void ui_slider_press(ui_slider_t *slider, gfx_coord2_t *pos)
344{
345 if (slider->held)
346 return;
347
348 slider->held = true;
349 slider->press_pos = *pos;
350 slider->last_pos = slider->pos;
351
352 (void) ui_slider_paint(slider);
353}
354
355/** Release slider.
356 *
357 * @param slider Slider
358 * @param pos Pointer position
359 */
360void ui_slider_release(ui_slider_t *slider, gfx_coord2_t *pos)
361{
362 if (!slider->held)
363 return;
364
365 ui_slider_update(slider, pos);
366 slider->held = false;
367}
368
369/** Pointer moved.
370 *
371 * @param slider Slider
372 * @param pos New pointer position
373 */
374void ui_slider_update(ui_slider_t *slider, gfx_coord2_t *pos)
375{
376 gfx_coord_t spos;
377 gfx_coord_t length;
378
379 if (slider->held) {
380 spos = slider->last_pos + pos->x - slider->press_pos.x;
381 length = ui_slider_length(slider);
382 if (spos < 0)
383 spos = 0;
384 if (spos > length)
385 spos = length;
386
387 if (spos != slider->pos) {
388 (void) ui_slider_btn_clear(slider);
389 slider->pos = spos;
390 (void) ui_slider_paint(slider);
391 ui_slider_moved(slider, spos);
392 }
393 }
394}
395
396/** Slider was moved.
397 *
398 * @param slider Slider
399 */
400void ui_slider_moved(ui_slider_t *slider, gfx_coord_t pos)
401{
402 if (slider->cb != NULL && slider->cb->moved != NULL)
403 slider->cb->moved(slider, slider->arg, pos);
404}
405
406/** Handle slider position event.
407 *
408 * @param slider Slider
409 * @param pos_event Position event
410 * @return @c ui_claimed iff the event is claimed
411 */
412ui_evclaim_t ui_slider_pos_event(ui_slider_t *slider, pos_event_t *event)
413{
414 gfx_coord2_t pos;
415 gfx_rect_t rect;
416
417 pos.x = event->hpos;
418 pos.y = event->vpos;
419
420 switch (event->type) {
421 case POS_PRESS:
422 ui_slider_btn_rect(slider, &rect);
423 if (gfx_pix_inside_rect(&pos, &rect)) {
424 ui_slider_press(slider, &pos);
425 slider->press_pos = pos;
426 return ui_claimed;
427 }
428 break;
429 case POS_RELEASE:
430 if (slider->held) {
431 ui_slider_release(slider, &pos);
432 return ui_claimed;
433 }
434 break;
435 case POS_UPDATE:
436 ui_slider_update(slider, &pos);
437 break;
438 }
439
440 return ui_unclaimed;
441}
442
443/** Destroy slider control.
444 *
445 * @param arg Argument (ui_slider_t *)
446 */
447void ui_slider_ctl_destroy(void *arg)
448{
449 ui_slider_t *slider = (ui_slider_t *) arg;
450
451 ui_slider_destroy(slider);
452}
453
454/** Paint slider control.
455 *
456 * @param arg Argument (ui_slider_t *)
457 * @return EOK on success or an error code
458 */
459errno_t ui_slider_ctl_paint(void *arg)
460{
461 ui_slider_t *slider = (ui_slider_t *) arg;
462
463 return ui_slider_paint(slider);
464}
465
466/** Handle slider control position event.
467 *
468 * @param arg Argument (ui_slider_t *)
469 * @param pos_event Position event
470 * @return @c ui_claimed iff the event is claimed
471 */
472ui_evclaim_t ui_slider_ctl_pos_event(void *arg, pos_event_t *event)
473{
474 ui_slider_t *slider = (ui_slider_t *) arg;
475
476 return ui_slider_pos_event(slider, event);
477}
478
479/** @}
480 */
Note: See TracBrowser for help on using the repository browser.