Dialogs may now be specified in XML files. Ported the 'press a key' dialog to a XML file as a test and example

git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@5739 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
auria
2010-08-15 22:12:58 +00:00
parent ee1f67b19d
commit 975451c7ec
10 changed files with 219 additions and 71 deletions

View File

@@ -61,7 +61,7 @@ bool LayoutManager::convertToCoord(std::string& x, int* absolute /* out */, int*
// ----------------------------------------------------------------------------
void LayoutManager::readCoords(Widget* self, Widget* parent)
void LayoutManager::readCoords(Widget* self, ITopLevelWidgetContainer* topLevelContainer, Widget* parent)
{
// determine widget position and size if not already done by sizers
std::string x = self->m_properties[PROP_X];
@@ -74,9 +74,11 @@ void LayoutManager::readCoords(Widget* self, Widget* parent)
unsigned int parent_w, parent_h, parent_x, parent_y;
if(parent == NULL)
{
core::dimension2d<u32> frame_size = GUIEngine::getDriver()->getCurrentRenderTargetSize();
parent_w = frame_size.Width;
parent_h = frame_size.Height;
//core::dimension2d<u32> frame_size = GUIEngine::getDriver()->getCurrentRenderTargetSize();
//parent_w = frame_size.Width;
//parent_h = frame_size.Height;
parent_w = topLevelContainer->getWidth();
parent_h = topLevelContainer->getHeight();
parent_x = 0;
parent_y = 0;
}
@@ -197,14 +199,15 @@ void LayoutManager::readCoords(Widget* self, Widget* parent)
// ----------------------------------------------------------------------------
void LayoutManager::calculateLayout(ptr_vector<Widget>& widgets, Widget* parent)
void LayoutManager::calculateLayout(ptr_vector<Widget>& widgets, ITopLevelWidgetContainer* topLevelContainer,
Widget* parent)
{
const unsigned short widgets_amount = widgets.size();
// ----- read x/y/size parameters
for (unsigned short n=0; n<widgets_amount; n++)
{
readCoords(widgets.get(n), parent);
readCoords(widgets.get(n), topLevelContainer, parent);
}//next widget
// ----- manage 'layout's if relevant
@@ -374,7 +377,7 @@ void LayoutManager::calculateLayout(ptr_vector<Widget>& widgets, Widget* parent)
// ----- also deal with containers' children
for(int n=0; n<widgets_amount; n++)
{
if(widgets[n].m_type == WTYPE_DIV) calculateLayout(widgets[n].m_children, &widgets[n]);
if(widgets[n].m_type == WTYPE_DIV) calculateLayout(widgets[n].m_children, topLevelContainer, &widgets[n]);
}
} // calculateLayout

View File

@@ -25,6 +25,15 @@ namespace GUIEngine
{
class Widget;
class ITopLevelWidgetContainer
{
public:
virtual ~ITopLevelWidgetContainer() {}
virtual int getWidth() = 0;
virtual int getHeight() = 0;
};
class LayoutManager
{
@@ -43,7 +52,7 @@ namespace GUIEngine
* \brief Find a widget's x, y, w and h coords from what is specified in the XML properties.
* Most notably, expands coords relative to parent and percentages.
*/
static void readCoords(Widget* self, Widget* parent);
static void readCoords(Widget* self, ITopLevelWidgetContainer* topLevelContainer, Widget* parent);
/**
* \brief Recursive call that lays out children widget within parent (or screen if none).
@@ -51,7 +60,8 @@ namespace GUIEngine
* Manages 'horizontal-row' and 'vertical-row' layouts, along with the proportions
* of the remaining children, as well as absolute sizes and locations.
*/
static void calculateLayout(ptr_vector<Widget>& widgets, Widget* parent=NULL);
static void calculateLayout(ptr_vector<Widget>& widgets, ITopLevelWidgetContainer* topLevelContainer,
Widget* parent=NULL);
};
}

View File

@@ -17,11 +17,20 @@
#include "graphics/irr_driver.hpp"
#include "guiengine/engine.hpp"
#include "guiengine/layout_manager.hpp"
#include "guiengine/modaldialog.hpp"
#include "guiengine/screen.hpp"
#include "guiengine/widget.hpp"
#include "input/input_manager.hpp"
#include "io/file_manager.hpp"
#include "irrlicht.h"
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
namespace GUIEngine
{
@@ -34,13 +43,53 @@ namespace GUIEngine
using namespace GUIEngine;
// ----------------------------------------------------------------------------
ModalDialog::ModalDialog(const float percentWidth, const float percentHeight)
{
doInit(percentWidth, percentHeight);
}
// ----------------------------------------------------------------------------
void traceChildren(const ptr_vector<Widget>& widgets, int indent=0)
{
for (int n=0; n<widgets.size(); n++)
{
for (int i=0; i<indent; i++) std::cout << " ";
std::cout << " Type " << widgets[n].getType() << " : "
<< const_cast<Widget*>(widgets.getConst(n))->m_properties[PROP_ID].c_str() << "\n";
traceChildren (widgets[n].getChildren(), indent+1);
}
}
ModalDialog::ModalDialog(const char* xmlFile, const float percentWidth, const float percentHeight)
{
// FIXME: dialog are destroyed when dismissed, this means the disk
// will be seeked everytime the same dialog is opened to read its
// XML file... cache loaded dialogs somehow maybe?
doInit(percentWidth, percentHeight);
IrrXMLReader* xml = irr::io::createIrrXMLReader( (file_manager->getGUIDir() + "/" + xmlFile).c_str() );
Screen::parseScreenFileDiv(xml, m_children, m_irrlicht_window);
delete xml;
std::cout << "Dialog children :\n";
traceChildren(m_children);
LayoutManager::calculateLayout( m_children, this );
addWidgetsRecursively(m_children);
}
// ----------------------------------------------------------------------------
void ModalDialog::doInit(const float percentWidth, const float percentHeight)
{
pointer_was_shown = irr_driver->isPointerShown();
irr_driver->showPointer();
const core::dimension2d<u32>& frame_size = GUIEngine::getDriver()->getCurrentRenderTargetSize();
const int w = (int)(frame_size.Width*percentWidth);
const int h = (int)(frame_size.Height*percentHeight);
@@ -51,12 +100,12 @@ ModalDialog::ModalDialog(const float percentWidth, const float percentHeight)
assert(w > 0);
assert(h > 0);
assert((unsigned int)w <= frame_size.Width);
assert((unsigned int)h <= frame_size.Height);
m_area = core::rect< s32 >( core::position2d< s32 >(frame_size.Width/2 - w/2, frame_size.Height/2 - h/2),
core::dimension2d< s32 >(w, h) );
core::dimension2d< s32 >(w, h) );
if (modalWindow != NULL) delete modalWindow;
modalWindow = this;
@@ -70,6 +119,8 @@ ModalDialog::ModalDialog(const float percentWidth, const float percentHeight)
input_manager->setMode(InputManager::MENU);
}
// ----------------------------------------------------------------------------
ModalDialog::~ModalDialog()
{
GUIEngine::getSkin()->m_dialog = false;
@@ -90,6 +141,8 @@ ModalDialog::~ModalDialog()
input_manager->setMode(m_previous_mode);
}
// ----------------------------------------------------------------------------
void ModalDialog::clearWindow()
{
const int children_amount = m_children.size();
@@ -111,6 +164,8 @@ void ModalDialog::clearWindow()
}
*/
}
// ----------------------------------------------------------------------------
void ModalDialog::dismiss()
{
@@ -118,24 +173,35 @@ void ModalDialog::dismiss()
modalWindow = NULL;
}
// ----------------------------------------------------------------------------
void ModalDialog::onEnterPressed()
{
if(modalWindow != NULL) modalWindow->onEnterPressedInternal();
}
// ----------------------------------------------------------------------------
bool ModalDialog::isADialogActive()
{
return modalWindow != NULL;
}
// ----------------------------------------------------------------------------
ModalDialog* ModalDialog::getCurrent()
{
return modalWindow;
}
// ----------------------------------------------------------------------------
void ModalDialog::onEnterPressedInternal()
{
}
// ----------------------------------------------------------------------------
Widget* ModalDialog::getLastWidget()
{
// FIXME: don't duplicate this code from 'Screen.cpp'
@@ -155,6 +221,9 @@ Widget* ModalDialog::getLastWidget()
}
return NULL;
}
// ----------------------------------------------------------------------------
Widget* ModalDialog::getFirstWidget()
{
// FIXME: don't duplicate this code from 'Screen.cpp'
@@ -174,3 +243,42 @@ Widget* ModalDialog::getFirstWidget()
return NULL;
}
// ----------------------------------------------------------------------------
// FIXME: this code was duplicated from Screen, find a way to share instead of duplicating
void ModalDialog::addWidgetsRecursively(ptr_vector<Widget>& widgets, Widget* parent)
{
const unsigned short widgets_amount = widgets.size();
// ------- add widgets
for (int n=0; n<widgets_amount; n++)
{
if (widgets[n].getType() == WTYPE_DIV)
{
widgets[n].add(); // Will do nothing, but will maybe reserve an ID
addWidgetsRecursively(widgets[n].m_children, &widgets[n]);
}
else
{
// warn if widget has no dimensions (except for ribbons and icons, where it is normal since it
// adjusts to its contents)
if ((widgets[n].m_w < 1 || widgets[n].m_h < 1) &&
widgets[n].getType() != WTYPE_RIBBON &&
widgets[n].getType() != WTYPE_ICON_BUTTON)
{
std::cerr << "/!\\ Warning /!\\ : widget " << widgets[n].m_properties[PROP_ID].c_str() << " has no dimensions" << std::endl;
}
if (widgets[n].m_x == -1 || widgets[n].m_y == -1)
{
std::cerr << "/!\\ Warning /!\\ : widget " << widgets[n].m_properties[PROP_ID].c_str() << " has no position" << std::endl;
}
widgets[n].add();
}
} // next widget
} // addWidgetsRecursively
// ----------------------------------------------------------------------------

