[feature] Allow using multiple columns in attribute forms

When using the drag and drop designer, a user can specify over how many
columns the fields should be distributed.

A double click on an existing group will allow adapting the value.
This commit is contained in:
Matthias Kuhn 2016-04-01 10:19:25 +02:00
parent 9c96be1214
commit faf6b2654c
9 changed files with 183 additions and 40 deletions

View File

@ -23,6 +23,7 @@
#include <QTreeWidgetItem>
#include <QComboBox>
#include <QRadioButton>
QgsAddTabOrGroup::QgsAddTabOrGroup( QgsVectorLayer *lyr, const QList < TabPair >& tabList, QWidget * parent )
: QDialog( parent )
@ -50,6 +51,8 @@ QgsAddTabOrGroup::QgsAddTabOrGroup( QgsVectorLayer *lyr, const QList < TabPair >
connect( mTabButton, SIGNAL( toggled( bool ) ), this, SLOT( on_mTabButton_toggled( bool ) ) );
connect( mGroupButton, SIGNAL( toggled( bool ) ), this, SLOT( on_mGroupButton_toggled( bool ) ) );
mColumnCountSpinBox->setValue( QSettings().value( "/qgis/attributeForm/defaultTabColumnCount", 1 ).toInt() );
setWindowTitle( tr( "Add tab or group for %1" ).arg( mLayer->name() ) );
} // QgsVectorLayerProperties ctor
@ -68,17 +71,46 @@ QTreeWidgetItem* QgsAddTabOrGroup::tab()
return tab.second;
}
int QgsAddTabOrGroup::columnCount() const
{
return mColumnCountSpinBox->value();
}
bool QgsAddTabOrGroup::tabButtonIsChecked()
{
return mTabButton->isChecked();
}
void QgsAddTabOrGroup::accept()
{
if ( mColumnCountSpinBox->value() > 0 )
{
if ( mGroupButton->isChecked() )
{
QSettings().setValue( "/qgis/attributeForm/defaultGroupColumnCount", mColumnCountSpinBox->value() );
}
else
{
QSettings().setValue( "/qgis/attributeForm/defaultTabColumnCount", mColumnCountSpinBox->value() );
}
}
QDialog::accept();
}
void QgsAddTabOrGroup::on_mGroupButton_toggled( bool checked )
{
mTabList->setEnabled( checked );
if ( checked )
{
mColumnCountSpinBox->setValue( QSettings().value( "/qgis/attributeForm/defaultGroupColumnCount", 1 ).toInt() );
}
}
void QgsAddTabOrGroup::on_mTabButton_toggled( bool checked )
{
mTabList->setEnabled( !checked );
if ( checked )
mColumnCountSpinBox->setValue( QSettings().value( "/qgis/attributeForm/defaultTabColumnCount", 1 ).toInt() );
}

View File

@ -40,9 +40,13 @@ class APP_EXPORT QgsAddTabOrGroup : public QDialog, private Ui::QgsAddTabOrGroup
QTreeWidgetItem* tab();
int columnCount() const;
bool tabButtonIsChecked();
public slots:
virtual void accept() override;
private slots:
void on_mGroupButton_toggled( bool checked );
void on_mTabButton_toggled( bool checked );

View File

