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