]> git.lizzy.rs Git - irrlicht.git/blob - include/IGUIElement.h
Fix IGUIElements not getting a tab order because of invisible or disabled parents.
[irrlicht.git] / include / IGUIElement.h
1 // Copyright (C) 2002-2012 Nikolaus Gebhardt\r
2 // This file is part of the "Irrlicht Engine".\r
3 // For conditions of distribution and use, see copyright notice in irrlicht.h\r
4 \r
5 #ifndef __I_GUI_ELEMENT_H_INCLUDED__\r
6 #define __I_GUI_ELEMENT_H_INCLUDED__\r
7 \r
8 #include "IReferenceCounted.h"\r
9 #include "rect.h"\r
10 #include "irrString.h"\r
11 #include "IEventReceiver.h"\r
12 #include "EGUIElementTypes.h"\r
13 #include "EGUIAlignment.h"\r
14 #include "IAttributes.h"\r
15 #include "IGUIEnvironment.h"\r
16 #include <cassert>\r
17 #include <algorithm>\r
18 #include <list>\r
19 #include <vector>\r
20 \r
21 namespace irr\r
22 {\r
23 namespace gui\r
24 {\r
25 //! Base class of all GUI elements.\r
26 class IGUIElement : virtual public IReferenceCounted, public IEventReceiver\r
27 {\r
28 public:\r
29 \r
30         //! Constructor\r
31         IGUIElement(EGUI_ELEMENT_TYPE type, IGUIEnvironment* environment, IGUIElement* parent,\r
32                 s32 id, const core::rect<s32>& rectangle)\r
33                 : Parent(0), RelativeRect(rectangle), AbsoluteRect(rectangle),\r
34                 AbsoluteClippingRect(rectangle), DesiredRect(rectangle),\r
35                 MaxSize(0,0), MinSize(1,1), IsVisible(true), IsEnabled(true),\r
36                 IsSubElement(false), NoClip(false), ID(id), IsTabStop(false), TabOrder(-1), IsTabGroup(false),\r
37                 AlignLeft(EGUIA_UPPERLEFT), AlignRight(EGUIA_UPPERLEFT), AlignTop(EGUIA_UPPERLEFT), AlignBottom(EGUIA_UPPERLEFT),\r
38                 Environment(environment), Type(type)\r
39         {\r
40                 #ifdef _DEBUG\r
41                 setDebugName("IGUIElement");\r
42                 #endif\r
43 \r
44                 // if we were given a parent to attach to\r
45                 if (parent)\r
46                 {\r
47                         parent->addChildToEnd(this);\r
48                         recalculateAbsolutePosition(true);\r
49                 }\r
50         }\r
51 \r
52 \r
53         //! Destructor\r
54         virtual ~IGUIElement()\r
55         {\r
56                 for (auto child : Children) {\r
57                         child->Parent = nullptr;\r
58                         child->drop();\r
59                 }\r
60         }\r
61 \r
62 \r
63         //! Returns parent of this element.\r
64         IGUIElement* getParent() const\r
65         {\r
66                 return Parent;\r
67         }\r
68 \r
69         //! Returns the relative rectangle of this element.\r
70         core::rect<s32> getRelativePosition() const\r
71         {\r
72                 return RelativeRect;\r
73         }\r
74 \r
75 \r
76         //! Sets the relative rectangle of this element.\r
77         /** \param r The absolute position to set */\r
78         void setRelativePosition(const core::rect<s32>& r)\r
79         {\r
80                 if (Parent)\r
81                 {\r
82                         const core::rect<s32>& r2 = Parent->getAbsolutePosition();\r
83 \r
84                         core::dimension2df d((f32)(r2.getSize().Width), (f32)(r2.getSize().Height));\r
85 \r
86                         if (AlignLeft   == EGUIA_SCALE)\r
87                                 ScaleRect.UpperLeftCorner.X = (f32)r.UpperLeftCorner.X / d.Width;\r
88                         if (AlignRight  == EGUIA_SCALE)\r
89                                 ScaleRect.LowerRightCorner.X = (f32)r.LowerRightCorner.X / d.Width;\r
90                         if (AlignTop    == EGUIA_SCALE)\r
91                                 ScaleRect.UpperLeftCorner.Y = (f32)r.UpperLeftCorner.Y / d.Height;\r
92                         if (AlignBottom == EGUIA_SCALE)\r
93                                 ScaleRect.LowerRightCorner.Y = (f32)r.LowerRightCorner.Y / d.Height;\r
94                 }\r
95 \r
96                 DesiredRect = r;\r
97                 updateAbsolutePosition();\r
98         }\r
99 \r
100         //! Sets the relative rectangle of this element, maintaining its current width and height\r
101         /** \param position The new relative position to set. Width and height will not be changed. */\r
102         void setRelativePosition(const core::position2di & position)\r
103         {\r
104                 const core::dimension2di mySize = RelativeRect.getSize();\r
105                 const core::rect<s32> rectangle(position.X, position.Y,\r
106                                                 position.X + mySize.Width, position.Y + mySize.Height);\r
107                 setRelativePosition(rectangle);\r
108         }\r
109 \r
110 \r
111         //! Sets the relative rectangle of this element as a proportion of its parent's area.\r
112         /** \note This method used to be 'void setRelativePosition(const core::rect<f32>& r)'\r
113         \param r  The rectangle to set, interpreted as a proportion of the parent's area.\r
114         Meaningful values are in the range [0...1], unless you intend this element to spill\r
115         outside its parent. */\r
116         void setRelativePositionProportional(const core::rect<f32>& r)\r
117         {\r
118                 if (!Parent)\r
119                         return;\r
120 \r
121                 const core::dimension2di& d = Parent->getAbsolutePosition().getSize();\r
122 \r
123                 DesiredRect = core::rect<s32>(\r
124                                         core::floor32((f32)d.Width * r.UpperLeftCorner.X),\r
125                                         core::floor32((f32)d.Height * r.UpperLeftCorner.Y),\r
126                                         core::floor32((f32)d.Width * r.LowerRightCorner.X),\r
127                                         core::floor32((f32)d.Height * r.LowerRightCorner.Y));\r
128 \r
129                 ScaleRect = r;\r
130 \r
131                 updateAbsolutePosition();\r
132         }\r
133 \r
134 \r
135         //! Gets the absolute rectangle of this element\r
136         core::rect<s32> getAbsolutePosition() const\r
137         {\r
138                 return AbsoluteRect;\r
139         }\r
140 \r
141 \r
142         //! Returns the visible area of the element.\r
143         core::rect<s32> getAbsoluteClippingRect() const\r
144         {\r
145                 return AbsoluteClippingRect;\r
146         }\r
147 \r
148 \r
149         //! Sets whether the element will ignore its parent's clipping rectangle\r
150         /** \param noClip If true, the element will not be clipped by its parent's clipping rectangle. */\r
151         void setNotClipped(bool noClip)\r
152         {\r
153                 NoClip = noClip;\r
154                 updateAbsolutePosition();\r
155         }\r
156 \r
157 \r
158         //! Gets whether the element will ignore its parent's clipping rectangle\r
159         /** \return true if the element is not clipped by its parent's clipping rectangle. */\r
160         bool isNotClipped() const\r
161         {\r
162                 return NoClip;\r
163         }\r
164 \r
165 \r
166         //! Sets the maximum size allowed for this element\r
167         /** If set to 0,0, there is no maximum size */\r
168         void setMaxSize(core::dimension2du size)\r
169         {\r
170                 MaxSize = size;\r
171                 updateAbsolutePosition();\r
172         }\r
173 \r
174 \r
175         //! Sets the minimum size allowed for this element\r
176         void setMinSize(core::dimension2du size)\r
177         {\r
178                 MinSize = size;\r
179                 if (MinSize.Width < 1)\r
180                         MinSize.Width = 1;\r
181                 if (MinSize.Height < 1)\r
182                         MinSize.Height = 1;\r
183                 updateAbsolutePosition();\r
184         }\r
185 \r
186 \r
187         //! The alignment defines how the borders of this element will be positioned when the parent element is resized.\r
188         void setAlignment(EGUI_ALIGNMENT left, EGUI_ALIGNMENT right, EGUI_ALIGNMENT top, EGUI_ALIGNMENT bottom)\r
189         {\r
190                 AlignLeft = left;\r
191                 AlignRight = right;\r
192                 AlignTop = top;\r
193                 AlignBottom = bottom;\r
194 \r
195                 if (Parent)\r
196                 {\r
197                         core::rect<s32> r(Parent->getAbsolutePosition());\r
198 \r
199                         core::dimension2df d((f32)r.getSize().Width, (f32)r.getSize().Height);\r
200 \r
201                         if (AlignLeft   == EGUIA_SCALE)\r
202                                 ScaleRect.UpperLeftCorner.X = (f32)DesiredRect.UpperLeftCorner.X / d.Width;\r
203                         if (AlignRight  == EGUIA_SCALE)\r
204                                 ScaleRect.LowerRightCorner.X = (f32)DesiredRect.LowerRightCorner.X / d.Width;\r
205                         if (AlignTop    == EGUIA_SCALE)\r
206                                 ScaleRect.UpperLeftCorner.Y = (f32)DesiredRect.UpperLeftCorner.Y / d.Height;\r
207                         if (AlignBottom == EGUIA_SCALE)\r
208                                 ScaleRect.LowerRightCorner.Y = (f32)DesiredRect.LowerRightCorner.Y / d.Height;\r
209                 }\r
210         }\r
211 \r
212         //! How left element border is aligned when parent is resized\r
213         EGUI_ALIGNMENT getAlignLeft() const\r
214         {\r
215                 return AlignLeft;\r
216         }\r
217 \r
218         //! How right element border is aligned when parent is resized\r
219         EGUI_ALIGNMENT getAlignRight() const\r
220         {\r
221                 return AlignRight;\r
222         }\r
223 \r
224         //! How top element border is aligned when parent is resized\r
225         EGUI_ALIGNMENT getAlignTop() const\r
226         {\r
227                 return AlignTop;\r
228         }\r
229 \r
230         //! How bottom element border is aligned when parent is resized\r
231         EGUI_ALIGNMENT getAlignBottom() const\r
232         {\r
233                 return AlignBottom;\r
234         }\r
235 \r
236         //! Updates the absolute position.\r
237         virtual void updateAbsolutePosition()\r
238         {\r
239                 recalculateAbsolutePosition(false);\r
240 \r
241                 // update all children\r
242                 for (auto child : Children)\r
243                 {\r
244                         child->updateAbsolutePosition();\r
245                 }\r
246         }\r
247 \r
248 \r
249         //! Returns the topmost GUI element at the specific position.\r
250         /**\r
251         This will check this GUI element and all of its descendants, so it\r
252         may return this GUI element.  To check all GUI elements, call this\r
253         function on device->getGUIEnvironment()->getRootGUIElement(). Note\r
254         that the root element is the size of the screen, so doing so (with\r
255         an on-screen point) will always return the root element if no other\r
256         element is above it at that point.\r
257         \param point: The point at which to find a GUI element.\r
258         \return The topmost GUI element at that point, or 0 if there are\r
259         no candidate elements at this point.\r
260         */\r
261         virtual IGUIElement* getElementFromPoint(const core::position2d<s32>& point)\r
262         {\r
263                 IGUIElement* target = 0;\r
264 \r
265                 if (isVisible())\r
266                 {\r
267                         // we have to search from back to front, because later children\r
268                         // might be drawn over the top of earlier ones.\r
269                         auto it = Children.rbegin();\r
270                         auto ie = Children.rend();\r
271                         while (it != ie)\r
272                         {\r
273                                 target = (*it)->getElementFromPoint(point);\r
274                                 if (target)\r
275                                         return target;\r
276 \r
277                                 ++it;\r
278                         }\r
279                 }\r
280 \r
281                 if (isVisible() && isPointInside(point))\r
282                         target = this;\r
283 \r
284                 return target;\r
285         }\r
286 \r
287 \r
288         //! Returns true if a point is within this element.\r
289         /** Elements with a shape other than a rectangle should override this method */\r
290         virtual bool isPointInside(const core::position2d<s32>& point) const\r
291         {\r
292                 return AbsoluteClippingRect.isPointInside(point);\r
293         }\r
294 \r
295 \r
296         //! Adds a GUI element as new child of this element.\r
297         virtual void addChild(IGUIElement* child)\r
298         {\r
299                 if ( child && child != this )\r
300                 {\r
301                         addChildToEnd(child);\r
302                         child->updateAbsolutePosition();\r
303                 }\r
304         }\r
305 \r
306         //! Removes a child.\r
307         virtual void removeChild(IGUIElement* child)\r
308         {\r
309                 assert(child->Parent == this);\r
310                 Children.erase(child->ParentPos);\r
311                 child->Parent = nullptr;\r
312                 child->drop();\r
313         }\r
314 \r
315         //! Removes all children.\r
316         virtual void removeAllChildren() {\r
317                 while (!Children.empty()) {\r
318                         auto child = Children.back();\r
319                         child->remove();\r
320                 }\r
321         }\r
322 \r
323         //! Removes this element from its parent.\r
324         virtual void remove()\r
325         {\r
326                 if (Parent)\r
327                         Parent->removeChild(this);\r
328         }\r
329 \r
330 \r
331         //! Draws the element and its children.\r
332         virtual void draw()\r
333         {\r
334                 if ( isVisible() )\r
335                 {\r
336                         for (auto child : Children)\r
337                                 child->draw();\r
338                 }\r
339         }\r
340 \r
341 \r
342         //! animate the element and its children.\r
343         virtual void OnPostRender(u32 timeMs)\r
344         {\r
345                 if ( isVisible() )\r
346                 {\r
347                         for (auto child : Children)\r
348                                 child->OnPostRender( timeMs );\r
349                 }\r
350         }\r
351 \r
352 \r
353         //! Moves this element.\r
354         virtual void move(core::position2d<s32> absoluteMovement)\r
355         {\r
356                 setRelativePosition(DesiredRect + absoluteMovement);\r
357         }\r
358 \r
359 \r
360         //! Returns true if element is visible.\r
361         virtual bool isVisible() const\r
362         {\r
363                 return IsVisible;\r
364         }\r
365 \r
366         //! Check whether the element is truly visible, taking into accounts its parents' visibility\r
367         /** \return true if the element and all its parents are visible,\r
368         false if this or any parent element is invisible. */\r
369         virtual bool isTrulyVisible() const\r
370         {\r
371                 if(!IsVisible)\r
372                         return false;\r
373 \r
374                 if(!Parent)\r
375                         return true;\r
376 \r
377                 return Parent->isTrulyVisible();\r
378         }\r
379 \r
380         //! Sets the visible state of this element.\r
381         virtual void setVisible(bool visible)\r
382         {\r
383                 IsVisible = visible;\r
384         }\r
385 \r
386 \r
387         //! Returns true if this element was created as part of its parent control\r
388         virtual bool isSubElement() const\r
389         {\r
390                 return IsSubElement;\r
391         }\r
392 \r
393 \r
394         //! Sets whether this control was created as part of its parent.\r
395         /** For example, it is true when a scrollbar is part of a listbox.\r
396         SubElements are not saved to disk when calling guiEnvironment->saveGUI() */\r
397         virtual void setSubElement(bool subElement)\r
398         {\r
399                 IsSubElement = subElement;\r
400         }\r
401 \r
402 \r
403         //! If set to true, the focus will visit this element when using the tab key to cycle through elements.\r
404         /** If this element is a tab group (see isTabGroup/setTabGroup) then\r
405         ctrl+tab will be used instead. */\r
406         void setTabStop(bool enable)\r
407         {\r
408                 IsTabStop = enable;\r
409         }\r
410 \r
411 \r
412         //! Returns true if this element can be focused by navigating with the tab key\r
413         bool isTabStop() const\r
414         {\r
415                 return IsTabStop;\r
416         }\r
417 \r
418 \r
419         //! Sets the priority of focus when using the tab key to navigate between a group of elements.\r
420         /** See setTabGroup, isTabGroup and getTabGroup for information on tab groups.\r
421         Elements with a lower number are focused first */\r
422         void setTabOrder(s32 index)\r
423         {\r
424                 // negative = autonumber\r
425                 if (index < 0)\r
426                 {\r
427                         TabOrder = 0;\r
428                         IGUIElement *el = getTabGroup();\r
429                         while (IsTabGroup && el && el->Parent)\r
430                                 el = el->Parent;\r
431 \r
432                         IGUIElement *first=0, *closest=0;\r
433                         if (el)\r
434                         {\r
435                                 // find the highest element number\r
436                                 el->getNextElement(-1, true, IsTabGroup, first, closest, true, true);\r
437                                 if (first)\r
438                                 {\r
439                                         TabOrder = first->getTabOrder() + 1;\r
440                                 }\r
441                         }\r
442 \r
443                 }\r
444                 else\r
445                         TabOrder = index;\r
446         }\r
447 \r
448 \r
449         //! Returns the number in the tab order sequence\r
450         s32 getTabOrder() const\r
451         {\r
452                 return TabOrder;\r
453         }\r
454 \r
455 \r
456         //! Sets whether this element is a container for a group of elements which can be navigated using the tab key.\r
457         /** For example, windows are tab groups.\r
458         Groups can be navigated using ctrl+tab, providing isTabStop is true. */\r
459         void setTabGroup(bool isGroup)\r
460         {\r
461                 IsTabGroup = isGroup;\r
462         }\r
463 \r
464 \r
465         //! Returns true if this element is a tab group.\r
466         bool isTabGroup() const\r
467         {\r
468                 return IsTabGroup;\r
469         }\r
470 \r
471 \r
472         //! Returns the container element which holds all elements in this element's tab group.\r
473         IGUIElement* getTabGroup()\r
474         {\r
475                 IGUIElement *ret=this;\r
476 \r
477                 while (ret && !ret->isTabGroup())\r
478                         ret = ret->getParent();\r
479 \r
480                 return ret;\r
481         }\r
482 \r
483 \r
484         //! Returns true if element is enabled\r
485         /** Currently elements do _not_ care about parent-states.\r
486                 So if you want to affect children you have to enable/disable them all.\r
487                 The only exception to this are sub-elements which also check their parent.\r
488         */\r
489         virtual bool isEnabled() const\r
490         {\r
491                 if ( isSubElement() && IsEnabled && getParent() )\r
492                         return getParent()->isEnabled();\r
493 \r
494                 return IsEnabled;\r
495         }\r
496 \r
497 \r
498         //! Sets the enabled state of this element.\r
499         virtual void setEnabled(bool enabled)\r
500         {\r
501                 IsEnabled = enabled;\r
502         }\r
503 \r
504 \r
505         //! Sets the new caption of this element.\r
506         virtual void setText(const wchar_t* text)\r
507         {\r
508                 Text = text;\r
509         }\r
510 \r
511 \r
512         //! Returns caption of this element.\r
513         virtual const wchar_t* getText() const\r
514         {\r
515                 return Text.c_str();\r
516         }\r
517 \r
518 \r
519         //! Sets the new caption of this element.\r
520         virtual void setToolTipText(const wchar_t* text)\r
521         {\r
522                 ToolTipText = text;\r
523         }\r
524 \r
525 \r
526         //! Returns caption of this element.\r
527         virtual const core::stringw& getToolTipText() const\r
528         {\r
529                 return ToolTipText;\r
530         }\r
531 \r
532 \r
533         //! Returns id. Can be used to identify the element.\r
534         virtual s32 getID() const\r
535         {\r
536                 return ID;\r
537         }\r
538 \r
539 \r
540         //! Sets the id of this element\r
541         virtual void setID(s32 id)\r
542         {\r
543                 ID = id;\r
544         }\r
545 \r
546 \r
547         //! Called if an event happened.\r
548         bool OnEvent(const SEvent& event) override\r
549         {\r
550                 return Parent ? Parent->OnEvent(event) : false;\r
551         }\r
552 \r
553 \r
554         //! Brings a child to front\r
555         /** \return True if successful, false if not. */\r
556         virtual bool bringToFront(IGUIElement* child)\r
557         {\r
558                 if (child->Parent != this)\r
559                         return false;\r
560                 if (std::next(child->ParentPos) == Children.end()) // already there\r
561                         return true;\r
562                 Children.erase(child->ParentPos);\r
563                 child->ParentPos = Children.insert(Children.end(), child);\r
564                 return true;\r
565         }\r
566 \r
567 \r
568         //! Moves a child to the back, so it's siblings are drawn on top of it\r
569         /** \return True if successful, false if not. */\r
570         virtual bool sendToBack(IGUIElement* child)\r
571         {\r
572                 if (child->Parent != this)\r
573                         return false;\r
574                 if (child->ParentPos == Children.begin()) // already there\r
575                         return true;\r
576                 Children.erase(child->ParentPos);\r
577                 child->ParentPos = Children.insert(Children.begin(), child);\r
578                 return true;\r
579         }\r
580 \r
581         //! Returns list with children of this element\r
582         virtual const std::list<IGUIElement*>& getChildren() const\r
583         {\r
584                 return Children;\r
585         }\r
586 \r
587 \r
588         //! Finds the first element with the given id.\r
589         /** \param id: Id to search for.\r
590         \param searchchildren: Set this to true, if also children of this\r
591         element may contain the element with the searched id and they\r
592         should be searched too.\r
593         \return Returns the first element with the given id. If no element\r
594         with this id was found, 0 is returned. */\r
595         virtual IGUIElement* getElementFromId(s32 id, bool searchchildren=false) const\r
596         {\r
597                 IGUIElement* e = 0;\r
598 \r
599                 for (auto child : Children)\r
600                 {\r
601                         if (child->getID() == id)\r
602                                 return child;\r
603 \r
604                         if (searchchildren)\r
605                                 e = child->getElementFromId(id, true);\r
606 \r
607                         if (e)\r
608                                 return e;\r
609                 }\r
610 \r
611                 return e;\r
612         }\r
613 \r
614 \r
615         //! returns true if the given element is a child of this one.\r
616         //! \param child: The child element to check\r
617         bool isMyChild(IGUIElement* child) const\r
618         {\r
619                 if (!child)\r
620                         return false;\r
621                 do\r
622                 {\r
623                         if (child->Parent)\r
624                                 child = child->Parent;\r
625 \r
626                 } while (child->Parent && child != this);\r
627 \r
628 \r
629                 return child == this;\r
630         }\r
631 \r
632 \r
633         //! searches elements to find the closest next element to tab to\r
634         /** \param startOrder: The TabOrder of the current element, -1 if none\r
635         \param reverse: true if searching for a lower number\r
636         \param group: true if searching for a higher one\r
637         \param first: element with the highest/lowest known tab order depending on search direction\r
638         \param closest: the closest match, depending on tab order and direction\r
639         \param includeInvisible: includes invisible elements in the search (default=false)\r
640         \param includeDisabled: includes disabled elements in the search (default=false)\r
641         \return true if successfully found an element, false to continue searching/fail */\r
642         bool getNextElement(s32 startOrder, bool reverse, bool group,\r
643                 IGUIElement*& first, IGUIElement*& closest, bool includeInvisible=false,\r
644                 bool includeDisabled=false) const\r
645         {\r
646                 // we'll stop searching if we find this number\r
647                 s32 wanted = startOrder + ( reverse ? -1 : 1 );\r
648                 if (wanted==-2)\r
649                         wanted = 1073741824; // maximum s32\r
650 \r
651                 auto it = Children.begin();\r
652 \r
653                 s32 closestOrder, currentOrder;\r
654 \r
655                 while(it != Children.end())\r
656                 {\r
657                         // ignore invisible elements and their children\r
658                         if ( ( (*it)->isVisible() || includeInvisible ) &&\r
659                                 (group == true || (*it)->isTabGroup() == false) )\r
660                         {\r
661                                 // ignore disabled, but children are checked (disabled is currently per element ignoring parent states)\r
662                                 if ( (*it)->isEnabled() || includeDisabled )\r
663                                 {\r
664                                         // only check tab stops and those with the same group status\r
665                                         if ((*it)->isTabStop() && ((*it)->isTabGroup() == group))\r
666                                         {\r
667                                                 currentOrder = (*it)->getTabOrder();\r
668 \r
669                                                 // is this what we're looking for?\r
670                                                 if (currentOrder == wanted)\r
671                                                 {\r
672                                                         closest = *it;\r
673                                                         return true;\r
674                                                 }\r
675 \r
676                                                 // is it closer than the current closest?\r
677                                                 if (closest)\r
678                                                 {\r
679                                                         closestOrder = closest->getTabOrder();\r
680                                                         if ( ( reverse && currentOrder > closestOrder && currentOrder < startOrder)\r
681                                                                 ||(!reverse && currentOrder < closestOrder && currentOrder > startOrder))\r
682                                                         {\r
683                                                                 closest = *it;\r
684                                                         }\r
685                                                 }\r
686                                                 else\r
687                                                 if ( (reverse && currentOrder < startOrder) || (!reverse && currentOrder > startOrder) )\r
688                                                 {\r
689                                                         closest = *it;\r
690                                                 }\r
691 \r
692                                                 // is it before the current first?\r
693                                                 if (first)\r
694                                                 {\r
695                                                         closestOrder = first->getTabOrder();\r
696 \r
697                                                         if ( (reverse && closestOrder < currentOrder) || (!reverse && closestOrder > currentOrder) )\r
698                                                         {\r
699                                                                 first = *it;\r
700                                                         }\r
701                                                 }\r
702                                                 else\r
703                                                 {\r
704                                                         first = *it;\r
705                                                 }\r
706                                         }\r
707                                 }\r
708                                 // search within children\r
709                                 if ((*it)->getNextElement(startOrder, reverse, group, first, closest, includeInvisible, includeDisabled))\r
710                                 {\r
711                                         return true;\r
712                                 }\r
713                         }\r
714                         ++it;\r
715                 }\r
716                 return false;\r
717         }\r
718 \r
719 \r
720         //! Returns the type of the gui element.\r
721         /** This is needed for the .NET wrapper but will be used\r
722         later for serializing and deserializing.\r
723         If you wrote your own GUIElements, you need to set the type for your element as first parameter\r
724         in the constructor of IGUIElement. For own (=unknown) elements, simply use EGUIET_ELEMENT as type */\r
725         EGUI_ELEMENT_TYPE getType() const\r
726         {\r
727                 return Type;\r
728         }\r
729 \r
730         //! Returns true if the gui element supports the given type.\r
731         /** This is mostly used to check if you can cast a gui element to the class that goes with the type.\r
732         Most gui elements will only support their own type, but if you derive your own classes from interfaces\r
733         you can overload this function and add a check for the type of the base-class additionally.\r
734         This allows for checks comparable to the dynamic_cast of c++ with enabled rtti.\r
735         Note that you can't do that by calling BaseClass::hasType(type), but you have to do an explicit\r
736         comparison check, because otherwise the base class usually just checks for the member variable\r
737         Type which contains the type of your derived class.\r
738         */\r
739         virtual bool hasType(EGUI_ELEMENT_TYPE type) const\r
740         {\r
741                 return type == Type;\r
742         }\r
743 \r
744 \r
745         //! Returns the type name of the gui element.\r
746         /** This is needed serializing elements. */\r
747         virtual const c8* getTypeName() const\r
748         {\r
749                 return GUIElementTypeNames[Type];\r
750         }\r
751 \r
752         //! Returns the name of the element.\r
753         /** \return Name as character string. */\r
754         virtual const c8* getName() const\r
755         {\r
756                 return Name.c_str();\r
757         }\r
758 \r
759 \r
760         //! Sets the name of the element.\r
761         /** \param name New name of the gui element. */\r
762         virtual void setName(const c8* name)\r
763         {\r
764                 Name = name;\r
765         }\r
766 \r
767 \r
768         //! Sets the name of the element.\r
769         /** \param name New name of the gui element. */\r
770         virtual void setName(const core::stringc& name)\r
771         {\r
772                 Name = name;\r
773         }\r
774 \r
775 \r
776         //! Returns whether the element takes input from the IME\r
777         virtual bool acceptsIME()\r
778         {\r
779                 return false;\r
780         }\r
781 \r
782 \r
783 protected:\r
784         // not virtual because needed in constructor\r
785         void addChildToEnd(IGUIElement* child)\r
786         {\r
787                 if (child)\r
788                 {\r
789                         child->grab(); // prevent destruction when removed\r
790                         child->remove(); // remove from old parent\r
791                         child->LastParentRect = getAbsolutePosition();\r
792                         child->Parent = this;\r
793                         child->ParentPos = Children.insert(Children.end(), child);\r
794                 }\r
795         }\r
796 \r
797 #ifndef NDEBUG\r
798         template<typename Iterator>\r
799         static size_t _fastSetChecksum(Iterator begin, Iterator end) {\r
800                 std::hash<typename Iterator::value_type> hasher;\r
801                 size_t checksum = 0;\r
802                 for (Iterator it = begin; it != end; ++it) {\r
803                         size_t h = hasher(*it);\r
804                         checksum ^= 966073049 + (h * 3432918353) + ((h >> 16) * 461845907);\r
805                 }\r
806                 return checksum;\r
807         }\r
808 #endif\r
809 \r
810         // Reorder children [from, to) to the order given by `neworder`\r
811         void reorderChildren(\r
812                 std::list<IGUIElement*>::iterator from,\r
813                 std::list<IGUIElement*>::iterator to,\r
814                 const std::vector<IGUIElement*> &neworder)\r
815         {\r
816                 assert(_fastSetChecksum(from, to) == _fastSetChecksum(neworder.begin(), neworder.end()));\r
817                 for (auto e : neworder)\r
818                 {\r
819                         *from = e;\r
820                         e->ParentPos = from;\r
821                         ++from;\r
822                 }\r
823                 assert(from == to);\r
824         }\r
825 \r
826 \r
827         // not virtual because needed in constructor\r
828         void recalculateAbsolutePosition(bool recursive)\r
829         {\r
830                 core::rect<s32> parentAbsolute(0,0,0,0);\r
831                 core::rect<s32> parentAbsoluteClip;\r
832                 f32 fw=0.f, fh=0.f;\r
833 \r
834                 if (Parent)\r
835                 {\r
836                         parentAbsolute = Parent->AbsoluteRect;\r
837 \r
838                         if (NoClip)\r
839                         {\r
840                                 IGUIElement* p=this;\r
841                                 while (p->Parent)\r
842                                         p = p->Parent;\r
843                                 parentAbsoluteClip = p->AbsoluteClippingRect;\r
844                         }\r
845                         else\r
846                                 parentAbsoluteClip = Parent->AbsoluteClippingRect;\r
847                 }\r
848 \r
849                 const s32 diffx = parentAbsolute.getWidth() - LastParentRect.getWidth();\r
850                 const s32 diffy = parentAbsolute.getHeight() - LastParentRect.getHeight();\r
851 \r
852                 if (AlignLeft == EGUIA_SCALE || AlignRight == EGUIA_SCALE)\r
853                         fw = (f32)parentAbsolute.getWidth();\r
854 \r
855                 if (AlignTop == EGUIA_SCALE || AlignBottom == EGUIA_SCALE)\r
856                         fh = (f32)parentAbsolute.getHeight();\r
857 \r
858                 switch (AlignLeft)\r
859                 {\r
860                         case EGUIA_UPPERLEFT:\r
861                                 break;\r
862                         case EGUIA_LOWERRIGHT:\r
863                                 DesiredRect.UpperLeftCorner.X += diffx;\r
864                                 break;\r
865                         case EGUIA_CENTER:\r
866                                 DesiredRect.UpperLeftCorner.X += diffx/2;\r
867                                 break;\r
868                         case EGUIA_SCALE:\r
869                                 DesiredRect.UpperLeftCorner.X = core::round32(ScaleRect.UpperLeftCorner.X * fw);\r
870                                 break;\r
871                 }\r
872 \r
873                 switch (AlignRight)\r
874                 {\r
875                         case EGUIA_UPPERLEFT:\r
876                                 break;\r
877                         case EGUIA_LOWERRIGHT:\r
878                                 DesiredRect.LowerRightCorner.X += diffx;\r
879                                 break;\r
880                         case EGUIA_CENTER:\r
881                                 DesiredRect.LowerRightCorner.X += diffx/2;\r
882                                 break;\r
883                         case EGUIA_SCALE:\r
884                                 DesiredRect.LowerRightCorner.X = core::round32(ScaleRect.LowerRightCorner.X * fw);\r
885                                 break;\r
886                 }\r
887 \r
888                 switch (AlignTop)\r
889                 {\r
890                         case EGUIA_UPPERLEFT:\r
891                                 break;\r
892                         case EGUIA_LOWERRIGHT:\r
893                                 DesiredRect.UpperLeftCorner.Y += diffy;\r
894                                 break;\r
895                         case EGUIA_CENTER:\r
896                                 DesiredRect.UpperLeftCorner.Y += diffy/2;\r
897                                 break;\r
898                         case EGUIA_SCALE:\r
899                                 DesiredRect.UpperLeftCorner.Y = core::round32(ScaleRect.UpperLeftCorner.Y * fh);\r
900                                 break;\r
901                 }\r
902 \r
903                 switch (AlignBottom)\r
904                 {\r
905                         case EGUIA_UPPERLEFT:\r
906                                 break;\r
907                         case EGUIA_LOWERRIGHT:\r
908                                 DesiredRect.LowerRightCorner.Y += diffy;\r
909                                 break;\r
910                         case EGUIA_CENTER:\r
911                                 DesiredRect.LowerRightCorner.Y += diffy/2;\r
912                                 break;\r
913                         case EGUIA_SCALE:\r
914                                 DesiredRect.LowerRightCorner.Y = core::round32(ScaleRect.LowerRightCorner.Y * fh);\r
915                                 break;\r
916                 }\r
917 \r
918                 RelativeRect = DesiredRect;\r
919 \r
920                 const s32 w = RelativeRect.getWidth();\r
921                 const s32 h = RelativeRect.getHeight();\r
922 \r
923                 // make sure the desired rectangle is allowed\r
924                 if (w < (s32)MinSize.Width)\r
925                         RelativeRect.LowerRightCorner.X = RelativeRect.UpperLeftCorner.X + MinSize.Width;\r
926                 if (h < (s32)MinSize.Height)\r
927                         RelativeRect.LowerRightCorner.Y = RelativeRect.UpperLeftCorner.Y + MinSize.Height;\r
928                 if (MaxSize.Width && w > (s32)MaxSize.Width)\r
929                         RelativeRect.LowerRightCorner.X = RelativeRect.UpperLeftCorner.X + MaxSize.Width;\r
930                 if (MaxSize.Height && h > (s32)MaxSize.Height)\r
931                         RelativeRect.LowerRightCorner.Y = RelativeRect.UpperLeftCorner.Y + MaxSize.Height;\r
932 \r
933                 RelativeRect.repair();\r
934 \r
935                 AbsoluteRect = RelativeRect + parentAbsolute.UpperLeftCorner;\r
936 \r
937                 if (!Parent)\r
938                         parentAbsoluteClip = AbsoluteRect;\r
939 \r
940                 AbsoluteClippingRect = AbsoluteRect;\r
941                 AbsoluteClippingRect.clipAgainst(parentAbsoluteClip);\r
942 \r
943                 LastParentRect = parentAbsolute;\r
944 \r
945                 if ( recursive )\r
946                 {\r
947                         // update all children\r
948                         for (auto child : Children)\r
949                         {\r
950                                 child->recalculateAbsolutePosition(recursive);\r
951                         }\r
952                 }\r
953         }\r
954 \r
955 protected:\r
956 \r
957         //! List of all children of this element\r
958         std::list<IGUIElement*> Children;\r
959 \r
960         //! Pointer to the parent\r
961         IGUIElement* Parent;\r
962 \r
963         //! Our position in the parent list. Only valid when Parent != nullptr\r
964         std::list<IGUIElement*>::iterator ParentPos;\r
965 \r
966         //! relative rect of element\r
967         core::rect<s32> RelativeRect;\r
968 \r
969         //! absolute rect of element\r
970         core::rect<s32> AbsoluteRect;\r
971 \r
972         //! absolute clipping rect of element\r
973         core::rect<s32> AbsoluteClippingRect;\r
974 \r
975         //! the rectangle the element would prefer to be,\r
976         //! if it was not constrained by parent or max/min size\r
977         core::rect<s32> DesiredRect;\r
978 \r
979         //! for calculating the difference when resizing parent\r
980         core::rect<s32> LastParentRect;\r
981 \r
982         //! relative scale of the element inside its parent\r
983         core::rect<f32> ScaleRect;\r
984 \r
985         //! maximum and minimum size of the element\r
986         core::dimension2du MaxSize, MinSize;\r
987 \r
988         //! is visible?\r
989         bool IsVisible;\r
990 \r
991         //! is enabled?\r
992         bool IsEnabled;\r
993 \r
994         //! is a part of a larger whole and should not be serialized?\r
995         bool IsSubElement;\r
996 \r
997         //! does this element ignore its parent's clipping rectangle?\r
998         bool NoClip;\r
999 \r
1000         //! caption\r
1001         core::stringw Text;\r
1002 \r
1003         //! tooltip\r
1004         core::stringw ToolTipText;\r
1005 \r
1006         //! users can set this for identifying the element by string\r
1007         core::stringc Name;\r
1008 \r
1009         //! users can set this for identifying the element by integer\r
1010         s32 ID;\r
1011 \r
1012         //! tab stop like in windows\r
1013         bool IsTabStop;\r
1014 \r
1015         //! tab order\r
1016         s32 TabOrder;\r
1017 \r
1018         //! tab groups are containers like windows, use ctrl+tab to navigate\r
1019         bool IsTabGroup;\r
1020 \r
1021         //! tells the element how to act when its parent is resized\r
1022         EGUI_ALIGNMENT AlignLeft, AlignRight, AlignTop, AlignBottom;\r
1023 \r
1024         //! GUI Environment\r
1025         IGUIEnvironment* Environment;\r
1026 \r
1027         //! type of element\r
1028         EGUI_ELEMENT_TYPE Type;\r
1029 };\r
1030 \r
1031 \r
1032 } // end namespace gui\r
1033 } // end namespace irr\r
1034 \r
1035 #endif\r
1036 \r