1 /* 2 * spinner2.h 3 * DIN Is Noise is copyright (c) 2006-2021 Jagannathan Sampath 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; re_pre_lisspinner2::re_pre_lis38 re_pre_lis (spinner2<Q>& _sp, click_listener* _pre) : sp (_sp), pre (_pre) {} clickedspinner2::re_pre_lis39 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; pre_lisspinner2::pre_lis51 pre_lis (spinner2<Q>& _sp, click_listener* _decl, click_listener* _incl) : sp (_sp), decl (_decl), incl (_incl) {} clickedspinner2::pre_lis52 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; dec_lisspinner2::dec_lis64 dec_lis (spinner2<Q>& _sp) : sp (_sp) {} clickedspinner2::dec_lis65 void clicked (button& b) {sp.decrease ();} 66 }; 67 68 template <typename Q> struct inc_lis : click_listener { 69 spinner2<Q>& sp; inc_lisspinner2::inc_lis70 inc_lis (spinner2<Q>& _sp) : sp (_sp) {} clickedspinner2::inc_lis71 void clicked (button& b) {sp.increase ();} 72 }; 73 74 template <typename Q> struct more_lis : click_listener { 75 spinner2<Q>& sp; more_lisspinner2::more_lis76 more_lis (spinner2<Q>& _sp) : sp (_sp) {} clickedspinner2::more_lis77 void clicked (button& b) {sp.toggle_delta();} 78 }; 79 80 template <typename Q> struct delta_lis : change_listener<field> { 81 spinner2<Q>* sp; spspinner2::delta_lis82 delta_lis (spinner2<Q>* _sp = 0) : sp (_sp) {} changedspinner2::delta_lis83 void changed (field& f) { 84 sp->delta0 = sp->delta = f; 85 sp->set_pos (sp->posx, sp->posy); 86 sp->call_listener (1, f); 87 } 88 }; 89 90 template <typename Q> struct variance_lis : change_listener<field> { 91 spinner2<Q>* sp; spspinner2::variance_lis92 variance_lis (spinner2<Q>* _sp = 0) : sp (_sp) {} changedspinner2::variance_lis93 void changed (field& f) { 94 tokenizer tz (f.text); 95 float mn, mx; tz >> mn >> mx; 96 sp->variance.setrd (mn, mx); 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; updowndecincspinner2108 void updowndecinc () { 109 dec.dir = arrow_button::down; 110 inc.dir = arrow_button::up; 111 } 112 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; lisspinner2::variancet142 variancet (spinner2<T>* sp = 0) : lis (sp) { 143 lbl.set_text (" ~% "); 144 fld.change_lsnr = &lis; 145 fld.expr = 0; 146 cb.set_text ("~"); 147 ui = 1; 148 } setrdspinner2::variancet149 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 } setfldspinner2::variancet155 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 162 int limits; 163 T lo, hi; 164 set_limitsspinner2165 void set_limits (T _lo, T _hi) { 166 limits = 1; 167 lo = _lo; 168 hi = _hi; 169 } 170 171 change_listener<field> *lis[3]; // 0 - f_value, 1 - f_delta, 2 - variance.fld = null 172 173 spinner2 (const std::string& _name = "unknown") : 174 decl(*this), incl(*this), 175 pre(1), 176 prel (*this, &decl, &incl), 177 reprel (*this, &prel), 178 mol(*this), 179 l_delta ("+-"), dell(this), variance(this) { 180 181 #ifndef __WIDGET_MOVE__ 182 lbl.set_listener (this); 183 #endif 184 185 widget* chld [] = { 186 this, 187 &dec, 188 &inc, 189 &f_value, 190 &more, 191 &l_delta, 192 &f_delta, 193 &variance.cb, 194 &variance.lbl, 195 &variance.fld 196 }; 197 for (int i = 0; i < 10; ++i) lbl.add_child (chld[i]); 198 199 dec.set_dir (arrow_button::left); 200 inc.set_dir (arrow_button::right); 201 inc.click_repeat = 1; 202 dec.click_repeat = 1; 203 204 dir = 1; 205 draw_more = 1; 206 more.set_dir (arrow_button::right); 207 208 set_pre (pre); 209 210 LISTEN (more,&mol) 211 212 l_delta.hide (); 213 f_delta.hide (); 214 215 lastv = value = 0; 216 217 f_value.change_lsnr = this; 218 f_delta.change_lsnr = &dell; 219 220 221 f_value.typing_lsnr = f_delta.typing_lsnr = this; 222 223 lis [0]=lis[1]=lis[2]=0; 224 225 limits = 0; 226 lo = hi = 0; 227 228 // see field::call_listener () 229 const std::type_info& ti = typeid (T); 230 std::string tn (ti.name()); 231 if (tn == "i") 232 f_value.type = f_delta.type = "int"; 233 else if (tn == "f") 234 f_value.type = f_delta.type = "double"; 235 236 draw_backlbl = 0; 237 238 239 } 240 set_posspinner2241 void set_pos (int x, int y) { 242 widget::set_pos (x, y); 243 int i = 0; 244 if (draw_more == 0 || variance.ui == 0) i = 1; 245 widget* w [] = {&variance.cb, &lbl, &dec, &inc, &f_value, &more, &variance.lbl, &variance.fld, &l_delta, &f_delta, }; 246 int xshift [] = {0, 0, 0, -1, 1, -1, 0, 0, 5, 1}; 247 int lft [] = {0, 0, fnt.lift, fnt.lift, 0, fnt.lift, 0, 0, 0, 0}; 248 for (; i < 10; ++i) { 249 x += xshift [i]; 250 widget* wi = w[i]; 251 wi->set_pos (x, y + lft[i]); 252 advance_right (x, *wi, SPACING); 253 } 254 set_pos_backlbl (); 255 } 256 set_pos_backlblspinner2257 void set_pos_backlbl () { 258 if (draw_backlbl) { 259 widget* w = 0; 260 if (f_delta.visible) w = &f_delta; 261 else if (draw_more) w = &more; 262 else w = &f_value; 263 backlbl.set_pos (w->extents.right + SPACING, w->extents.bottom - fnt.lift); 264 } 265 } 266 updatespinner2267 void update () { 268 lbl.update (); 269 backlbl.update (); 270 l_delta.update (); 271 f_value.update (); 272 f_delta.update (); 273 variance.cb.update (); 274 variance.lbl.update (); 275 variance.fld.update (); 276 set_pos (posx, posy); 277 inc.update (); 278 dec.update (); 279 more.update (); 280 } 281 toggle_deltaspinner2282 void toggle_delta () { 283 if (more.dir == arrow_button::right) { 284 l_delta.show (); 285 f_delta.show (); 286 if (variance.ui) { 287 variance.lbl.show (); 288 variance.fld.show (); 289 } 290 more.set_dir (arrow_button::left); 291 } else { 292 l_delta.hide (); 293 f_delta.hide (); 294 if (variance.ui) { 295 variance.lbl.hide (); 296 variance.fld.hide (); 297 } 298 more.set_dir (arrow_button::right); 299 } 300 set_pos_backlbl (); 301 abort_selectors (); 302 } 303 304 void change_value (int _dir, double scl = 1.0) { 305 dir = _dir; 306 delta = scl * delta0; 307 value += dir_delta (); 308 f_value = value; 309 changed (f_value); 310 } 311 decreasespinner2312 void decrease () { 313 change_value (-1); 314 } 315 increasespinner2316 void increase () { 317 change_value (+1); 318 } 319 320 spinner2<T>& operator++ () { 321 change_value (+1); 322 return *this; 323 } 324 325 spinner2<T>& operator-- () { 326 change_value (-1); 327 return *this; 328 } 329 changedspinner2330 void changed (checkbutton& cb) { 331 if (orient == NONE) { 332 cb.turn_off (0); 333 if (mouse_slider0.active) cant_mouse_slide (); 334 } else { 335 if (cb.state) mouse_slider0.add (this); else mouse_slider0.remove (this); 336 if (SHIFT == 0) activate_mouse_slider (); 337 } 338 } 339 changedspinner2340 void changed (field& f) { 341 if (limits) { 342 T v = f; 343 if (clamp<T>(lo, v, hi)) f = v; 344 } 345 value = f; 346 if (f.edited) { 347 delta = value - lastv; 348 if (delta > 0) { 349 dir = 1; 350 } else { 351 dir = -1; 352 delta = -delta; 353 } 354 } 355 lastv = value; 356 set_pos (posx, posy); 357 call_listener (0, f); 358 } 359 call_listenerspinner2360 void call_listener (int i, field& f) { 361 change_listener<field>* lisi = lis[i]; 362 if (lisi) lisi->changed (f); 363 } 364 typingspinner2365 void typing (field& f) { 366 f.update (); 367 set_pos (posx, posy); 368 } 369 mousedspinner2370 void moused (int _dir, double scl) { 371 change_value (_dir, scl); 372 } 373 after_slidespinner2374 void after_slide () { 375 lbl.turn_off (DONT_CALL_LISTENER); 376 set_delta (delta0 * mouse_slider0.scale.value); 377 if (null) set_value (0); 378 } 379 drawspinner2380 void draw () { 381 glColor3f (clr.r, clr.g, clr.b); 382 widget* w [] = {&lbl, &dec, &inc, &f_value}; 383 for (int i = 0, j = 4; i < j; ++i) w[i]->draw (); 384 if (variance.ui) variance.cb.draw (); 385 if (draw_more) { 386 more.draw (); 387 if (more.dir == arrow_button::left) { 388 l_delta.draw (); 389 f_delta.draw (); 390 if (variance.ui) { 391 variance.lbl.draw (); 392 variance.fld.draw (); 393 } 394 } 395 } 396 if (draw_backlbl) backlbl.draw (); 397 } 398 set_valuespinner2399 void set_value (T t) { 400 value = t; 401 lastv = value; 402 f_value = value; 403 set_pos (posx, posy); 404 } 405 set_deltaspinner2406 void set_delta (T t) { 407 delta0 = delta = t; 408 f_delta = (T) delta0; 409 set_pos (posx, posy); 410 } 411 412 void set_listener (change_listener<field>* _lis, int id = 0) { 413 lis [id] = _lis; 414 } 415 416 void set_text (const std::string& l, const std::string& bl = "") { 417 lbl.set_text (l); 418 set_name (l); 419 mouse_slider_listener::name = l; 420 if (bl != "") { 421 backlbl.set_text (bl); 422 draw_backlbl = 1; 423 } 424 } 425 426 void set_moveable (int m, int mc = 0, int* pmb = &lmb) {lbl.set_moveable (m, mc, pmb);} 427 handle_inputspinner2428 int handle_input () { 429 430 int r = lbl.handle_input (); 431 if (r) return r; 432 433 if (variance.ui && variance.cb.handle_input()) return 1; 434 435 int d1 = 0, i1 = 0, m1 = 0; 436 d1 = dec.handle_input (); 437 if (d1 == 0) { 438 i1 = inc.handle_input (); 439 if (i1 == 0) 440 m1 = more.handle_input (); 441 } 442 443 int c = d1 | i1 | m1; 444 if (c) return c; 445 446 int s = f_value.handle_input (); 447 if (s) return s; 448 449 if (more.dir == arrow_button::left) { 450 int fd = f_delta.handle_input (); 451 if (fd) return fd; 452 if (variance.ui) { 453 if (variance.fld.handle_input()) return 1; 454 } 455 } 456 457 return 0; 458 459 } 460 dir_deltaspinner2461 inline T dir_delta () { 462 return dir * delta; 463 } 464 operatorspinner2465 inline T operator() () { 466 if (variance.cb.state) return ( variance.rd () * value ); else return value; 467 } 468 469 void set (const std::string& t, T d, T lmin, T lmax, change_listener<field>* l = 0, int _pre = 1) { 470 set_text (t); 471 set_delta (d); 472 set_limits (lmin, lmax); 473 set_listener (l); 474 set_pre (_pre); 475 } 476 setspinner2477 void set (const std::string& t, T d, change_listener<field>* l) { 478 set_text (t); 479 set_delta (d); 480 set_listener (l); 481 } 482 setspinner2483 void set (T d, T lmin, T lmax, change_listener<field>* l) { 484 set_delta (d); 485 set_limits (lmin, lmax); 486 set_listener (l); 487 } 488 set_prespinner2489 void set_pre (int _pre) { 490 pre = _pre; 491 if (pre) { 492 LISTEN (dec,&prel) 493 LISTEN (inc,&prel) 494 } else { 495 LISTEN (dec,&decl) 496 LISTEN (inc,&incl) 497 } 498 } 499 500 }; 501 502 template <typename T> ifstream& operator>> (ifstream& f, spinner2<T>& spn) { 503 504 int state; 505 float minn, maxx; 506 507 f >> state >> minn >> maxx; 508 509 spn.variance.cb.set_state (state, DONT_CALL_LISTENER); 510 spn.variance.rd.set (minn, maxx); 511 spn.variance.setfld (); 512 513 return f; 514 515 } 516 517 template <typename T> ofstream& operator<< (ofstream& f, spinner2<T>& spn) { 518 f << spn.variance.cb.state << spc << spn.variance.rd.min << spc << spn.variance.rd.max << spc; 519 return f; 520 } 521 522 #endif 523