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