Dialog Configurations

From CEGUI Wiki - Crazy Eddie's GUI System (Open Source)
Revision as of 13:35, 6 May 2006 by Rackle (Talk | contribs) (DialogPositions.cpp)

Jump to: navigation, search

Introduction

The position of the various dialogs (frame windows) are stored within .layout files. Closing and then restarting the application will reset the position of these dialogs. This code saves the dialog positions into an XML file and when the application restarts will read the contents of this XML file and reposition the various dialogs to their last saved positions.

Sample_Demo7

This demo contains 3 dialogs. Let's store their positions.

Edit Sample_Demo7.h:

  • add #include "Widgets1/DialogPositions.h"
  • add DialogPositions m_dialogPositions; within the protected section

Edit Sample_Demo7.cpp to add the following at the end of the Demo7Sample::initialiseSample() function, between initDemoEventWiring(); and return true;:

 m_dialogPositions.loadPositions("../datafiles/configs/Sample_Demo7.xml", "../datafiles/configs/DialogPositions.xsd");
 m_dialogPositions.addDialog("Demo7/Window1");
 m_dialogPositions.addDialog("Demo7/Window2");
 m_dialogPositions.addDialog("Demo7/Window3");

Add the following to Demo7Sample::cleanupSample()

 m_dialogPositions.savePositions();

That's it, now the 3 windows within that demo will "remember" their positions.

XML Position File

The call to loadPositions() takes two parameters: the XML file to load and the schema definition. If the XML file does not exist an exception will be thrown. This can be avoided by creating an empty XML file; the exception is thrown only when the file does not exist, not when it is empty. Then you can either manually write the XML contents yourself or use a little trick to have it written automatically.

Calling addDialog() and passing the window name as parameter will register that window name to have its position written to the XML file. The next call to savePositions() will write the position coordinates of that registered window and the next time loadPositions() is called the window will be automatically registered.

Warning: the Demo7Sample::cleanupSample() is never called when exiting the demo, despite what the comments might say. A solution is presented on the message board.

Please discuss this snipper in the Storing Dialog Positions thread on the message board.

Files

DialogPositions.h

#pragma once

#include "CEGUI.h"
#include "CEGUIXMLHandler.h"
#include "CEGUIXMLParser.h"
#include "CEGUIXMLAttributes.h"
#include <map>

class DialogPositions : public CEGUI::XMLHandler
{
public:
	DialogPositions();
	bool loadPositions(const CEGUI::String& xmlFile, const CEGUI::String& schema); // Load the position of every stored dialog
	bool savePositions(); // Store the position of every dialogs
	bool addDialog(const CEGUI::String& dialog); // Add a dialog to be monitored
	const CEGUI::Point& getSavedPosition(const CEGUI::String& dialog); // Return the position of the specified dialog
private:
    void elementStart(const CEGUI::String& element, const CEGUI::XMLAttributes& attributes);
	static const CEGUI::String m_DialogPositionElement; // Tag name for dialog positions element
	static const CEGUI::String m_positionElement; // Tag name for position elements
	static const char windowNameAttribute[]; // Attribute name that stores the name of the window
	static const char windowXPosAttribute[]; // Attribute name that stores the X position of the window
	static const char windowYPosAttribute[]; // Attribute name that stores the X position of the window
	std::map<CEGUI::String, CEGUI::Point> m_dialogPositions; // Map of the dialog positions
	CEGUI::String m_positionFile; // File containing the positions
	CEGUI::Point m_defaultPosition; // Default position, used by getDialogPosition()
};

DialogPositions.cpp

#include "DialogPositions.h"

//Definition of XML elements and attributes
const CEGUI::String DialogPositions::m_DialogPositionElement( "DialogPositions" );
const CEGUI::String DialogPositions::m_positionElement( "Position" );
const char DialogPositions::windowNameAttribute[]	= "Name";
const char DialogPositions::windowXPosAttribute[]	= "XPos";
const char DialogPositions::windowYPosAttribute[]	= "YPos";

DialogPositions::DialogPositions()
{
	m_defaultPosition.d_x = 0.0f;
	m_defaultPosition.d_y = 0.0f;
}

bool DialogPositions::loadPositions(const CEGUI::String& xmlFile, const CEGUI::String& schema)
{
	// Load the position of every stored dialog
	assert(!xmlFile.empty() && "You must specify an xml file to loadPositions()");
	m_positionFile = xmlFile;
	CEGUI::System::getSingleton().getXMLParser()->parseXMLFile(*this, m_positionFile, schema, "");
	return true;
}

