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