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

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

Client-side UI rendering

It is possible to turn on and off and if turned on, one can also
enable or disable window double buffering (currently both options
are build-time).

  • 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 rc = gfx_update(slider->res->gc);
309 if (rc != EOK)
310 goto error;
311
312 return EOK;
313error:
314 return rc;
315}
316
317/** Clear slider button.
318 *
319 * @param slider Slider
320 * @return EOK on success or an error code
321 */
322errno_t ui_slider_btn_clear(ui_slider_t *slider)
323{
324 gfx_rect_t rect;
325 errno_t rc;
326
327 ui_slider_btn_rect(slider, &rect);
328
329 rc = gfx_set_color(slider->res->gc, slider->res->wnd_face_color);
330 if (rc != EOK)
331 goto error;
332
333 rc = gfx_fill_rect(slider->res->gc, &rect);
334 if (rc != EOK)
335 goto error;
336
337 return EOK;
338error:
339 return rc;
340}
341
342/** Press down slider.
343 *
344 * @param slider Slider
345 * @param pos Pointer position
346 */
347void ui_slider_press(ui_slider_t *slider, gfx_coord2_t *pos)
348{
349 if (slider->held)
350 return;
351
352 slider->held = true;
353 slider->press_pos = *pos;
354 slider->last_pos = slider->pos;
355
356 (void) ui_slider_paint(slider);
357}
358
359/** Release slider.
360 *
361 * @param slider Slider
362 * @param pos Pointer position
363 */
364void ui_slider_release(ui_slider_t *slider, gfx_coord2_t *pos)
365{
366 if (!slider->held)
367 return;
368
369 ui_slider_update(slider, pos);
370 slider->held = false;
371}
372
373/** Pointer moved.
374 *
375 * @param slider Slider
376 * @param pos New pointer position
377 */
378void ui_slider_update(ui_slider_t *slider, gfx_coord2_t *pos)
379{
380 gfx_coord_t spos;
381 gfx_coord_t length;
382
383 if (slider->held) {
384 spos = slider->last_pos + pos->x - slider->press_pos.x;
385 length = ui_slider_length(slider);
386 if (spos < 0)
387 spos = 0;
388 if (spos > length)
389 spos = length;
390
391 if (spos != slider->pos) {
392 (void) ui_slider_btn_clear(slider);
393 slider->pos = spos;
394 (void) ui_slider_paint(slider);
395 ui_slider_moved(slider, spos);
396 }
397 }
398}
399
400/** Slider was moved.
401 *
402 * @param slider Slider
403 */
404void ui_slider_moved(ui_slider_t *slider, gfx_coord_t pos)
405{
406 if (slider->cb != NULL && slider->cb->moved != NULL)
407 slider->cb->moved(slider, slider->arg, pos);
408}
409
410/** Handle slider position event.
411 *
412 * @param slider Slider
413 * @param pos_event Position event
414 * @return @c ui_claimed iff the event is claimed
415 */
416ui_evclaim_t ui_slider_pos_event(ui_slider_t *slider, pos_event_t *event)
417{
418 gfx_coord2_t pos;
419 gfx_rect_t rect;
420
421 pos.x = event->hpos;
422 pos.y = event->vpos;
423
424 switch (event->type) {
425 case POS_PRESS:
426 ui_slider_btn_rect(slider, &rect);
427 if (gfx_pix_inside_rect(&pos, &rect)) {
428 ui_slider_press(slider, &pos);
429 slider->press_pos = pos;
430 return ui_claimed;
431 }
432 break;
433 case POS_RELEASE:
434 if (slider->held) {
435 ui_slider_release(slider, &pos);
436 return ui_claimed;
437 }
438 break;
439 case POS_UPDATE:
440 ui_slider_update(slider, &pos);
441 break;
442 }
443
444 return ui_unclaimed;
445}
446
447/** Destroy slider control.
448 *
449 * @param arg Argument (ui_slider_t *)
450 */
451void ui_slider_ctl_destroy(void *arg)
452{
453 ui_slider_t *slider = (ui_slider_t *) arg;
454
455 ui_slider_destroy(slider);
456}
457
458/** Paint slider control.
459 *
460 * @param arg Argument (ui_slider_t *)
461 * @return EOK on success or an error code
462 */
463errno_t ui_slider_ctl_paint(void *arg)
464{
465 ui_slider_t *slider = (ui_slider_t *) arg;
466
467 return ui_slider_paint(slider);
468}
469
470/** Handle slider control position event.
471 *
472 * @param arg Argument (ui_slider_t *)
473 * @param pos_event Position event
474 * @return @c ui_claimed iff the event is claimed
475 */
476ui_evclaim_t ui_slider_ctl_pos_event(void *arg, pos_event_t *event)
477{
478 ui_slider_t *slider = (ui_slider_t *) arg;
479
480 return ui_slider_pos_event(slider, event);
481}
482
483/** @}
484 */
Note: See TracBrowser for help on using the repository browser.