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