View File

@@ -21,6 +21,7 @@
#include "irrlicht.h"
#include "utils/ptr_vector.hpp"
#include "guiengine/event_handler.hpp"
#include "guiengine/layout_manager.hpp"
#include "guiengine/skin.hpp"
#include "input/input_manager.hpp"
@@ -42,8 +43,12 @@ namespace GUIEngine
* need to keep track of instances yourself)
* \ingroup guiengine
*/
class ModalDialog : public SkinWidgetContainer
class ModalDialog : public SkinWidgetContainer, public ITopLevelWidgetContainer
{
private:
/** Because C++ doesn't support constructor delegation... */
void doInit(const float percentWidth, const float percentHeight);
protected:
irr::gui::IGUIWindow* m_irrlicht_window;
irr::core::rect< irr::s32 > m_area;
@@ -51,13 +56,22 @@ namespace GUIEngine
InputManager::InputDriverMode m_previous_mode;
/**
* Creates a modal dialog with given percentage of screen width and height
* \brief Creates a modal dialog with given percentage of screen width and height
*/
ModalDialog(const float percentWidth, const float percentHeight);
/**
* \brief Creates a modal dialog with given percentage of screen width and height
*/
ModalDialog(const char* xmlFile, const float percentWidth, const float percentHeight);
virtual void onEnterPressedInternal();
void clearWindow();
void addWidgetsRecursively(ptr_vector<Widget>& widgets, Widget* parent=NULL);
public:
ptr_vector<Widget> m_children;
@@ -87,6 +101,16 @@ namespace GUIEngine
/** Override to be notified of updates */
virtual void onUpdate(float dt) { }
/**
* \brief Implementing callback from ITopLevelWidgetContainer
*/
virtual int getWidth() { return m_area.getWidth(); }
/**
* \brief Implementing callback from ITopLevelWidgetContainer
*/
virtual int getHeight() { return m_area.getHeight(); }
};
}

