irrlicht/source/Irrlicht/CGUIEnvironment.cpp
cutealien 0368c5b09d IGUIEnvironment::drawAll has now a parameter to allow disabling the automatic resize to driver screensize.
That makes it easier to use partial screens, while using automatic alignment. 
Default is still automatic resizing as before, thought it got changed a bit as it looked a bit strange.
Before it only set the lower-right corner, but compared to driver screensizes, so it behaved a bit strange
when the leftTop corner of the rootGuiElement was changed (different than when the rightBottom corner was set).
Could be the idea before was that only clipping matters.

git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@6163 dfc29bdd-3216-0410-991c-e03cc46cb475
2020-12-01 13:42:44 +00:00

1693 lines
40 KiB
C++

// Copyright (C) 2002-2012 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
#include "CGUIEnvironment.h"
#ifdef _IRR_COMPILE_WITH_GUI_
#include "IVideoDriver.h"
#include "CGUISkin.h"
#include "CGUIButton.h"
#include "CGUIWindow.h"
#include "CGUIScrollBar.h"
#include "CGUIFont.h"
#include "CGUISpriteBank.h"
#include "CGUIImage.h"
#include "CGUIMeshViewer.h"
#include "CGUICheckBox.h"
#include "CGUIListBox.h"
#include "CGUITreeView.h"
#include "CGUIImageList.h"
#include "CGUIFileOpenDialog.h"
#include "CGUIColorSelectDialog.h"
#include "CGUIStaticText.h"
#include "CGUIEditBox.h"
#include "CGUISpinBox.h"
#include "CGUIInOutFader.h"
#include "CGUIMessageBox.h"
#include "CGUIModalScreen.h"
#include "CGUITabControl.h"
#include "CGUIContextMenu.h"
#include "CGUIComboBox.h"
#include "CGUIMenu.h"
#include "CGUIToolBar.h"
#include "CGUITable.h"
#include "CGUIProfiler.h"
#include "CDefaultGUIElementFactory.h"
#include "IWriteFile.h"
#include "IXMLWriter.h"
#include "BuiltInFont.h"
#include "os.h"
namespace irr
{
namespace gui
{
const wchar_t IRR_XML_FORMAT_GUI_ENV[] = L"irr_gui";
const wchar_t IRR_XML_FORMAT_GUI_ELEMENT[] = L"element";
const wchar_t IRR_XML_FORMAT_GUI_ELEMENT_ATTR_TYPE[] = L"type";
const io::path CGUIEnvironment::DefaultFontName = "#DefaultFont";
//! constructor
CGUIEnvironment::CGUIEnvironment(io::IFileSystem* fs, video::IVideoDriver* driver, IOSOperator* op)
: IGUIElement(EGUIET_ROOT, 0, 0, 0, core::rect<s32>(driver ? core::dimension2d<s32>(driver->getScreenSize()) : core::dimension2d<s32>(0,0))),
Driver(driver), Hovered(0), HoveredNoSubelement(0), Focus(0), LastHoveredMousePos(0,0), CurrentSkin(0),
FileSystem(fs), UserReceiver(0), Operator(op), FocusFlags(EFF_SET_ON_LMOUSE_DOWN|EFF_SET_ON_TAB)
{
if (Driver)
Driver->grab();
if (FileSystem)
FileSystem->grab();
if (Operator)
Operator->grab();
#ifdef _DEBUG
IGUIEnvironment::setDebugName("CGUIEnvironment");
#endif
// gui factory
IGUIElementFactory* factory = new CDefaultGUIElementFactory(this);
registerGUIElementFactory(factory);
factory->drop();
loadBuiltInFont();
IGUISkin* skin = createSkin( gui::EGST_WINDOWS_METALLIC );
setSkin(skin);
skin->drop();
//set tooltip default
ToolTip.LastTime = 0;
ToolTip.EnterTime = 0;
ToolTip.LaunchTime = 1000;
ToolTip.RelaunchTime = 500;
ToolTip.Element = 0;
// environment is root tab group
Environment = this;
setTabGroup(true);
}
//! destructor
CGUIEnvironment::~CGUIEnvironment()
{
if ( HoveredNoSubelement && HoveredNoSubelement != this )
{
HoveredNoSubelement->drop();
HoveredNoSubelement = 0;
}
if (Hovered && Hovered != this)
{
Hovered->drop();
Hovered = 0;
}
if (Focus)
{
Focus->drop();
Focus = 0;
}
if (ToolTip.Element)
{
ToolTip.Element->drop();
ToolTip.Element = 0;
}
// drop skin
if (CurrentSkin)
{
CurrentSkin->drop();
CurrentSkin = 0;
}
u32 i;
// delete all sprite banks
for (i=0; i<Banks.size(); ++i)
if (Banks[i].Bank)
Banks[i].Bank->drop();
// delete all fonts
for (i=0; i<Fonts.size(); ++i)
Fonts[i].Font->drop();
// remove all factories
for (i=0; i<GUIElementFactoryList.size(); ++i)
GUIElementFactoryList[i]->drop();
if (Operator)
{
Operator->drop();
Operator = 0;
}
if (FileSystem)
{
FileSystem->drop();
FileSystem = 0;
}
if (Driver)
{
Driver->drop();
Driver = 0;
}
}
void CGUIEnvironment::loadBuiltInFont()
{
io::IReadFile* file = FileSystem->createMemoryReadFile(BuiltInFontData,
BuiltInFontDataSize, DefaultFontName, false);
CGUIFont* font = new CGUIFont(this, DefaultFontName );
if (!font->load(file))
{
os::Printer::log("Error: Could not load built-in Font. Did you compile without the BMP loader?", ELL_ERROR);
font->drop();
file->drop();
return;
}
SFont f;
f.NamedPath.setPath(DefaultFontName);
f.Font = font;
Fonts.push_back(f);
file->drop();
}
//! draws all gui elements
void CGUIEnvironment::drawAll(bool useScreenSize)
{
if (useScreenSize && Driver)
{
core::dimension2d<s32> dim(Driver->getScreenSize());
if (AbsoluteRect.LowerRightCorner.X != dim.Width ||
AbsoluteRect.UpperLeftCorner.X != 0 ||
AbsoluteRect.LowerRightCorner.Y != dim.Height ||
AbsoluteRect.UpperLeftCorner.Y != 0
)
{
setRelativePosition(core::recti(0,0,dim.Width, dim.Height));
}
}
// make sure tooltip is always on top
if (ToolTip.Element)
bringToFront(ToolTip.Element);
draw();
OnPostRender ( os::Timer::getTime () );
}
//! sets the focus to an element
bool CGUIEnvironment::setFocus(IGUIElement* element)
{
if (Focus == element)
{
return false;
}
// GUI Environment should just reset the focus to 0
if (element == this)
element = 0;
// stop element from being deleted
if (element)
element->grab();
// focus may change or be removed in this call
IGUIElement *currentFocus = 0;
if (Focus)
{
currentFocus = Focus;
currentFocus->grab();
SEvent e;
e.EventType = EET_GUI_EVENT;
e.GUIEvent.Caller = Focus;
e.GUIEvent.Element = element;
e.GUIEvent.EventType = EGET_ELEMENT_FOCUS_LOST;
if (Focus->OnEvent(e))
{
if (element)
element->drop();
currentFocus->drop();
return false;
}
currentFocus->drop();
currentFocus = 0;
}
if (element)
{
currentFocus = Focus;
if (currentFocus)
currentFocus->grab();
// send focused event
SEvent e;
e.EventType = EET_GUI_EVENT;
e.GUIEvent.Caller = element;
e.GUIEvent.Element = Focus;
e.GUIEvent.EventType = EGET_ELEMENT_FOCUSED;
if (element->OnEvent(e))
{
if (element)
element->drop();
if (currentFocus)
currentFocus->drop();
return false;
}
}
if (currentFocus)
currentFocus->drop();
if (Focus)
Focus->drop();
// element is the new focus so it doesn't have to be dropped
Focus = element;
return true;
}
//! returns the element with the focus
IGUIElement* CGUIEnvironment::getFocus() const
{
return Focus;
}
//! returns the element last known to be under the mouse cursor
IGUIElement* CGUIEnvironment::getHovered() const
{
return Hovered;
}
//! removes the focus from an element
bool CGUIEnvironment::removeFocus(IGUIElement* element)
{
if (Focus && Focus==element)
{
SEvent e;
e.EventType = EET_GUI_EVENT;
e.GUIEvent.Caller = Focus;
e.GUIEvent.Element = 0;
e.GUIEvent.EventType = EGET_ELEMENT_FOCUS_LOST;
if (Focus->OnEvent(e))
{
return false;
}
}
if (Focus)
{
Focus->drop();
Focus = 0;
}
return true;
}
//! Returns whether the element has focus
bool CGUIEnvironment::hasFocus(const IGUIElement* element, bool checkSubElements) const
{
if (element == Focus)
return true;
if ( !checkSubElements || !element )
return false;
IGUIElement* f = Focus;
while ( f && f->isSubElement() )
{
f = f->getParent();
if ( f == element )
return true;
}
return false;
}
//! returns the current video driver
video::IVideoDriver* CGUIEnvironment::getVideoDriver() const
{
return Driver;
}
//! returns the current file system
io::IFileSystem* CGUIEnvironment::getFileSystem() const
{
return FileSystem;
}
//! returns a pointer to the OS operator
IOSOperator* CGUIEnvironment::getOSOperator() const
{
return Operator;
}
//! clear all GUI elements
void CGUIEnvironment::clear()
{
// Remove the focus
if (Focus)
{
Focus->drop();
Focus = 0;
}
if (Hovered && Hovered != this)
{
Hovered->drop();
Hovered = 0;
}
if ( HoveredNoSubelement && HoveredNoSubelement != this)
{
HoveredNoSubelement->drop();
HoveredNoSubelement = 0;
}
// get the root's children in case the root changes in future
const core::list<IGUIElement*>& children = getRootGUIElement()->getChildren();
while (!children.empty())
(*children.getLast())->remove();
}
//! called by ui if an event happened.
bool CGUIEnvironment::OnEvent(const SEvent& event)
{
bool ret = false;
if (UserReceiver
&& (event.EventType != EET_MOUSE_INPUT_EVENT)
&& (event.EventType != EET_KEY_INPUT_EVENT)
&& (event.EventType != EET_GUI_EVENT || event.GUIEvent.Caller != this))
{
ret = UserReceiver->OnEvent(event);
}
return ret;
}
//
void CGUIEnvironment::OnPostRender( u32 time )
{
// launch tooltip
if ( ToolTip.Element == 0 &&
HoveredNoSubelement && HoveredNoSubelement != this &&
(time - ToolTip.EnterTime >= ToolTip.LaunchTime
|| (time - ToolTip.LastTime >= ToolTip.RelaunchTime && time - ToolTip.LastTime < ToolTip.LaunchTime)) &&
HoveredNoSubelement->getToolTipText().size() &&
getSkin() &&
getSkin()->getFont(EGDF_TOOLTIP)
)
{
core::rect<s32> pos;
pos.UpperLeftCorner = LastHoveredMousePos;
core::dimension2du dim = getSkin()->getFont(EGDF_TOOLTIP)->getDimension(HoveredNoSubelement->getToolTipText().c_str());
dim.Width += getSkin()->getSize(EGDS_TEXT_DISTANCE_X)*2;
dim.Height += getSkin()->getSize(EGDS_TEXT_DISTANCE_Y)*2;
pos.UpperLeftCorner.Y -= dim.Height+1;
pos.LowerRightCorner.Y = pos.UpperLeftCorner.Y + dim.Height-1;
pos.LowerRightCorner.X = pos.UpperLeftCorner.X + dim.Width;
pos.constrainTo(getAbsolutePosition());
ToolTip.Element = addStaticText(HoveredNoSubelement->getToolTipText().c_str(), pos, true, true, this, -1, true);
ToolTip.Element->setOverrideColor(getSkin()->getColor(EGDC_TOOLTIP));
ToolTip.Element->setBackgroundColor(getSkin()->getColor(EGDC_TOOLTIP_BACKGROUND));
ToolTip.Element->setOverrideFont(getSkin()->getFont(EGDF_TOOLTIP));
ToolTip.Element->setSubElement(true);
ToolTip.Element->grab();
s32 textHeight = ToolTip.Element->getTextHeight();
pos = ToolTip.Element->getRelativePosition();
pos.LowerRightCorner.Y = pos.UpperLeftCorner.Y + textHeight;
ToolTip.Element->setRelativePosition(pos);
}
if (ToolTip.Element && ToolTip.Element->isVisible() ) // (isVisible() check only because we might use visibility for ToolTip one day)
{
ToolTip.LastTime = time;
// got invisible or removed in the meantime?
if ( !HoveredNoSubelement ||
!HoveredNoSubelement->isVisible() ||
!HoveredNoSubelement->getParent()
) // got invisible or removed in the meantime?
{
ToolTip.Element->remove();
ToolTip.Element->drop();
ToolTip.Element = 0;
}
}
IGUIElement::OnPostRender ( time );
}
//
void CGUIEnvironment::updateHoveredElement(core::position2d<s32> mousePos)
{
IGUIElement* lastHovered = Hovered;
IGUIElement* lastHoveredNoSubelement = HoveredNoSubelement;
LastHoveredMousePos = mousePos;
Hovered = getElementFromPoint(mousePos);
if ( ToolTip.Element && Hovered == ToolTip.Element )
{
// When the mouse is over the ToolTip we remove that so it will be re-created at a new position.
// Note that ToolTip.EnterTime does not get changed here, so it will be re-created at once.
ToolTip.Element->remove();
ToolTip.Element->drop();
ToolTip.Element = 0;
// Get the real Hovered
Hovered = getElementFromPoint(mousePos);
}
// for tooltips we want the element itself and not some of it's subelements
HoveredNoSubelement = Hovered;
while ( HoveredNoSubelement && HoveredNoSubelement->isSubElement() )
{
HoveredNoSubelement = HoveredNoSubelement->getParent();
}
if (Hovered && Hovered != this)
Hovered->grab();
if ( HoveredNoSubelement && HoveredNoSubelement != this)
HoveredNoSubelement->grab();
if (Hovered != lastHovered)
{
SEvent event;
event.EventType = EET_GUI_EVENT;
if (lastHovered)
{
event.GUIEvent.Caller = lastHovered;
event.GUIEvent.Element = 0;
event.GUIEvent.EventType = EGET_ELEMENT_LEFT;
lastHovered->OnEvent(event);
}
if ( Hovered )
{
event.GUIEvent.Caller = Hovered;
event.GUIEvent.Element = Hovered;
event.GUIEvent.EventType = EGET_ELEMENT_HOVERED;
Hovered->OnEvent(event);
}
}
if ( lastHoveredNoSubelement != HoveredNoSubelement )
{
if (ToolTip.Element)
{
ToolTip.Element->remove();
ToolTip.Element->drop();
ToolTip.Element = 0;
}
if ( HoveredNoSubelement )
{
u32 now = os::Timer::getTime();
ToolTip.EnterTime = now;
}
}
if (lastHovered && lastHovered != this)
lastHovered->drop();
if (lastHoveredNoSubelement && lastHoveredNoSubelement != this)
lastHoveredNoSubelement->drop();
}
//! This sets a new event receiver for gui events. Usually you do not have to
//! use this method, it is used by the internal engine.
void CGUIEnvironment::setUserEventReceiver(IEventReceiver* evr)
{
UserReceiver = evr;
}
//! posts an input event to the environment
bool CGUIEnvironment::postEventFromUser(const SEvent& event)
{
switch(event.EventType)
{
case EET_GUI_EVENT:
{
// hey, why is the user sending gui events..?
}
break;
case EET_MOUSE_INPUT_EVENT:
updateHoveredElement(core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y));
if ( Hovered != Focus )
{
IGUIElement * focusCandidate = Hovered;
// Only allow enabled elements to be focused (unless EFF_CAN_FOCUS_DISABLED is set)
if ( Hovered && !Hovered->isEnabled() && !(FocusFlags & EFF_CAN_FOCUS_DISABLED))
focusCandidate = NULL; // we still remove focus from the active element
// Please don't merge this into a single if clause, it's easier to debug the way it is
if (FocusFlags & EFF_SET_ON_LMOUSE_DOWN &&
event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN )
{
setFocus(focusCandidate);
}
else if ( FocusFlags & EFF_SET_ON_RMOUSE_DOWN &&
event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN )
{
setFocus(focusCandidate);
}
else if ( FocusFlags & EFF_SET_ON_MOUSE_OVER &&
event.MouseInput.Event == EMIE_MOUSE_MOVED )
{
setFocus(focusCandidate);
}
}
// sending input to focus
if (Focus && Focus->OnEvent(event))
return true;
// focus could have died in last call
if (!Focus && Hovered)
{
return Hovered->OnEvent(event);
}
break;
case EET_KEY_INPUT_EVENT:
{
if (Focus && Focus->OnEvent(event))
return true;
// For keys we handle the event before changing focus to give elements the chance for catching the TAB
// Send focus changing event
if (FocusFlags & EFF_SET_ON_TAB &&
event.EventType == EET_KEY_INPUT_EVENT &&
event.KeyInput.PressedDown &&
event.KeyInput.Key == KEY_TAB)
{
IGUIElement *next = getNextElement(event.KeyInput.Shift, event.KeyInput.Control);
if (next && next != Focus)
{
if (setFocus(next))
return true;
}
}
}
break;
default:
break;
} // end switch
return false;
}
//! returns the current gui skin
IGUISkin* CGUIEnvironment::getSkin() const
{
return CurrentSkin;
}
//! Sets a new GUI Skin
void CGUIEnvironment::setSkin(IGUISkin* skin)
{
if (CurrentSkin==skin)
return;
if (CurrentSkin)
CurrentSkin->drop();
CurrentSkin = skin;
if (CurrentSkin)
CurrentSkin->grab();
}
//! Creates a new GUI Skin based on a template.
/** \return Returns a pointer to the created skin.
If you no longer need the skin, you should call IGUISkin::drop().
See IReferenceCounted::drop() for more information. */
IGUISkin* CGUIEnvironment::createSkin(EGUI_SKIN_TYPE type)
{
IGUISkin* skin = new CGUISkin(type, Driver);
IGUIFont* builtinfont = getBuiltInFont();
IGUIFontBitmap* bitfont = 0;
if (builtinfont && builtinfont->getType() == EGFT_BITMAP)
bitfont = (IGUIFontBitmap*)builtinfont;
IGUISpriteBank* bank = 0;
skin->setFont(builtinfont);
if (bitfont)
bank = bitfont->getSpriteBank();
skin->setSpriteBank(bank);
return skin;
}
//! Returns the default element factory which can create all built in elements
IGUIElementFactory* CGUIEnvironment::getDefaultGUIElementFactory() const
{
return getGUIElementFactory(0);
}
//! Adds an element factory to the gui environment.
/** Use this to extend the gui environment with new element types which it should be
able to create automatically, for example when loading data from xml files. */
void CGUIEnvironment::registerGUIElementFactory(IGUIElementFactory* factoryToAdd)
{
if (factoryToAdd)
{
factoryToAdd->grab();
GUIElementFactoryList.push_back(factoryToAdd);
}
}
//! Returns amount of registered scene node factories.
u32 CGUIEnvironment::getRegisteredGUIElementFactoryCount() const
{
return GUIElementFactoryList.size();
}
//! Returns a scene node factory by index
IGUIElementFactory* CGUIEnvironment::getGUIElementFactory(u32 index) const
{
if (index < GUIElementFactoryList.size())
return GUIElementFactoryList[index];
else
return 0;
}
//! adds a GUI Element using its name
IGUIElement* CGUIEnvironment::addGUIElement(const c8* elementName, IGUIElement* parent)
{
IGUIElement* node=0;
if (!parent)
parent = this;
for (s32 i=GUIElementFactoryList.size()-1; i>=0 && !node; --i)
node = GUIElementFactoryList[i]->addGUIElement(elementName, parent);
return node;
}
//! Saves the current gui into a file.
//! \param filename: Name of the file .
bool CGUIEnvironment::saveGUI(const io::path& filename, IGUIElement* start)
{
io::IWriteFile* file = FileSystem->createAndWriteFile(filename);
if (!file)
{
return false;
}
bool ret = saveGUI(file, start);
file->drop();
return ret;
}
//! Saves the current gui into a file.
bool CGUIEnvironment::saveGUI(io::IWriteFile* file, IGUIElement* start)
{
if (!file)
{
return false;
}
io::IXMLWriter* writer = FileSystem->createXMLWriter(file);
if (!writer)
{
return false;
}
writer->writeXMLHeader();
writeGUIElement(writer, start ? start : this);
writer->drop();
return true;
}
//! Loads the gui. Note that the current gui is not cleared before.
//! \param filename: Name of the file.
bool CGUIEnvironment::loadGUI(const io::path& filename, IGUIElement* parent)
{
io::IReadFile* read = FileSystem->createAndOpenFile(filename);
if (!read)
{
os::Printer::log("Unable to open gui file", filename, ELL_ERROR);
return false;
}
bool ret = loadGUI(read, parent);
read->drop();
return ret;
}
//! Loads the gui. Note that the current gui is not cleared before.
bool CGUIEnvironment::loadGUI(io::IReadFile* file, IGUIElement* parent)
{
if (!file)
{
os::Printer::log("Unable to open GUI file", ELL_ERROR);
return false;
}
io::IXMLReader* reader = FileSystem->createXMLReader(file);
if (!reader)
{
os::Printer::log("GUI is not a valid XML file", file->getFileName(), ELL_ERROR);
return false;
}
// read file
while(reader->read())
{
readGUIElement(reader, parent);
}
// finish up
reader->drop();
return true;
}
//! reads an element
void CGUIEnvironment::readGUIElement(io::IXMLReader* reader, IGUIElement* node)
{
if (!reader)
return;
io::EXML_NODE nodeType = reader->getNodeType();
if (nodeType == io::EXN_NONE || nodeType == io::EXN_UNKNOWN || nodeType == io::EXN_ELEMENT_END)
return;
IGUIElement* deferedNode = 0;
if (!wcscmp(IRR_XML_FORMAT_GUI_ENV, reader->getNodeName()))
{
// GuiEnvironment always must be this as it would serialize into a wrong element otherwise.
// So we use the given node next time
if ( node && node != this )
deferedNode = node;
node = this; // root
}
else if (!wcscmp(IRR_XML_FORMAT_GUI_ELEMENT, reader->getNodeName()))
{
// find node type and create it
const core::stringc attrName = reader->getAttributeValue(IRR_XML_FORMAT_GUI_ELEMENT_ATTR_TYPE);
node = addGUIElement(attrName.c_str(), node);
if (!node)
os::Printer::log("Could not create GUI element of unknown type", attrName.c_str());
}
// read attributes
while(reader->read())
{
bool endreached = false;
switch (reader->getNodeType())
{
case io::EXN_ELEMENT_END:
if (!wcscmp(IRR_XML_FORMAT_GUI_ELEMENT, reader->getNodeName()) ||
!wcscmp(IRR_XML_FORMAT_GUI_ENV, reader->getNodeName()))
{
endreached = true;
}
break;
case io::EXN_ELEMENT:
if (!wcscmp(L"attributes", reader->getNodeName()))
{
// read attributes
io::IAttributes* attr = FileSystem->createEmptyAttributes(Driver);
attr->read(reader, true);
if (node)
node->deserializeAttributes(attr);
attr->drop();
}
else
if (!wcscmp(IRR_XML_FORMAT_GUI_ELEMENT, reader->getNodeName()) ||
!wcscmp(IRR_XML_FORMAT_GUI_ENV, reader->getNodeName()))
{
if ( deferedNode )
readGUIElement(reader, deferedNode);
else
readGUIElement(reader, node);
}
else
{
os::Printer::log("Found unknown element in irrlicht GUI file",
core::stringc(reader->getNodeName()).c_str());
}
break;
default:
break;
}
if (endreached)
break;
}
}
//! writes an element
void CGUIEnvironment::writeGUIElement(io::IXMLWriter* writer, IGUIElement* node)
{
if (!writer || !node )
return;
const wchar_t* name = 0;
// write properties
io::IAttributes* attr = FileSystem->createEmptyAttributes();
node->serializeAttributes(attr);
// all gui elements must have at least one attribute
// if they have nothing then we ignore them.
if (attr->getAttributeCount() != 0)
{
if (node == this)
{
name = IRR_XML_FORMAT_GUI_ENV;
writer->writeElement(name, false);
}
else
{
name = IRR_XML_FORMAT_GUI_ELEMENT;
writer->writeElement(name, false, IRR_XML_FORMAT_GUI_ELEMENT_ATTR_TYPE,
core::stringw(node->getTypeName()).c_str());
}
writer->writeLineBreak();
attr->write(writer);
}
// write children
core::list<IGUIElement*>::ConstIterator it = node->getChildren().begin();
for (; it != node->getChildren().end(); ++it)
{
if (!(*it)->isSubElement())
{
writer->writeLineBreak();
writeGUIElement(writer, (*it));
}
}
// write closing brace if required
if (attr->getAttributeCount() != 0)
{
writer->writeClosingTag(name);
writer->writeLineBreak();
}
attr->drop();
}
//! Writes attributes of the environment
void CGUIEnvironment::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const
{
IGUISkin* skin = getSkin();
if (skin)
{
out->addEnum("Skin", getSkin()->getType(), GUISkinTypeNames);
skin->serializeAttributes(out, options);
}
}
//! Reads attributes of the environment
void CGUIEnvironment::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options)
{
if (in->existsAttribute("Skin"))
{
IGUISkin *skin = getSkin();
EGUI_SKIN_TYPE t = (EGUI_SKIN_TYPE) in->getAttributeAsEnumeration("Skin",GUISkinTypeNames);
if ( !skin || t != skin->getType())
{
skin = createSkin(t);
setSkin(skin);
skin->drop();
}
skin = getSkin();
if (skin)
{
skin->deserializeAttributes(in, options);
}
}
RelativeRect = AbsoluteRect =
core::rect<s32>(Driver ? core::dimension2di(Driver->getScreenSize()) : core::dimension2d<s32>(0,0));
}
//! adds a button. The returned pointer must not be dropped.
IGUIButton* CGUIEnvironment::addButton(const core::rect<s32>& rectangle, IGUIElement* parent, s32 id, const wchar_t* text, const wchar_t *tooltiptext)
{
IGUIButton* button = new CGUIButton(this, parent ? parent : this, id, rectangle);
if (text)
button->setText(text);
if ( tooltiptext )
button->setToolTipText ( tooltiptext );
button->drop();
return button;
}
//! adds a window. The returned pointer must not be dropped.
IGUIWindow* CGUIEnvironment::addWindow(const core::rect<s32>& rectangle, bool modal,
const wchar_t* text, IGUIElement* parent, s32 id)
{
parent = parent ? parent : this;
IGUIWindow* win = new CGUIWindow(this, parent, id, rectangle);
if (text)
win->setText(text);
win->drop();
if (modal)
{
// 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
// careful not to get virtual function call, like OnEvent, in the window.
CGUIModalScreen * modalScreen = new CGUIModalScreen(this, parent, -1);
modalScreen->drop();
modalScreen->addChild(win);
}
return win;
}
//! adds a modal screen. The returned pointer must not be dropped.
IGUIElement* CGUIEnvironment::addModalScreen(IGUIElement* parent)
{
parent = parent ? parent : this;
IGUIElement *win = new CGUIModalScreen(this, parent, -1);
win->drop();
return win;
}
//! Adds a message box.
IGUIWindow* CGUIEnvironment::addMessageBox(const wchar_t* caption, const wchar_t* text,
bool modal, s32 flag, IGUIElement* parent, s32 id, video::ITexture* image)
{
if (!CurrentSkin)
return 0;
parent = parent ? parent : this;
core::rect<s32> rect;
core::dimension2d<u32> screenDim, msgBoxDim;
screenDim.Width = parent->getAbsolutePosition().getWidth();
screenDim.Height = parent->getAbsolutePosition().getHeight();
msgBoxDim.Width = 2;
msgBoxDim.Height = 2;
rect.UpperLeftCorner.X = (screenDim.Width - msgBoxDim.Width) / 2;
rect.UpperLeftCorner.Y = (screenDim.Height - msgBoxDim.Height) / 2;
rect.LowerRightCorner.X = rect.UpperLeftCorner.X + msgBoxDim.Width;
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + msgBoxDim.Height;
IGUIWindow* win = new CGUIMessageBox(this, caption, text, flag,
parent, id, rect, image);
win->drop();
if (modal)
{
// 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
// careful not to get virtual function call, like OnEvent, in the CGUIMessageBox.
CGUIModalScreen * modalScreen = new CGUIModalScreen(this, parent, -1);
modalScreen->drop();
modalScreen->addChild( win );
}
return win;
}
//! adds a scrollbar. The returned pointer must not be dropped.
IGUIScrollBar* CGUIEnvironment::addScrollBar(bool horizontal, const core::rect<s32>& rectangle, IGUIElement* parent, s32 id)
{
IGUIScrollBar* bar = new CGUIScrollBar(horizontal, this, parent ? parent : this, id, rectangle);
bar->drop();
return bar;
}
//! Adds a table to the environment
IGUITable* CGUIEnvironment::addTable(const core::rect<s32>& rectangle, IGUIElement* parent, s32 id, bool drawBackground)
{
CGUITable* b = new CGUITable(this, parent ? parent : this, id, rectangle, true, drawBackground, false);
b->drop();
return b;
}
//! Adds an element to display the information from the Irrlicht profiler
IGUIProfiler* CGUIEnvironment::addProfilerDisplay(const core::rect<s32>& rectangle, IGUIElement* parent, s32 id)
{
CGUIProfiler* p = new CGUIProfiler(this, parent ? parent : this, id, rectangle, NULL);
p->drop();
return p;
}
//! Adds an image element.
IGUIImage* CGUIEnvironment::addImage(video::ITexture* image, core::position2d<s32> pos,
bool useAlphaChannel, IGUIElement* parent, s32 id, const wchar_t* text)
{
core::dimension2d<s32> sz(0,0);
if (image)
sz = core::dimension2d<s32>(image->getOriginalSize());
IGUIImage* img = new CGUIImage(this, parent ? parent : this,
id, core::rect<s32>(pos, sz));
if (text)
img->setText(text);
if (useAlphaChannel)
img->setUseAlphaChannel(true);
if (image)
img->setImage(image);
img->drop();
return img;
}
//! adds an image. The returned pointer must not be dropped.
IGUIImage* CGUIEnvironment::addImage(const core::rect<s32>& rectangle, IGUIElement* parent, s32 id, const wchar_t* text, bool useAlphaChannel)
{
IGUIImage* img = new CGUIImage(this, parent ? parent : this,
id, rectangle);
if (text)
img->setText(text);
if ( useAlphaChannel )
img->setUseAlphaChannel(true);
img->drop();
return img;
}
//! adds an mesh viewer. The returned pointer must not be dropped.
IGUIMeshViewer* CGUIEnvironment::addMeshViewer(const core::rect<s32>& rectangle, IGUIElement* parent, s32 id, const wchar_t* text)
{
IGUIMeshViewer* v = new CGUIMeshViewer(this, parent ? parent : this,
id, rectangle);
if (text)
v->setText(text);
v->drop();
return v;
}
//! adds a checkbox
IGUICheckBox* CGUIEnvironment::addCheckBox(bool checked, const core::rect<s32>& rectangle, IGUIElement* parent, s32 id, const wchar_t* text)
{
IGUICheckBox* b = new CGUICheckBox(checked, this,
parent ? parent : this , id , rectangle);
if (text)
b->setText(text);
b->drop();
return b;
}
//! adds a list box
IGUIListBox* CGUIEnvironment::addListBox(const core::rect<s32>& rectangle,
IGUIElement* parent, s32 id, bool drawBackground)
{
IGUIListBox* b = new CGUIListBox(this, parent ? parent : this, id, rectangle,
true, drawBackground, false);
if (CurrentSkin && CurrentSkin->getSpriteBank())
{
b->setSpriteBank(CurrentSkin->getSpriteBank());
}
else if (getBuiltInFont() && getBuiltInFont()->getType() == EGFT_BITMAP)
{
b->setSpriteBank( ((IGUIFontBitmap*)getBuiltInFont())->getSpriteBank());
}
b->drop();
return b;
}
//! adds a tree view
IGUITreeView* CGUIEnvironment::addTreeView(const core::rect<s32>& rectangle,
IGUIElement* parent, s32 id,
bool drawBackground,
bool scrollBarVertical, bool scrollBarHorizontal)
{
IGUITreeView* b = new CGUITreeView(this, parent ? parent : this, id, rectangle,
true, drawBackground, scrollBarVertical, scrollBarHorizontal);
b->setIconFont ( getBuiltInFont () );
b->drop();
return b;
}
//! adds a file open dialog. The returned pointer must not be dropped.
IGUIFileOpenDialog* CGUIEnvironment::addFileOpenDialog(const wchar_t* title,
bool modal, IGUIElement* parent, s32 id,
bool restoreCWD, io::path::char_type* startDir)
{
parent = parent ? parent : this;
IGUIFileOpenDialog* d = new CGUIFileOpenDialog(title, this, parent, id,
restoreCWD, startDir);
d->drop();
if (modal)
{
// 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
// careful not to get virtual function call, like OnEvent, in the window.
CGUIModalScreen * modalScreen = new CGUIModalScreen(this, parent, -1);
modalScreen->drop();
modalScreen->addChild(d);
}
return d;
}
//! adds a color select dialog. The returned pointer must not be dropped.
IGUIColorSelectDialog* CGUIEnvironment::addColorSelectDialog(const wchar_t* title,
bool modal, IGUIElement* parent, s32 id)
{
parent = parent ? parent : this;
IGUIColorSelectDialog* d = new CGUIColorSelectDialog( title,
this, parent, id);
d->drop();
if (modal)
{
// 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
// careful not to get virtual function call, like OnEvent, in the window.
CGUIModalScreen * modalScreen = new CGUIModalScreen(this, parent, -1);
modalScreen->drop();
modalScreen->addChild(d);
}
return d;
}
//! adds a static text. The returned pointer must not be dropped.
IGUIStaticText* CGUIEnvironment::addStaticText(const wchar_t* text,
const core::rect<s32>& rectangle,
bool border, bool wordWrap,
IGUIElement* parent, s32 id, bool background)
{
IGUIStaticText* d = new CGUIStaticText(text, border, this,
parent ? parent : this, id, rectangle, background);
d->setWordWrap(wordWrap);
d->drop();
return d;
}
//! Adds an edit box. The returned pointer must not be dropped.
IGUIEditBox* CGUIEnvironment::addEditBox(const wchar_t* text,
const core::rect<s32>& rectangle, bool border,
IGUIElement* parent, s32 id)
{
IGUIEditBox* d = new CGUIEditBox(text, border, this,
parent ? parent : this, id, rectangle);
d->drop();
return d;
}
//! Adds a spin box to the environment
IGUISpinBox* CGUIEnvironment::addSpinBox(const wchar_t* text,
const core::rect<s32> &rectangle,
bool border,IGUIElement* parent, s32 id)
{
IGUISpinBox* d = new CGUISpinBox(text, border,this,
parent ? parent : this, id, rectangle);
d->drop();
return d;
}
//! Adds a tab control to the environment.
IGUITabControl* CGUIEnvironment::addTabControl(const core::rect<s32>& rectangle,
IGUIElement* parent, bool fillbackground, bool border, s32 id)
{
IGUITabControl* t = new CGUITabControl(this, parent ? parent : this,
rectangle, fillbackground, border, id);
t->drop();
return t;
}
//! Adds tab to the environment.
IGUITab* CGUIEnvironment::addTab(const core::rect<s32>& rectangle,
IGUIElement* parent, s32 id)
{
IGUITab* t = new CGUITab(this, parent ? parent : this,
rectangle, id);
t->drop();
return t;
}
//! Adds a context menu to the environment.
IGUIContextMenu* CGUIEnvironment::addContextMenu(const core::rect<s32>& rectangle,
IGUIElement* parent, s32 id)
{
IGUIContextMenu* c = new CGUIContextMenu(this,
parent ? parent : this, id, rectangle, true);
c->drop();
return c;
}
//! Adds a menu to the environment.
IGUIContextMenu* CGUIEnvironment::addMenu(IGUIElement* parent, s32 id)
{
if (!parent)
parent = this;
IGUIContextMenu* c = new CGUIMenu(this,
parent, id, core::rect<s32>(0,0,
parent->getAbsolutePosition().getWidth(),
parent->getAbsolutePosition().getHeight()));
c->drop();
return c;
}
//! Adds a toolbar to the environment. It is like a menu is always placed on top
//! in its parent, and contains buttons.
IGUIToolBar* CGUIEnvironment::addToolBar(IGUIElement* parent, s32 id)
{
if (!parent)
parent = this;
IGUIToolBar* b = new CGUIToolBar(this, parent, id, core::rect<s32>(0,0,10,10));
b->drop();
return b;
}
//! Adds an element for fading in or out.
IGUIInOutFader* CGUIEnvironment::addInOutFader(const core::rect<s32>* rectangle, IGUIElement* parent, s32 id)
{
core::rect<s32> rect;
if (rectangle)
rect = *rectangle;
else if (Driver)
rect = core::rect<s32>(core::dimension2di(Driver->getScreenSize()));
if (!parent)
parent = this;
IGUIInOutFader* fader = new CGUIInOutFader(this, parent, id, rect);
fader->drop();
return fader;
}
//! Adds a combo box to the environment.
IGUIComboBox* CGUIEnvironment::addComboBox(const core::rect<s32>& rectangle,
IGUIElement* parent, s32 id)
{
IGUIComboBox* t = new CGUIComboBox(this, parent ? parent : this,
id, rectangle);
t->drop();
return t;
}
//! returns the font
IGUIFont* CGUIEnvironment::getFont(const io::path& filename)
{
// search existing font
SFont f;
f.NamedPath.setPath(filename);
s32 index = Fonts.binary_search(f);
if (index != -1)
return Fonts[index].Font;
// font doesn't exist, attempt to load it
// does the file exist?
if (!FileSystem->existFile(filename))
{
os::Printer::log("Could not load font because the file does not exist", f.NamedPath.getPath(), ELL_ERROR);
return 0;
}
IGUIFont* ifont=0;
io::IXMLReader *xml = FileSystem->createXMLReader(filename );
if (xml)
{
// this is an XML font, but we need to know what type
EGUI_FONT_TYPE t = EGFT_CUSTOM;
bool found=false;
while(!found && xml->read())
{
if (xml->getNodeType() == io::EXN_ELEMENT)
{
if (core::stringw(L"font") == xml->getNodeName())
{
if (core::stringw(L"vector") == xml->getAttributeValue(L"type"))
{
t = EGFT_VECTOR;
found=true;
}
else if (core::stringw(L"bitmap") == xml->getAttributeValue(L"type"))
{
t = EGFT_BITMAP;
found=true;
}
else found=true;
}
}
}
if (t==EGFT_BITMAP)
{
CGUIFont* font = new CGUIFont(this, filename);
ifont = (IGUIFont*)font;
// load the font
io::path directory;
core::splitFilename(filename, &directory);
if (!font->load(xml, directory))
{
font->drop();
font = 0;
ifont = 0;
}
}
else if (t==EGFT_VECTOR)
{
// todo: vector fonts
os::Printer::log("Unable to load font, XML vector fonts are not supported yet", f.NamedPath, ELL_ERROR);
//CGUIFontVector* font = new CGUIFontVector(Driver);
//ifont = (IGUIFont*)font;
//if (!font->load(xml))
}
xml->drop();
}
if (!ifont)
{
CGUIFont* font = new CGUIFont(this, f.NamedPath.getPath() );
ifont = (IGUIFont*)font;
if (!font->load(f.NamedPath.getPath()))
{
font->drop();
return 0;
}
}
// add to fonts.
f.Font = ifont;
Fonts.push_back(f);
return ifont;
}
//! add an externally loaded font
IGUIFont* CGUIEnvironment::addFont(const io::path& name, IGUIFont* font)
{
if (font)
{
SFont f;
f.NamedPath.setPath(name);
s32 index = Fonts.binary_search(f);
if (index != -1)
return Fonts[index].Font;
f.Font = font;
Fonts.push_back(f);
font->grab();
}
return font;
}
//! remove loaded font
void CGUIEnvironment::removeFont(IGUIFont* font)
{
if ( !font )
return;
for ( u32 i=0; i<Fonts.size(); ++i )
{
if ( Fonts[i].Font == font )
{
Fonts[i].Font->drop();
Fonts.erase(i);
return;
}
}
}
//! returns default font
IGUIFont* CGUIEnvironment::getBuiltInFont() const
{
if (Fonts.empty())
return 0;
return Fonts[0].Font;
}
IGUISpriteBank* CGUIEnvironment::getSpriteBank(const io::path& filename)
{
// search for the file name
SSpriteBank b;
b.NamedPath.setPath(filename);
s32 index = Banks.binary_search(b);
if (index != -1)
return Banks[index].Bank;
// we don't have this sprite bank, we should load it
if (!FileSystem->existFile(b.NamedPath.getPath()))
{
if ( filename != DefaultFontName )
{
os::Printer::log("Could not load sprite bank because the file does not exist", b.NamedPath.getPath(), ELL_DEBUG);
}
return 0;
}
// todo: load it!
return 0;
}
IGUISpriteBank* CGUIEnvironment::addEmptySpriteBank(const io::path& name)
{
// no duplicate names allowed
SSpriteBank b;
b.NamedPath.setPath(name);
const s32 index = Banks.binary_search(b);
if (index != -1)
return 0;
// create a new sprite bank
b.Bank = new CGUISpriteBank(this);
Banks.push_back(b);
return b.Bank;
}
//! Creates the image list from the given texture.
IGUIImageList* CGUIEnvironment::createImageList( video::ITexture* texture,
core::dimension2d<s32> imageSize, bool useAlphaChannel )
{
CGUIImageList* imageList = new CGUIImageList( Driver );
if( !imageList->createImageList( texture, imageSize, useAlphaChannel ) )
{
imageList->drop();
return 0;
}
return imageList;
}
//! Returns the root gui element.
IGUIElement* CGUIEnvironment::getRootGUIElement()
{
return this;
}
//! Returns the next element in the tab group starting at the focused element
IGUIElement* CGUIEnvironment::getNextElement(bool reverse, bool group)
{
// start the search at the root of the current tab group
IGUIElement *startPos = Focus ? Focus->getTabGroup() : 0;
s32 startOrder = -1;
// if we're searching for a group
if (group && startPos)
{
startOrder = startPos->getTabOrder();
}
else
if (!group && Focus && !Focus->isTabGroup())
{
startOrder = Focus->getTabOrder();
if (startOrder == -1)
{
// this element is not part of the tab cycle,
// but its parent might be...
IGUIElement *el = Focus;
while (el && el->getParent() && startOrder == -1)
{
el = el->getParent();
startOrder = el->getTabOrder();
}
}
}
if (group || !startPos)
startPos = this; // start at the root
// find the element
IGUIElement *closest = 0;
IGUIElement *first = 0;
startPos->getNextElement(startOrder, reverse, group, first, closest, false, (FocusFlags & EFF_CAN_FOCUS_DISABLED) != 0);
if (closest)
return closest; // we found an element
else if (first)
return first; // go to the end or the start
else if (group)
return this; // no group found? root group
else
return 0;
}
void CGUIEnvironment::setFocusBehavior(u32 flags)
{
FocusFlags = flags;
}
u32 CGUIEnvironment::getFocusBehavior() const
{
return FocusFlags;
}
//! creates an GUI Environment
IGUIEnvironment* createGUIEnvironment(io::IFileSystem* fs,
video::IVideoDriver* Driver,
IOSOperator* op)
{
return new CGUIEnvironment(fs, Driver, op);
}
} // end namespace gui
} // end namespace irr
#endif // _IRR_COMPILE_WITH_GUI_