bool DialogPositions::savePositions()
{
	// Store the position of every registered dialog, either within
	// the the loaded XML file or those specified via addDialog() 
	assert(!m_positionFile.empty() && "You must specify an xml file by calling loadPositions() before savePositions()");
	CEGUI::WindowManager& winMgr = CEGUI::WindowManager::getSingleton();
	CEGUI::Point position;
	std::map<CEGUI::String, CEGUI::Point>::iterator itPosition;

	// Try to open the XML file in writing mode
    std::ofstream fileSave;
	fileSave.open(m_positionFile.c_str(), std::ios::out) ;
    if( !fileSave.is_open() )
    {
		CEGUI::Logger::getSingleton().logEvent("Could not write to windows position file ("
												+ m_positionFile
												+ ").  Is it read-only?",
												CEGUI::Errors);
        return false;
    }

	// Write the header
	fileSave << "<?xml version=\"1.0\" ?>" << std::endl
			<< "<" << m_DialogPositionElement << ">"
			<< std::endl;

	// Write each dialog's position
	for(itPosition = m_dialogPositions.begin(); itPosition != m_dialogPositions.end(); ++itPosition)
	{
		position = winMgr.getWindow(itPosition->first)->getPosition();
		fileSave << "  <" << m_positionElement.c_str() << " "
			<< windowNameAttribute << "=\"" << itPosition->first.c_str() << "\" "
			<< windowXPosAttribute << "=\"" << position.d_x  << "\" "
			<< windowYPosAttribute << "=\"" << position.d_y  << "\" "
			<< "/>" << std::endl;
	}

	// Write the footer
	fileSave << "</" << m_DialogPositionElement << ">" << std::endl;
	fileSave.close();
	return true;
}

bool DialogPositions::addDialog(const CEGUI::String& dialog)
{
	// Add a dialog to be monitored
	// This can be used to automatically write the contents of the initial XML file
	// rather than editing it by hand.  The XML file passed to loadPositions() MUST
	// exist but it can be empty
	std::map<CEGUI::String, CEGUI::Point>::iterator itPosition;
	itPosition = m_dialogPositions.find(dialog);
	if(itPosition != m_dialogPositions.end())
		return false; // This dialog is already added

	m_dialogPositions[dialog] = CEGUI::WindowManager::getSingleton().getWindow(dialog)->getPosition();
	return true;
}

const CEGUI::Point& DialogPositions::getSavedPosition(const CEGUI::String& dialog)
{
	// Return the position of the specified dialog
	CEGUI::Point position;
	std::map<CEGUI::String, CEGUI::Point>::iterator itPosition;
	itPosition = m_dialogPositions.find(dialog);
	return itPosition == m_dialogPositions.end() ? m_defaultPosition : (*itPosition).second;
}

void DialogPositions::elementStart(const CEGUI::String& element, const CEGUI::XMLAttributes& attributes)
{
	// Parse the <Position> elements
	CEGUI::String name;
	CEGUI::Point position;
	if (element == m_positionElement)
    {
        name = attributes.getValueAsString(windowNameAttribute);
		position.d_x = attributes.getValueAsFloat(windowXPosAttribute);
        position.d_y = attributes.getValueAsFloat(windowYPosAttribute);

		// Prevent dialogs from opening beyond the screen limits
		if(position.d_x > 1.0f)
			position.d_x = 0.99f;
		if(position.d_y > 1.0f)
			position.d_y = 0.99f;

		// Set the position of the dialog
		CEGUI::Window* widget = CEGUI::WindowManager::getSingleton().getWindow(name);
		widget->setPosition(position);
		m_dialogPositions[name] = position;
	}
}

DialogPositions.xsd

<?xml version="1.0"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">

	<xsd:element name="DialogPositions" type="DialogPositionsType"/>

	<xsd:complexType name="DialogPositionsType">
		<xsd:sequence>
			<xsd:element name="Position" type="PositionType" maxOccurs="unbounded"/>
		</xsd:sequence>
	</xsd:complexType>

	<xsd:complexType name="PositionType">
		<xsd:attribute name="Name" type="xsd:string" use="required"/>
		<xsd:attribute name="XPos" type="xsd:decimal" use="required"/>
		<xsd:attribute name="YPos" type="xsd:decimal" use="required"/>
	</xsd:complexType>
	
</xsd:schema>