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