[go: up one dir, main page]

Menu

[r152]: / lpi_gui.h  Maximize  Restore  History

Download this file

967 lines (755 with data), 41.9 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
/*
Copyright (c) 2005-2009 Lode Vandevenne
All rights reserved.
This file is part of Lode's Programming Interface.
Lode's Programming Interface is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Lode's Programming Interface is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Lode's Programming Interface. If not, see <http://www.gnu.org/licenses/>.
*/
/*
lpi_gui: an OpenGL GUI. Well actually now it is updated to support any lpi::IDrawer2D, so it can use OpenGL or something else.
*/
#pragma once
#include <map>
#include "lpi_gui_base.h"
#include "lpi_color.h"
#include "lpi_texture.h"
#include "lpi_gui_drawer.h"
/*
TODO:
[ ] modal vs modeless dialogs or elements
[ ] progress bar (and a good multilevel one)
[ ] menu (both menu bar and right click popup menus)
[ ] minimizable windows (and bar to dock them in?)
[ ] spinner (value with up and down button)
[ ] dropdown list
[ ] tree (and flat list and table)
[ ] file dialog
[ ] toolbar (just a bunch of image buttons, not hard to make)
[ ] textfield with multiple lines
[ ] status bar in windows
Hint:
When executing the GUI in a gameloop, when you have put all elements in a MainContainer, you have to
call each frame both "handle" and "draw" on the container. While it doesn't really matter in what order you call them,
it works best if you do "handle" first and then immediatly "draw", because then the graphics are shown to the
user one frame sooner than if you'd do the inverse. The more things you do between "handle" and "draw", the more
there will appear mouse lag, but if you call them immediatly after each other there won't be any mouse lag in the GUI.
So the best order is:
-*beginning of frame*
-pause a few milliseconds to give the CPU something else to do
-clear screen (so that all new things can be drawn on it)
-calculations of the game or program (including drawing game or program related things except the gui)
-handle lpi gui
-draw lpi gui
-redraw screen (that is, make all changes of this frame visible)
-*end of frame*
*/
namespace lpi
{
namespace gui
{
class Element;
class ToolTipManager //this is made to draw the tooltip at the very end to avoid other gui items to be drawn over it
{
private:
std::map<const Element*, std::string> elements;
public:
void registerElement(Element* element, const std::string& tip); //doing this overrides the tooltip the element itself generates, but usually the element itself won't generate any tooltip at all on its own and using this function of the tooltipmanager is the only way to get a tooltip for that element
void draw(const Element* root, IGUIDrawer& drawer) const;
static void drawToolTip(const std::string& tip, IGUIDrawer& drawer);
};
/*
Hovering elements can be caused by any GUI Element. Examples are:
-menu's (regular menus, which give a popup when opened)
-dropdown lists: the dropped down part
-opened colorchooser in dynamic page
-...
This allows GUI elements to generate something that appears in front of everything
else, no matter how much levels higher or lower it is in the tree. But, beware,
to avoid resulting in a messy screen, not too many elements should have a hovering
element open at the same time. Typically, if you click the mouse outside of one hovering
panel, it should disappear. If you make a GUI element that doesn't do that, there
could be created many hovering panels and then all usefullnes is lost since only
one can be at the top and there's only so much screenspace.
The effect is reached by drawing these elements after all the others, and by
handling them without any other elements going over them to prevent blocking mouseOver.
*/
class IHoverManager
{
public:
virtual void addHoverElement(Element* element) = 0;
};
class InternalContainer //container inside elements, for elements that contain sub elements to work (e.g. scrollbar exists out of background and 3 buttons)
{
private:
std::vector<Element*> elements;
std::map<Element*, Sticky> sticky;
public:
const std::vector<Element*>& getElements() const { return elements; }
std::vector<Element*>& getElements() { return elements; }
void resize(const Pos<int>& oldPos, const Pos<int>& newPos); //this resizes the 2D size of elements, not the amount of elements
void move(int x, int y);
void setElementOver(bool state); //this says to all elements whether or not another element is in front of it in Z order, causing mouse to not work
void manageHover(IHoverManager& hover);
void setBasicMouseInfo(const IInput& input);
const Element* hitTest(const IInput& input) const;
void addSubElement(Element* element, const Sticky& sticky, Element* parent);
void insertSubElement(size_t index, Element* element, const Sticky& sticky, Element* parent);
void clearSubElements();
Element* getElement(size_t i) const { return elements[i]; }
size_t findElementIndex(Element* element);
void removeElement(Element* element);
Sticky getSticky(Element* element) const;
void setSticky(Element* element, const Sticky& sticky, Element* parent);
void setStickyRelative(Element* element, Element* parent); //automatically calculates the sticky for completely relative position, based on the current sizes
void setStickyFull(Element* element, Element* parent); //automatically calculates the sticky for "full" behaviour, sides will follow parent's corresponding size, compared to current position
private:
void initSubElement(Element* element, const Sticky& sticky, Element* parent);
void setStickyElementSize(Element* element, Element* parent);
void setStickyElementSize(Element* element, const Pos<int>& newPos);
};
class Element : public ElementRectangular
{
private:
MouseState mouse_state_for_containers; //for bookkeeping by containers that contain this element TODO: make container itself remember one per element
protected:
bool elementOver; //true if there is an element over this element, causing the mouse NOT to be over this one (Z-order related)
bool enabled; //if false, the draw() and handle() functions don't do anything, and mouse checks return false. So then it's invisible, inactive, totally not present.
////minimum size
virtual int getMinSizeX() { return 0; } //you can't resize this element to something smaller than this
virtual int getMinSizeY() { return 0; }
protected:
void autoActivate(const IInput& input, MouseState& auto_activate_mouse_state, bool& control_active); //utility function used by text input controls, they need a member variable like "MouseState auto_activate_mouse_state" in them for this and a bool "control_active", and in their handleWidget, put, "autoActivate(input, auto_activate_mouse_state, control_active); if(!control_active) return;"
virtual void drawImpl(IGUIDrawer& drawer) const = 0; //called by draw(), this one can be overloaded for each widget defined below
virtual void handleImpl(const IInput& input);
virtual void moveImpl(int x, int y); //Override this if you have subelements, unless you use addSubElement in ElementComposite.
virtual void resizeImpl(const Pos<int>& newPos); //always called after resize, will resize the other elements to the correct size. Override this if you have subelements, unless you use addSubElement in ElementComposite. When resizeWidget is called, you can get the new size from newPos, while the old size is still in x0, y0, x1, y1 from this and will be set after resizeWidget is called.
virtual void manageHoverImpl(IHoverManager& hover);
/*
drawGUIPart: convenient helper function to draw a sub-element with its size and mouseoverstate etc...,
so that you don't always have to write the same code to create GUIPartMod, and give the 4 coordinates,
over and over.
*/
static void drawGUIPart(IGUIDrawer& drawer, const Element* element, GUIPart part);
static void drawGUIPartColor(IGUIDrawer& drawer, const Element* element, GUIPart part, const ColorRGB& color);
static void drawGUIPartText(IGUIDrawer& drawer, const Element* element, GUIPart part, const std::string& text);
public:
Element();
virtual ~Element(){};
bool isEnabled() const { return enabled; }
void setEnabled(bool i_enabled = true);
////mouse overrides
virtual bool mouseOver(const IInput& input) const;
virtual bool mouseGrabbable() const;
virtual bool mouseActive() const { return enabled; }
////core functions of gui::Elements
void draw(IGUIDrawer& drawer) const; //will draw the actual widget and do some other always-to-do stuff, do NOT override this function
void handle(const IInput& input);
virtual void move(int x, int y);
virtual void resize(int x0, int y0, int x1, int y1); //especially useful for windows and their container; parameters are the new values for x0, y0, x1 and y1 so this function can both move the object to a target and resize
void manageHover(IHoverManager& hover);
/*
hitTest: if you override this, only return publically accessible elements, not internal parts of the element. For example, it's ok for a window to return elements you placed inside of it or itself, but not to return its top bar or close button.
It's for example not ok for a scrollbar to return its arrow buttons or the scroller button
TODO: revise this.
*/
virtual const Element* hitTest(const IInput& input) const;
virtual Element* hitTest(const IInput& input);
virtual bool isFloating() const; //returns true if it's an element that should go to the top if you click on it (like a window). This is used by Container to bring elements to the top.
void moveTo(int x, int y);
void moveCenterTo(int x, int y);
void growX0(int d) { resize(x0 + d, y0 , x1 , y1 ); } //growing can also be shrinking
void growY0(int d) { resize(x0 , y0 + d, x1 , y1 ); }
void growX1(int d) { resize(x0 , y0 , x1 + d, y1 ); }
void growY1(int d) { resize(x0 , y0 , x1 , y1 + d); }
void growSizeX0(int sizex) { resize(x1 - sizex, y0 , x1 , y1 ); } //growing can also be shrinking
void growSizeY0(int sizey) { resize(x0 , y1 - sizey, x1 , y1 ); }
void growSizeX1(int sizex) { resize(x0 , y0 , x0 + sizex, y1 ); }
void growSizeY1(int sizey) { resize(x0 , y0 , x1 , y0 + sizey); }
////custom tooltip
virtual void drawToolTip(IGUIDrawer& drawer) const; //override if you can invent a fallback tooltip to draw for the element, but it's not required, the TooltipManager only uses this if no other tooltip was specified by the user (use the static ToolTipManager::drawToolTip function if you want the default style)
virtual void setElementOver(bool state); //ALL gui types that have gui elements inside of them, must set elementOver of all gui elements inside of them too! ==> override this virtual function for those. Override this if you have subelements, unless you use addSubElement in ElementComposite.
bool hasElementOver() const;
MouseState& getMouseStateForContainer() { return mouse_state_for_containers; }
////for debugging: if you've got no idea what's going on with a GUI element, this function at least is guaranteed to show where it is (if in screen)
void drawDebugBorder(IGUIDrawer& drawer, const ColorRGB& color = RGB_Red) const;
void drawDebugCross(IGUIDrawer& drawer, const ColorRGB& color = RGB_Red) const;
void drag(const IInput& input, MouseButton button = LMB); //utility function, for moving this element when it's dragged by the mouse
};
class ElementComposite : public Element //element with "internal container" to automatically handle child elements for you
{
protected:
InternalContainer ic;
protected:
void addSubElement(Element* element, const Sticky& sticky = STICKYDEFAULT); //only used for INTERNAL parts of the gui element, such as the buttons in a scrollbar, hence this function is protected
void clearSubElements();
public:
virtual void move(int x, int y);
virtual void setElementOver(bool state);
virtual void resize(int x0, int y0, int x1, int y1);
virtual void manageHoverImpl(IHoverManager& hover);
//virtual const Element* hitTest(const IInput& input);
};
/*
ElementWrapper can be used (by inheritance) if your element contains one element in it that should have the same
size and so on, as if it's your element, but you want to wrap it to add some functionality
without inheriting from it directly.
*/
class ElementWrapper : public Element
{
protected:
Element* wrapped;
public:
ElementWrapper(Element* wrapped) : wrapped(wrapped) {};
virtual void move(int x, int y) { Element::move(x, y); wrapped->move(x, y); }
virtual void setElementOver(bool state) { Element::setElementOver(state); wrapped->setElementOver(state); }
virtual void resize(int x0, int y0, int x1, int y1) { Element::resize(x0, y0, x1, y1); wrapped->resize(x0, y0, x1, y1); }
virtual void manageHoverImpl(IHoverManager& hover) { Element::manageHoverImpl(hover); wrapped->manageHover(hover); }
virtual Element* hitTest(const IInput& input) { return wrapped->hitTest(input); }
virtual void drawImpl(IGUIDrawer& drawer) const { if(enabled) wrapped->draw(drawer); }
virtual void handleImpl(const IInput& input) { if(enabled) wrapped->handle(input); }
};
class Label //convenience class: elements that want an optional label (e.g. checkboxes) can inherit from this and use drawLabel(this) in their drawWidget function
{
private:
std::string label;
int labelX; //label position is relative to the position of the element
int labelY;
Font labelFont;
protected:
void drawLabel(IGUIDrawer& drawer, const Element* element) const;
public:
Label();
virtual ~Label(){}
void makeLabel(const std::string& label, int labelX, int labelY, const Font& labelFont = FONT_Default);
};
//Dummy = exactly the same as Element but not abstract, nothing implemented except pure virtuals of Element
class Dummy : public Element
{
void drawImpl(IGUIDrawer& /*drawer*/) const {}
};
class Button : public Element
{
/*
the button has 3 separate graphical elements:
*) text
*) an image (or two)
*) a panel (resisable rectangle with sides)
*/
public:
Button();
////part "back image"
bool enableImage;
HTexture* image[3]; //0=normal, 1=mouse over, 2=mouse down
int imageOffsetx;
int imageOffsety;
ColorRGB imageColor[3]; //0=normal, 1=mouse over, 2=mouse down
////part "front image"
bool enableImage2;
HTexture* image2[3]; //0=normal, 1=mouse over, 2=mouse down
int imageOffsetx2;
int imageOffsety2;
ColorRGB imageColor2[3]; //0=normal, 1=mouse over, 2=mouse down
////part "text"
bool enableText;
std::string text;
int textOffsetx;
int textOffsety;
Font font[3];
void autoTextSize(ITextDrawer* drawer, int extrasize = 0); //will automaticly adjust it's size to fit text size
////part "panel"
bool enablePanel;
int panelOffsetx;
int panelOffsety;
//special options
/*
mouseDownVisualStyle: when to change the image of the button to the mouseDown image
0: only when mouseDown()
1: only when mouseDownHere()
2: only when grabbed()
*/
int mouseDownVisualStyle;
////make functions
//image only constructor (without offset)
void makeImage(int x, int y,
HTexture* texture1, HTexture* texture2, HTexture* texture3, const ColorRGB& imageColor1 = RGB_White, const ColorRGB& imageColor2 = RGB_Brightred, const ColorRGB& imageColor3 = RGB_Grey); //image
void makeImage(int x, int y, HTexture* texture123, const ColorRGB& imageColor1 = RGB_White, const ColorRGB& imageColor2 = RGB_Brightred, const ColorRGB& imageColor3 = RGB_Grey);
void addFrontImage(HTexture* texture1, HTexture* texture2, HTexture* texture3,
const ColorRGB& imageColor1 = RGB_White, const ColorRGB& imageColor2 = RGB_Brightred, const ColorRGB& imageColor3 = RGB_Grey);
void addFrontImage(HTexture* texture);
//text only constructor (without offset)
void makeText(int x, int y, //basic properties
const std::string& text, //text
ITextDrawer& drawer); //drawer is for auto-text-size
//panel + text constructor (text always in center of panel, no offsets and thinking needed)
//this is the constructor with default parameters
void makeTextPanel(int x, int y, const std::string& text, int sizex = 64, int sizey = 24); //basic properties + actual text; give drawer if you want the text centered
virtual void drawImpl(IGUIDrawer& drawer) const;
virtual void handleImpl(const IInput& input);
//mouse functions without having to give the IInput
/*bool clicked(MouseButton button = LMB);
bool pressed(MouseButton button = LMB);*/
private:
MouseState button_drawing_mouse_test;
bool draw_mouse_button_down_style;
};
//The Scrollbar
class Scrollbar : public ElementComposite
{
private:
double oldTime; //in seconds
void init(const IGUIDrawer& geom);
//buttons of the scrollbar
Dummy buttonUp;
Dummy buttonDown;
Dummy scroller;
public:
//get length and begin and end coordinates of the slider part (the part between the up and down buttons) (relative to x, y of the scrollbar)
int getSliderSize() const;
int getSliderStart() const;
int getSliderEnd() const;
virtual void handleImpl(const IInput& input);
virtual void drawImpl(IGUIDrawer& drawer) const;
Direction direction; //0 = vertical, 1 = horizontal
double scrollSize; //length of the total scrollbar (in steps)
double scrollPos; //position of the scroller on the bar (in steps)
double scrollSpeed; //if speedMode == 0: steps / second, if speedMode == 1: seconds / whole bar
double absoluteSpeed; //steps / second
int speedMode;
void setRelativeScrollSpeed(); //time = time to scroll from top to bottom (in seconds)
void setRelativePosition(float position); //position = 0.0-1.0
Scrollbar();
void makeVertical(int x, int y, int length,
double scrollSize,
const IGUIDrawer& geom,
double scrollPos = 0, double offset = 0, double scrollSpeed = 1,
int speedMode = 1);
void makeHorizontal(int x, int y, int length,
double scrollSize,
const IGUIDrawer& geom,
double scrollPos = 0, double offset = 0, double scrollSpeed = 1,
int speedMode = 1);
void scroll(const IInput& input, int dir); //make it scroll from an external command
double offset; //used as an offset of ScrollPos to get/set the scroll value with offset added with the functions below
double getValue() const;
void setValue(double value);
bool forwardedMouseScrollUp() const; //see int forwardedScroll;
bool forwardedMouseScrollDown() const; //see int forwardedScroll;
void forwardScroll(int scroll); //see int forwardedScroll;
protected:
mutable int forwardedScroll; //forwarded mouse scroll event from other element; +1 is down, -1 is up
};
class ScrollbarPair : public ElementComposite
{
public:
ScrollbarPair();
void make(int x, int y, int sizex, int sizey, double scrollSizeH, double scrollSizeV, const IGUIDrawer& geom);
Scrollbar vbar;
Scrollbar hbar;
Dummy corner; //the corner piece between the two scrollbars
virtual void handleImpl(const IInput& input);
virtual void drawImpl(IGUIDrawer& drawer) const;
virtual void resizeImpl(const Pos<int>& newPos);
bool venabled;
bool henabled;
void disableV();
void disableH();
void enableV();
void enableH();
bool conserveCorner; //if false, the cornerpiece will disappear if one scrollbar is gone, if true, it'll only disappear if both scrollbars are disabled
//size of the area without the scrollbars
int getVisiblex() const; //returns x1 - x0 - vbar.getWidth() if there's a vbar, or x1 - x0 if there's no vbar
int getVisibley() const; //returns y1 - y0 - hbar.getHeight() if there's a hbar, or y1 - y0 if there's no hbar
};
//the Slider is a simplified version of the scrollbar (no up and down buttons) that also looks different
class Slider : public ElementComposite
{
private:
Dummy slider; //(by default round) button
GUIPart slidertype;
GUIPart rulertype;
public:
Slider();
double getValue() const; //a number between 0.0 and scrollSize
void setValue(double value);
double getRelValue() const; //a number between 0.0 and 1.0
void setRelValue(double value);
Direction direction;
double scrollSize;
double scrollPos;
void makeHorizontal(int x, int y, int length, double scrollSize, const IGUIDrawer& geom);
void makeVertical(int x, int y, int length, double scrollSize, const IGUIDrawer& geom);
void makeSmallHorizontal(int x, int y, int length, double scrollSize, const IGUIDrawer& geom);
double screenPosToScrollPos(int screenPos);
int scrollPosToScreenPos(double scrollPos);
virtual void drawImpl(IGUIDrawer& drawer) const;
virtual void handleImpl(const IInput& input);
};
class Container : public Element
{
protected:
InternalContainer elements;
protected:
void drawElements(IGUIDrawer& drawer) const;
void drawElementsPopup(IGUIDrawer& drawer) const;
public:
Container();
Container(IGUIDrawer& drawer); //drawer is used to initialize the size of the container to the full screen size of the drawer
virtual void handleImpl(const IInput& input); //you're supposed to handle() before you draw()
virtual void drawImpl(IGUIDrawer& drawer) const;
virtual void manageHoverImpl(IHoverManager& hover);
//push the element without affecting absolute position
void pushTop(Element* element, const Sticky& sticky = STICKYDEFAULT);
void pushBottom(Element* element, const Sticky& sticky = STICKYDEFAULT);
void insert(size_t pos, Element* element, const Sticky& sticky = STICKYDEFAULT);
//push the element so that its top left is relative to the top left of this container, thus moving it if the container isn't at 0,0
void pushTopRelative(Element* element, const Sticky& sticky = STICKYDEFAULT);
void pushBottomRelative(Element* element, const Sticky& sticky = STICKYDEFAULT);
void insertRelative(size_t pos, Element* element, const Sticky& sticky = STICKYDEFAULT);
//push the element at the given x, y (relative to this container's top left)
void pushTopAt(Element* element, int x, int y, const Sticky& sticky = STICKYDEFAULT);
void pushBottomAt(Element* element, int x, int y, const Sticky& sticky = STICKYDEFAULT);
void insertAt(size_t pos, Element* element, int x, int y, const Sticky& sticky = STICKYDEFAULT);
Element* getElement(size_t i) const { return elements.getElement(i); }
void bringToTop(Element* element); //precondition: element must already be in the list
void centerElement(Element* element); //TODO: remove this function from this class, if necessary put it somewhere else as utility function
void remove(Element* element);
unsigned long size() const;
void clear(); //clears all the elements
void putInside(unsigned long i);
virtual void moveImpl(int x, int y);
void make(int x, int y, int sizex, int sizey);
void getRelativeElementPos(Element& element, int& ex, int& ey) const;
virtual void resizeImpl(const Pos<int>& newPos);
virtual void setElementOver(bool state);
virtual const Element* hitTest(const IInput& input) const;
void setSizeToElements(); //makes the size of the container as big as the elements. This resizes the container in a non-sticky way: no element is affected
};
class Group : public Container
{
public:
virtual bool isInside(int x, int y) const; //difference with the isInside from other guielements, is that it checks all sub elements, not itself, for mouseovers
virtual void drawImpl(IGUIDrawer& drawer) const;
};
class IModalFrameHandler //functionality the MainContainer needs to do modal gameloops
{
public:
virtual bool doFrame() = 0; //should do everything needed every frame, including clearing the screen and redrawing it if necessary (or even draw lots of extra stuff from a computer game in the background). Returns true normally, false if you need to force-quit.
virtual IGUIDrawer& getDrawer() = 0; //since GUIDrawer also has a getInput in it, this is also used for getting the IInput
virtual void getScreenSize(int& x0, int& y0, int& x1, int& y1) = 0;
};
class Dialog; //forward declaration of class Dialog for the doModalDialog function of MainContainer
/*
MainContainer has a system for handling hovering elements. Use this as the container spanning
the whole screen in which every other element is contained, to make hovering (popups) work. Without
this or an alternative system where you use IHoverManager somehow yourself, menu's, dropdown lists,
and other elements that use hovering elements won't work.
TODO: give MainContainer also a ToolTipManager and let it handle it
*/
class MainContainer : public IHoverManager, public ElementComposite
{
private:
Container c;
Container e;
Group h;
ToolTipManager tooltips;
public:
MainContainer();
MainContainer(IGUIDrawer& drawer);
void ctor();
ToolTipManager& getToolTipManager();
const ToolTipManager& getToolTipManager() const;
virtual void addHoverElement(Element* element);
virtual void handleImpl(const IInput& input);
virtual void drawImpl(IGUIDrawer& drawer) const;
//push the element without affecting absolute position
void pushTop(Element* element, const Sticky& sticky = STICKYDEFAULT){e.pushTop(element, sticky);}
void pushBottom(Element* element, const Sticky& sticky = STICKYDEFAULT){e.pushBottom(element, sticky);}
void insert(size_t pos, Element* element, const Sticky& sticky = STICKYDEFAULT){e.insert(pos, element, sticky);}
//push the element so that its top left is relative to the top left of this container, thus moving it if the container isn't at 0,0
void pushTopRelative(Element* element, const Sticky& sticky = STICKYDEFAULT){e.pushTop(element, sticky);}
void pushBottomRelative(Element* element, const Sticky& sticky = STICKYDEFAULT){e.pushBottomRelative(element, sticky);}
void insertRelative(size_t pos, Element* element, const Sticky& sticky = STICKYDEFAULT){e.insertRelative(pos, element, sticky);}
//push the element at the given x, y (relative to this container's top left)
void pushTopAt(Element* element, int x, int y, const Sticky& sticky = STICKYDEFAULT){e.pushTopAt(element, x, y, sticky);}
void pushBottomAt(Element* element, int x, int y, const Sticky& sticky = STICKYDEFAULT){e.pushBottomAt(element, x, y, sticky);}
void insertAt(size_t pos, Element* element, int x, int y, const Sticky& sticky = STICKYDEFAULT){e.insertAt(pos, element, x, y, sticky);}
virtual const Element* hitTest(const IInput& input) const;
bool doModalDialog(Dialog& dialog, IModalFrameHandler& frame); //Blocking function call. Runs a gameloop where only the given dialog is handled. Returns true normally, false if the loop during modal dialog showing was force-quit. After running doModalDialog(), you should check for ok/cancel with dialog's getResult, and if it was OK, use the value if the dialog (if any).
};
class ScrollElement : public ElementComposite //a.k.a "ScrollZone"
{
/*
ScrollElement will contain 1 element (which can be a container with more elements).
Depending on the size of that element compared to this ScrollElement,
1 or 2 scrollbars will appear, allowing to scroll to see all of that element.
The size of that element isn't affected by this ScrollElement.
The position of that element is completely controlled by this ScrollElement or its scrollbars
*/
protected:
bool keepelementsinside; //false by default
public:
Element* element;
ScrollElement();
virtual void handleImpl(const IInput& input); //you're supposed to handle() before you draw()
virtual void drawImpl(IGUIDrawer& drawer) const;
virtual void manageHoverImpl(IHoverManager& hover);
virtual void moveImpl(int x, int y);
virtual void setElementOver(bool state);
virtual const Element* hitTest(const IInput& input) const;
void setKeepElementsInside(bool set) { keepelementsinside = set; }
void make(int x, int y, int sizex, int sizey, Element* element, const IGUIDrawer& geom);
////everything concerning the scrollability
ScrollbarPair bars;
//the zone where elements are drawn: the size of this container excluding the scrollbarpair's bars
int getVisibleSizeX() const;
int getVisibleSizeY() const;
int getVisibleX0() const;
int getVisibleY0() const;
int getVisibleX1() const;
int getVisibleY1() const;
int oldScrollx; //used to move elements every frame when you scroll the bars
int oldScrolly;
void moveAreaTo(int x, int y); //moves the area to given position, and all the elements, but not the bars and x0, y0, x1, y1, Used when you scroll.
void forwardScrollToVerticalScrollbar(int scroll) //if you scroll the mouse and want this container's vertical scrollbar to handle it, use this! -1 for up, +1 for down
{
bars.vbar.forwardScroll(scroll);
}
void updateBars(); //this is called automatically now and then, but can also be called by you at any time when the element changed position to ensure the scrollers position won't override your new position
protected:
virtual bool mouseInVisibleZone(const IInput& input) const; //is the mouse in the zone where elements are drawn
void initBars();
void toggleBars(); //turns the bars on or of depending on if they're needed or not
};
//Window is a container for other gui elements that'll move and get drawn at the command of the window
class Window : public ElementComposite
{
protected:
Container container;
ScrollElement scroll;
ColorRGB colorMod;
Dummy closeButton;
Dummy resizer;
bool enableTop; //enable the top bar of the window (then you can drag it with this instead of everywhere on the window)
bool closeEnabled; //close button is enabled
bool enableResizer;
bool resizerOverContainer;
virtual int getMinSizeX() { return 128; }
virtual int getMinSizeY() { return 64; }
protected:
////container
int getContainerLowest() const;
int getContainerHighest() const;
int getContainerLeftmost() const;
int getContainerRightmost() const;
Container* getContainer() { return &container; }
//get the parameters for the container surface on which the elements in the window will be
int getContainerX0() const { return container.getX0(); }
int getContainerY0() const { return container.getY0(); }
int getContainerX1() const { return container.getX1(); }
int getContainerY1() const { return container.getY1(); }
int getContainerSizeX() const { return container.getSizeX(); }
int getContainerSizeY() const { return container.getSizeY(); }
int getRelContainerStart() const { return container.getY0() - y0; }
int getRelContentStart() const;
//the drawImpl call will first do drawWindow, then drawElements. You can override drawImpl and call these function too and do something else between them if needed.
void drawWindow(IGUIDrawer& drawer) const;
void drawElements(IGUIDrawer& drawer) const;
public:
Window();
//push the element without affecting absolute position
void pushTop(Element* element, const Sticky& sticky = STICKYDEFAULT);
void pushBottom(Element* element, const Sticky& sticky = STICKYDEFAULT);
void insert(size_t pos, Element* element, const Sticky& sticky = STICKYDEFAULT);
//push the element so that its top left is relative to the top left of this container, thus moving it if the container isn't at 0,0
void pushTopRelative(Element* element, const Sticky& sticky = STICKYDEFAULT);
void pushBottomRelative(Element* element, const Sticky& sticky = STICKYDEFAULT);
void insertRelative(size_t pos, Element* element, const Sticky& sticky = STICKYDEFAULT);
//push the element at the given x, y (relative to this container's top left)
void pushTopAt(Element* element, int x, int y, const Sticky& sticky = STICKYDEFAULT);
void pushBottomAt(Element* element, int x, int y, const Sticky& sticky = STICKYDEFAULT);
void insertAt(size_t pos, Element* element, int x, int y, const Sticky& sticky = STICKYDEFAULT);
void bringToTop(Element* element); //precondition: element must already be in the list
void remove(Element* element);
unsigned long size() const;
void putInside(int i);
//these scrollbars will be part of the container
void addScrollbars(const IGUIDrawer& geom);
void removeScrollbars();
void updateScroll(); //call this after elements inside window changed size or position. It updates size of container to elements inside it, then updates scroll (updateBars()). I've had this work very well together with a zoomable and movable image inside a window with scrollbars!!
void addTop(const IGUIDrawer& geom);
Dummy top; //todo make this private or protected (currently used by the unittest)
////optional part "title"
std::string title;
Font titleFont;
void addTitle(const std::string& title, const Font& titleFont = FONT_Default);
void setTitle(const std::string& title); //only to be used after "addTitle" (or the x, y position will be messed up)
////optional part "close button"
bool closed;
void addCloseButton(const IGUIDrawer& geom); //ofsset from top *right* corner, choose style of close button by making it, it's the built in texture by default
////optional part "resizer" = draggable bottom right corner with diagonally striped image
void addResizer(const IGUIDrawer& geom, bool overContainer = false);
virtual void drawImpl(IGUIDrawer& drawer) const;
virtual void manageHoverImpl(IHoverManager& hover);
virtual void handleImpl(const IInput& input);
virtual bool isFloating() const;
virtual const Element* hitTest(const IInput& input) const;
////useful for the close button
void close() { closed = 1; setEnabled(false); } //use this if closed == 1
void unClose() { closed = 0; setEnabled(true); }
void toggleClose() { if(closed) unClose(); else close(); }
void setColorMod(const ColorRGB& color) { colorMod = color; }
};
//The Checkbox
class Checkbox : public Element, public Label
{
private:
int textOffsetX;
int textOffsetY;
//bool downAndTested; //if mouse is down and that is already handled, leave this on so that it'll ignore mouse till it's back up
void positionText(); //automaticly place the text a few pixels next to the checkbox, in the center
bool checked; //if true: checked, if false: unchecked
int toggleOnMouseUp;
//text
bool enableText; //the text is a title drawn next to the checkbox, with automaticly calculated position
std::string text;
Font font;
//for when using checkbox with custom image
bool useCustomImages;
HTexture* texture[4];
ColorRGB colorMod[4];
bool useCustomImages2;
HTexture* texture2[4];
ColorRGB colorMod2[4];
public:
Checkbox();
void make(int x, int y, bool checked = 0, int toggleOnMouseUp = 0);
void makeSmall(int x, int y, bool checked = 0, int toggleOnMouseUp = 0);
void addText(const std::string& text, const Font& font = FONT_Default);
virtual void drawImpl(IGUIDrawer& drawer) const; //also handles it by calling handle(): toggles when mouse down or not
virtual void handleImpl(const IInput& input);
void setText(const std::string& newText);
const std::string& getText() const { return text; }
void toggle();
void check() { checked = true; }
void uncheck() { checked = false; }
bool isChecked() { return checked; }
void setChecked(bool check) { checked = check; }
//TODO: make the functions below more logical and clean
//for giving it alternative textures. texture1 = not checked, mouse not over. Texture2 = not checked, mouse over. Texture3 = checked, mouse not over. Texture4 = checked, mouse over.
void setTexturesAndColors(HTexture* texture1, HTexture* texture2, HTexture* texture3, HTexture* texture4,
const ColorRGB& color1, const ColorRGB& color2, const ColorRGB& color3, const ColorRGB& color4);
//without mouseOver effect
void setTexturesAndColors(HTexture* texture1, HTexture* texture2,
const ColorRGB& color1, const ColorRGB& color2);
void addFrontImage(HTexture* texture1, HTexture* texture2, HTexture* texture3, HTexture* texture4,
const ColorRGB& imageColor1 = RGB_White, const ColorRGB& imageColor2 = RGB_Grey, const ColorRGB& imageColor3 = RGB_White, const ColorRGB& imageColor4 = RGB_Grey);
void addFrontImage(HTexture* texture);
void setCustomColor(const ColorRGB& color);
void setCustomColor2(const ColorRGB& color); //for the front image
};
//The bulletlist, a list of checkboxes where only one can be selected
class BulletList : public ElementComposite
{
public:
std::vector <Checkbox*> bullet; //todo: make private
Checkbox prototype;
BulletList();
~BulletList();
void clear();
virtual void drawImpl(IGUIDrawer& drawer) const;
virtual void handleImpl(const IInput& input);
void make(int x, int y, unsigned long amount, int xDiff, int yDiff); //diff = the location difference between successive checkboxes
void make(int x, int y, unsigned long amount, int xDiff, int yDiff, unsigned long amountx); //make in 2D pattern
void setCorrectSize();
//set style of the bullets by using prototype.make([checkbox parameters where x and y will be ignored])
int check(); //returns which one is checked
int xDiff; //just added for "book keeping"
int yDiff; //just added for "book keeping"
std::string getText(unsigned long i) const;
const std::string& getCurrentText() const;
void addText(const std::string& text, unsigned long i);
void set(unsigned long i);
virtual const Element* hitTest(const IInput& input) const;
private:
int lastChecked; //remember which one was checked, so it can know when a new one is checked, which one that is
};
class Text : public Element
{
public:
bool useNewLine;
Font font;
virtual void drawImpl(IGUIDrawer& drawer) const;
Text();
void make(int x = 0, int y = 0, const std::string& text = "", const Font& font = FONT_Default);
void setText(const std::string& text);
const std::string& getText() const { return text; }
private:
std::string text;
};
class Image : public Element
{
private:
ITexture* image;
public:
ColorRGB colorMod;
virtual void drawImpl(IGUIDrawer& drawer) const;
Image();
void make(int x, int y, ITexture* image, const ColorRGB& colorMod = RGB_White);
void make(int x, int y, int sizex, int sizey, ITexture* image, const ColorRGB& colorMod = RGB_White);
};
class Tabs : public ElementComposite
{
private:
struct Tab : public Element
{
std::string name; //name of this tab
Container container; //contains the GUI elements inside this tab
Tab();
virtual void drawImpl(IGUIDrawer& drawer) const;
};
std::vector<Tab*> tabs;
size_t selected_tab;
void generateTabSizes();
void updateActiveContainer();
public:
Tabs();
~Tabs();
void addTab(const std::string& name);
size_t getNumTabs() const;
Container& getTabContent(size_t index) const;
void clear();
size_t getSelectedTab() const { return selected_tab; }
void selectTab(size_t i_index);
virtual void drawImpl(IGUIDrawer& drawer) const;
virtual void manageHoverImpl(IHoverManager& hover);
virtual void handleImpl(const IInput& input);
};
//a Dialog can be done "Modal" by MainContainer
class Dialog : public Window
{
public:
enum Result
{
OK, //there's no "yes" or "no" results. A yes/no dialog has a boolean result for that, similar to how a color dialog has a ColorRGB result and so on.
CANCEL
};
protected:
Result result;
public:
Dialog()
: result(CANCEL)
{
}
virtual bool done() const; //returns true if the dialog is closed by the user (by pressing ok, cancel, ...)
virtual Result getResult() const; //get this after done was true. This is NOT the value of the dialog (like the color of a color dialog or the yes/no boolean of a yesno dialog). Instead, this only indicates if the value is valid (if it's OK) or not (if it's CANCEL)
};
} //namespace gui
} //namespace lpi