View File

@@ -147,7 +147,7 @@ void Screen::calculateLayout()
{
assert(m_magic_number == 0xCAFEC001);
// build layout
LayoutManager::calculateLayout( m_widgets );
LayoutManager::calculateLayout( m_widgets, this );
} // calculateLayout
// -----------------------------------------------------------------------------
@@ -382,4 +382,21 @@ Widget* Screen::getLastWidget(ptr_vector<Widget>* within_vector)
return NULL;
} // getLastWidget
// -----------------------------------------------------------------------------
int Screen::getWidth()
{
core::dimension2d<u32> frame_size = GUIEngine::getDriver()->getCurrentRenderTargetSize();
return frame_size.Width;
}
// -----------------------------------------------------------------------------
int Screen::getHeight()
{
core::dimension2d<u32> frame_size = GUIEngine::getDriver()->getCurrentRenderTargetSize();
return frame_size.Height;
}
// -----------------------------------------------------------------------------

View File

@@ -27,6 +27,7 @@
#include "config/stk_config.hpp"
#include "guiengine/engine.hpp"
#include "guiengine/layout_manager.hpp"
#include "guiengine/widget.hpp"
#include "input/input.hpp"
#include "utils/ptr_vector.hpp"
@@ -80,7 +81,7 @@ namespace GUIEngine
*
* \ingroup guiengine
*/
class Screen
class Screen : public ITopLevelWidgetContainer
{
private:
/** True if the race (if it is running) should be paused when this
@@ -102,14 +103,6 @@ namespace GUIEngine
/** to catch errors as early as possible, for debugging purposes only */
unsigned int m_magic_number;
/**
* \ingroup guiengine
* \brief Loads a GUI screen from its XML file.
* Builds a hierarchy of Widget objects whose contents are a direct transcription of the XML file,
* with little analysis or layout performed on them.
*/
static void parseScreenFileDiv(irr::io::IrrXMLReader* xml, ptr_vector<Widget>& append_to);
protected:
bool m_throttle_FPS;
@@ -130,6 +123,15 @@ namespace GUIEngine
public:
/**
* \ingroup guiengine
* \brief Loads a GUI screen from its XML file.
* Builds a hierarchy of Widget objects whose contents are a direct transcription of the XML file,
* with little analysis or layout performed on them.
*/
static void parseScreenFileDiv(irr::io::IrrXMLReader* xml, ptr_vector<Widget>& append_to,
irr::gui::IGUIElement* parent = NULL);
/** \brief creates a dummy incomplete object; only use to override behaviour in sub-class */
Screen(bool pause_race=true);
@@ -286,7 +288,19 @@ namespace GUIEngine
*/
virtual void onUpdate(float dt, irr::video::IVideoDriver*) { };
/**
* \return which music to play at this screen
*/
virtual MusicInformation* getMusic() const { return stk_config->m_title_music; }
/**
* \brief Implementing method from ITopLevelWidgetContainer
*/
virtual int getWidth();
/**
* \brief Implementing method from ITopLevelWidgetContainer
*/
virtual int getHeight();
};
}

