CHANGED: #1471 Widget property templates are now stored in a tree. Also added some new controls to the widget property dialog.

--HG--
branch : gsoc2012-gui-editor
This commit is contained in:
dfighter1985 2012-11-17 04:55:12 +01:00
parent d83c375981
commit 2cebadaa79
12 changed files with 496 additions and 106 deletions

View file

@ -31,6 +31,7 @@
#include "../../3rdparty/qtpropertybrowser/QtTreePropertyBrowser"
#include "widget_properties.h"
#include "widget_info_tree.h"
#include "widget_properties_parser.h"
#include "widget_hierarchy.h"
#include "widget_serializer.h"
@ -61,14 +62,17 @@ namespace GUIEditor
viewPort = new NelGUIWidget;
setCentralWidget( viewPort );
widgetInfoTree = new CWidgetInfoTree;
createMenus();
readSettings();
CWidgetPropParser parser;
parser.setWidgetPropMap( &widgetInfo );
parser.setWidgetInfoTree( widgetInfoTree );
parser.parseGUIWidgets();
widgetProps->setupWidgetInfo( &widgetInfo );
widgetProps->setupWidgetInfo( widgetInfoTree );
QDockWidget *dock = new QDockWidget( "Widget Hierarchy", this );
dock->setAllowedAreas( Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea );
@ -80,7 +84,7 @@ namespace GUIEditor
dock->setAllowedAreas( Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea );
QtTreePropertyBrowser *propBrowser = new QtTreePropertyBrowser;
browserCtrl.setupWidgetInfo( widgetInfo );
browserCtrl.setupWidgetInfo( widgetInfoTree );
browserCtrl.setBrowser( propBrowser );
dock->setWidget( propBrowser );
addDockWidget( Qt::RightDockWidgetArea, dock );
@ -116,6 +120,9 @@ namespace GUIEditor
// no deletion needed for these, since dockwidget owns them
hierarchyView = NULL;
propBrowser = NULL;
delete widgetInfoTree;
widgetInfoTree = NULL;
}
QUndoStack *GUIEditorWindow::undoStack() const

View file

@ -35,6 +35,7 @@ namespace GUIEditor
class ProcList;
class ProjectWindow;
class NelGUIWidget;
class CWidgetInfoTree;
class GUIEditorWindow: public QMainWindow
{
@ -76,6 +77,8 @@ private:
ProjectWindow *projectWindow;
NelGUIWidget *viewPort;
CWidgetInfoTree *widgetInfoTree;
CPropBrowserCtrl browserCtrl;
QString currentProject;
QString currentProjectFile;

View file

@ -21,6 +21,7 @@
#include "nel/gui/interface_group.h"
#include "nel/gui/widget_manager.h"
#include <typeinfo>
#include "widget_info_tree.h"
namespace GUIEditor
{
@ -42,14 +43,18 @@ namespace GUIEditor
browser = b;
}
void CPropBrowserCtrl::setupWidgetInfo( const std::map< std::string, SWidgetInfo > &info )
void CPropBrowserCtrl::setupWidgetInfo( CWidgetInfoTree *tree )
{
widgetInfo.clear();
std::map< std::string, SWidgetInfo >::const_iterator itr;
for( itr = info.begin(); itr != info.end(); ++itr )
std::vector< std::string > names;
tree->getNames( names );
std::vector< std::string >::const_iterator itr;
for( itr = names.begin(); itr != names.end(); ++itr )
{
const SWidgetInfo &w = itr->second;
CWidgetInfoTreeNode *node = tree->findNodeByName( *itr );
const SWidgetInfo &w = node->getInfo();
widgetInfo[ w.GUIName ] = w;
}
}

View file

@ -34,6 +34,7 @@ namespace NLGUI
namespace GUIEditor
{
class CWidgetInfoTree;
/// This class controls the Widget property browser widget.
/// It receives signals from the widget that draws/manages the Nel GUI widgets, and handles them.
@ -45,7 +46,7 @@ namespace GUIEditor
CPropBrowserCtrl();
~CPropBrowserCtrl();
void setBrowser( QtTreePropertyBrowser *b );
void setupWidgetInfo( const std::map< std::string, SWidgetInfo > &info );
void setupWidgetInfo( CWidgetInfoTree *tree );
void clear();
public Q_SLOTS:
@ -60,9 +61,8 @@ namespace GUIEditor
QtTreePropertyBrowser *browser;
QtVariantPropertyManager *propertyMgr;
std::map< std::string, SWidgetInfo > widgetInfo;
std::string currentElement;
std::map< std::string, SWidgetInfo > widgetInfo;
};
}