@ -39,6 +39,7 @@
#include <QSettings>
#include <QFileDialog>
#include <QHBoxLayout>
#include <QFormLayout>
QgsFieldsProperties::QgsFieldsProperties( QgsVectorLayer *layer, QWidget* parent )
: QWidget( parent )
@ -169,13 +170,14 @@ QTreeWidgetItem *QgsFieldsProperties::loadAttributeEditorTreeItem( QgsAttributeE
case QgsAttributeEditorElement::AeTypeContainer:
{
newWidget = mDesignerTree->addItem( parent, DesignerTreeItemData( DesignerTreeItemData::Container, widgetDef->name() ) );
DesignerTreeItemData itemData( DesignerTreeItemData::Container, widgetDef->name() );
const QgsAttributeEditorContainer* container = dynamic_cast<const QgsAttributeEditorContainer*>( widgetDef );
if ( !container )
{
break;
}
itemData.setColumnCount( container->columnCount() );
newWidget = mDesignerTree->addItem( parent, itemData );
Q_FOREACH ( QgsAttributeEditorElement* wdg, container->children() )
{
@ -425,12 +427,12 @@ void QgsFieldsProperties::on_mAddTabOrGroupButton_clicked()
QString name = addTabOrGroup.name();
if ( addTabOrGroup.tabButtonIsChecked() )
{
mDesignerTree->addContainer( mDesignerTree->invisibleRootItem(), name );
mDesignerTree->addContainer( mDesignerTree->invisibleRootItem(), name, addTabOrGroup.columnCount() );
}
else
{
QTreeWidgetItem* tabItem = addTabOrGroup.tab();
mDesignerTree->addContainer( tabItem, name );
mDesignerTree->addContainer( tabItem, name, addTabOrGroup.columnCount() );
}
}
@ -830,6 +832,7 @@ QgsAttributeEditorElement* QgsFieldsProperties::createAttributeEditorWidget( QTr
case DesignerTreeItemData::Container:
{
QgsAttributeEditorContainer* container = new QgsAttributeEditorContainer( item->text( 0 ), parent );
container->setColumnCount( itemData.columnCount() );
for ( int t = 0; t < item->childCount(); t++ )
{
@ -919,8 +922,6 @@ void QgsFieldsProperties::apply()
}
}
// tabs and groups
mLayer->clearAttributeEditorWidgets();
for ( int t = 0; t < mDesignerTree->invisibleRootItem()->childCount(); t++ )
@ -1032,24 +1033,32 @@ QMimeData* DragList::mimeData( const QList<QTableWidgetItem*> items ) const
* DesignerTree implementation
*/
QTreeWidgetItem* DesignerTree::addContainer( QTreeWidgetItem* parent, const QString& title )
QTreeWidgetItem* DesignerTree::addContainer( QTreeWidgetItem* parent, const QString& title, int columnCount )
{
QTreeWidgetItem *newItem = new QTreeWidgetItem( QStringList() << title );
newItem->setBackground( 0, QBrush( Qt::lightGray ) );
newItem->setFlags( Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled );
newItem->setData( 0, QgsFieldsProperties::DesignerTreeRole, QgsFieldsProperties::DesignerTreeItemData( QgsFieldsProperties::DesignerTreeItemData::Container, title ).asQVariant() );
newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled );
QgsFieldsProperties::DesignerTreeItemData itemData( QgsFieldsProperties::DesignerTreeItemData::Container, title );
itemData.setColumnCount( columnCount );
newItem->setData( 0, QgsFieldsProperties::DesignerTreeRole, itemData.asQVariant() );
parent->addChild( newItem );
newItem->setExpanded( true );
return newItem;
}
DesignerTree::DesignerTree( QWidget* parent )
: QTreeWidget( parent )
{
connect( this, SIGNAL( itemDoubleClicked( QTreeWidgetItem*, int ) ), this, SLOT( onItemDoubleClicked( QTreeWidgetItem*, int ) ) );
}
QTreeWidgetItem* DesignerTree::addItem( QTreeWidgetItem* parent, QgsFieldsProperties::DesignerTreeItemData data )
{
QTreeWidgetItem* newItem = new QTreeWidgetItem( QStringList() << data.name() );
newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled );
if ( data.type() == QgsFieldsProperties::DesignerTreeItemData::Container )
{
newItem->setFlags( Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled );
newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled );
newItem->setBackground( 0, QBrush( Qt::lightGray ) );
#if 0
@ -1198,6 +1207,44 @@ QMimeData* DesignerTree::mimeData( const QList<QTreeWidgetItem*> items ) const
return data;
}
void DesignerTree::onItemDoubleClicked( QTreeWidgetItem* item, int column )
{
Q_UNUSED( column )
QgsFieldsProperties::DesignerTreeItemData itemData = item->data( 0, QgsFieldsProperties::DesignerTreeRole ).value<QgsFieldsProperties::DesignerTreeItemData>();
if ( itemData.type() == QgsFieldsProperties::DesignerTreeItemData::Container )
{
QDialog dlg;
dlg.setWindowTitle( tr( "Configure container" ) );
QFormLayout* layout = new QFormLayout() ;
dlg.setLayout( layout );
QLineEdit* title = new QLineEdit( itemData.name() );
QSpinBox* columnCount = new QSpinBox();
columnCount->setRange( 1, 5 );
columnCount->setValue( itemData.columnCount() );
layout->addRow( tr( "Title" ), title );
layout->addRow( tr( "Column count" ), columnCount );
QDialogButtonBox* buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok
| QDialogButtonBox::Cancel );
connect( buttonBox, SIGNAL( accepted() ), &dlg, SLOT( accept() ) );
connect( buttonBox, SIGNAL( rejected() ), &dlg, SLOT( reject() ) );
layout->addWidget( buttonBox );
if ( dlg.exec() )
{
itemData.setColumnCount( columnCount->value() );
itemData.setName( title->text() );
item->setData( 0, QgsFieldsProperties::DesignerTreeRole, itemData.asQVariant() );
item->setText( 0, title->text() );
}
}
}
/*
* Serialization helpers for DesigerTreeItemData so we can stuff this easily into QMimeData
*/

View File

