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