1 /*
2 * Copyright (c) 2018-2019 Hanspeter Portner (dev@open-music-kontrollers.ch)
3 *
4 * This is free software: you can redistribute it and/or modify
5 * it under the terms of the Artistic License 2.0 as published by
6 * The Perl Foundation.
7 *
8 * This source is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * Artistic License 2.0 for more details.
12 *
13 * You should have received a copy of the Artistic License 2.0
14 * along the source as a COPYING file. If not, obtain it from
15 * http://www.perlfoundation.org/artistic_license_2_0.
16 */
17
18 #include "base_internal.h"
19
20 typedef struct _d2tk_atom_body_scroll_t d2tk_atom_body_scroll_t;
21
22 struct _d2tk_atom_body_scroll_t {
23 float offset [2];
24 };
25
26 struct _d2tk_scrollbar_t {
27 d2tk_id_t id;
28 d2tk_flag_t flags;
29 int32_t max [2];
30 int32_t num [2];
31 d2tk_atom_body_scroll_t *atom_body;
32 const d2tk_rect_t *rect;
33 d2tk_rect_t sub;
34 };
35
36 const size_t d2tk_atom_body_scroll_sz = sizeof(d2tk_atom_body_scroll_t);
37 const size_t d2tk_scrollbar_sz = sizeof(d2tk_scrollbar_t);
38
39 D2TK_API d2tk_scrollbar_t *
d2tk_scrollbar_begin(d2tk_base_t * base,const d2tk_rect_t * rect,d2tk_id_t id,d2tk_flag_t flags,const uint32_t max[2],const uint32_t num[2],d2tk_scrollbar_t * scrollbar)40 d2tk_scrollbar_begin(d2tk_base_t *base, const d2tk_rect_t *rect, d2tk_id_t id,
41 d2tk_flag_t flags, const uint32_t max [2], const uint32_t num [2],
42 d2tk_scrollbar_t *scrollbar)
43 {
44 scrollbar->id = id;
45 scrollbar->flags = flags;
46 scrollbar->max[0] = max[0];
47 scrollbar->max[1] = max[1];
48 scrollbar->num[0] = num[0];
49 scrollbar->num[1] = num[1];
50 scrollbar->rect = rect;
51 scrollbar->sub = *rect;
52 scrollbar->atom_body = _d2tk_base_get_atom(base, id, D2TK_ATOM_SCROLL, NULL);
53
54 const d2tk_coord_t s = 10; //FIXME
55
56 if(flags & D2TK_FLAG_SCROLL_X)
57 {
58 scrollbar->sub.h -= s;
59 }
60
61 if(flags & D2TK_FLAG_SCROLL_Y)
62 {
63 scrollbar->sub.w -= s;
64 }
65
66 return scrollbar;
67 }
68
69 D2TK_API bool
d2tk_scrollbar_not_end(d2tk_scrollbar_t * scrollbar)70 d2tk_scrollbar_not_end(d2tk_scrollbar_t *scrollbar)
71 {
72 return scrollbar ? true : false;
73 }
74
75 static void
_d2tk_draw_scrollbar(d2tk_core_t * core,d2tk_state_t hstate,d2tk_state_t vstate,const d2tk_rect_t * hbar,const d2tk_rect_t * vbar,const d2tk_style_t * style,d2tk_flag_t flags)76 _d2tk_draw_scrollbar(d2tk_core_t *core, d2tk_state_t hstate, d2tk_state_t vstate,
77 const d2tk_rect_t *hbar, const d2tk_rect_t *vbar, const d2tk_style_t *style,
78 d2tk_flag_t flags)
79 {
80 const d2tk_hash_dict_t dict [] = {
81 { &hstate, sizeof(d2tk_state_t) },
82 { &vstate, sizeof(d2tk_state_t) },
83 { hbar, sizeof(d2tk_rect_t) },
84 { vbar, sizeof(d2tk_rect_t) },
85 { style, sizeof(d2tk_style_t) },
86 { &flags, sizeof(d2tk_flag_t) },
87 { NULL, 0 }
88 };
89 const uint64_t hash = d2tk_hash_dict(dict);
90
91 D2TK_CORE_WIDGET(core, hash, widget)
92 {
93 if(flags & D2TK_FLAG_SCROLL_X)
94 {
95 d2tk_triple_t triple = D2TK_TRIPLE_NONE;
96
97 if(d2tk_state_is_active(hstate))
98 {
99 triple |= D2TK_TRIPLE_ACTIVE;
100 }
101
102 if(d2tk_state_is_hot(hstate))
103 {
104 triple |= D2TK_TRIPLE_HOT;
105 }
106
107 if(d2tk_state_is_focused(hstate))
108 {
109 triple |= D2TK_TRIPLE_FOCUS;
110 }
111
112 const size_t ref = d2tk_core_bbox_push(core, true, hbar);
113
114 d2tk_core_begin_path(core);
115 d2tk_core_rounded_rect(core, hbar, style->rounding);
116 d2tk_core_color(core, style->fill_color[triple]);
117 d2tk_core_stroke_width(core, 0);
118 d2tk_core_fill(core);
119
120 d2tk_core_begin_path(core);
121 d2tk_core_rounded_rect(core, hbar, style->rounding);
122 d2tk_core_color(core, style->stroke_color[triple]);
123 d2tk_core_stroke_width(core, style->border_width);
124 d2tk_core_stroke(core);
125
126 d2tk_core_bbox_pop(core, ref);\
127 }
128
129 if(flags & D2TK_FLAG_SCROLL_Y)
130 {
131 d2tk_triple_t triple = D2TK_TRIPLE_NONE;
132
133 if(d2tk_state_is_active(vstate))
134 {
135 triple |= D2TK_TRIPLE_ACTIVE;
136 }
137
138 if(d2tk_state_is_hot(vstate))
139 {
140 triple |= D2TK_TRIPLE_HOT;
141 }
142
143 if(d2tk_state_is_focused(vstate))
144 {
145 triple |= D2TK_TRIPLE_FOCUS;
146 }
147
148 const size_t ref = d2tk_core_bbox_push(core, true, vbar);
149
150 d2tk_core_begin_path(core);
151 d2tk_core_rounded_rect(core, vbar, style->rounding);
152 d2tk_core_color(core, style->fill_color[triple]);
153 d2tk_core_stroke_width(core, 0);
154 d2tk_core_fill(core);
155
156 d2tk_core_begin_path(core);
157 d2tk_core_rounded_rect(core, vbar, style->rounding);
158 d2tk_core_color(core, style->stroke_color[triple]);
159 d2tk_core_stroke_width(core, style->border_width);
160 d2tk_core_stroke(core);
161
162 d2tk_core_bbox_pop(core, ref);
163 }
164 }
165 }
166
167 D2TK_API d2tk_scrollbar_t *
d2tk_scrollbar_next(d2tk_base_t * base,d2tk_scrollbar_t * scrollbar)168 d2tk_scrollbar_next(d2tk_base_t *base, d2tk_scrollbar_t *scrollbar)
169 {
170 const d2tk_style_t *style = d2tk_base_get_style(base);
171 const d2tk_coord_t s = 10; //FIXME
172 const d2tk_coord_t s2 = s*2;
173
174 const d2tk_id_t id = scrollbar->id;
175 d2tk_flag_t flags = scrollbar->flags;
176 const int32_t *max = scrollbar->max;
177 const int32_t *num = scrollbar->num;
178 const d2tk_rect_t *rect = scrollbar->rect;
179 float *scroll = scrollbar->atom_body->offset;
180
181 const int32_t rel_max [2] = {
182 max[0] - num[0],
183 max[1] - num[1]
184 };
185 d2tk_rect_t hbar = {
186 .x = rect->x,
187 .y = rect->y + rect->h - s,
188 .w = rect->w,
189 .h = s
190 };
191 d2tk_rect_t vbar = {
192 .x = rect->x + rect->w - s,
193 .y = rect->y,
194 .w = s,
195 .h = rect->h
196 };
197 d2tk_rect_t sub = *rect;
198 d2tk_state_t hstate = D2TK_STATE_NONE;
199 d2tk_state_t vstate = D2TK_STATE_NONE;
200
201 const d2tk_id_t hid = (1 << 24) | id;
202 const d2tk_id_t vid = (2 << 24) | id;
203
204 if(max[0] < num[0])
205 {
206 flags &= ~D2TK_FLAG_SCROLL_X;
207 }
208
209 if(max[1] < num[1])
210 {
211 flags &= ~D2TK_FLAG_SCROLL_Y;
212 }
213
214 if(flags & D2TK_FLAG_SCROLL_X)
215 {
216 sub.h -= s;
217
218 hstate |= d2tk_base_is_active_hot(base, hid, &hbar, D2TK_FLAG_SCROLL_X);
219 }
220
221 if(flags & D2TK_FLAG_SCROLL_Y)
222 {
223 sub.w -= s;
224
225 vstate |= d2tk_base_is_active_hot(base, vid, &vbar, D2TK_FLAG_SCROLL_Y);
226 }
227
228 if(d2tk_base_is_hit(base, &sub))
229 {
230 if(flags & D2TK_FLAG_SCROLL_X)
231 {
232 hstate |= _d2tk_base_is_active_hot_horizontal_scroll(base);
233 }
234
235 if(flags & D2TK_FLAG_SCROLL_Y)
236 {
237 vstate |= _d2tk_base_is_active_hot_vertical_scroll(base);
238 }
239 }
240
241 const float old_scroll [2] = {
242 scroll[0],
243 scroll[1]
244 };
245
246 if(flags & D2TK_FLAG_SCROLL_X)
247 {
248 int32_t dw = hbar.w * num[0] / max[0];
249 d2tk_clip_int32(s2, &dw, dw);
250 const float w = (float)(hbar.w - dw) / rel_max[0];
251
252 if(d2tk_state_is_scroll_right(hstate))
253 {
254 scroll[0] += base->scroll.odx;
255 }
256 else if(d2tk_state_is_scroll_left(hstate))
257 {
258 scroll[0] += base->scroll.odx;
259 }
260 else if(d2tk_state_is_motion(hstate))
261 {
262 const float adx = base->mouse.dx;
263
264 scroll[0] += adx / w;
265 }
266
267 // always do clipping, as max may have changed in due course
268 d2tk_clip_float(0, &scroll[0], rel_max[0]);
269
270 hbar.w = dw;
271 hbar.x += scroll[0]*w;
272 }
273
274 if(flags & D2TK_FLAG_SCROLL_Y)
275 {
276 int32_t dh = vbar.h * num[1] / max[1];
277 d2tk_clip_int32(s2, &dh, dh);
278 const float h = (float)(vbar.h - dh) / rel_max[1];
279
280 if(d2tk_state_is_scroll_down(vstate))
281 {
282 scroll[1] -= base->scroll.ody;
283 }
284 else if(d2tk_state_is_scroll_up(vstate))
285 {
286 scroll[1] -= base->scroll.ody;
287 }
288 else if(d2tk_state_is_motion(vstate))
289 {
290 const float ady = base->mouse.dy;
291
292 scroll[1] += ady / h;
293 }
294
295 // always do clipping, as max may have changed in due course
296 d2tk_clip_float(0, &scroll[1], rel_max[1]);
297
298 vbar.h = dh;
299 vbar.y += scroll[1]*h;
300 }
301
302 if( (old_scroll[0] != scroll[0]) || (old_scroll[1] != scroll[1]) )
303 {
304 d2tk_base_set_again(base);
305 }
306
307 d2tk_core_t *core = base->core;
308
309 _d2tk_draw_scrollbar(core, hstate, vstate, &hbar, &vbar, style, flags);
310
311 //return state; //FIXME
312 return NULL;
313 }
314
315 D2TK_API float
d2tk_scrollbar_get_offset_y(d2tk_scrollbar_t * scrollbar)316 d2tk_scrollbar_get_offset_y(d2tk_scrollbar_t *scrollbar)
317 {
318 return scrollbar->atom_body->offset[1];
319 }
320
321 D2TK_API float
d2tk_scrollbar_get_offset_x(d2tk_scrollbar_t * scrollbar)322 d2tk_scrollbar_get_offset_x(d2tk_scrollbar_t *scrollbar)
323 {
324 return scrollbar->atom_body->offset[0];
325 }
326
327 D2TK_API const d2tk_rect_t *
d2tk_scrollbar_get_rect(d2tk_scrollbar_t * scrollbar)328 d2tk_scrollbar_get_rect(d2tk_scrollbar_t *scrollbar)
329 {
330 return &scrollbar->sub;
331 }
332