View file

@ -56,6 +56,25 @@ namespace GUIEditor
resolved = false;
isAbstract = true;
}
~SWidgetInfo()
{
resolved = false;
isAbstract = false;
}
/// Find a property by it's name
std::vector< SPropEntry >::iterator findProp( const std::string &name )
{
std::vector< SPropEntry >::iterator itr = props.begin();
while( itr != props.end() )
{
if( itr->propName == name )
break;
++itr;
}
return itr;
}
};
}

View file

@ -0,0 +1,94 @@
// Object Viewer Qt GUI Editor plugin <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef WIDGET_INFO_TREE_H
#define WIDGET_INFO_TREE_H
#include "widget_info_tree_node.h"
#include "nel/misc/debug.h"
namespace GUIEditor
{
class CWidgetInfoTree
{
public:
CWidgetInfoTree()
{
root = NULL;
}
~CWidgetInfoTree()
{
delete root;
root = NULL;
}
/// Find a node by it's name
CWidgetInfoTreeNode* findNodeByName( const std::string &name )
{
if( root == NULL )
return NULL;
return root->findNodeByName( name );
}
void addRootNode( SWidgetInfo &info )
{
nlassert( root == NULL );
root = CWidgetInfoTreeNode::create( info );
}
/// Add a new node as a child to it's ancestor
bool addNode( SWidgetInfo &info )
{
CWidgetInfoTreeNode *node = findNodeByName( info.ancestor );
if( node == NULL )
return false;
else
node->addChild( info );
return true;
}
/// Finds a node and removes it
bool removeNode( SWidgetInfo *info )
{
CWidgetInfoTreeNode *node = findNodeByName( info->name );
if( node == NULL )
return false;
if( node == root )
root = NULL;
if( node->getParent() != NULL )
node->getParent()->removeChildByNameND( node->getInfo().name );
delete node;
return true;
}
/// Get the node names and put them into the vector
void getNames( std::vector< std::string > &v ) const
{
if( root == NULL )
return;
root->getNames( v );
}
private:
CWidgetInfoTreeNode *root;
};
}
#endif

View file

