1 // Hyperbolic Rogue -- dialogs 2 // Copyright (C) 2011-2018 Zeno Rogue, see 'hyper.cpp' for details 3 4 /** \file dialogs.cpp 5 * \brief Implementation of various generic dialogs and elements of dialog windows 6 */ 7 8 #include "hyper.h" 9 namespace hr { 10 11 EX const char* COLORBAR = "###"; 12 13 EX namespace dialog { 14 15 #if HDR 16 #define IFM(x) (mousing?"":x) 17 18 static const int DONT_SHOW = 16; 19 20 enum tDialogItem {diTitle, diItem, diBreak, diHelp, diInfo, diIntSlider, diSlider, diBigItem, diKeyboard}; 21 22 struct item { 23 tDialogItem type; 24 string body; 25 string value; 26 int key; 27 color_t color, colorv, colork, colors, colorc; 28 int scale; 29 double param; 30 int p1, p2, p3; 31 int position; 32 }; 33 34 struct scaler { 35 ld (*direct) (ld); 36 ld (*inverse) (ld); 37 bool positive; 38 }; 39 identity_f(ld x)40 static inline ld identity_f(ld x) { return x; } 41 42 const static scaler identity = {identity_f, identity_f, false}; 43 const static scaler logarithmic = {log, exp, true}; 44 const static scaler asinhic = {asinh, sinh, false}; __anon1ab684400102() 45 const static scaler asinhic100 = {[] (ld x) { return asinh(x*100); }, [] (ld x) { return sinh(x)/100; }, false}; 46 47 struct numberEditor { 48 ld *editwhat; 49 string s; 50 ld vmin, vmax, step, dft; 51 string title, help; 52 scaler sc; 53 int *intval; ld intbuf; 54 bool animatable; 55 }; 56 57 extern numberEditor ne; 58 scaleLog()59 inline void scaleLog() { ne.sc = logarithmic; } scaleSinh()60 inline void scaleSinh() { ne.sc = asinhic; } scaleSinh100()61 inline void scaleSinh100() { ne.sc = asinhic100; } 62 #endif 63 64 EX color_t dialogcolor = 0xC0C0C0; 65 addBack()66 EX void addBack() { 67 addItem(XLAT("go back"), 68 (cmode & sm::NUMBER) ? SDLK_RETURN : 69 ISWEB ? SDLK_BACKSPACE : 70 SDLK_ESCAPE); 71 } 72 addHelp()73 EX void addHelp() { 74 addItem(XLAT("help"), SDLK_F1); 75 } 76 77 EX namespace zoom { 78 int zoomf = 1, shiftx, shifty; 79 bool zoomoff = false; 80 nozoom()81 void nozoom() { 82 zoomf = 1; shiftx = 0; shifty = 0; zoomoff = false; 83 } 84 initzoom()85 void initzoom() { 86 zoomf = 3; 87 shiftx = -2*mousex; 88 if(mousex < vid.xres / 6) shiftx = 0; 89 if(mousex > vid.xres * 5 / 6) shiftx = -2 * vid.xres; 90 shifty = -2*mousey; 91 if(mousey < vid.yres / 6) shifty = 0; 92 if(mousey > vid.yres * 5 / 6) shifty = -2 * vid.yres; 93 } 94 stopzoom()95 void stopzoom() { zoomoff = true; } 96 displayfr(int x,int y,int b,int size,const string & s,color_t color,int align)97 EX bool displayfr(int x, int y, int b, int size, const string &s, color_t color, int align) { 98 return hr::displayfr(x * zoomf + shiftx, y * zoomf + shifty, b, size * zoomf, s, color, align); 99 } 100 101 EX bool displayfr_highlight(int x, int y, int b, int size, const string &s, color_t color, int align, int hicolor IS(0xFFFF00)) { 102 bool clicked = hr::displayfr(x * zoomf + shiftx, y * zoomf + shifty, b, size * zoomf, s, color, align); 103 if(clicked) hr::displayfr(x * zoomf + shiftx, y * zoomf + shifty, b, size * zoomf, s, hicolor, align); 104 return clicked; 105 } 106 EX } 107 108 #if CAP_MENUSCALING && CAP_SDL handleZooming(SDL_Event & ev)109 EX void handleZooming(SDL_Event &ev) { 110 using namespace zoom; 111 if(zoomoff || !(cmode & sm::ZOOMABLE)) { 112 nozoom(); return; 113 } 114 if(ev.type == SDL_MOUSEBUTTONDOWN) initzoom(); 115 if(ev.type == SDL_MOUSEBUTTONUP && zoomf > 1) stopzoom(); 116 } 117 #endif 118 #if !(CAP_MENUSCALING && CAP_SDL) handleZooming(SDL_Event & ev)119 EX void handleZooming(SDL_Event &ev) {} 120 #endif 121 122 EX vector<item> items; 123 lastItem()124 EX item& lastItem() { return items[items.size() - 1]; } 125 titleItem()126 EX item& titleItem() { return items[0]; } 127 128 EX map<int, reaction_t> key_actions; 129 add_key_action(int key,const reaction_t & action)130 EX void add_key_action(int key, const reaction_t& action) { 131 key_actions[key] = action; 132 } 133 add_key_action_adjust(int & key,const reaction_t & action)134 EX void add_key_action_adjust(int& key, const reaction_t& action) { 135 while(key_actions.count(key)) key++; 136 add_key_action(key, action); 137 } 138 extend()139 EX void extend() { 140 items.back().key = items[isize(items)-2].key; 141 } 142 add_action(const reaction_t & action)143 EX void add_action(const reaction_t& action) { 144 add_key_action_adjust(lastItem().key, action); 145 } 146 add_action_push(const reaction_t & action)147 EX void add_action_push(const reaction_t& action) { add_action([action] { pushScreen(action); }); } 148 add_action_push_clear(const reaction_t & action)149 EX void add_action_push_clear(const reaction_t& action) { add_action([action] { clearMessages(); pushScreen(action); }); } 150 handler(int sym,int uni)151 EX void handler(int sym, int uni) { 152 dialog::handleNavigation(sym, uni); 153 if(doexiton(sym, uni)) popScreen(); 154 } 155 init()156 EX void init() { 157 items.clear(); 158 key_actions.clear(); 159 keyhandler = dialog::handler; 160 } 161 keyname(int k)162 EX string keyname(int k) { 163 if(k == 0) return ""; 164 if(k == SDLK_ESCAPE) return "Esc"; 165 if(k == SDLK_F5) return "F5"; 166 if(k == SDLK_F10) return "F10"; 167 if(k == SDLK_F9) return "F9"; 168 if(k == SDLK_F1) return "F1"; 169 if(k == SDLK_HOME) return "Home"; 170 if(k == SDLK_BACKSPACE) return "Backspace"; 171 if(k == SDLK_RETURN) return "Enter"; 172 if(k == 32) return "space"; 173 if(k >= 1 && k <= 26) { string s = "Ctrl+"; s += (k+64); return s; } 174 if(k < 128) { string s; s += k; return s; } 175 if(k == 508) return "Alt+8"; 176 return "?"; 177 } 178 addSlider(double d1,double d2,double d3,int key)179 EX void addSlider(double d1, double d2, double d3, int key) { 180 item it; 181 it.type = diSlider; 182 it.color = dialogcolor; 183 it.scale = 100; 184 it.key = key; 185 it.param = (d2-d1) / (d3-d1); 186 items.push_back(it); 187 } 188 addIntSlider(int d1,int d2,int d3,int key)189 EX void addIntSlider(int d1, int d2, int d3, int key) { 190 item it; 191 it.type = diIntSlider; 192 it.color = dialogcolor; 193 it.scale = 100; 194 it.key = key; 195 it.p1 = (d2-d1); 196 it.p2 = (d3-d1); 197 items.push_back(it); 198 } 199 addSelItem(string body,string value,int key)200 EX void addSelItem(string body, string value, int key) { 201 item it; 202 it.type = diItem; 203 it.body = body; 204 it.value = value; 205 it.key = key; 206 it.color = dialogcolor; 207 it.colork = 0x808080; 208 it.colorv = 0x80A040; 209 it.colorc = 0xFFD500; 210 it.colors = 0xFF8000; 211 if(value == ONOFF(true)) it.colorv = 0x40FF40; 212 if(value == ONOFF(false)) it.colorv = 0xC04040; 213 it.scale = 100; 214 items.push_back(it); 215 } 216 addBoolItem(string body,bool value,int key)217 EX void addBoolItem(string body, bool value, int key) { 218 addSelItem(body, ONOFF(value), key); 219 } 220 displaycolor(color_t col)221 EX int displaycolor(color_t col) { 222 int c = col >> 8; 223 if(!c) return 0x181818; 224 return c; 225 } 226 addKeyboardItem(string keys)227 EX void addKeyboardItem(string keys) { 228 item it; 229 it.type = diKeyboard; 230 it.body = keys; 231 it.color = dialogcolor; 232 it.colors = 0xFF8000; 233 it.scale = 150; 234 items.push_back(it); 235 } 236 addColorItem(string body,int value,int key)237 EX void addColorItem(string body, int value, int key) { 238 item it; 239 it.type = diItem; 240 it.body = body; 241 it.value = COLORBAR; 242 it.key = key; 243 it.color = it.colorv = displaycolor(value); 244 it.colors = it.color ^ 0x404040; 245 it.colorc = it.color ^ 0x808080; 246 it.colork = 0x808080; 247 it.scale = 100; 248 items.push_back(it); 249 } 250 addHelp(string body)251 EX void addHelp(string body) { 252 item it; 253 it.type = diHelp; 254 it.body = body; 255 it.scale = 100; 256 257 if(isize(body) >= 500) it.scale = 70; 258 259 items.push_back(it); 260 } 261 addInfo(string body,color_t color IS (dialogcolor))262 EX void addInfo(string body, color_t color IS(dialogcolor)) { 263 item it; 264 it.type = diInfo; 265 it.body = body; 266 it.color = color; 267 it.scale = 100; 268 items.push_back(it); 269 } 270 addItem(string body,int key)271 EX void addItem(string body, int key) { 272 item it; 273 it.type = diItem; 274 it.body = body; 275 it.key = key; 276 it.color = dialogcolor; 277 it.colork = 0x808080; 278 it.colors = 0xFFD500; 279 it.colorc = 0xFF8000; 280 it.scale = 100; 281 items.push_back(it); 282 } 283 addBigItem(string body,int key)284 EX void addBigItem(string body, int key) { 285 item it; 286 it.type = diBigItem; 287 it.body = body; 288 it.key = key; 289 it.color = 0xC06000; 290 it.colors = 0xFFD500; 291 it.colorc = 0xFF8000; 292 it.scale = 150; 293 items.push_back(it); 294 } 295 addBreak(int val)296 EX int addBreak(int val) { 297 item it; 298 it.type = diBreak; 299 it.scale = val; 300 items.push_back(it); 301 return items.size()-1; 302 } 303 addTitle(string body,color_t color,int scale)304 EX void addTitle(string body, color_t color, int scale) { 305 item it; 306 it.type = diTitle; 307 it.body = body; 308 it.color = color; 309 it.scale = scale; 310 items.push_back(it); 311 } 312 313 EX void init(string title, color_t color IS(0xE8E8E8), int scale IS(150), int brk IS(60)) { 314 init(); 315 addTitle(title, color, scale); 316 addBreak(brk); 317 } 318 319 EX int dcenter, dwidth; 320 321 EX int dialogflags; 322 displayLong(string str,int siz,int y,bool measure)323 EX int displayLong(string str, int siz, int y, bool measure) { 324 325 int last = 0; 326 int lastspace = 0; 327 328 int xs, xo; 329 if(current_display->sidescreen) 330 xs = dwidth - vid.fsize*2, xo = vid.yres + vid.fsize; 331 else 332 xs = vid.xres * 618/1000, xo = vid.xres * 186/1000; 333 334 for(int i=0; i<=isize(str); i++) { 335 int ls = 0; 336 int prev = last; 337 if(str[i] == ' ') lastspace = i; 338 if(textwidth(siz, str.substr(last, i-last)) > xs) { 339 if(lastspace == last) ls = i-1, last = i-1; 340 else ls = lastspace, last = ls+1; 341 } 342 if(str[i] == 10 || i == isize(str)) ls = i, last = i+1; 343 if(ls) { 344 if(!measure) displayfr(xo, y, 2, siz, str.substr(prev, ls-prev), dialogcolor, 0); 345 if(ls == prev) y += siz/2; 346 else y += siz; 347 lastspace = last; 348 } 349 350 } 351 352 y += siz/2; 353 return y; 354 } 355 356 EX int tothei, dialogwidth, dfsize, dfspace, leftwidth, rightwidth, innerwidth, itemx, keyx, valuex; 357 358 EX string highlight_text; 359 measure()360 EX void measure() { 361 tothei = 0; 362 dialogwidth = 0; 363 innerwidth = 0; 364 int N = items.size(); 365 for(int i=0; i<N; i++) { 366 if(items[i].type == diHelp) 367 tothei += displayLong(items[i].body, dfsize * items[i].scale / 100, 0, true); 368 else { 369 tothei += dfspace * items[i].scale / 100; 370 if(items[i].type == diItem) 371 innerwidth = max(innerwidth, textwidth(dfsize * items[i].scale / 100, items[i].body)); 372 if(items[i].type == diTitle || items[i].type == diInfo || items[i].type == diBigItem) 373 dialogwidth = max(dialogwidth, textwidth(dfsize * items[i].scale / 100, items[i].body) * 10/9); 374 } 375 } 376 377 leftwidth = ISMOBILE ? 0 : textwidth(dfsize, "MMMMM") + dfsize/2; 378 rightwidth = textwidth(dfsize, "MMMMMMMM") + dfsize/2; 379 380 int fwidth = innerwidth + leftwidth + rightwidth; 381 dialogwidth = max(dialogwidth, fwidth); 382 itemx = dcenter - fwidth / 2 + leftwidth; 383 keyx = dcenter - fwidth / 2 + leftwidth - dfsize/2; 384 valuex = dcenter - fwidth / 2 + leftwidth + innerwidth + dfsize/2; 385 } 386 387 EX purehookset hooks_display_dialog; 388 389 EX vector<int> key_queue; 390 queue_key(int key)391 EX void queue_key(int key) { key_queue.push_back(key); } 392 display()393 EX void display() { 394 395 callhooks(hooks_display_dialog); 396 int N = items.size(); 397 dfsize = vid.fsize; 398 #if ISMOBILE || ISPANDORA 399 dfsize *= 3; 400 #endif 401 dfspace = dfsize * 5/4; 402 403 dcenter = vid.xres/2; 404 dwidth = vid.xres; 405 406 if(current_display->sidescreen) { 407 dwidth = vid.xres - vid.yres; 408 dcenter = vid.xres - dwidth / 2; 409 } 410 411 measure(); 412 413 while(tothei > vid.yres - 5 * vid.fsize) { 414 int adfsize = int(dfsize * sqrt((vid.yres - 5. * vid.fsize) / tothei)); 415 if(adfsize < dfsize-1) dfsize = adfsize + 1; 416 else dfsize--; 417 dfspace = dfsize * 5/4; 418 measure(); 419 } 420 while(dialogwidth > dwidth) { 421 int adfsize = int(dfsize * sqrt(vid.xres * 1. / dialogwidth)); 422 if(adfsize < dfsize-1) dfsize = adfsize + 1; 423 else dfsize--; // keep dfspace 424 measure(); 425 } 426 427 tothei = (vid.yres - tothei) / 2; 428 429 for(int i=0; i<N; i++) { 430 item& I = items[i]; 431 432 if(I.type == diHelp) { 433 tothei = displayLong(items[i].body, dfsize * items[i].scale / 100, tothei, false); 434 continue; 435 } 436 437 int top = tothei; 438 tothei += dfspace * I.scale / 100; 439 int mid = (top + tothei) / 2; 440 I.position = mid; 441 if(I.type == diTitle || I.type == diInfo) { 442 bool xthis = (mousey >= top && mousey < tothei && I.key); 443 if(cmode & sm::DIALOG_STRICT_X) 444 xthis = xthis && (mousex >= dcenter - dialogwidth/2 && mousex <= dcenter + dialogwidth/2); 445 displayfr(dcenter, mid, 2, dfsize * I.scale/100, I.body, I.color, 8); 446 if(xthis) getcstat = I.key; 447 } 448 else if(I.type == diItem || I.type == diBigItem) { 449 bool xthis = (mousey >= top && mousey < tothei); 450 if(cmode & sm::DIALOG_STRICT_X) 451 xthis = xthis && (mousex >= dcenter - dialogwidth/2 && mousex <= dcenter + dialogwidth/2); 452 #if ISMOBILE 453 if(xthis && mousepressed) 454 I.color = I.colorc; 455 #else 456 if(xthis && mousemoved) { 457 highlight_text = I.body; 458 mousemoved = false; 459 } 460 if(highlight_text == I.body) { 461 I.color = (xthis&&mousepressed&&actonrelease) ? I.colorc : I.colors; 462 } 463 #endif 464 465 if(I.type == diBigItem) { 466 displayfr(dcenter, mid, 2, dfsize * I.scale/100, I.body, I.color, 8); 467 } 468 else { 469 if(!mousing) 470 displayfr(keyx, mid, 2, dfsize * I.scale/100, keyname(I.key), I.colork, 16); 471 displayfr(itemx, mid, 2, dfsize * I.scale/100, I.body, I.color, 0); 472 int siz = dfsize * I.scale/100; 473 while(siz > 6 && textwidth(siz, I.value) >= vid.xres - valuex) siz--; 474 displayfr(valuex, mid, 2, siz, I.value, I.colorv, 0); 475 } 476 if(xthis) getcstat = I.key; 477 } 478 else if(among(I.type, diSlider, diIntSlider)) { 479 bool xthis = (mousey >= top && mousey < tothei); 480 int sl, sr; 481 if(current_display->sidescreen) 482 sl = vid.yres + vid.fsize*2, sr = vid.xres - vid.fsize*2; 483 else 484 sl = vid.xres/4, sr = vid.xres*3/4; 485 int sw = sr-sl; 486 if(I.type == diSlider) { 487 displayfr(sl, mid, 2, dfsize * I.scale/100, "(", I.color, 16); 488 displayfr(sl + double(sw * I.param), mid, 2, dfsize * I.scale/100, "#", I.color, 8); 489 displayfr(sr, mid, 2, dfsize * I.scale/100, ")", I.color, 0); 490 } 491 else { 492 displayfr(sl, mid, 2, dfsize * I.scale/100, "{", I.color, 16); 493 if(I.p2 < sw / 4) for(int a=0; a<=I.p2; a++) if(a != I.p1) 494 displayfr(sl + double(sw * a / I.p2), mid, 2, dfsize * I.scale/100, a == I.p1 ? "#" : ".", I.color, 8); 495 displayfr(sl + double(sw * I.p1 / I.p2), mid, 2, dfsize * I.scale/100, "#", I.color, 8); 496 displayfr(sr, mid, 2, dfsize * I.scale/100, "}", I.color, 0); 497 } 498 if(xthis) getcstat = I.key, inslider = true, slider_x = mousex; 499 } 500 else if(I.type == diKeyboard) { 501 int len = 0; 502 for(char c: I.body) 503 if(c == ' ' || c == '\t') len += 3; 504 else len++; 505 int sl, sr; 506 if(current_display->sidescreen) 507 sl = vid.yres + vid.fsize*2, sr = vid.xres - vid.fsize*2; 508 else 509 sl = vid.xres/4, sr = vid.xres*3/4; 510 int pos = 0; 511 for(char c: I.body) { 512 string s = ""; 513 s += c; 514 int nlen = 1; 515 if(c == ' ') s = "SPACE", nlen = 3; 516 if(c == '\b') s = "⌫", nlen = 1; 517 if(c == '\r' || c == '\n') s = "⏎", nlen = 1; 518 if(c == 1) s = "←", nlen = 1; 519 if(c == 2) s = "→", nlen = 1; 520 if(c == 3) s = "π"; 521 if(c == '\t') s = "CLEAR", nlen = 3; 522 int left = sl + (sr-sl) * pos / len; 523 pos += nlen; 524 int right = sl + (sr-sl) * pos / len; 525 bool in = (mousex >= left && mousex <= right && mousey >= top && mousey < tothei); 526 int xpos = (left + right) / 2; 527 if(in) { 528 if(c == 1) getcstat = SDLK_LEFT; 529 else if(c == 2) getcstat = SDLK_RIGHT; 530 else getcstat = c; 531 } 532 displayfr(xpos, mid, 2, dfsize * I.scale/100, s, in ? I.colors : I.color, 8); 533 } 534 } 535 } 536 } 537 isitem(item & it)538 bool isitem(item& it) { 539 return it.type == diItem || it.type == diBigItem; 540 } 541 handle_actions(int & sym,int & uni)542 EX void handle_actions(int &sym, int &uni) { 543 if(key_actions.count(uni)) { 544 key_actions[uni](); 545 sym = uni = 0; 546 return; 547 } 548 if(key_actions.count(sym)) { 549 key_actions[sym](); 550 sym = uni = 0; 551 return; 552 } 553 } 554 handleNavigation(int & sym,int & uni)555 EX void handleNavigation(int &sym, int &uni) { 556 if(uni == '\n' || uni == '\r' || DIRECTIONKEY == SDLK_KP5) { 557 for(int i=0; i<isize(items); i++) 558 if(isitem(items[i])) 559 if(items[i].body == highlight_text) { 560 uni = sym = items[i].key; 561 handle_actions(sym, uni); 562 return; 563 } 564 } 565 if(DKEY == SDLK_PAGEDOWN) { 566 uni = sym = 0; 567 for(int i=0; i<isize(items); i++) 568 if(isitem(items[i])) 569 highlight_text = items[i].body; 570 } 571 if(DKEY == SDLK_PAGEUP) { 572 uni = sym = 0; 573 for(int i=0; i<isize(items); i++) 574 if(isitem(items[i])) { 575 highlight_text = items[i].body; 576 break; 577 } 578 } 579 if(DKEY == SDLK_UP) { 580 uni = sym = 0; 581 string last = ""; 582 for(int i=0; i<isize(items); i++) 583 if(isitem(items[i])) 584 last = items[i].body; 585 for(int i=0; i<isize(items); i++) 586 if(isitem(items[i])) { 587 if(items[i].body == highlight_text) { 588 highlight_text = last; return; 589 } 590 else last = items[i].body; 591 } 592 highlight_text = last; 593 } 594 if(DKEY == SDLK_DOWN) { 595 uni = sym = 0; 596 int state = 0; 597 for(int i=0; i<isize(items); i++) 598 if(isitem(items[i])) { 599 if(state) { highlight_text = items[i].body; return; } 600 else if(items[i].body == highlight_text) state = 1; 601 } 602 for(int i=0; i<isize(items); i++) 603 if(isitem(items[i])) { 604 highlight_text = items[i].body; 605 break; 606 } 607 } 608 handle_actions(sym, uni); 609 } 610 611 color_t colorhistory[10] = { 612 0x202020FF, 0x800000FF, 0x008000FF, 0x000080FF, 613 0x404040FF, 0xC0C0C0FF, 0x804000FF, 0xC0C000FF, 614 0x408040FF, 0xFFD500FF 615 }, lch; 616 617 EX color_t *palette; 618 619 int colorp = 0; 620 621 color_t *colorPointer; 622 handleKeyColor(int sym,int uni)623 EX void handleKeyColor(int sym, int uni) { 624 unsigned& color = *colorPointer; 625 int shift = colorAlpha ? 0 : 8; 626 627 if(uni >= 'A' && uni <= 'D') { 628 int x = (slider_x - (dcenter-dwidth/4)) * 510 / dwidth; 629 if(x < 0) x = 0; 630 if(x > 255) x = 255; 631 part(color, uni - 'A') = x; 632 } 633 else if(uni == ' ' || uni == '\n' || uni == '\r') { 634 bool inHistory = false; 635 for(int i=0; i<10; i++) if(colorhistory[i] == (color << shift)) 636 inHistory = true; 637 if(!inHistory) { colorhistory[lch] = (color << shift); lch++; lch %= 10; } 638 popScreen(); 639 if(reaction) reaction(); 640 if(reaction_final) reaction_final(); 641 } 642 else if(uni >= '0' && uni <= '9') { 643 color = colorhistory[uni - '0'] >> shift; 644 } 645 else if(palette && uni >= 'a' && uni < 'a'+(int) palette[0]) { 646 color = palette[1 + uni - 'a'] >> shift; 647 } 648 else if(DKEY == SDLK_DOWN) { 649 colorp = (colorp-1) & 3; 650 } 651 else if(DKEY == SDLK_UP) { 652 colorp = (colorp+1) & 3; 653 } 654 else if(DKEY == SDLK_LEFT) { 655 unsigned char* pts = (unsigned char*) &color; 656 pts[colorp] -= abs(shiftmul) < .6 ? 1 : 17; 657 } 658 else if(DKEY == SDLK_RIGHT) { 659 unsigned char* pts = (unsigned char*) &color; 660 pts[colorp] += abs(shiftmul) < .6 ? 1 : 17; 661 } 662 else if(doexiton(sym, uni)) { 663 popScreen(); 664 if(reaction_final) reaction_final(); 665 } 666 } 667 668 EX bool colorAlpha; 669 drawColorDialog()670 EX void drawColorDialog() { 671 cmode = sm::NUMBER | dialogflags; 672 if(cmode & sm::SIDE) gamescreen(0); 673 else emptyscreen(); 674 675 dcenter = vid.xres/2; 676 dwidth = vid.xres; 677 678 if(current_display->sidescreen) { 679 dwidth = vid.xres - vid.yres; 680 dcenter = vid.xres - dwidth / 2; 681 } 682 683 color_t color = *colorPointer; 684 685 int ash = 8; 686 687 for(int j=0; j<10; j++) { 688 int x = dcenter + vid.fsize * 2 * (j-5); 689 int y = vid.yres / 2- 5 * vid.fsize; 690 691 string s0 = ""; s0 += ('0'+j); 692 693 vid.fsize *= 2; 694 displayColorButton(x, y, s0, '0'+j, 0, 0, colorhistory[j] >> ash); 695 vid.fsize /= 2; 696 } 697 698 if(palette) { 699 int q = palette[0]; 700 for(int i=0; i<q; i++) { 701 int x = dcenter + vid.fsize * (2 * i-q); 702 int y = vid.yres / 2- 7 * vid.fsize; 703 string s0 = ""; s0 += ('a'+i); 704 vid.fsize *= 2; 705 displayColorButton(x, y, s0, 'a'+i, 0, 0, palette[i+1] >> ash); 706 vid.fsize /= 2; 707 } 708 } 709 710 for(int i=0; i<4; i++) { 711 int y = vid.yres / 2 + (2-i) * vid.fsize * 2; 712 if(i == 3 && !colorAlpha) continue; 713 714 color_t col = ((i==colorp) && !mousing) ? 0xFFD500 : dialogcolor; 715 716 displayColorButton(dcenter - dwidth/4, y, "(", 0, 16, 0, col); 717 string rgt = ") "; rgt += "ABGR" [i+(colorAlpha?0:1)]; 718 displayColorButton(dcenter + dwidth/4, y, rgt, 0, 0, 0, col); 719 displayColorButton(dcenter - dwidth/4 + dwidth * part(color, i) / 510, y, "#", 0, 8, 0, col); 720 721 if(mousey >= y - vid.fsize && mousey < y + vid.fsize) 722 getcstat = 'A' + i, inslider = true, slider_x = mousex; 723 } 724 725 displayColorButton(dcenter, vid.yres/2+vid.fsize * 6, XLAT("select this color") + " : " + format(colorAlpha ? "%08X" : "%06X", color), ' ', 8, 0, color >> (colorAlpha ? ash : 0)); 726 727 if(extra_options) extra_options(); 728 729 keyhandler = handleKeyColor; 730 } 731 openColorDialog(unsigned int & col,unsigned int * pal IS (palette))732 EX void openColorDialog(unsigned int& col, unsigned int *pal IS(palette)) { 733 colorPointer = &col; palette = pal; 734 colorAlpha = true; 735 dialogflags = 0; 736 pushScreen(drawColorDialog); 737 reaction = reaction_t(); 738 extra_options = reaction_t(); 739 } 740 741 EX numberEditor ne; 742 editingDetail()743 EX bool editingDetail() { 744 return ne.editwhat == &vid.highdetail || ne.editwhat == &vid.middetail; 745 } 746 ldtoint(ld x)747 int ldtoint(ld x) { 748 if(x > 0) return int(x+.5); 749 else return int(x-.5); 750 } 751 disp(ld x)752 EX string disp(ld x) { 753 if(dialogflags & sm::HEXEDIT) return "0x" + itsh((unsigned long long)(x)); 754 else if(ne.intval) return its(ldtoint(x)); 755 else return fts(x); } 756 757 EX reaction_t reaction; 758 EX reaction_t reaction_final; 759 760 EX reaction_t extra_options; 761 apply_slider()762 EX void apply_slider() { 763 if(ne.intval) *ne.intval = ldtoint(*ne.editwhat); 764 if(reaction) reaction(); 765 if(ne.intval) *ne.editwhat = *ne.intval; 766 ne.s = disp(*ne.editwhat); 767 #if CAP_ANIMATIONS 768 anims::deanimate(*ne.editwhat); 769 #endif 770 } 771 use_hexeditor()772 EX void use_hexeditor() { 773 dialogflags |= sm::HEXEDIT; 774 ne.s = disp(*ne.editwhat); 775 } 776 apply_edit()777 EX void apply_edit() { 778 try { 779 exp_parser ep; 780 ep.s = ne.s; 781 ld x = ep.rparse(); 782 if(ne.sc.positive && x <= 0) return; 783 *ne.editwhat = x; 784 if(ne.intval) *ne.intval = ldtoint(*ne.editwhat); 785 #if CAP_ANIMATIONS 786 if(ne.animatable) anims::animate_parameter(*ne.editwhat, ne.s, reaction ? reaction : reaction_final); 787 #endif 788 if(reaction) reaction(); 789 } 790 catch(const hr_parse_exception&) { 791 } 792 } 793 bound_low(ld val)794 EX void bound_low(ld val) { 795 auto r = reaction; 796 reaction = [r, val] () { 797 if(*ne.editwhat < val) { 798 *ne.editwhat = val; 799 if(ne.intval) *ne.intval = ldtoint(*ne.editwhat); 800 } 801 if(r) r(); 802 }; 803 } 804 bound_up(ld val)805 EX void bound_up(ld val) { 806 auto r = reaction; 807 reaction = [r, val] () { 808 if(*ne.editwhat > val) { 809 *ne.editwhat = val; 810 if(ne.intval) *ne.intval = ldtoint(*ne.editwhat); 811 } 812 if(r) r(); 813 }; 814 } 815 816 EX int numberdark; 817 formula_keyboard(bool lr)818 EX void formula_keyboard(bool lr) { 819 addKeyboardItem("1234567890"); 820 addKeyboardItem("=+-*/^()\x3"); 821 addKeyboardItem("qwertyuiop"); 822 addKeyboardItem("asdfghjkl"); 823 addKeyboardItem("zxcvbnm,.\b"); 824 addKeyboardItem(lr ? " \t\x1\x2" : " \t"); 825 } 826 827 EX bool onscreen_keyboard = ISMOBILE; 828 number_dialog_help()829 EX void number_dialog_help() { 830 init("number dialog help"); 831 dialog::addBreak(100); 832 dialog::addHelp(XLAT("You can enter formulas in this dialog.")); 833 dialog::addBreak(100); 834 dialog::addHelp(XLAT("Functions available:")); 835 addHelp(available_functions()); 836 dialog::addBreak(100); 837 dialog::addHelp(XLAT("Constants and variables available:")); 838 addHelp(available_constants()); 839 if(ne.animatable) { 840 dialog::addBreak(100); 841 dialog::addHelp(XLAT("Animations:")); 842 dialog::addHelp(XLAT("a..b -- animate linearly from a to b")); 843 dialog::addHelp(XLAT("a..b..|c..d -- animate from a to b, then from c to d")); 844 dialog::addHelp(XLAT("a../x..b../y -- change smoothly, x and y are derivatives")); 845 } 846 847 /* "Most parameters can be animated simply by using '..' in their editing dialog. " 848 "For example, the value of a parameter set to 0..1 will grow linearly from 0 to 1. " 849 "You can also use functions (e.g. cos(0..2*pi)) and refer to other parameters." 850 )); */ 851 852 #if CAP_ANIMATIONS 853 dialog::addBreak(50); 854 auto f = find_edit(ne.intval ? (void*) ne.intval : (void*) ne.editwhat); 855 if(f) 856 dialog::addHelp(XLAT("Parameter names, e.g. '%1'", f->parameter_name)); 857 else 858 dialog::addHelp(XLAT("Parameter names")); 859 dialog::addBreak(50); 860 for(auto& ap: anims::aps) { 861 string what = "?"; 862 for(auto& p: params) if(p.second->affects(ap.value)) what = p.first; 863 dialog::addInfo(what + " = " + ap.formula); 864 } 865 #endif 866 dialog::addBreak(50); 867 dialog::addHelp(XLAT("These can be combined, e.g. %1", "projection*sin(0..2*pi)")); 868 display(); 869 } 870 parser_help()871 EX void parser_help() { 872 ne.editwhat = nullptr; 873 ne.intval = nullptr; 874 addItem("help", SDLK_F1); 875 add_action_push(number_dialog_help); 876 } 877 drawNumberDialog()878 EX void drawNumberDialog() { 879 cmode = sm::NUMBER | dialogflags; 880 if(numberdark < DONT_SHOW) 881 gamescreen(numberdark); 882 else emptyscreen(); 883 init(ne.title); 884 addInfo(ne.s); 885 if(ne.intval && ne.sc.direct == &identity_f) 886 addIntSlider(int(ne.vmin), int(*ne.editwhat), int(ne.vmax), 500); 887 else 888 addSlider(ne.sc.direct(ne.vmin), ne.sc.direct(*ne.editwhat), ne.sc.direct(ne.vmax), 500); 889 addBreak(100); 890 #if !ISMOBILE 891 addHelp(XLAT("You can scroll with arrow keys -- Ctrl to fine-tune")); 892 addBreak(100); 893 #endif 894 895 dialog::addBack(); 896 addSelItem(XLAT("default value"), disp(ne.dft), SDLK_HOME); 897 add_edit(onscreen_keyboard); 898 addItem("help", SDLK_F1); 899 add_action_push(number_dialog_help); 900 901 addBreak(100); 902 903 if(ne.help != "") { 904 addHelp(ne.help); 905 // bool scal = !ISMOBILE && !ISPANDORA && isize(ne.help) > 160; 906 // if(scal) lastItem().scale = 30; 907 } 908 909 if(extra_options) extra_options(); 910 911 if(onscreen_keyboard) { 912 addBreak(100); 913 formula_keyboard(false); 914 } 915 916 display(); 917 918 keyhandler = [] (int sym, int uni) { 919 handleNavigation(sym, uni); 920 if((uni >= '0' && uni <= '9') || among(uni, '.', '+', '-', '*', '/', '^', '(', ')', ',', '|', 3) || (uni >= 'a' && uni <= 'z')) { 921 if(uni == 3) ne.s += "pi"; 922 else ne.s += uni; 923 apply_edit(); 924 } 925 else if(uni == '\b' || uni == '\t') { 926 ne.s = ne.s. substr(0, isize(ne.s)-1); 927 sscanf(ne.s.c_str(), LDF, ne.editwhat); 928 apply_edit(); 929 } 930 #if !ISMOBILE 931 else if(DKEY == SDLK_RIGHT) { 932 if(ne.intval && abs(shiftmul) < .6) 933 (*ne.editwhat)++; 934 else 935 *ne.editwhat = ne.sc.inverse(ne.sc.direct(*ne.editwhat) + shiftmul * ne.step); 936 if(abs(*ne.editwhat) < ne.step * 1e-6 && !ne.intval) *ne.editwhat = 0; 937 apply_slider(); 938 } 939 else if(DKEY == SDLK_LEFT) { 940 if(ne.intval && abs(shiftmul) < .6) 941 (*ne.editwhat)--; 942 else 943 *ne.editwhat = ne.sc.inverse(ne.sc.direct(*ne.editwhat) - shiftmul * ne.step); 944 if(abs(*ne.editwhat) < ne.step * 1e-6 && !ne.intval) *ne.editwhat = 0; 945 apply_slider(); 946 } 947 #endif 948 else if(sym == SDLK_HOME) { 949 *ne.editwhat = ne.dft; 950 apply_slider(); 951 } 952 else if(uni == 500) { 953 int sl, sr; 954 if(current_display->sidescreen) 955 sl = vid.yres + vid.fsize*2, sr = vid.xres - vid.fsize*2; 956 else 957 sl = vid.xres/4, sr = vid.xres*3/4; 958 ld d = (slider_x - sl + .0) / (sr-sl); 959 ld val = ne.sc.inverse(d * (ne.sc.direct(ne.vmax) - ne.sc.direct(ne.vmin)) + ne.sc.direct(ne.vmin)); 960 ld nextval = ne.sc.inverse((mousex + 1. - sl) / (sr - sl) * (ne.sc.direct(ne.vmax) - ne.sc.direct(ne.vmin)) + ne.sc.direct(ne.vmin)); 961 ld dif = abs(val - nextval); 962 if(dif > 1e-6) { 963 ld mul = 1; 964 while(dif < 10) dif *= 10, mul *= 10; 965 val *= mul; 966 val = floor(val + 0.5); 967 val /= mul; 968 } 969 *ne.editwhat = val; 970 971 apply_slider(); 972 } 973 else if(doexiton(sym, uni)) { popScreen(); if(reaction_final) reaction_final(); } 974 }; 975 } 976 977 int nlpage = 1; 978 int wheelshift = 0; 979 handlePage(int & nl,int & nlm,int perpage)980 EX int handlePage(int& nl, int& nlm, int perpage) { 981 nlm = nl; 982 int onl = nl; 983 int ret = 0; 984 if(nlpage) { 985 nl = nlm = perpage; 986 if(nlpage == 2) ret = nlm; 987 int w = wheelshift; 988 int realw = 0; 989 while(w<0 && ret) { 990 ret--; w++; realw--; 991 } 992 while(w>0 && ret+perpage < onl) { 993 ret++; w--; realw++; 994 } 995 wheelshift = realw; 996 if(ret+nl > onl) nl = onl-ret; 997 } 998 return ret; 999 } 1000 displayPageButtons(int i,bool pages)1001 EX void displayPageButtons(int i, bool pages) { 1002 int i0 = vid.yres - vid.fsize; 1003 int xr = vid.xres / 80; 1004 if(pages) if(displayfrZH(xr*8, i0, 1, vid.fsize, IFM("1 - ") + XLAT("page") + " 1", nlpage == 1 ? 0xD8D8C0 : dialogcolor, 8)) 1005 getcstat = '1'; 1006 if(pages) if(displayfrZH(xr*24, i0, 1, vid.fsize, IFM("2 - ") + XLAT("page") + " 2", nlpage == 1 ? 0xD8D8C0 : dialogcolor, 8)) 1007 getcstat = '2'; 1008 if(pages) if(displayfrZH(xr*40, i0, 1, vid.fsize, IFM("3 - ") + XLAT("all"), nlpage == 1 ? 0xD8D8C0 : dialogcolor, 8)) 1009 getcstat = '3'; 1010 if(i&1) if(displayfrZH(xr*56, i0, 1, vid.fsize, IFM(keyname(SDLK_ESCAPE) + " - ") + XLAT("go back"), dialogcolor, 8)) 1011 getcstat = '0'; 1012 if(i&2) if(displayfrZH(xr*72, i0, 1, vid.fsize, IFM("F1 - ") + XLAT("help"), dialogcolor, 8)) 1013 getcstat = SDLK_F1; 1014 if(i&4) if(displayfrZH(xr*8, i0, 1, vid.fsize, IFM("1 - ") + XLAT("plain"), dialogcolor, 8)) 1015 getcstat = '1'; 1016 } 1017 handlePageButtons(int uni)1018 EX bool handlePageButtons(int uni) { 1019 if(uni == '1') nlpage = 1, wheelshift = 0; 1020 else if(uni == '2') nlpage = 2, wheelshift = 0; 1021 else if(uni == '3') nlpage = 0, wheelshift = 0; 1022 else if(uni == PSEUDOKEY_WHEELUP) wheelshift--; 1023 else if(uni == PSEUDOKEY_WHEELDOWN) wheelshift++; 1024 else return false; 1025 return true; 1026 } 1027 editNumber(ld & x,ld vmin,ld vmax,ld step,ld dft,string title,string help)1028 EX void editNumber(ld& x, ld vmin, ld vmax, ld step, ld dft, string title, string help) { 1029 ne.editwhat = &x; 1030 ne.s = disp(x); 1031 ne.vmin = vmin; 1032 ne.vmax = vmax; 1033 ne.step = step; 1034 ne.dft = dft; 1035 ne.title = title; 1036 ne.help = help; 1037 ne.sc = identity; 1038 ne.intval = NULL; 1039 dialogflags = 0; 1040 if(cmode & sm::SIDE) dialogflags |= sm::MAYDARK | sm::SIDE; 1041 cmode |= sm::NUMBER; 1042 pushScreen(drawNumberDialog); 1043 reaction = reaction_t(); 1044 reaction_final = reaction_t(); 1045 extra_options = reaction_t(); 1046 numberdark = 0; 1047 ne.animatable = true; 1048 #if CAP_ANIMATIONS 1049 anims::get_parameter_animation(x, ne.s); 1050 #endif 1051 } 1052 editNumber(int & x,int vmin,int vmax,ld step,int dft,string title,string help)1053 EX void editNumber(int& x, int vmin, int vmax, ld step, int dft, string title, string help) { 1054 editNumber(ne.intbuf, vmin, vmax, step, dft, title, help); 1055 ne.intbuf = x; ne.intval = &x; ne.s = its(x); 1056 ne.animatable = false; 1057 } 1058 helpToEdit(int & x,int vmin,int vmax,int step,int dft)1059 EX void helpToEdit(int& x, int vmin, int vmax, int step, int dft) { 1060 popScreen(); 1061 string title = "help"; 1062 if(help[0] == '@') { 1063 int iv = help.find("\t"); 1064 int id = help.find("\n"); 1065 title = help.substr(iv+1, id-iv-1); 1066 help = help.substr(id+1); 1067 } 1068 editNumber(x, vmin, vmax, step, dft, title, help); 1069 } 1070 1071 //-- choose file dialog-- 1072 1073 #define CDIR dialogcolor 1074 #define CFILE forecolor 1075 filecmp(const pair<string,color_t> & f1,const pair<string,color_t> & f2)1076 bool filecmp(const pair<string,color_t> &f1, const pair<string,color_t> &f2) { 1077 if(f1.first == "../") return true; 1078 if(f2.first == "../") return false; 1079 if(f1.second != f2.second) 1080 return f1.second == CDIR; 1081 return f1.first < f2.first; 1082 } 1083 1084 string filecaption, cfileext; 1085 string *cfileptr; 1086 bool editext = false; 1087 1088 bool_reaction_t file_action; 1089 1090 void handleKeyFile(int sym, int uni); 1091 drawFileDialog()1092 EX void drawFileDialog() { 1093 displayfr(vid.xres/2, 30 + vid.fsize, 2, vid.fsize, 1094 filecaption, forecolor, 8); 1095 1096 string& cfile = *cfileptr; 1097 1098 displayfr(vid.xres/2, 34 + vid.fsize * 2, 2, vid.fsize, 1099 cfile, 0xFFFF00, 8); 1100 1101 displayColorButton(vid.xres*1/4, 38+vid.fsize * 3, 1102 XLAT("F4 = extension"), SDLK_F4, 8, 0, editext ? 0xFF00FF : 0xFFFF00, editext ? 0x800080 : 0x808000); 1103 displayButton(vid.xres*2/4, 38+vid.fsize * 3, 1104 XLAT("Enter = choose"), SDLK_RETURN, 8); 1105 displayButton(vid.xres*3/4, 38+vid.fsize * 3, 1106 XLAT("Esc = cancel"), SDLK_ESCAPE, 8); 1107 1108 v.clear(); 1109 1110 DIR *d; 1111 struct dirent *dir; 1112 1113 string where = "."; 1114 for(int i=0; i<isize(cfile); i++) 1115 if(cfile[i] == '/' || cfile[i] == '\\') 1116 where = cfile.substr(0, i+1); 1117 1118 d = opendir(where.c_str()); 1119 if (d) { 1120 while ((dir = readdir(d)) != NULL) { 1121 string s = dir->d_name; 1122 if(s != ".." && s[0] == '.') ; 1123 else if(isize(s) > 4 && s.substr(isize(s)-4) == cfileext) 1124 v.push_back(make_pair(s, CFILE)); 1125 else if(dir->d_type & DT_DIR) 1126 v.push_back(make_pair(s+"/", CDIR)); 1127 } 1128 closedir(d); 1129 } 1130 sort(v.begin(), v.end(), filecmp); 1131 1132 int q = v.size(); 1133 int percolumn = (vid.yres-38) / (vid.fsize+5) - 4; 1134 int columns = 1 + (q-1) / percolumn; 1135 1136 for(int i=0; i<q; i++) { 1137 int x = 16 + (vid.xres * (i/percolumn)) / columns; 1138 int y = 42 + vid.fsize * 4 + (vid.fsize+5) * (i % percolumn); 1139 1140 displayColorButton(x, y, v[i].first, 1000 + i, 0, 0, v[i].second, 0xFFFF00); 1141 } 1142 1143 keyhandler = handleKeyFile; 1144 } 1145 handleKeyFile(int sym,int uni)1146 EX void handleKeyFile(int sym, int uni) { 1147 string& s(*cfileptr); 1148 int i = isize(s) - (editext?0:4); 1149 1150 if(sym == SDLK_ESCAPE) { 1151 popScreen(); 1152 } 1153 else if(sym == SDLK_RETURN || sym == SDLK_KP_ENTER) { 1154 // we pop and re-push, in case if action opens something 1155 popScreen(); 1156 if(!file_action()) pushScreen(drawFileDialog); 1157 } 1158 else if(sym == SDLK_F4) { 1159 editext = !editext; 1160 } 1161 else if(sym == SDLK_BACKSPACE && i) { 1162 s.erase(i-1, 1); 1163 } 1164 else if(uni >= 32 && uni < 127) { 1165 s.insert(i, s0 + char(uni)); 1166 } 1167 else if(uni >= 1000 && uni <= 1000+isize(v)) { 1168 string where = "", what = s, whereparent = "../"; 1169 for(int i=0; i<isize(s); i++) 1170 if(s[i] == '/') { 1171 if(i >= 2 && s.substr(i-2,3) == "../") 1172 whereparent = s.substr(0, i+1) + "../"; 1173 else 1174 whereparent = where; 1175 where = s.substr(0, i+1), what = s.substr(i+1); 1176 } 1177 int i = uni - 1000; 1178 if(v[i].first == "../") { 1179 s = whereparent + what; 1180 } 1181 else if(v[i].second == CDIR) 1182 s = where + v[i].first + what; 1183 else 1184 s = where + v[i].first; 1185 } 1186 return; 1187 } 1188 openFileDialog(string & filename,string fcap,string ext,bool_reaction_t action)1189 EX void openFileDialog(string& filename, string fcap, string ext, bool_reaction_t action) { 1190 cfileptr = &filename; 1191 filecaption = fcap; 1192 cfileext = ext; 1193 file_action = action; 1194 pushScreen(dialog::drawFileDialog); 1195 } 1196 1197 // infix/v/vpush 1198 1199 EX string infix; 1200 1201 string foreign_letters = "ÁÄÇÈÉÍÎÖÚÜßàáâãäçèéêìíîïòóôõöøùúüýąćČčĎďĘęĚěğİıŁłńňŘřŚśŞşŠšŤťůŹźŻżŽž"; 1202 string latin_letters = "AACEEIIOUUsAAAAACEEEIIIIOOOOOOUUUYACCCDDEEEEGIILLNNRRSSSSSSTTUZZZZZZ"; 1203 hasInfix(const string & s)1204 EX bool hasInfix(const string &s) { 1205 if(infix == "") return true; 1206 string t = ""; 1207 for(int i=0; i<isize(s); i++) { 1208 char c = s[i]; 1209 char tt = 0; 1210 if(c >= 'a' && c <= 'z') tt += c - 32; 1211 else if(c >= 'A' && c <= 'Z') tt += c; 1212 else if(c == '@') tt += c; 1213 1214 if(tt == 0) for(int k=0; k<isize(latin_letters); k++) { 1215 if(s[i] == foreign_letters[2*k] && s[i+1] == foreign_letters[2*k+1]) { 1216 if(latin_letters[k] == 's') t += "SS"; 1217 else tt += latin_letters[k]; 1218 } 1219 } 1220 1221 if(tt) t += tt; 1222 } 1223 return t.find(infix) != string::npos; 1224 } 1225 editInfix(int uni)1226 EX bool editInfix(int uni) { 1227 if(uni >= 'A' && uni <= 'Z') infix += uni; 1228 else if(uni >= 'a' && uni <= 'z') infix += uni-32; 1229 else if(infix != "" && uni == 8) infix = infix.substr(0, isize(infix)-1); 1230 else if(infix != "" && uni != 0) infix = ""; 1231 else return false; 1232 return true; 1233 } 1234 1235 EX vector<pair<string, color_t> > v; 1236 vpush(color_t color,const char * name)1237 EX void vpush(color_t color, const char *name) { 1238 string s = XLATN(name); 1239 if(!hasInfix(s)) return; 1240 dialog::v.push_back(make_pair(s, color)); 1241 } 1242 1243 int editpos = 0; 1244 EX string *edited_string; 1245 view_edited_string()1246 EX string view_edited_string() { 1247 string cs = *edited_string; 1248 if(editpos < 0) editpos = 0; 1249 if(editpos > isize(cs)) editpos = isize(cs); 1250 cs.insert(editpos, "°"); 1251 return cs; 1252 } 1253 start_editing(string & s)1254 EX void start_editing(string& s) { 1255 edited_string = &s; 1256 editpos = isize(s); 1257 } 1258 editchecker(int sym,int uni)1259 EX string editchecker(int sym, int uni) { 1260 if(uni >= 32 && uni < 127) return string("") + char(uni); 1261 return ""; 1262 } 1263 handle_edit_string(int sym,int uni,function<string (int,int)> checker IS (editchecker))1264 EX bool handle_edit_string(int sym, int uni, function<string(int, int)> checker IS(editchecker)) { 1265 auto& es = *edited_string; 1266 string u2; 1267 if(DKEY == SDLK_LEFT) editpos--; 1268 else if(DKEY == SDLK_RIGHT) editpos++; 1269 else if(uni == 8) { 1270 if(editpos == 0) return true; 1271 es.replace(editpos-1, 1, ""); 1272 editpos--; 1273 } 1274 else if((u2 = checker(sym, uni)) != "") { 1275 for(char c: u2) { 1276 es.insert(editpos, 1, c); 1277 editpos ++; 1278 } 1279 } 1280 else return false; 1281 return true; 1282 } 1283 string_edit_dialog()1284 EX void string_edit_dialog() { 1285 cmode = sm::NUMBER | dialogflags; 1286 if(numberdark < DONT_SHOW) 1287 gamescreen(numberdark); 1288 else emptyscreen(); 1289 init(ne.title); 1290 addInfo(view_edited_string()); 1291 addBreak(100); 1292 formula_keyboard(true); 1293 addBreak(100); 1294 dialog::addBack(); 1295 addBreak(100); 1296 1297 if(ne.help != "") { 1298 addHelp(ne.help); 1299 } 1300 1301 if(extra_options) extra_options(); 1302 1303 display(); 1304 1305 keyhandler = [] (int sym, int uni) { 1306 handleNavigation(sym, uni); 1307 if(handle_edit_string(sym, uni)) ; 1308 else if(doexiton(sym, uni)) { 1309 popScreen(); 1310 if(reaction_final) reaction_final(); 1311 } 1312 }; 1313 } 1314 edit_string(string & s,string title,string help)1315 EX void edit_string(string& s, string title, string help) { 1316 start_editing(s); 1317 ne.title = title; 1318 ne.help = help; 1319 dialogflags = 0; 1320 if(cmode & sm::SIDE) dialogflags |= sm::MAYDARK | sm::SIDE; 1321 cmode |= sm::NUMBER; 1322 pushScreen(string_edit_dialog); 1323 reaction = reaction_t(); 1324 extra_options = reaction_t(); 1325 numberdark = 0; 1326 } 1327 confirm_dialog(const string & text,const reaction_t & act)1328 EX void confirm_dialog(const string& text, const reaction_t& act) { 1329 gamescreen(1); 1330 dialog::addBreak(250); 1331 dialog::init(XLAT("WARNING"), 0xFF0000, 150, 100); 1332 dialog::addHelp(text); 1333 dialog::addItem(XLAT("YES"), 'y'); 1334 auto yes = [act] () { popScreen(); act(); }; 1335 dialog::add_action(yes); 1336 dialog::add_key_action(SDLK_RETURN, yes); 1337 dialog::addItem(XLAT("NO"), 'n'); 1338 dialog::add_action([] () { popScreen(); }); 1339 dialog::display(); 1340 } 1341 addBoolItem_action(const string & s,bool & b,char c)1342 EX void addBoolItem_action(const string& s, bool& b, char c) { 1343 dialog::addBoolItem(s, b, c); 1344 dialog::add_action([&b] { b = !b; }); 1345 } 1346 addBoolItem_action_neg(const string & s,bool & b,char c)1347 EX void addBoolItem_action_neg(const string& s, bool& b, char c) { 1348 dialog::addBoolItem(s, !b, c); 1349 dialog::add_action([&b] { b = !b; }); 1350 } 1351 cheat_forbidden()1352 EX bool cheat_forbidden() { 1353 if(tactic::on && !cheater) { 1354 addMessage(XLAT("Not available in the pure tactics mode!")); 1355 return true; 1356 } 1357 if(daily::on) { 1358 addMessage(XLAT("Not available in the daily challenge!")); 1359 return true; 1360 } 1361 return false; 1362 } 1363 add_action_confirmed(const reaction_t & act)1364 EX void add_action_confirmed(const reaction_t& act) { 1365 dialog::add_action(dialog::add_confirmation(act)); 1366 } 1367 1368 #if HDR 1369 addBoolItem_choice(const string & s,T & b,T val,char c)1370 template<class T> void addBoolItem_choice(const string& s, T& b, T val, char c) { 1371 addBoolItem(s, b == val, c); 1372 add_action([&b, val] { b = val; }); 1373 } 1374 cheat_if_confirmed(const reaction_t & act)1375 inline void cheat_if_confirmed(const reaction_t& act) { 1376 if(cheat_forbidden()) 1377 return; 1378 if(needConfirmationEvenIfSaved()) pushScreen([act] () { confirm_dialog(XLAT("This will enable the cheat mode, making this game ineligible for scoring. Are you sure?"), act); }); 1379 else act(); 1380 } 1381 do_if_confirmed(const reaction_t & act)1382 inline void do_if_confirmed(const reaction_t& act) { 1383 if(needConfirmationEvenIfSaved()) pushScreen([act] () { confirm_dialog(XLAT("This will end your current game and start a new one. Are you sure?"), act); }); 1384 else act(); 1385 } 1386 push_confirm_dialog(const reaction_t & act,const string & s)1387 inline void push_confirm_dialog(const reaction_t& act, const string& s) { 1388 pushScreen([act, s] () { confirm_dialog(s, act); }); 1389 } 1390 add_confirmation(const reaction_t & act)1391 inline reaction_t add_confirmation(const reaction_t& act) { 1392 return [act] { do_if_confirmed(act); }; 1393 } 1394 #endif 1395 1396 } 1397 1398 } 1399