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(uint 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 : #define BUILDCOLUMNS(type, o, setup, columndata, contents) do { \
1575 : if(buildparent) \
1576 : { \
1577 : type *o = buildparent->buildtype<type>(); \
1578 : setup; \
1579 : o->buildchildren(columndata, contents); \
1580 : } \
1581 : } while(0)
1582 :
1583 : struct Table final : Object
1584 : {
1585 : float spacew, spaceh, subw, subh;
1586 : std::vector<float> widths;
1587 0 : static const char *typestr()
1588 : {
1589 0 : return "#Table";
1590 : }
1591 :
1592 0 : const char *gettype() const override final
1593 : {
1594 0 : return typestr();
1595 : }
1596 :
1597 0 : void setup(float spacew_ = 0, float spaceh_ = 0)
1598 : {
1599 0 : Object::setup();
1600 0 : spacew = spacew_;
1601 0 : spaceh = spaceh_;
1602 0 : }
1603 :
1604 0 : uchar childalign() const override final
1605 : {
1606 0 : return 0;
1607 : }
1608 :
1609 0 : void layout() override final
1610 : {
1611 0 : widths.clear();
1612 :
1613 0 : w = subh = 0;
1614 0 : for(Object *o : children)
1615 : {
1616 0 : o->layout();
1617 0 : int cols = o->childcolumns();
1618 0 : while(static_cast<int>(widths.size()) < cols)
1619 : {
1620 0 : widths.push_back(0);
1621 : }
1622 0 : for(int j = 0; j < cols; ++j)
1623 : {
1624 0 : const Object *c = o->children[j];
1625 0 : if(c->w > widths[j])
1626 : {
1627 0 : widths[j] = c->w;
1628 : }
1629 : }
1630 0 : w = std::max(w, o->w);
1631 0 : subh += o->h;
1632 : }
1633 :
1634 0 : subw = 0;
1635 0 : for(const float &i : widths)
1636 : {
1637 0 : subw += i;
1638 : }
1639 0 : w = std::max(w, subw + spacew*std::max(static_cast<int>(widths.size()) - 1, 0));
1640 0 : h = subh + spaceh*std::max(static_cast<int>(children.size()) - 1, 0);
1641 0 : }
1642 :
1643 0 : void adjustchildren() override final
1644 : {
1645 0 : if(children.empty())
1646 : {
1647 0 : return;
1648 : }
1649 0 : float offsety = 0,
1650 0 : sy = 0,
1651 0 : cspace = (w - subw) / std::max(static_cast<int>(widths.size()) - 1, 1),
1652 0 : cstep = (w - subw) / widths.size(),
1653 0 : rspace = (h - subh) / std::max(static_cast<int>(children.size()) - 1, 1),
1654 0 : rstep = (h - subh) / children.size();
1655 0 : for(Object *o : children)
1656 : {
1657 0 : o->x = 0;
1658 0 : o->y = offsety;
1659 0 : o->w = w;
1660 0 : offsety += o->h + rspace;
1661 0 : float sh = o->h + rstep;
1662 0 : o->adjustlayout(0, sy, w, sh);
1663 0 : sy += sh;
1664 :
1665 0 : float offsetx = 0;
1666 0 : float sx = 0;
1667 0 : int cols = o->childcolumns();
1668 0 : for(int j = 0; j < cols; ++j)
1669 : {
1670 0 : Object *c = o->children[j];
1671 0 : c->x = offsetx;
1672 0 : offsetx += widths[j] + cspace;
1673 0 : float sw = widths[j] + cstep;
1674 0 : c->adjustlayout(sx, 0, sw, o->h);
1675 0 : sx += sw;
1676 : }
1677 : }
1678 : }
1679 : };
1680 :
1681 : struct Spacer final : Object
1682 : {
1683 : float spacew, spaceh;
1684 :
1685 0 : Spacer() : spacew(), spaceh() {}
1686 :
1687 0 : void setup(float spacew_, float spaceh_)
1688 : {
1689 0 : Object::setup();
1690 0 : spacew = spacew_;
1691 0 : spaceh = spaceh_;
1692 0 : }
1693 :
1694 0 : static const char *typestr()
1695 : {
1696 0 : return "#Spacer";
1697 : }
1698 :
1699 0 : const char *gettype() const override final
1700 : {
1701 0 : return typestr();
1702 : }
1703 :
1704 0 : void layout() override final
1705 : {
1706 0 : w = spacew;
1707 0 : h = spaceh;
1708 0 : for(Object *o : children)
1709 : {
1710 0 : o->x = spacew;
1711 0 : o->y = spaceh;
1712 0 : o->layout();
1713 0 : w = std::max(w, o->x + o->w);
1714 0 : h = std::max(h, o->y + o->h);
1715 : }
1716 0 : w += spacew;
1717 0 : h += spaceh;
1718 0 : }
1719 :
1720 0 : void adjustchildren() override final
1721 : {
1722 0 : adjustchildrento(spacew, spaceh, w - 2*spacew, h - 2*spaceh);
1723 0 : }
1724 : };
1725 :
1726 : struct Offsetter final : Object
1727 : {
1728 : float offsetx, offsety;
1729 :
1730 0 : void setup(float offsetx_, float offsety_)
1731 : {
1732 0 : Object::setup();
1733 0 : offsetx = offsetx_;
1734 0 : offsety = offsety_;
1735 0 : }
1736 :
1737 0 : static const char *typestr()
1738 : {
1739 0 : return "#Offsetter";
1740 : }
1741 :
1742 0 : const char *gettype() const override final
1743 : {
1744 0 : return typestr();
1745 : }
1746 :
1747 0 : void layout() override final
1748 : {
1749 0 : Object::layout();
1750 :
1751 0 : for(Object *o : children)
1752 : {
1753 0 : o->x += offsetx;
1754 0 : o->y += offsety;
1755 : }
1756 :
1757 0 : w += offsetx;
1758 0 : h += offsety;
1759 0 : }
1760 :
1761 0 : void adjustchildren() override final
1762 : {
1763 0 : adjustchildrento(offsetx, offsety, w - offsetx, h - offsety);
1764 0 : }
1765 : };
1766 :
1767 : struct Filler : Object
1768 : {
1769 : float minw, minh;
1770 :
1771 0 : void setup(float minw_, float minh_)
1772 : {
1773 0 : Object::setup();
1774 0 : minw = minw_;
1775 0 : minh = minh_;
1776 0 : }
1777 :
1778 0 : static const char *typestr()
1779 : {
1780 0 : return "#Filler";
1781 : }
1782 :
1783 0 : const char *gettype() const override
1784 : {
1785 0 : return typestr();
1786 : }
1787 :
1788 0 : void layout() override final
1789 : {
1790 0 : Object::layout();
1791 0 : w = std::max(w, minw);
1792 0 : h = std::max(h, minh);
1793 0 : }
1794 : };
1795 :
1796 : struct Target : Filler
1797 : {
1798 0 : static const char *typestr()
1799 : {
1800 0 : return "#Target";
1801 : }
1802 :
1803 0 : const char *gettype() const override
1804 : {
1805 0 : return typestr();
1806 : }
1807 0 : bool target(float, float) override final //note unnamed function parameters
1808 : {
1809 0 : return true;
1810 : }
1811 : };
1812 :
1813 : struct Color
1814 : {
1815 : uchar r, g, b, a;
1816 :
1817 0 : Color() {}
1818 :
1819 : //converts an int color to components
1820 4 : Color(uint c) : r((c>>16)&0xFF), g((c>>8)&0xFF), b(c&0xFF), a(c>>24 ? c>>24 : 0xFF) {}
1821 :
1822 : //converts an int color w/o alpha and alpha channel to components
1823 : Color(uint c, uchar a) : r((c>>16)&0xFF), g((c>>8)&0xFF), b(c&0xFF), a(a) {}
1824 :
1825 : //assigns components normally
1826 4 : Color(uchar r, uchar g, uchar b, uchar a = 255) : r(r), g(g), b(b), a(a) {}
1827 :
1828 0 : void init() { gle::colorub(r, g, b, a); }
1829 0 : void attrib() { gle::attribub(r, g, b, a); }
1830 :
1831 0 : static void def() { gle::defcolor(4, GL_UNSIGNED_BYTE); }
1832 : };
1833 :
1834 : #undef LOOP_CHILDREN_REV
1835 : #undef LOOP_CHILD_RANGE
1836 :
1837 : struct FillColor : Target
1838 : {
1839 : enum
1840 : {
1841 : SOLID = 0,
1842 : MODULATE
1843 : };
1844 :
1845 : int type;
1846 : Color color;
1847 :
1848 0 : void setup(int type_, const Color &color_, float minw_ = 0, float minh_ = 0)
1849 : {
1850 0 : Target::setup(minw_, minh_);
1851 0 : type = type_;
1852 0 : color = color_;
1853 0 : }
1854 :
1855 0 : static const char *typestr()
1856 : {
1857 0 : return "#FillColor";
1858 : }
1859 :
1860 0 : const char *gettype() const override
1861 : {
1862 0 : return typestr();
1863 : }
1864 :
1865 0 : void startdraw() const override
1866 : {
1867 0 : hudnotextureshader->set();
1868 0 : gle::defvertex(2);
1869 0 : }
1870 :
1871 0 : void draw(float sx, float sy) override
1872 : {
1873 0 : changedraw(Change_Shader | Change_Color | Change_Blend);
1874 0 : if(type==MODULATE)
1875 : {
1876 0 : modblend();
1877 : }
1878 : else
1879 : {
1880 0 : resetblend();
1881 : }
1882 0 : color.init();
1883 0 : gle::begin(GL_TRIANGLE_STRIP);
1884 0 : gle::attribf(sx+w, sy);
1885 0 : gle::attribf(sx, sy);
1886 0 : gle::attribf(sx+w, sy+h);
1887 0 : gle::attribf(sx, sy+h);
1888 0 : gle::end();
1889 :
1890 0 : Object::draw(sx, sy);
1891 0 : }
1892 : };
1893 :
1894 : class Gradient final : public FillColor
1895 : {
1896 : public:
1897 : enum { VERTICAL, HORIZONTAL };
1898 :
1899 : int dir;
1900 :
1901 0 : void setup(int type_, int dir_, const Color &color_, const Color &color2_, float minw_ = 0, float minh_ = 0)
1902 : {
1903 0 : FillColor::setup(type_, color_, minw_, minh_);
1904 0 : dir = dir_;
1905 0 : color2 = color2_;
1906 0 : }
1907 :
1908 0 : static const char *typestr()
1909 : {
1910 0 : return "#Gradient";
1911 : }
1912 :
1913 : protected:
1914 0 : const char *gettype() const override final
1915 : {
1916 0 : return typestr();
1917 : }
1918 :
1919 0 : void startdraw() const override final
1920 : {
1921 0 : hudnotextureshader->set();
1922 0 : gle::defvertex(2);
1923 0 : Color::def();
1924 0 : }
1925 :
1926 : private:
1927 : Color color2;
1928 :
1929 0 : void draw(float sx, float sy) override final
1930 : {
1931 0 : changedraw(Change_Shader | Change_Color | Change_Blend);
1932 0 : if(type==MODULATE)
1933 : {
1934 0 : modblend();
1935 : }
1936 : else
1937 : {
1938 0 : resetblend();
1939 : }
1940 0 : gle::begin(GL_TRIANGLE_STRIP);
1941 0 : gle::attribf(sx+w, sy); (dir == HORIZONTAL ? color2 : color).attrib();
1942 0 : gle::attribf(sx, sy); color.attrib();
1943 0 : gle::attribf(sx+w, sy+h); color2.attrib();
1944 0 : gle::attribf(sx, sy+h); (dir == HORIZONTAL ? color : color2).attrib();
1945 0 : gle::end();
1946 :
1947 0 : Object::draw(sx, sy);
1948 0 : }
1949 : };
1950 :
1951 : class Line final : public Filler
1952 : {
1953 : public:
1954 0 : void setup(const Color &color_, float minw_ = 0, float minh_ = 0)
1955 : {
1956 0 : Filler::setup(minw_, minh_);
1957 0 : color = color_;
1958 0 : }
1959 :
1960 0 : static const char *typestr()
1961 : {
1962 0 : return "#Line";
1963 : }
1964 :
1965 0 : const char *gettype() const override final
1966 : {
1967 0 : return typestr();
1968 : }
1969 :
1970 : protected:
1971 0 : void startdraw() const override final
1972 : {
1973 0 : hudnotextureshader->set();
1974 0 : gle::defvertex(2);
1975 0 : }
1976 :
1977 0 : void draw(float sx, float sy) override final
1978 : {
1979 0 : changedraw(Change_Shader | Change_Color);
1980 :
1981 0 : color.init();
1982 0 : gle::begin(GL_LINES);
1983 0 : gle::attribf(sx, sy);
1984 0 : gle::attribf(sx+w, sy+h);
1985 0 : gle::end();
1986 :
1987 0 : Object::draw(sx, sy);
1988 0 : }
1989 :
1990 : private:
1991 : Color color;
1992 : };
1993 :
1994 : class Outline final : public Filler
1995 : {
1996 : public:
1997 :
1998 0 : void setup(const Color &color_, float minw_ = 0, float minh_ = 0)
1999 : {
2000 0 : Filler::setup(minw_, minh_);
2001 0 : color = color_;
2002 0 : }
2003 :
2004 0 : const char *gettype() const override final
2005 : {
2006 0 : return typestr();
2007 : }
2008 :
2009 0 : static const char *typestr()
2010 : {
2011 0 : return "#Outline";
2012 : }
2013 :
2014 0 : void draw(float sx, float sy) override final
2015 : {
2016 0 : changedraw(Change_Shader | Change_Color);
2017 :
2018 0 : color.init();
2019 0 : gle::begin(GL_LINE_LOOP);
2020 0 : gle::attribf(sx, sy);
2021 0 : gle::attribf(sx+w, sy);
2022 0 : gle::attribf(sx+w, sy+h);
2023 0 : gle::attribf(sx, sy+h);
2024 0 : gle::end();
2025 :
2026 0 : Object::draw(sx, sy);
2027 0 : }
2028 : protected:
2029 0 : void startdraw() const override final
2030 : {
2031 0 : hudnotextureshader->set();
2032 0 : gle::defvertex(2);
2033 0 : }
2034 : private:
2035 : Color color;
2036 : };
2037 :
2038 0 : static bool checkalphamask(Texture *tex, float x, float y)
2039 : {
2040 0 : if(!tex->alphamask)
2041 : {
2042 0 : tex->loadalphamask();
2043 0 : if(!tex->alphamask)
2044 : {
2045 0 : return true;
2046 : }
2047 : }
2048 0 : int tx = std::clamp(static_cast<int>(x*tex->xs), 0, tex->xs-1),
2049 0 : ty = std::clamp(static_cast<int>(y*tex->ys), 0, tex->ys-1);
2050 0 : if(tex->alphamask[ty*((tex->xs+7)/8) + tx/8] & (1<<(tx%8)))
2051 : {
2052 0 : return true;
2053 : }
2054 0 : return false;
2055 : }
2056 :
2057 : class Image : public Filler
2058 : {
2059 : public:
2060 0 : void setup(Texture *tex_, float minw_ = 0, float minh_ = 0)
2061 : {
2062 0 : Filler::setup(minw_, minh_);
2063 0 : tex = tex_;
2064 0 : }
2065 :
2066 0 : const char *gettype() const override
2067 : {
2068 0 : return typestr();
2069 : }
2070 :
2071 0 : void draw(float sx, float sy) override
2072 : {
2073 0 : if(tex != notexture)
2074 : {
2075 0 : bindtex();
2076 0 : quads(sx, sy, w, h);
2077 : }
2078 :
2079 0 : Object::draw(sx, sy);
2080 0 : }
2081 :
2082 : protected:
2083 : Texture *tex;
2084 :
2085 0 : bool target(float cx, float cy) override
2086 : {
2087 0 : return !(tex->type&Texture::ALPHA) || checkalphamask(tex, cx/w, cy/h);
2088 : }
2089 :
2090 0 : void bindtex()
2091 : {
2092 0 : changedraw();
2093 0 : if(lasttex != tex)
2094 : {
2095 0 : if(lasttex)
2096 : {
2097 0 : gle::end();
2098 : }
2099 0 : lasttex = tex;
2100 0 : glBindTexture(GL_TEXTURE_2D, tex->id);
2101 : }
2102 0 : }
2103 :
2104 0 : void startdraw() const override final
2105 : {
2106 0 : lasttex = nullptr;
2107 :
2108 0 : gle::defvertex(2);
2109 0 : gle::deftexcoord0();
2110 0 : gle::begin(GL_TRIANGLE_STRIP);
2111 0 : }
2112 :
2113 0 : void enddraw() const override final
2114 : {
2115 0 : gle::end();
2116 0 : }
2117 :
2118 : private:
2119 : static Texture *lasttex;
2120 : };
2121 :
2122 : Texture *Image::lasttex = nullptr;
2123 :
2124 : class CroppedImage final : public Image
2125 : {
2126 : public:
2127 0 : void setup(Texture *tex_, float minw_ = 0, float minh_ = 0, float cropx_ = 0, float cropy_ = 0, float cropw_ = 1, float croph_ = 1)
2128 : {
2129 0 : Image::setup(tex_, minw_, minh_);
2130 0 : cropx = cropx_;
2131 0 : cropy = cropy_;
2132 0 : cropw = cropw_;
2133 0 : croph = croph_;
2134 0 : }
2135 :
2136 0 : static const char *typestr()
2137 : {
2138 0 : return "#CroppedImage";
2139 : }
2140 :
2141 : private:
2142 0 : bool target(float cx, float cy) override final
2143 : {
2144 0 : return !(tex->type&Texture::ALPHA) || checkalphamask(tex, cropx + cx/w*cropw, cropy + cy/h*croph);
2145 : }
2146 :
2147 0 : void draw(float sx, float sy) override final
2148 : {
2149 0 : if(tex == notexture)
2150 : {
2151 0 : Object::draw(sx, sy);
2152 0 : return;
2153 : }
2154 :
2155 0 : bindtex();
2156 0 : quads(sx, sy, w, h, cropx, cropy, cropw, croph);
2157 :
2158 0 : Object::draw(sx, sy);
2159 : }
2160 :
2161 0 : const char *gettype() const override final
2162 : {
2163 0 : return typestr();
2164 : }
2165 :
2166 : float cropx, cropy, cropw, croph;
2167 : };
2168 :
2169 : struct StretchedImage final : Image
2170 : {
2171 0 : static const char *typestr()
2172 : {
2173 0 : return "#StretchedImage";
2174 : }
2175 :
2176 0 : const char *gettype() const override final
2177 : {
2178 0 : return typestr();
2179 : }
2180 :
2181 0 : bool target(float cx, float cy) override final
2182 : {
2183 0 : if(!(tex->type&Texture::ALPHA))
2184 : {
2185 0 : return true;
2186 : }
2187 : float mx, my;
2188 0 : if(w <= minw)
2189 : {
2190 0 : mx = cx/w;
2191 : }
2192 0 : else if(cx < minw/2)
2193 : {
2194 0 : mx = cx/minw;
2195 : }
2196 0 : else if(cx >= w - minw/2)
2197 : {
2198 0 : mx = 1 - (w - cx) / minw;
2199 : }
2200 : else
2201 : {
2202 0 : mx = 0.5f;
2203 : }
2204 0 : if(h <= minh)
2205 : {
2206 0 : my = cy/h;
2207 : }
2208 0 : else if(cy < minh/2)
2209 : {
2210 0 : my = cy/minh;
2211 : }
2212 0 : else if(cy >= h - minh/2)
2213 : {
2214 0 : my = 1 - (h - cy) / minh;
2215 : }
2216 : else
2217 : {
2218 0 : my = 0.5f;
2219 : }
2220 :
2221 0 : return checkalphamask(tex, mx, my);
2222 : }
2223 :
2224 0 : void draw(float sx, float sy) override final
2225 : {
2226 0 : if(tex == notexture)
2227 : {
2228 0 : Object::draw(sx, sy);
2229 0 : return;
2230 : }
2231 :
2232 0 : bindtex();
2233 :
2234 0 : float splitw = (minw ? std::min(minw, w) : w) / 2,
2235 0 : splith = (minh ? std::min(minh, h) : h) / 2,
2236 0 : vy = sy,
2237 0 : ty = 0;
2238 0 : for(int i = 0; i < 3; ++i)
2239 : {
2240 0 : float vh = 0,
2241 0 : th = 0;
2242 0 : switch(i)
2243 : {
2244 0 : case 0:
2245 : {
2246 0 : if(splith < h - splith)
2247 : {
2248 0 : vh = splith;
2249 0 : th = 0.5f;
2250 : }
2251 : else
2252 : {
2253 0 : vh = h;
2254 0 : th = 1;
2255 : }
2256 0 : break;
2257 : }
2258 0 : case 1:
2259 : {
2260 0 : vh = h - 2*splith;
2261 0 : th = 0;
2262 0 : break;
2263 : }
2264 0 : case 2:
2265 : {
2266 0 : vh = splith;
2267 0 : th = 0.5f;
2268 0 : break;
2269 : }
2270 : }
2271 0 : float vx = sx,
2272 0 : tx = 0;
2273 0 : for(int j = 0; j < 3; ++j)
2274 : {
2275 0 : float vw = 0,
2276 0 : tw = 0;
2277 0 : switch(j)
2278 : {
2279 0 : case 0:
2280 : {
2281 0 : if(splitw < w - splitw)
2282 : {
2283 0 : vw = splitw;
2284 0 : tw = 0.5f;
2285 : }
2286 : else
2287 : {
2288 0 : vw = w;
2289 0 : tw = 1;
2290 : }
2291 0 : break;
2292 : }
2293 0 : case 1:
2294 : {
2295 0 : vw = w - 2*splitw;
2296 0 : tw = 0;
2297 0 : break;
2298 : }
2299 0 : case 2:
2300 : {
2301 0 : vw = splitw;
2302 0 : tw = 0.5f;
2303 0 : break;
2304 : }
2305 : }
2306 0 : quads(vx, vy, vw, vh, tx, ty, tw, th);
2307 0 : vx += vw;
2308 0 : tx += tw;
2309 0 : if(tx >= 1)
2310 : {
2311 0 : break;
2312 : }
2313 : }
2314 0 : vy += vh;
2315 0 : ty += th;
2316 0 : if(ty >= 1)
2317 : {
2318 0 : break;
2319 : }
2320 : }
2321 :
2322 0 : Object::draw(sx, sy);
2323 : }
2324 : };
2325 :
2326 : struct BorderedImage final : Image
2327 : {
2328 : float texborder, screenborder;
2329 :
2330 0 : void setup(Texture *tex_, float texborder_, float screenborder_)
2331 : {
2332 0 : Image::setup(tex_);
2333 0 : texborder = texborder_;
2334 0 : screenborder = screenborder_;
2335 0 : }
2336 :
2337 0 : static const char *typestr()
2338 : {
2339 0 : return "#BorderedImage";
2340 : }
2341 :
2342 0 : const char *gettype() const override final
2343 : {
2344 0 : return typestr();
2345 : }
2346 :
2347 0 : bool target(float cx, float cy) override final
2348 : {
2349 0 : if(!(tex->type&Texture::ALPHA))
2350 : {
2351 0 : return true;
2352 : }
2353 : float mx, my;
2354 0 : if(cx < screenborder)
2355 : {
2356 0 : mx = cx/screenborder*texborder;
2357 : }
2358 0 : else if(cx >= w - screenborder)
2359 : {
2360 0 : mx = 1-texborder + (cx - (w - screenborder))/screenborder*texborder;
2361 : }
2362 : else
2363 : {
2364 0 : mx = texborder + (cx - screenborder)/(w - 2*screenborder)*(1 - 2*texborder);
2365 : }
2366 0 : if(cy < screenborder)
2367 : {
2368 0 : my = cy/screenborder*texborder;
2369 : }
2370 0 : else if(cy >= h - screenborder)
2371 : {
2372 0 : my = 1-texborder + (cy - (h - screenborder))/screenborder*texborder;
2373 : }
2374 : else
2375 : {
2376 0 : my = texborder + (cy - screenborder)/(h - 2*screenborder)*(1 - 2*texborder);
2377 : }
2378 0 : return checkalphamask(tex, mx, my);
2379 : }
2380 :
2381 0 : void draw(float sx, float sy) override final
2382 : {
2383 0 : if(tex == notexture)
2384 : {
2385 0 : Object::draw(sx, sy);
2386 0 : return;
2387 : }
2388 :
2389 0 : bindtex();
2390 :
2391 0 : float vy = sy,
2392 0 : ty = 0;
2393 0 : for(int i = 0; i < 3; ++i)
2394 : {
2395 0 : float vh = 0,
2396 0 : th = 0;
2397 0 : switch(i)
2398 : {
2399 0 : case 0:
2400 : {
2401 0 : vh = screenborder;
2402 0 : th = texborder;
2403 0 : break;
2404 : }
2405 0 : case 1:
2406 : {
2407 0 : vh = h - 2*screenborder;
2408 0 : th = 1 - 2*texborder;
2409 0 : break;
2410 : }
2411 0 : case 2:
2412 : {
2413 0 : vh = screenborder;
2414 0 : th = texborder;
2415 0 : break;
2416 : }
2417 : }
2418 0 : float vx = sx,
2419 0 : tx = 0;
2420 0 : for(int j = 0; j < 3; ++j)
2421 : {
2422 0 : float vw = 0,
2423 0 : tw = 0;
2424 0 : switch(j)
2425 : {
2426 0 : case 0:
2427 : {
2428 0 : vw = screenborder;
2429 0 : tw = texborder;
2430 0 : break;
2431 : }
2432 0 : case 1:
2433 : {
2434 0 : vw = w - 2*screenborder;
2435 0 : tw = 1 - 2*texborder;
2436 0 : break;
2437 : }
2438 0 : case 2:
2439 : {
2440 0 : vw = screenborder;
2441 0 : tw = texborder;
2442 0 : break;
2443 : }
2444 : }
2445 0 : quads(vx, vy, vw, vh, tx, ty, tw, th);
2446 0 : vx += vw;
2447 0 : tx += tw;
2448 : }
2449 0 : vy += vh;
2450 0 : ty += th;
2451 : }
2452 :
2453 0 : Object::draw(sx, sy);
2454 : }
2455 : };
2456 :
2457 : struct TiledImage final : Image
2458 : {
2459 : float tilew, tileh;
2460 :
2461 0 : void setup(Texture *tex_, float minw_ = 0, float minh_ = 0, float tilew_ = 0, float tileh_ = 0)
2462 : {
2463 0 : Image::setup(tex_, minw_, minh_);
2464 0 : tilew = tilew_;
2465 0 : tileh = tileh_;
2466 0 : }
2467 :
2468 0 : static const char *typestr()
2469 : {
2470 0 : return "#TiledImage";
2471 : }
2472 :
2473 0 : const char *gettype() const override final
2474 : {
2475 0 : return typestr();
2476 : }
2477 :
2478 0 : bool target(float cx, float cy) override final
2479 : {
2480 0 : if(!(tex->type&Texture::ALPHA))
2481 : {
2482 0 : return true;
2483 : }
2484 :
2485 0 : return checkalphamask(tex, std::fmod(cx/tilew, 1), std::fmod(cy/tileh, 1));
2486 : }
2487 :
2488 0 : void draw(float sx, float sy) override final
2489 : {
2490 0 : if(tex == notexture)
2491 : {
2492 0 : Object::draw(sx, sy);
2493 0 : return;
2494 : }
2495 0 : bindtex();
2496 0 : if(tex->clamp)
2497 : {
2498 0 : for(float dy = 0; dy < h; dy += tileh)
2499 : {
2500 0 : float dh = std::min(tileh, h - dy);
2501 0 : for(float dx = 0; dx < w; dx += tilew)
2502 : {
2503 0 : float dw = std::min(tilew, w - dx);
2504 0 : quads(sx + dx, sy + dy, dw, dh, 0, 0, dw / tilew, dh / tileh);
2505 : }
2506 : }
2507 : }
2508 : else
2509 : {
2510 0 : quads(sx, sy, w, h, 0, 0, w/tilew, h/tileh);
2511 : }
2512 0 : Object::draw(sx, sy);
2513 : }
2514 : };
2515 :
2516 : class Shape : public Filler
2517 : {
2518 : public:
2519 : enum
2520 : {
2521 : SOLID = 0,
2522 : OUTLINE,
2523 : MODULATE
2524 : };
2525 :
2526 0 : void setup(const Color &color_, int type_ = SOLID, float minw_ = 0, float minh_ = 0)
2527 : {
2528 0 : Filler::setup(minw_, minh_);
2529 :
2530 0 : color = color_;
2531 0 : type = type_;
2532 0 : }
2533 :
2534 : protected:
2535 : int type;
2536 : Color color;
2537 :
2538 0 : void startdraw() const override final
2539 : {
2540 0 : hudnotextureshader->set();
2541 0 : gle::defvertex(2);
2542 0 : }
2543 :
2544 : };
2545 :
2546 : struct Triangle final : Shape
2547 : {
2548 : vec2 a, b, c;
2549 :
2550 0 : void setup(const Color &color_, float w = 0, float h = 0, int angle = 0, int type_ = SOLID)
2551 : {
2552 0 : a = vec2(0, -h*2.0f/3);
2553 0 : b = vec2(-w/2, h/3);
2554 0 : c = vec2(w/2, h/3);
2555 0 : if(angle)
2556 : {
2557 0 : vec2 rot = sincosmod360(-angle);
2558 0 : a.rotate_around_z(rot);
2559 0 : b.rotate_around_z(rot);
2560 0 : c.rotate_around_z(rot);
2561 : }
2562 0 : vec2 bbmin = vec2(a).min(b).min(c);
2563 0 : a.sub(bbmin);
2564 0 : b.sub(bbmin);
2565 0 : c.sub(bbmin);
2566 0 : vec2 bbmax = vec2(a).max(b).max(c);
2567 :
2568 0 : Shape::setup(color_, type_, bbmax.x, bbmax.y);
2569 0 : }
2570 :
2571 0 : static const char *typestr()
2572 : {
2573 0 : return "#Triangle";
2574 : }
2575 :
2576 0 : const char *gettype() const override final
2577 : {
2578 0 : return typestr();
2579 : }
2580 :
2581 0 : bool target(float cx, float cy) override final
2582 : {
2583 0 : if(type == OUTLINE)
2584 : {
2585 0 : return false;
2586 : }
2587 0 : bool side = vec2(cx, cy).sub(b).cross(vec2(a).sub(b)) < 0;
2588 0 : return (vec2(cx, cy).sub(c).cross(vec2(b).sub(c)) < 0) == side &&
2589 0 : (vec2(cx, cy).sub(a).cross(vec2(c).sub(a)) < 0) == side;
2590 : }
2591 :
2592 0 : void draw(float sx, float sy) override final
2593 : {
2594 0 : Object::draw(sx, sy);
2595 :
2596 0 : changedraw(Change_Shader | Change_Color | Change_Blend);
2597 0 : if(type==MODULATE)
2598 : {
2599 0 : modblend();
2600 : }
2601 : else
2602 : {
2603 0 : resetblend();
2604 : }
2605 0 : color.init();
2606 0 : gle::begin(type == OUTLINE ? GL_LINE_LOOP : GL_TRIANGLES);
2607 0 : gle::attrib(vec2(sx, sy).add(a));
2608 0 : gle::attrib(vec2(sx, sy).add(b));
2609 0 : gle::attrib(vec2(sx, sy).add(c));
2610 0 : gle::end();
2611 0 : }
2612 : };
2613 :
2614 : struct Circle final : Shape
2615 : {
2616 : float radius;
2617 :
2618 0 : void setup(const Color &color_, float size, int type_ = SOLID)
2619 : {
2620 0 : Shape::setup(color_, type_, size, size);
2621 :
2622 0 : radius = size/2;
2623 0 : }
2624 :
2625 0 : static const char *typestr()
2626 : {
2627 0 : return "#Circle";
2628 : }
2629 :
2630 0 : const char *gettype() const override final
2631 : {
2632 0 : return typestr();
2633 : }
2634 :
2635 0 : bool target(float cx, float cy) override final
2636 : {
2637 0 : if(type == OUTLINE)
2638 : {
2639 0 : return false;
2640 : }
2641 0 : float r = radius <= 0 ? std::min(w, h)/2 : radius;
2642 0 : return vec2(cx, cy).sub(r).squaredlen() <= r*r;
2643 : }
2644 :
2645 0 : void draw(float sx, float sy) override final
2646 : {
2647 0 : Object::draw(sx, sy);
2648 :
2649 0 : changedraw(Change_Shader | Change_Color | Change_Blend);
2650 0 : if(type==MODULATE)
2651 : {
2652 0 : modblend();
2653 : }
2654 : else
2655 : {
2656 0 : resetblend();
2657 : }
2658 :
2659 0 : float r = radius <= 0 ? std::min(w, h)/2 : radius;
2660 0 : color.init();
2661 0 : vec2 center(sx + r, sy + r);
2662 0 : if(type == OUTLINE)
2663 : {
2664 0 : gle::begin(GL_LINE_LOOP);
2665 0 : for(int angle = 0; angle < 360; angle += 360/15)
2666 0 : gle::attrib(vec2(sincos360[angle]).mul(r).add(center));
2667 0 : gle::end();
2668 : }
2669 : else
2670 : {
2671 0 : gle::begin(GL_TRIANGLE_FAN);
2672 0 : gle::attrib(center);
2673 0 : gle::attribf(center.x + r, center.y);
2674 0 : for(int angle = 360/15; angle < 360; angle += 360/15)
2675 : {
2676 0 : vec2 p = vec2(sincos360[angle]).mul(r).add(center);
2677 0 : gle::attrib(p);
2678 0 : gle::attrib(p);
2679 : }
2680 0 : gle::attribf(center.x + r, center.y);
2681 0 : gle::end();
2682 : }
2683 0 : }
2684 : };
2685 :
2686 : // default size of text in terms of rows per screenful
2687 : static VARP(uitextrows, 1, 24, 200);
2688 : static FVAR(uitextscale, 1, 0, 0);
2689 :
2690 0 : static void setstring(char*& dst, const char* src)
2691 : {
2692 0 : if(dst)
2693 : {
2694 0 : if(dst != src && std::strcmp(dst, src))
2695 : {
2696 0 : delete[] dst;
2697 0 : dst = newstring(src);
2698 : }
2699 : }
2700 : else
2701 : {
2702 0 : dst = newstring(src);
2703 : }
2704 0 : }
2705 :
2706 : struct Text : Object
2707 : {
2708 : public:
2709 :
2710 0 : void setup(float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1)
2711 : {
2712 0 : Object::setup();
2713 :
2714 0 : scale = scale_;
2715 0 : color = color_;
2716 0 : wrap = wrap_;
2717 0 : }
2718 :
2719 0 : virtual const char *getstr() const
2720 : {
2721 0 : return "";
2722 : }
2723 :
2724 : protected:
2725 0 : const char *gettype() const override
2726 : {
2727 0 : return typestr();
2728 : }
2729 :
2730 0 : void draw(float sx, float sy) override final
2731 : {
2732 0 : Object::draw(sx, sy);
2733 :
2734 0 : changedraw(Change_Shader | Change_Color);
2735 :
2736 0 : float oldscale = textscale;
2737 0 : textscale = drawscale();
2738 0 : ttr.fontsize(36);
2739 0 : const float conscalefactor = 0.000666;
2740 0 : pushhudscale(conscalefactor);
2741 0 : ttr.renderttf(getstr(), {color.r, color.g, color.b, color.a}, sx*1500, sy*1500, scale*33);
2742 0 : pophudmatrix();
2743 : //draw_text(getstr(), sx/textscale, sy/textscale, 0, color.g, color.b, color.a, -1, wrap >= 0 ? static_cast<int>(wrap/textscale) : -1);
2744 :
2745 0 : textscale = oldscale;
2746 0 : }
2747 :
2748 0 : void layout() override final
2749 : {
2750 0 : Object::layout();
2751 :
2752 0 : float k = drawscale(), tw, th;
2753 0 : ttr.ttfbounds(getstr(), tw, th, 42);
2754 0 : w = std::max(w, tw*k);
2755 0 : h = std::max(h, th*k);
2756 0 : }
2757 :
2758 : private:
2759 : float scale, wrap;
2760 : Color color;
2761 :
2762 0 : static const char *typestr()
2763 : {
2764 0 : return "#Text";
2765 : }
2766 :
2767 0 : float drawscale() const
2768 : {
2769 0 : return scale / FONTH;
2770 : }
2771 :
2772 : };
2773 :
2774 : struct TextString final : Text
2775 : {
2776 : std::string str;
2777 :
2778 0 : TextString() : str("")
2779 : {
2780 0 : }
2781 :
2782 0 : ~TextString()
2783 0 : {
2784 0 : }
2785 :
2786 0 : void setup(const char *str_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1)
2787 : {
2788 0 : Text::setup(scale_, color_, wrap_);
2789 :
2790 0 : str = std::string(str_);
2791 0 : }
2792 :
2793 0 : static const char *typestr()
2794 : {
2795 0 : return "#TextString";
2796 : }
2797 :
2798 0 : const char *gettype() const override final
2799 : {
2800 0 : return typestr();
2801 : }
2802 :
2803 0 : const char *getstr() const override final
2804 : {
2805 0 : return str.c_str();
2806 : }
2807 : };
2808 :
2809 : struct TextInt final : Text
2810 : {
2811 : int val;
2812 : std::array<char, 20> str;
2813 :
2814 0 : TextInt() : val(0) { str[0] = '0'; str[1] = '\0'; }
2815 :
2816 0 : void setup(int val_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1)
2817 : {
2818 0 : Text::setup(scale_, color_, wrap_);
2819 :
2820 0 : if(val != val_)
2821 : {
2822 0 : val = val_;
2823 0 : intformat(str.data(), val, str.size());
2824 : }
2825 0 : }
2826 :
2827 0 : static const char *typestr()
2828 : {
2829 0 : return "#TextInt";
2830 : }
2831 :
2832 0 : const char *gettype() const override final
2833 : {
2834 0 : return typestr();
2835 : }
2836 :
2837 0 : const char *getstr() const override final
2838 : {
2839 0 : return str.data();
2840 : }
2841 : };
2842 :
2843 : struct TextFloat final : Text
2844 : {
2845 : float val;
2846 : std::array<char, 20> str;
2847 :
2848 0 : TextFloat() : val(0) { std::memcpy(str.data(), "0.0", 4); }
2849 :
2850 0 : void setup(float val_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1)
2851 : {
2852 0 : Text::setup(scale_, color_, wrap_);
2853 :
2854 0 : if(val != val_)
2855 : {
2856 0 : val = val_;
2857 0 : floatformat(str.data(), val, str.size());
2858 : }
2859 0 : }
2860 :
2861 0 : static const char *typestr()
2862 : {
2863 0 : return "#TextFloat";
2864 : }
2865 :
2866 0 : const char *gettype() const override final
2867 : {
2868 0 : return typestr();
2869 : }
2870 :
2871 0 : const char *getstr() const override final
2872 : {
2873 0 : return str.data();
2874 : }
2875 : };
2876 :
2877 : struct Font final : Object
2878 : {
2879 : ::Font *font;
2880 :
2881 0 : Font() : font(nullptr) {}
2882 :
2883 0 : void setup()
2884 : {
2885 0 : Object::setup();
2886 0 : }
2887 :
2888 0 : void layout() override final
2889 : {
2890 0 : pushfont();
2891 0 : setfont(font);
2892 0 : Object::layout();
2893 0 : popfont();
2894 0 : }
2895 :
2896 0 : void draw(float sx, float sy) override final
2897 : {
2898 0 : pushfont();
2899 0 : setfont(font);
2900 0 : Object::draw(sx, sy);
2901 0 : popfont();
2902 0 : }
2903 :
2904 0 : void buildchildren(const uint *contents)
2905 : {
2906 0 : pushfont();
2907 0 : setfont(font);
2908 0 : Object::buildchildren(contents);
2909 0 : popfont();
2910 0 : }
2911 :
2912 : #define DOSTATE(flags, func) \
2913 : void func##children(float cx, float cy, int mask, bool inside, int setflags) override final \
2914 : { \
2915 : pushfont(); \
2916 : setfont(font); \
2917 : Object::func##children(cx, cy, mask, inside, setflags); \
2918 : popfont(); \
2919 : }
2920 0 : DOSTATES
2921 : #undef DOSTATE
2922 :
2923 0 : bool rawkey(int code, bool isdown) override final
2924 : {
2925 0 : pushfont();
2926 0 : setfont(font);
2927 0 : bool result = Object::rawkey(code, isdown);
2928 0 : popfont();
2929 0 : return result;
2930 : }
2931 :
2932 0 : bool key(int code, bool isdown) override final
2933 : {
2934 0 : pushfont();
2935 0 : setfont(font);
2936 0 : bool result = Object::key(code, isdown);
2937 0 : popfont();
2938 0 : return result;
2939 : }
2940 :
2941 0 : bool textinput(const char *str, int len) override final
2942 : {
2943 0 : pushfont();
2944 0 : setfont(font);
2945 0 : bool result = Object::textinput(str, len);
2946 0 : popfont();
2947 0 : return result;
2948 : }
2949 : };
2950 :
2951 : static float uicontextscale = 0;
2952 :
2953 1 : static void uicontextscalecmd()
2954 : {
2955 1 : floatret(FONTH*uicontextscale);
2956 1 : }
2957 :
2958 : struct Console final : Filler
2959 : {
2960 0 : void setup(float minw_ = 0, float minh_ = 0)
2961 : {
2962 0 : Filler::setup(minw_, minh_);
2963 0 : }
2964 :
2965 0 : static const char *typestr()
2966 : {
2967 0 : return "#Console";
2968 : }
2969 :
2970 0 : const char *gettype() const override final
2971 : {
2972 0 : return typestr();
2973 : }
2974 :
2975 0 : float drawscale() const
2976 : {
2977 0 : return uicontextscale;
2978 : }
2979 :
2980 0 : void draw(float sx, float sy) override final
2981 : {
2982 0 : Object::draw(sx, sy);
2983 :
2984 0 : changedraw(Change_Shader | Change_Color);
2985 :
2986 0 : float k = drawscale();
2987 0 : pushhudtranslate(sx, sy, k);
2988 0 : renderfullconsole(w/k, h/k);
2989 0 : pophudmatrix();
2990 0 : }
2991 : };
2992 :
2993 : class Clipper : public Object
2994 : {
2995 : public:
2996 : float virtw, virth;
2997 :
2998 0 : void setup(float clipw_ = 0, float cliph_ = 0)
2999 : {
3000 0 : Object::setup();
3001 0 : clipw = clipw_;
3002 0 : cliph = cliph_;
3003 0 : virtw = virth = 0;
3004 0 : }
3005 :
3006 0 : static const char *typestr()
3007 : {
3008 0 : return "#Clipper";
3009 : }
3010 :
3011 0 : void layout() override
3012 : {
3013 0 : Object::layout();
3014 :
3015 0 : virtw = w;
3016 0 : virth = h;
3017 0 : if(clipw)
3018 : {
3019 0 : w = std::min(w, clipw);
3020 : }
3021 0 : if(cliph)
3022 : {
3023 0 : h = std::min(h, cliph);
3024 : }
3025 0 : }
3026 :
3027 0 : void draw(float sx, float sy) override
3028 : {
3029 0 : if((clipw && virtw > clipw) || (cliph && virth > cliph))
3030 : {
3031 0 : stopdrawing();
3032 0 : pushclip(sx, sy, w, h);
3033 0 : Object::draw(sx, sy);
3034 0 : stopdrawing();
3035 0 : popclip();
3036 : }
3037 : else
3038 : {
3039 0 : Object::draw(sx, sy);
3040 : }
3041 0 : }
3042 :
3043 : protected:
3044 : float clipw, cliph;
3045 :
3046 0 : const char *gettype() const override
3047 : {
3048 0 : return typestr();
3049 : }
3050 :
3051 0 : void adjustchildren() override final
3052 : {
3053 0 : adjustchildrento(0, 0, virtw, virth);
3054 0 : }
3055 :
3056 : };
3057 :
3058 : struct Scroller final : Clipper
3059 : {
3060 : float offsetx, offsety;
3061 :
3062 0 : Scroller() : offsetx(0), offsety(0) {}
3063 :
3064 0 : void setup(float clipw_ = 0, float cliph_ = 0)
3065 : {
3066 0 : Clipper::setup(clipw_, cliph_);
3067 0 : }
3068 :
3069 0 : static const char *typestr()
3070 : {
3071 0 : return "#Scroller";
3072 : }
3073 :
3074 0 : const char *gettype() const override final
3075 : {
3076 0 : return typestr();
3077 : }
3078 :
3079 0 : void layout() override final
3080 : {
3081 0 : Clipper::layout();
3082 0 : offsetx = std::min(offsetx, hlimit());
3083 0 : offsety = std::min(offsety, vlimit());
3084 0 : }
3085 :
3086 : #define DOSTATE(flags, func) \
3087 : void func##children(float cx, float cy, int mask, bool inside, int setflags) override final \
3088 : { \
3089 : cx += offsetx; \
3090 : cy += offsety; \
3091 : if(cx < virtw && cy < virth) \
3092 : { \
3093 : Clipper::func##children(cx, cy, mask, inside, setflags); \
3094 : } \
3095 : }
3096 0 : DOSTATES
3097 : #undef DOSTATE
3098 :
3099 0 : void draw(float sx, float sy) override final
3100 : {
3101 0 : if((clipw && virtw > clipw) || (cliph && virth > cliph))
3102 : {
3103 0 : stopdrawing();
3104 0 : pushclip(sx, sy, w, h);
3105 0 : Object::draw(sx - offsetx, sy - offsety);
3106 0 : stopdrawing();
3107 0 : popclip();
3108 : }
3109 : else
3110 : {
3111 0 : Object::draw(sx, sy);
3112 : }
3113 0 : }
3114 :
3115 0 : float hlimit() const
3116 : {
3117 0 : return std::max(virtw - w, 0.0f);
3118 : }
3119 :
3120 0 : float vlimit() const
3121 : {
3122 0 : return std::max(virth - h, 0.0f);
3123 : }
3124 :
3125 0 : float hoffset() const
3126 : {
3127 0 : return offsetx / std::max(virtw, w);
3128 : }
3129 :
3130 0 : float voffset() const
3131 : {
3132 0 : return offsety / std::max(virth, h);
3133 : }
3134 :
3135 0 : float hscale() const
3136 : {
3137 0 : return w / std::max(virtw, w);
3138 : }
3139 :
3140 0 : float vscale() const
3141 : {
3142 0 : return h / std::max(virth, h);
3143 : }
3144 :
3145 0 : void addhscroll(float hscroll)
3146 : {
3147 0 : sethscroll(offsetx + hscroll);
3148 0 : }
3149 :
3150 0 : void addvscroll(float vscroll)
3151 : {
3152 0 : setvscroll(offsety + vscroll);
3153 0 : }
3154 :
3155 0 : void sethscroll(float hscroll)
3156 : {
3157 0 : offsetx = std::clamp(hscroll, 0.0f, hlimit());
3158 0 : }
3159 :
3160 0 : void setvscroll(float vscroll)
3161 : {
3162 0 : offsety = std::clamp(vscroll, 0.0f, vlimit());
3163 0 : }
3164 :
3165 : void scrollup(float cx, float cy) override final;
3166 :
3167 : void scrolldown(float cx, float cy) override final;
3168 : };
3169 :
3170 : class ScrollButton final : public Object
3171 : {
3172 : public:
3173 0 : static const char *typestr()
3174 : {
3175 0 : return "#ScrollButton";
3176 : }
3177 :
3178 : protected:
3179 0 : const char *gettype() const override final
3180 : {
3181 0 : return typestr();
3182 : }
3183 : };
3184 :
3185 : class ScrollBar : public Object
3186 : {
3187 :
3188 : public:
3189 0 : ScrollBar() : offsetx(0), offsety(0) {}
3190 :
3191 0 : static const char *typestr()
3192 : {
3193 0 : return "#ScrollBar";
3194 : }
3195 :
3196 0 : void hold(float cx, float cy) override final
3197 : {
3198 0 : ScrollButton *button = static_cast<ScrollButton *>(find(ScrollButton::typestr(), false));
3199 0 : if(button && button->haschildstate(State_Hold))
3200 : {
3201 0 : movebutton(button, offsetx, offsety, cx - button->x, cy - button->y);
3202 : }
3203 0 : }
3204 :
3205 0 : void press(float cx, float cy) override final
3206 : {
3207 0 : ScrollButton *button = static_cast<ScrollButton *>(find(ScrollButton::typestr(), false));
3208 0 : if(button && button->haschildstate(State_Press))
3209 : {
3210 0 : offsetx = cx - button->x;
3211 0 : offsety = cy - button->y;
3212 : }
3213 : else
3214 : {
3215 0 : scrollto(cx, cy, true);
3216 : }
3217 0 : }
3218 :
3219 0 : void arrowscroll(float dir)
3220 : {
3221 0 : addscroll(dir*curtime/1000.0f);
3222 0 : }
3223 : void wheelscroll(float step);
3224 0 : virtual int wheelscrolldirection() const
3225 : {
3226 0 : return 1;
3227 : }
3228 :
3229 : protected:
3230 0 : const char *gettype() const override
3231 : {
3232 0 : return typestr();
3233 : }
3234 :
3235 0 : const char *gettypename() const override final
3236 : {
3237 0 : return typestr();
3238 : }
3239 :
3240 0 : bool target(float, float) override final //note unnamed function parameters
3241 : {
3242 0 : return true;
3243 : }
3244 :
3245 0 : virtual void scrollto(float, float, bool) {} //note unnamed function parameters
3246 : virtual void movebutton(Object *o, float fromx, float fromy, float tox, float toy) = 0;
3247 : virtual void addscroll(Scroller *scroller, float dir) = 0;
3248 :
3249 : private:
3250 : float offsetx, offsety;
3251 :
3252 0 : void addscroll(float dir)
3253 : {
3254 0 : Scroller *scroller = static_cast<Scroller *>(findsibling(Scroller::typestr()));
3255 0 : if(scroller)
3256 : {
3257 0 : addscroll(scroller, dir);
3258 : }
3259 0 : }
3260 : };
3261 :
3262 0 : void Scroller::scrollup(float, float) //note unnamed function parameters
3263 : {
3264 0 : ScrollBar *scrollbar = static_cast<ScrollBar *>(findsibling(ScrollBar::typestr()));
3265 0 : if(scrollbar)
3266 : {
3267 0 : scrollbar->wheelscroll(-scrollbar->wheelscrolldirection());
3268 : }
3269 0 : }
3270 :
3271 0 : void Scroller::scrolldown(float, float) //note unnamed function parameters
3272 : {
3273 0 : ScrollBar *scrollbar = static_cast<ScrollBar *>(findsibling(ScrollBar::typestr()));
3274 0 : if(scrollbar)
3275 : {
3276 0 : scrollbar->wheelscroll(scrollbar->wheelscrolldirection());
3277 : }
3278 0 : }
3279 :
3280 : struct ScrollArrow : Object
3281 : {
3282 : float arrowspeed;
3283 :
3284 0 : void setup(float arrowspeed_ = 0)
3285 : {
3286 0 : Object::setup();
3287 0 : arrowspeed = arrowspeed_;
3288 0 : }
3289 :
3290 0 : static const char *typestr()
3291 : {
3292 0 : return "#ScrollArrow";
3293 : }
3294 :
3295 0 : const char *gettype() const override final
3296 : {
3297 0 : return typestr();
3298 : }
3299 :
3300 0 : void hold(float, float) override final //note unnamed function parameters
3301 : {
3302 0 : ScrollBar *scrollbar = static_cast<ScrollBar *>(findsibling(ScrollBar::typestr()));
3303 0 : if(scrollbar)
3304 : {
3305 0 : scrollbar->arrowscroll(arrowspeed);
3306 : }
3307 0 : }
3308 : };
3309 :
3310 : static VARP(uiscrollsteptime, 0, 50, 1000);
3311 :
3312 0 : void ScrollBar::wheelscroll(float step)
3313 : {
3314 0 : ScrollArrow *arrow = static_cast<ScrollArrow *>(findsibling(ScrollArrow::typestr()));
3315 0 : if(arrow)
3316 : {
3317 0 : addscroll(arrow->arrowspeed*step*uiscrollsteptime/1000.0f);
3318 : }
3319 0 : }
3320 :
3321 : struct HorizontalScrollBar final : ScrollBar
3322 : {
3323 0 : static const char *typestr()
3324 : {
3325 0 : return "#HorizontalScrollBar";
3326 : }
3327 :
3328 0 : const char *gettype() const override final
3329 : {
3330 0 : return typestr();
3331 : }
3332 :
3333 0 : void addscroll(Scroller *scroller, float dir) override final
3334 : {
3335 0 : scroller->addhscroll(dir);
3336 0 : }
3337 :
3338 0 : void scrollto(float cx, float, bool closest = false) override final //note unnamed function parameter
3339 : {
3340 0 : Scroller *scroller = static_cast<Scroller *>(findsibling(Scroller::typestr()));
3341 0 : if(!scroller)
3342 : {
3343 0 : return;
3344 : }
3345 0 : ScrollButton *button = static_cast<ScrollButton *>(find(ScrollButton::typestr(), false));
3346 0 : if(!button)
3347 : {
3348 0 : return;
3349 : }
3350 0 : float bscale = (w - button->w) / (1 - scroller->hscale()),
3351 0 : offset = bscale > 1e-3f ? (closest && cx >= button->x + button->w ? cx - button->w : cx)/bscale : 0;
3352 0 : scroller->sethscroll(offset*scroller->virtw);
3353 : }
3354 :
3355 0 : void adjustchildren() override final
3356 : {
3357 0 : Scroller *scroller = static_cast<Scroller *>(findsibling(Scroller::typestr()));
3358 0 : if(!scroller)
3359 : {
3360 0 : return;
3361 : }
3362 0 : ScrollButton *button = static_cast<ScrollButton *>(find(ScrollButton::typestr(), false));
3363 0 : if(!button)
3364 : {
3365 0 : return;
3366 : }
3367 0 : float bw = w*scroller->hscale();
3368 0 : button->w = std::max(button->w, bw);
3369 0 : float bscale = scroller->hscale() < 1 ? (w - button->w) / (1 - scroller->hscale()) : 1;
3370 0 : button->x = scroller->hoffset()*bscale;
3371 0 : button->adjust &= ~Align_HMask;
3372 :
3373 0 : ScrollBar::adjustchildren();
3374 : }
3375 :
3376 0 : void movebutton(Object *o, float fromx, float, float tox, float toy) override final //note unnamed function parameter
3377 : {
3378 0 : scrollto(o->x + tox - fromx, o->y + toy);
3379 0 : }
3380 : };
3381 :
3382 : struct VerticalScrollBar final : ScrollBar
3383 : {
3384 0 : static const char *typestr()
3385 : {
3386 0 : return "#VerticalScrollBar";
3387 : }
3388 :
3389 0 : const char *gettype() const override final
3390 : {
3391 0 : return typestr();
3392 : }
3393 :
3394 0 : void addscroll(Scroller *scroller, float dir) override final
3395 : {
3396 0 : scroller->addvscroll(dir);
3397 0 : }
3398 :
3399 0 : void scrollto(float, float cy, bool closest = false) override final //note unnamed function parameter
3400 : {
3401 0 : Scroller *scroller = static_cast<Scroller *>(findsibling(Scroller::typestr()));
3402 0 : if(!scroller)
3403 : {
3404 0 : return;
3405 : }
3406 0 : ScrollButton *button = static_cast<ScrollButton *>(find(ScrollButton::typestr(), false));
3407 0 : if(!button)
3408 : {
3409 0 : return;
3410 : }
3411 0 : float bscale = (h - button->h) / (1 - scroller->vscale()),
3412 0 : offset = bscale > 1e-3f ? (closest && cy >= button->y + button->h ? cy - button->h : cy)/bscale : 0;
3413 0 : scroller->setvscroll(offset*scroller->virth);
3414 : }
3415 :
3416 0 : void adjustchildren() override final
3417 : {
3418 0 : Scroller *scroller = static_cast<Scroller *>(findsibling(Scroller::typestr()));
3419 0 : if(!scroller)
3420 : {
3421 0 : return;
3422 : }
3423 0 : ScrollButton *button = static_cast<ScrollButton *>(find(ScrollButton::typestr(), false));
3424 0 : if(!button)
3425 : {
3426 0 : return;
3427 : }
3428 0 : float bh = h*scroller->vscale();
3429 0 : button->h = std::max(button->h, bh);
3430 0 : float bscale = scroller->vscale() < 1 ? (h - button->h) / (1 - scroller->vscale()) : 1;
3431 0 : button->y = scroller->voffset()*bscale;
3432 0 : button->adjust &= ~Align_VMask;
3433 :
3434 0 : ScrollBar::adjustchildren();
3435 : }
3436 :
3437 0 : void movebutton(Object *o, float, float fromy, float tox, float toy) override final //note unnamed function parameter
3438 : {
3439 0 : scrollto(o->x + tox, o->y + toy - fromy);
3440 0 : }
3441 :
3442 0 : int wheelscrolldirection() const override final
3443 : {
3444 0 : return -1;
3445 : }
3446 : };
3447 :
3448 : struct SliderButton final : Object
3449 : {
3450 0 : static const char *typestr()
3451 : {
3452 0 : return "#SliderButton";
3453 : }
3454 :
3455 0 : const char *gettype() const override final
3456 : {
3457 0 : return typestr();
3458 : }
3459 : };
3460 :
3461 0 : static double getfval(ident *id, double val = 0)
3462 : {
3463 0 : switch(id->type)
3464 : {
3465 0 : case Id_Var:
3466 : {
3467 0 : val = *id->val.storage.i;
3468 0 : break;
3469 : }
3470 0 : case Id_FloatVar:
3471 : {
3472 0 : val = *id->val.storage.f;
3473 0 : break;
3474 : }
3475 0 : case Id_StringVar:
3476 : {
3477 0 : val = parsenumber(*id->val.storage.s);
3478 0 : break;
3479 : }
3480 0 : case Id_Alias:
3481 : {
3482 0 : val = id->getnumber();
3483 0 : break;
3484 : }
3485 0 : case Id_Command:
3486 : {
3487 : tagval t;
3488 0 : executeret(id, nullptr, 0, true, t);
3489 0 : val = t.getnumber();
3490 0 : t.cleanup();
3491 0 : break;
3492 : }
3493 : }
3494 0 : return val;
3495 : }
3496 :
3497 0 : static void setfval(ident *id, double val, uint *onchange = nullptr)
3498 : {
3499 0 : switch(id->type)
3500 : {
3501 0 : case Id_Var:
3502 : {
3503 0 : setvarchecked(id, static_cast<int>(std::clamp(val, double(INT_MIN), double(INT_MAX))));
3504 0 : break;
3505 : }
3506 0 : case Id_FloatVar:
3507 : {
3508 0 : setfvarchecked(id, val);
3509 0 : break;
3510 : }
3511 0 : case Id_StringVar:
3512 : {
3513 0 : setsvarchecked(id, numberstr(val));
3514 0 : break;
3515 : }
3516 0 : case Id_Alias:
3517 : {
3518 0 : alias(id->name, numberstr(val));
3519 0 : break;
3520 : }
3521 0 : case Id_Command:
3522 : {
3523 : tagval t;
3524 0 : t.setnumber(val);
3525 0 : execute(id, &t, 1);
3526 0 : break;
3527 : }
3528 : }
3529 0 : if(onchange && (*onchange&Code_OpMask) != Code_Exit)
3530 : {
3531 0 : execute(onchange);
3532 : }
3533 0 : }
3534 :
3535 : struct Slider : Object
3536 : {
3537 0 : Slider() : val(0), vmin(0), vmax(0), vstep(0), id(nullptr), changed(false) {}
3538 :
3539 0 : void setup(ident *id_, double vmin_ = 0, double vmax_ = 0, double vstep_ = 1, uint *onchange = nullptr)
3540 : {
3541 0 : Object::setup();
3542 0 : if(!vmin_ && !vmax_)
3543 : {
3544 0 : switch(id_->type)
3545 : {
3546 0 : case Id_Var:
3547 : {
3548 0 : vmin_ = id_->val.i.min;
3549 0 : vmax_ = id_->val.i.max;
3550 0 : break;
3551 : }
3552 0 : case Id_FloatVar:
3553 : {
3554 0 : vmin_ = id_->val.f.min;
3555 0 : vmax_ = id_->val.f.max;
3556 0 : break;
3557 : }
3558 : }
3559 : }
3560 0 : if(id != id_)
3561 : {
3562 0 : changed = false;
3563 : }
3564 0 : id = id_;
3565 0 : vmin = vmin_;
3566 0 : vmax = vmax_;
3567 0 : vstep = vstep_ > 0 ? vstep_ : 1;
3568 0 : if(changed)
3569 : {
3570 0 : setfval(id, val, onchange);
3571 0 : changed = false;
3572 : }
3573 : else
3574 : {
3575 0 : val = getfval(id, vmin);
3576 : }
3577 0 : }
3578 :
3579 0 : static const char *typestr()
3580 : {
3581 0 : return "#Slider";
3582 : }
3583 :
3584 0 : const char *gettype() const override
3585 : {
3586 0 : return typestr();
3587 : }
3588 :
3589 0 : const char *gettypename() const override final
3590 : {
3591 0 : return typestr();
3592 : }
3593 :
3594 0 : bool target(float, float) override final //note unnamed function parameters
3595 : {
3596 0 : return true;
3597 : }
3598 :
3599 0 : void arrowscroll(double dir)
3600 : {
3601 0 : double newval = val + dir*vstep;
3602 0 : newval += vstep * (newval < 0 ? -0.5 : 0.5);
3603 0 : newval -= std::fmod(newval, vstep);
3604 0 : newval = std::clamp(newval, std::min(vmin, vmax), std::max(vmin, vmax));
3605 0 : if(val != newval)
3606 : {
3607 0 : changeval(newval);
3608 : }
3609 0 : }
3610 :
3611 : void wheelscroll(float step);
3612 0 : virtual int wheelscrolldirection() const
3613 : {
3614 0 : return 1;
3615 : }
3616 :
3617 0 : void scrollup(float, float) override final //note unnamed function parameters
3618 : {
3619 0 : wheelscroll(-wheelscrolldirection());
3620 0 : }
3621 :
3622 0 : void scrolldown(float, float) override final //note unnamed function parameters
3623 : {
3624 0 : wheelscroll(wheelscrolldirection());
3625 0 : }
3626 :
3627 0 : virtual void scrollto(float, float) {} //note unnamed function parameters
3628 :
3629 0 : void hold(float cx, float cy) override final
3630 : {
3631 0 : scrollto(cx, cy);
3632 0 : }
3633 :
3634 0 : void changeval(double newval)
3635 : {
3636 0 : val = newval;
3637 0 : changed = true;
3638 0 : }
3639 :
3640 : protected:
3641 : double val, vmin, vmax, vstep;
3642 :
3643 : private:
3644 : ident *id;
3645 : bool changed;
3646 : };
3647 :
3648 : static VARP(uislidersteptime, 0, 50, 1000);
3649 :
3650 : struct SliderArrow final : Object
3651 : {
3652 : double stepdir;
3653 : int laststep;
3654 :
3655 0 : SliderArrow() : laststep(0) {}
3656 :
3657 0 : void setup(double dir_ = 0)
3658 : {
3659 0 : Object::setup();
3660 0 : stepdir = dir_;
3661 0 : }
3662 :
3663 0 : static const char *typestr()
3664 : {
3665 0 : return "#SliderArrow";
3666 : }
3667 :
3668 0 : const char *gettype() const override final
3669 : {
3670 0 : return typestr();
3671 : }
3672 :
3673 0 : void press(float, float) override final //note unnamed function parameters
3674 : {
3675 0 : laststep = totalmillis + 2*uislidersteptime;
3676 :
3677 0 : Slider *slider = static_cast<Slider *>(findsibling(Slider::typestr()));
3678 0 : if(slider)
3679 : {
3680 0 : slider->arrowscroll(stepdir);
3681 : }
3682 0 : }
3683 :
3684 0 : void hold(float, float) override final //note unnamed function parameters
3685 : {
3686 0 : if(totalmillis < laststep + uislidersteptime)
3687 : {
3688 0 : return;
3689 : }
3690 0 : laststep = totalmillis;
3691 :
3692 0 : Slider *slider = static_cast<Slider *>(findsibling(Slider::typestr()));
3693 0 : if(slider)
3694 : {
3695 0 : slider->arrowscroll(stepdir);
3696 : }
3697 : }
3698 : };
3699 :
3700 0 : void Slider::wheelscroll(float step)
3701 : {
3702 0 : SliderArrow *arrow = static_cast<SliderArrow *>(findsibling(SliderArrow::typestr()));
3703 0 : if(arrow)
3704 : {
3705 0 : step *= arrow->stepdir;
3706 : }
3707 0 : arrowscroll(step);
3708 0 : }
3709 :
3710 : struct HorizontalSlider final : Slider
3711 : {
3712 0 : static const char *typestr()
3713 : {
3714 0 : return "#HorizontalSlider";
3715 : }
3716 :
3717 0 : const char *gettype() const override final
3718 : {
3719 0 : return typestr();
3720 : }
3721 :
3722 0 : void scrollto(float cx, float) override final //note unnamed function parameter
3723 : {
3724 0 : SliderButton *button = static_cast<SliderButton *>(find(SliderButton::typestr(), false));
3725 0 : if(!button)
3726 : {
3727 0 : return;
3728 : }
3729 0 : float offset = w > button->w ? std::clamp((cx - button->w/2)/(w - button->w), 0.0f, 1.0f) : 0.0f;
3730 0 : int step = static_cast<int>((val - vmin) / vstep),
3731 0 : bstep = static_cast<int>(offset * (vmax - vmin) / vstep);
3732 0 : if(step != bstep)
3733 : {
3734 0 : changeval(bstep * vstep + vmin);
3735 : }
3736 : }
3737 :
3738 0 : void adjustchildren() override final
3739 : {
3740 0 : SliderButton *button = static_cast<SliderButton *>(find(SliderButton::typestr(), false));
3741 0 : if(!button)
3742 : {
3743 0 : return;
3744 : }
3745 0 : int step = static_cast<int>((val - vmin) / vstep),
3746 0 : bstep = static_cast<int>(button->x / (w - button->w) * (vmax - vmin) / vstep);
3747 0 : if(step != bstep)
3748 : {
3749 0 : button->x = (w - button->w) * step * vstep / (vmax - vmin);
3750 : }
3751 0 : button->adjust &= ~Align_HMask;
3752 :
3753 0 : Slider::adjustchildren();
3754 : }
3755 : };
3756 :
3757 : struct VerticalSlider final : Slider
3758 : {
3759 0 : static const char *typestr()
3760 : {
3761 0 : return "#VerticalSlider";
3762 : }
3763 :
3764 0 : const char *gettype() const override final
3765 : {
3766 0 : return typestr();
3767 : }
3768 :
3769 0 : void scrollto(float, float cy) override final //note unnamed function parameter
3770 : {
3771 0 : SliderButton *button = static_cast<SliderButton *>(find(SliderButton::typestr(), false));
3772 0 : if(!button)
3773 : {
3774 0 : return;
3775 : }
3776 0 : float offset = h > button->h ? std::clamp((cy - button->h/2)/(h - button->h), 0.0f, 1.0f) : 0.0f;
3777 0 : int step = static_cast<int>((val - vmin) / vstep),
3778 0 : bstep = static_cast<int>(offset * (vmax - vmin) / vstep);
3779 0 : if(step != bstep)
3780 : {
3781 0 : changeval(bstep * vstep + vmin);
3782 : }
3783 : }
3784 :
3785 0 : void adjustchildren() override final
3786 : {
3787 0 : SliderButton *button = static_cast<SliderButton *>(find(SliderButton::typestr(), false));
3788 0 : if(!button)
3789 : {
3790 0 : return;
3791 : }
3792 0 : int step = static_cast<int>((val - vmin) / vstep),
3793 0 : bstep = static_cast<int>(button->y / (h - button->h) * (vmax - vmin) / vstep);
3794 0 : if(step != bstep)
3795 : {
3796 0 : button->y = (h - button->h) * step * vstep / (vmax - vmin);
3797 : }
3798 0 : button->adjust &= ~Align_VMask;
3799 :
3800 0 : Slider::adjustchildren();
3801 : }
3802 :
3803 0 : int wheelscrolldirection() const override final
3804 : {
3805 0 : return -1;
3806 : }
3807 : };
3808 :
3809 : struct TextEditor : Object
3810 : {
3811 : static TextEditor *focus;
3812 :
3813 0 : TextEditor() : edit(nullptr), keyfilter("") {}
3814 :
3815 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)
3816 : {
3817 0 : Object::setup();
3818 0 : Editor *edit_ = useeditor(name, mode, false, initval);
3819 0 : if(edit_ != edit)
3820 : {
3821 0 : if(edit)
3822 : {
3823 0 : clearfocus();
3824 : }
3825 0 : edit = edit_;
3826 : }
3827 0 : else if(isfocus() && !hasstate(State_Hover))
3828 : {
3829 0 : commit();
3830 : }
3831 0 : if(initval && edit->mode == Editor_Focused && !isfocus())
3832 : {
3833 0 : edit->init(initval);
3834 : }
3835 0 : edit->active = true;
3836 0 : edit->linewrap = length < 0;
3837 0 : edit->maxx = edit->linewrap ? -1 : length;
3838 0 : edit->maxy = height <= 0 ? 1 : -1;
3839 0 : edit->pixelwidth = std::abs(length)*fontwidth();
3840 0 : if(edit->linewrap && edit->maxy == 1)
3841 : {
3842 0 : edit->updateheight();
3843 : }
3844 : else
3845 : {
3846 0 : edit->pixelheight = FONTH*std::max(height, 1);
3847 : }
3848 0 : scale = scale_;
3849 0 : if(keyfilter_)
3850 : {
3851 0 : keyfilter = std::string(keyfilter_);
3852 : }
3853 : else
3854 : {
3855 0 : keyfilter = std::string();
3856 : }
3857 0 : }
3858 0 : ~TextEditor()
3859 0 : {
3860 0 : clearfocus();
3861 0 : keyfilter = std::string();
3862 0 : }
3863 :
3864 0 : static void setfocus(TextEditor *e)
3865 : {
3866 0 : if(focus == e)
3867 : {
3868 0 : return;
3869 : }
3870 0 : focus = e;
3871 0 : bool allowtextinput = focus!=nullptr && focus->allowtextinput();
3872 0 : ::textinput(allowtextinput, TextInput_GUI);
3873 0 : ::keyrepeat(allowtextinput, KeyRepeat_GUI);
3874 : }
3875 0 : void setfocus()
3876 : {
3877 0 : setfocus(this);
3878 0 : }
3879 :
3880 0 : void clearfocus()
3881 : {
3882 0 : if(focus == this)
3883 : {
3884 0 : setfocus(nullptr);
3885 : }
3886 0 : }
3887 :
3888 0 : bool isfocus() const
3889 : {
3890 0 : return focus == this;
3891 : }
3892 :
3893 0 : static const char *typestr()
3894 : {
3895 0 : return "#TextEditor";
3896 : }
3897 :
3898 0 : const char *gettype() const override
3899 : {
3900 0 : return typestr();
3901 : }
3902 :
3903 0 : bool target(float, float) override final//note unnamed function parameters
3904 : {
3905 0 : return true;
3906 : }
3907 :
3908 0 : float drawscale() const
3909 : {
3910 0 : return scale / FONTH;
3911 : }
3912 :
3913 0 : void draw(float sx, float sy) override final
3914 : {
3915 0 : changedraw(Change_Shader | Change_Color);
3916 :
3917 0 : edit->rendered = true;
3918 :
3919 0 : float k = drawscale();
3920 0 : pushhudtranslate(sx, sy, k);
3921 :
3922 0 : edit->draw(fontwidth()/2, 0, 0xFFFFFF);
3923 :
3924 0 : pophudmatrix();
3925 :
3926 0 : Object::draw(sx, sy);
3927 0 : }
3928 :
3929 0 : void layout() override final
3930 : {
3931 0 : Object::layout();
3932 :
3933 0 : float k = drawscale();
3934 0 : w = std::max(w, (edit->pixelwidth + fontwidth())*k);
3935 0 : h = std::max(h, edit->pixelheight*k);
3936 0 : }
3937 :
3938 0 : virtual void resetmark(float cx, float cy)
3939 : {
3940 0 : edit->mark(false);
3941 0 : offsetx = cx;
3942 0 : offsety = cy;
3943 0 : }
3944 :
3945 0 : void press(float cx, float cy) override final
3946 : {
3947 0 : setfocus();
3948 0 : resetmark(cx, cy);
3949 0 : }
3950 :
3951 0 : void hold(float cx, float cy) override final
3952 : {
3953 0 : if(isfocus())
3954 : {
3955 0 : float k = drawscale();
3956 0 : bool dragged = std::max(std::fabs(cx - offsetx), std::fabs(cy - offsety)) > (FONTH/8.0f)*k;
3957 0 : edit->hit(static_cast<int>(std::floor(cx/k - fontwidth()/2)), static_cast<int>(std::floor(cy/k)), dragged);
3958 : }
3959 0 : }
3960 :
3961 0 : void scrollup(float, float) override final //note unnamed function parameters
3962 : {
3963 0 : edit->scrollup();
3964 0 : }
3965 :
3966 0 : void scrolldown(float, float) override final //note unnamed function parameters
3967 : {
3968 0 : edit->scrolldown();
3969 0 : }
3970 :
3971 0 : virtual void cancel()
3972 : {
3973 0 : clearfocus();
3974 0 : }
3975 :
3976 0 : virtual void commit()
3977 : {
3978 0 : clearfocus();
3979 0 : }
3980 :
3981 0 : bool key(int code, bool isdown) override final
3982 : {
3983 0 : if(Object::key(code, isdown))
3984 : {
3985 0 : return true;
3986 : }
3987 0 : if(!isfocus())
3988 : {
3989 0 : return false;
3990 : }
3991 0 : switch(code)
3992 : {
3993 0 : case SDLK_ESCAPE:
3994 : {
3995 0 : if(isdown)
3996 : {
3997 0 : cancel();
3998 : }
3999 0 : return true;
4000 : }
4001 0 : case SDLK_RETURN:
4002 : case SDLK_TAB:
4003 : {
4004 0 : if(edit->maxy != 1)
4005 : {
4006 0 : break;
4007 : }
4008 : [[fallthrough]];
4009 : }
4010 : case SDLK_KP_ENTER:
4011 : {
4012 0 : if(isdown)
4013 : {
4014 0 : commit();
4015 : }
4016 0 : return true;
4017 : }
4018 : }
4019 0 : if(isdown)
4020 : {
4021 0 : edit->key(code);
4022 : }
4023 0 : return true;
4024 : }
4025 :
4026 0 : virtual bool allowtextinput() const
4027 : {
4028 0 : return true;
4029 : }
4030 :
4031 0 : bool textinput(const char *str, int len) override final
4032 : {
4033 0 : if(Object::textinput(str, len))
4034 : {
4035 0 : return true;
4036 : }
4037 0 : if(!isfocus() || !allowtextinput())
4038 : {
4039 0 : return false;
4040 : }
4041 0 : if(keyfilter.empty())
4042 : {
4043 0 : edit->input(str, len);
4044 : }
4045 : else
4046 : {
4047 0 : while(len > 0)
4048 : {
4049 0 : int accept = std::min(len, static_cast<int>(std::strspn(str, keyfilter.c_str())));
4050 0 : if(accept > 0)
4051 : {
4052 0 : edit->input(str, accept);
4053 : }
4054 0 : str += accept + 1;
4055 0 : len -= accept + 1;
4056 0 : if(len <= 0)
4057 : {
4058 0 : break;
4059 : }
4060 0 : int reject = static_cast<int>(std::strcspn(str, keyfilter.c_str()));
4061 0 : str += reject;
4062 0 : str -= reject;
4063 : }
4064 : }
4065 0 : return true;
4066 : }
4067 : protected:
4068 : Editor *edit;
4069 : private:
4070 : std::string keyfilter;
4071 : float scale, offsetx, offsety;
4072 :
4073 : };
4074 :
4075 : TextEditor *TextEditor::focus = nullptr;
4076 :
4077 0 : static const char *getsval(ident *id, bool &shouldfree, const char *val = "")
4078 : {
4079 0 : switch(id->type)
4080 : {
4081 0 : case Id_Var:
4082 : {
4083 0 : val = intstr(*id->val.storage.i);
4084 0 : break;
4085 : }
4086 0 : case Id_FloatVar:
4087 : {
4088 0 : val = floatstr(*id->val.storage.f);
4089 0 : break;
4090 : }
4091 0 : case Id_StringVar:
4092 : {
4093 0 : val = *id->val.storage.s;
4094 0 : break;
4095 : }
4096 0 : case Id_Alias:
4097 : {
4098 0 : val = id->getstr();
4099 0 : break;
4100 : }
4101 0 : case Id_Command:
4102 : {
4103 0 : val = executestr(id, nullptr, 0, true);
4104 0 : shouldfree = true;
4105 0 : break;
4106 : }
4107 : }
4108 0 : return val;
4109 : }
4110 :
4111 0 : static void setsval(ident *id, const char *val, uint *onchange = nullptr)
4112 : {
4113 0 : switch(id->type)
4114 : {
4115 0 : case Id_Var:
4116 : {
4117 0 : setvarchecked(id, parseint(val));
4118 0 : break;
4119 : }
4120 0 : case Id_FloatVar:
4121 : {
4122 0 : setfvarchecked(id, parsefloat(val));
4123 0 : break;
4124 : }
4125 0 : case Id_StringVar:
4126 : {
4127 0 : setsvarchecked(id, val);
4128 0 : break;
4129 : }
4130 0 : case Id_Alias:
4131 : {
4132 0 : alias(id->name, val);
4133 0 : break;
4134 : }
4135 0 : case Id_Command:
4136 : {
4137 : tagval t;
4138 0 : t.setstr(newstring(val));
4139 0 : execute(id, &t, 1);
4140 0 : break;
4141 : }
4142 : }
4143 0 : if(onchange && (*onchange&Code_OpMask) != Code_Exit)
4144 : {
4145 0 : execute(onchange);
4146 : }
4147 0 : }
4148 :
4149 : struct Field : TextEditor
4150 : {
4151 : ident *id;
4152 : bool changed;
4153 :
4154 0 : Field() : id(nullptr), changed(false) {}
4155 :
4156 0 : void setup(ident *id_, int length, uint *onchange, float scale = 1, const char *keyfilter_ = nullptr)
4157 : {
4158 0 : if(isfocus() && !hasstate(State_Hover))
4159 : {
4160 0 : commit();
4161 : }
4162 0 : if(changed)
4163 : {
4164 0 : if(id == id_)
4165 : {
4166 0 : setsval(id, edit->lines[0].text, onchange);
4167 : }
4168 0 : changed = false;
4169 : }
4170 0 : bool shouldfree = false;
4171 0 : const char *initval = id != id_ || !isfocus() ? getsval(id_, shouldfree) : nullptr;
4172 0 : TextEditor::setup(id_->name, length, 0, scale, initval, Editor_Focused, keyfilter_);
4173 0 : if(shouldfree)
4174 : {
4175 0 : delete[] initval;
4176 : }
4177 0 : id = id_;
4178 0 : }
4179 :
4180 0 : static const char *typestr()
4181 : {
4182 0 : return "#Field";
4183 : }
4184 :
4185 0 : const char *gettype() const override
4186 : {
4187 0 : return typestr();
4188 : }
4189 :
4190 0 : void commit() override final
4191 : {
4192 0 : TextEditor::commit();
4193 0 : changed = true;
4194 0 : }
4195 :
4196 0 : void cancel() override final
4197 : {
4198 0 : TextEditor::cancel();
4199 0 : changed = false;
4200 0 : }
4201 : };
4202 :
4203 : struct KeyField final : Field
4204 : {
4205 0 : static const char *typestr()
4206 : {
4207 0 : return "#KeyField";
4208 : }
4209 :
4210 0 : const char *gettype() const override final
4211 : {
4212 0 : return typestr();
4213 : }
4214 :
4215 0 : void resetmark(float cx, float cy) override final
4216 : {
4217 0 : edit->clear();
4218 0 : Field::resetmark(cx, cy);
4219 0 : }
4220 :
4221 0 : void insertkey(int code)
4222 : {
4223 0 : const char *keyname = getkeyname(code);
4224 0 : if(keyname)
4225 : {
4226 0 : if(!edit->empty())
4227 : {
4228 0 : edit->insert(" ");
4229 : }
4230 0 : edit->insert(keyname);
4231 : }
4232 0 : }
4233 :
4234 0 : bool rawkey(int code, bool isdown) override final
4235 : {
4236 0 : if(Object::rawkey(code, isdown))
4237 : {
4238 0 : return true;
4239 : }
4240 0 : if(!isfocus() || !isdown)
4241 : {
4242 0 : return false;
4243 : }
4244 0 : if(code == SDLK_ESCAPE)
4245 : {
4246 0 : commit();
4247 : }
4248 : else
4249 : {
4250 0 : insertkey(code);
4251 : }
4252 0 : return true;
4253 : }
4254 :
4255 0 : bool allowtextinput() const override final
4256 : {
4257 0 : return false;
4258 : }
4259 : };
4260 :
4261 : struct Preview : Target
4262 : {
4263 0 : void startdraw() const override final
4264 : {
4265 0 : glDisable(GL_BLEND);
4266 :
4267 0 : if(clipstack.size())
4268 : {
4269 0 : glDisable(GL_SCISSOR_TEST);
4270 : }
4271 0 : }
4272 :
4273 0 : void enddraw() const override final
4274 : {
4275 0 : glEnable(GL_BLEND);
4276 :
4277 0 : if(clipstack.size())
4278 : {
4279 0 : glEnable(GL_SCISSOR_TEST);
4280 : }
4281 0 : }
4282 : };
4283 :
4284 : struct ModelPreview final : Preview
4285 : {
4286 : std::string name;
4287 : int anim;
4288 :
4289 0 : ModelPreview() : name("") {}
4290 0 : ~ModelPreview() {}
4291 :
4292 0 : void setup(const char *name_, const char *animspec, float minw_, float minh_)
4293 : {
4294 0 : Preview::setup(minw_, minh_);
4295 0 : name = std::string(name_);
4296 :
4297 0 : anim = Anim_All;
4298 0 : if(animspec[0])
4299 : {
4300 0 : if(isdigit(animspec[0]))
4301 : {
4302 0 : anim = parseint(animspec);
4303 0 : if(anim >= 0)
4304 : {
4305 0 : anim %= Anim_Index;
4306 : }
4307 : else
4308 : {
4309 0 : anim = Anim_All;
4310 : }
4311 : }
4312 : else
4313 : {
4314 0 : std::vector<size_t> anims = findanims(animspec);
4315 0 : if(anims.size())
4316 : {
4317 0 : anim = anims[0];
4318 : }
4319 0 : }
4320 : }
4321 0 : anim |= Anim_Loop;
4322 0 : }
4323 :
4324 0 : static const char *typestr()
4325 : {
4326 0 : return "#ModelPreview";
4327 : }
4328 :
4329 0 : const char *gettype() const override final
4330 : {
4331 0 : return typestr();
4332 : }
4333 :
4334 0 : void draw(float sx, float sy) override final
4335 : {
4336 0 : Object::draw(sx, sy);
4337 :
4338 0 : changedraw(Change_Shader);
4339 :
4340 : int sx1, sy1, sx2, sy2;
4341 0 : window->calcscissor(sx, sy, sx+w, sy+h, sx1, sy1, sx2, sy2, false);
4342 0 : modelpreview.start(sx1, sy1, sx2-sx1, sy2-sy1, false, clipstack.size() > 0);
4343 0 : model *m = loadmodel(name);
4344 0 : if(m)
4345 : {
4346 0 : vec center, radius;
4347 0 : m->boundbox(center, radius);
4348 : float yaw;
4349 0 : vec o = calcmodelpreviewpos(radius, yaw).sub(center);
4350 0 : rendermodel(name, anim, o, yaw, 0, 0, 0, nullptr, nullptr, 0);
4351 : }
4352 0 : if(clipstack.size())
4353 : {
4354 0 : clipstack.back().scissor();
4355 : }
4356 0 : modelpreview.end();
4357 0 : }
4358 : };
4359 :
4360 : class PrefabPreview final : public Preview
4361 : {
4362 : public:
4363 0 : PrefabPreview() : name(nullptr)
4364 : {
4365 0 : }
4366 :
4367 0 : ~PrefabPreview()
4368 0 : {
4369 0 : delete[] name;
4370 0 : }
4371 :
4372 0 : void setup(const char *name_, int color_, float minw_, float minh_)
4373 : {
4374 0 : Preview::setup(minw_, minh_);
4375 0 : setstring(name, name_);
4376 0 : color = vec::hexcolor(color_);
4377 0 : }
4378 :
4379 0 : static const char *typestr()
4380 : {
4381 0 : return "#PrefabPreview";
4382 : }
4383 :
4384 0 : const char *gettype() const override final
4385 : {
4386 0 : return typestr();
4387 : }
4388 :
4389 0 : void draw(float sx, float sy) override final
4390 : {
4391 0 : Object::draw(sx, sy);
4392 0 : changedraw(Change_Shader);
4393 : int sx1, sy1, sx2, sy2;
4394 0 : window->calcscissor(sx, sy, sx+w, sy+h, sx1, sy1, sx2, sy2, false);
4395 0 : modelpreview.start(sx1, sy1, sx2-sx1, sy2-sy1, false, clipstack.size() > 0);
4396 0 : previewprefab(name, color);
4397 0 : if(clipstack.size())
4398 : {
4399 0 : clipstack.back().scissor();
4400 : }
4401 0 : modelpreview.end();
4402 0 : }
4403 : private:
4404 : char *name;
4405 : vec color;
4406 : };
4407 :
4408 : static VARP(uislotviewtime, 0, 25, 1000);
4409 : static int lastthumbnail = 0;
4410 :
4411 : class SlotViewer : public Target
4412 : {
4413 : public:
4414 0 : void setup(int index_, float minw_ = 0, float minh_ = 0)
4415 : {
4416 0 : Target::setup(minw_, minh_);
4417 0 : index = index_;
4418 0 : }
4419 :
4420 0 : static const char *typestr()
4421 : {
4422 0 : return "#SlotViewer";
4423 : }
4424 :
4425 0 : const char *gettype() const override
4426 : {
4427 0 : return typestr();
4428 : }
4429 :
4430 0 : void draw(float sx, float sy) override
4431 : {
4432 0 : Slot &slot = lookupslot(index, false);
4433 0 : previewslot(slot, *slot.variants, sx, sy);
4434 :
4435 0 : Object::draw(sx, sy);
4436 0 : }
4437 :
4438 : protected:
4439 : int index;
4440 :
4441 0 : void previewslot(Slot &slot, VSlot &vslot, float x, float y)
4442 : {
4443 0 : if(slot.sts.empty())
4444 : {
4445 0 : return;
4446 : }
4447 0 : Texture *t = nullptr,
4448 0 : *glowtex = nullptr;
4449 0 : if(slot.loaded)
4450 : {
4451 0 : t = slot.sts[0].t;
4452 0 : if(t == notexture)
4453 : {
4454 0 : return;
4455 : }
4456 0 : Slot &slot = *vslot.slot;
4457 0 : if(slot.texmask&(1 << Tex_Glow))
4458 : {
4459 0 : for(uint j = 0; j < slot.sts.size(); j++)
4460 : {
4461 0 : if(slot.sts[j].type == Tex_Glow)
4462 : {
4463 0 : glowtex = slot.sts[j].t;
4464 0 : break;
4465 : }
4466 : }
4467 : }
4468 : }
4469 : else
4470 : {
4471 0 : if(!slot.thumbnail)
4472 : {
4473 0 : if(totalmillis - lastthumbnail < uislotviewtime)
4474 : {
4475 0 : return;
4476 : }
4477 0 : slot.loadthumbnail();
4478 0 : lastthumbnail = totalmillis;
4479 : }
4480 0 : if(slot.thumbnail != notexture)
4481 : {
4482 0 : t = slot.thumbnail;
4483 : }
4484 : else
4485 : {
4486 0 : return;
4487 : }
4488 : }
4489 :
4490 0 : changedraw(Change_Shader | Change_Color);
4491 :
4492 0 : SETSHADER(hudrgb);
4493 0 : std::array<vec2,4> tc = { vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1) };
4494 0 : int xoff = vslot.offset.x(),
4495 0 : yoff = vslot.offset.y();
4496 0 : if(vslot.rotation)
4497 : {
4498 0 : const TexRotation &r = texrotations[vslot.rotation];
4499 0 : if(r.swapxy)
4500 : {
4501 0 : std::swap(xoff, yoff);
4502 0 : for(int k = 0; k < 4; ++k)
4503 : {
4504 0 : std::swap(tc[k].x, tc[k].y);
4505 : }
4506 : }
4507 0 : if(r.flipx)
4508 : {
4509 0 : xoff *= -1;
4510 0 : for(int k = 0; k < 4; ++k)
4511 : {
4512 0 : tc[k].x *= -1;
4513 : }
4514 : }
4515 0 : if(r.flipy)
4516 : {
4517 0 : yoff *= -1;
4518 0 : for(int k = 0; k < 4; ++k)
4519 : {
4520 0 : tc[k].y *= -1;
4521 : }
4522 : }
4523 : }
4524 0 : float xt = std::min(1.0f, t->xs/static_cast<float>(t->ys)),
4525 0 : yt = std::min(1.0f, t->ys/static_cast<float>(t->xs));
4526 0 : for(int k = 0; k < 4; ++k)
4527 : {
4528 0 : tc[k].x = tc[k].x/xt - static_cast<float>(xoff)/t->xs;
4529 0 : tc[k].y = tc[k].y/yt - static_cast<float>(yoff)/t->ys;
4530 : }
4531 0 : glBindTexture(GL_TEXTURE_2D, t->id);
4532 0 : if(slot.loaded)
4533 : {
4534 0 : gle::color(vslot.colorscale);
4535 : }
4536 : else
4537 : {
4538 0 : gle::colorf(1, 1, 1);
4539 : }
4540 0 : quad(x, y, w, h, tc);
4541 0 : if(glowtex)
4542 : {
4543 0 : glBlendFunc(GL_SRC_ALPHA, GL_ONE);
4544 0 : glBindTexture(GL_TEXTURE_2D, glowtex->id);
4545 0 : gle::color(vslot.glowcolor);
4546 0 : quad(x, y, w, h, tc);
4547 0 : glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
4548 : }
4549 : }
4550 : };
4551 :
4552 : class VSlotViewer final : public SlotViewer
4553 : {
4554 : public:
4555 0 : static const char *typestr()
4556 : {
4557 0 : return "#VSlotViewer";
4558 : }
4559 :
4560 0 : const char *gettype() const final
4561 : {
4562 0 : return typestr();
4563 : }
4564 :
4565 0 : void draw(float sx, float sy) final
4566 : {
4567 0 : VSlot &vslot = lookupvslot(index, false);
4568 0 : previewslot(*vslot.slot, vslot, sx, sy);
4569 :
4570 0 : Object::draw(sx, sy);
4571 0 : }
4572 : };
4573 :
4574 : //new ui command
4575 1 : static void newui(const char *name, const char *contents, const char *onshow, const char *onhide)
4576 : {
4577 1 : std::unordered_map<std::string, Window *>::const_iterator itr = windows.find(name);
4578 1 : if(itr != windows.end())
4579 : {
4580 0 : if((*itr).second == UI::window)
4581 : {
4582 0 : return;
4583 : }
4584 0 : world->hide((*itr).second);
4585 0 : windows.erase(itr);
4586 0 : delete window;
4587 : }
4588 3 : windows[name] = new Window(name, contents, onshow, onhide);
4589 : }
4590 :
4591 : //command
4592 1 : static void uiallowinput(const int *val)
4593 : {
4594 1 : if(window)
4595 : {
4596 0 : if(*val >= 0)
4597 : {
4598 0 : window->allowinput = *val!=0;
4599 : }
4600 0 : intret(window->allowinput ? 1 : 0);
4601 : }
4602 1 : }
4603 :
4604 : //command
4605 1 : void uieschide (const int *val)
4606 : {
4607 1 : if(window)
4608 : {
4609 0 : if(*val >= 0)
4610 : {
4611 0 : window->eschide = *val!=0;
4612 0 : intret(window->eschide ? 1 : 0);
4613 : }
4614 : }
4615 1 : }
4616 :
4617 : //used in iengine
4618 3 : bool showui(const char *name)
4619 : {
4620 3 : if(!world)
4621 : {
4622 3 : return false;
4623 : }
4624 0 : std::unordered_map<std::string, Window *>::const_iterator itr = windows.find(name);
4625 0 : return (itr != windows.end()) && world->show((*itr).second);
4626 : }
4627 :
4628 : //used in iengine
4629 2 : bool hideui(const char *name)
4630 : {
4631 2 : if(!world)
4632 : {
4633 2 : return false;
4634 : }
4635 0 : if(!name)
4636 : {
4637 0 : return world->hideall() > 0;
4638 : }
4639 0 : std::unordered_map<std::string, Window *>::const_iterator itr = windows.find(name);
4640 0 : return (itr != windows.end()) && world->hide((*itr).second);
4641 : }
4642 :
4643 : //used in console
4644 2 : bool toggleui(const char *name)
4645 : {
4646 2 : if(showui(name))
4647 : {
4648 0 : return true;
4649 : }
4650 2 : hideui(name);
4651 2 : return false;
4652 : }
4653 :
4654 : //used in iengine
4655 1 : void holdui(const char *name, bool on)
4656 : {
4657 1 : if(!world)
4658 : {
4659 1 : return;
4660 : }
4661 0 : if(on)
4662 : {
4663 0 : showui(name);
4664 : }
4665 : else
4666 : {
4667 0 : hideui(name);
4668 : }
4669 : }
4670 :
4671 : //used in iengine
4672 3 : bool uivisible(const char *name)
4673 : {
4674 3 : if(!name)
4675 : {
4676 0 : return world->children.size() > 0;
4677 : }
4678 3 : std::unordered_map<std::string, Window *>::const_iterator itr = windows.find(name);
4679 3 : if(itr != windows.end() && (*itr).second)
4680 : {
4681 0 : return std::find(world->children.begin(), world->children.end(), (*itr).second) != world->children.end();
4682 : }
4683 3 : return false;
4684 : }
4685 :
4686 26 : static void ifstateval(bool state, tagval * t, tagval * f)
4687 : {
4688 26 : if(state)
4689 : {
4690 0 : if(t->type == Value_Null)
4691 : {
4692 0 : intret(1);
4693 : }
4694 : else
4695 : {
4696 0 : result(*t);
4697 : }
4698 : }
4699 26 : else if(f->type == Value_Null)
4700 : {
4701 26 : intret(0);
4702 : }
4703 : else
4704 : {
4705 0 : result(*f);
4706 : }
4707 26 : }
4708 :
4709 0 : static float parsepixeloffset(const tagval *t, int size)
4710 : {
4711 0 : switch(t->type)
4712 : {
4713 0 : case Value_Integer:
4714 : {
4715 0 : return t->i;
4716 : }
4717 0 : case Value_Float:
4718 : {
4719 0 : return t->f;
4720 : }
4721 0 : case Value_Null:
4722 : {
4723 0 : return 0;
4724 : }
4725 0 : default:
4726 : {
4727 0 : const char *s = t->getstr();
4728 : char *end;
4729 0 : float val = std::strtod(s, &end);
4730 0 : return *end == 'p' ? val/size : val;
4731 : }
4732 : }
4733 : }
4734 :
4735 8 : static void buildtext(tagval &t, float scale, float scalemod, const Color &color, float wrap, const uint *children)
4736 : {
4737 8 : if(scale <= 0)
4738 : {
4739 8 : scale = 1;
4740 : }
4741 8 : scale *= scalemod;
4742 8 : switch(t.type)
4743 : {
4744 0 : case Value_Integer:
4745 : {
4746 0 : BUILD(TextInt, o, o->setup(t.i, scale, color, wrap), children);
4747 0 : break;
4748 : }
4749 0 : case Value_Float:
4750 : {
4751 0 : BUILD(TextFloat, o, o->setup(t.f, scale, color, wrap), children);
4752 0 : break;
4753 : }
4754 0 : case Value_CString:
4755 : case Value_Macro:
4756 : case Value_String:
4757 : {
4758 0 : if(t.s[0])
4759 : {
4760 0 : BUILD(TextString, o, o->setup(t.s, scale, color, wrap), children);
4761 0 : break;
4762 : }
4763 : [[fallthrough]];
4764 : }
4765 : default:
4766 : {
4767 8 : BUILD(Object, o, o->setup(), children);
4768 8 : break;
4769 : }
4770 : }
4771 8 : }
4772 :
4773 1 : void inituicmds()
4774 : {
4775 :
4776 1 : static auto showuicmd = [] (const char * name)
4777 : {
4778 1 : if(!world)
4779 : {
4780 1 : intret(-1);
4781 1 : return;
4782 : }
4783 0 : intret(showui(name) ? 1 : 0);
4784 : };
4785 :
4786 1 : static auto hideuicmd = [] (const char * name)
4787 : {
4788 1 : if(!world)
4789 : {
4790 1 : intret(-1);
4791 1 : return;
4792 : }
4793 0 : intret(hideui(name) ? 1 : 0);
4794 : };
4795 :
4796 1 : static auto hidetopuicmd = [] ()
4797 : {
4798 1 : if(!world)
4799 : {
4800 1 : intret(-1);
4801 1 : return;
4802 : }
4803 0 : intret(world->hidetop() ? 1 : 0);
4804 : };
4805 :
4806 1 : static auto hidealluicmd = [] ()
4807 : {
4808 1 : if(!world)
4809 : {
4810 1 : intret(-1);
4811 1 : return;
4812 : }
4813 0 : intret(world->hideall());
4814 : };
4815 :
4816 1 : static auto toggleuicmd = [] (const char * name)
4817 : {
4818 :
4819 1 : intret(toggleui(name) ? 1 : 0);
4820 1 : };
4821 :
4822 1 : static auto holduicmd = [] (const char * name, const int * down)
4823 : {
4824 1 : holdui(name, *down!=0);
4825 1 : };
4826 :
4827 1 : static auto uivisiblecmd = [] (const char * name)
4828 : {
4829 1 : intret(uivisible(name) ? 1 : 0);
4830 1 : };
4831 :
4832 1 : addcommand("showui", reinterpret_cast<identfun>(+showuicmd), "s", Id_Command);
4833 1 : addcommand("hideui", reinterpret_cast<identfun>(+hideuicmd), "s", Id_Command);
4834 1 : addcommand("hidetopui", reinterpret_cast<identfun>(+hidetopuicmd),"", Id_Command);
4835 1 : addcommand("hideallui", reinterpret_cast<identfun>(+hidealluicmd),"", Id_Command);
4836 1 : addcommand("toggleui", reinterpret_cast<identfun>(+toggleuicmd), "s", Id_Command);
4837 1 : addcommand("holdui", reinterpret_cast<identfun>(+holduicmd), "sD", Id_Command);
4838 1 : addcommand("uivisible", reinterpret_cast<identfun>(+uivisiblecmd),"s", Id_Command);
4839 :
4840 1 : static auto uinamecmd = [] ()
4841 : {
4842 1 : if(window)
4843 : {
4844 0 : result(window->name.c_str());
4845 : }
4846 1 : };
4847 1 : addcommand("uiname", reinterpret_cast<identfun>(+uinamecmd), "", Id_Command);
4848 :
4849 : #define DOSTATE(flags, func) \
4850 : addcommand("ui" #func, reinterpret_cast<identfun>(+[] (const uint *t, const uint *f) \
4851 : { \
4852 : executeret(buildparent && buildparent->haschildstate(flags) ? t : f);\
4853 : }), "ee", Id_Command); \
4854 : addcommand("ui" #func "?", reinterpret_cast<identfun>(+[] (tagval *t, tagval *f) \
4855 : { \
4856 : ifstateval(buildparent && buildparent->haschildstate(flags), t, f);\
4857 : }), "tt", Id_Command); \
4858 : addcommand("ui" #func "+", reinterpret_cast<identfun>(+[] (const uint *t, const uint *f) \
4859 : { \
4860 : executeret(buildparent && static_cast<int>(buildparent->children.size()) > buildchild && buildparent->children[buildchild]->haschildstate(flags) ? t : f); \
4861 : }), "ee", Id_Command); \
4862 : addcommand("ui" #func "+?", reinterpret_cast<identfun>(+[] (tagval *t, tagval *f) \
4863 : { \
4864 : ifstateval(buildparent && static_cast<int>(buildparent->children.size()) > buildchild && buildparent->children[buildchild]->haschildstate(flags), t, f); \
4865 : }), "tt", Id_Command);
4866 49 : DOSTATES
4867 : #undef DOSTATE
4868 :
4869 1 : addcommand("uifocus", reinterpret_cast<identfun>(+[] (const uint *t, const uint *f)
4870 : {
4871 1 : executeret(buildparent && TextEditor::focus == buildparent ? t : f);
4872 1 : }), "ee", Id_Command);
4873 1 : addcommand("uifocus?", reinterpret_cast<identfun>(+[] (tagval *t, tagval *f)
4874 : {
4875 1 : ifstateval(buildparent && TextEditor::focus == buildparent, t, f);
4876 1 : }), "tt", Id_Command);
4877 1 : addcommand("uifocus+", reinterpret_cast<identfun>(+[] (const uint *t, const uint *f)
4878 : {
4879 1 : executeret(buildparent && static_cast<int>(buildparent->children.size()) > buildchild && TextEditor::focus == buildparent->children[buildchild] ? t : f);
4880 1 : }), "ee", Id_Command);
4881 1 : addcommand("uifocus+?", reinterpret_cast<identfun>(+[] (tagval *t, tagval *f)
4882 : {
4883 1 : ifstateval(buildparent && static_cast<int>(buildparent->children.size()) > buildchild && TextEditor::focus == buildparent->children[buildchild], t, f);
4884 1 : }), "tt", Id_Command);
4885 1 : addcommand("uialign", reinterpret_cast<identfun>(+[] (const int *xalign, const int *yalign)
4886 : {
4887 1 : if(buildparent)
4888 : {
4889 0 : buildparent->setalign(*xalign, *yalign);
4890 : }
4891 1 : }), "ii", Id_Command);
4892 1 : addcommand("uialign-", reinterpret_cast<identfun>(+[] (const int *xalign, const int *yalign)
4893 : {
4894 1 : if(buildparent && buildchild > 0)
4895 : {
4896 0 : buildparent->children[buildchild-1]->setalign(*xalign, *yalign);
4897 : }
4898 1 : }), "ii", Id_Command);
4899 1 : addcommand("uialign*", reinterpret_cast<identfun>(+[] (const int *xalign, const int *yalign)
4900 : {
4901 1 : if(buildparent)
4902 : {
4903 0 : for(int i = 0; i < buildchild; ++i)
4904 : {
4905 0 : buildparent->children[i]->setalign(*xalign, *yalign);
4906 : }
4907 : }
4908 1 : }), "ii", Id_Command);
4909 1 : addcommand("uiclamp", reinterpret_cast<identfun>(+[] (const int *left, const int *right, const int *top, const int *bottom)
4910 : {
4911 1 : if(buildparent)
4912 : {
4913 0 : buildparent->setclamp(*left, *right, *top, *bottom);
4914 : }
4915 1 : }), "iiii", Id_Command);
4916 1 : addcommand("uiclamp-", reinterpret_cast<identfun>(+[] (const int *left, const int *right, const int *top, const int *bottom)
4917 : {
4918 1 : if(buildparent && buildchild > 0)
4919 : {
4920 0 : buildparent->children[buildchild-1]->setclamp(*left, *right, *top, *bottom);
4921 : }
4922 1 : }), "iiii", Id_Command);
4923 1 : addcommand("uiclamp*", reinterpret_cast<identfun>(+[] (const int *left, const int *right, const int *top, const int *bottom)
4924 : {
4925 1 : if(buildparent)
4926 : {
4927 0 : for(int i = 0; i < buildchild; ++i)
4928 : {
4929 0 : buildparent->children[i]->setclamp(*left, *right, *top, *bottom);
4930 : }
4931 : }
4932 1 : }), "iiii", Id_Command);
4933 1 : addcommand("uigroup", reinterpret_cast<identfun>(+[] (const uint *children)
4934 : {
4935 1 : BUILD(Object, o, o->setup(), children);
4936 1 : }), "e", Id_Command);
4937 1 : addcommand("uihlist", reinterpret_cast<identfun>(+[] (const float *space, const uint *children)
4938 : {
4939 1 : BUILD(HorizontalList, o, o->setup(*space), children);
4940 1 : }), "fe", Id_Command);
4941 1 : addcommand("uivlist", reinterpret_cast<identfun>(+[] (const float *space, const uint *children)
4942 : {
4943 1 : BUILD(VerticalList, o, o->setup(*space), children);
4944 1 : }), "fe", Id_Command);
4945 1 : addcommand("uilist", reinterpret_cast<identfun>(+[] (const float *space, const uint *children)
4946 : {
4947 1 : for(Object *parent = buildparent; parent && !parent->istype<VerticalList>(); parent = parent->parent)
4948 : {
4949 0 : if(parent->istype<HorizontalList>())
4950 : {
4951 0 : BUILD(VerticalList, o, o->setup(*space), children); return;
4952 : }
4953 : }
4954 1 : BUILD(HorizontalList, o, o->setup(*space), children);
4955 1 : }), "fe", Id_Command);
4956 1 : addcommand("uigrid", reinterpret_cast<identfun>(+[] (const int *columns, const float *spacew, const float *spaceh, const uint *children)
4957 : {
4958 1 : BUILD(Grid, o, o->setup(*columns, *spacew, *spaceh), children);
4959 1 : }), "iffe", Id_Command);
4960 1 : addcommand("uitableheader", reinterpret_cast<identfun>(+[] (const uint *columndata, const uint *children)
4961 : {
4962 1 : BUILDCOLUMNS(TableHeader, o, o->setup(), columndata, children);
4963 1 : }), "ee", Id_Command);
4964 1 : addcommand("uitablerow", reinterpret_cast<identfun>(+[] (const uint *columndata, const uint *children)
4965 : {
4966 1 : BUILDCOLUMNS(TableRow, o, o->setup(), columndata, children);
4967 1 : }), "ee", Id_Command);
4968 1 : addcommand("uitable", reinterpret_cast<identfun>(+[] (const float *spacew, const float *spaceh, const uint *children)
4969 : {
4970 1 : BUILD(Table, o, o->setup(*spacew, *spaceh), children);
4971 1 : }), "ffe", Id_Command);
4972 1 : addcommand("uispace", reinterpret_cast<identfun>(+[] (const float *spacew, const float *spaceh, const uint *children)
4973 : {
4974 1 : BUILD(Spacer, o, o->setup(*spacew, *spaceh), children);
4975 1 : }), "ffe", Id_Command);
4976 1 : addcommand("uioffset", reinterpret_cast<identfun>(+[] (const float *offsetx, const float *offsety, const uint *children)
4977 : {
4978 1 : BUILD(Offsetter, o, o->setup(*offsetx, *offsety), children);
4979 1 : }), "ffe", Id_Command);
4980 1 : addcommand("uifill", reinterpret_cast<identfun>(+[] (const float *minw, const float *minh, const uint *children)
4981 : {
4982 1 : BUILD(Filler, o, o->setup(*minw, *minh), children);
4983 1 : }), "ffe", Id_Command);
4984 1 : addcommand("uitarget", reinterpret_cast<identfun>(+[] (const float *minw, const float *minh, const uint *children)
4985 : {
4986 1 : BUILD(Target, o, o->setup(*minw, *minh), children);
4987 1 : }), "ffe", Id_Command);
4988 1 : addcommand("uiclip", reinterpret_cast<identfun>(+[] (const float *clipw, const float *cliph, const uint *children)
4989 : {
4990 1 : BUILD(Clipper, o, o->setup(*clipw, *cliph), children);
4991 1 : }), "ffe", Id_Command);
4992 2 : addcommand("uiscroll", reinterpret_cast<identfun>(+[] (float *clipw, float *cliph, uint *children) { BUILD(Scroller, o, o->setup(*clipw, *cliph), children); }), "ffe", Id_Command);
4993 2 : addcommand("uihscrolloffset", reinterpret_cast<identfun>(+[] () { if(buildparent && buildparent->istype<Scroller>()) { const Scroller *scroller = static_cast<const Scroller *>(buildparent); floatret(scroller->offsetx);} }), "", Id_Command);
4994 2 : addcommand("uivscrolloffset", reinterpret_cast<identfun>(+[] () { if(buildparent && buildparent->istype<Scroller>()) { const Scroller *scroller = static_cast<const Scroller *>(buildparent); floatret(scroller->offsety);} }), "", Id_Command);
4995 2 : addcommand("uihscrollbar", reinterpret_cast<identfun>(+[] (uint *children) { BUILD(HorizontalScrollBar, o, o->setup(), children); }), "e", Id_Command);
4996 2 : addcommand("uivscrollbar", reinterpret_cast<identfun>(+[] (uint *children) { BUILD(VerticalScrollBar, o, o->setup(), children); }), "e", Id_Command);
4997 2 : addcommand("uiscrollarrow", reinterpret_cast<identfun>(+[] (float *dir, uint *children) { BUILD(ScrollArrow, o, o->setup(*dir), children); }), "fe", Id_Command);
4998 2 : addcommand("uiscrollbutton", reinterpret_cast<identfun>(+[] (uint *children) { BUILD(ScrollButton, o, o->setup(), children); }), "e", Id_Command);
4999 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);
5000 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);
5001 2 : addcommand("uisliderarrow", reinterpret_cast<identfun>(+[] (float *dir, uint *children) { BUILD(SliderArrow, o, o->setup(*dir), children); }), "fe", Id_Command);
5002 2 : addcommand("uisliderbutton", reinterpret_cast<identfun>(+[] (uint *children) { BUILD(SliderButton, o, o->setup(), children); }), "e", Id_Command);
5003 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);
5004 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);
5005 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);
5006 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);
5007 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);
5008 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);
5009 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);
5010 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);
5011 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);
5012 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);
5013 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);
5014 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);
5015 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);
5016 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);
5017 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);
5018 1 : addcommand("uitext", reinterpret_cast<identfun>(+[] (tagval *text, const float *scale, const uint *children)
5019 : {
5020 1 : buildtext(*text, *scale, uitextscale, Color(255, 255, 255), -1, children);
5021 2 : }), "tfe", Id_Command);
5022 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);
5023 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);
5024 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);
5025 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);
5026 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);
5027 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);
5028 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);
5029 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);
5030 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);
5031 2 : addcommand("uifont", reinterpret_cast<identfun>(+[] (uint *children) { BUILD(Font, o, o->setup(), children); }), "e", Id_Command);
5032 2 : addcommand("uiabovehud", reinterpret_cast<identfun>(+[] () { if(window) window->abovehud = true; }), "", Id_Command);
5033 2 : addcommand("uiconsole", reinterpret_cast<identfun>(+[] (float *minw, float *minh, uint *children) { BUILD(Console, o, o->setup(*minw, *minh), children); }), "ffe", Id_Command);
5034 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);
5035 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);
5036 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);
5037 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);
5038 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);
5039 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);
5040 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);
5041 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);
5042 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);
5043 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);
5044 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);
5045 :
5046 1 : addcommand("uicontextscale", reinterpret_cast<identfun>(uicontextscalecmd), "", Id_Command);
5047 1 : addcommand("newui", reinterpret_cast<identfun>(newui), "ssss", Id_Command);
5048 1 : addcommand("uiallowinput", reinterpret_cast<identfun>(uiallowinput), "b", Id_Command);
5049 1 : addcommand("uieschide", reinterpret_cast<identfun>(uieschide), "b", Id_Command);
5050 1 : }
5051 :
5052 0 : bool hascursor()
5053 : {
5054 0 : return world->allowinput();
5055 : }
5056 :
5057 0 : void getcursorpos(float &x, float &y)
5058 : {
5059 0 : if(hascursor())
5060 : {
5061 0 : x = cursorx;
5062 0 : y = cursory;
5063 : }
5064 : else
5065 : {
5066 0 : x = y = 0.5f;
5067 : }
5068 0 : }
5069 :
5070 0 : void resetcursor()
5071 : {
5072 0 : cursorx = cursory = 0.5f;
5073 0 : }
5074 :
5075 : static FVARP(uisensitivity, 1e-4f, 1, 1e4f);
5076 :
5077 0 : bool movecursor(int dx, int dy)
5078 : {
5079 0 : if(!hascursor())
5080 : {
5081 0 : return false;
5082 : }
5083 0 : cursorx = std::clamp(cursorx + dx*uisensitivity/hudw(), 0.0f, 1.0f);
5084 0 : cursory = std::clamp(cursory + dy*uisensitivity/hudh(), 0.0f, 1.0f);
5085 0 : return true;
5086 : }
5087 :
5088 0 : bool keypress(int code, bool isdown)
5089 : {
5090 0 : if(world->rawkey(code, isdown))
5091 : {
5092 0 : return true;
5093 : }
5094 0 : int action = 0,
5095 0 : hold = 0;
5096 0 : switch(code)
5097 : {
5098 0 : case Key_Left:
5099 : {
5100 0 : action = isdown ? State_Press : State_Release; hold = State_Hold;
5101 0 : break;
5102 : }
5103 0 : case Key_Middle:
5104 : {
5105 0 : action = isdown ? State_AltPress : State_AltRelease; hold = State_AltHold;
5106 0 : break;
5107 : }
5108 0 : case Key_Right:
5109 : {
5110 0 : action = isdown ? State_EscPress : State_EscRelease; hold = State_EscHold;
5111 0 : break;
5112 : }
5113 0 : case Key_ScrollUp:
5114 : {
5115 0 : action = State_ScrollUp;
5116 0 : break;
5117 : }
5118 0 : case Key_ScrollDown:
5119 : {
5120 0 : action = State_ScrollDown;
5121 0 : break;
5122 : }
5123 : }
5124 0 : if(action)
5125 : {
5126 0 : if(isdown)
5127 : {
5128 0 : if(hold)
5129 : {
5130 0 : world->clearstate(hold);
5131 : }
5132 0 : if(world->setstate(action, cursorx, cursory, 0, true, action|hold))
5133 : {
5134 0 : return true;
5135 : }
5136 : }
5137 0 : else if(hold)
5138 : {
5139 0 : if(world->setstate(action, cursorx, cursory, hold, true, action))
5140 : {
5141 0 : world->clearstate(hold);
5142 0 : return true;
5143 : }
5144 0 : world->clearstate(hold);
5145 : }
5146 0 : return world->allowinput();
5147 : }
5148 0 : return world->key(code, isdown);
5149 : }
5150 :
5151 0 : bool textinput(const char *str, int len)
5152 : {
5153 0 : return world->textinput(str, len);
5154 : }
5155 :
5156 0 : void setup()
5157 : {
5158 0 : world = new World;
5159 0 : }
5160 :
5161 0 : void cleanup()
5162 : {
5163 0 : world->children.clear();
5164 0 : for(auto &[k, i] : windows)
5165 : {
5166 0 : delete i;
5167 : }
5168 0 : windows.clear();
5169 0 : if(world)
5170 : {
5171 0 : delete world;
5172 0 : world = nullptr;
5173 : }
5174 0 : }
5175 :
5176 0 : void calctextscale()
5177 : {
5178 0 : uitextscale = 1.0f/uitextrows;
5179 :
5180 0 : int tw = hudw(),
5181 0 : th = hudh();
5182 0 : if(forceaspect)
5183 : {
5184 0 : tw = static_cast<int>(std::ceil(th*forceaspect));
5185 : }
5186 0 : gettextres(tw, th);
5187 0 : uicontextscale = conscale/th;
5188 0 : }
5189 :
5190 0 : void update()
5191 : {
5192 0 : readyeditors();
5193 :
5194 0 : world->setstate(State_Hover, cursorx, cursory, world->childstate & State_HoldMask);
5195 0 : if(world->childstate & State_Hold)
5196 : {
5197 0 : world->setstate(State_Hold, cursorx, cursory, State_Hold, false);
5198 : }
5199 0 : if(world->childstate & State_AltHold)
5200 : {
5201 0 : world->setstate(State_AltHold, cursorx, cursory, State_AltHold, false);
5202 : }
5203 0 : if(world->childstate & State_EscHold)
5204 : {
5205 0 : world->setstate(State_EscHold, cursorx, cursory, State_EscHold, false);
5206 : }
5207 :
5208 0 : calctextscale();
5209 0 : world->build();
5210 0 : flusheditors();
5211 0 : }
5212 :
5213 0 : void render()
5214 : {
5215 0 : world->layout();
5216 0 : world->adjustchildren();
5217 0 : world->draw();
5218 0 : }
5219 :
5220 0 : float abovehud()
5221 : {
5222 0 : return world->abovehud();
5223 : }
5224 : }
|