@ -21,6 +21,7 @@
#include <QTableWidget>
#include <QTreeWidget>
#include <QWidget>
#include <QSpinBox>
#include "qgsvectorlayer.h"
@ -68,9 +69,13 @@ class APP_EXPORT QgsFieldsProperties : public QWidget, private Ui_QgsFieldsPrope
QVariant asQVariant() { return QVariant::fromValue<DesignerTreeItemData>( *this ); }
protected:
int columnCount() const { return mColumnCount; }
void setColumnCount( int count ) { mColumnCount = count; }
private:
Type mType;
QString mName;
int mColumnCount;
};
/**
@ -244,11 +249,9 @@ class DesignerTree : public QTreeWidget
Q_OBJECT
public:
explicit DesignerTree( QWidget* parent = nullptr )
: QTreeWidget( parent )
{}
explicit DesignerTree( QWidget* parent = nullptr );
QTreeWidgetItem* addItem( QTreeWidgetItem* parent, QgsFieldsProperties::DesignerTreeItemData data );
QTreeWidgetItem* addContainer( QTreeWidgetItem* parent, const QString& title );
QTreeWidgetItem* addContainer( QTreeWidgetItem* parent, const QString& title , int columnCount );
protected:
virtual void dragMoveEvent( QDragMoveEvent *event ) override;
@ -260,6 +263,9 @@ class DesignerTree : public QTreeWidget
protected:
virtual QStringList mimeTypes() const override;
virtual QMimeData* mimeData( const QList<QTreeWidgetItem*> items ) const override;
private slots:
void onItemDoubleClicked( QTreeWidgetItem* item, int column );
};
Q_DECLARE_METATYPE( QgsFieldsProperties::FieldConfig )

View File

@ -369,6 +369,11 @@ QgsAttributeEditorElement* QgsEditFormConfig::attributeEditorElementFromDomEleme
if ( elem.tagName() == "attributeEditorContainer" )
{
QgsAttributeEditorContainer* container = new QgsAttributeEditorContainer( elem.attribute( "name" ), parent );
bool ok;
int cc = elem.attribute( "columnCount" ).toInt( &ok );
if ( !ok )
cc = 0;
container->setColumnCount( cc );
QDomNodeList childNodeList = elem.childNodes();
@ -420,3 +425,13 @@ void QgsEditFormConfig::onRelationsLoaded()
}
}
}
int QgsAttributeEditorContainer::columnCount() const
{
return mColumnCount;
}
void QgsAttributeEditorContainer::setColumnCount( int columnCount )
{
mColumnCount = columnCount;
}

View File

@ -158,14 +158,23 @@ class CORE_EXPORT QgsAttributeEditorContainer : public QgsAttributeEditorElement
/**
* Change the name of this container
*
* @param name
*/
void setName( const QString& name );
/**
* Get the number of columns in this group
*/
int columnCount() const;
/**
* Set the number of columns in this group
*/
void setColumnCount( int columnCount );
private:
bool mIsGroupBox;
QList<QgsAttributeEditorElement*> mChildren;
int mColumnCount;
};
/**

View File

@ -3827,6 +3827,7 @@ QDomElement QgsAttributeEditorContainer::toDomElement( QDomDocument& doc ) const
{
QDomElement elem = doc.createElement( "attributeEditorContainer" );
elem.setAttribute( "name", mName );
elem.setAttribute( "columnCount", mColumnCount );
Q_FOREACH ( QgsAttributeEditorElement* child, mChildren )
{

View File

@ -735,6 +735,11 @@ QWidget* QgsAttributeForm::createWidgetFromDef( const QgsAttributeEditorElement
if ( !container )
break;
int columnCount = container->columnCount();
if ( columnCount <= 0 )
columnCount = 1;
QWidget* myContainer;
if ( container->isGroupBox() )
{
@ -759,7 +764,8 @@ QWidget* QgsAttributeForm::createWidgetFromDef( const QgsAttributeEditorElement
QGridLayout* gbLayout = new QGridLayout();
myContainer->setLayout( gbLayout );
int index = 0;
int row = 0;
int column = 0;
QList<QgsAttributeEditorElement*> children = container->children();
@ -771,25 +777,31 @@ QWidget* QgsAttributeForm::createWidgetFromDef( const QgsAttributeEditorElement
if ( labelText.isNull() )
{
gbLayout->addWidget( editor, index, 0, 1, 2 );
gbLayout->addWidget( editor, row, column, 1, 2 );
column += 2;
}
else
{
QLabel* mypLabel = new QLabel( labelText );
if ( labelOnTop )
{
gbLayout->addWidget( mypLabel, index, 0, 1, 2 );
++index;
gbLayout->addWidget( editor, index, 0, 1, 2 );
gbLayout->addWidget( mypLabel, row, column, 1, 2 );
++row;
gbLayout->addWidget( editor, row, column, 1, 2 );
column += 2;
}
else
{
gbLayout->addWidget( mypLabel, index, 0 );
gbLayout->addWidget( editor, index, 1 );
gbLayout->addWidget( mypLabel, row, column++ );
gbLayout->addWidget( editor, row, column++ );
}
}
++index;
if ( column >= columnCount * 2 )
{
column = 0;
row += 1;
}
}
QWidget* spacer = new QWidget();
spacer->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred );

View File

@ -7,23 +7,13 @@
<x>0</x>
<y>0</y>
<width>447</width>
<height>164</height>
<height>238</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="3" column="1">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0" colspan="2">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
@ -78,7 +68,7 @@
</item>
</layout>
</item>
<item row="1" column="0" rowspan="3" colspan="2">
<item row="3" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -91,6 +81,33 @@
</property>
</spacer>
</item>
<item row="4" column="1">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Number of columns</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="mColumnCountSpinBox">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>5</number>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>