@ -0,0 +1,182 @@
// Object Viewer Qt GUI Editor plugin <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef WIDGET_INFO_TREE_NODE
#define WIDGET_INFO_TREE_NODE
#include "widget_info.h"
namespace GUIEditor
{
/// Widget Tree Info Node
class CWidgetInfoTreeNode
{
public:
CWidgetInfoTreeNode( SWidgetInfo &info )
{
this->info = info;
parent = NULL;
children.clear();
}
~CWidgetInfoTreeNode()
{
removeChildren();
parent = NULL;
}
/// Set the parent of this node
void setParent( CWidgetInfoTreeNode *newParent )
{
parent = newParent;
}
/// Returns the parent of this node
CWidgetInfoTreeNode* getParent() const
{
return parent;
}
/// Create a new node
static CWidgetInfoTreeNode* create( SWidgetInfo &info )
{
return new CWidgetInfoTreeNode( info );
}
/// Get the WidgetInfo of this node
SWidgetInfo& getInfo()
{
return info;
}
/// Add a new child node
void addChild( SWidgetInfo &info )
{
CWidgetInfoTreeNode *node = CWidgetInfoTreeNode::create( info );
node->setParent( this );
children.push_back( node );
// copy the properties to the child, since they inherit them
for( std::vector< SPropEntry >::const_iterator itr = this->info.props.begin(); itr != this->info.props.end(); ++itr )
{
node->addProperty( *itr );
}
}
/// Remove child by name
bool removeChildByName( const std::string &name )
{
for( std::vector< CWidgetInfoTreeNode* >::const_iterator itr = children.begin(); itr != children.end(); ++itr )
{
if( ( *itr )->getInfo().name == name )
{
children.erase( itr );
delete ( *itr );
return true;
}
}
return false;
}
/// Remove child by name, but don't delete the child
bool removeChildByNameND( const std::string &name )
{
for( std::vector< CWidgetInfoTreeNode* >::const_iterator itr = children.begin(); itr != children.end(); ++itr )
{
if( ( *itr )->getInfo().name == name )
{
children.erase( itr );
return true;
}
}
return false;
}
/// Remove child by ancestor's name
bool removeChildByAncestor( const std::string &ancestor )
{
for( std::vector< CWidgetInfoTreeNode* >::const_iterator itr = children.begin(); itr != children.end(); ++itr )
{
if( ( *itr )->getInfo().ancestor == ancestor )
{
children.erase( itr );
delete ( *itr );
return true;
}
}
return false;
}
/// Remove all children
void removeChildren()
{
std::vector< CWidgetInfoTreeNode* >::iterator itr = children.begin();
while( itr != children.end() )
{
CWidgetInfoTreeNode *node = *itr;
++itr;
delete node;
}
children.clear();
}
/// Add new property to the node
void addProperty( const SPropEntry &prop )
{
info.props.push_back( prop );
}
/// Find the node by it's name and then return a pointer to it
CWidgetInfoTreeNode* findNodeByName( const std::string &name )
{
if( info.name == name )
return this;
CWidgetInfoTreeNode *node = NULL;
for( std::vector< CWidgetInfoTreeNode* >::iterator itr = children.begin(); itr != children.end(); ++itr )
{
node = ( *itr )->findNodeByName( name );
if( node != NULL )
return node;
}
return NULL;
}
/// Get the node names and put them into the vector
void getNames( std::vector< std::string > &v ) const
{
v.push_back( info.name );
for( std::vector< CWidgetInfoTreeNode* >::const_iterator itr = children.begin(); itr != children.end(); ++itr )
( *itr )->getNames( v );
}
private:
SWidgetInfo info;
CWidgetInfoTreeNode *parent;
std::vector< CWidgetInfoTreeNode* > children;
};
}
#endif

View file

