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