Rev 2105 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1787 | jag | 1 | /* |
1799 | jag | 2 | * spinner2.h |
2302 | jag | 3 | * DIN Is Noise is copyright (c) 2006-2025 Jagannathan Sampath |
1787 | jag | 4 | * For more information, please visit http://dinisnoise.org/ |
5 | */ |
||
6 | |||
7 | |||
8 | #ifndef __spinner22 |
||
9 | #define __spinner22 |
||
10 | |||
11 | #include "input.h" |
||
12 | #include "font.h" |
||
13 | #include "widget.h" |
||
14 | #include "label.h" |
||
15 | #include "arrow_button.h" |
||
16 | #include "checkbutton.h" |
||
17 | #include "field.h" |
||
18 | #include "utils.h" |
||
19 | #include "mouse_slider.h" |
||
20 | #include "tokenizer.h" |
||
21 | |||
22 | #include <string> |
||
23 | #include <typeinfo> |
||
24 | |||
25 | extern void abort_selectors (); |
||
26 | extern int SPACING; |
||
27 | extern const char spc; |
||
28 | extern char BUFFER[]; |
||
29 | |||
30 | extern button& detach_from_menu (widget** wa, int n, int posx, int posy); |
||
31 | extern void attach_to_menu (widget** wa, int n); |
||
32 | |||
33 | template <typename T> struct spinner2 : widget, state_listener, change_listener<field>, typing_listener, mouse_slider_listener, nullt { |
||
34 | |||
35 | template <typename Q> struct re_pre_lis : click_listener { |
||
36 | spinner2<Q>& sp; |
||
37 | click_listener* pre; |
||
38 | re_pre_lis (spinner2<Q>& _sp, click_listener* _pre) : sp (_sp), pre (_pre) {} |
||
39 | void clicked (button& b) { |
||
40 | widget* w[] = {&sp}; |
||
41 | attach_to_menu (w, 1); |
||
42 | LISTEN (sp.dec, pre) |
||
43 | LISTEN (sp.inc, pre) |
||
44 | if (sp.null) sp.set_value (0); |
||
45 | } |
||
46 | }; |
||
47 | |||
48 | template <typename Q> struct pre_lis : click_listener { |
||
49 | spinner2<Q>& sp; |
||
50 | click_listener *decl, *incl; |
||
51 | pre_lis (spinner2<Q>& _sp, click_listener* _decl, click_listener* _incl) : sp (_sp), decl (_decl), incl (_incl) {} |
||
52 | void clicked (button& b) { |
||
53 | sp.dec.set_listener (decl); |
||
54 | sp.inc.set_listener (incl); |
||
55 | widget* wa [] = {&sp}; |
||
56 | button& b_close = detach_from_menu (wa, 1, sp.dec.posx, sp.lbl.posy); |
||
57 | LISTEN (b_close, &sp.reprel); |
||
58 | b.call_listener (); |
||
59 | } |
||
60 | }; |
||
61 | |||
62 | template <typename Q> struct dec_lis : click_listener { |
||
63 | spinner2<Q>& sp; |
||
64 | dec_lis (spinner2<Q>& _sp) : sp (_sp) {} |
||
65 | void clicked (button& b) {sp.decrease ();} |
||
66 | }; |
||
67 | |||
68 | template <typename Q> struct inc_lis : click_listener { |
||
69 | spinner2<Q>& sp; |
||
70 | inc_lis (spinner2<Q>& _sp) : sp (_sp) {} |
||
71 | void clicked (button& b) {sp.increase ();} |
||
72 | }; |
||
73 | |||
74 | template <typename Q> struct more_lis : click_listener { |
||
75 | spinner2<Q>& sp; |
||
76 | more_lis (spinner2<Q>& _sp) : sp (_sp) {} |
||
77 | void clicked (button& b) {sp.toggle_delta();} |
||
78 | }; |
||
79 | |||
80 | template <typename Q> struct delta_lis : change_listener<field> { |
||
81 | spinner2<Q>* sp; |
||
82 | delta_lis (spinner2<Q>* _sp = 0) : sp (_sp) {} |
||
83 | void changed (field& f) { |
||
84 | sp->delta0 = sp->delta = f; |
||
85 | sp->call_listener (1, f); |
||
86 | } |
||
87 | }; |
||
88 | |||
89 | template <typename Q> struct variance_lis : change_listener<field> { |
||
90 | spinner2<Q>* sp; |
||
91 | variance_lis (spinner2<Q>* _sp = 0) : sp (_sp) {} |
||
92 | void changed (field& f) { |
||
93 | tokenizer tz (f.text); |
||
94 | float mn, mx; tz >> mn >> mx; |
||
95 | sp->variance.setrd (mn, mx); |
||
1852 | jag | 96 | sp->set_pos (sp->posx, sp->posy); |
1787 | jag | 97 | sp->call_listener (2, f); |
98 | } |
||
99 | }; |
||
100 | |||
101 | checkbutton lbl; // main label |
||
102 | |||
103 | label backlbl; // back label, after field |
||
104 | int draw_backlbl; |
||
105 | |||
106 | // increase/decrease buttons |
||
107 | arrow_button dec, inc; |
||
1805 | jag | 108 | void updowndecinc () { |
109 | dec.dir = arrow_button::down; |
||
110 | inc.dir = arrow_button::up; |
||
111 | } |
||
112 | |||
1787 | jag | 113 | dec_lis<T> decl; |
114 | inc_lis<T> incl; |
||
115 | int dir; // < 0 = decrease, > 0 = increase |
||
116 | |||
117 | // pre listeners so can detach from menu |
||
118 | int pre; |
||
119 | pre_lis<T> prel; |
||
120 | re_pre_lis<T> reprel; |
||
121 | |||
122 | // value |
||
123 | field f_value; |
||
124 | T value; |
||
125 | T lastv; |
||
126 | |||
127 | int draw_more; |
||
128 | arrow_button more; |
||
129 | more_lis<T> mol; |
||
130 | |||
131 | label l_delta; |
||
132 | field f_delta; |
||
133 | delta_lis<T> dell; |
||
134 | |||
135 | struct variancet { |
||
136 | checkbutton cb; |
||
137 | label lbl; |
||
138 | field fld; |
||
139 | variance_lis<T> lis; |
||
140 | rnd<float> rd; |
||
141 | int ui; |
||
142 | variancet (spinner2<T>* sp = 0) : lis (sp) { |
||
2084 | jag | 143 | lbl.set_text (" ~ "); |
1787 | jag | 144 | fld.change_lsnr = &lis; |
145 | fld.expr = 0; |
||
146 | cb.set_text ("~"); |
||
147 | ui = 1; |
||
148 | } |
||
149 | void setrd (float s, float t) { |
||
150 | // assume s,t in % |
||
151 | s /= 100.0f; |
||
152 | t /= 100.0f; |
||
153 | rd.set (s, t); |
||
154 | } |
||
155 | void setfld () { |
||
156 | int i = 100 * rd.min, j = 100 * rd.max; |
||
157 | sprintf (BUFFER, "%d %d", i, j); |
||
158 | fld.set_text (BUFFER); |
||
159 | } |
||
160 | } variance; |
||
161 | |||
1957 | jag | 162 | T variedval () { |
163 | return variance.rd () * value; |
||
164 | } |
||
165 | |||
1787 | jag | 166 | int limits; |
167 | T lo, hi; |
||
168 | |||
169 | void set_limits (T _lo, T _hi) { |
||
170 | limits = 1; |
||
171 | lo = _lo; |
||
172 | hi = _hi; |
||
173 | } |
||
174 | |||
175 | change_listener<field> *lis[3]; // 0 - f_value, 1 - f_delta, 2 - variance.fld = null |
||
176 | |||
177 | spinner2 (const std::string& _name = "unknown") : |
||
178 | decl(*this), incl(*this), |
||
179 | pre(1), |
||
180 | prel (*this, &decl, &incl), |
||
181 | reprel (*this, &prel), |
||
182 | mol(*this), |
||
183 | l_delta ("+-"), dell(this), variance(this) { |
||
184 | |||
185 | #ifndef __WIDGET_MOVE__ |
||
186 | lbl.set_listener (this); |
||
187 | #endif |
||
188 | |||
189 | widget* chld [] = { |
||
190 | this, |
||
191 | &dec, |
||
192 | &inc, |
||
193 | &f_value, |
||
194 | &more, |
||
195 | &l_delta, |
||
196 | &f_delta, |
||
197 | &variance.cb, |
||
198 | &variance.lbl, |
||
199 | &variance.fld |
||
200 | }; |
||
201 | for (int i = 0; i < 10; ++i) lbl.add_child (chld[i]); |
||
202 | |||
203 | dec.set_dir (arrow_button::left); |
||
204 | inc.set_dir (arrow_button::right); |
||
205 | inc.click_repeat = 1; |
||
206 | dec.click_repeat = 1; |
||
207 | |||
208 | dir = 1; |
||
209 | draw_more = 1; |
||
210 | more.set_dir (arrow_button::right); |
||
211 | |||
212 | set_pre (pre); |
||
213 | |||
214 | LISTEN (more,&mol) |
||
215 | |||
216 | l_delta.hide (); |
||
217 | f_delta.hide (); |
||
218 | |||
219 | lastv = value = 0; |
||
220 | |||
221 | f_value.change_lsnr = this; |
||
222 | f_delta.change_lsnr = &dell; |
||
223 | |||
224 | |||
1852 | jag | 225 | f_value.typing_lsnr = variance.fld.typing_lsnr = this; |
1787 | jag | 226 | |
227 | lis [0]=lis[1]=lis[2]=0; |
||
228 | |||
229 | limits = 0; |
||
230 | lo = hi = 0; |
||
231 | |||
232 | // see field::call_listener () |
||
233 | const std::type_info& ti = typeid (T); |
||
234 | std::string tn (ti.name()); |
||
235 | if (tn == "i") |
||
236 | f_value.type = f_delta.type = "int"; |
||
237 | else if (tn == "f") |
||
238 | f_value.type = f_delta.type = "double"; |
||
239 | |||
240 | draw_backlbl = 0; |
||
241 | |||
2036 | jag | 242 | vary = &variance.cb; |
1787 | jag | 243 | |
244 | } |
||
245 | |||
246 | void set_pos (int x, int y) { |
||
247 | widget::set_pos (x, y); |
||
248 | int i = 0; |
||
249 | if (draw_more == 0 || variance.ui == 0) i = 1; |
||
1838 | jag | 250 | widget* w [] = {&variance.cb, &lbl, &dec, &inc, &f_value, &more, &variance.lbl, &variance.fld, &l_delta, &f_delta, }; |
251 | int xshift [] = {0, 0, 0, -1, 1, -1, 0, 0, 5, 1}; |
||
1787 | jag | 252 | int lft [] = {0, 0, fnt.lift, fnt.lift, 0, fnt.lift, 0, 0, 0, 0}; |
253 | for (; i < 10; ++i) { |
||
254 | x += xshift [i]; |
||
255 | widget* wi = w[i]; |
||
256 | wi->set_pos (x, y + lft[i]); |
||
257 | advance_right (x, *wi, SPACING); |
||
258 | } |
||
259 | set_pos_backlbl (); |
||
260 | } |
||
261 | |||
262 | void set_pos_backlbl () { |
||
263 | if (draw_backlbl) { |
||
264 | widget* w = 0; |
||
1838 | jag | 265 | if (f_delta.visible) w = &f_delta; |
1787 | jag | 266 | else if (draw_more) w = &more; |
267 | else w = &f_value; |
||
268 | backlbl.set_pos (w->extents.right + SPACING, w->extents.bottom - fnt.lift); |
||
269 | } |
||
270 | } |
||
271 | |||
272 | void update () { |
||
273 | lbl.update (); |
||
274 | backlbl.update (); |
||
275 | l_delta.update (); |
||
276 | f_value.update (); |
||
277 | f_delta.update (); |
||
278 | variance.cb.update (); |
||
279 | variance.lbl.update (); |
||
280 | variance.fld.update (); |
||
281 | set_pos (posx, posy); |
||
282 | inc.update (); |
||
283 | dec.update (); |
||
284 | more.update (); |
||
285 | } |
||
286 | |||
287 | void toggle_delta () { |
||
288 | if (more.dir == arrow_button::right) { |
||
289 | l_delta.show (); |
||
290 | f_delta.show (); |
||
291 | if (variance.ui) { |
||
292 | variance.lbl.show (); |
||
293 | variance.fld.show (); |
||
294 | } |
||
295 | more.set_dir (arrow_button::left); |
||
296 | } else { |
||
297 | l_delta.hide (); |
||
298 | f_delta.hide (); |
||
299 | if (variance.ui) { |
||
300 | variance.lbl.hide (); |
||
301 | variance.fld.hide (); |
||
302 | } |
||
303 | more.set_dir (arrow_button::right); |
||
304 | } |
||
305 | set_pos_backlbl (); |
||
306 | abort_selectors (); |
||
307 | } |
||
308 | |||
2105 | jag | 309 | void change_value (int _dir, double scl = 1.0, int upd = 0) { |
310 | dir = _dir; |
||
311 | delta = scl * delta0; |
||
312 | value += dir_delta (); |
||
313 | if (limits) clamp<T>(lo, value, hi); |
||
314 | if (pre == 0 || upd) { |
||
315 | f_value = value; |
||
316 | set_pos (posx, posy); |
||
317 | } |
||
318 | call_listener (0, f_value); |
||
1787 | jag | 319 | } |
320 | |||
2105 | jag | 321 | spinner2<T>& operator++ () { |
322 | change_value (+1, 1, 1); |
||
323 | return *this; |
||
324 | } |
||
325 | |||
326 | spinner2<T>& operator-- () { |
||
327 | change_value (-1, 1, 1); |
||
328 | return *this; |
||
329 | } |
||
330 | |||
1787 | jag | 331 | void increase () { |
2105 | jag | 332 | change_value (+1, 1, 1); |
1787 | jag | 333 | } |
334 | |||
2105 | jag | 335 | void decrease () { |
336 | change_value (-1, 1, 1); |
||
1787 | jag | 337 | } |
338 | |||
2105 | jag | 339 | void changed (field& f) { |
1787 | jag | 340 | |
2105 | jag | 341 | if (limits) { |
342 | T v = f; |
||
343 | if (clamp<T>(lo, v, hi)) f = v; |
||
344 | value = v; |
||
345 | } else |
||
346 | value = f; |
||
347 | |||
348 | delta = value - lastv; |
||
349 | if (delta > 0) { |
||
350 | dir = 1; |
||
351 | } else { |
||
352 | dir = -1; |
||
353 | delta = -delta; |
||
354 | } |
||
355 | lastv = value; |
||
356 | |||
357 | set_pos (posx, posy); |
||
358 | |||
359 | call_listener (0, f); |
||
360 | |||
361 | } |
||
362 | |||
1787 | jag | 363 | void changed (checkbutton& cb) { |
364 | if (orient == NONE) { |
||
365 | cb.turn_off (0); |
||
366 | if (mouse_slider0.active) cant_mouse_slide (); |
||
367 | } else { |
||
368 | if (cb.state) mouse_slider0.add (this); else mouse_slider0.remove (this); |
||
369 | if (SHIFT == 0) activate_mouse_slider (); |
||
370 | } |
||
371 | } |
||
372 | |||
373 | void call_listener (int i, field& f) { |
||
374 | change_listener<field>* lisi = lis[i]; |
||
375 | if (lisi) lisi->changed (f); |
||
376 | } |
||
377 | |||
378 | void typing (field& f) { |
||
379 | f.update (); |
||
380 | set_pos (posx, posy); |
||
381 | } |
||
382 | |||
383 | void moused (int _dir, double scl) { |
||
384 | change_value (_dir, scl); |
||
385 | } |
||
386 | |||
387 | void after_slide () { |
||
388 | lbl.turn_off (DONT_CALL_LISTENER); |
||
389 | set_delta (delta0 * mouse_slider0.scale.value); |
||
2105 | jag | 390 | if (null) |
391 | set_value (0); |
||
392 | else { |
||
393 | if (pre) set_value (value); |
||
394 | } |
||
1787 | jag | 395 | } |
396 | |||
397 | void draw () { |
||
398 | glColor3f (clr.r, clr.g, clr.b); |
||
399 | widget* w [] = {&lbl, &dec, &inc, &f_value}; |
||
400 | for (int i = 0, j = 4; i < j; ++i) w[i]->draw (); |
||
401 | if (variance.ui) variance.cb.draw (); |
||
402 | if (draw_more) { |
||
403 | more.draw (); |
||
404 | if (more.dir == arrow_button::left) { |
||
405 | l_delta.draw (); |
||
406 | f_delta.draw (); |
||
407 | if (variance.ui) { |
||
408 | variance.lbl.draw (); |
||
409 | variance.fld.draw (); |
||
410 | } |
||
411 | } |
||
412 | } |
||
413 | if (draw_backlbl) backlbl.draw (); |
||
414 | } |
||
415 | |||
416 | void set_value (T t) { |
||
417 | value = t; |
||
418 | lastv = value; |
||
419 | f_value = value; |
||
420 | set_pos (posx, posy); |
||
421 | } |
||
422 | |||
423 | void set_delta (T t) { |
||
424 | delta0 = delta = t; |
||
425 | f_delta = (T) delta0; |
||
426 | set_pos (posx, posy); |
||
427 | } |
||
428 | |||
429 | void set_listener (change_listener<field>* _lis, int id = 0) { |
||
430 | lis [id] = _lis; |
||
431 | } |
||
432 | |||
433 | void set_text (const std::string& l, const std::string& bl = "") { |
||
434 | lbl.set_text (l); |
||
435 | set_name (l); |
||
436 | mouse_slider_listener::name = l; |
||
437 | if (bl != "") { |
||
438 | backlbl.set_text (bl); |
||
439 | draw_backlbl = 1; |
||
440 | } |
||
441 | } |
||
442 | |||
443 | void set_moveable (int m, int mc = 0, int* pmb = &lmb) {lbl.set_moveable (m, mc, pmb);} |
||
444 | |||
445 | int handle_input () { |
||
446 | |||
447 | int r = lbl.handle_input (); |
||
448 | if (r) return r; |
||
449 | |||
450 | if (variance.ui && variance.cb.handle_input()) return 1; |
||
451 | |||
452 | int d1 = 0, i1 = 0, m1 = 0; |
||
453 | d1 = dec.handle_input (); |
||
454 | if (d1 == 0) { |
||
455 | i1 = inc.handle_input (); |
||
456 | if (i1 == 0) |
||
457 | m1 = more.handle_input (); |
||
458 | } |
||
459 | |||
460 | int c = d1 | i1 | m1; |
||
461 | if (c) return c; |
||
462 | |||
463 | int s = f_value.handle_input (); |
||
464 | if (s) return s; |
||
465 | |||
466 | if (more.dir == arrow_button::left) { |
||
467 | int fd = f_delta.handle_input (); |
||
468 | if (fd) return fd; |
||
469 | if (variance.ui) { |
||
470 | if (variance.fld.handle_input()) return 1; |
||
471 | } |
||
472 | } |
||
473 | |||
474 | return 0; |
||
475 | |||
476 | } |
||
477 | |||
478 | inline T dir_delta () { |
||
479 | return dir * delta; |
||
480 | } |
||
481 | |||
482 | inline T operator() () { |
||
483 | if (variance.cb.state) return ( variance.rd () * value ); else return value; |
||
484 | } |
||
485 | |||
486 | void set (const std::string& t, T d, T lmin, T lmax, change_listener<field>* l = 0, int _pre = 1) { |
||
487 | set_text (t); |
||
488 | set_delta (d); |
||
489 | set_limits (lmin, lmax); |
||
490 | set_listener (l); |
||
491 | set_pre (_pre); |
||
492 | } |
||
493 | |||
494 | void set (const std::string& t, T d, change_listener<field>* l) { |
||
495 | set_text (t); |
||
496 | set_delta (d); |
||
497 | set_listener (l); |
||
498 | } |
||
499 | |||
500 | void set (T d, T lmin, T lmax, change_listener<field>* l) { |
||
501 | set_delta (d); |
||
502 | set_limits (lmin, lmax); |
||
503 | set_listener (l); |
||
504 | } |
||
505 | |||
506 | void set_pre (int _pre) { |
||
507 | pre = _pre; |
||
508 | if (pre) { |
||
509 | LISTEN (dec,&prel) |
||
510 | LISTEN (inc,&prel) |
||
511 | } else { |
||
512 | LISTEN (dec,&decl) |
||
513 | LISTEN (inc,&incl) |
||
514 | } |
||
515 | } |
||
516 | |||
517 | }; |
||
518 | |||
1859 | jag | 519 | template<> inline int spinner2<int>::operator() () { |
520 | if (variance.cb.state) return ( variance.rd () * value + 0.5f); else return value; |
||
521 | } |
||
522 | |||
1787 | jag | 523 | template <typename T> ifstream& operator>> (ifstream& f, spinner2<T>& spn) { |
524 | |||
525 | int state; |
||
526 | float minn, maxx; |
||
527 | |||
528 | f >> state >> minn >> maxx; |
||
529 | |||
530 | spn.variance.cb.set_state (state, DONT_CALL_LISTENER); |
||
531 | spn.variance.rd.set (minn, maxx); |
||
532 | spn.variance.setfld (); |
||
533 | |||
534 | return f; |
||
535 | |||
536 | } |
||
537 | |||
538 | template <typename T> ofstream& operator<< (ofstream& f, spinner2<T>& spn) { |
||
539 | f << spn.variance.cb.state << spc << spn.variance.rd.min << spc << spn.variance.rd.max << spc; |
||
540 | return f; |
||
541 | } |
||
542 | |||
1852 | jag | 543 | template <class P, class Q> Q getval (spinner2<P>& sp, Q& src) { |
1849 | jag | 544 | typename spinner2<P>::variancet& var = sp.variance; |
1852 | jag | 545 | if (var.cb.state) |
546 | return var.rd () * src; |
||
547 | else |
||
548 | return src; |
||
1849 | jag | 549 | } |
1787 | jag | 550 | #endif |