View File

@@ -34,16 +34,20 @@ using namespace io;
using namespace gui;
using namespace GUIEngine;
void Screen::parseScreenFileDiv(irr::io::IrrXMLReader* xml, ptr_vector<Widget>& append_to)
void Screen::parseScreenFileDiv(irr::io::IrrXMLReader* xml, ptr_vector<Widget>& append_to,
irr::gui::IGUIElement* parent)
{
// parse XML file
while(xml && xml->read())
while (xml && xml->read())
{
switch(xml->getNodeType())
switch (xml->getNodeType())
{
case irr::io::EXN_TEXT:
{
break;
}
case irr::io::EXN_ELEMENT:
{
/* find which type of widget is specified by the current tag, and instanciate it */
@@ -188,10 +192,15 @@ if(prop_name != NULL) widget.m_properties[prop_flag] = prop_name; else widget.m_
widget.m_text = _(text);
}
if (parent != NULL)
{
widget.setParent(parent);
}
/* a new div starts here, continue parsing with this new div as new parent */
if (widget.getType() == WTYPE_DIV || widget.getType() == WTYPE_RIBBON)
{
parseScreenFileDiv( xml, append_to[append_to.size()-1].m_children );
parseScreenFileDiv( xml, append_to[append_to.size()-1].m_children, parent );
}
}// end case EXN_ELEMENT

View File

@@ -124,6 +124,7 @@ namespace GUIEngine
friend class ProgressBarWidget;
friend class DynamicRibbonWidget;
friend class LayoutManager;
friend class ModalDialog;
/** When true, this widget shall use a bigger and more colourful font */
bool m_title_font;
@@ -374,6 +375,8 @@ namespace GUIEngine
static bool isFocusableId(const int id);
const ptr_vector<Widget>& getChildren() const { return m_children; }
/**
* Override in children to possibly receive updates (you may need to register to
* them first)

View File

@@ -81,7 +81,7 @@ void RibbonWidget::add()
for (int i=0; i<subbuttons_amount; i++)
{
// FIXME: a little unclean to invoke layout code here?
LayoutManager::readCoords(m_children.get(i), this);
LayoutManager::readCoords(m_children.get(i), NULL, this);
if (m_children[i].m_type != WTYPE_ICON_BUTTON && m_children[i].m_type != WTYPE_BUTTON)
{

View File

@@ -30,48 +30,8 @@ using namespace irr::gui;
// ------------------------------------------------------------------------------------------------------
PressAKeyDialog::PressAKeyDialog(const float w, const float h) :
ModalDialog(w, h)
ModalDialog("press_a_key_dialog.stkgui", w, h)
{
LabelWidget* widget = new LabelWidget();
widget->m_text = _("Press a key");
widget->m_properties[PROP_TEXT_ALIGN] = "center";
widget->m_x = 0;
widget->m_y = 0;
widget->m_w = m_area.getWidth();
widget->m_h = m_area.getHeight()/2;
widget->setParent(m_irrlicht_window);
m_children.push_back(widget);
widget->add();
//IGUIFont* font = GUIEngine::getFont();
const int textHeight = GUIEngine::getFontHeight();
ButtonWidget* assignToEsc = new ButtonWidget();
assignToEsc->m_properties[PROP_ID] = "assignEsc";
// I18N: In the "press a key" dialog, in the options to edit the key bindings
assignToEsc->m_text = _("Assign to ESC key");
assignToEsc->m_x = 15;
assignToEsc->m_y = m_area.getHeight() - (textHeight + 15)*2;
assignToEsc->m_w = m_area.getWidth() - 30;
assignToEsc->m_h = textHeight + 6;
assignToEsc->setParent(m_irrlicht_window);
m_children.push_back(assignToEsc);
assignToEsc->add();
ButtonWidget* cancelBtn = new ButtonWidget();
cancelBtn->m_properties[PROP_ID] = "cancel";
cancelBtn->m_text = _("Press ESC to cancel");
cancelBtn->m_x = 15;
cancelBtn->m_y = m_area.getHeight() - textHeight - 15;
cancelBtn->m_w = m_area.getWidth() - 30;
cancelBtn->m_h = textHeight + 6;
cancelBtn->setParent(m_irrlicht_window);
m_children.push_back(cancelBtn);
cancelBtn->add();
}
// ------------------------------------------------------------------------------------------------------