Line data Source code
1 : /* ui.cpp: cubescript user interfaces and menu functionality
2 : *
3 : * ui.cpp defines a series of convenient objects that can be created and destroyed
4 : * as groups to be used as interfaces by programs. They can be configured to grab
5 : * user input or not, (the former being useful for interactive menus, the latter
6 : * for passive interface material such as a HUD).
7 : *
8 : * ui.cpp uses font functionality in rendertext as well as textedit for interactive
9 : * text modification objects
10 : */
11 :
12 : #include "../libprimis-headers/cube.h"
13 : #include "../../shared/geomexts.h"
14 : #include "../../shared/glemu.h"
15 : #include "../../shared/glexts.h"
16 :
17 : #include <memory>
18 : #include <optional>
19 :
20 : #include "console.h"
21 : #include "control.h"
22 : #include "input.h"
23 : #include "ui.h"
24 : #include "cs.h"
25 :
26 : #include "world/entities.h"
27 : #include "world/octaedit.h"
28 : #include "world/bih.h"
29 :
30 : #include "render/hud.h"
31 :
32 : //model needs bih's objects
33 : #include "model/model.h"
34 : //textedit.h needs rendertext's objects
35 : #include "render/rendergl.h"
36 : #include "render/renderlights.h"
37 : #include "render/rendermodel.h"
38 : #include "render/rendertext.h"
39 : #include "render/renderttf.h"
40 : #include "render/shader.h"
41 : #include "render/shaderparam.h"
42 : #include "render/texture.h"
43 :
44 : #include "textedit.h"
45 :
46 : #include "render/renderwindow.h"
47 :
48 : /* a quick note on unnamed function arguments, used here for many derived functions:
49 : *
50 : * c++ does legally allow functions to be defined with parameters with no name:
51 : * this is to allow the derived functions to match the same function "signature"
52 : * as the parent class without needlessly defining parameter names that don't
53 : * actually get used -- this can be confusing (one expects to see parameters in
54 : * a function actually get used)
55 : *
56 : * obviously, anything actually passed to them will be lost as there is no name
57 : * with which to access them inside the function body
58 : *
59 : * example:
60 : *
61 : * bool target(float, float) //note unnamed function parameters
62 : * {
63 : * return true; //note that neither parameter was used in the body
64 : * }
65 : */
66 :
67 : static ModelPreview modelpreview = ModelPreview();
68 :
69 : namespace UI
70 : {
71 : class ClipArea final
72 : {
73 : public:
74 0 : ClipArea(float x, float y, float w, float h) : x1(x), y1(y), x2(x+w), y2(y+h) {}
75 :
76 0 : void intersect(const ClipArea &c)
77 : {
78 0 : x1 = std::max(x1, c.x1);
79 0 : y1 = std::max(y1, c.y1);
80 0 : x2 = std::max(x1, std::min(x2, c.x2));
81 0 : y2 = std::max(y1, std::min(y2, c.y2));
82 :
83 0 : }
84 :
85 0 : bool isfullyclipped(float x, float y, float w, float h) const
86 : {
87 0 : return x1 == x2 || y1 == y2 || x >= x2 || y >= y2 || x+w <= x1 || y+h <= y1;
88 : }
89 :
90 : void scissor();
91 : private:
92 : float x1, y1, x2, y2;
93 : };
94 :
95 : class Object;
96 :
97 : namespace
98 : {
99 : float cursorx = 0.499f,
100 : cursory = 0.499f;
101 :
102 0 : void quads(float x, float y, float w, float h, float tx = 0, float ty = 0, float tw = 1, float th = 1)
103 : {
104 0 : gle::defvertex(2);
105 0 : gle::deftexcoord0();
106 0 : gle::begin(GL_TRIANGLE_STRIP);
107 0 : gle::attribf(x+w, y); gle::attribf(tx+tw, ty);
108 0 : gle::attribf(x, y); gle::attribf(tx, ty);
109 0 : gle::attribf(x+w, y+h); gle::attribf(tx+tw, ty+th);
110 0 : gle::attribf(x, y+h); gle::attribf(tx, ty+th);
111 0 : gle::end();
112 0 : }
113 :
114 0 : void quad(float x, float y, float w, float h, const std::array<vec2, 4> &tc)
115 : {
116 0 : gle::defvertex(2);
117 0 : gle::deftexcoord0();
118 0 : gle::begin(GL_TRIANGLE_STRIP);
119 0 : gle::attribf(x+w, y); gle::attrib(tc[1]);
120 0 : gle::attribf(x, y); gle::attrib(tc[0]);
121 0 : gle::attribf(x+w, y+h); gle::attrib(tc[2]);
122 0 : gle::attribf(x, y+h); gle::attrib(tc[3]);
123 0 : gle::end();
124 0 : }
125 :
126 : std::vector<ClipArea> clipstack;
127 :
128 0 : void pushclip(float x, float y, float w, float h)
129 : {
130 0 : if(clipstack.empty())
131 : {
132 0 : glEnable(GL_SCISSOR_TEST);
133 : }
134 0 : ClipArea &c = clipstack.emplace_back(ClipArea(x, y, w, h));
135 0 : if(clipstack.size() >= 2)
136 : {
137 0 : c.intersect(clipstack[clipstack.size()-2]);
138 : }
139 0 : c.scissor();
140 0 : }
141 :
142 0 : void popclip()
143 : {
144 0 : clipstack.pop_back();
145 0 : if(clipstack.empty())
146 : {
147 0 : glDisable(GL_SCISSOR_TEST);
148 : }
149 : else
150 : {
151 0 : clipstack.back().scissor();
152 : }
153 0 : }
154 :
155 0 : bool isfullyclipped(float x, float y, float w, float h)
156 : {
157 0 : if(clipstack.empty())
158 : {
159 0 : return false;
160 : }
161 0 : return clipstack.back().isfullyclipped(x, y, w, h);
162 : }
163 :
164 : enum Alignment
165 : {
166 : Align_Mask = 0xF,
167 :
168 : Align_HMask = 0x3,
169 : Align_HShift = 0,
170 : Align_HNone = 0,
171 : Align_Left = 1,
172 : Align_HCenter = 2,
173 : Align_Right = 3,
174 :
175 : Align_VMask = 0xC,
176 : Align_VShift = 2,
177 : Align_VNone = 0 << 2,
178 : Align_Top = 1 << 2,
179 : Align_VCenter = 2 << 2,
180 : Align_Bottom = 3 << 2,
181 : };
182 :
183 : enum ClampDirection
184 : {
185 : Clamp_Mask = 0xF0,
186 : Clamp_Left = 0x10,
187 : Clamp_Right = 0x20,
188 : Clamp_Top = 0x40,
189 : Clamp_Bottom = 0x80,
190 :
191 : NO_ADJUST = Align_HNone | Align_VNone,
192 : };
193 :
194 : enum ElementState
195 : {
196 : State_Hover = 1 << 0,
197 : State_Press = 1 << 1,
198 : State_Hold = 1 << 2,
199 : State_Release = 1 << 3,
200 : State_AltPress = 1 << 4,
201 : State_AltHold = 1 << 5,
202 : State_AltRelease = 1 << 6,
203 : State_EscPress = 1 << 7,
204 : State_EscHold = 1 << 8,
205 : State_EscRelease = 1 << 9,
206 : State_ScrollUp = 1 << 10,
207 : State_ScrollDown = 1 << 11,
208 : State_Hidden = 1 << 12,
209 :
210 : State_HoldMask = State_Hold | State_AltHold | State_EscHold
211 : };
212 :
213 : enum ElementBlend
214 : {
215 : Blend_Alpha,
216 : Blend_Mod
217 : };
218 :
219 : enum ChangeDrawFlags
220 : {
221 : Change_Shader = 1 << 0,
222 : Change_Color = 1 << 1,
223 : Change_Blend = 1 << 2
224 : };
225 :
226 : Object *buildparent = nullptr;
227 : int buildchild = -1;
228 :
229 : int changed = 0;
230 :
231 : const Object *drawing = nullptr;
232 :
233 : int blendtype = Blend_Alpha;
234 :
235 0 : void changeblend(int type, GLenum src, GLenum dst)
236 : {
237 0 : if(blendtype != type)
238 : {
239 0 : blendtype = type;
240 0 : glBlendFunc(src, dst);
241 : }
242 0 : }
243 : }
244 :
245 :
246 :
247 : //type: the type of object to build
248 : //o the name of the temp variable to use
249 : //setup: a snippet of c++ to run to set up the object
250 : //contents: the content passed to the buildchildren() call
251 : #define BUILD(type, o, setup, contents) do { \
252 : if(buildparent) \
253 : { \
254 : type *o = buildparent->buildtype<type>(); \
255 : setup; \
256 : o->buildchildren(contents); \
257 : } \
258 : } while(0)
259 :
260 :
261 0 : void resetblend()
262 : {
263 0 : changeblend(Blend_Alpha, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
264 0 : }
265 :
266 0 : void modblend()
267 : {
268 0 : changeblend(Blend_Mod, GL_ZERO, GL_SRC_COLOR);
269 0 : }
270 :
271 : class Object
272 : {
273 : public:
274 : float x, y, w, h;
275 : std::vector<Object *> children;
276 : uchar adjust;
277 : ushort state, childstate;
278 : Object *parent;
279 :
280 : //note reverse iteration
281 : #define LOOP_CHILDREN_REV(o, body) do { \
282 : for(int i = static_cast<int>(children.size()); --i >=0;) \
283 : { \
284 : Object *o = children.at(i); \
285 : body; \
286 : } \
287 : } while(0)
288 :
289 : #define LOOP_CHILD_RANGE(start, end, o, body) do { \
290 : for(int i = start; i < end; i++) \
291 : { \
292 : Object *o = children.at(i); \
293 : body; \
294 : } \
295 : } while(0)
296 :
297 1 : Object() : x(), y(), w(), h(), children(), adjust(0), state(0), childstate(0), parent() {}
298 :
299 : Object(const Object &o) = delete;
300 0 : virtual ~Object()
301 0 : {
302 0 : clearchildren();
303 0 : }
304 : template<class T>
305 0 : T *buildtype()
306 : {
307 : T *t;
308 0 : if(static_cast<int>(children.size()) > buildchild )
309 : {
310 0 : Object *o = children[buildchild];
311 0 : if(o->istype<T>())
312 : {
313 0 : t = static_cast<T *>(o);
314 : }
315 : else
316 : {
317 0 : delete o;
318 0 : t = new T;
319 0 : children[buildchild] = t;
320 : }
321 : }
322 : else
323 : {
324 0 : t = new T;
325 0 : children.push_back(t);
326 : }
327 0 : t->reset(this);
328 0 : buildchild++;
329 0 : return t;
330 : }
331 :
332 0 : virtual void layout()
333 : {
334 0 : w = h = 0;
335 0 : for(Object *o : children)
336 : {
337 0 : o->x = o->y = 0;
338 0 : o->layout();
339 0 : w = std::max(w, o->x + o->w);
340 0 : h = std::max(h, o->y + o->h);
341 : }
342 0 : }
343 :
344 0 : void buildchildren(uint *contents)
345 : {
346 0 : if((*contents&Code_OpMask) == Code_Exit)
347 : {
348 0 : children.erase(children.begin(), children.end());
349 : }
350 : else
351 : {
352 0 : Object *oldparent = buildparent;
353 0 : int oldchild = buildchild;
354 0 : buildparent = this;
355 0 : buildchild = 0;
356 0 : executeret(contents);
357 0 : while(static_cast<int>(children.size()) > buildchild)
358 : {
359 0 : children.pop_back();
360 : }
361 0 : buildparent = oldparent;
362 0 : buildchild = oldchild;
363 : }
364 0 : resetstate();
365 0 : }
366 :
367 0 : void clearstate(int flags)
368 : {
369 0 : state &= ~flags;
370 0 : if(childstate & flags)
371 : {
372 0 : for(Object *o : children)
373 : {
374 0 : if((o->state | o->childstate) & flags) o->clearstate(flags);
375 : }
376 0 : childstate &= ~flags;
377 : }
378 0 : }
379 :
380 0 : void enddraw(int change) const
381 : {
382 0 : enddraw();
383 0 : changed &= ~change;
384 0 : if(changed)
385 : {
386 0 : if(changed & Change_Shader)
387 : {
388 0 : hudshader->set();
389 : }
390 0 : if(changed & Change_Color)
391 : {
392 0 : gle::colorf(1, 1, 1);
393 : }
394 0 : if(changed & Change_Blend)
395 : {
396 0 : resetblend();
397 : }
398 : }
399 0 : }
400 :
401 0 : void resetchildstate()
402 : {
403 0 : resetstate();
404 0 : for(Object *o : children)
405 : {
406 0 : o->resetchildstate();
407 : }
408 0 : }
409 :
410 0 : bool hasstate(int flags) const
411 : {
412 0 : return ((state & ~childstate) & flags) != 0;
413 : }
414 :
415 0 : bool haschildstate(int flags) const
416 : {
417 0 : return ((state | childstate) & flags) != 0;
418 : }
419 :
420 0 : virtual void draw(float sx, float sy)
421 : {
422 0 : for(Object *o : children)
423 : {
424 0 : if(!isfullyclipped(sx + o->x, sy + o->y, o->w, o->h))
425 : {
426 0 : o->draw(sx + o->x, sy + o->y);
427 : }
428 : }
429 0 : }
430 :
431 : /* DOSTATES: executes the DOSTATE macro for the applicable special keys
432 : *
433 : * ***NOTE***: DOSTATE is not defined by default, and is defined manually
434 : * in the code at every location the DOSTATES macro is called --
435 : * you cannot merely call DOSTATES whenever you wish;
436 : * you must also define DOSTATE (and then undefine it)
437 : */
438 : #define DOSTATES \
439 : DOSTATE(State_Hover, hover) \
440 : DOSTATE(State_Press, press) \
441 : DOSTATE(State_Hold, hold) \
442 : DOSTATE(State_Release, release) \
443 : DOSTATE(State_AltHold, althold) \
444 : DOSTATE(State_AltPress, altpress) \
445 : DOSTATE(State_AltRelease, altrelease) \
446 : DOSTATE(State_EscHold, eschold) \
447 : DOSTATE(State_EscPress, escpress) \
448 : DOSTATE(State_EscRelease, escrelease) \
449 : DOSTATE(State_ScrollUp, scrollup) \
450 : DOSTATE(State_ScrollDown, scrolldown)
451 :
452 0 : bool setstate(int state, float cx, float cy, int mask = 0, bool inside = true, int setflags = 0)
453 : {
454 0 : switch(state)
455 : {
456 : #define DOSTATE(flags, func) case flags: func##children(cx, cy, mask, inside, setflags | flags); return haschildstate(flags);
457 0 : DOSTATES
458 : #undef DOSTATE
459 : }
460 0 : return false;
461 : }
462 :
463 0 : void setup()
464 : {
465 0 : }
466 :
467 : template<class T>
468 0 : bool istype() const
469 : {
470 0 : return T::typestr() == gettype();
471 : }
472 :
473 0 : virtual bool rawkey(int code, bool isdown)
474 : {
475 0 : LOOP_CHILDREN_REV(o,
476 : {
477 : if(o->rawkey(code, isdown))
478 : {
479 : return true;
480 : }
481 : });
482 0 : return false;
483 : }
484 :
485 0 : virtual bool key(int code, bool isdown)
486 : {
487 0 : LOOP_CHILDREN_REV(o,
488 : {
489 : if(o->key(code, isdown))
490 : {
491 : return true;
492 : }
493 : });
494 0 : return false;
495 : }
496 :
497 0 : virtual bool textinput(const char *str, int len)
498 : {
499 0 : LOOP_CHILDREN_REV(o,
500 : {
501 : if(o->textinput(str, len))
502 : {
503 : return true;
504 : }
505 : });
506 0 : return false;
507 : }
508 :
509 0 : virtual int childcolumns() const
510 : {
511 0 : return static_cast<int>(children.size());
512 : }
513 :
514 0 : void adjustlayout(float px, float py, float pw, float ph)
515 : {
516 0 : switch(adjust & Align_HMask)
517 : {
518 0 : case Align_Left:
519 : {
520 0 : x = px;
521 0 : break;
522 : }
523 0 : case Align_HCenter:
524 : {
525 0 : x = px + (pw - w) / 2;
526 0 : break;
527 : }
528 0 : case Align_Right:
529 : {
530 0 : x = px + pw - w;
531 0 : break;
532 : }
533 : }
534 :
535 0 : switch(adjust & Align_VMask)
536 : {
537 0 : case Align_Top:
538 : {
539 0 : y = py;
540 0 : break;
541 : }
542 0 : case Align_VCenter:
543 : {
544 0 : y = py + (ph - h) / 2;
545 0 : break;
546 : }
547 0 : case Align_Bottom:
548 : {
549 0 : y = py + ph - h;
550 0 : break;
551 : }
552 : }
553 :
554 0 : if(adjust & Clamp_Mask)
555 : {
556 0 : if(adjust & Clamp_Left)
557 : {
558 0 : w += x - px;
559 0 : x = px;
560 : }
561 0 : if(adjust & Clamp_Right)
562 : {
563 0 : w = px + pw - x;
564 : }
565 0 : if(adjust & Clamp_Top)
566 : {
567 0 : h += y - py;
568 0 : y = py;
569 : }
570 0 : if(adjust & Clamp_Bottom)
571 : {
572 0 : h = py + ph - y;
573 : }
574 : }
575 :
576 0 : adjustchildren();
577 0 : }
578 :
579 0 : void setalign(int xalign, int yalign)
580 : {
581 0 : adjust &= ~Align_Mask;
582 0 : adjust |= (std::clamp(xalign, -2, 1)+2) << Align_HShift;
583 0 : adjust |= (std::clamp(yalign, -2, 1)+2) << Align_VShift;
584 0 : }
585 :
586 0 : void setclamp(int left, int right, int top, int bottom)
587 : {
588 0 : adjust &= ~Clamp_Mask;
589 0 : if(left)
590 : {
591 0 : adjust |= Clamp_Left;
592 : }
593 0 : if(right)
594 : {
595 0 : adjust |= Clamp_Right;
596 : }
597 0 : if(top)
598 : {
599 0 : adjust |= Clamp_Top;
600 : }
601 0 : if(bottom)
602 : {
603 0 : adjust |= Clamp_Bottom;
604 : }
605 0 : }
606 : protected:
607 :
608 0 : void reset()
609 : {
610 0 : resetlayout();
611 0 : parent = nullptr;
612 0 : adjust = Align_HCenter | Align_VCenter;
613 0 : }
614 :
615 0 : virtual uchar childalign() const
616 : {
617 0 : return Align_HCenter | Align_VCenter;
618 : }
619 :
620 0 : void reset(Object *parent_)
621 : {
622 0 : resetlayout();
623 0 : parent = parent_;
624 0 : adjust = parent->childalign();
625 0 : }
626 :
627 0 : void clearchildren()
628 : {
629 0 : for(Object *o : children)
630 : {
631 0 : delete o;
632 : }
633 0 : children.clear();
634 0 : }
635 :
636 0 : void adjustchildrento(float px, float py, float pw, float ph)
637 : {
638 0 : for(Object *o : children)
639 : {
640 0 : o->adjustlayout(px, py, pw, ph);
641 : }
642 0 : }
643 :
644 0 : virtual void adjustchildren()
645 : {
646 0 : adjustchildrento(0, 0, w, h);
647 0 : }
648 :
649 0 : virtual bool target(float, float) //note unnamed function parameters
650 : {
651 0 : return false;
652 : }
653 :
654 0 : virtual void startdraw() const {}
655 0 : virtual void enddraw() const {}
656 :
657 0 : void changedraw(int change = 0) const
658 : {
659 0 : if(!drawing)
660 : {
661 0 : startdraw();
662 0 : changed = change;
663 : }
664 0 : else if(drawing->gettype() != gettype())
665 : {
666 0 : drawing->enddraw(change);
667 0 : startdraw();
668 0 : changed = change;
669 : }
670 0 : drawing = this;
671 0 : }
672 :
673 0 : void resetstate()
674 : {
675 0 : state &= State_HoldMask;
676 0 : childstate &= State_HoldMask;
677 0 : }
678 :
679 0 : void changechildstate(Object * o, void (Object::*member)(float, float, int, bool, int), float ox, float oy, int mask, bool inside, int setflags)
680 : {
681 0 : (o->*member)(ox, oy, mask, inside, setflags); /*child's->func##children fxn called*/
682 0 : childstate |= (o->state | o->childstate) & (setflags); /*set childstate*/
683 0 : }
684 :
685 0 : void propagatestate(float cx, float cy, int mask, bool inside, int setflags, void (UI::Object::*method)(float, float, int, bool, int))
686 : {
687 0 : for(int i = static_cast<int>(children.size()); --i >= 0;)
688 : {
689 0 : Object *o = children.at(i);
690 0 : if(((o->state | o->childstate) & mask) != mask)
691 : {
692 0 : continue;
693 : }
694 0 : float ox = cx - o->x; /*offset x*/
695 0 : float oy = cy - o->y; /*offset y*/
696 0 : if(!inside)
697 : {
698 0 : ox = std::clamp(ox, 0.0f, o->w); /*clamp offsets to Object bounds in x*/
699 0 : oy = std::clamp(oy, 0.0f, o->h); /*clamp offsets to Object bounds in y*/
700 0 : changechildstate(o, method, ox, oy, mask, inside, setflags);
701 : }
702 0 : else if(ox >= 0 && ox < o->w && oy >= 0 && oy < o->h) /*if in bounds execute body*/
703 : {
704 0 : changechildstate(o, method, ox, oy, mask, inside, setflags);
705 : }
706 : }
707 0 : }
708 :
709 : #define DOSTATE(flags, func) \
710 : virtual void func##children(float cx, float cy, int mask, bool inside, int setflags) \
711 : { \
712 : propagatestate(cx, cy, mask, inside, setflags, &UI::Object::func##children); \
713 : if(target(cx, cy)) \
714 : { \
715 : state |= (setflags); \
716 : } \
717 : func(cx, cy); \
718 : } \
719 : virtual void func(float, float) {} /*note unnamed function parameters*/
720 0 : DOSTATES
721 : #undef DOSTATE
722 :
723 0 : virtual const char *gettype() const
724 : {
725 0 : return typestr();
726 : }
727 :
728 0 : virtual const char *gettypename() const
729 : {
730 0 : return gettype();
731 : }
732 :
733 0 : Object *find(const char *name, bool recurse = true, const Object *exclude = nullptr) const
734 : {
735 0 : for(Object *o : children)
736 : {
737 0 : if(o != exclude && o->isnamed(name))
738 : {
739 0 : return o;
740 : }
741 : }
742 0 : if(recurse)
743 : {
744 0 : for(Object *o : children)
745 : {
746 0 : if(o != exclude)
747 : {
748 0 : Object *found = o->find(name);
749 0 : if(found)
750 : {
751 0 : return found;
752 : }
753 : }
754 : }
755 : }
756 0 : return nullptr;
757 : }
758 :
759 0 : Object *findsibling(const char *name) const
760 : {
761 0 : for(const Object *prev = this, *cur = parent; cur; prev = cur, cur = cur->parent)
762 : {
763 0 : Object *o = cur->find(name, true, prev);
764 0 : if(o)
765 : {
766 0 : return o;
767 : }
768 : }
769 0 : return nullptr;
770 : }
771 :
772 : private:
773 :
774 0 : virtual const char *getname() const
775 : {
776 0 : return gettype();
777 : }
778 :
779 0 : void resetlayout()
780 : {
781 0 : x = y = w = h = 0;
782 0 : }
783 :
784 0 : static const char *typestr()
785 : {
786 0 : return "#Object";
787 : }
788 :
789 0 : bool isnamed(const char *name) const
790 : {
791 0 : return name[0] == '#' ? name == gettypename() : !std::strcmp(name, getname());
792 : }
793 :
794 : };
795 :
796 0 : static void stopdrawing()
797 : {
798 0 : if(drawing)
799 : {
800 0 : drawing->enddraw(0);
801 0 : drawing = nullptr;
802 : }
803 0 : }
804 :
805 : struct Window;
806 :
807 : static Window *window = nullptr;
808 :
809 : struct Window final : Object
810 : {
811 : std::string name;
812 : uint *contents, *onshow, *onhide;
813 : bool allowinput, eschide, abovehud;
814 : float px, py, pw, ph;
815 : vec2 sscale, soffset;
816 :
817 1 : Window(const char *name, const char *contents, const char *onshow, const char *onhide) :
818 1 : name(name),
819 1 : contents(compilecode(contents)),
820 1 : onshow(onshow && onshow[0] ? compilecode(onshow) : nullptr),
821 1 : onhide(onhide && onhide[0] ? compilecode(onhide) : nullptr),
822 1 : allowinput(true), eschide(true), abovehud(false),
823 1 : px(0), py(0), pw(0), ph(0),
824 2 : sscale(1, 1), soffset(0, 0)
825 : {
826 1 : }
827 0 : ~Window()
828 0 : {
829 0 : freecode(contents);
830 0 : freecode(onshow);
831 0 : freecode(onhide);
832 0 : }
833 :
834 0 : static const char *typestr()
835 : {
836 0 : return "#Window";
837 : }
838 :
839 0 : const char *gettype() const override final
840 : {
841 0 : return typestr();
842 : }
843 :
844 0 : const char *getname() const override final
845 : {
846 0 : return name.c_str();
847 : }
848 :
849 : void build();
850 :
851 0 : void hide()
852 : {
853 0 : if(onhide)
854 : {
855 0 : execute(onhide);
856 : }
857 0 : }
858 :
859 0 : void show()
860 : {
861 0 : state |= State_Hidden;
862 0 : clearstate(State_HoldMask);
863 0 : if(onshow)
864 : {
865 0 : execute(onshow);
866 : }
867 0 : }
868 :
869 0 : void setup()
870 : {
871 0 : Object::setup();
872 0 : allowinput = eschide = true;
873 0 : abovehud = false;
874 0 : px = py = pw = ph = 0;
875 0 : }
876 :
877 0 : void layout() override final
878 : {
879 0 : if(state & State_Hidden)
880 : {
881 0 : w = h = 0;
882 0 : return;
883 : }
884 0 : window = this;
885 0 : Object::layout();
886 0 : window = nullptr;
887 : }
888 :
889 0 : void draw(float sx, float sy) override final
890 : {
891 0 : if(state & State_Hidden)
892 : {
893 0 : return;
894 : }
895 0 : window = this;
896 :
897 0 : projection();
898 0 : hudshader->set();
899 :
900 0 : glEnable(GL_BLEND);
901 0 : blendtype = Blend_Alpha;
902 0 : glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
903 0 : gle::colorf(1, 1, 1);
904 :
905 0 : changed = 0;
906 0 : drawing = nullptr;
907 :
908 0 : Object::draw(sx, sy);
909 :
910 0 : stopdrawing();
911 :
912 0 : glDisable(GL_BLEND);
913 :
914 0 : window = nullptr;
915 : }
916 :
917 0 : void draw()
918 : {
919 0 : draw(x, y);
920 0 : }
921 :
922 0 : void adjustchildren() override final
923 : {
924 0 : if(state & State_Hidden)
925 : {
926 0 : return;
927 : }
928 0 : window = this;
929 0 : Object::adjustchildren();
930 0 : window = nullptr;
931 : }
932 :
933 0 : void adjustlayout()
934 : {
935 0 : float aspect = static_cast<float>(hudw())/hudh();
936 0 : ph = std::max(std::max(h, w/aspect), 1.0f);
937 0 : pw = aspect*ph;
938 0 : Object::adjustlayout(0, 0, pw, ph);
939 0 : }
940 :
941 : #define DOSTATE(flags, func) \
942 : void func##children(float cx, float cy, int mask, bool inside, int setflags) override final \
943 : { \
944 : if(!allowinput || state&State_Hidden || pw <= 0 || ph <= 0) \
945 : { \
946 : return; \
947 : } \
948 : cx = cx*pw + px-x; \
949 : cy = cy*ph + py-y; \
950 : if(!inside || (cx >= 0 && cy >= 0 && cx < w && cy < h)) \
951 : { \
952 : Object::func##children(cx, cy, mask, inside, setflags); \
953 : } \
954 : }
955 0 : DOSTATES
956 : #undef DOSTATE
957 :
958 : void escrelease(float cx, float cy) override final;
959 :
960 0 : void projection()
961 : {
962 0 : hudmatrix.ortho(px, px + pw, py + ph, py, -1, 1);
963 0 : resethudmatrix();
964 0 : sscale = vec2(hudmatrix.a.x, hudmatrix.b.y).mul(0.5f);
965 0 : soffset = vec2(hudmatrix.d.x, hudmatrix.d.y).mul(0.5f).add(0.5f);
966 0 : }
967 :
968 0 : void calcscissor(float x1, float y1, float x2, float y2, int &sx1, int &sy1, int &sx2, int &sy2, bool clip = true) const
969 : {
970 0 : vec2 s1 = vec2(x1, y2).mul(sscale).add(soffset),
971 0 : s2 = vec2(x2, y1).mul(sscale).add(soffset);
972 0 : sx1 = static_cast<int>(std::floor(s1.x*hudw() + 0.5f));
973 0 : sy1 = static_cast<int>(std::floor(s1.y*hudh() + 0.5f));
974 0 : sx2 = static_cast<int>(std::floor(s2.x*hudw() + 0.5f));
975 0 : sy2 = static_cast<int>(std::floor(s2.y*hudh() + 0.5f));
976 0 : if(clip)
977 : {
978 0 : sx1 = std::clamp(sx1, 0, hudw());
979 0 : sy1 = std::clamp(sy1, 0, hudh());
980 0 : sx2 = std::clamp(sx2, 0, hudw());
981 0 : sy2 = std::clamp(sy2, 0, hudh());
982 : }
983 0 : }
984 :
985 0 : float calcabovehud() const
986 : {
987 0 : return 1 - (y*sscale.y + soffset.y);
988 : }
989 : };
990 :
991 : static std::unordered_map<std::string, Window *> windows;
992 :
993 0 : void ClipArea::scissor()
994 : {
995 : int sx1, sy1, sx2, sy2;
996 0 : window->calcscissor(x1, y1, x2, y2, sx1, sy1, sx2, sy2);
997 0 : glScissor(sx1, sy1, sx2-sx1, sy2-sy1);
998 0 : }
999 :
1000 : struct World final : Object
1001 : {
1002 0 : static const char *typestr() { return "#World"; }
1003 0 : const char *gettype() const override final
1004 : {
1005 0 : return typestr();
1006 : }
1007 :
1008 : #define LOOP_WINDOWS(o, body) do { \
1009 : for(uint i = 0; i < children.size(); i++) \
1010 : { \
1011 : Window *o = static_cast<Window *>(children[i]); \
1012 : body; \
1013 : } \
1014 : } while(0)
1015 : //note reverse iteration
1016 : #define LOOP_WINDOWS_REV(o, body) do { \
1017 : for(int i = static_cast<int>(children.size()); --i >=0;) \
1018 : { \
1019 : Window *o = static_cast<Window *>(children[i]); \
1020 : body; \
1021 : } \
1022 : } while(0)
1023 :
1024 0 : void adjustchildren() override final
1025 : {
1026 0 : LOOP_WINDOWS(w, w->adjustlayout());
1027 0 : }
1028 :
1029 : #define DOSTATE(flags, func) \
1030 : void func##children(float cx, float cy, int mask, bool inside, int setflags) override final \
1031 : { \
1032 : LOOP_WINDOWS_REV(w, \
1033 : { \
1034 : if(((w->state | w->childstate) & mask) != mask) \
1035 : { \
1036 : continue; \
1037 : } \
1038 : w->func##children(cx, cy, mask, inside, setflags); \
1039 : int wflags = (w->state | w->childstate) & (setflags); \
1040 : if(wflags) \
1041 : { \
1042 : childstate |= wflags; \
1043 : break; \
1044 : } \
1045 : }); \
1046 : }
1047 0 : DOSTATES
1048 : #undef DOSTATE
1049 :
1050 0 : void build()
1051 : {
1052 0 : reset();
1053 0 : setup();
1054 0 : LOOP_WINDOWS(w,
1055 : {
1056 : w->build();
1057 : if(children.size() <= i )
1058 : {
1059 : break;
1060 : }
1061 : if(children.at(i) != w)
1062 : {
1063 : i--;
1064 : }
1065 : });
1066 0 : resetstate();
1067 0 : }
1068 :
1069 0 : bool show(Window *w)
1070 : {
1071 : //if w is not found anywhere
1072 0 : if(std::find(children.begin(), children.end(), w) != children.end())
1073 : {
1074 0 : return false;
1075 : }
1076 0 : w->resetchildstate();
1077 0 : children.push_back(w);
1078 0 : w->show();
1079 0 : return true;
1080 : }
1081 :
1082 0 : void hide(Window *w, int index)
1083 : {
1084 0 : children.erase(children.begin() + index);
1085 0 : childstate = 0;
1086 0 : for(Object *o : children)
1087 : {
1088 0 : childstate |= o->state | o->childstate;
1089 : }
1090 0 : w->hide();
1091 0 : }
1092 :
1093 0 : bool hide(Window *w)
1094 : {
1095 0 : if(!w) //check that a window was passed
1096 : {
1097 0 : return false;
1098 : }
1099 0 : if(!children.size()) //check that there are children
1100 : {
1101 0 : return false;
1102 : }
1103 0 : else if(std::find(children.begin(), children.end(), w) != children.end())
1104 : {
1105 0 : hide(w, std::distance(children.begin(), std::find(children.begin(), children.end(), w)));
1106 0 : return true;
1107 : }
1108 : else
1109 : {
1110 0 : return false;
1111 : }
1112 : }
1113 :
1114 0 : bool hidetop()
1115 : {
1116 0 : LOOP_WINDOWS_REV(w,
1117 : {
1118 : if(w->allowinput && !(w->state & State_Hidden))
1119 : {
1120 : hide(w, i);
1121 : return true;
1122 : }
1123 : });
1124 0 : return false;
1125 : }
1126 :
1127 0 : int hideall()
1128 : {
1129 0 : int hidden = 0;
1130 0 : LOOP_WINDOWS_REV(w,
1131 : {
1132 : hide(w, i);
1133 : hidden++;
1134 : });
1135 0 : return hidden;
1136 : }
1137 :
1138 0 : bool allowinput() const
1139 : {
1140 0 : LOOP_WINDOWS(w,
1141 : {
1142 : if(w->allowinput && !(w->state & State_Hidden))
1143 : {
1144 : return true;
1145 : }
1146 : });
1147 0 : return false;
1148 : }
1149 :
1150 0 : void draw(float, float) override final //note unnamed function parameters
1151 : {
1152 0 : }
1153 :
1154 0 : void draw()
1155 : {
1156 0 : if(children.empty())
1157 : {
1158 0 : return;
1159 : }
1160 0 : LOOP_WINDOWS(w, w->draw());
1161 : }
1162 :
1163 0 : float abovehud()
1164 : {
1165 0 : float y = 1;
1166 0 : LOOP_WINDOWS(w,
1167 : {
1168 : if(w->abovehud && !(w->state & State_Hidden))
1169 : {
1170 : y = std::min(y, w->calcabovehud());
1171 : }
1172 : });
1173 0 : return y;
1174 : }
1175 : };
1176 :
1177 : #undef LOOP_WINDOWS_REV
1178 : #undef LOOP_WINDOWS
1179 :
1180 : static World *world = nullptr;
1181 :
1182 0 : void Window::escrelease(float, float) //note unnamed function parameters
1183 : {
1184 0 : if(eschide)
1185 : {
1186 0 : world->hide(this);
1187 : }
1188 0 : }
1189 :
1190 0 : void Window::build()
1191 : {
1192 0 : reset(world);
1193 0 : setup();
1194 0 : window = this;
1195 0 : buildchildren(contents);
1196 0 : window = nullptr;
1197 0 : }
1198 :
1199 : struct HorizontalList final : Object
1200 : {
1201 : float space, subw;
1202 :
1203 0 : HorizontalList () : space(), subw() {}
1204 :
1205 0 : static const char *typestr()
1206 : {
1207 0 : return "#HorizontalList";
1208 : }
1209 :
1210 0 : const char *gettype() const override final
1211 : {
1212 0 : return typestr();
1213 : }
1214 :
1215 0 : void setup(float space_ = 0)
1216 : {
1217 0 : Object::setup();
1218 0 : space = space_;
1219 0 : }
1220 :
1221 0 : uchar childalign() const override final
1222 : {
1223 0 : return Align_VCenter;
1224 : }
1225 :
1226 0 : void layout() override final
1227 : {
1228 0 : subw = h = 0;
1229 0 : for(Object *o : children)
1230 : {
1231 0 : o->x = subw;
1232 0 : o->y = 0;
1233 0 : o->layout();
1234 0 : subw += o->w;
1235 0 : h = std::max(h, o->y + o->h);
1236 : }
1237 0 : w = subw + space*std::max(static_cast<int>(children.size()) - 1, 0);
1238 0 : }
1239 :
1240 0 : void adjustchildren() override final
1241 : {
1242 0 : if(children.empty())
1243 : {
1244 0 : return;
1245 : }
1246 0 : float offset = 0,
1247 0 : sx = 0,
1248 0 : cspace = (w - subw) / std::max(static_cast<int>(children.size()) - 1, 1),
1249 0 : cstep = (w - subw) / children.size();
1250 0 : for(int i = 0; i < static_cast<int>(children.size()); i++)
1251 : {
1252 0 : Object *o = children[i];
1253 0 : o->x = offset;
1254 0 : offset += o->w + cspace;
1255 0 : float sw = o->w + cstep;
1256 0 : o->adjustlayout(sx, 0, sw, h);
1257 0 : sx += sw;
1258 : }
1259 : }
1260 : };
1261 :
1262 : struct VerticalList final : Object
1263 : {
1264 : float space, subh;
1265 :
1266 0 : VerticalList() : space(), subh() {}
1267 :
1268 0 : static const char *typestr()
1269 : {
1270 0 : return "#VerticalList";
1271 : }
1272 :
1273 0 : const char *gettype() const override final
1274 : {
1275 0 : return typestr();
1276 : }
1277 :
1278 0 : void setup(float space_ = 0)
1279 : {
1280 0 : Object::setup();
1281 0 : space = space_;
1282 0 : }
1283 :
1284 0 : uchar childalign() const override final
1285 : {
1286 0 : return Align_HCenter;
1287 : }
1288 :
1289 0 : void layout() override final
1290 : {
1291 0 : w = subh = 0;
1292 0 : for(Object *o : children)
1293 : {
1294 0 : o->x = 0;
1295 0 : o->y = subh;
1296 0 : o->layout();
1297 0 : subh += o->h;
1298 0 : w = std::max(w, o->x + o->w);
1299 : }
1300 0 : h = subh + space*std::max(static_cast<int>(children.size()) - 1, 0);
1301 0 : }
1302 :
1303 0 : void adjustchildren() override final
1304 : {
1305 0 : if(children.empty())
1306 : {
1307 0 : return;
1308 : }
1309 :
1310 0 : float offset = 0,
1311 0 : sy = 0,
1312 0 : rspace = (h - subh) / std::max(static_cast<int>(children.size()) - 1, 1),
1313 0 : rstep = (h - subh) / children.size();
1314 0 : for(Object *o : children)
1315 : {
1316 0 : o->y = offset;
1317 0 : offset += o->h + rspace;
1318 0 : float sh = o->h + rstep;
1319 0 : o->adjustlayout(0, sy, w, sh);
1320 0 : sy += sh;
1321 : }
1322 : }
1323 : };
1324 :
1325 : struct Grid final : Object
1326 : {
1327 : int columns;
1328 : float spacew, spaceh, subw, subh;
1329 : std::vector<float> widths, heights;
1330 :
1331 0 : Grid() : columns(), spacew(), spaceh(), subw(), subh() {}
1332 :
1333 0 : static const char *typestr()
1334 : {
1335 0 : return "#Grid";
1336 : }
1337 :
1338 0 : const char *gettype() const override final
1339 : {
1340 0 : return typestr();
1341 : }
1342 :
1343 0 : void setup(int columns_, float spacew_ = 0, float spaceh_ = 0)
1344 : {
1345 0 : Object::setup();
1346 0 : columns = columns_;
1347 0 : spacew = spacew_;
1348 0 : spaceh = spaceh_;
1349 0 : }
1350 :
1351 0 : uchar childalign() const override final
1352 : {
1353 0 : return 0;
1354 : }
1355 :
1356 0 : void layout() override final
1357 : {
1358 0 : widths.clear();
1359 0 : heights.clear();
1360 :
1361 0 : int column = 0,
1362 0 : row = 0;
1363 0 : for(Object *o : children)
1364 : {
1365 0 : o->layout();
1366 0 : if(column >= static_cast<int>(widths.size()))
1367 : {
1368 0 : widths.push_back(o->w);
1369 : }
1370 0 : else if(o->w > widths[column])
1371 : {
1372 0 : widths[column] = o->w;
1373 : }
1374 0 : if(row >= static_cast<int>(heights.size()))
1375 : {
1376 0 : heights.push_back(o->h);
1377 : }
1378 0 : else if(o->h > heights[row])
1379 : {
1380 0 : heights[row] = o->h;
1381 : }
1382 0 : column = (column + 1) % columns;
1383 0 : if(!column)
1384 : {
1385 0 : row++;
1386 : }
1387 : }
1388 :
1389 0 : subw = subh = 0;
1390 0 : for(const float &i : widths)
1391 : {
1392 0 : subw += i;
1393 : }
1394 0 : for(const float &i : heights)
1395 : {
1396 0 : subh += i;
1397 : }
1398 0 : w = subw + spacew*std::max(static_cast<int>(widths.size()) - 1, 0);
1399 0 : h = subh + spaceh*std::max(static_cast<int>(heights.size()) - 1, 0);
1400 0 : }
1401 :
1402 0 : void adjustchildren() override final
1403 : {
1404 0 : if(children.empty())
1405 : {
1406 0 : return;
1407 : }
1408 0 : int row = 0,
1409 0 : column = 0;
1410 0 : float offsety = 0,
1411 0 : sy = 0,
1412 0 : offsetx = 0,
1413 0 : sx = 0,
1414 0 : cspace = (w - subw) / std::max(static_cast<int>(widths.size()) - 1, 1),
1415 0 : cstep = (w - subw) / widths.size(),
1416 0 : rspace = (h - subh) / std::max(static_cast<int>(heights.size()) - 1, 1),
1417 0 : rstep = (h - subh) / heights.size();
1418 0 : for(Object *o : children)
1419 : {
1420 0 : o->x = offsetx;
1421 0 : o->y = offsety;
1422 0 : o->adjustlayout(sx, sy, widths[column] + cstep, heights[row] + rstep);
1423 0 : offsetx += widths[column] + cspace;
1424 0 : sx += widths[column] + cstep;
1425 0 : column = (column + 1) % columns;
1426 0 : if(!column)
1427 : {
1428 0 : offsetx = sx = 0;
1429 0 : offsety += heights[row] + rspace;
1430 0 : sy += heights[row] + rstep;
1431 0 : row++;
1432 : }
1433 : }
1434 : }
1435 : };
1436 :
1437 : struct TableHeader : Object
1438 : {
1439 0 : TableHeader() : columns(-1) {}
1440 :
1441 0 : static const char *typestr()
1442 : {
1443 0 : return "#TableHeader";
1444 : }
1445 0 : const char *gettype() const override
1446 : {
1447 0 : return typestr();
1448 : }
1449 :
1450 0 : uchar childalign() const override final
1451 : {
1452 0 : return columns < 0 ? Align_VCenter : Align_HCenter | Align_VCenter;
1453 : }
1454 :
1455 0 : int childcolumns() const override final
1456 : {
1457 0 : return columns;
1458 : }
1459 :
1460 0 : void buildchildren(const uint *columndata, const uint *contents)
1461 : {
1462 0 : Object *oldparent = buildparent;
1463 0 : int oldchild = buildchild;
1464 0 : buildparent = this;
1465 0 : buildchild = 0;
1466 0 : executeret(columndata);
1467 0 : if(columns != buildchild)
1468 : {
1469 0 : while(static_cast<int>(children.size()) > buildchild)
1470 : {
1471 0 : children.pop_back();
1472 : }
1473 : }
1474 0 : columns = buildchild;
1475 0 : if((*contents&Code_OpMask) != Code_Exit)
1476 : {
1477 0 : executeret(contents);
1478 : }
1479 0 : while(static_cast<int>(children.size()) > buildchild)
1480 : {
1481 0 : children.pop_back();
1482 : }
1483 0 : buildparent = oldparent;
1484 0 : buildchild = oldchild;
1485 0 : resetstate();
1486 0 : }
1487 :
1488 0 : void adjustchildren() override final
1489 : {
1490 0 : LOOP_CHILD_RANGE(columns, static_cast<int>(children.size()), o, o->adjustlayout(0, 0, w, h));
1491 0 : }
1492 :
1493 0 : void draw(float sx, float sy) override final
1494 : {
1495 0 : LOOP_CHILD_RANGE(columns, static_cast<int>(children.size()), o,
1496 : {
1497 : if(!isfullyclipped(sx + o->x, sy + o->y, o->w, o->h))
1498 : {
1499 : o->draw(sx + o->x, sy + o->y);
1500 : }
1501 : });
1502 0 : LOOP_CHILD_RANGE(0, columns, o,
1503 : {
1504 : if(!isfullyclipped(sx + o->x, sy + o->y, o->w, o->h))
1505 : {
1506 : o->draw(sx + o->x, sy + o->y);
1507 : }
1508 : });
1509 0 : }
1510 : private:
1511 : int columns;
1512 : };
1513 :
1514 : struct TableRow final: TableHeader
1515 : {
1516 0 : static const char *typestr() { return "#TableRow"; }
1517 0 : const char *gettype() const override final
1518 : {
1519 0 : return typestr();
1520 : }
1521 :
1522 0 : bool target(float, float) override final //note unnamed function parameters
1523 : {
1524 0 : return true;
1525 : }
1526 : };
1527 :
1528 : #define BUILDCOLUMNS(type, o, setup, columndata, contents) do { \
1529 : if(buildparent) \
1530 : { \
1531 : type *o = buildparent->buildtype<type>(); \
1532 : setup; \
1533 : o->buildchildren(columndata, contents); \
1534 : } \
1535 : } while(0)
1536 :
1537 : struct Table final : Object
1538 : {
1539 : float spacew, spaceh, subw, subh;
1540 : std::vector<float> widths;
1541 0 : static const char *typestr()
1542 : {
1543 0 : return "#Table";
1544 : }
1545 0 : const char *gettype() const override final
1546 : {
1547 0 : return typestr();
1548 : }
1549 :
1550 0 : void setup(float spacew_ = 0, float spaceh_ = 0)
1551 : {
1552 0 : Object::setup();
1553 0 : spacew = spacew_;
1554 0 : spaceh = spaceh_;
1555 0 : }
1556 :
1557 0 : uchar childalign() const override final
1558 : {
1559 0 : return 0;
1560 : }
1561 :
1562 0 : void layout() override final
1563 : {
1564 0 : widths.clear();
1565 :
1566 0 : w = subh = 0;
1567 0 : for(Object *o : children)
1568 : {
1569 0 : o->layout();
1570 0 : int cols = o->childcolumns();
1571 0 : while(static_cast<int>(widths.size()) < cols)
1572 : {
1573 0 : widths.push_back(0);
1574 : }
1575 0 : for(int j = 0; j < cols; ++j)
1576 : {
1577 0 : Object *c = o->children[j];
1578 0 : if(c->w > widths[j])
1579 : {
1580 0 : widths[j] = c->w;
1581 : }
1582 : }
1583 0 : w = std::max(w, o->w);
1584 0 : subh += o->h;
1585 : }
1586 :
1587 0 : subw = 0;
1588 0 : for(const float &i : widths)
1589 : {
1590 0 : subw += i;
1591 : }
1592 0 : w = std::max(w, subw + spacew*std::max(static_cast<int>(widths.size()) - 1, 0));
1593 0 : h = subh + spaceh*std::max(static_cast<int>(children.size()) - 1, 0);
1594 0 : }
1595 :
1596 0 : void adjustchildren() override final
1597 : {
1598 0 : if(children.empty())
1599 : {
1600 0 : return;
1601 : }
1602 0 : float offsety = 0,
1603 0 : sy = 0,
1604 0 : cspace = (w - subw) / std::max(static_cast<int>(widths.size()) - 1, 1),
1605 0 : cstep = (w - subw) / widths.size(),
1606 0 : rspace = (h - subh) / std::max(static_cast<int>(children.size()) - 1, 1),
1607 0 : rstep = (h - subh) / children.size();
1608 0 : for(Object *o : children)
1609 : {
1610 0 : o->x = 0;
1611 0 : o->y = offsety;
1612 0 : o->w = w;
1613 0 : offsety += o->h + rspace;
1614 0 : float sh = o->h + rstep;
1615 0 : o->adjustlayout(0, sy, w, sh);
1616 0 : sy += sh;
1617 :
1618 0 : float offsetx = 0;
1619 0 : float sx = 0;
1620 0 : int cols = o->childcolumns();
1621 0 : for(int j = 0; j < cols; ++j)
1622 : {
1623 0 : Object *c = o->children[j];
1624 0 : c->x = offsetx;
1625 0 : offsetx += widths[j] + cspace;
1626 0 : float sw = widths[j] + cstep;
1627 0 : c->adjustlayout(sx, 0, sw, o->h);
1628 0 : sx += sw;
1629 : }
1630 : }
1631 : }
1632 : };
1633 :
1634 : struct Spacer final : Object
1635 : {
1636 : float spacew, spaceh;
1637 :
1638 0 : Spacer() : spacew(), spaceh() {}
1639 :
1640 0 : void setup(float spacew_, float spaceh_)
1641 : {
1642 0 : Object::setup();
1643 0 : spacew = spacew_;
1644 0 : spaceh = spaceh_;
1645 0 : }
1646 :
1647 0 : static const char *typestr()
1648 : {
1649 0 : return "#Spacer";
1650 : }
1651 :
1652 0 : const char *gettype() const override final
1653 : {
1654 0 : return typestr();
1655 : }
1656 :
1657 0 : void layout() override final
1658 : {
1659 0 : w = spacew;
1660 0 : h = spaceh;
1661 0 : for(Object *o : children)
1662 : {
1663 0 : o->x = spacew;
1664 0 : o->y = spaceh;
1665 0 : o->layout();
1666 0 : w = std::max(w, o->x + o->w);
1667 0 : h = std::max(h, o->y + o->h);
1668 : }
1669 0 : w += spacew;
1670 0 : h += spaceh;
1671 0 : }
1672 :
1673 0 : void adjustchildren() override final
1674 : {
1675 0 : adjustchildrento(spacew, spaceh, w - 2*spacew, h - 2*spaceh);
1676 0 : }
1677 : };
1678 :
1679 : struct Offsetter final : Object
1680 : {
1681 : float offsetx, offsety;
1682 :
1683 0 : void setup(float offsetx_, float offsety_)
1684 : {
1685 0 : Object::setup();
1686 0 : offsetx = offsetx_;
1687 0 : offsety = offsety_;
1688 0 : }
1689 :
1690 0 : static const char *typestr() { return "#Offsetter"; }
1691 0 : const char *gettype() const override final
1692 : {
1693 0 : return typestr();
1694 : }
1695 :
1696 0 : void layout() override final
1697 : {
1698 0 : Object::layout();
1699 :
1700 0 : for(Object *o : children)
1701 : {
1702 0 : o->x += offsetx;
1703 0 : o->y += offsety;
1704 : }
1705 :
1706 0 : w += offsetx;
1707 0 : h += offsety;
1708 0 : }
1709 :
1710 0 : void adjustchildren() override final
1711 : {
1712 0 : adjustchildrento(offsetx, offsety, w - offsetx, h - offsety);
1713 0 : }
1714 : };
1715 :
1716 : struct Filler : Object
1717 : {
1718 : float minw, minh;
1719 :
1720 0 : void setup(float minw_, float minh_)
1721 : {
1722 0 : Object::setup();
1723 0 : minw = minw_;
1724 0 : minh = minh_;
1725 0 : }
1726 :
1727 0 : static const char *typestr()
1728 : {
1729 0 : return "#Filler";
1730 : }
1731 :
1732 0 : const char *gettype() const override
1733 : {
1734 0 : return typestr();
1735 : }
1736 :
1737 0 : void layout() override final
1738 : {
1739 0 : Object::layout();
1740 0 : w = std::max(w, minw);
1741 0 : h = std::max(h, minh);
1742 0 : }
1743 : };
1744 :
1745 : struct Target : Filler
1746 : {
1747 0 : static const char *typestr()
1748 : {
1749 0 : return "#Target";
1750 : }
1751 0 : const char *gettype() const override
1752 : {
1753 0 : return typestr();
1754 : }
1755 0 : bool target(float, float) override final //note unnamed function parameters
1756 : {
1757 0 : return true;
1758 : }
1759 : };
1760 :
1761 : struct Color
1762 : {
1763 : uchar r, g, b, a;
1764 :
1765 0 : Color() {}
1766 :
1767 : //converts an int color to components
1768 4 : Color(uint c) : r((c>>16)&0xFF), g((c>>8)&0xFF), b(c&0xFF), a(c>>24 ? c>>24 : 0xFF) {}
1769 :
1770 : //converts an int color w/o alpha and alpha channel to components
1771 : Color(uint c, uchar a) : r((c>>16)&0xFF), g((c>>8)&0xFF), b(c&0xFF), a(a) {}
1772 :
1773 : //assigns components normally
1774 4 : Color(uchar r, uchar g, uchar b, uchar a = 255) : r(r), g(g), b(b), a(a) {}
1775 :
1776 0 : void init() { gle::colorub(r, g, b, a); }
1777 0 : void attrib() { gle::attribub(r, g, b, a); }
1778 :
1779 0 : static void def() { gle::defcolor(4, GL_UNSIGNED_BYTE); }
1780 : };
1781 :
1782 : #undef LOOP_CHILDREN_REV
1783 : #undef LOOP_CHILD_RANGE
1784 :
1785 : struct FillColor : Target
1786 : {
1787 : enum
1788 : {
1789 : SOLID = 0,
1790 : MODULATE
1791 : };
1792 :
1793 : int type;
1794 : Color color;
1795 :
1796 0 : void setup(int type_, const Color &color_, float minw_ = 0, float minh_ = 0)
1797 : {
1798 0 : Target::setup(minw_, minh_);
1799 0 : type = type_;
1800 0 : color = color_;
1801 0 : }
1802 :
1803 0 : static const char *typestr() { return "#FillColor"; }
1804 0 : const char *gettype() const override
1805 : {
1806 0 : return typestr();
1807 : }
1808 :
1809 0 : void startdraw() const override
1810 : {
1811 0 : hudnotextureshader->set();
1812 0 : gle::defvertex(2);
1813 0 : }
1814 :
1815 0 : void draw(float sx, float sy) override
1816 : {
1817 0 : changedraw(Change_Shader | Change_Color | Change_Blend);
1818 0 : if(type==MODULATE)
1819 : {
1820 0 : modblend();
1821 : }
1822 : else
1823 : {
1824 0 : resetblend();
1825 : }
1826 0 : color.init();
1827 0 : gle::begin(GL_TRIANGLE_STRIP);
1828 0 : gle::attribf(sx+w, sy);
1829 0 : gle::attribf(sx, sy);
1830 0 : gle::attribf(sx+w, sy+h);
1831 0 : gle::attribf(sx, sy+h);
1832 0 : gle::end();
1833 :
1834 0 : Object::draw(sx, sy);
1835 0 : }
1836 : };
1837 :
1838 : class Gradient final : public FillColor
1839 : {
1840 : public:
1841 : enum { VERTICAL, HORIZONTAL };
1842 :
1843 : int dir;
1844 :
1845 0 : void setup(int type_, int dir_, const Color &color_, const Color &color2_, float minw_ = 0, float minh_ = 0)
1846 : {
1847 0 : FillColor::setup(type_, color_, minw_, minh_);
1848 0 : dir = dir_;
1849 0 : color2 = color2_;
1850 0 : }
1851 :
1852 0 : static const char *typestr() { return "#Gradient"; }
1853 :
1854 : protected:
1855 0 : const char *gettype() const override final
1856 : {
1857 0 : return typestr();
1858 : }
1859 :
1860 0 : void startdraw() const override final
1861 : {
1862 0 : hudnotextureshader->set();
1863 0 : gle::defvertex(2);
1864 0 : Color::def();
1865 0 : }
1866 :
1867 : private:
1868 : Color color2;
1869 :
1870 0 : void draw(float sx, float sy) override final
1871 : {
1872 0 : changedraw(Change_Shader | Change_Color | Change_Blend);
1873 0 : if(type==MODULATE)
1874 : {
1875 0 : modblend();
1876 : }
1877 : else
1878 : {
1879 0 : resetblend();
1880 : }
1881 0 : gle::begin(GL_TRIANGLE_STRIP);
1882 0 : gle::attribf(sx+w, sy); (dir == HORIZONTAL ? color2 : color).attrib();
1883 0 : gle::attribf(sx, sy); color.attrib();
1884 0 : gle::attribf(sx+w, sy+h); color2.attrib();
1885 0 : gle::attribf(sx, sy+h); (dir == HORIZONTAL ? color : color2).attrib();
1886 0 : gle::end();
1887 :
1888 0 : Object::draw(sx, sy);
1889 0 : }
1890 : };
1891 :
1892 : class Line final : public Filler
1893 : {
1894 : public:
1895 0 : void setup(const Color &color_, float minw_ = 0, float minh_ = 0)
1896 : {
1897 0 : Filler::setup(minw_, minh_);
1898 0 : color = color_;
1899 0 : }
1900 :
1901 0 : static const char *typestr() { return "#Line"; }
1902 :
1903 0 : const char *gettype() const override final
1904 : {
1905 0 : return typestr();
1906 : }
1907 :
1908 : protected:
1909 0 : void startdraw() const override final
1910 : {
1911 0 : hudnotextureshader->set();
1912 0 : gle::defvertex(2);
1913 0 : }
1914 :
1915 0 : void draw(float sx, float sy) override final
1916 : {
1917 0 : changedraw(Change_Shader | Change_Color);
1918 :
1919 0 : color.init();
1920 0 : gle::begin(GL_LINES);
1921 0 : gle::attribf(sx, sy);
1922 0 : gle::attribf(sx+w, sy+h);
1923 0 : gle::end();
1924 :
1925 0 : Object::draw(sx, sy);
1926 0 : }
1927 :
1928 : private:
1929 : Color color;
1930 : };
1931 :
1932 : class Outline final : public Filler
1933 : {
1934 : public:
1935 :
1936 0 : void setup(const Color &color_, float minw_ = 0, float minh_ = 0)
1937 : {
1938 0 : Filler::setup(minw_, minh_);
1939 0 : color = color_;
1940 0 : }
1941 :
1942 0 : const char *gettype() const override final
1943 : {
1944 0 : return typestr();
1945 : }
1946 0 : static const char *typestr() { return "#Outline"; }
1947 :
1948 0 : void draw(float sx, float sy) override final
1949 : {
1950 0 : changedraw(Change_Shader | Change_Color);
1951 :
1952 0 : color.init();
1953 0 : gle::begin(GL_LINE_LOOP);
1954 0 : gle::attribf(sx, sy);
1955 0 : gle::attribf(sx+w, sy);
1956 0 : gle::attribf(sx+w, sy+h);
1957 0 : gle::attribf(sx, sy+h);
1958 0 : gle::end();
1959 :
1960 0 : Object::draw(sx, sy);
1961 0 : }
1962 : protected:
1963 0 : void startdraw() const override final
1964 : {
1965 0 : hudnotextureshader->set();
1966 0 : gle::defvertex(2);
1967 0 : }
1968 : private:
1969 : Color color;
1970 : };
1971 :
1972 0 : static bool checkalphamask(Texture *tex, float x, float y)
1973 : {
1974 0 : if(!tex->alphamask)
1975 : {
1976 0 : tex->loadalphamask();
1977 0 : if(!tex->alphamask)
1978 : {
1979 0 : return true;
1980 : }
1981 : }
1982 0 : int tx = std::clamp(static_cast<int>(x*tex->xs), 0, tex->xs-1),
1983 0 : ty = std::clamp(static_cast<int>(y*tex->ys), 0, tex->ys-1);
1984 0 : if(tex->alphamask[ty*((tex->xs+7)/8) + tx/8] & (1<<(tx%8)))
1985 : {
1986 0 : return true;
1987 : }
1988 0 : return false;
1989 : }
1990 :
1991 : class Image : public Filler
1992 : {
1993 : public:
1994 0 : void setup(Texture *tex_, float minw_ = 0, float minh_ = 0)
1995 : {
1996 0 : Filler::setup(minw_, minh_);
1997 0 : tex = tex_;
1998 0 : }
1999 :
2000 0 : const char *gettype() const override
2001 : {
2002 0 : return typestr();
2003 : }
2004 :
2005 0 : void draw(float sx, float sy) override
2006 : {
2007 0 : if(tex != notexture)
2008 : {
2009 0 : bindtex();
2010 0 : quads(sx, sy, w, h);
2011 : }
2012 :
2013 0 : Object::draw(sx, sy);
2014 0 : }
2015 :
2016 : protected:
2017 : Texture *tex;
2018 :
2019 0 : bool target(float cx, float cy) override
2020 : {
2021 0 : return !(tex->type&Texture::ALPHA) || checkalphamask(tex, cx/w, cy/h);
2022 : }
2023 :
2024 0 : void bindtex()
2025 : {
2026 0 : changedraw();
2027 0 : if(lasttex != tex)
2028 : {
2029 0 : if(lasttex)
2030 : {
2031 0 : gle::end();
2032 : }
2033 0 : lasttex = tex;
2034 0 : glBindTexture(GL_TEXTURE_2D, tex->id);
2035 : }
2036 0 : }
2037 :
2038 0 : void startdraw() const override final
2039 : {
2040 0 : lasttex = nullptr;
2041 :
2042 0 : gle::defvertex(2);
2043 0 : gle::deftexcoord0();
2044 0 : gle::begin(GL_TRIANGLE_STRIP);
2045 0 : }
2046 :
2047 0 : void enddraw() const override final
2048 : {
2049 0 : gle::end();
2050 0 : }
2051 :
2052 : private:
2053 : static Texture *lasttex;
2054 : };
2055 :
2056 : Texture *Image::lasttex = nullptr;
2057 :
2058 : class CroppedImage final : public Image
2059 : {
2060 : public:
2061 0 : void setup(Texture *tex_, float minw_ = 0, float minh_ = 0, float cropx_ = 0, float cropy_ = 0, float cropw_ = 1, float croph_ = 1)
2062 : {
2063 0 : Image::setup(tex_, minw_, minh_);
2064 0 : cropx = cropx_;
2065 0 : cropy = cropy_;
2066 0 : cropw = cropw_;
2067 0 : croph = croph_;
2068 0 : }
2069 :
2070 0 : static const char *typestr() { return "#CroppedImage"; }
2071 :
2072 : private:
2073 0 : bool target(float cx, float cy) override final
2074 : {
2075 0 : return !(tex->type&Texture::ALPHA) || checkalphamask(tex, cropx + cx/w*cropw, cropy + cy/h*croph);
2076 : }
2077 :
2078 0 : void draw(float sx, float sy) override final
2079 : {
2080 0 : if(tex == notexture)
2081 : {
2082 0 : Object::draw(sx, sy);
2083 0 : return;
2084 : }
2085 :
2086 0 : bindtex();
2087 0 : quads(sx, sy, w, h, cropx, cropy, cropw, croph);
2088 :
2089 0 : Object::draw(sx, sy);
2090 : }
2091 :
2092 0 : const char *gettype() const override final
2093 : {
2094 0 : return typestr();
2095 : }
2096 :
2097 : float cropx, cropy, cropw, croph;
2098 : };
2099 :
2100 : struct StretchedImage final : Image
2101 : {
2102 0 : static const char *typestr() { return "#StretchedImage"; }
2103 0 : const char *gettype() const override final
2104 : {
2105 0 : return typestr();
2106 : }
2107 :
2108 0 : bool target(float cx, float cy) override final
2109 : {
2110 0 : if(!(tex->type&Texture::ALPHA))
2111 : {
2112 0 : return true;
2113 : }
2114 : float mx, my;
2115 0 : if(w <= minw)
2116 : {
2117 0 : mx = cx/w;
2118 : }
2119 0 : else if(cx < minw/2)
2120 : {
2121 0 : mx = cx/minw;
2122 : }
2123 0 : else if(cx >= w - minw/2)
2124 : {
2125 0 : mx = 1 - (w - cx) / minw;
2126 : }
2127 : else
2128 : {
2129 0 : mx = 0.5f;
2130 : }
2131 0 : if(h <= minh)
2132 : {
2133 0 : my = cy/h;
2134 : }
2135 0 : else if(cy < minh/2)
2136 : {
2137 0 : my = cy/minh;
2138 : }
2139 0 : else if(cy >= h - minh/2)
2140 : {
2141 0 : my = 1 - (h - cy) / minh;
2142 : }
2143 : else
2144 : {
2145 0 : my = 0.5f;
2146 : }
2147 :
2148 0 : return checkalphamask(tex, mx, my);
2149 : }
2150 :
2151 0 : void draw(float sx, float sy) override final
2152 : {
2153 0 : if(tex == notexture)
2154 : {
2155 0 : Object::draw(sx, sy);
2156 0 : return;
2157 : }
2158 :
2159 0 : bindtex();
2160 :
2161 0 : float splitw = (minw ? std::min(minw, w) : w) / 2,
2162 0 : splith = (minh ? std::min(minh, h) : h) / 2,
2163 0 : vy = sy,
2164 0 : ty = 0;
2165 0 : for(int i = 0; i < 3; ++i)
2166 : {
2167 0 : float vh = 0,
2168 0 : th = 0;
2169 0 : switch(i)
2170 : {
2171 0 : case 0:
2172 : {
2173 0 : if(splith < h - splith)
2174 : {
2175 0 : vh = splith;
2176 0 : th = 0.5f;
2177 : }
2178 : else
2179 : {
2180 0 : vh = h;
2181 0 : th = 1;
2182 : }
2183 0 : break;
2184 : }
2185 0 : case 1:
2186 : {
2187 0 : vh = h - 2*splith;
2188 0 : th = 0;
2189 0 : break;
2190 : }
2191 0 : case 2:
2192 : {
2193 0 : vh = splith;
2194 0 : th = 0.5f;
2195 0 : break;
2196 : }
2197 : }
2198 0 : float vx = sx,
2199 0 : tx = 0;
2200 0 : for(int j = 0; j < 3; ++j)
2201 : {
2202 0 : float vw = 0,
2203 0 : tw = 0;
2204 0 : switch(j)
2205 : {
2206 0 : case 0:
2207 : {
2208 0 : if(splitw < w - splitw)
2209 : {
2210 0 : vw = splitw;
2211 0 : tw = 0.5f;
2212 : }
2213 : else
2214 : {
2215 0 : vw = w;
2216 0 : tw = 1;
2217 : }
2218 0 : break;
2219 : }
2220 0 : case 1:
2221 : {
2222 0 : vw = w - 2*splitw;
2223 0 : tw = 0;
2224 0 : break;
2225 : }
2226 0 : case 2:
2227 : {
2228 0 : vw = splitw;
2229 0 : tw = 0.5f;
2230 0 : break;
2231 : }
2232 : }
2233 0 : quads(vx, vy, vw, vh, tx, ty, tw, th);
2234 0 : vx += vw;
2235 0 : tx += tw;
2236 0 : if(tx >= 1)
2237 : {
2238 0 : break;
2239 : }
2240 : }
2241 0 : vy += vh;
2242 0 : ty += th;
2243 0 : if(ty >= 1)
2244 : {
2245 0 : break;
2246 : }
2247 : }
2248 :
2249 0 : Object::draw(sx, sy);
2250 : }
2251 : };
2252 :
2253 : struct BorderedImage final : Image
2254 : {
2255 : float texborder, screenborder;
2256 :
2257 0 : void setup(Texture *tex_, float texborder_, float screenborder_)
2258 : {
2259 0 : Image::setup(tex_);
2260 0 : texborder = texborder_;
2261 0 : screenborder = screenborder_;
2262 0 : }
2263 :
2264 0 : static const char *typestr() { return "#BorderedImage"; }
2265 0 : const char *gettype() const override final
2266 : {
2267 0 : return typestr();
2268 : }
2269 :
2270 0 : bool target(float cx, float cy) override final
2271 : {
2272 0 : if(!(tex->type&Texture::ALPHA))
2273 : {
2274 0 : return true;
2275 : }
2276 : float mx, my;
2277 0 : if(cx < screenborder)
2278 : {
2279 0 : mx = cx/screenborder*texborder;
2280 : }
2281 0 : else if(cx >= w - screenborder)
2282 : {
2283 0 : mx = 1-texborder + (cx - (w - screenborder))/screenborder*texborder;
2284 : }
2285 : else
2286 : {
2287 0 : mx = texborder + (cx - screenborder)/(w - 2*screenborder)*(1 - 2*texborder);
2288 : }
2289 0 : if(cy < screenborder)
2290 : {
2291 0 : my = cy/screenborder*texborder;
2292 : }
2293 0 : else if(cy >= h - screenborder)
2294 : {
2295 0 : my = 1-texborder + (cy - (h - screenborder))/screenborder*texborder;
2296 : }
2297 : else
2298 : {
2299 0 : my = texborder + (cy - screenborder)/(h - 2*screenborder)*(1 - 2*texborder);
2300 : }
2301 0 : return checkalphamask(tex, mx, my);
2302 : }
2303 :
2304 0 : void draw(float sx, float sy) override final
2305 : {
2306 0 : if(tex == notexture)
2307 : {
2308 0 : Object::draw(sx, sy);
2309 0 : return;
2310 : }
2311 :
2312 0 : bindtex();
2313 :
2314 0 : float vy = sy,
2315 0 : ty = 0;
2316 0 : for(int i = 0; i < 3; ++i)
2317 : {
2318 0 : float vh = 0,
2319 0 : th = 0;
2320 0 : switch(i)
2321 : {
2322 0 : case 0:
2323 : {
2324 0 : vh = screenborder;
2325 0 : th = texborder;
2326 0 : break;
2327 : }
2328 0 : case 1:
2329 : {
2330 0 : vh = h - 2*screenborder;
2331 0 : th = 1 - 2*texborder;
2332 0 : break;
2333 : }
2334 0 : case 2:
2335 : {
2336 0 : vh = screenborder;
2337 0 : th = texborder;
2338 0 : break;
2339 : }
2340 : }
2341 0 : float vx = sx,
2342 0 : tx = 0;
2343 0 : for(int j = 0; j < 3; ++j)
2344 : {
2345 0 : float vw = 0,
2346 0 : tw = 0;
2347 0 : switch(j)
2348 : {
2349 0 : case 0:
2350 : {
2351 0 : vw = screenborder;
2352 0 : tw = texborder;
2353 0 : break;
2354 : }
2355 0 : case 1:
2356 : {
2357 0 : vw = w - 2*screenborder;
2358 0 : tw = 1 - 2*texborder;
2359 0 : break;
2360 : }
2361 0 : case 2:
2362 : {
2363 0 : vw = screenborder;
2364 0 : tw = texborder;
2365 0 : break;
2366 : }
2367 : }
2368 0 : quads(vx, vy, vw, vh, tx, ty, tw, th);
2369 0 : vx += vw;
2370 0 : tx += tw;
2371 : }
2372 0 : vy += vh;
2373 0 : ty += th;
2374 : }
2375 :
2376 0 : Object::draw(sx, sy);
2377 : }
2378 : };
2379 :
2380 : struct TiledImage final : Image
2381 : {
2382 : float tilew, tileh;
2383 :
2384 0 : void setup(Texture *tex_, float minw_ = 0, float minh_ = 0, float tilew_ = 0, float tileh_ = 0)
2385 : {
2386 0 : Image::setup(tex_, minw_, minh_);
2387 0 : tilew = tilew_;
2388 0 : tileh = tileh_;
2389 0 : }
2390 :
2391 0 : static const char *typestr()
2392 : {
2393 0 : return "#TiledImage";
2394 : }
2395 :
2396 0 : const char *gettype() const override final
2397 : {
2398 0 : return typestr();
2399 : }
2400 :
2401 0 : bool target(float cx, float cy) override final
2402 : {
2403 0 : if(!(tex->type&Texture::ALPHA))
2404 : {
2405 0 : return true;
2406 : }
2407 :
2408 0 : return checkalphamask(tex, std::fmod(cx/tilew, 1), std::fmod(cy/tileh, 1));
2409 : }
2410 :
2411 0 : void draw(float sx, float sy) override final
2412 : {
2413 0 : if(tex == notexture)
2414 : {
2415 0 : Object::draw(sx, sy);
2416 0 : return;
2417 : }
2418 0 : bindtex();
2419 0 : if(tex->clamp)
2420 : {
2421 0 : for(float dy = 0; dy < h; dy += tileh)
2422 : {
2423 0 : float dh = std::min(tileh, h - dy);
2424 0 : for(float dx = 0; dx < w; dx += tilew)
2425 : {
2426 0 : float dw = std::min(tilew, w - dx);
2427 0 : quads(sx + dx, sy + dy, dw, dh, 0, 0, dw / tilew, dh / tileh);
2428 : }
2429 : }
2430 : }
2431 : else
2432 : {
2433 0 : quads(sx, sy, w, h, 0, 0, w/tilew, h/tileh);
2434 : }
2435 0 : Object::draw(sx, sy);
2436 : }
2437 : };
2438 :
2439 : class Shape : public Filler
2440 : {
2441 : public:
2442 : enum
2443 : {
2444 : SOLID = 0,
2445 : OUTLINE,
2446 : MODULATE
2447 : };
2448 :
2449 0 : void setup(const Color &color_, int type_ = SOLID, float minw_ = 0, float minh_ = 0)
2450 : {
2451 0 : Filler::setup(minw_, minh_);
2452 :
2453 0 : color = color_;
2454 0 : type = type_;
2455 0 : }
2456 :
2457 : protected:
2458 : int type;
2459 : Color color;
2460 :
2461 0 : void startdraw() const override final
2462 : {
2463 0 : hudnotextureshader->set();
2464 0 : gle::defvertex(2);
2465 0 : }
2466 :
2467 : };
2468 :
2469 : struct Triangle final : Shape
2470 : {
2471 : vec2 a, b, c;
2472 :
2473 0 : void setup(const Color &color_, float w = 0, float h = 0, int angle = 0, int type_ = SOLID)
2474 : {
2475 0 : a = vec2(0, -h*2.0f/3);
2476 0 : b = vec2(-w/2, h/3);
2477 0 : c = vec2(w/2, h/3);
2478 0 : if(angle)
2479 : {
2480 0 : vec2 rot = sincosmod360(-angle);
2481 0 : a.rotate_around_z(rot);
2482 0 : b.rotate_around_z(rot);
2483 0 : c.rotate_around_z(rot);
2484 : }
2485 0 : vec2 bbmin = vec2(a).min(b).min(c);
2486 0 : a.sub(bbmin);
2487 0 : b.sub(bbmin);
2488 0 : c.sub(bbmin);
2489 0 : vec2 bbmax = vec2(a).max(b).max(c);
2490 :
2491 0 : Shape::setup(color_, type_, bbmax.x, bbmax.y);
2492 0 : }
2493 :
2494 0 : static const char *typestr()
2495 : {
2496 0 : return "#Triangle";
2497 : }
2498 0 : const char *gettype() const override final
2499 : {
2500 0 : return typestr();
2501 : }
2502 :
2503 0 : bool target(float cx, float cy) override final
2504 : {
2505 0 : if(type == OUTLINE)
2506 : {
2507 0 : return false;
2508 : }
2509 0 : bool side = vec2(cx, cy).sub(b).cross(vec2(a).sub(b)) < 0;
2510 0 : return (vec2(cx, cy).sub(c).cross(vec2(b).sub(c)) < 0) == side &&
2511 0 : (vec2(cx, cy).sub(a).cross(vec2(c).sub(a)) < 0) == side;
2512 : }
2513 :
2514 0 : void draw(float sx, float sy) override final
2515 : {
2516 0 : Object::draw(sx, sy);
2517 :
2518 0 : changedraw(Change_Shader | Change_Color | Change_Blend);
2519 0 : if(type==MODULATE)
2520 : {
2521 0 : modblend();
2522 : }
2523 : else
2524 : {
2525 0 : resetblend();
2526 : }
2527 0 : color.init();
2528 0 : gle::begin(type == OUTLINE ? GL_LINE_LOOP : GL_TRIANGLES);
2529 0 : gle::attrib(vec2(sx, sy).add(a));
2530 0 : gle::attrib(vec2(sx, sy).add(b));
2531 0 : gle::attrib(vec2(sx, sy).add(c));
2532 0 : gle::end();
2533 0 : }
2534 : };
2535 :
2536 : struct Circle final : Shape
2537 : {
2538 : float radius;
2539 :
2540 0 : void setup(const Color &color_, float size, int type_ = SOLID)
2541 : {
2542 0 : Shape::setup(color_, type_, size, size);
2543 :
2544 0 : radius = size/2;
2545 0 : }
2546 :
2547 0 : static const char *typestr() { return "#Circle"; }
2548 0 : const char *gettype() const override final
2549 : {
2550 0 : return typestr();
2551 : }
2552 :
2553 0 : bool target(float cx, float cy) override final
2554 : {
2555 0 : if(type == OUTLINE)
2556 : {
2557 0 : return false;
2558 : }
2559 0 : float r = radius <= 0 ? std::min(w, h)/2 : radius;
2560 0 : return vec2(cx, cy).sub(r).squaredlen() <= r*r;
2561 : }
2562 :
2563 0 : void draw(float sx, float sy) override final
2564 : {
2565 0 : Object::draw(sx, sy);
2566 :
2567 0 : changedraw(Change_Shader | Change_Color | Change_Blend);
2568 0 : if(type==MODULATE)
2569 : {
2570 0 : modblend();
2571 : }
2572 : else
2573 : {
2574 0 : resetblend();
2575 : }
2576 :
2577 0 : float r = radius <= 0 ? std::min(w, h)/2 : radius;
2578 0 : color.init();
2579 0 : vec2 center(sx + r, sy + r);
2580 0 : if(type == OUTLINE)
2581 : {
2582 0 : gle::begin(GL_LINE_LOOP);
2583 0 : for(int angle = 0; angle < 360; angle += 360/15)
2584 0 : gle::attrib(vec2(sincos360[angle]).mul(r).add(center));
2585 0 : gle::end();
2586 : }
2587 : else
2588 : {
2589 0 : gle::begin(GL_TRIANGLE_FAN);
2590 0 : gle::attrib(center);
2591 0 : gle::attribf(center.x + r, center.y);
2592 0 : for(int angle = 360/15; angle < 360; angle += 360/15)
2593 : {
2594 0 : vec2 p = vec2(sincos360[angle]).mul(r).add(center);
2595 0 : gle::attrib(p);
2596 0 : gle::attrib(p);
2597 : }
2598 0 : gle::attribf(center.x + r, center.y);
2599 0 : gle::end();
2600 : }
2601 0 : }
2602 : };
2603 :
2604 : // default size of text in terms of rows per screenful
2605 : static VARP(uitextrows, 1, 24, 200);
2606 : static FVAR(uitextscale, 1, 0, 0);
2607 :
2608 0 : static void setstring(char*& dst, const char* src)
2609 : {
2610 0 : if(dst)
2611 : {
2612 0 : if(dst != src && std::strcmp(dst, src))
2613 : {
2614 0 : delete[] dst;
2615 0 : dst = newstring(src);
2616 : }
2617 : }
2618 : else
2619 : {
2620 0 : dst = newstring(src);
2621 : }
2622 0 : }
2623 :
2624 : struct Text : Object
2625 : {
2626 : public:
2627 :
2628 0 : void setup(float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1)
2629 : {
2630 0 : Object::setup();
2631 :
2632 0 : scale = scale_;
2633 0 : color = color_;
2634 0 : wrap = wrap_;
2635 0 : }
2636 :
2637 0 : virtual const char *getstr() const
2638 : {
2639 0 : return "";
2640 : }
2641 :
2642 : protected:
2643 0 : const char *gettype() const override
2644 : {
2645 0 : return typestr();
2646 : }
2647 :
2648 0 : void draw(float sx, float sy) override final
2649 : {
2650 0 : Object::draw(sx, sy);
2651 :
2652 0 : changedraw(Change_Shader | Change_Color);
2653 :
2654 0 : float oldscale = textscale;
2655 0 : textscale = drawscale();
2656 0 : ttr.fontsize(36);
2657 0 : const float conscalefactor = 0.000666;
2658 0 : pushhudscale(conscalefactor);
2659 0 : ttr.renderttf(getstr(), {color.r, color.g, color.b, color.a}, sx*1500, sy*1500, scale*33);
2660 0 : pophudmatrix();
2661 : //draw_text(getstr(), sx/textscale, sy/textscale, 0, color.g, color.b, color.a, -1, wrap >= 0 ? static_cast<int>(wrap/textscale) : -1);
2662 :
2663 0 : textscale = oldscale;
2664 0 : }
2665 :
2666 0 : void layout() override final
2667 : {
2668 0 : Object::layout();
2669 :
2670 0 : float k = drawscale(), tw, th;
2671 0 : ttr.ttfbounds(getstr(), tw, th, 42);
2672 0 : w = std::max(w, tw*k);
2673 0 : h = std::max(h, th*k);
2674 0 : }
2675 :
2676 : private:
2677 : float scale, wrap;
2678 : Color color;
2679 :
2680 0 : static const char *typestr()
2681 : {
2682 0 : return "#Text";
2683 : }
2684 :
2685 0 : float drawscale() const
2686 : {
2687 0 : return scale / FONTH;
2688 : }
2689 :
2690 : };
2691 :
2692 : struct TextString final : Text
2693 : {
2694 : std::string str;
2695 :
2696 0 : TextString() : str("")
2697 : {
2698 0 : }
2699 :
2700 0 : ~TextString()
2701 0 : {
2702 0 : }
2703 :
2704 0 : void setup(const char *str_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1)
2705 : {
2706 0 : Text::setup(scale_, color_, wrap_);
2707 :
2708 0 : str = std::string(str_);
2709 0 : }
2710 :
2711 0 : static const char *typestr()
2712 : {
2713 0 : return "#TextString";
2714 : }
2715 :
2716 0 : const char *gettype() const override final
2717 : {
2718 0 : return typestr();
2719 : }
2720 :
2721 0 : const char *getstr() const override final
2722 : {
2723 0 : return str.c_str();
2724 : }
2725 : };
2726 :
2727 : struct TextInt final : Text
2728 : {
2729 : int val;
2730 : char str[20];
2731 :
2732 0 : TextInt() : val(0) { str[0] = '0'; str[1] = '\0'; }
2733 :
2734 0 : void setup(int val_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1)
2735 : {
2736 0 : Text::setup(scale_, color_, wrap_);
2737 :
2738 0 : if(val != val_)
2739 : {
2740 0 : val = val_;
2741 0 : intformat(str, val, sizeof(str));
2742 : }
2743 0 : }
2744 :
2745 0 : static const char *typestr()
2746 : {
2747 0 : return "#TextInt";
2748 : }
2749 :
2750 0 : const char *gettype() const override final
2751 : {
2752 0 : return typestr();
2753 : }
2754 :
2755 0 : const char *getstr() const override final
2756 : {
2757 0 : return str;
2758 : }
2759 : };
2760 :
2761 : struct TextFloat final : Text
2762 : {
2763 : float val;
2764 : char str[20];
2765 :
2766 0 : TextFloat() : val(0) { std::memcpy(str, "0.0", 4); }
2767 :
2768 0 : void setup(float val_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1)
2769 : {
2770 0 : Text::setup(scale_, color_, wrap_);
2771 :
2772 0 : if(val != val_)
2773 : {
2774 0 : val = val_;
2775 0 : floatformat(str, val, sizeof(str));
2776 : }
2777 0 : }
2778 :
2779 0 : static const char *typestr()
2780 : {
2781 0 : return "#TextFloat";
2782 : }
2783 :
2784 0 : const char *gettype() const override final
2785 : {
2786 0 : return typestr();
2787 : }
2788 :
2789 0 : const char *getstr() const override final
2790 : {
2791 0 : return str;
2792 : }
2793 : };
2794 :
2795 : struct Font final : Object
2796 : {
2797 : ::font *font;
2798 :
2799 0 : Font() : font(nullptr) {}
2800 :
2801 0 : void setup()
2802 : {
2803 0 : Object::setup();
2804 0 : }
2805 :
2806 0 : void layout() override final
2807 : {
2808 0 : pushfont();
2809 0 : setfont(font);
2810 0 : Object::layout();
2811 0 : popfont();
2812 0 : }
2813 :
2814 0 : void draw(float sx, float sy) override final
2815 : {
2816 0 : pushfont();
2817 0 : setfont(font);
2818 0 : Object::draw(sx, sy);
2819 0 : popfont();
2820 0 : }
2821 :
2822 0 : void buildchildren(uint *contents)
2823 : {
2824 0 : pushfont();
2825 0 : setfont(font);
2826 0 : Object::buildchildren(contents);
2827 0 : popfont();
2828 0 : }
2829 :
2830 : #define DOSTATE(flags, func) \
2831 : void func##children(float cx, float cy, int mask, bool inside, int setflags) override final \
2832 : { \
2833 : pushfont(); \
2834 : setfont(font); \
2835 : Object::func##children(cx, cy, mask, inside, setflags); \
2836 : popfont(); \
2837 : }
2838 0 : DOSTATES
2839 : #undef DOSTATE
2840 :
2841 0 : bool rawkey(int code, bool isdown) override final
2842 : {
2843 0 : pushfont();
2844 0 : setfont(font);
2845 0 : bool result = Object::rawkey(code, isdown);
2846 0 : popfont();
2847 0 : return result;
2848 : }
2849 :
2850 0 : bool key(int code, bool isdown) override final
2851 : {
2852 0 : pushfont();
2853 0 : setfont(font);
2854 0 : bool result = Object::key(code, isdown);
2855 0 : popfont();
2856 0 : return result;
2857 : }
2858 :
2859 0 : bool textinput(const char *str, int len) override final
2860 : {
2861 0 : pushfont();
2862 0 : setfont(font);
2863 0 : bool result = Object::textinput(str, len);
2864 0 : popfont();
2865 0 : return result;
2866 : }
2867 : };
2868 :
2869 : static float uicontextscale = 0;
2870 :
2871 1 : void uicontextscalecmd()
2872 : {
2873 1 : floatret(FONTH*uicontextscale);
2874 1 : }
2875 :
2876 : struct Console final : Filler
2877 : {
2878 0 : void setup(float minw_ = 0, float minh_ = 0)
2879 : {
2880 0 : Filler::setup(minw_, minh_);
2881 0 : }
2882 :
2883 0 : static const char *typestr()
2884 : {
2885 0 : return "#Console";
2886 : }
2887 :
2888 0 : const char *gettype() const override final
2889 : {
2890 0 : return typestr();
2891 : }
2892 :
2893 0 : float drawscale() const
2894 : {
2895 0 : return uicontextscale;
2896 : }
2897 :
2898 0 : void draw(float sx, float sy) override final
2899 : {
2900 0 : Object::draw(sx, sy);
2901 :
2902 0 : changedraw(Change_Shader | Change_Color);
2903 :
2904 0 : float k = drawscale();
2905 0 : pushhudtranslate(sx, sy, k);
2906 0 : renderfullconsole(w/k, h/k);
2907 0 : pophudmatrix();
2908 0 : }
2909 : };
2910 :
2911 : class Clipper : public Object
2912 : {
2913 : public:
2914 : float virtw, virth;
2915 :
2916 0 : void setup(float clipw_ = 0, float cliph_ = 0)
2917 : {
2918 0 : Object::setup();
2919 0 : clipw = clipw_;
2920 0 : cliph = cliph_;
2921 0 : virtw = virth = 0;
2922 0 : }
2923 :
2924 0 : static const char *typestr()
2925 : {
2926 0 : return "#Clipper";
2927 : }
2928 :
2929 0 : void layout() override
2930 : {
2931 0 : Object::layout();
2932 :
2933 0 : virtw = w;
2934 0 : virth = h;
2935 0 : if(clipw)
2936 : {
2937 0 : w = std::min(w, clipw);
2938 : }
2939 0 : if(cliph)
2940 : {
2941 0 : h = std::min(h, cliph);
2942 : }
2943 0 : }
2944 :
2945 0 : void draw(float sx, float sy) override
2946 : {
2947 0 : if((clipw && virtw > clipw) || (cliph && virth > cliph))
2948 : {
2949 0 : stopdrawing();
2950 0 : pushclip(sx, sy, w, h);
2951 0 : Object::draw(sx, sy);
2952 0 : stopdrawing();
2953 0 : popclip();
2954 : }
2955 : else
2956 : {
2957 0 : Object::draw(sx, sy);
2958 : }
2959 0 : }
2960 :
2961 : protected:
2962 : float clipw, cliph;
2963 :
2964 0 : const char *gettype() const override
2965 : {
2966 0 : return typestr();
2967 : }
2968 :
2969 0 : void adjustchildren() override final
2970 : {
2971 0 : adjustchildrento(0, 0, virtw, virth);
2972 0 : }
2973 :
2974 : };
2975 :
2976 : struct Scroller final : Clipper
2977 : {
2978 : float offsetx, offsety;
2979 :
2980 0 : Scroller() : offsetx(0), offsety(0) {}
2981 :
2982 0 : void setup(float clipw_ = 0, float cliph_ = 0)
2983 : {
2984 0 : Clipper::setup(clipw_, cliph_);
2985 0 : }
2986 :
2987 0 : static const char *typestr()
2988 : {
2989 0 : return "#Scroller";
2990 : }
2991 :
2992 0 : const char *gettype() const override final
2993 : {
2994 0 : return typestr();
2995 : }
2996 :
2997 0 : void layout() override final
2998 : {
2999 0 : Clipper::layout();
3000 0 : offsetx = std::min(offsetx, hlimit());
3001 0 : offsety = std::min(offsety, vlimit());
3002 0 : }
3003 :
3004 : #define DOSTATE(flags, func) \
3005 : void func##children(float cx, float cy, int mask, bool inside, int setflags) override final \
3006 : { \
3007 : cx += offsetx; \
3008 : cy += offsety; \
3009 : if(cx < virtw && cy < virth) \
3010 : { \
3011 : Clipper::func##children(cx, cy, mask, inside, setflags); \
3012 : } \
3013 : }
3014 0 : DOSTATES
3015 : #undef DOSTATE
3016 :
3017 0 : void draw(float sx, float sy) override final
3018 : {
3019 0 : if((clipw && virtw > clipw) || (cliph && virth > cliph))
3020 : {
3021 0 : stopdrawing();
3022 0 : pushclip(sx, sy, w, h);
3023 0 : Object::draw(sx - offsetx, sy - offsety);
3024 0 : stopdrawing();
3025 0 : popclip();
3026 : }
3027 : else
3028 : {
3029 0 : Object::draw(sx, sy);
3030 : }
3031 0 : }
3032 :
3033 0 : float hlimit() const
3034 : {
3035 0 : return std::max(virtw - w, 0.0f);
3036 : }
3037 :
3038 0 : float vlimit() const
3039 : {
3040 0 : return std::max(virth - h, 0.0f);
3041 : }
3042 :
3043 0 : float hoffset() const
3044 : {
3045 0 : return offsetx / std::max(virtw, w);
3046 : }
3047 :
3048 0 : float voffset() const
3049 : {
3050 0 : return offsety / std::max(virth, h);
3051 : }
3052 :
3053 0 : float hscale() const
3054 : {
3055 0 : return w / std::max(virtw, w);
3056 : }
3057 :
3058 0 : float vscale() const
3059 : {
3060 0 : return h / std::max(virth, h);
3061 : }
3062 :
3063 0 : void addhscroll(float hscroll)
3064 : {
3065 0 : sethscroll(offsetx + hscroll);
3066 0 : }
3067 :
3068 0 : void addvscroll(float vscroll)
3069 : {
3070 0 : setvscroll(offsety + vscroll);
3071 0 : }
3072 :
3073 0 : void sethscroll(float hscroll)
3074 : {
3075 0 : offsetx = std::clamp(hscroll, 0.0f, hlimit());
3076 0 : }
3077 :
3078 0 : void setvscroll(float vscroll)
3079 : {
3080 0 : offsety = std::clamp(vscroll, 0.0f, vlimit());
3081 0 : }
3082 :
3083 : void scrollup(float cx, float cy) override final;
3084 :
3085 : void scrolldown(float cx, float cy) override final;
3086 : };
3087 :
3088 : class ScrollButton final : public Object
3089 : {
3090 : public:
3091 0 : static const char *typestr()
3092 : {
3093 0 : return "#ScrollButton";
3094 : }
3095 :
3096 : protected:
3097 0 : const char *gettype() const override final
3098 : {
3099 0 : return typestr();
3100 : }
3101 : };
3102 :
3103 : class ScrollBar : public Object
3104 : {
3105 :
3106 : public:
3107 0 : ScrollBar() : offsetx(0), offsety(0) {}
3108 :
3109 0 : static const char *typestr()
3110 : {
3111 0 : return "#ScrollBar";
3112 : }
3113 :
3114 0 : void hold(float cx, float cy) override final
3115 : {
3116 0 : ScrollButton *button = static_cast<ScrollButton *>(find(ScrollButton::typestr(), false));
3117 0 : if(button && button->haschildstate(State_Hold))
3118 : {
3119 0 : movebutton(button, offsetx, offsety, cx - button->x, cy - button->y);
3120 : }
3121 0 : }
3122 :
3123 0 : void press(float cx, float cy) override final
3124 : {
3125 0 : ScrollButton *button = static_cast<ScrollButton *>(find(ScrollButton::typestr(), false));
3126 0 : if(button && button->haschildstate(State_Press))
3127 : {
3128 0 : offsetx = cx - button->x;
3129 0 : offsety = cy - button->y;
3130 : }
3131 : else
3132 : {
3133 0 : scrollto(cx, cy, true);
3134 : }
3135 0 : }
3136 :
3137 0 : void arrowscroll(float dir)
3138 : {
3139 0 : addscroll(dir*curtime/1000.0f);
3140 0 : }
3141 : void wheelscroll(float step);
3142 0 : virtual int wheelscrolldirection() const
3143 : {
3144 0 : return 1;
3145 : }
3146 :
3147 : protected:
3148 0 : const char *gettype() const override
3149 : {
3150 0 : return typestr();
3151 : }
3152 :
3153 0 : const char *gettypename() const override final
3154 : {
3155 0 : return typestr();
3156 : }
3157 :
3158 0 : bool target(float, float) override final //note unnamed function parameters
3159 : {
3160 0 : return true;
3161 : }
3162 :
3163 0 : virtual void scrollto(float, float, bool) {} //note unnamed function parameters
3164 : virtual void movebutton(Object *o, float fromx, float fromy, float tox, float toy) = 0;
3165 : virtual void addscroll(Scroller *scroller, float dir) = 0;
3166 :
3167 : private:
3168 : float offsetx, offsety;
3169 :
3170 0 : void addscroll(float dir)
3171 : {
3172 0 : Scroller *scroller = static_cast<Scroller *>(findsibling(Scroller::typestr()));
3173 0 : if(scroller)
3174 : {
3175 0 : addscroll(scroller, dir);
3176 : }
3177 0 : }
3178 : };
3179 :
3180 0 : void Scroller::scrollup(float, float) //note unnamed function parameters
3181 : {
3182 0 : ScrollBar *scrollbar = static_cast<ScrollBar *>(findsibling(ScrollBar::typestr()));
3183 0 : if(scrollbar)
3184 : {
3185 0 : scrollbar->wheelscroll(-scrollbar->wheelscrolldirection());
3186 : }
3187 0 : }
3188 :
3189 0 : void Scroller::scrolldown(float, float) //note unnamed function parameters
3190 : {
3191 0 : ScrollBar *scrollbar = static_cast<ScrollBar *>(findsibling(ScrollBar::typestr()));
3192 0 : if(scrollbar)
3193 : {
3194 0 : scrollbar->wheelscroll(scrollbar->wheelscrolldirection());
3195 : }
3196 0 : }
3197 :
3198 : struct ScrollArrow : Object
3199 : {
3200 : float arrowspeed;
3201 :
3202 0 : void setup(float arrowspeed_ = 0)
3203 : {
3204 0 : Object::setup();
3205 0 : arrowspeed = arrowspeed_;
3206 0 : }
3207 :
3208 0 : static const char *typestr()
3209 : {
3210 0 : return "#ScrollArrow";
3211 : }
3212 :
3213 0 : const char *gettype() const override final
3214 : {
3215 0 : return typestr();
3216 : }
3217 :
3218 0 : void hold(float, float) override final //note unnamed function parameters
3219 : {
3220 0 : ScrollBar *scrollbar = static_cast<ScrollBar *>(findsibling(ScrollBar::typestr()));
3221 0 : if(scrollbar)
3222 : {
3223 0 : scrollbar->arrowscroll(arrowspeed);
3224 : }
3225 0 : }
3226 : };
3227 :
3228 : static VARP(uiscrollsteptime, 0, 50, 1000);
3229 :
3230 0 : void ScrollBar::wheelscroll(float step)
3231 : {
3232 0 : ScrollArrow *arrow = static_cast<ScrollArrow *>(findsibling(ScrollArrow::typestr()));
3233 0 : if(arrow)
3234 : {
3235 0 : addscroll(arrow->arrowspeed*step*uiscrollsteptime/1000.0f);
3236 : }
3237 0 : }
3238 :
3239 : struct HorizontalScrollBar final : ScrollBar
3240 : {
3241 0 : static const char *typestr()
3242 : {
3243 0 : return "#HorizontalScrollBar";
3244 : }
3245 :
3246 0 : const char *gettype() const override final
3247 : {
3248 0 : return typestr();
3249 : }
3250 :
3251 0 : void addscroll(Scroller *scroller, float dir) override final
3252 : {
3253 0 : scroller->addhscroll(dir);
3254 0 : }
3255 :
3256 0 : void scrollto(float cx, float, bool closest = false) override final //note unnamed function parameter
3257 : {
3258 0 : Scroller *scroller = static_cast<Scroller *>(findsibling(Scroller::typestr()));
3259 0 : if(!scroller)
3260 : {
3261 0 : return;
3262 : }
3263 0 : ScrollButton *button = static_cast<ScrollButton *>(find(ScrollButton::typestr(), false));
3264 0 : if(!button)
3265 : {
3266 0 : return;
3267 : }
3268 0 : float bscale = (w - button->w) / (1 - scroller->hscale()),
3269 0 : offset = bscale > 1e-3f ? (closest && cx >= button->x + button->w ? cx - button->w : cx)/bscale : 0;
3270 0 : scroller->sethscroll(offset*scroller->virtw);
3271 : }
3272 :
3273 0 : void adjustchildren() override final
3274 : {
3275 0 : Scroller *scroller = static_cast<Scroller *>(findsibling(Scroller::typestr()));
3276 0 : if(!scroller)
3277 : {
3278 0 : return;
3279 : }
3280 0 : ScrollButton *button = static_cast<ScrollButton *>(find(ScrollButton::typestr(), false));
3281 0 : if(!button)
3282 : {
3283 0 : return;
3284 : }
3285 0 : float bw = w*scroller->hscale();
3286 0 : button->w = std::max(button->w, bw);
3287 0 : float bscale = scroller->hscale() < 1 ? (w - button->w) / (1 - scroller->hscale()) : 1;
3288 0 : button->x = scroller->hoffset()*bscale;
3289 0 : button->adjust &= ~Align_HMask;
3290 :
3291 0 : ScrollBar::adjustchildren();
3292 : }
3293 :
3294 0 : void movebutton(Object *o, float fromx, float, float tox, float toy) override final //note unnamed function parameter
3295 : {
3296 0 : scrollto(o->x + tox - fromx, o->y + toy);
3297 0 : }
3298 : };
3299 :
3300 : struct VerticalScrollBar final : ScrollBar
3301 : {
3302 0 : static const char *typestr()
3303 : {
3304 0 : return "#VerticalScrollBar";
3305 : }
3306 :
3307 0 : const char *gettype() const override final
3308 : {
3309 0 : return typestr();
3310 : }
3311 :
3312 0 : void addscroll(Scroller *scroller, float dir) override final
3313 : {
3314 0 : scroller->addvscroll(dir);
3315 0 : }
3316 :
3317 0 : void scrollto(float, float cy, bool closest = false) override final //note unnamed function parameter
3318 : {
3319 0 : Scroller *scroller = static_cast<Scroller *>(findsibling(Scroller::typestr()));
3320 0 : if(!scroller)
3321 : {
3322 0 : return;
3323 : }
3324 0 : ScrollButton *button = static_cast<ScrollButton *>(find(ScrollButton::typestr(), false));
3325 0 : if(!button)
3326 : {
3327 0 : return;
3328 : }
3329 0 : float bscale = (h - button->h) / (1 - scroller->vscale()),
3330 0 : offset = bscale > 1e-3f ? (closest && cy >= button->y + button->h ? cy - button->h : cy)/bscale : 0;
3331 0 : scroller->setvscroll(offset*scroller->virth);
3332 : }
3333 :
3334 0 : void adjustchildren() override final
3335 : {
3336 0 : Scroller *scroller = static_cast<Scroller *>(findsibling(Scroller::typestr()));
3337 0 : if(!scroller)
3338 : {
3339 0 : return;
3340 : }
3341 0 : ScrollButton *button = static_cast<ScrollButton *>(find(ScrollButton::typestr(), false));
3342 0 : if(!button)
3343 : {
3344 0 : return;
3345 : }
3346 0 : float bh = h*scroller->vscale();
3347 0 : button->h = std::max(button->h, bh);
3348 0 : float bscale = scroller->vscale() < 1 ? (h - button->h) / (1 - scroller->vscale()) : 1;
3349 0 : button->y = scroller->voffset()*bscale;
3350 0 : button->adjust &= ~Align_VMask;
3351 :
3352 0 : ScrollBar::adjustchildren();
3353 : }
3354 :
3355 0 : void movebutton(Object *o, float, float fromy, float tox, float toy) override final //note unnamed function parameter
3356 : {
3357 0 : scrollto(o->x + tox, o->y + toy - fromy);
3358 0 : }
3359 :
3360 0 : int wheelscrolldirection() const override final
3361 : {
3362 0 : return -1;
3363 : }
3364 : };
3365 :
3366 : struct SliderButton final : Object
3367 : {
3368 0 : static const char *typestr()
3369 : {
3370 0 : return "#SliderButton";
3371 : }
3372 0 : const char *gettype() const override final
3373 : {
3374 0 : return typestr();
3375 : }
3376 : };
3377 :
3378 0 : static double getfval(ident *id, double val = 0)
3379 : {
3380 0 : switch(id->type)
3381 : {
3382 0 : case Id_Var:
3383 : {
3384 0 : val = *id->val.storage.i;
3385 0 : break;
3386 : }
3387 0 : case Id_FloatVar:
3388 : {
3389 0 : val = *id->val.storage.f;
3390 0 : break;
3391 : }
3392 0 : case Id_StringVar:
3393 : {
3394 0 : val = parsenumber(*id->val.storage.s);
3395 0 : break;
3396 : }
3397 0 : case Id_Alias:
3398 : {
3399 0 : val = id->getnumber();
3400 0 : break;
3401 : }
3402 0 : case Id_Command:
3403 : {
3404 : tagval t;
3405 0 : executeret(id, nullptr, 0, true, t);
3406 0 : val = t.getnumber();
3407 0 : t.cleanup();
3408 0 : break;
3409 : }
3410 : }
3411 0 : return val;
3412 : }
3413 :
3414 0 : static void setfval(ident *id, double val, uint *onchange = nullptr)
3415 : {
3416 0 : switch(id->type)
3417 : {
3418 0 : case Id_Var:
3419 : {
3420 0 : setvarchecked(id, static_cast<int>(std::clamp(val, double(INT_MIN), double(INT_MAX))));
3421 0 : break;
3422 : }
3423 0 : case Id_FloatVar:
3424 : {
3425 0 : setfvarchecked(id, val);
3426 0 : break;
3427 : }
3428 0 : case Id_StringVar:
3429 : {
3430 0 : setsvarchecked(id, numberstr(val));
3431 0 : break;
3432 : }
3433 0 : case Id_Alias:
3434 : {
3435 0 : alias(id->name, numberstr(val));
3436 0 : break;
3437 : }
3438 0 : case Id_Command:
3439 : {
3440 : tagval t;
3441 0 : t.setnumber(val);
3442 0 : execute(id, &t, 1);
3443 0 : break;
3444 : }
3445 : }
3446 0 : if(onchange && (*onchange&Code_OpMask) != Code_Exit)
3447 : {
3448 0 : execute(onchange);
3449 : }
3450 0 : }
3451 :
3452 : struct Slider : Object
3453 : {
3454 0 : Slider() : val(0), vmin(0), vmax(0), vstep(0), id(nullptr), changed(false) {}
3455 :
3456 0 : void setup(ident *id_, double vmin_ = 0, double vmax_ = 0, double vstep_ = 1, uint *onchange = nullptr)
3457 : {
3458 0 : Object::setup();
3459 0 : if(!vmin_ && !vmax_)
3460 : {
3461 0 : switch(id_->type)
3462 : {
3463 0 : case Id_Var:
3464 : {
3465 0 : vmin_ = id_->val.i.min;
3466 0 : vmax_ = id_->val.i.max;
3467 0 : break;
3468 : }
3469 0 : case Id_FloatVar:
3470 : {
3471 0 : vmin_ = id_->val.f.min;
3472 0 : vmax_ = id_->val.f.max;
3473 0 : break;
3474 : }
3475 : }
3476 : }
3477 0 : if(id != id_)
3478 : {
3479 0 : changed = false;
3480 : }
3481 0 : id = id_;
3482 0 : vmin = vmin_;
3483 0 : vmax = vmax_;
3484 0 : vstep = vstep_ > 0 ? vstep_ : 1;
3485 0 : if(changed)
3486 : {
3487 0 : setfval(id, val, onchange);
3488 0 : changed = false;
3489 : }
3490 : else
3491 : {
3492 0 : val = getfval(id, vmin);
3493 : }
3494 0 : }
3495 :
3496 0 : static const char *typestr()
3497 : {
3498 0 : return "#Slider";
3499 : }
3500 :
3501 0 : const char *gettype() const override
3502 : {
3503 0 : return typestr();
3504 : }
3505 :
3506 0 : const char *gettypename() const override final
3507 : {
3508 0 : return typestr();
3509 : }
3510 :
3511 0 : bool target(float, float) override final //note unnamed function parameters
3512 : {
3513 0 : return true;
3514 : }
3515 :
3516 0 : void arrowscroll(double dir)
3517 : {
3518 0 : double newval = val + dir*vstep;
3519 0 : newval += vstep * (newval < 0 ? -0.5 : 0.5);
3520 0 : newval -= std::fmod(newval, vstep);
3521 0 : newval = std::clamp(newval, std::min(vmin, vmax), std::max(vmin, vmax));
3522 0 : if(val != newval)
3523 : {
3524 0 : changeval(newval);
3525 : }
3526 0 : }
3527 :
3528 : void wheelscroll(float step);
3529 0 : virtual int wheelscrolldirection() const
3530 : {
3531 0 : return 1;
3532 : }
3533 :
3534 0 : void scrollup(float, float) override final //note unnamed function parameters
3535 : {
3536 0 : wheelscroll(-wheelscrolldirection());
3537 0 : }
3538 :
3539 0 : void scrolldown(float, float) override final //note unnamed function parameters
3540 : {
3541 0 : wheelscroll(wheelscrolldirection());
3542 0 : }
3543 :
3544 0 : virtual void scrollto(float, float) {} //note unnamed function parameters
3545 :
3546 0 : void hold(float cx, float cy) override final
3547 : {
3548 0 : scrollto(cx, cy);
3549 0 : }
3550 :
3551 0 : void changeval(double newval)
3552 : {
3553 0 : val = newval;
3554 0 : changed = true;
3555 0 : }
3556 :
3557 : protected:
3558 : double val, vmin, vmax, vstep;
3559 :
3560 : private:
3561 : ident *id;
3562 : bool changed;
3563 : };
3564 :
3565 : static VARP(uislidersteptime, 0, 50, 1000);
3566 :
3567 : struct SliderArrow final : Object
3568 : {
3569 : double stepdir;
3570 : int laststep;
3571 :
3572 0 : SliderArrow() : laststep(0) {}
3573 :
3574 0 : void setup(double dir_ = 0)
3575 : {
3576 0 : Object::setup();
3577 0 : stepdir = dir_;
3578 0 : }
3579 :
3580 0 : static const char *typestr()
3581 : {
3582 0 : return "#SliderArrow";
3583 : }
3584 :
3585 0 : const char *gettype() const override final
3586 : {
3587 0 : return typestr();
3588 : }
3589 :
3590 0 : void press(float, float) override final //note unnamed function parameters
3591 : {
3592 0 : laststep = totalmillis + 2*uislidersteptime;
3593 :
3594 0 : Slider *slider = static_cast<Slider *>(findsibling(Slider::typestr()));
3595 0 : if(slider)
3596 : {
3597 0 : slider->arrowscroll(stepdir);
3598 : }
3599 0 : }
3600 :
3601 0 : void hold(float, float) override final //note unnamed function parameters
3602 : {
3603 0 : if(totalmillis < laststep + uislidersteptime)
3604 : {
3605 0 : return;
3606 : }
3607 0 : laststep = totalmillis;
3608 :
3609 0 : Slider *slider = static_cast<Slider *>(findsibling(Slider::typestr()));
3610 0 : if(slider)
3611 : {
3612 0 : slider->arrowscroll(stepdir);
3613 : }
3614 : }
3615 : };
3616 :
3617 0 : void Slider::wheelscroll(float step)
3618 : {
3619 0 : SliderArrow *arrow = static_cast<SliderArrow *>(findsibling(SliderArrow::typestr()));
3620 0 : if(arrow)
3621 : {
3622 0 : step *= arrow->stepdir;
3623 : }
3624 0 : arrowscroll(step);
3625 0 : }
3626 :
3627 : struct HorizontalSlider final : Slider
3628 : {
3629 0 : static const char *typestr()
3630 : {
3631 0 : return "#HorizontalSlider";
3632 : }
3633 :
3634 0 : const char *gettype() const override final
3635 : {
3636 0 : return typestr();
3637 : }
3638 :
3639 0 : void scrollto(float cx, float) override final //note unnamed function parameter
3640 : {
3641 0 : SliderButton *button = static_cast<SliderButton *>(find(SliderButton::typestr(), false));
3642 0 : if(!button)
3643 : {
3644 0 : return;
3645 : }
3646 0 : float offset = w > button->w ? std::clamp((cx - button->w/2)/(w - button->w), 0.0f, 1.0f) : 0.0f;
3647 0 : int step = static_cast<int>((val - vmin) / vstep),
3648 0 : bstep = static_cast<int>(offset * (vmax - vmin) / vstep);
3649 0 : if(step != bstep)
3650 : {
3651 0 : changeval(bstep * vstep + vmin);
3652 : }
3653 : }
3654 :
3655 0 : void adjustchildren() override final
3656 : {
3657 0 : SliderButton *button = static_cast<SliderButton *>(find(SliderButton::typestr(), false));
3658 0 : if(!button)
3659 : {
3660 0 : return;
3661 : }
3662 0 : int step = static_cast<int>((val - vmin) / vstep),
3663 0 : bstep = static_cast<int>(button->x / (w - button->w) * (vmax - vmin) / vstep);
3664 0 : if(step != bstep)
3665 : {
3666 0 : button->x = (w - button->w) * step * vstep / (vmax - vmin);
3667 : }
3668 0 : button->adjust &= ~Align_HMask;
3669 :
3670 0 : Slider::adjustchildren();
3671 : }
3672 : };
3673 :
3674 : struct VerticalSlider final : Slider
3675 : {
3676 0 : static const char *typestr()
3677 : {
3678 0 : return "#VerticalSlider";
3679 : }
3680 :
3681 0 : const char *gettype() const override final
3682 : {
3683 0 : return typestr();
3684 : }
3685 :
3686 0 : void scrollto(float, float cy) override final //note unnamed function parameter
3687 : {
3688 0 : SliderButton *button = static_cast<SliderButton *>(find(SliderButton::typestr(), false));
3689 0 : if(!button)
3690 : {
3691 0 : return;
3692 : }
3693 0 : float offset = h > button->h ? std::clamp((cy - button->h/2)/(h - button->h), 0.0f, 1.0f) : 0.0f;
3694 0 : int step = static_cast<int>((val - vmin) / vstep),
3695 0 : bstep = static_cast<int>(offset * (vmax - vmin) / vstep);
3696 0 : if(step != bstep)
3697 : {
3698 0 : changeval(bstep * vstep + vmin);
3699 : }
3700 : }
3701 :
3702 0 : void adjustchildren() override final
3703 : {
3704 0 : SliderButton *button = static_cast<SliderButton *>(find(SliderButton::typestr(), false));
3705 0 : if(!button)
3706 : {
3707 0 : return;
3708 : }
3709 0 : int step = static_cast<int>((val - vmin) / vstep),
3710 0 : bstep = static_cast<int>(button->y / (h - button->h) * (vmax - vmin) / vstep);
3711 0 : if(step != bstep)
3712 : {
3713 0 : button->y = (h - button->h) * step * vstep / (vmax - vmin);
3714 : }
3715 0 : button->adjust &= ~Align_VMask;
3716 :
3717 0 : Slider::adjustchildren();
3718 : }
3719 :
3720 0 : int wheelscrolldirection() const override final
3721 : {
3722 0 : return -1;
3723 : }
3724 : };
3725 :
3726 : struct TextEditor : Object
3727 : {
3728 : static TextEditor *focus;
3729 :
3730 0 : TextEditor() : edit(nullptr), keyfilter("") {}
3731 :
3732 0 : void setup(const char *name, int length, int height, float scale_ = 1, const char *initval = nullptr, int mode = Editor_Used, const char *keyfilter_ = nullptr)
3733 : {
3734 0 : Object::setup();
3735 0 : Editor *edit_ = useeditor(name, mode, false, initval);
3736 0 : if(edit_ != edit)
3737 : {
3738 0 : if(edit)
3739 : {
3740 0 : clearfocus();
3741 : }
3742 0 : edit = edit_;
3743 : }
3744 0 : else if(isfocus() && !hasstate(State_Hover))
3745 : {
3746 0 : commit();
3747 : }
3748 0 : if(initval && edit->mode == Editor_Focused && !isfocus())
3749 : {
3750 0 : edit->init(initval);
3751 : }
3752 0 : edit->active = true;
3753 0 : edit->linewrap = length < 0;
3754 0 : edit->maxx = edit->linewrap ? -1 : length;
3755 0 : edit->maxy = height <= 0 ? 1 : -1;
3756 0 : edit->pixelwidth = std::abs(length)*fontwidth();
3757 0 : if(edit->linewrap && edit->maxy == 1)
3758 : {
3759 0 : edit->updateheight();
3760 : }
3761 : else
3762 : {
3763 0 : edit->pixelheight = FONTH*std::max(height, 1);
3764 : }
3765 0 : scale = scale_;
3766 0 : if(keyfilter_)
3767 : {
3768 0 : keyfilter = std::string(keyfilter_);
3769 : }
3770 : else
3771 : {
3772 0 : keyfilter = std::string();
3773 : }
3774 0 : }
3775 0 : ~TextEditor()
3776 0 : {
3777 0 : clearfocus();
3778 0 : keyfilter = std::string();
3779 0 : }
3780 :
3781 0 : static void setfocus(TextEditor *e)
3782 : {
3783 0 : if(focus == e)
3784 : {
3785 0 : return;
3786 : }
3787 0 : focus = e;
3788 0 : bool allowtextinput = focus!=nullptr && focus->allowtextinput();
3789 0 : ::textinput(allowtextinput, TextInput_GUI);
3790 0 : ::keyrepeat(allowtextinput, KeyRepeat_GUI);
3791 : }
3792 0 : void setfocus()
3793 : {
3794 0 : setfocus(this);
3795 0 : }
3796 :
3797 0 : void clearfocus()
3798 : {
3799 0 : if(focus == this)
3800 : {
3801 0 : setfocus(nullptr);
3802 : }
3803 0 : }
3804 :
3805 0 : bool isfocus() const
3806 : {
3807 0 : return focus == this;
3808 : }
3809 :
3810 0 : static const char *typestr()
3811 : {
3812 0 : return "#TextEditor";
3813 : }
3814 :
3815 0 : const char *gettype() const override
3816 : {
3817 0 : return typestr();
3818 : }
3819 :
3820 0 : bool target(float, float) override final//note unnamed function parameters
3821 : {
3822 0 : return true;
3823 : }
3824 :
3825 0 : float drawscale() const
3826 : {
3827 0 : return scale / FONTH;
3828 : }
3829 :
3830 0 : void draw(float sx, float sy) override final
3831 : {
3832 0 : changedraw(Change_Shader | Change_Color);
3833 :
3834 0 : edit->rendered = true;
3835 :
3836 0 : float k = drawscale();
3837 0 : pushhudtranslate(sx, sy, k);
3838 :
3839 0 : edit->draw(fontwidth()/2, 0, 0xFFFFFF);
3840 :
3841 0 : pophudmatrix();
3842 :
3843 0 : Object::draw(sx, sy);
3844 0 : }
3845 :
3846 0 : void layout() override final
3847 : {
3848 0 : Object::layout();
3849 :
3850 0 : float k = drawscale();
3851 0 : w = std::max(w, (edit->pixelwidth + fontwidth())*k);
3852 0 : h = std::max(h, edit->pixelheight*k);
3853 0 : }
3854 :
3855 0 : virtual void resetmark(float cx, float cy)
3856 : {
3857 0 : edit->mark(false);
3858 0 : offsetx = cx;
3859 0 : offsety = cy;
3860 0 : }
3861 :
3862 0 : void press(float cx, float cy) override final
3863 : {
3864 0 : setfocus();
3865 0 : resetmark(cx, cy);
3866 0 : }
3867 :
3868 0 : void hold(float cx, float cy) override final
3869 : {
3870 0 : if(isfocus())
3871 : {
3872 0 : float k = drawscale();
3873 0 : bool dragged = std::max(std::fabs(cx - offsetx), std::fabs(cy - offsety)) > (FONTH/8.0f)*k;
3874 0 : edit->hit(static_cast<int>(std::floor(cx/k - fontwidth()/2)), static_cast<int>(std::floor(cy/k)), dragged);
3875 : }
3876 0 : }
3877 :
3878 0 : void scrollup(float, float) override final //note unnamed function parameters
3879 : {
3880 0 : edit->scrollup();
3881 0 : }
3882 :
3883 0 : void scrolldown(float, float) override final //note unnamed function parameters
3884 : {
3885 0 : edit->scrolldown();
3886 0 : }
3887 :
3888 0 : virtual void cancel()
3889 : {
3890 0 : clearfocus();
3891 0 : }
3892 :
3893 0 : virtual void commit()
3894 : {
3895 0 : clearfocus();
3896 0 : }
3897 :
3898 0 : bool key(int code, bool isdown) override final
3899 : {
3900 0 : if(Object::key(code, isdown))
3901 : {
3902 0 : return true;
3903 : }
3904 0 : if(!isfocus())
3905 : {
3906 0 : return false;
3907 : }
3908 0 : switch(code)
3909 : {
3910 0 : case SDLK_ESCAPE:
3911 : {
3912 0 : if(isdown)
3913 : {
3914 0 : cancel();
3915 : }
3916 0 : return true;
3917 : }
3918 0 : case SDLK_RETURN:
3919 : case SDLK_TAB:
3920 : {
3921 0 : if(edit->maxy != 1)
3922 : {
3923 0 : break;
3924 : }
3925 : [[fallthrough]];
3926 : }
3927 : case SDLK_KP_ENTER:
3928 : {
3929 0 : if(isdown)
3930 : {
3931 0 : commit();
3932 : }
3933 0 : return true;
3934 : }
3935 : }
3936 0 : if(isdown)
3937 : {
3938 0 : edit->key(code);
3939 : }
3940 0 : return true;
3941 : }
3942 :
3943 0 : virtual bool allowtextinput() const
3944 : {
3945 0 : return true;
3946 : }
3947 :
3948 0 : bool textinput(const char *str, int len) override final
3949 : {
3950 0 : if(Object::textinput(str, len))
3951 : {
3952 0 : return true;
3953 : }
3954 0 : if(!isfocus() || !allowtextinput())
3955 : {
3956 0 : return false;
3957 : }
3958 0 : if(keyfilter.empty())
3959 : {
3960 0 : edit->input(str, len);
3961 : }
3962 : else
3963 : {
3964 0 : while(len > 0)
3965 : {
3966 0 : int accept = std::min(len, static_cast<int>(std::strspn(str, keyfilter.c_str())));
3967 0 : if(accept > 0)
3968 : {
3969 0 : edit->input(str, accept);
3970 : }
3971 0 : str += accept + 1;
3972 0 : len -= accept + 1;
3973 0 : if(len <= 0)
3974 : {
3975 0 : break;
3976 : }
3977 0 : int reject = static_cast<int>(std::strcspn(str, keyfilter.c_str()));
3978 0 : str += reject;
3979 0 : str -= reject;
3980 : }
3981 : }
3982 0 : return true;
3983 : }
3984 : protected:
3985 : Editor *edit;
3986 : private:
3987 : std::string keyfilter;
3988 : float scale, offsetx, offsety;
3989 :
3990 : };
3991 :
3992 : TextEditor *TextEditor::focus = nullptr;
3993 :
3994 0 : static const char *getsval(ident *id, bool &shouldfree, const char *val = "")
3995 : {
3996 0 : switch(id->type)
3997 : {
3998 0 : case Id_Var:
3999 : {
4000 0 : val = intstr(*id->val.storage.i);
4001 0 : break;
4002 : }
4003 0 : case Id_FloatVar:
4004 : {
4005 0 : val = floatstr(*id->val.storage.f);
4006 0 : break;
4007 : }
4008 0 : case Id_StringVar:
4009 : {
4010 0 : val = *id->val.storage.s;
4011 0 : break;
4012 : }
4013 0 : case Id_Alias:
4014 : {
4015 0 : val = id->getstr();
4016 0 : break;
4017 : }
4018 0 : case Id_Command:
4019 : {
4020 0 : val = executestr(id, nullptr, 0, true);
4021 0 : shouldfree = true;
4022 0 : break;
4023 : }
4024 : }
4025 0 : return val;
4026 : }
4027 :
4028 0 : static void setsval(ident *id, const char *val, uint *onchange = nullptr)
4029 : {
4030 0 : switch(id->type)
4031 : {
4032 0 : case Id_Var:
4033 : {
4034 0 : setvarchecked(id, parseint(val));
4035 0 : break;
4036 : }
4037 0 : case Id_FloatVar:
4038 : {
4039 0 : setfvarchecked(id, parsefloat(val));
4040 0 : break;
4041 : }
4042 0 : case Id_StringVar:
4043 : {
4044 0 : setsvarchecked(id, val);
4045 0 : break;
4046 : }
4047 0 : case Id_Alias:
4048 : {
4049 0 : alias(id->name, val);
4050 0 : break;
4051 : }
4052 0 : case Id_Command:
4053 : {
4054 : tagval t;
4055 0 : t.setstr(newstring(val));
4056 0 : execute(id, &t, 1);
4057 0 : break;
4058 : }
4059 : }
4060 0 : if(onchange && (*onchange&Code_OpMask) != Code_Exit)
4061 : {
4062 0 : execute(onchange);
4063 : }
4064 0 : }
4065 :
4066 : struct Field : TextEditor
4067 : {
4068 : ident *id;
4069 : bool changed;
4070 :
4071 0 : Field() : id(nullptr), changed(false) {}
4072 :
4073 0 : void setup(ident *id_, int length, uint *onchange, float scale = 1, const char *keyfilter_ = nullptr)
4074 : {
4075 0 : if(isfocus() && !hasstate(State_Hover))
4076 : {
4077 0 : commit();
4078 : }
4079 0 : if(changed)
4080 : {
4081 0 : if(id == id_)
4082 : {
4083 0 : setsval(id, edit->lines[0].text, onchange);
4084 : }
4085 0 : changed = false;
4086 : }
4087 0 : bool shouldfree = false;
4088 0 : const char *initval = id != id_ || !isfocus() ? getsval(id_, shouldfree) : nullptr;
4089 0 : TextEditor::setup(id_->name, length, 0, scale, initval, Editor_Focused, keyfilter_);
4090 0 : if(shouldfree)
4091 : {
4092 0 : delete[] initval;
4093 : }
4094 0 : id = id_;
4095 0 : }
4096 :
4097 0 : static const char *typestr()
4098 : {
4099 0 : return "#Field";
4100 : }
4101 0 : const char *gettype() const override
4102 : {
4103 0 : return typestr();
4104 : }
4105 :
4106 0 : void commit() override final
4107 : {
4108 0 : TextEditor::commit();
4109 0 : changed = true;
4110 0 : }
4111 :
4112 0 : void cancel() override final
4113 : {
4114 0 : TextEditor::cancel();
4115 0 : changed = false;
4116 0 : }
4117 : };
4118 :
4119 : struct KeyField final : Field
4120 : {
4121 0 : static const char *typestr()
4122 : {
4123 0 : return "#KeyField";
4124 : }
4125 0 : const char *gettype() const override final
4126 : {
4127 0 : return typestr();
4128 : }
4129 :
4130 0 : void resetmark(float cx, float cy) override final
4131 : {
4132 0 : edit->clear();
4133 0 : Field::resetmark(cx, cy);
4134 0 : }
4135 :
4136 0 : void insertkey(int code)
4137 : {
4138 0 : const char *keyname = getkeyname(code);
4139 0 : if(keyname)
4140 : {
4141 0 : if(!edit->empty())
4142 : {
4143 0 : edit->insert(" ");
4144 : }
4145 0 : edit->insert(keyname);
4146 : }
4147 0 : }
4148 :
4149 0 : bool rawkey(int code, bool isdown) override final
4150 : {
4151 0 : if(Object::rawkey(code, isdown))
4152 : {
4153 0 : return true;
4154 : }
4155 0 : if(!isfocus() || !isdown)
4156 : {
4157 0 : return false;
4158 : }
4159 0 : if(code == SDLK_ESCAPE)
4160 : {
4161 0 : commit();
4162 : }
4163 : else
4164 : {
4165 0 : insertkey(code);
4166 : }
4167 0 : return true;
4168 : }
4169 :
4170 0 : bool allowtextinput() const override final
4171 : {
4172 0 : return false;
4173 : }
4174 : };
4175 :
4176 : struct Preview : Target
4177 : {
4178 0 : void startdraw() const override final
4179 : {
4180 0 : glDisable(GL_BLEND);
4181 :
4182 0 : if(clipstack.size())
4183 : {
4184 0 : glDisable(GL_SCISSOR_TEST);
4185 : }
4186 0 : }
4187 :
4188 0 : void enddraw() const override final
4189 : {
4190 0 : glEnable(GL_BLEND);
4191 :
4192 0 : if(clipstack.size())
4193 : {
4194 0 : glEnable(GL_SCISSOR_TEST);
4195 : }
4196 0 : }
4197 : };
4198 :
4199 : struct ModelPreview final : Preview
4200 : {
4201 : std::string name;
4202 : int anim;
4203 :
4204 0 : ModelPreview() : name("") {}
4205 0 : ~ModelPreview() {}
4206 :
4207 0 : void setup(const char *name_, const char *animspec, float minw_, float minh_)
4208 : {
4209 0 : Preview::setup(minw_, minh_);
4210 0 : name = std::string(name_);
4211 :
4212 0 : anim = Anim_All;
4213 0 : if(animspec[0])
4214 : {
4215 0 : if(isdigit(animspec[0]))
4216 : {
4217 0 : anim = parseint(animspec);
4218 0 : if(anim >= 0)
4219 : {
4220 0 : anim %= Anim_Index;
4221 : }
4222 : else
4223 : {
4224 0 : anim = Anim_All;
4225 : }
4226 : }
4227 : else
4228 : {
4229 0 : std::vector<size_t> anims = findanims(animspec);
4230 0 : if(anims.size())
4231 : {
4232 0 : anim = anims[0];
4233 : }
4234 0 : }
4235 : }
4236 0 : anim |= Anim_Loop;
4237 0 : }
4238 :
4239 0 : static const char *typestr()
4240 : {
4241 0 : return "#ModelPreview";
4242 : }
4243 0 : const char *gettype() const override final
4244 : {
4245 0 : return typestr();
4246 : }
4247 :
4248 0 : void draw(float sx, float sy) override final
4249 : {
4250 0 : Object::draw(sx, sy);
4251 :
4252 0 : changedraw(Change_Shader);
4253 :
4254 : int sx1, sy1, sx2, sy2;
4255 0 : window->calcscissor(sx, sy, sx+w, sy+h, sx1, sy1, sx2, sy2, false);
4256 0 : modelpreview.start(sx1, sy1, sx2-sx1, sy2-sy1, false, clipstack.size() > 0);
4257 0 : model *m = loadmodel(name);
4258 0 : if(m)
4259 : {
4260 0 : vec center, radius;
4261 0 : m->boundbox(center, radius);
4262 : float yaw;
4263 0 : vec o = calcmodelpreviewpos(radius, yaw).sub(center);
4264 0 : rendermodel(name, anim, o, yaw, 0, 0, 0, nullptr, nullptr, 0);
4265 : }
4266 0 : if(clipstack.size())
4267 : {
4268 0 : clipstack.back().scissor();
4269 : }
4270 0 : modelpreview.end();
4271 0 : }
4272 : };
4273 :
4274 : class PrefabPreview final : public Preview
4275 : {
4276 : public:
4277 0 : PrefabPreview() : name(nullptr)
4278 : {
4279 0 : }
4280 :
4281 0 : ~PrefabPreview()
4282 0 : {
4283 0 : delete[] name;
4284 0 : }
4285 :
4286 0 : void setup(const char *name_, int color_, float minw_, float minh_)
4287 : {
4288 0 : Preview::setup(minw_, minh_);
4289 0 : setstring(name, name_);
4290 0 : color = vec::hexcolor(color_);
4291 0 : }
4292 :
4293 0 : static const char *typestr()
4294 : {
4295 0 : return "#PrefabPreview";
4296 : }
4297 :
4298 0 : const char *gettype() const override final
4299 : {
4300 0 : return typestr();
4301 : }
4302 :
4303 0 : void draw(float sx, float sy) override final
4304 : {
4305 0 : Object::draw(sx, sy);
4306 0 : changedraw(Change_Shader);
4307 : int sx1, sy1, sx2, sy2;
4308 0 : window->calcscissor(sx, sy, sx+w, sy+h, sx1, sy1, sx2, sy2, false);
4309 0 : modelpreview.start(sx1, sy1, sx2-sx1, sy2-sy1, false, clipstack.size() > 0);
4310 0 : previewprefab(name, color);
4311 0 : if(clipstack.size())
4312 : {
4313 0 : clipstack.back().scissor();
4314 : }
4315 0 : modelpreview.end();
4316 0 : }
4317 : private:
4318 : char *name;
4319 : vec color;
4320 : };
4321 :
4322 : static VARP(uislotviewtime, 0, 25, 1000);
4323 : static int lastthumbnail = 0;
4324 :
4325 : class SlotViewer : public Target
4326 : {
4327 : public:
4328 0 : void setup(int index_, float minw_ = 0, float minh_ = 0)
4329 : {
4330 0 : Target::setup(minw_, minh_);
4331 0 : index = index_;
4332 0 : }
4333 :
4334 0 : static const char *typestr() { return "#SlotViewer"; }
4335 0 : const char *gettype() const override
4336 : {
4337 0 : return typestr();
4338 : }
4339 :
4340 0 : void draw(float sx, float sy) override
4341 : {
4342 0 : Slot &slot = lookupslot(index, false);
4343 0 : previewslot(slot, *slot.variants, sx, sy);
4344 :
4345 0 : Object::draw(sx, sy);
4346 0 : }
4347 :
4348 : protected:
4349 : int index;
4350 :
4351 0 : void previewslot(Slot &slot, VSlot &vslot, float x, float y)
4352 : {
4353 0 : if(slot.sts.empty())
4354 : {
4355 0 : return;
4356 : }
4357 0 : Texture *t = nullptr,
4358 0 : *glowtex = nullptr;
4359 0 : if(slot.loaded)
4360 : {
4361 0 : t = slot.sts[0].t;
4362 0 : if(t == notexture)
4363 : {
4364 0 : return;
4365 : }
4366 0 : Slot &slot = *vslot.slot;
4367 0 : if(slot.texmask&(1 << Tex_Glow))
4368 : {
4369 0 : for(uint j = 0; j < slot.sts.size(); j++)
4370 : {
4371 0 : if(slot.sts[j].type == Tex_Glow)
4372 : {
4373 0 : glowtex = slot.sts[j].t;
4374 0 : break;
4375 : }
4376 : }
4377 : }
4378 : }
4379 : else
4380 : {
4381 0 : if(!slot.thumbnail)
4382 : {
4383 0 : if(totalmillis - lastthumbnail < uislotviewtime)
4384 : {
4385 0 : return;
4386 : }
4387 0 : slot.loadthumbnail();
4388 0 : lastthumbnail = totalmillis;
4389 : }
4390 0 : if(slot.thumbnail != notexture)
4391 : {
4392 0 : t = slot.thumbnail;
4393 : }
4394 : else
4395 : {
4396 0 : return;
4397 : }
4398 : }
4399 :
4400 0 : changedraw(Change_Shader | Change_Color);
4401 :
4402 0 : SETSHADER(hudrgb);
4403 0 : std::array<vec2,4> tc = { vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1) };
4404 0 : int xoff = vslot.offset.x(),
4405 0 : yoff = vslot.offset.y();
4406 0 : if(vslot.rotation)
4407 : {
4408 0 : const texrotation &r = texrotations[vslot.rotation];
4409 0 : if(r.swapxy)
4410 : {
4411 0 : std::swap(xoff, yoff);
4412 0 : for(int k = 0; k < 4; ++k)
4413 : {
4414 0 : std::swap(tc[k].x, tc[k].y);
4415 : }
4416 : }
4417 0 : if(r.flipx)
4418 : {
4419 0 : xoff *= -1;
4420 0 : for(int k = 0; k < 4; ++k)
4421 : {
4422 0 : tc[k].x *= -1;
4423 : }
4424 : }
4425 0 : if(r.flipy)
4426 : {
4427 0 : yoff *= -1;
4428 0 : for(int k = 0; k < 4; ++k)
4429 : {
4430 0 : tc[k].y *= -1;
4431 : }
4432 : }
4433 : }
4434 0 : float xt = std::min(1.0f, t->xs/static_cast<float>(t->ys)),
4435 0 : yt = std::min(1.0f, t->ys/static_cast<float>(t->xs));
4436 0 : for(int k = 0; k < 4; ++k)
4437 : {
4438 0 : tc[k].x = tc[k].x/xt - static_cast<float>(xoff)/t->xs;
4439 0 : tc[k].y = tc[k].y/yt - static_cast<float>(yoff)/t->ys;
4440 : }
4441 0 : glBindTexture(GL_TEXTURE_2D, t->id);
4442 0 : if(slot.loaded)
4443 : {
4444 0 : gle::color(vslot.colorscale);
4445 : }
4446 : else
4447 : {
4448 0 : gle::colorf(1, 1, 1);
4449 : }
4450 0 : quad(x, y, w, h, tc);
4451 0 : if(glowtex)
4452 : {
4453 0 : glBlendFunc(GL_SRC_ALPHA, GL_ONE);
4454 0 : glBindTexture(GL_TEXTURE_2D, glowtex->id);
4455 0 : gle::color(vslot.glowcolor);
4456 0 : quad(x, y, w, h, tc);
4457 0 : glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
4458 : }
4459 : }
4460 : };
4461 :
4462 : class VSlotViewer final : public SlotViewer
4463 : {
4464 : public:
4465 0 : static const char *typestr()
4466 : {
4467 0 : return "#VSlotViewer";
4468 : }
4469 :
4470 0 : const char *gettype() const final
4471 : {
4472 0 : return typestr();
4473 : }
4474 :
4475 0 : void draw(float sx, float sy) final
4476 : {
4477 0 : VSlot &vslot = lookupvslot(index, false);
4478 0 : previewslot(*vslot.slot, vslot, sx, sy);
4479 :
4480 0 : Object::draw(sx, sy);
4481 0 : }
4482 : };
4483 :
4484 : //new ui command
4485 1 : static void newui(const char *name, const char *contents, const char *onshow, const char *onhide)
4486 : {
4487 1 : auto itr = windows.find(name);
4488 1 : if(itr != windows.end())
4489 : {
4490 0 : if((*itr).second == UI::window)
4491 : {
4492 0 : return;
4493 : }
4494 0 : world->hide((*itr).second);
4495 0 : windows.erase(itr);
4496 0 : delete window;
4497 : }
4498 3 : windows[name] = new Window(name, contents, onshow, onhide);
4499 : }
4500 :
4501 : //command
4502 1 : static void uiallowinput(const int *val)
4503 : {
4504 1 : if(window)
4505 : {
4506 0 : if(*val >= 0)
4507 : {
4508 0 : window->allowinput = *val!=0;
4509 : }
4510 0 : intret(window->allowinput ? 1 : 0);
4511 : }
4512 1 : }
4513 :
4514 : //command
4515 1 : void uieschide (const int *val)
4516 : {
4517 1 : if(window)
4518 : {
4519 0 : if(*val >= 0)
4520 : {
4521 0 : window->eschide = *val!=0;
4522 0 : intret(window->eschide ? 1 : 0);
4523 : }
4524 : }
4525 1 : }
4526 :
4527 : //used in iengine
4528 3 : bool showui(const char *name)
4529 : {
4530 3 : if(!world)
4531 : {
4532 3 : return false;
4533 : }
4534 0 : auto itr = windows.find(name);
4535 0 : return (itr != windows.end()) && world->show((*itr).second);
4536 : }
4537 :
4538 : //used in iengine
4539 2 : bool hideui(const char *name)
4540 : {
4541 2 : if(!world)
4542 : {
4543 2 : return false;
4544 : }
4545 0 : if(!name)
4546 : {
4547 0 : return world->hideall() > 0;
4548 : }
4549 0 : auto itr = windows.find(name);
4550 0 : return (itr != windows.end()) && world->hide((*itr).second);
4551 : }
4552 :
4553 : //used in console
4554 2 : bool toggleui(const char *name)
4555 : {
4556 2 : if(showui(name))
4557 : {
4558 0 : return true;
4559 : }
4560 2 : hideui(name);
4561 2 : return false;
4562 : }
4563 :
4564 : //used in iengine
4565 1 : void holdui(const char *name, bool on)
4566 : {
4567 1 : if(!world)
4568 : {
4569 1 : return;
4570 : }
4571 0 : if(on)
4572 : {
4573 0 : showui(name);
4574 : }
4575 : else
4576 : {
4577 0 : hideui(name);
4578 : }
4579 : }
4580 :
4581 : //used in iengine
4582 3 : bool uivisible(const char *name)
4583 : {
4584 3 : if(!name)
4585 : {
4586 0 : return world->children.size() > 0;
4587 : }
4588 3 : auto itr = windows.find(name);
4589 3 : if(itr != windows.end() && (*itr).second)
4590 : {
4591 0 : return std::find(world->children.begin(), world->children.end(), (*itr).second) != world->children.end();
4592 : }
4593 3 : return false;
4594 : }
4595 :
4596 26 : static void ifstateval(bool state, tagval * t, tagval * f)
4597 : {
4598 26 : if(state)
4599 : {
4600 0 : if(t->type == Value_Null)
4601 : {
4602 0 : intret(1);
4603 : }
4604 : else
4605 : {
4606 0 : result(*t);
4607 : }
4608 : }
4609 26 : else if(f->type == Value_Null)
4610 : {
4611 26 : intret(0);
4612 : }
4613 : else
4614 : {
4615 0 : result(*f);
4616 : }
4617 26 : }
4618 :
4619 0 : static float parsepixeloffset(const tagval *t, int size)
4620 : {
4621 0 : switch(t->type)
4622 : {
4623 0 : case Value_Integer:
4624 : {
4625 0 : return t->i;
4626 : }
4627 0 : case Value_Float:
4628 : {
4629 0 : return t->f;
4630 : }
4631 0 : case Value_Null:
4632 : {
4633 0 : return 0;
4634 : }
4635 0 : default:
4636 : {
4637 0 : const char *s = t->getstr();
4638 : char *end;
4639 0 : float val = std::strtod(s, &end);
4640 0 : return *end == 'p' ? val/size : val;
4641 : }
4642 : }
4643 : }
4644 :
4645 8 : static void buildtext(tagval &t, float scale, float scalemod, const Color &color, float wrap, uint *children)
4646 : {
4647 8 : if(scale <= 0)
4648 : {
4649 8 : scale = 1;
4650 : }
4651 8 : scale *= scalemod;
4652 8 : switch(t.type)
4653 : {
4654 0 : case Value_Integer:
4655 : {
4656 0 : BUILD(TextInt, o, o->setup(t.i, scale, color, wrap), children);
4657 0 : break;
4658 : }
4659 0 : case Value_Float:
4660 : {
4661 0 : BUILD(TextFloat, o, o->setup(t.f, scale, color, wrap), children);
4662 0 : break;
4663 : }
4664 0 : case Value_CString:
4665 : case Value_Macro:
4666 : case Value_String:
4667 : {
4668 0 : if(t.s[0])
4669 : {
4670 0 : BUILD(TextString, o, o->setup(t.s, scale, color, wrap), children);
4671 0 : break;
4672 : }
4673 : [[fallthrough]];
4674 : }
4675 : default:
4676 : {
4677 8 : BUILD(Object, o, o->setup(), children);
4678 8 : break;
4679 : }
4680 : }
4681 8 : }
4682 :
4683 1 : void inituicmds()
4684 : {
4685 :
4686 1 : static auto showuicmd = [] (const char * name)
4687 : {
4688 1 : if(!world)
4689 : {
4690 1 : intret(-1);
4691 1 : return;
4692 : }
4693 0 : intret(showui(name) ? 1 : 0);
4694 : };
4695 :
4696 1 : static auto hideuicmd = [] (const char * name)
4697 : {
4698 1 : if(!world)
4699 : {
4700 1 : intret(-1);
4701 1 : return;
4702 : }
4703 0 : intret(hideui(name) ? 1 : 0);
4704 : };
4705 :
4706 1 : static auto hidetopuicmd = [] ()
4707 : {
4708 1 : if(!world)
4709 : {
4710 1 : intret(-1);
4711 1 : return;
4712 : }
4713 0 : intret(world->hidetop() ? 1 : 0);
4714 : };
4715 :
4716 1 : static auto hidealluicmd = [] ()
4717 : {
4718 1 : if(!world)
4719 : {
4720 1 : intret(-1);
4721 1 : return;
4722 : }
4723 0 : intret(world->hideall());
4724 : };
4725 :
4726 1 : static auto toggleuicmd = [] (const char * name)
4727 : {
4728 :
4729 1 : intret(toggleui(name) ? 1 : 0);
4730 1 : };
4731 :
4732 1 : static auto holduicmd = [] (const char * name, const int * down)
4733 : {
4734 1 : holdui(name, *down!=0);
4735 1 : };
4736 :
4737 1 : static auto uivisiblecmd = [] (const char * name)
4738 : {
4739 1 : intret(uivisible(name) ? 1 : 0);
4740 1 : };
4741 :
4742 1 : addcommand("showui", reinterpret_cast<identfun>(+showuicmd), "s", Id_Command);
4743 1 : addcommand("hideui", reinterpret_cast<identfun>(+hideuicmd), "s", Id_Command);
4744 1 : addcommand("hidetopui", reinterpret_cast<identfun>(+hidetopuicmd),"", Id_Command);
4745 1 : addcommand("hideallui", reinterpret_cast<identfun>(+hidealluicmd),"", Id_Command);
4746 1 : addcommand("toggleui", reinterpret_cast<identfun>(+toggleuicmd), "s", Id_Command);
4747 1 : addcommand("holdui", reinterpret_cast<identfun>(+holduicmd), "sD", Id_Command);
4748 1 : addcommand("uivisible", reinterpret_cast<identfun>(+uivisiblecmd),"s", Id_Command);
4749 :
4750 1 : static auto uinamecmd = [] ()
4751 : {
4752 1 : if(window)
4753 : {
4754 0 : result(window->name.c_str());
4755 : }
4756 1 : };
4757 1 : addcommand("uiname", reinterpret_cast<identfun>(+uinamecmd), "", Id_Command);
4758 :
4759 : #define DOSTATE(flags, func) \
4760 : addcommand("ui" #func, reinterpret_cast<identfun>(+[] (uint *t, uint *f) { executeret(buildparent && buildparent->haschildstate(flags) ? t : f); }), "ee", Id_Command); \
4761 : addcommand("ui" #func "?", reinterpret_cast<identfun>(+[] (tagval *t, tagval *f) { ifstateval(buildparent && buildparent->haschildstate(flags), t, f); }), "tt", Id_Command); \
4762 : addcommand("ui" #func "+", reinterpret_cast<identfun>(+[] (uint *t, uint *f) { executeret(buildparent && static_cast<int>(buildparent->children.size()) > buildchild && buildparent->children[buildchild]->haschildstate(flags) ? t : f); }), "ee", Id_Command); \
4763 : addcommand("ui" #func "+?", reinterpret_cast<identfun>(+[] (tagval *t, tagval *f) { ifstateval(buildparent && static_cast<int>(buildparent->children.size()) > buildchild && buildparent->children[buildchild]->haschildstate(flags), t, f); }), "tt", Id_Command);
4764 49 : DOSTATES
4765 : #undef DOSTATE
4766 :
4767 2 : addcommand("uifocus", reinterpret_cast<identfun>(+[] (uint *t, uint *f) { executeret(buildparent && TextEditor::focus == buildparent ? t : f); }), "ee", Id_Command);
4768 2 : addcommand("uifocus?", reinterpret_cast<identfun>(+[] (tagval *t, tagval *f) { ifstateval(buildparent && TextEditor::focus == buildparent, t, f); }), "tt", Id_Command);
4769 2 : addcommand("uifocus+", reinterpret_cast<identfun>(+[] (uint *t, uint *f) { executeret(buildparent && static_cast<int>(buildparent->children.size()) > buildchild && TextEditor::focus == buildparent->children[buildchild] ? t : f); }), "ee", Id_Command);
4770 2 : addcommand("uifocus+?", reinterpret_cast<identfun>(+[] (tagval *t, tagval *f) { ifstateval(buildparent && static_cast<int>(buildparent->children.size()) > buildchild && TextEditor::focus == buildparent->children[buildchild], t, f); }), "tt", Id_Command);
4771 1 : addcommand("uialign", reinterpret_cast<identfun>(+[] (int *xalign, int *yalign)
4772 : {
4773 : {
4774 1 : if(buildparent)
4775 : {
4776 0 : buildparent->setalign(*xalign, *yalign);
4777 : }
4778 : };
4779 1 : }), "ii", Id_Command);
4780 1 : addcommand("uialign-", reinterpret_cast<identfun>(+[] (int *xalign, int *yalign)
4781 : {
4782 : {
4783 1 : if(buildparent && buildchild > 0)
4784 : {
4785 0 : buildparent->children[buildchild-1]->setalign(*xalign, *yalign);
4786 : }
4787 : };
4788 1 : }), "ii", Id_Command);
4789 1 : addcommand("uialign*", reinterpret_cast<identfun>(+[] (int *xalign, int *yalign)
4790 : {
4791 : {
4792 1 : if(buildparent)
4793 : {
4794 0 : for(int i = 0; i < buildchild; ++i)
4795 : {
4796 0 : buildparent->children[i]->setalign(*xalign, *yalign);
4797 : }
4798 : }
4799 : };
4800 1 : }), "ii", Id_Command);
4801 2 : addcommand("uiclamp", reinterpret_cast<identfun>(+[] (int *left, int *right, int *top, int *bottom) { { if(buildparent) { buildparent->setclamp(*left, *right, *top, *bottom); } }; }), "iiii", Id_Command);
4802 2 : addcommand("uiclamp-", reinterpret_cast<identfun>(+[] (int *left, int *right, int *top, int *bottom) { { if(buildparent && buildchild > 0) { buildparent->children[buildchild-1]->setclamp(*left, *right, *top, *bottom); } }; }), "iiii", Id_Command);
4803 2 : addcommand("uiclamp*", reinterpret_cast<identfun>(+[] (int *left, int *right, int *top, int *bottom) { { if(buildparent) { for(int i = 0; i < buildchild; ++i) { buildparent->children[i]->setclamp(*left, *right, *top, *bottom); } } }; }), "iiii", Id_Command);
4804 2 : addcommand("uigroup", reinterpret_cast<identfun>(+[] (uint *children) { BUILD(Object, o, o->setup(), children); }), "e", Id_Command);
4805 2 : addcommand("uihlist", reinterpret_cast<identfun>(+[] (float *space, uint *children) { BUILD(HorizontalList, o, o->setup(*space), children); }), "fe", Id_Command);
4806 2 : addcommand("uivlist", reinterpret_cast<identfun>(+[] (float *space, uint *children) { BUILD(VerticalList, o, o->setup(*space), children); }), "fe", Id_Command);
4807 2 : addcommand("uilist", reinterpret_cast<identfun>(+[] (float *space, uint *children) { { for(Object *parent = buildparent; parent && !parent->istype<VerticalList>(); parent = parent->parent) { if(parent->istype<HorizontalList>()) { BUILD(VerticalList, o, o->setup(*space), children); return; } } BUILD(HorizontalList, o, o->setup(*space), children); }; }), "fe", Id_Command);
4808 2 : addcommand("uigrid", reinterpret_cast<identfun>(+[] (int *columns, float *spacew, float *spaceh, uint *children) { BUILD(Grid, o, o->setup(*columns, *spacew, *spaceh), children); }), "iffe", Id_Command);
4809 2 : addcommand("uitableheader", reinterpret_cast<identfun>(+[] (uint *columndata, uint *children) { BUILDCOLUMNS(TableHeader, o, o->setup(), columndata, children); }), "ee", Id_Command);
4810 2 : addcommand("uitablerow", reinterpret_cast<identfun>(+[] (uint *columndata, uint *children) { BUILDCOLUMNS(TableRow, o, o->setup(), columndata, children); }), "ee", Id_Command);
4811 2 : addcommand("uitable", reinterpret_cast<identfun>(+[] (float *spacew, float *spaceh, uint *children) { BUILD(Table, o, o->setup(*spacew, *spaceh), children); }), "ffe", Id_Command);
4812 2 : addcommand("uispace", reinterpret_cast<identfun>(+[] (float *spacew, float *spaceh, uint *children) { BUILD(Spacer, o, o->setup(*spacew, *spaceh), children); }), "ffe", Id_Command);
4813 2 : addcommand("uioffset", reinterpret_cast<identfun>(+[] (float *offsetx, float *offsety, uint *children) { BUILD(Offsetter, o, o->setup(*offsetx, *offsety), children); }), "ffe", Id_Command);
4814 2 : addcommand("uifill", reinterpret_cast<identfun>(+[] (float *minw, float *minh, uint *children) { BUILD(Filler, o, o->setup(*minw, *minh), children); }), "ffe", Id_Command);
4815 2 : addcommand("uitarget", reinterpret_cast<identfun>(+[] (float *minw, float *minh, uint *children) { BUILD(Target, o, o->setup(*minw, *minh), children); }), "ffe", Id_Command);
4816 2 : addcommand("uiclip", reinterpret_cast<identfun>(+[] (float *clipw, float *cliph, uint *children) { BUILD(Clipper, o, o->setup(*clipw, *cliph), children); }), "ffe", Id_Command);
4817 2 : addcommand("uiscroll", reinterpret_cast<identfun>(+[] (float *clipw, float *cliph, uint *children) { BUILD(Scroller, o, o->setup(*clipw, *cliph), children); }), "ffe", Id_Command);
4818 2 : addcommand("uihscrolloffset", reinterpret_cast<identfun>(+[] () { { if(buildparent && buildparent->istype<Scroller>()) { Scroller *scroller = static_cast<Scroller *>(buildparent); floatret(scroller->offsetx); } }; }), "", Id_Command);
4819 2 : addcommand("uivscrolloffset", reinterpret_cast<identfun>(+[] () { { if(buildparent && buildparent->istype<Scroller>()) { Scroller *scroller = static_cast<Scroller *>(buildparent); floatret(scroller->offsety); } }; }), "", Id_Command);
4820 2 : addcommand("uihscrollbar", reinterpret_cast<identfun>(+[] (uint *children) { BUILD(HorizontalScrollBar, o, o->setup(), children); }), "e", Id_Command);
4821 2 : addcommand("uivscrollbar", reinterpret_cast<identfun>(+[] (uint *children) { BUILD(VerticalScrollBar, o, o->setup(), children); }), "e", Id_Command);
4822 2 : addcommand("uiscrollarrow", reinterpret_cast<identfun>(+[] (float *dir, uint *children) { BUILD(ScrollArrow, o, o->setup(*dir), children); }), "fe", Id_Command);
4823 2 : addcommand("uiscrollbutton", reinterpret_cast<identfun>(+[] (uint *children) { BUILD(ScrollButton, o, o->setup(), children); }), "e", Id_Command);
4824 2 : addcommand("uihslider", reinterpret_cast<identfun>(+[] (ident *var, float *vmin, float *vmax, float *vstep, uint *onchange, uint *children) { BUILD(HorizontalSlider, o, o->setup(var, *vmin, *vmax, *vstep, onchange), children); }), "rfffee", Id_Command);
4825 2 : addcommand("uivslider", reinterpret_cast<identfun>(+[] (ident *var, float *vmin, float *vmax, float *vstep, uint *onchange, uint *children) { BUILD(VerticalSlider, o, o->setup(var, *vmin, *vmax, *vstep, onchange), children); }), "rfffee", Id_Command);
4826 2 : addcommand("uisliderarrow", reinterpret_cast<identfun>(+[] (float *dir, uint *children) { BUILD(SliderArrow, o, o->setup(*dir), children); }), "fe", Id_Command);
4827 2 : addcommand("uisliderbutton", reinterpret_cast<identfun>(+[] (uint *children) { BUILD(SliderButton, o, o->setup(), children); }), "e", Id_Command);
4828 2 : addcommand("uicolor", reinterpret_cast<identfun>(+[] (int *c, float *minw, float *minh, uint *children) { BUILD(FillColor, o, o->setup(FillColor::SOLID, Color(*c), *minw, *minh), children); }), "iffe", Id_Command);
4829 2 : addcommand("uimodcolor", reinterpret_cast<identfun>(+[] (int *c, float *minw, float *minh, uint *children) { BUILD(FillColor, o, o->setup(FillColor::MODULATE, Color(*c), *minw, *minh), children); }), "iffe", Id_Command);
4830 2 : addcommand("uivgradient", reinterpret_cast<identfun>(+[] (int *c, int *c2, float *minw, float *minh, uint *children) { BUILD(Gradient, o, o->setup(Gradient::SOLID, Gradient::VERTICAL, Color(*c), Color(*c2), *minw, *minh), children); }), "iiffe", Id_Command);
4831 2 : addcommand("uimodvgradient", reinterpret_cast<identfun>(+[] (int *c, int *c2, float *minw, float *minh, uint *children) { BUILD(Gradient, o, o->setup(Gradient::MODULATE, Gradient::VERTICAL, Color(*c), Color(*c2), *minw, *minh), children); }), "iiffe", Id_Command);
4832 2 : addcommand("uihgradient", reinterpret_cast<identfun>(+[] (int *c, int *c2, float *minw, float *minh, uint *children) { BUILD(Gradient, o, o->setup(Gradient::SOLID, Gradient::HORIZONTAL, Color(*c), Color(*c2), *minw, *minh), children); }), "iiffe", Id_Command);
4833 2 : addcommand("uimodhgradient", reinterpret_cast<identfun>(+[] (int *c, int *c2, float *minw, float *minh, uint *children) { BUILD(Gradient, o, o->setup(Gradient::MODULATE, Gradient::HORIZONTAL, Color(*c), Color(*c2), *minw, *minh), children); }), "iiffe", Id_Command);
4834 2 : addcommand("uioutline", reinterpret_cast<identfun>(+[] (int *c, float *minw, float *minh, uint *children) { BUILD(Outline, o, o->setup(Color(*c), *minw, *minh), children); }), "iffe", Id_Command);
4835 2 : addcommand("uiline", reinterpret_cast<identfun>(+[] (int *c, float *minw, float *minh, uint *children) { BUILD(Line, o, o->setup(Color(*c), *minw, *minh), children); }), "iffe", Id_Command);
4836 2 : addcommand("uitriangle", reinterpret_cast<identfun>(+[] (int *c, float *minw, float *minh, int *angle, uint *children) { BUILD(Triangle, o, o->setup(Color(*c), *minw, *minh, *angle, Triangle::SOLID), children); }), "iffie", Id_Command);
4837 2 : addcommand("uitriangleoutline", reinterpret_cast<identfun>(+[] (int *c, float *minw, float *minh, int *angle, uint *children) { BUILD(Triangle, o, o->setup(Color(*c), *minw, *minh, *angle, Triangle::OUTLINE), children); }), "iffie", Id_Command);
4838 2 : addcommand("uimodtriangle", reinterpret_cast<identfun>(+[] (int *c, float *minw, float *minh, int *angle, uint *children) { BUILD(Triangle, o, o->setup(Color(*c), *minw, *minh, *angle, Triangle::MODULATE), children); }), "iffie", Id_Command);
4839 2 : addcommand("uicircle", reinterpret_cast<identfun>(+[] (int *c, float *size, uint *children) { BUILD(Circle, o, o->setup(Color(*c), *size, Circle::SOLID), children); }), "ife", Id_Command);
4840 2 : addcommand("uicircleoutline", reinterpret_cast<identfun>(+[] (int *c, float *size, uint *children) { BUILD(Circle, o, o->setup(Color(*c), *size, Circle::OUTLINE), children); }), "ife", Id_Command);
4841 2 : addcommand("uimodcircle", reinterpret_cast<identfun>(+[] (int *c, float *size, uint *children) { BUILD(Circle, o, o->setup(Color(*c), *size, Circle::MODULATE), children); }), "ife", Id_Command);
4842 2 : addcommand("uicolortext", reinterpret_cast<identfun>(+[] (tagval *text, int *c, float *scale, uint *children) { buildtext(*text, *scale, uitextscale, Color(*c), -1, children); }), "tife", Id_Command);
4843 1 : addcommand("uitext", reinterpret_cast<identfun>(+[] (tagval *text, float *scale, uint *children)
4844 : {
4845 1 : buildtext(*text, *scale, uitextscale, Color(255, 255, 255), -1, children);
4846 2 : }), "tfe", Id_Command);
4847 2 : addcommand("uitextfill", reinterpret_cast<identfun>(+[] (float *minw, float *minh, uint *children) { BUILD(Filler, o, o->setup(*minw * uitextscale*0.5f, *minh * uitextscale), children); }), "ffe", Id_Command);
4848 2 : addcommand("uiwrapcolortext", reinterpret_cast<identfun>(+[] (tagval *text, float *wrap, int *c, float *scale, uint *children) { buildtext(*text, *scale, uitextscale, Color(*c), *wrap, children); }), "tfife", Id_Command);
4849 2 : addcommand("uiwraptext", reinterpret_cast<identfun>(+[] (tagval *text, float *wrap, float *scale, uint *children) { buildtext(*text, *scale, uitextscale, Color(255, 255, 255), *wrap, children); }), "tffe", Id_Command);
4850 2 : addcommand("uicolorcontext", reinterpret_cast<identfun>(+[] (tagval *text, int *c, float *scale, uint *children) { buildtext(*text, *scale, FONTH*uicontextscale, Color(*c), -1, children); }), "tife", Id_Command);
4851 2 : addcommand("uicontext", reinterpret_cast<identfun>(+[] (tagval *text, float *scale, uint *children) { buildtext(*text, *scale, FONTH*uicontextscale, Color(255, 255, 255), -1, children); }), "tfe", Id_Command);
4852 2 : addcommand("uicontextfill", reinterpret_cast<identfun>(+[] (float *minw, float *minh, uint *children) { BUILD(Filler, o, o->setup(*minw * FONTH*uicontextscale*0.5f, *minh * FONTH*uicontextscale), children); }), "ffe", Id_Command);
4853 2 : addcommand("uiwrapcolorcontext", reinterpret_cast<identfun>(+[] (tagval *text, float *wrap, int *c, float *scale, uint *children) { buildtext(*text, *scale, FONTH*uicontextscale, Color(*c), *wrap, children); }), "tfife", Id_Command);
4854 2 : addcommand("uiwrapcontext", reinterpret_cast<identfun>(+[] (tagval *text, float *wrap, float *scale, uint *children) { buildtext(*text, *scale, FONTH*uicontextscale, Color(255, 255, 255), *wrap, children); }), "tffe", Id_Command);
4855 2 : addcommand("uitexteditor", reinterpret_cast<identfun>(+[] (char *name, int *length, int *height, float *scale, char *initval, int *mode, uint *children) { BUILD(TextEditor, o, o->setup(name, *length, *height, (*scale <= 0 ? 1 : *scale) * uitextscale, initval, *mode <= 0 ? Editor_Forever : *mode), children); }), "siifsie", Id_Command);
4856 2 : addcommand("uifont", reinterpret_cast<identfun>(+[] (uint *children) { BUILD(Font, o, o->setup(), children); }), "e", Id_Command);
4857 2 : addcommand("uiabovehud", reinterpret_cast<identfun>(+[] () { { if(window) window->abovehud = true; }; }), "", Id_Command);;
4858 2 : addcommand("uiconsole", reinterpret_cast<identfun>(+[] (float *minw, float *minh, uint *children) { BUILD(Console, o, o->setup(*minw, *minh), children); }), "ffe", Id_Command);
4859 2 : addcommand("uifield", reinterpret_cast<identfun>(+[] (ident *var, int *length, uint *onchange, float *scale, uint *children) { BUILD(Field, o, o->setup(var, *length, onchange, (*scale <= 0 ? 1 : *scale) * uitextscale), children); }), "riefe", Id_Command);
4860 2 : addcommand("uikeyfield", reinterpret_cast<identfun>(+[] (ident *var, int *length, uint *onchange, float *scale, uint *children) { BUILD(KeyField, o, o->setup(var, *length, onchange, (*scale <= 0 ? 1 : *scale) * uitextscale), children); }), "riefe", Id_Command);
4861 2 : addcommand("uiimage", reinterpret_cast<identfun>(+[] (char *texname, float *minw, float *minh, uint *children) { BUILD(Image, o, o->setup(textureload(texname, 3, true, false), *minw, *minh), children); }), "sffe", Id_Command);
4862 2 : addcommand("uistretchedimage", reinterpret_cast<identfun>(+[] (char *texname, float *minw, float *minh, uint *children) { BUILD(StretchedImage, o, o->setup(textureload(texname, 3, true, false), *minw, *minh), children); }), "sffe", Id_Command);
4863 2 : addcommand("uicroppedimage", reinterpret_cast<identfun>(+[] (char *texname, float *minw, float *minh, tagval *cropx, tagval *cropy, tagval *cropw, tagval *croph, uint *children) { BUILD(CroppedImage, o, { Texture *tex = textureload(texname, 3, true, false); o->setup(tex, *minw, *minh, parsepixeloffset(cropx, tex->xs), parsepixeloffset(cropy, tex->ys), parsepixeloffset(cropw, tex->xs), parsepixeloffset(croph, tex->ys)); }, children); }), "sfftttte", Id_Command);
4864 2 : addcommand("uiborderedimage", reinterpret_cast<identfun>(+[] (char *texname, tagval *texborder, float *screenborder, uint *children) { BUILD(BorderedImage, o, { Texture *tex = textureload(texname, 3, true, false); o->setup(tex, parsepixeloffset(texborder, tex->xs), *screenborder); }, children); }), "stfe", Id_Command);
4865 2 : addcommand("uitiledimage", reinterpret_cast<identfun>(+[] (char *texname, float *tilew, float *tileh, float *minw, float *minh, uint *children) { BUILD(TiledImage, o, { Texture *tex = textureload(texname, 0, true, false); o->setup(tex, *minw, *minh, *tilew <= 0 ? 1 : *tilew, *tileh <= 0 ? 1 : *tileh); }, children); }), "sffffe", Id_Command);
4866 2 : addcommand("uimodelpreview", reinterpret_cast<identfun>(+[] (char *model, char *animspec, float *minw, float *minh, uint *children) { BUILD(ModelPreview, o, o->setup(model, animspec, *minw, *minh), children); }), "ssffe", Id_Command);
4867 2 : addcommand("uiprefabpreview", reinterpret_cast<identfun>(+[] (char *prefab, int *color, float *minw, float *minh, uint *children) { BUILD(PrefabPreview, o, o->setup(prefab, *color, *minw, *minh), children); }), "siffe", Id_Command);
4868 2 : addcommand("uislotview", reinterpret_cast<identfun>(+[] (int *index, float *minw, float *minh, uint *children) { BUILD(SlotViewer, o, o->setup(*index, *minw, *minh), children); }), "iffe", Id_Command);
4869 2 : addcommand("uivslotview", reinterpret_cast<identfun>(+[] (int *index, float *minw, float *minh, uint *children) { BUILD(VSlotViewer, o, o->setup(*index, *minw, *minh), children); }), "iffe", Id_Command);
4870 :
4871 1 : addcommand("uicontextscale", reinterpret_cast<identfun>(uicontextscalecmd), "", Id_Command);
4872 1 : addcommand("newui", reinterpret_cast<identfun>(newui), "ssss", Id_Command);
4873 1 : addcommand("uiallowinput", reinterpret_cast<identfun>(uiallowinput), "b", Id_Command);
4874 1 : addcommand("uieschide", reinterpret_cast<identfun>(uieschide), "b", Id_Command);
4875 1 : }
4876 :
4877 0 : bool hascursor()
4878 : {
4879 0 : return world->allowinput();
4880 : }
4881 :
4882 0 : void getcursorpos(float &x, float &y)
4883 : {
4884 0 : if(hascursor())
4885 : {
4886 0 : x = cursorx;
4887 0 : y = cursory;
4888 : }
4889 : else
4890 : {
4891 0 : x = y = 0.5f;
4892 : }
4893 0 : }
4894 :
4895 0 : void resetcursor()
4896 : {
4897 0 : cursorx = cursory = 0.5f;
4898 0 : }
4899 :
4900 : static FVARP(uisensitivity, 1e-4f, 1, 1e4f);
4901 :
4902 0 : bool movecursor(int dx, int dy)
4903 : {
4904 0 : if(!hascursor())
4905 : {
4906 0 : return false;
4907 : }
4908 0 : cursorx = std::clamp(cursorx + dx*uisensitivity/hudw(), 0.0f, 1.0f);
4909 0 : cursory = std::clamp(cursory + dy*uisensitivity/hudh(), 0.0f, 1.0f);
4910 0 : return true;
4911 : }
4912 :
4913 0 : bool keypress(int code, bool isdown)
4914 : {
4915 0 : if(world->rawkey(code, isdown))
4916 : {
4917 0 : return true;
4918 : }
4919 0 : int action = 0,
4920 0 : hold = 0;
4921 0 : switch(code)
4922 : {
4923 0 : case Key_Left:
4924 : {
4925 0 : action = isdown ? State_Press : State_Release; hold = State_Hold;
4926 0 : break;
4927 : }
4928 0 : case Key_Middle:
4929 : {
4930 0 : action = isdown ? State_AltPress : State_AltRelease; hold = State_AltHold;
4931 0 : break;
4932 : }
4933 0 : case Key_Right:
4934 : {
4935 0 : action = isdown ? State_EscPress : State_EscRelease; hold = State_EscHold;
4936 0 : break;
4937 : }
4938 0 : case Key_ScrollUp:
4939 : {
4940 0 : action = State_ScrollUp;
4941 0 : break;
4942 : }
4943 0 : case Key_ScrollDown:
4944 : {
4945 0 : action = State_ScrollDown;
4946 0 : break;
4947 : }
4948 : }
4949 0 : if(action)
4950 : {
4951 0 : if(isdown)
4952 : {
4953 0 : if(hold)
4954 : {
4955 0 : world->clearstate(hold);
4956 : }
4957 0 : if(world->setstate(action, cursorx, cursory, 0, true, action|hold))
4958 : {
4959 0 : return true;
4960 : }
4961 : }
4962 0 : else if(hold)
4963 : {
4964 0 : if(world->setstate(action, cursorx, cursory, hold, true, action))
4965 : {
4966 0 : world->clearstate(hold);
4967 0 : return true;
4968 : }
4969 0 : world->clearstate(hold);
4970 : }
4971 0 : return world->allowinput();
4972 : }
4973 0 : return world->key(code, isdown);
4974 : }
4975 :
4976 0 : bool textinput(const char *str, int len)
4977 : {
4978 0 : return world->textinput(str, len);
4979 : }
4980 :
4981 0 : void setup()
4982 : {
4983 0 : world = new World;
4984 0 : }
4985 :
4986 0 : void cleanup()
4987 : {
4988 0 : world->children.clear();
4989 0 : for(auto &[k, i] : windows)
4990 : {
4991 0 : delete i;
4992 : }
4993 0 : windows.clear();
4994 0 : if(world)
4995 : {
4996 0 : delete world;
4997 0 : world = nullptr;
4998 : }
4999 0 : }
5000 :
5001 0 : void calctextscale()
5002 : {
5003 0 : uitextscale = 1.0f/uitextrows;
5004 :
5005 0 : int tw = hudw(),
5006 0 : th = hudh();
5007 0 : if(forceaspect)
5008 : {
5009 0 : tw = static_cast<int>(std::ceil(th*forceaspect));
5010 : }
5011 0 : gettextres(tw, th);
5012 0 : uicontextscale = conscale/th;
5013 0 : }
5014 :
5015 0 : void update()
5016 : {
5017 0 : readyeditors();
5018 :
5019 0 : world->setstate(State_Hover, cursorx, cursory, world->childstate & State_HoldMask);
5020 0 : if(world->childstate & State_Hold)
5021 : {
5022 0 : world->setstate(State_Hold, cursorx, cursory, State_Hold, false);
5023 : }
5024 0 : if(world->childstate & State_AltHold)
5025 : {
5026 0 : world->setstate(State_AltHold, cursorx, cursory, State_AltHold, false);
5027 : }
5028 0 : if(world->childstate & State_EscHold)
5029 : {
5030 0 : world->setstate(State_EscHold, cursorx, cursory, State_EscHold, false);
5031 : }
5032 :
5033 0 : calctextscale();
5034 0 : world->build();
5035 0 : flusheditors();
5036 0 : }
5037 :
5038 0 : void render()
5039 : {
5040 0 : world->layout();
5041 0 : world->adjustchildren();
5042 0 : world->draw();
5043 0 : }
5044 :
5045 0 : float abovehud()
5046 : {
5047 0 : return world->abovehud();
5048 : }
5049 : }
|