@ -15,32 +15,34 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "widget_properties.h"
#include "widget_info_tree.h"
#include <qmessagebox.h>
namespace GUIEditor{
CWidgetProperties::CWidgetProperties( QWidget *parent ) :
QWidget( parent )
{
setupUi( this );
connect( closeButton, SIGNAL( clicked(bool) ), this, SLOT( hide() ) );
connect( rmWButton, SIGNAL( clicked( bool ) ), this, SLOT( onRemoveWButtonClicked() ) );
connect( rmPButton, SIGNAL( clicked( bool ) ), this, SLOT( onRemovePButtonClicked() ) );
}
CWidgetProperties::~CWidgetProperties()
{
}
void CWidgetProperties::setupWidgetInfo( std::map< std::string, SWidgetInfo > *info )
void CWidgetProperties::setupWidgetInfo( CWidgetInfoTree *tree )
{
widgetInfo = info;
for( std::map< std::string, SWidgetInfo >::iterator itr = info->begin(); itr != info->end(); ++itr ){
widgetList->addItem( itr->first.c_str() );
}
this->tree = tree;
buildWidgetList();
onListSelectionChanged( 0 );
connect( widgetList, SIGNAL( currentRowChanged( int ) ), this, SLOT( onListSelectionChanged( int ) ) );
}
void CWidgetProperties::onListSelectionChanged( int i )
{
if( i < 0 )
return;
if( i >= widgetList->count() )
return;
@ -48,18 +50,82 @@ namespace GUIEditor{
setPropsOf( item->text().toStdString().c_str() );
}
void CWidgetProperties::onRemoveWButtonClicked()
{
if( widgetList->count() == 0 )
return;
QString widgetName = widgetList->currentItem()->text();
int reply = QMessageBox::question( this,
tr( "Removing a widget" ),
tr( "Are you sure you want to remove %1?" ).arg( widgetName ),
QMessageBox::Yes | QMessageBox::Cancel
);
if( reply != QMessageBox::Yes )
return;
/*
Remove the damned thing here
*/
buildWidgetList();
}
void CWidgetProperties::onRemovePButtonClicked()
{
QTreeWidgetItem *item = widgetPropTree->currentItem();
CWidgetInfoTreeNode *node = tree->findNodeByName( widgetList->currentItem()->text().toStdString() );
if( ( item == NULL ) || ( node == NULL ) )
return;
std::string name = item->text( 0 ).toStdString();
std::vector< SPropEntry >::const_iterator itr = node->getInfo().findProp( name );
if( itr == node->getInfo().props.end() )
return;
int reply = QMessageBox::question( this,
tr( "Removing a property" ),
tr( "Are you sure you want to remove" ).arg( QString( name.c_str() ) ),
QMessageBox::Yes | QMessageBox::Cancel
);
if( reply != QMessageBox::Yes )
return;
/*
Remove the damned thing here
*/
onListSelectionChanged( widgetList->currentRow() );
}
void CWidgetProperties::buildWidgetList()
{
widgetList->clear();
std::vector< std::string > widgetNames;
tree->getNames( widgetNames );
std::sort( widgetNames.begin(), widgetNames.end() );
for( std::vector< std::string >::const_iterator itr = widgetNames.begin(); itr != widgetNames.end(); ++itr )
widgetList->addItem( itr->c_str() );
widgetList->setCurrentRow( 0 );
}
void CWidgetProperties::setPropsOf( const char *name )
{
std::map< std::string, SWidgetInfo >::iterator itr =
widgetInfo->find( name );
if( itr == widgetInfo->end() )
CWidgetInfoTreeNode *node = tree->findNodeByName( name );
if( node == NULL )
return;
widgetPropTree->clear();
std::vector< SPropEntry > &v = itr->second.props;
for( std::vector< SPropEntry >::iterator itr2 = v.begin(); itr2 != v.end(); ++itr2 )
const std::vector< SPropEntry > &v = node->getInfo().props;
for( std::vector< SPropEntry >::const_iterator itr2 = v.begin(); itr2 != v.end(); ++itr2 )
{
SPropEntry e = *itr2;
QTreeWidgetItem *item = new QTreeWidgetItem;
@ -69,6 +135,5 @@ namespace GUIEditor{
widgetPropTree->addTopLevelItem( item );
}
}
}

View file

@ -26,6 +26,8 @@
namespace GUIEditor
{
class CWidgetInfoTree;
class CWidgetProperties : public QWidget, public Ui::WidgetProperties
{
Q_OBJECT
@ -33,14 +35,25 @@ namespace GUIEditor
public:
CWidgetProperties( QWidget *parent = NULL );
~CWidgetProperties();
void setupWidgetInfo( std::map< std::string, SWidgetInfo > *info );
void setupWidgetInfo( CWidgetInfoTree *tree );
private Q_SLOTS:
void onListSelectionChanged( int i );
/// Removes widget from the list
void onRemoveWButtonClicked();
/// Removes widget property from the list
void onRemovePButtonClicked();
private:
/// Builds the widget list
void buildWidgetList();
/// Builds the property list for the currently selected widget
void setPropsOf( const char *name );
std::map< std::string, SWidgetInfo > *widgetInfo;
CWidgetInfoTree *tree;
};
}

View file

@ -6,15 +6,15 @@
<rect>
<x>0</x>
<y>0</y>
<width>618</width>
<height>308</height>
<width>703</width>
<height>302</height>
</rect>
</property>
<property name="windowTitle">
<string>Widget Properties</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QListWidget" name="widgetList"/>
@ -40,38 +40,37 @@
</item>
</layout>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>56</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>428</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="closeButton">
<widget class="QPushButton" name="addWButton">
<property name="text">
<string>Close</string>
<string>Add</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="rmWButton">
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QPushButton" name="addPButton">
<property name="text">
<string>Add</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="rmPButton">
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>

View file

@ -19,11 +19,21 @@
#include <QDir>
#include <QStringList>
#include "nel/misc/debug.h"
#include "widget_info_tree.h"
using namespace NLMISC;
namespace GUIEditor
{
CWidgetPropParser::CWidgetPropParser()
{
}
CWidgetPropParser::~CWidgetPropParser()
{
widgetInfoTree = NULL;
}
void CWidgetPropParser::parseGUIWidgets()
{
QDir d( "widgets" );
@ -47,7 +57,7 @@ namespace GUIEditor
while( itr.hasNext() )
parseGUIWidget( "widgets/" + itr.next() );
resolveInheritance();
buildWidgetInfoTree();
widgetInfo = NULL;
}
@ -209,61 +219,48 @@ namespace GUIEditor
}
}
bool propCompare( const SPropEntry &left, const SPropEntry &right )
{
return left.propName < right.propName;
}
void CWidgetPropParser::resolveInheritance()
void CWidgetPropParser::buildWidgetInfoTree()
{
// First find the root node
// It is the one which has no ancestor
SWidgetInfo *info = NULL;
for( std::map< std::string, SWidgetInfo >::iterator itr = widgetInfo->begin(); itr != widgetInfo->end(); ++itr )
{
resolveInheritanceFor( itr->first );
std::sort( itr->second.props.begin(), itr->second.props.end(), propCompare );
}
}
void CWidgetPropParser::resolveInheritanceFor( const std::string name )
if( itr->second.ancestor.empty() )
{
if( name.empty() )
info = &( itr->second );
break;
}
}
// No root node, cannot continue!
if( info == NULL )
return;
std::map< std::string, SWidgetInfo >::iterator itr =
widgetInfo->find( name );
if( itr == widgetInfo->end() )
return;
SWidgetInfo *info = &(itr->second);
if( info->resolved )
return;
if( info->ancestor.empty() )
return;
std::vector< SPropEntry > &props = info->props;
SWidgetInfo *info2 = info;
widgetInfoTree->addRootNode( *info );
info->resolved = true;
// Now add the rest of the widgets
bool added = false;
do
{
if( info2->ancestor.empty() )
break;
added = false;
std::map< std::string, SWidgetInfo >::iterator itr2 =
widgetInfo->find( info2->ancestor );
if( itr2 == widgetInfo->end() )
break;
info2 = &( itr2->second );
for( std::vector< SPropEntry >::iterator propItr = info2->props.begin(); propItr != info2->props.end(); ++propItr )
props.push_back( *propItr );
for( std::map< std::string, SWidgetInfo >::iterator itr = widgetInfo->begin(); itr != widgetInfo->end(); ++itr )
{
// Skip if already added
if( itr->second.resolved )
continue;
// Try to add it, if successful, mark it
if( widgetInfoTree->addNode( itr->second ) )
{
itr->second.resolved = true;
added = true;
}
while( !info2->resolved );
info->resolved = true;
}
}
while( added );
}
}

View file

@ -26,14 +26,18 @@
namespace GUIEditor
{
class CWidgetInfoTree;
/// Parser for the widget properties XML files
class CWidgetPropParser
{
public:
CWidgetPropParser(){}
~CWidgetPropParser(){}
CWidgetPropParser();
~CWidgetPropParser();
void setWidgetPropMap( std::map< std::string, SWidgetInfo > *info ){ widgetInfo = info; }
void setWidgetInfoTree( CWidgetInfoTree *tree ){ widgetInfoTree = tree; }
/// Parse the GUI widget template definitions
void parseGUIWidgets();
private:
@ -41,10 +45,12 @@ namespace GUIEditor
void parseGUIWidgetXML( QFile &file );
QString parseGUIWidgetHeader( QXmlStreamReader &reader );
void parseGUIWidgetProperties( QXmlStreamReader &reader, const QString &widgetName );
void resolveInheritance();
void resolveInheritanceFor( const std::string name );
/// Build the widget info tree from the parsed data
void buildWidgetInfoTree();
std::map< std::string, SWidgetInfo > *widgetInfo;
CWidgetInfoTree *widgetInfoTree;
};
}