2 // Copyright (C) 2002-2012 Nikolaus Gebhardt
\r
3 // This file is part of the "Irrlicht Engine".
\r
4 // For conditions of distribution and use, see copyright notice in irrlicht.h
\r
6 #include "CGUIEnvironment.h"
\r
8 #ifdef _IRR_COMPILE_WITH_GUI_
\r
10 #include "IVideoDriver.h"
\r
12 #include "CGUISkin.h"
\r
13 #include "CGUIButton.h"
\r
14 #include "CGUIWindow.h"
\r
15 #include "CGUIScrollBar.h"
\r
16 #include "CGUIFont.h"
\r
17 #include "CGUISpriteBank.h"
\r
18 #include "CGUIImage.h"
\r
19 #include "CGUIMeshViewer.h"
\r
20 #include "CGUICheckBox.h"
\r
21 #include "CGUIListBox.h"
\r
22 #include "CGUITreeView.h"
\r
23 #include "CGUIImageList.h"
\r
24 #include "CGUIFileOpenDialog.h"
\r
25 #include "CGUIColorSelectDialog.h"
\r
26 #include "CGUIStaticText.h"
\r
27 #include "CGUIEditBox.h"
\r
28 #include "CGUISpinBox.h"
\r
29 #include "CGUIInOutFader.h"
\r
30 #include "CGUIMessageBox.h"
\r
31 #include "CGUIModalScreen.h"
\r
32 #include "CGUITabControl.h"
\r
33 #include "CGUIContextMenu.h"
\r
34 #include "CGUIComboBox.h"
\r
35 #include "CGUIMenu.h"
\r
36 #include "CGUIToolBar.h"
\r
37 #include "CGUITable.h"
\r
38 #include "CGUIProfiler.h"
\r
40 #include "CDefaultGUIElementFactory.h"
\r
41 #include "IWriteFile.h"
\r
42 #include "IXMLWriter.h"
\r
44 #include "BuiltInFont.h"
\r
52 const wchar_t IRR_XML_FORMAT_GUI_ENV[] = L"irr_gui";
\r
53 const wchar_t IRR_XML_FORMAT_GUI_ELEMENT[] = L"element";
\r
54 const wchar_t IRR_XML_FORMAT_GUI_ELEMENT_ATTR_TYPE[] = L"type";
\r
56 const io::path CGUIEnvironment::DefaultFontName = "#DefaultFont";
\r
59 CGUIEnvironment::CGUIEnvironment(io::IFileSystem* fs, video::IVideoDriver* driver, IOSOperator* op)
\r
60 : IGUIElement(EGUIET_ROOT, 0, 0, 0, core::rect<s32>(driver ? core::dimension2d<s32>(driver->getScreenSize()) : core::dimension2d<s32>(0,0))),
\r
61 Driver(driver), Hovered(0), HoveredNoSubelement(0), Focus(0), LastHoveredMousePos(0,0), CurrentSkin(0),
\r
62 FileSystem(fs), UserReceiver(0), Operator(op), FocusFlags(EFF_SET_ON_LMOUSE_DOWN|EFF_SET_ON_TAB)
\r
74 IGUIEnvironment::setDebugName("CGUIEnvironment");
\r
78 IGUIElementFactory* factory = new CDefaultGUIElementFactory(this);
\r
79 registerGUIElementFactory(factory);
\r
84 IGUISkin* skin = createSkin( gui::EGST_WINDOWS_METALLIC );
\r
88 //set tooltip default
\r
89 ToolTip.LastTime = 0;
\r
90 ToolTip.EnterTime = 0;
\r
91 ToolTip.LaunchTime = 1000;
\r
92 ToolTip.RelaunchTime = 500;
\r
93 ToolTip.Element = 0;
\r
95 // environment is root tab group
\r
102 CGUIEnvironment::~CGUIEnvironment()
\r
104 clearDeletionQueue();
\r
106 if ( HoveredNoSubelement && HoveredNoSubelement != this )
\r
108 HoveredNoSubelement->drop();
\r
109 HoveredNoSubelement = 0;
\r
112 if (Hovered && Hovered != this)
\r
124 if (ToolTip.Element)
\r
126 ToolTip.Element->drop();
\r
127 ToolTip.Element = 0;
\r
133 CurrentSkin->drop();
\r
139 // delete all sprite banks
\r
140 for (i=0; i<Banks.size(); ++i)
\r
142 Banks[i].Bank->drop();
\r
144 // delete all fonts
\r
145 for (i=0; i<Fonts.size(); ++i)
\r
146 Fonts[i].Font->drop();
\r
148 // remove all factories
\r
149 for (i=0; i<GUIElementFactoryList.size(); ++i)
\r
150 GUIElementFactoryList[i]->drop();
\r
160 FileSystem->drop();
\r
172 void CGUIEnvironment::loadBuiltInFont()
\r
174 io::IReadFile* file = FileSystem->createMemoryReadFile(BuiltInFontData,
\r
175 BuiltInFontDataSize, DefaultFontName, false);
\r
177 CGUIFont* font = new CGUIFont(this, DefaultFontName );
\r
178 if (!font->load(file))
\r
180 os::Printer::log("Error: Could not load built-in Font. Did you compile without the BMP loader?", ELL_ERROR);
\r
187 f.NamedPath.setPath(DefaultFontName);
\r
189 Fonts.push_back(f);
\r
195 //! draws all gui elements
\r
196 void CGUIEnvironment::drawAll(bool useScreenSize)
\r
198 if (useScreenSize && Driver)
\r
200 core::dimension2d<s32> dim(Driver->getScreenSize());
\r
201 if (AbsoluteRect.LowerRightCorner.X != dim.Width ||
\r
202 AbsoluteRect.UpperLeftCorner.X != 0 ||
\r
203 AbsoluteRect.LowerRightCorner.Y != dim.Height ||
\r
204 AbsoluteRect.UpperLeftCorner.Y != 0
\r
207 setRelativePosition(core::recti(0,0,dim.Width, dim.Height));
\r
211 // make sure tooltip is always on top
\r
212 if (ToolTip.Element)
\r
213 bringToFront(ToolTip.Element);
\r
216 OnPostRender ( os::Timer::getTime () );
\r
218 clearDeletionQueue();
\r
222 //! sets the focus to an element
\r
223 bool CGUIEnvironment::setFocus(IGUIElement* element)
\r
225 if (Focus == element)
\r
230 // GUI Environment should just reset the focus to 0
\r
231 if (element == this)
\r
234 // stop element from being deleted
\r
238 // focus may change or be removed in this call
\r
239 IGUIElement *currentFocus = 0;
\r
242 currentFocus = Focus;
\r
243 currentFocus->grab();
\r
245 e.EventType = EET_GUI_EVENT;
\r
246 e.GUIEvent.Caller = Focus;
\r
247 e.GUIEvent.Element = element;
\r
248 e.GUIEvent.EventType = EGET_ELEMENT_FOCUS_LOST;
\r
249 if (Focus->OnEvent(e))
\r
253 currentFocus->drop();
\r
256 currentFocus->drop();
\r
262 currentFocus = Focus;
\r
264 currentFocus->grab();
\r
266 // send focused event
\r
268 e.EventType = EET_GUI_EVENT;
\r
269 e.GUIEvent.Caller = element;
\r
270 e.GUIEvent.Element = Focus;
\r
271 e.GUIEvent.EventType = EGET_ELEMENT_FOCUSED;
\r
272 if (element->OnEvent(e))
\r
277 currentFocus->drop();
\r
283 currentFocus->drop();
\r
288 // element is the new focus so it doesn't have to be dropped
\r
295 //! returns the element with the focus
\r
296 IGUIElement* CGUIEnvironment::getFocus() const
\r
301 //! returns the element last known to be under the mouse cursor
\r
302 IGUIElement* CGUIEnvironment::getHovered() const
\r
308 //! removes the focus from an element
\r
309 bool CGUIEnvironment::removeFocus(IGUIElement* element)
\r
311 if (Focus && Focus==element)
\r
314 e.EventType = EET_GUI_EVENT;
\r
315 e.GUIEvent.Caller = Focus;
\r
316 e.GUIEvent.Element = 0;
\r
317 e.GUIEvent.EventType = EGET_ELEMENT_FOCUS_LOST;
\r
318 if (Focus->OnEvent(e))
\r
333 //! Returns whether the element has focus
\r
334 bool CGUIEnvironment::hasFocus(const IGUIElement* element, bool checkSubElements) const
\r
336 if (element == Focus)
\r
339 if ( !checkSubElements || !element )
\r
342 IGUIElement* f = Focus;
\r
343 while ( f && f->isSubElement() )
\r
345 f = f->getParent();
\r
346 if ( f == element )
\r
353 //! returns the current video driver
\r
354 video::IVideoDriver* CGUIEnvironment::getVideoDriver() const
\r
360 //! returns the current file system
\r
361 io::IFileSystem* CGUIEnvironment::getFileSystem() const
\r
367 //! returns a pointer to the OS operator
\r
368 IOSOperator* CGUIEnvironment::getOSOperator() const
\r
374 //! clear all GUI elements
\r
375 void CGUIEnvironment::clear()
\r
377 // Remove the focus
\r
384 if (Hovered && Hovered != this)
\r
389 if ( HoveredNoSubelement && HoveredNoSubelement != this)
\r
391 HoveredNoSubelement->drop();
\r
392 HoveredNoSubelement = 0;
\r
395 // get the root's children in case the root changes in future
\r
396 const core::list<IGUIElement*>& children = getRootGUIElement()->getChildren();
\r
398 while (!children.empty())
\r
399 (*children.getLast())->remove();
\r
403 //! called by ui if an event happened.
\r
404 bool CGUIEnvironment::OnEvent(const SEvent& event)
\r
409 && (event.EventType != EET_MOUSE_INPUT_EVENT)
\r
410 && (event.EventType != EET_KEY_INPUT_EVENT)
\r
411 && (event.EventType != EET_GUI_EVENT || event.GUIEvent.Caller != this))
\r
413 ret = UserReceiver->OnEvent(event);
\r
420 void CGUIEnvironment::OnPostRender( u32 time )
\r
423 if ( ToolTip.Element == 0 &&
\r
424 HoveredNoSubelement && HoveredNoSubelement != this &&
\r
425 (time - ToolTip.EnterTime >= ToolTip.LaunchTime
\r
426 || (time - ToolTip.LastTime >= ToolTip.RelaunchTime && time - ToolTip.LastTime < ToolTip.LaunchTime)) &&
\r
427 HoveredNoSubelement->getToolTipText().size() &&
\r
429 getSkin()->getFont(EGDF_TOOLTIP)
\r
432 core::rect<s32> pos;
\r
434 pos.UpperLeftCorner = LastHoveredMousePos;
\r
435 core::dimension2du dim = getSkin()->getFont(EGDF_TOOLTIP)->getDimension(HoveredNoSubelement->getToolTipText().c_str());
\r
436 dim.Width += getSkin()->getSize(EGDS_TEXT_DISTANCE_X)*2;
\r
437 dim.Height += getSkin()->getSize(EGDS_TEXT_DISTANCE_Y)*2;
\r
439 pos.UpperLeftCorner.Y -= dim.Height+1;
\r
440 pos.LowerRightCorner.Y = pos.UpperLeftCorner.Y + dim.Height-1;
\r
441 pos.LowerRightCorner.X = pos.UpperLeftCorner.X + dim.Width;
\r
443 pos.constrainTo(getAbsolutePosition());
\r
445 ToolTip.Element = addStaticText(HoveredNoSubelement->getToolTipText().c_str(), pos, true, true, this, -1, true);
\r
446 ToolTip.Element->setOverrideColor(getSkin()->getColor(EGDC_TOOLTIP));
\r
447 ToolTip.Element->setBackgroundColor(getSkin()->getColor(EGDC_TOOLTIP_BACKGROUND));
\r
448 ToolTip.Element->setOverrideFont(getSkin()->getFont(EGDF_TOOLTIP));
\r
449 ToolTip.Element->setSubElement(true);
\r
450 ToolTip.Element->grab();
\r
452 s32 textHeight = ToolTip.Element->getTextHeight();
\r
453 pos = ToolTip.Element->getRelativePosition();
\r
454 pos.LowerRightCorner.Y = pos.UpperLeftCorner.Y + textHeight;
\r
455 ToolTip.Element->setRelativePosition(pos);
\r
458 if (ToolTip.Element && ToolTip.Element->isVisible() ) // (isVisible() check only because we might use visibility for ToolTip one day)
\r
460 ToolTip.LastTime = time;
\r
462 // got invisible or removed in the meantime?
\r
463 if ( !HoveredNoSubelement ||
\r
464 !HoveredNoSubelement->isVisible() ||
\r
465 !HoveredNoSubelement->getParent()
\r
466 ) // got invisible or removed in the meantime?
\r
468 ToolTip.Element->remove();
\r
469 ToolTip.Element->drop();
\r
470 ToolTip.Element = 0;
\r
474 IGUIElement::OnPostRender ( time );
\r
477 void CGUIEnvironment::addToDeletionQueue(IGUIElement* element)
\r
483 DeletionQueue.push_back(element);
\r
486 void CGUIEnvironment::clearDeletionQueue()
\r
488 if (DeletionQueue.empty())
\r
491 for (u32 i=0; i<DeletionQueue.size(); ++i)
\r
493 DeletionQueue[i]->remove();
\r
494 DeletionQueue[i]->drop();
\r
497 DeletionQueue.clear();
\r
501 void CGUIEnvironment::updateHoveredElement(core::position2d<s32> mousePos)
\r
503 IGUIElement* lastHovered = Hovered;
\r
504 IGUIElement* lastHoveredNoSubelement = HoveredNoSubelement;
\r
505 LastHoveredMousePos = mousePos;
\r
507 Hovered = getElementFromPoint(mousePos);
\r
509 if ( ToolTip.Element && Hovered == ToolTip.Element )
\r
511 // When the mouse is over the ToolTip we remove that so it will be re-created at a new position.
\r
512 // Note that ToolTip.EnterTime does not get changed here, so it will be re-created at once.
\r
513 ToolTip.Element->remove();
\r
514 ToolTip.Element->drop();
\r
515 ToolTip.Element = 0;
\r
517 // Get the real Hovered
\r
518 Hovered = getElementFromPoint(mousePos);
\r
521 // for tooltips we want the element itself and not some of it's subelements
\r
522 HoveredNoSubelement = Hovered;
\r
523 while ( HoveredNoSubelement && HoveredNoSubelement->isSubElement() )
\r
525 HoveredNoSubelement = HoveredNoSubelement->getParent();
\r
528 if (Hovered && Hovered != this)
\r
530 if ( HoveredNoSubelement && HoveredNoSubelement != this)
\r
531 HoveredNoSubelement->grab();
\r
533 if (Hovered != lastHovered)
\r
536 event.EventType = EET_GUI_EVENT;
\r
540 event.GUIEvent.Caller = lastHovered;
\r
541 event.GUIEvent.Element = 0;
\r
542 event.GUIEvent.EventType = EGET_ELEMENT_LEFT;
\r
543 lastHovered->OnEvent(event);
\r
548 event.GUIEvent.Caller = Hovered;
\r
549 event.GUIEvent.Element = Hovered;
\r
550 event.GUIEvent.EventType = EGET_ELEMENT_HOVERED;
\r
551 Hovered->OnEvent(event);
\r
555 if ( lastHoveredNoSubelement != HoveredNoSubelement )
\r
557 if (ToolTip.Element)
\r
559 ToolTip.Element->remove();
\r
560 ToolTip.Element->drop();
\r
561 ToolTip.Element = 0;
\r
564 if ( HoveredNoSubelement )
\r
566 u32 now = os::Timer::getTime();
\r
567 ToolTip.EnterTime = now;
\r
571 if (lastHovered && lastHovered != this)
\r
572 lastHovered->drop();
\r
573 if (lastHoveredNoSubelement && lastHoveredNoSubelement != this)
\r
574 lastHoveredNoSubelement->drop();
\r
578 //! This sets a new event receiver for gui events. Usually you do not have to
\r
579 //! use this method, it is used by the internal engine.
\r
580 void CGUIEnvironment::setUserEventReceiver(IEventReceiver* evr)
\r
582 UserReceiver = evr;
\r
586 //! posts an input event to the environment
\r
587 bool CGUIEnvironment::postEventFromUser(const SEvent& event)
\r
589 switch(event.EventType)
\r
591 case EET_GUI_EVENT:
\r
593 // hey, why is the user sending gui events..?
\r
597 case EET_MOUSE_INPUT_EVENT:
\r
599 updateHoveredElement(core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y));
\r
601 if ( Hovered != Focus )
\r
603 IGUIElement * focusCandidate = Hovered;
\r
605 // Only allow enabled elements to be focused (unless EFF_CAN_FOCUS_DISABLED is set)
\r
606 if ( Hovered && !Hovered->isEnabled() && !(FocusFlags & EFF_CAN_FOCUS_DISABLED))
\r
607 focusCandidate = NULL; // we still remove focus from the active element
\r
609 // Please don't merge this into a single if clause, it's easier to debug the way it is
\r
610 if (FocusFlags & EFF_SET_ON_LMOUSE_DOWN &&
\r
611 event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN )
\r
613 setFocus(focusCandidate);
\r
615 else if ( FocusFlags & EFF_SET_ON_RMOUSE_DOWN &&
\r
616 event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN )
\r
618 setFocus(focusCandidate);
\r
620 else if ( FocusFlags & EFF_SET_ON_MOUSE_OVER &&
\r
621 event.MouseInput.Event == EMIE_MOUSE_MOVED )
\r
623 setFocus(focusCandidate);
\r
627 // sending input to focus
\r
628 if (Focus && Focus->OnEvent(event))
\r
631 // focus could have died in last call
\r
632 if (!Focus && Hovered)
\r
634 return Hovered->OnEvent(event);
\r
638 case EET_KEY_INPUT_EVENT:
\r
640 if (Focus && Focus->OnEvent(event))
\r
643 // For keys we handle the event before changing focus to give elements the chance for catching the TAB
\r
644 // Send focus changing event
\r
645 if (FocusFlags & EFF_SET_ON_TAB &&
\r
646 event.EventType == EET_KEY_INPUT_EVENT &&
\r
647 event.KeyInput.PressedDown &&
\r
648 event.KeyInput.Key == KEY_TAB)
\r
650 IGUIElement *next = getNextElement(event.KeyInput.Shift, event.KeyInput.Control);
\r
651 if (next && next != Focus)
\r
653 if (setFocus(next))
\r
667 //! returns the current gui skin
\r
668 IGUISkin* CGUIEnvironment::getSkin() const
\r
670 return CurrentSkin;
\r
674 //! Sets a new GUI Skin
\r
675 void CGUIEnvironment::setSkin(IGUISkin* skin)
\r
677 if (CurrentSkin==skin)
\r
681 CurrentSkin->drop();
\r
683 CurrentSkin = skin;
\r
686 CurrentSkin->grab();
\r
690 //! Creates a new GUI Skin based on a template.
\r
691 /** \return Returns a pointer to the created skin.
\r
692 If you no longer need the skin, you should call IGUISkin::drop().
\r
693 See IReferenceCounted::drop() for more information. */
\r
694 IGUISkin* CGUIEnvironment::createSkin(EGUI_SKIN_TYPE type)
\r
696 IGUISkin* skin = new CGUISkin(type, Driver);
\r
698 IGUIFont* builtinfont = getBuiltInFont();
\r
699 IGUIFontBitmap* bitfont = 0;
\r
700 if (builtinfont && builtinfont->getType() == EGFT_BITMAP)
\r
701 bitfont = (IGUIFontBitmap*)builtinfont;
\r
703 IGUISpriteBank* bank = 0;
\r
704 skin->setFont(builtinfont);
\r
707 bank = bitfont->getSpriteBank();
\r
709 skin->setSpriteBank(bank);
\r
715 //! Returns the default element factory which can create all built in elements
\r
716 IGUIElementFactory* CGUIEnvironment::getDefaultGUIElementFactory() const
\r
718 return getGUIElementFactory(0);
\r
722 //! Adds an element factory to the gui environment.
\r
723 /** Use this to extend the gui environment with new element types which it should be
\r
724 able to create automatically, for example when loading data from xml files. */
\r
725 void CGUIEnvironment::registerGUIElementFactory(IGUIElementFactory* factoryToAdd)
\r
729 factoryToAdd->grab();
\r
730 GUIElementFactoryList.push_back(factoryToAdd);
\r
735 //! Returns amount of registered scene node factories.
\r
736 u32 CGUIEnvironment::getRegisteredGUIElementFactoryCount() const
\r
738 return GUIElementFactoryList.size();
\r
742 //! Returns a scene node factory by index
\r
743 IGUIElementFactory* CGUIEnvironment::getGUIElementFactory(u32 index) const
\r
745 if (index < GUIElementFactoryList.size())
\r
746 return GUIElementFactoryList[index];
\r
752 //! adds a GUI Element using its name
\r
753 IGUIElement* CGUIEnvironment::addGUIElement(const c8* elementName, IGUIElement* parent)
\r
755 IGUIElement* node=0;
\r
760 for (s32 i=GUIElementFactoryList.size()-1; i>=0 && !node; --i)
\r
761 node = GUIElementFactoryList[i]->addGUIElement(elementName, parent);
\r
768 //! Saves the current gui into a file.
\r
769 //! \param filename: Name of the file .
\r
770 bool CGUIEnvironment::saveGUI(const io::path& filename, IGUIElement* start)
\r
772 io::IWriteFile* file = FileSystem->createAndWriteFile(filename);
\r
778 bool ret = saveGUI(file, start);
\r
784 //! Saves the current gui into a file.
\r
785 bool CGUIEnvironment::saveGUI(io::IWriteFile* file, IGUIElement* start)
\r
792 io::IXMLWriter* writer = FileSystem->createXMLWriter(file);
\r
798 writer->writeXMLHeader();
\r
799 writeGUIElement(writer, start ? start : this);
\r
806 //! Loads the gui. Note that the current gui is not cleared before.
\r
807 //! \param filename: Name of the file.
\r
808 bool CGUIEnvironment::loadGUI(const io::path& filename, IGUIElement* parent)
\r
810 io::IReadFile* read = FileSystem->createAndOpenFile(filename);
\r
813 os::Printer::log("Unable to open gui file", filename, ELL_ERROR);
\r
817 bool ret = loadGUI(read, parent);
\r
824 //! Loads the gui. Note that the current gui is not cleared before.
\r
825 bool CGUIEnvironment::loadGUI(io::IReadFile* file, IGUIElement* parent)
\r
829 os::Printer::log("Unable to open GUI file", ELL_ERROR);
\r
833 io::IXMLReader* reader = FileSystem->createXMLReader(file);
\r
836 os::Printer::log("GUI is not a valid XML file", file->getFileName(), ELL_ERROR);
\r
841 while(reader->read())
\r
843 readGUIElement(reader, parent);
\r
853 //! reads an element
\r
854 void CGUIEnvironment::readGUIElement(io::IXMLReader* reader, IGUIElement* node)
\r
859 io::EXML_NODE nodeType = reader->getNodeType();
\r
861 if (nodeType == io::EXN_NONE || nodeType == io::EXN_UNKNOWN || nodeType == io::EXN_ELEMENT_END)
\r
864 IGUIElement* deferedNode = 0;
\r
865 if (!wcscmp(IRR_XML_FORMAT_GUI_ENV, reader->getNodeName()))
\r
867 // GuiEnvironment always must be this as it would serialize into a wrong element otherwise.
\r
868 // So we use the given node next time
\r
869 if ( node && node != this )
\r
870 deferedNode = node;
\r
871 node = this; // root
\r
873 else if (!wcscmp(IRR_XML_FORMAT_GUI_ELEMENT, reader->getNodeName()))
\r
875 // find node type and create it
\r
876 const core::stringc attrName = reader->getAttributeValue(IRR_XML_FORMAT_GUI_ELEMENT_ATTR_TYPE);
\r
878 node = addGUIElement(attrName.c_str(), node);
\r
881 os::Printer::log("Could not create GUI element of unknown type", attrName.c_str());
\r
886 while(reader->read())
\r
888 bool endreached = false;
\r
890 switch (reader->getNodeType())
\r
892 case io::EXN_ELEMENT_END:
\r
893 if (!wcscmp(IRR_XML_FORMAT_GUI_ELEMENT, reader->getNodeName()) ||
\r
894 !wcscmp(IRR_XML_FORMAT_GUI_ENV, reader->getNodeName()))
\r
899 case io::EXN_ELEMENT:
\r
900 if (!wcscmp(L"attributes", reader->getNodeName()))
\r
903 io::IAttributes* attr = FileSystem->createEmptyAttributes(Driver);
\r
904 attr->read(reader, true);
\r
907 node->deserializeAttributes(attr);
\r
912 if (!wcscmp(IRR_XML_FORMAT_GUI_ELEMENT, reader->getNodeName()) ||
\r
913 !wcscmp(IRR_XML_FORMAT_GUI_ENV, reader->getNodeName()))
\r
916 readGUIElement(reader, deferedNode);
\r
918 readGUIElement(reader, node);
\r
922 os::Printer::log("Found unknown element in irrlicht GUI file",
\r
923 core::stringc(reader->getNodeName()).c_str());
\r
937 //! writes an element
\r
938 void CGUIEnvironment::writeGUIElement(io::IXMLWriter* writer, IGUIElement* node)
\r
940 if (!writer || !node )
\r
943 const wchar_t* name = 0;
\r
945 // write properties
\r
947 io::IAttributes* attr = FileSystem->createEmptyAttributes();
\r
948 node->serializeAttributes(attr);
\r
950 // all gui elements must have at least one attribute
\r
951 // if they have nothing then we ignore them.
\r
952 if (attr->getAttributeCount() != 0)
\r
956 name = IRR_XML_FORMAT_GUI_ENV;
\r
957 writer->writeElement(name, false);
\r
961 name = IRR_XML_FORMAT_GUI_ELEMENT;
\r
962 writer->writeElement(name, false, IRR_XML_FORMAT_GUI_ELEMENT_ATTR_TYPE,
\r
963 core::stringw(node->getTypeName()).c_str());
\r
966 writer->writeLineBreak();
\r
968 attr->write(writer);
\r
973 core::list<IGUIElement*>::ConstIterator it = node->getChildren().begin();
\r
974 for (; it != node->getChildren().end(); ++it)
\r
976 if (!(*it)->isSubElement())
\r
978 writer->writeLineBreak();
\r
979 writeGUIElement(writer, (*it));
\r
983 // write closing brace if required
\r
984 if (attr->getAttributeCount() != 0)
\r
986 writer->writeClosingTag(name);
\r
987 writer->writeLineBreak();
\r
994 //! Writes attributes of the environment
\r
995 void CGUIEnvironment::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const
\r
997 IGUISkin* skin = getSkin();
\r
1001 out->addEnum("Skin", getSkin()->getType(), GUISkinTypeNames);
\r
1002 skin->serializeAttributes(out, options);
\r
1007 //! Reads attributes of the environment
\r
1008 void CGUIEnvironment::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options)
\r
1010 if (in->existsAttribute("Skin"))
\r
1012 IGUISkin *skin = getSkin();
\r
1014 EGUI_SKIN_TYPE t = (EGUI_SKIN_TYPE) in->getAttributeAsEnumeration("Skin",GUISkinTypeNames);
\r
1015 if ( !skin || t != skin->getType())
\r
1017 skin = createSkin(t);
\r
1026 skin->deserializeAttributes(in, options);
\r
1031 RelativeRect = AbsoluteRect =
\r
1032 core::rect<s32>(Driver ? core::dimension2di(Driver->getScreenSize()) : core::dimension2d<s32>(0,0));
\r
1036 //! adds a button. The returned pointer must not be dropped.
\r
1037 IGUIButton* CGUIEnvironment::addButton(const core::rect<s32>& rectangle, IGUIElement* parent, s32 id, const wchar_t* text, const wchar_t *tooltiptext)
\r
1039 IGUIButton* button = new CGUIButton(this, parent ? parent : this, id, rectangle);
\r
1041 button->setText(text);
\r
1043 if ( tooltiptext )
\r
1044 button->setToolTipText ( tooltiptext );
\r
1051 //! adds a window. The returned pointer must not be dropped.
\r
1052 IGUIWindow* CGUIEnvironment::addWindow(const core::rect<s32>& rectangle, bool modal,
\r
1053 const wchar_t* text, IGUIElement* parent, s32 id)
\r
1055 parent = parent ? parent : this;
\r
1057 IGUIWindow* win = new CGUIWindow(this, parent, id, rectangle);
\r
1059 win->setText(text);
\r
1064 // Careful, don't just set the modal as parent above. That will mess up the focus (and is hard to change because we have to be very
\r
1065 // careful not to get virtual function call, like OnEvent, in the window.
\r
1066 CGUIModalScreen * modalScreen = new CGUIModalScreen(this, parent, -1);
\r
1067 modalScreen->drop();
\r
1068 modalScreen->addChild(win);
\r
1075 //! adds a modal screen. The returned pointer must not be dropped.
\r
1076 IGUIElement* CGUIEnvironment::addModalScreen(IGUIElement* parent)
\r
1078 parent = parent ? parent : this;
\r
1080 IGUIElement *win = new CGUIModalScreen(this, parent, -1);
\r
1087 //! Adds a message box.
\r
1088 IGUIWindow* CGUIEnvironment::addMessageBox(const wchar_t* caption, const wchar_t* text,
\r
1089 bool modal, s32 flag, IGUIElement* parent, s32 id, video::ITexture* image)
\r
1094 parent = parent ? parent : this;
\r
1096 core::rect<s32> rect;
\r
1097 core::dimension2d<u32> screenDim, msgBoxDim;
\r
1099 screenDim.Width = parent->getAbsolutePosition().getWidth();
\r
1100 screenDim.Height = parent->getAbsolutePosition().getHeight();
\r
1101 msgBoxDim.Width = 2;
\r
1102 msgBoxDim.Height = 2;
\r
1104 rect.UpperLeftCorner.X = (screenDim.Width - msgBoxDim.Width) / 2;
\r
1105 rect.UpperLeftCorner.Y = (screenDim.Height - msgBoxDim.Height) / 2;
\r
1106 rect.LowerRightCorner.X = rect.UpperLeftCorner.X + msgBoxDim.Width;
\r
1107 rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + msgBoxDim.Height;
\r
1109 IGUIWindow* win = new CGUIMessageBox(this, caption, text, flag,
\r
1110 parent, id, rect, image);
\r
1115 // Careful, don't just set the modal as parent above. That will mess up the focus (and is hard to change because we have to be very
\r
1116 // careful not to get virtual function call, like OnEvent, in the CGUIMessageBox.
\r
1117 CGUIModalScreen * modalScreen = new CGUIModalScreen(this, parent, -1);
\r
1118 modalScreen->drop();
\r
1119 modalScreen->addChild( win );
\r
1127 //! adds a scrollbar. The returned pointer must not be dropped.
\r
1128 IGUIScrollBar* CGUIEnvironment::addScrollBar(bool horizontal, const core::rect<s32>& rectangle, IGUIElement* parent, s32 id)
\r
1130 IGUIScrollBar* bar = new CGUIScrollBar(horizontal, this, parent ? parent : this, id, rectangle);
\r
1135 //! Adds a table to the environment
\r
1136 IGUITable* CGUIEnvironment::addTable(const core::rect<s32>& rectangle, IGUIElement* parent, s32 id, bool drawBackground)
\r
1138 CGUITable* b = new CGUITable(this, parent ? parent : this, id, rectangle, true, drawBackground, false);
\r
1143 //! Adds an element to display the information from the Irrlicht profiler
\r
1144 IGUIProfiler* CGUIEnvironment::addProfilerDisplay(const core::rect<s32>& rectangle, IGUIElement* parent, s32 id)
\r
1146 CGUIProfiler* p = new CGUIProfiler(this, parent ? parent : this, id, rectangle, NULL);
\r
1151 //! Adds an image element.
\r
1152 IGUIImage* CGUIEnvironment::addImage(video::ITexture* image, core::position2d<s32> pos,
\r
1153 bool useAlphaChannel, IGUIElement* parent, s32 id, const wchar_t* text)
\r
1155 core::dimension2d<s32> sz(0,0);
\r
1157 sz = core::dimension2d<s32>(image->getOriginalSize());
\r
1159 IGUIImage* img = new CGUIImage(this, parent ? parent : this,
\r
1160 id, core::rect<s32>(pos, sz));
\r
1163 img->setText(text);
\r
1165 if (useAlphaChannel)
\r
1166 img->setUseAlphaChannel(true);
\r
1169 img->setImage(image);
\r
1176 //! adds an image. The returned pointer must not be dropped.
\r
1177 IGUIImage* CGUIEnvironment::addImage(const core::rect<s32>& rectangle, IGUIElement* parent, s32 id, const wchar_t* text, bool useAlphaChannel)
\r
1179 IGUIImage* img = new CGUIImage(this, parent ? parent : this,
\r
1183 img->setText(text);
\r
1185 if ( useAlphaChannel )
\r
1186 img->setUseAlphaChannel(true);
\r
1193 //! adds an mesh viewer. The returned pointer must not be dropped.
\r
1194 IGUIMeshViewer* CGUIEnvironment::addMeshViewer(const core::rect<s32>& rectangle, IGUIElement* parent, s32 id, const wchar_t* text)
\r
1196 IGUIMeshViewer* v = new CGUIMeshViewer(this, parent ? parent : this,
\r
1207 //! adds a checkbox
\r
1208 IGUICheckBox* CGUIEnvironment::addCheckBox(bool checked, const core::rect<s32>& rectangle, IGUIElement* parent, s32 id, const wchar_t* text)
\r
1210 IGUICheckBox* b = new CGUICheckBox(checked, this,
\r
1211 parent ? parent : this , id , rectangle);
\r
1221 //! adds a list box
\r
1222 IGUIListBox* CGUIEnvironment::addListBox(const core::rect<s32>& rectangle,
\r
1223 IGUIElement* parent, s32 id, bool drawBackground)
\r
1225 IGUIListBox* b = new CGUIListBox(this, parent ? parent : this, id, rectangle,
\r
1226 true, drawBackground, false);
\r
1228 if (CurrentSkin && CurrentSkin->getSpriteBank())
\r
1230 b->setSpriteBank(CurrentSkin->getSpriteBank());
\r
1232 else if (getBuiltInFont() && getBuiltInFont()->getType() == EGFT_BITMAP)
\r
1234 b->setSpriteBank( ((IGUIFontBitmap*)getBuiltInFont())->getSpriteBank());
\r
1241 //! adds a tree view
\r
1242 IGUITreeView* CGUIEnvironment::addTreeView(const core::rect<s32>& rectangle,
\r
1243 IGUIElement* parent, s32 id,
\r
1244 bool drawBackground,
\r
1245 bool scrollBarVertical, bool scrollBarHorizontal)
\r
1247 IGUITreeView* b = new CGUITreeView(this, parent ? parent : this, id, rectangle,
\r
1248 true, drawBackground, scrollBarVertical, scrollBarHorizontal);
\r
1250 b->setIconFont ( getBuiltInFont () );
\r
1255 //! adds a file open dialog. The returned pointer must not be dropped.
\r
1256 IGUIFileOpenDialog* CGUIEnvironment::addFileOpenDialog(const wchar_t* title,
\r
1257 bool modal, IGUIElement* parent, s32 id,
\r
1258 bool restoreCWD, io::path::char_type* startDir)
\r
1260 parent = parent ? parent : this;
\r
1262 IGUIFileOpenDialog* d = new CGUIFileOpenDialog(title, this, parent, id,
\r
1263 restoreCWD, startDir);
\r
1268 // Careful, don't just set the modal as parent above. That will mess up the focus (and is hard to change because we have to be very
\r
1269 // careful not to get virtual function call, like OnEvent, in the window.
\r
1270 CGUIModalScreen * modalScreen = new CGUIModalScreen(this, parent, -1);
\r
1271 modalScreen->drop();
\r
1272 modalScreen->addChild(d);
\r
1279 //! adds a color select dialog. The returned pointer must not be dropped.
\r
1280 IGUIColorSelectDialog* CGUIEnvironment::addColorSelectDialog(const wchar_t* title,
\r
1281 bool modal, IGUIElement* parent, s32 id)
\r
1283 parent = parent ? parent : this;
\r
1285 IGUIColorSelectDialog* d = new CGUIColorSelectDialog( title,
\r
1286 this, parent, id);
\r
1291 // Careful, don't just set the modal as parent above. That will mess up the focus (and is hard to change because we have to be very
\r
1292 // careful not to get virtual function call, like OnEvent, in the window.
\r
1293 CGUIModalScreen * modalScreen = new CGUIModalScreen(this, parent, -1);
\r
1294 modalScreen->drop();
\r
1295 modalScreen->addChild(d);
\r
1302 //! adds a static text. The returned pointer must not be dropped.
\r
1303 IGUIStaticText* CGUIEnvironment::addStaticText(const wchar_t* text,
\r
1304 const core::rect<s32>& rectangle,
\r
1305 bool border, bool wordWrap,
\r
1306 IGUIElement* parent, s32 id, bool background)
\r
1308 IGUIStaticText* d = new CGUIStaticText(text, border, this,
\r
1309 parent ? parent : this, id, rectangle, background);
\r
1311 d->setWordWrap(wordWrap);
\r
1318 //! Adds an edit box. The returned pointer must not be dropped.
\r
1319 IGUIEditBox* CGUIEnvironment::addEditBox(const wchar_t* text,
\r
1320 const core::rect<s32>& rectangle, bool border,
\r
1321 IGUIElement* parent, s32 id)
\r
1323 IGUIEditBox* d = new CGUIEditBox(text, border, this,
\r
1324 parent ? parent : this, id, rectangle);
\r
1331 //! Adds a spin box to the environment
\r
1332 IGUISpinBox* CGUIEnvironment::addSpinBox(const wchar_t* text,
\r
1333 const core::rect<s32> &rectangle,
\r
1334 bool border,IGUIElement* parent, s32 id)
\r
1336 IGUISpinBox* d = new CGUISpinBox(text, border,this,
\r
1337 parent ? parent : this, id, rectangle);
\r
1344 //! Adds a tab control to the environment.
\r
1345 IGUITabControl* CGUIEnvironment::addTabControl(const core::rect<s32>& rectangle,
\r
1346 IGUIElement* parent, bool fillbackground, bool border, s32 id)
\r
1348 IGUITabControl* t = new CGUITabControl(this, parent ? parent : this,
\r
1349 rectangle, fillbackground, border, id);
\r
1355 //! Adds tab to the environment.
\r
1356 IGUITab* CGUIEnvironment::addTab(const core::rect<s32>& rectangle,
\r
1357 IGUIElement* parent, s32 id)
\r
1359 IGUITab* t = new CGUITab(this, parent ? parent : this,
\r
1366 //! Adds a context menu to the environment.
\r
1367 IGUIContextMenu* CGUIEnvironment::addContextMenu(const core::rect<s32>& rectangle,
\r
1368 IGUIElement* parent, s32 id)
\r
1370 IGUIContextMenu* c = new CGUIContextMenu(this,
\r
1371 parent ? parent : this, id, rectangle, true);
\r
1377 //! Adds a menu to the environment.
\r
1378 IGUIContextMenu* CGUIEnvironment::addMenu(IGUIElement* parent, s32 id)
\r
1383 IGUIContextMenu* c = new CGUIMenu(this,
\r
1384 parent, id, core::rect<s32>(0,0,
\r
1385 parent->getAbsolutePosition().getWidth(),
\r
1386 parent->getAbsolutePosition().getHeight()));
\r
1393 //! Adds a toolbar to the environment. It is like a menu is always placed on top
\r
1394 //! in its parent, and contains buttons.
\r
1395 IGUIToolBar* CGUIEnvironment::addToolBar(IGUIElement* parent, s32 id)
\r
1400 IGUIToolBar* b = new CGUIToolBar(this, parent, id, core::rect<s32>(0,0,10,10));
\r
1406 //! Adds an element for fading in or out.
\r
1407 IGUIInOutFader* CGUIEnvironment::addInOutFader(const core::rect<s32>* rectangle, IGUIElement* parent, s32 id)
\r
1409 core::rect<s32> rect;
\r
1412 rect = *rectangle;
\r
1414 rect = core::rect<s32>(core::dimension2di(Driver->getScreenSize()));
\r
1419 IGUIInOutFader* fader = new CGUIInOutFader(this, parent, id, rect);
\r
1425 //! Adds a combo box to the environment.
\r
1426 IGUIComboBox* CGUIEnvironment::addComboBox(const core::rect<s32>& rectangle,
\r
1427 IGUIElement* parent, s32 id)
\r
1429 IGUIComboBox* t = new CGUIComboBox(this, parent ? parent : this,
\r
1436 //! returns the font
\r
1437 IGUIFont* CGUIEnvironment::getFont(const io::path& filename)
\r
1439 // search existing font
\r
1442 f.NamedPath.setPath(filename);
\r
1444 s32 index = Fonts.binary_search(f);
\r
1446 return Fonts[index].Font;
\r
1448 // font doesn't exist, attempt to load it
\r
1450 // does the file exist?
\r
1452 if (!FileSystem->existFile(filename))
\r
1454 os::Printer::log("Could not load font because the file does not exist", f.NamedPath.getPath(), ELL_ERROR);
\r
1458 IGUIFont* ifont=0;
\r
1459 io::IXMLReader *xml = FileSystem->createXMLReader(filename );
\r
1462 // this is an XML font, but we need to know what type
\r
1463 EGUI_FONT_TYPE t = EGFT_CUSTOM;
\r
1466 while(!found && xml->read())
\r
1468 if (xml->getNodeType() == io::EXN_ELEMENT)
\r
1470 if (core::stringw(L"font") == xml->getNodeName())
\r
1472 if (core::stringw(L"vector") == xml->getAttributeValue(L"type"))
\r
1477 else if (core::stringw(L"bitmap") == xml->getAttributeValue(L"type"))
\r
1487 if (t==EGFT_BITMAP)
\r
1489 CGUIFont* font = new CGUIFont(this, filename);
\r
1490 ifont = (IGUIFont*)font;
\r
1493 io::path directory;
\r
1494 core::splitFilename(filename, &directory);
\r
1495 if (!font->load(xml, directory))
\r
1502 else if (t==EGFT_VECTOR)
\r
1504 // todo: vector fonts
\r
1505 os::Printer::log("Unable to load font, XML vector fonts are not supported yet", f.NamedPath, ELL_ERROR);
\r
1507 //CGUIFontVector* font = new CGUIFontVector(Driver);
\r
1508 //ifont = (IGUIFont*)font;
\r
1509 //if (!font->load(xml))
\r
1518 CGUIFont* font = new CGUIFont(this, f.NamedPath.getPath() );
\r
1519 ifont = (IGUIFont*)font;
\r
1520 if (!font->load(f.NamedPath.getPath()))
\r
1530 Fonts.push_back(f);
\r
1536 //! add an externally loaded font
\r
1537 IGUIFont* CGUIEnvironment::addFont(const io::path& name, IGUIFont* font)
\r
1542 f.NamedPath.setPath(name);
\r
1543 s32 index = Fonts.binary_search(f);
\r
1545 return Fonts[index].Font;
\r
1547 Fonts.push_back(f);
\r
1553 //! remove loaded font
\r
1554 void CGUIEnvironment::removeFont(IGUIFont* font)
\r
1558 for ( u32 i=0; i<Fonts.size(); ++i )
\r
1560 if ( Fonts[i].Font == font )
\r
1562 Fonts[i].Font->drop();
\r
1569 //! returns default font
\r
1570 IGUIFont* CGUIEnvironment::getBuiltInFont() const
\r
1572 if (Fonts.empty())
\r
1575 return Fonts[0].Font;
\r
1579 IGUISpriteBank* CGUIEnvironment::getSpriteBank(const io::path& filename)
\r
1581 // search for the file name
\r
1584 b.NamedPath.setPath(filename);
\r
1586 s32 index = Banks.binary_search(b);
\r
1588 return Banks[index].Bank;
\r
1590 // we don't have this sprite bank, we should load it
\r
1591 if (!FileSystem->existFile(b.NamedPath.getPath()))
\r
1593 if ( filename != DefaultFontName )
\r
1595 os::Printer::log("Could not load sprite bank because the file does not exist", b.NamedPath.getPath(), ELL_DEBUG);
\r
1606 IGUISpriteBank* CGUIEnvironment::addEmptySpriteBank(const io::path& name)
\r
1608 // no duplicate names allowed
\r
1611 b.NamedPath.setPath(name);
\r
1613 const s32 index = Banks.binary_search(b);
\r
1617 // create a new sprite bank
\r
1619 b.Bank = new CGUISpriteBank(this);
\r
1620 Banks.push_back(b);
\r
1626 //! Creates the image list from the given texture.
\r
1627 IGUIImageList* CGUIEnvironment::createImageList( video::ITexture* texture,
\r
1628 core::dimension2d<s32> imageSize, bool useAlphaChannel )
\r
1630 CGUIImageList* imageList = new CGUIImageList( Driver );
\r
1631 if( !imageList->createImageList( texture, imageSize, useAlphaChannel ) )
\r
1633 imageList->drop();
\r
1640 //! Returns the root gui element.
\r
1641 IGUIElement* CGUIEnvironment::getRootGUIElement()
\r
1647 //! Returns the next element in the tab group starting at the focused element
\r
1648 IGUIElement* CGUIEnvironment::getNextElement(bool reverse, bool group)
\r
1650 // start the search at the root of the current tab group
\r
1651 IGUIElement *startPos = Focus ? Focus->getTabGroup() : 0;
\r
1652 s32 startOrder = -1;
\r
1654 // if we're searching for a group
\r
1655 if (group && startPos)
\r
1657 startOrder = startPos->getTabOrder();
\r
1660 if (!group && Focus && !Focus->isTabGroup())
\r
1662 startOrder = Focus->getTabOrder();
\r
1663 if (startOrder == -1)
\r
1665 // this element is not part of the tab cycle,
\r
1666 // but its parent might be...
\r
1667 IGUIElement *el = Focus;
\r
1668 while (el && el->getParent() && startOrder == -1)
\r
1670 el = el->getParent();
\r
1671 startOrder = el->getTabOrder();
\r
1677 if (group || !startPos)
\r
1678 startPos = this; // start at the root
\r
1680 // find the element
\r
1681 IGUIElement *closest = 0;
\r
1682 IGUIElement *first = 0;
\r
1683 startPos->getNextElement(startOrder, reverse, group, first, closest, false, (FocusFlags & EFF_CAN_FOCUS_DISABLED) != 0);
\r
1686 return closest; // we found an element
\r
1688 return first; // go to the end or the start
\r
1690 return this; // no group found? root group
\r
1695 void CGUIEnvironment::setFocusBehavior(u32 flags)
\r
1697 FocusFlags = flags;
\r
1700 u32 CGUIEnvironment::getFocusBehavior() const
\r
1702 return FocusFlags;
\r
1705 //! creates an GUI Environment
\r
1706 IGUIEnvironment* createGUIEnvironment(io::IFileSystem* fs,
\r
1707 video::IVideoDriver* Driver,
\r
1710 return new CGUIEnvironment(fs, Driver, op);
\r
1714 } // end namespace gui
\r
1715 } // end namespace irr
\r
1717 #endif // _IRR_COMPILE_WITH_GUI_
\r