diff --git a/src/gui/vector/qgsattributesformmodel.cpp b/src/gui/vector/qgsattributesformmodel.cpp index 4a32410fa24..f9fb5dd30c5 100644 --- a/src/gui/vector/qgsattributesformmodel.cpp +++ b/src/gui/vector/qgsattributesformmodel.cpp @@ -1,6 +1,11 @@ + +#include "qgsactionmanager.h" #include "qgsattributesformmodel.h" #include "qgsgui.h" #include "qgseditorwidgetregistry.h" +#include "qgsattributeeditoraction.h" +#include "qgsattributeeditorcontainer.h" +#include "qgsattributeeditorrelation.h" /* @@ -219,9 +224,12 @@ QVariant AttributeFormTreeItem::data( int role ) const return QVariant::fromValue( mItemData ); case QgsAttributesAvailableWidgetsModel::FieldPropertiesRoles::FieldConfigRole: return QVariant::fromValue( mFieldConfigData ); - case QgsAttributesAvailableWidgetsModel::FieldPropertiesRoles::FieldNameRole: + case QgsAttributesAvailableWidgetsModel::FieldPropertiesRoles::TreeItemNameRole: + return mName; + case QgsAttributesAvailableWidgetsModel::FieldPropertiesRoles::TreeItemIdRole: + return mItemId; default: - return mFieldNameData; + return QVariant(); } return QVariant(); } @@ -240,12 +248,21 @@ bool AttributeFormTreeItem::setData( int role, const QVariant &value ) mFieldConfigData = value.value< QgsAttributeFormTreeData::FieldConfig >(); return true; } - case QgsAttributesAvailableWidgetsModel::FieldPropertiesRoles::FieldNameRole: + case QgsAttributesAvailableWidgetsModel::FieldPropertiesRoles::TreeItemNameRole: { - mFieldNameData = value.toString(); + mName = value.toString(); return true; } case QgsAttributesAvailableWidgetsModel::FieldPropertiesRoles::TreeItemTypeRole: + { + mItemType = static_cast( value.toInt() ); + return true; + } + case QgsAttributesAvailableWidgetsModel::FieldPropertiesRoles::TreeItemIdRole: + { + mItemId = value.toString(); + return true; + } default: return false; } @@ -278,7 +295,6 @@ QgsAttributesAvailableWidgetsModel::QgsAttributesAvailableWidgetsModel( QgsVecto , mProject( project ) , mRootItem( std::make_unique< AttributeFormTreeItem >() ) { - rebuild(); } QgsAttributesAvailableWidgetsModel::~QgsAttributesAvailableWidgetsModel() = default; @@ -299,7 +315,7 @@ AttributeFormTreeItem *QgsAttributesAvailableWidgetsModel::getItem( const QModel return mRootItem.get(); } -void QgsAttributesAvailableWidgetsModel::rebuild() +void QgsAttributesAvailableWidgetsModel::populate() { beginResetModel(); mRootItem->deleteChildren(); @@ -317,9 +333,11 @@ void QgsAttributesAvailableWidgetsModel::rebuild() QgsAttributeFormTreeData::FieldConfig cfg( mLayer, i ); - auto item = std::make_unique< AttributeFormTreeItem >( QgsAttributeFormTreeData::AttributeFormTreeItemType::Field, itemData, field.name() ); + auto item = std::make_unique< AttributeFormTreeItem >(); item->setData( FieldConfigRole, cfg ); - item->setData( FieldNameRole, field.name() ); + item->setData( TreeItemNameRole, field.name() ); + item->setData( TreeItemTypeRole, QgsAttributeFormTreeData::Field ); + item->setData( DnDTreeRole, itemData ); item->setIcon( fields.iconForField( i, true ) ); itemFields->addChildItem( std::move( item ) ); @@ -348,29 +366,41 @@ void QgsAttributesAvailableWidgetsModel::rebuild() QgsAttributeFormTreeData::DnDTreeItemData itemData = QgsAttributeFormTreeData::DnDTreeItemData(); itemData.setShowLabel( true ); - auto itemRelation = std::make_unique< AttributeFormTreeItem >( QgsAttributeFormTreeData::AttributeFormTreeItemType::Relation, itemData, relation.id(), name ); - itemRelation->setData( FieldNameRole, relation.id() ); + auto itemRelation = std::make_unique< AttributeFormTreeItem >(); + itemRelation->setData( TreeItemTypeRole, QgsAttributeFormTreeData::Relation ); + itemRelation->setData( TreeItemNameRole, name ); + itemRelation->setData( TreeItemIdRole, relation.id() ); + itemRelation->setData( DnDTreeRole, itemData ); itemRelations->addChildItem( std::move( itemRelation ) ); } mRootItem->addChildItem( std::move( itemRelations ) ); - // Form actions - // catItemData = DnDTreeItemData( DnDTreeItemData::WidgetType, QStringLiteral( "Actions" ), tr( "Actions" ) ); - // catitem = mAvailableWidgetsTree->addItem( mAvailableWidgetsTree->invisibleRootItem(), catItemData ); + // Load form actions - // const QList actions { mLayer->actions()->actions() }; + auto itemActions = std::make_unique< AttributeFormTreeItem >( QgsAttributeFormTreeData::AttributeFormTreeItemType::WidgetType, QStringLiteral( "Actions" ), tr( "Actions" ) ); + const QList actions { mLayer->actions()->actions() }; - // for ( const auto &action : std::as_const( actions ) ) - // { - // if ( action.isValid() && action.runable() && ( action.actionScopes().contains( QStringLiteral( "Feature" ) ) || action.actionScopes().contains( QStringLiteral( "Layer" ) ) ) ) - // { - // const QString actionTitle { action.shortTitle().isEmpty() ? action.name() : action.shortTitle() }; - // DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Action, action.id().toString(), actionTitle ); - // itemData.setShowLabel( true ); - // mAvailableWidgetsTree->addItem( catitem, itemData ); - // } - // } + for ( const auto &action : std::as_const( actions ) ) + { + if ( action.isValid() && action.runable() && ( action.actionScopes().contains( QStringLiteral( "Feature" ) ) || action.actionScopes().contains( QStringLiteral( "Layer" ) ) ) ) + { + const QString actionTitle { action.shortTitle().isEmpty() ? action.name() : action.shortTitle() }; + + QgsAttributeFormTreeData::DnDTreeItemData itemData = QgsAttributeFormTreeData::DnDTreeItemData(); + itemData.setShowLabel( true ); + + auto itemAction = std::make_unique< AttributeFormTreeItem >(); + itemAction->setData( TreeItemIdRole, action.id().toString() ); + itemAction->setData( TreeItemTypeRole, QgsAttributeFormTreeData::Action ); + itemAction->setData( TreeItemNameRole, actionTitle ); + itemAction->setData( DnDTreeRole, itemData ); + + itemActions->addChildItem( std::move( itemAction ) ); + } + } + + mRootItem->addChildItem( std::move( itemActions ) ); // QML/HTML widgets @@ -459,7 +489,7 @@ QVariant QgsAttributesAvailableWidgetsModel::data( const QModelIndex &index, int { case Qt::DisplayRole: { - return item->displayName(); + return item->displayName().isEmpty() ? ( item->name().isEmpty() ? item->id() : item->name() ) : item->displayName(); } case Qt::ToolTipRole: @@ -488,8 +518,9 @@ QVariant QgsAttributesAvailableWidgetsModel::data( const QModelIndex &index, int case DnDTreeRole: case FieldConfigRole: - case FieldNameRole: + case TreeItemNameRole: case TreeItemTypeRole: + case TreeItemIdRole: return item->data( role ); default: @@ -501,7 +532,8 @@ bool QgsAttributesAvailableWidgetsModel::setData( const QModelIndex &index, cons { if ( role != QgsAttributesAvailableWidgetsModel::FieldPropertiesRoles::DnDTreeRole && role != QgsAttributesAvailableWidgetsModel::FieldPropertiesRoles::FieldConfigRole - && role != QgsAttributesAvailableWidgetsModel::FieldPropertiesRoles::FieldNameRole ) + && role != QgsAttributesAvailableWidgetsModel::FieldPropertiesRoles::TreeItemIdRole + && role != QgsAttributesAvailableWidgetsModel::FieldPropertiesRoles::TreeItemNameRole ) return false; AttributeFormTreeItem *item = getItem( index ); @@ -603,3 +635,448 @@ QModelIndex QgsAttributesAvailableWidgetsModel::getFieldModelIndex( const QStrin // endRemoveColumns(); // return true; // } + +// +// AttributeFormLayoutTreeItem implementation +// + +AttributeFormLayoutTreeItem::AttributeFormLayoutTreeItem( QgsAttributeFormTreeData::AttributeFormTreeItemType itemType, const QString &name, const QString &displayName, AttributeFormLayoutTreeItem *parent ) + : mName( name ) + , mDisplayName( !displayName.isEmpty() ? displayName : name ) + , mItemType( itemType ) + , mParent( parent ) +{} + +AttributeFormLayoutTreeItem::AttributeFormLayoutTreeItem( QgsAttributeFormTreeData::AttributeFormTreeItemType itemType, const QgsAttributeFormTreeData::DnDTreeItemData &data, const QString &name, const QString &displayName, AttributeFormLayoutTreeItem *parent ) + : mName( name ) + , mDisplayName( !displayName.isEmpty() ? displayName : name ) + , mItemType( itemType ) + , mItemData( data ) + , mParent( parent ) +{} + +AttributeFormLayoutTreeItem *AttributeFormLayoutTreeItem::child( int number ) +{ + if ( !mChildren.empty() && number >= 0 && number < childCount() ) + return mChildren.at( number ).get(); + + return nullptr; +} + +AttributeFormLayoutTreeItem *AttributeFormLayoutTreeItem::firstChild( const QString &name ) const +{ + if ( !mChildren.empty() && name.trimmed().isEmpty() ) + return nullptr; + + // Search for first matching item by name + const auto it = std::find_if( mChildren.cbegin(), mChildren.cend(), [name]( const std::unique_ptr< AttributeFormLayoutTreeItem > &treeItem ) { + return treeItem->name() == name; + } ); + + if ( it != mChildren.cend() ) + return it->get(); + + return nullptr; +} + +int AttributeFormLayoutTreeItem::childCount() const +{ + return int( mChildren.size() ); +} + +int AttributeFormLayoutTreeItem::row() const +{ + if ( !mParent ) + return 0; + + const auto it = std::find_if( mParent->mChildren.cbegin(), mParent->mChildren.cend(), [this]( const std::unique_ptr< AttributeFormLayoutTreeItem > &treeItem ) { + return treeItem.get() == this; + } ); + + if ( it != mParent->mChildren.cend() ) + return std::distance( mParent->mChildren.cbegin(), it ); + Q_ASSERT( false ); // should not happen + return -1; +} + +QVariant AttributeFormLayoutTreeItem::data( int role ) const +{ + switch ( role ) + { + case QgsAttributesFormLayoutModel::FieldPropertiesRoles::TreeItemTypeRole: + return mItemType; + case QgsAttributesFormLayoutModel::FieldPropertiesRoles::DnDTreeRole: + return QVariant::fromValue( mItemData ); + case QgsAttributesFormLayoutModel::FieldPropertiesRoles::TreeItemNameRole: + return mName; + case QgsAttributesFormLayoutModel::FieldPropertiesRoles::TreeItemIdRole: + return mItemId; + default: + return mItemType; + } + return QVariant(); +} + +bool AttributeFormLayoutTreeItem::setData( int role, const QVariant &value ) +{ + switch ( role ) + { + case QgsAttributesFormLayoutModel::FieldPropertiesRoles::DnDTreeRole: + { + mItemData = value.value< QgsAttributeFormTreeData::DnDTreeItemData >(); + return true; + } + case QgsAttributesFormLayoutModel::FieldPropertiesRoles::TreeItemNameRole: + { + mName = value.toString(); + return true; + } + case QgsAttributesFormLayoutModel::FieldPropertiesRoles::TreeItemIdRole: + { + mItemId = value.toString(); + return true; + } + case QgsAttributesFormLayoutModel::FieldPropertiesRoles::TreeItemTypeRole: + { + mItemType = static_cast< QgsAttributeFormTreeData::AttributeFormTreeItemType >( value.toInt() ); + return true; + } + default: + return false; + } + return false; +} + +void AttributeFormLayoutTreeItem::addChildItem( std::unique_ptr< AttributeFormLayoutTreeItem > &&item ) +{ + if ( !item ) + return; + + Q_ASSERT( !item->mParent ); + item->mParent = this; + + mChildren.push_back( std::move( item ) ); +} + +void AttributeFormLayoutTreeItem::deleteChildren() +{ + mChildren.clear(); +} + +// +// QgsAttributesFormLayoutModel +// + +QgsAttributesFormLayoutModel::QgsAttributesFormLayoutModel( QgsVectorLayer *layer, QObject *parent ) + : QAbstractItemModel( parent ) + , mLayer( layer ) + , mRootItem( std::make_unique< AttributeFormLayoutTreeItem >() ) +{ + populate(); +} + +QgsAttributesFormLayoutModel::~QgsAttributesFormLayoutModel() = default; + +QVariant QgsAttributesFormLayoutModel::headerData( int section, Qt::Orientation orientation, int role ) const +{ + Q_UNUSED( section ) + return orientation == Qt::Horizontal && role == Qt::DisplayRole ? tr( "Form Layout" ) : QVariant {}; +} + +AttributeFormLayoutTreeItem *QgsAttributesFormLayoutModel::getItem( const QModelIndex &index ) const +{ + if ( index.isValid() ) + { + if ( auto *item = static_cast( index.internalPointer() ) ) + return item; + } + return mRootItem.get(); +} + +void QgsAttributesFormLayoutModel::populate() +{ + beginResetModel(); + mRootItem->deleteChildren(); + + const auto editorElements = mLayer->editFormConfig().tabs(); + for ( QgsAttributeEditorElement *editorElement : editorElements ) + { + loadAttributeEditorElementItem( editorElement, mRootItem.get() ); + } + + endResetModel(); +} + +void QgsAttributesFormLayoutModel::loadAttributeEditorElementItem( QgsAttributeEditorElement *const editorElement, AttributeFormLayoutTreeItem *parent ) +{ + auto setCommonProperties = [editorElement]( QgsAttributeFormTreeData::DnDTreeItemData &itemData ) { + itemData.setShowLabel( editorElement->showLabel() ); + itemData.setLabelStyle( editorElement->labelStyle() ); + itemData.setHorizontalStretch( editorElement->horizontalStretch() ); + itemData.setVerticalStretch( editorElement->verticalStretch() ); + }; + + auto editorItem = std::make_unique< AttributeFormLayoutTreeItem >(); + + switch ( editorElement->type() ) + { + case Qgis::AttributeEditorType::Field: + { + QgsAttributeFormTreeData::DnDTreeItemData itemData = QgsAttributeFormTreeData::DnDTreeItemData(); + setCommonProperties( itemData ); + + editorItem->setData( TreeItemNameRole, editorElement->name() ); + editorItem->setData( TreeItemTypeRole, QgsAttributeFormTreeData::Field ); + editorItem->setData( DnDTreeRole, itemData ); + + break; + } + + case Qgis::AttributeEditorType::Action: + { + const QgsAttributeEditorAction *actionEditor = static_cast( editorElement ); + const QgsAction action { actionEditor->action( mLayer ) }; + if ( action.isValid() ) + { + QgsAttributeFormTreeData::DnDTreeItemData itemData = QgsAttributeFormTreeData::DnDTreeItemData(); + setCommonProperties( itemData ); + + editorItem->setData( TreeItemIdRole, action.id().toString() ); + editorItem->setData( TreeItemNameRole, action.shortTitle().isEmpty() ? action.name() : action.shortTitle() ); + editorItem->setData( TreeItemTypeRole, QgsAttributeFormTreeData::Action ); + editorItem->setData( DnDTreeRole, itemData ); + } + else + { + QgsDebugError( QStringLiteral( "Invalid form action" ) ); + } + break; + } + + case Qgis::AttributeEditorType::Relation: + { + QgsAttributeFormTreeData::DnDTreeItemData itemData = QgsAttributeFormTreeData::DnDTreeItemData(); + setCommonProperties( itemData ); + + const QgsAttributeEditorRelation *relationEditor = static_cast( editorElement ); + QgsAttributeFormTreeData::RelationEditorConfiguration relationEditorConfig; + relationEditorConfig.mRelationWidgetType = relationEditor->relationWidgetTypeId(); + relationEditorConfig.mRelationWidgetConfig = relationEditor->relationEditorConfiguration(); + relationEditorConfig.nmRelationId = relationEditor->nmRelationId(); + relationEditorConfig.forceSuppressFormPopup = relationEditor->forceSuppressFormPopup(); + relationEditorConfig.label = relationEditor->label(); + itemData.setRelationEditorConfiguration( relationEditorConfig ); + + editorItem->setData( TreeItemIdRole, relationEditor->relation().id() ); + editorItem->setData( TreeItemNameRole, relationEditor->relation().name() ); + editorItem->setData( TreeItemTypeRole, QgsAttributeFormTreeData::Relation ); + editorItem->setData( DnDTreeRole, itemData ); + + break; + } + + case Qgis::AttributeEditorType::Container: + { + QgsAttributeFormTreeData::DnDTreeItemData itemData = QgsAttributeFormTreeData::DnDTreeItemData(); + setCommonProperties( itemData ); + + editorItem->setData( TreeItemNameRole, editorElement->name() ); + editorItem->setData( TreeItemTypeRole, QgsAttributeFormTreeData::Container ); + + const QgsAttributeEditorContainer *container = static_cast( editorElement ); + if ( !container ) + break; + + itemData.setColumnCount( container->columnCount() ); + itemData.setContainerType( container->type() ); + itemData.setBackgroundColor( container->backgroundColor() ); + itemData.setVisibilityExpression( container->visibilityExpression() ); + itemData.setCollapsedExpression( container->collapsedExpression() ); + itemData.setCollapsed( container->collapsed() ); + + editorItem->setData( DnDTreeRole, itemData ); + + const QList children = container->children(); + for ( QgsAttributeEditorElement *childElement : children ) + { + loadAttributeEditorElementItem( childElement, editorItem.get() ); + } + break; + } + + // case Qgis::AttributeEditorType::QmlElement: + // { + // const QgsAttributeEditorQmlElement *qmlElementEditor = static_cast( editorElement ); + // DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::QmlWidget, editorElement->name(), editorElement->name() ); + // QmlElementEditorConfiguration qmlEdConfig; + // qmlEdConfig.qmlCode = qmlElementEditor->qmlCode(); + // itemData.setQmlElementEditorConfiguration( qmlEdConfig ); + // setCommonProperties( itemData ); + // newWidget = tree->addItem( parent, itemData ); + // break; + // } + + // case Qgis::AttributeEditorType::HtmlElement: + // { + // const QgsAttributeEditorHtmlElement *htmlElementEditor = static_cast( editorElement ); + // DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::HtmlWidget, editorElement->name(), editorElement->name() ); + // HtmlElementEditorConfiguration htmlEdConfig; + // htmlEdConfig.htmlCode = htmlElementEditor->htmlCode(); + // itemData.setHtmlElementEditorConfiguration( htmlEdConfig ); + // setCommonProperties( itemData ); + // newWidget = tree->addItem( parent, itemData ); + // break; + // } + + // case Qgis::AttributeEditorType::TextElement: + // { + // const QgsAttributeEditorTextElement *textElementEditor = static_cast( editorElement ); + // DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::TextWidget, editorElement->name(), editorElement->name() ); + // TextElementEditorConfiguration textEdConfig; + // textEdConfig.text = textElementEditor->text(); + // itemData.setTextElementEditorConfiguration( textEdConfig ); + // setCommonProperties( itemData ); + // newWidget = tree->addItem( parent, itemData ); + // break; + // } + + // case Qgis::AttributeEditorType::SpacerElement: + // { + // const QgsAttributeEditorSpacerElement *spacerElementEditor = static_cast( editorElement ); + // DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::SpacerWidget, editorElement->name(), editorElement->name() ); + // SpacerElementEditorConfiguration spacerEdConfig; + // spacerEdConfig.drawLine = spacerElementEditor->drawLine(); + // itemData.setSpacerElementEditorConfiguration( spacerEdConfig ); + // setCommonProperties( itemData ); + // itemData.setShowLabel( false ); + // newWidget = tree->addItem( parent, itemData ); + // break; + // } + + // case Qgis::AttributeEditorType::Invalid: + // { + // QgsDebugError( QStringLiteral( "Not loading invalid attribute editor type..." ) ); + // break; + // } + } + + parent->addChildItem( std::move( editorItem ) ); + + // if ( newWidget ) + // newWidget->setExpanded( true ); + + // return newWidget; +} + +int QgsAttributesFormLayoutModel::rowCount( const QModelIndex &parent ) const +{ + if ( parent.isValid() && parent.column() > 0 ) + return 0; + + const AttributeFormLayoutTreeItem *parentItem = getItem( parent ); + + return parentItem ? parentItem->childCount() : 0; +} + +int QgsAttributesFormLayoutModel::columnCount( const QModelIndex &parent ) const +{ + Q_UNUSED( parent ); + return 1; +} + +QModelIndex QgsAttributesFormLayoutModel::index( int row, int column, const QModelIndex &parent ) const +{ + if ( !hasIndex( row, column, parent ) ) + return QModelIndex(); + + AttributeFormLayoutTreeItem *parentItem = getItem( parent ); + if ( !parentItem ) + return QModelIndex(); + + if ( AttributeFormLayoutTreeItem *childItem = parentItem->child( row ) ) + return createIndex( row, column, childItem ); + + return QModelIndex(); +} + +QModelIndex QgsAttributesFormLayoutModel::parent( const QModelIndex &index ) const +{ + if ( !index.isValid() ) + return QModelIndex(); + + AttributeFormLayoutTreeItem *childItem = getItem( index ); + AttributeFormLayoutTreeItem *parentItem = childItem ? childItem->parent() : nullptr; + + return ( parentItem != mRootItem.get() && parentItem != nullptr ) + ? createIndex( parentItem->row(), 0, parentItem ) + : QModelIndex {}; +} + +QVariant QgsAttributesFormLayoutModel::data( const QModelIndex &index, int role ) const +{ + if ( !index.isValid() ) + return QVariant(); + + // if ( role != Qt::DisplayRole && role != Qt::EditRole ) + // return QVariant(); + + const AttributeFormLayoutTreeItem *item = getItem( index ); + if ( !item ) + return QVariant(); + + switch ( role ) + { + case Qt::DisplayRole: + { + return item->displayName().isEmpty() ? ( item->name().isEmpty() ? item->id() : item->name() ) : item->displayName(); + } + + case Qt::ToolTipRole: + { + if ( item->type() == QgsAttributeFormTreeData::AttributeFormTreeItemType::Field ) + { + // const auto cfg = item->data( FieldPropertiesRoles::FieldConfigRole ).value(); + // if ( !cfg.mAlias.isEmpty() ) + // return tr( "%1 (%2)" ).arg( item->name(), cfg.mAlias ); + // else + return item->name(); + } + return QVariant(); + } + + case Qt::DecorationRole: + return item->icon(); + + case Qt::BackgroundRole: + { + if ( item->type() == QgsAttributeFormTreeData::AttributeFormTreeItemType::Container ) + return QBrush( Qt::lightGray ); + + return QVariant(); + } + + case DnDTreeRole: + //case FieldConfigRole: + //case TreeItemNameRole: + case TreeItemTypeRole: + return item->data( role ); + + default: + return QVariant(); + } +} + +bool QgsAttributesFormLayoutModel::setData( const QModelIndex &index, const QVariant &value, int role ) +{ + if ( role != QgsAttributesFormLayoutModel::FieldPropertiesRoles::DnDTreeRole + && role != QgsAttributesFormLayoutModel::FieldPropertiesRoles::TreeItemTypeRole ) // TODO + return false; + + AttributeFormLayoutTreeItem *item = getItem( index ); + bool result = item->setData( role, value ); + + if ( result ) + emit dataChanged( index, index, { role } ); + + return result; +} diff --git a/src/gui/vector/qgsattributesformmodel.h b/src/gui/vector/qgsattributesformmodel.h index 1eb59574ec1..d12ecd219f2 100644 --- a/src/gui/vector/qgsattributesformmodel.h +++ b/src/gui/vector/qgsattributesformmodel.h @@ -87,7 +87,6 @@ class QgsAttributeFormTreeData class DnDTreeItemData { public: - //do we need that DnDTreeItemData() = default; // DnDTreeItemData( const QColor &backgroundColor = QColor() ) @@ -309,7 +308,6 @@ class QgsAttributeFormTreeData }; }; - class AttributeFormTreeItem { public: @@ -341,6 +339,9 @@ class AttributeFormTreeItem QgsAttributeFormTreeData::AttributeFormTreeItemType type() const { return mItemType; } + QString id() const { return mItemId; } + void setId( const QString &itemId ) { mItemId = itemId; } + QString name() const { return mName; } void setName( const QString &name ) { mName = name; } @@ -357,10 +358,10 @@ class AttributeFormTreeItem std::vector< std::unique_ptr< AttributeFormTreeItem > > mChildren; QgsAttributeFormTreeData::AttributeFormTreeItemType mItemType; + QString mItemId; QgsAttributeFormTreeData::DnDTreeItemData mItemData; QgsAttributeFormTreeData::FieldConfig mFieldConfigData; - QString mFieldNameData; AttributeFormTreeItem *mParent; }; @@ -375,8 +376,9 @@ class QgsAttributesAvailableWidgetsModel : public QAbstractItemModel { DnDTreeRole = Qt::UserRole, FieldConfigRole, - FieldNameRole, + TreeItemNameRole, TreeItemTypeRole, + TreeItemIdRole, // Useful for elements like actions }; explicit QgsAttributesAvailableWidgetsModel( QgsVectorLayer *layer, QgsProject *project, QObject *parent = nullptr ); @@ -408,7 +410,7 @@ class QgsAttributesAvailableWidgetsModel : public QAbstractItemModel QModelIndex getFieldModelIndex( const QString &fieldName ) const; public slots: - void rebuild(); + void populate(); private: AttributeFormTreeItem *getItem( const QModelIndex &index ) const; @@ -452,6 +454,121 @@ class QgsAttributesAvailableWidgetsModel : public QAbstractItemModel // }; +class AttributeFormLayoutTreeItem +{ + public: + AttributeFormLayoutTreeItem() = default; + explicit AttributeFormLayoutTreeItem( QgsAttributeFormTreeData::AttributeFormTreeItemType itemType, const QString &name, const QString &displayName = QString(), AttributeFormLayoutTreeItem *parent = nullptr ); + explicit AttributeFormLayoutTreeItem( QgsAttributeFormTreeData::AttributeFormTreeItemType itemType, const QgsAttributeFormTreeData::DnDTreeItemData &data, const QString &name, const QString &displayName = QString(), AttributeFormLayoutTreeItem *parent = nullptr ); + + AttributeFormLayoutTreeItem *child( int number ); + AttributeFormLayoutTreeItem *firstChild( const QString &name ) const; + int childCount() const; + //bool insertChildren(int position, int count, int columns); + //bool insertColumns(int position, int columns); + AttributeFormLayoutTreeItem *parent() { return mParent; } + //bool removeChildren(int position, int count); + //bool removeColumns(int position, int columns); + int row() const; + QVariant data( int role ) const; + bool setData( int role, const QVariant &value ); + + /** + * Adds a \a child to this item. Takes ownership of the child. + */ + void addChildItem( std::unique_ptr< AttributeFormLayoutTreeItem > &&child ); + + /** + * Deletes all child items from this item. + */ + void deleteChildren(); + + QgsAttributeFormTreeData::AttributeFormTreeItemType type() const { return mItemType; } + + QString id() const { return mItemId; } + void setId( const QString &itemId ) { mItemId = itemId; } + + QString name() const { return mName; } + void setName( const QString &name ) { mName = name; } + + QString displayName() const { return mDisplayName; } + void setDisplayName( const QString &displayName ) { mDisplayName = displayName; } + + QIcon icon() const { return mIcon; } + void setIcon( const QIcon &icon ) { mIcon = icon; } + + private: + QString mItemId; + QString mName; + QString mDisplayName; + QIcon mIcon; + + std::vector< std::unique_ptr< AttributeFormLayoutTreeItem > > mChildren; + QgsAttributeFormTreeData::AttributeFormTreeItemType mItemType; + + QgsAttributeFormTreeData::DnDTreeItemData mItemData; + QgsAttributeFormTreeData::FieldConfig mFieldConfigData; + + AttributeFormLayoutTreeItem *mParent; +}; + + +class QgsAttributesFormLayoutModel : public QAbstractItemModel +{ + Q_OBJECT + + public: + enum FieldPropertiesRoles + { + DnDTreeRole = Qt::UserRole, + //FieldConfigRole, + TreeItemNameRole, + TreeItemIdRole, // Items may have ids, to ease comparison. Used by Relations, fields and actions. + TreeItemTypeRole, + }; + + explicit QgsAttributesFormLayoutModel( QgsVectorLayer *layer, QObject *parent = nullptr ); + + ~QgsAttributesFormLayoutModel() override; + + // Header: + QVariant headerData( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const override; + + // Basic functionality: + QModelIndex index( int row, int column, const QModelIndex &parent = QModelIndex() ) const override; + QModelIndex parent( const QModelIndex &index ) const override; + + int rowCount( const QModelIndex &parent = QModelIndex() ) const override; + int columnCount( const QModelIndex &parent = QModelIndex() ) const override; + + QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const override; + bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole ) override; + + // Add data: + //bool insertRows( int row, int count, const QModelIndex &parent = QModelIndex() ) override; + //bool insertColumns( int column, int count, const QModelIndex &parent = QModelIndex() ) override; + + // Remove data: + //bool removeRows( int row, int count, const QModelIndex &parent = QModelIndex() ) override; + //bool removeColumns( int column, int count, const QModelIndex &parent = QModelIndex() ) override; + + // Other methods + //QModelIndex getFieldModelIndex( const QString &fieldName ) const; + + void loadAttributeEditorElementItem( QgsAttributeEditorElement *const editorElement, AttributeFormLayoutTreeItem *parent ); + + public slots: + void populate(); + + private: + AttributeFormLayoutTreeItem *getItem( const QModelIndex &index ) const; + + QgsVectorLayer *mLayer; + QgsProject *mProject; + std::unique_ptr< AttributeFormLayoutTreeItem > mRootItem; +}; + + Q_DECLARE_METATYPE( QgsAttributeFormTreeData::RelationEditorConfiguration ) Q_DECLARE_METATYPE( QgsAttributeFormTreeData::FieldConfig ) Q_DECLARE_METATYPE( QgsAttributeFormTreeData::DnDTreeItemData ) diff --git a/src/gui/vector/qgsattributesformproperties.cpp b/src/gui/vector/qgsattributesformproperties.cpp index 913697b9ec7..a954c591dc6 100644 --- a/src/gui/vector/qgsattributesformproperties.cpp +++ b/src/gui/vector/qgsattributesformproperties.cpp @@ -62,12 +62,11 @@ QgsAttributesFormProperties::QgsAttributesFormProperties( QgsVectorLayer *layer, // available widgets tree QGridLayout *availableWidgetsWidgetLayout = new QGridLayout; mAvailableWidgetsTree = new QgsAttributesDnDTree( mLayer ); - mAvailableWidgetsTreeView = new QTreeView( this ); + mAvailableWidgetsTreeView = new QgsAttributesFormTreeView( mLayer, this ); availableWidgetsWidgetLayout->addWidget( mAvailableWidgetsTreeView ); availableWidgetsWidgetLayout->setContentsMargins( 0, 0, 0, 0 ); mAvailableWidgetsWidget->setLayout( availableWidgetsWidgetLayout ); mAvailableWidgetsTreeView->setSelectionMode( QAbstractItemView::SelectionMode::ExtendedSelection ); - //mAvailableWidgetsTree->setHeaderLabels( QStringList() << tr( "Available Widgets" ) ); //mAvailableWidgetsTree->setType( QgsAttributesDnDTree::Type::Drag ); //mAvailableWidgetsTree->setContextMenuPolicy( Qt::CustomContextMenu ); @@ -77,17 +76,21 @@ QgsAttributesFormProperties::QgsAttributesFormProperties( QgsVectorLayer *layer, // form layout tree QGridLayout *formLayoutWidgetLayout = new QGridLayout; mFormLayoutTree = new QgsAttributesDnDTree( mLayer ); + mFormLayoutTreeView = new QgsAttributesFormTreeView( mLayer, this ); mFormLayoutWidget->setLayout( formLayoutWidgetLayout ); - formLayoutWidgetLayout->addWidget( mFormLayoutTree ); + formLayoutWidgetLayout->addWidget( mFormLayoutTreeView ); formLayoutWidgetLayout->setContentsMargins( 0, 0, 0, 0 ); - mFormLayoutTree->setHeaderLabels( QStringList() << tr( "Form Layout" ) ); - mFormLayoutTree->setType( QgsAttributesDnDTree::Type::Drop ); + //mFormLayoutTree->setType( QgsAttributesDnDTree::Type::Drop ); + + mFormLayoutModel = new QgsAttributesFormLayoutModel( mLayer, this ); + mFormLayoutTreeView->setModel( mFormLayoutModel ); connect( mAvailableWidgetsTreeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged ); //connect( mAvailableWidgetsTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged ); //connect( mAvailableWidgetsTree, &QWidget::customContextMenuRequested, this, &QgsAttributesFormProperties::onContextMenuRequested ); connect( mFormLayoutTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged ); + connect( mAddTabOrGroupButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::addContainer ); connect( mRemoveTabOrGroupButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::removeTabOrGroupButton ); connect( mInvertSelectionButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::onInvertSelectionButtonClicked ); @@ -119,7 +122,7 @@ QgsAttributesFormProperties::QgsAttributesFormProperties( QgsVectorLayer *layer, void QgsAttributesFormProperties::init() { initAvailableWidgetsTree(); - //initFormLayoutTree(); + initFormLayoutTree(); initLayoutConfig(); initInitPython(); @@ -133,7 +136,7 @@ void QgsAttributesFormProperties::initAvailableWidgetsTree() mAvailableWidgetsTreeView->setAcceptDrops( false ); mAvailableWidgetsTreeView->setDragDropMode( QAbstractItemView::DragOnly ); - //mAvailableWidgetsModel->rebuild(); + mAvailableWidgetsModel->populate(); } void QgsAttributesFormProperties::initFormLayoutTree() @@ -146,11 +149,7 @@ void QgsAttributesFormProperties::initFormLayoutTree() mFormLayoutTree->setAcceptDrops( true ); mFormLayoutTree->setDragDropMode( QAbstractItemView::DragDrop ); - const auto constTabs = mLayer->editFormConfig().tabs(); - for ( QgsAttributeEditorElement *wdg : constTabs ) - { - loadAttributeEditorTreeItem( wdg, mFormLayoutTree->invisibleRootItem(), mFormLayoutTree ); - } + mFormLayoutModel->populate(); } @@ -223,7 +222,7 @@ void QgsAttributesFormProperties::loadAttributeTypeDialog() QModelIndex index = mAvailableWidgetsTreeView->selectionModel()->selectedRows().at( 0 ); const QgsAttributeFormTreeData::FieldConfig cfg = mAvailableWidgetsModel->data( index, QgsAttributesAvailableWidgetsModel::FieldPropertiesRoles::FieldConfigRole ).value< QgsAttributeFormTreeData::FieldConfig >(); - const QString fieldName = mAvailableWidgetsModel->data( index, QgsAttributesAvailableWidgetsModel::FieldPropertiesRoles::FieldNameRole ).toString(); + const QString fieldName = mAvailableWidgetsModel->data( index, QgsAttributesAvailableWidgetsModel::FieldPropertiesRoles::TreeItemNameRole ).toString(); const int fieldIndex = mLayer->fields().indexOf( fieldName ); if ( fieldIndex < 0 ) @@ -347,7 +346,7 @@ void QgsAttributesFormProperties::storeAttributeTypeDialog() // for ( auto itemIt = QTreeWidgetItemIterator( mAvailableWidgetsTree ); *itemIt; ++itemIt ) // { // QTreeWidgetItem *item = *itemIt; - // if ( item->data( 0, FieldNameRole ).toString() == fieldName ) + // if ( item->data( 0, TreeItemNameRole ).toString() == fieldName ) // item->setData( 0, FieldConfigRole, QVariant::fromValue( cfg ) ); // } } @@ -399,154 +398,6 @@ void QgsAttributesFormProperties::loadAttributeContainerEdit() mAttributeTypeFrame->layout()->addWidget( mAttributeContainerEdit ); } -QTreeWidgetItem *QgsAttributesFormProperties::loadAttributeEditorTreeItem( QgsAttributeEditorElement *const widgetDef, QTreeWidgetItem *parent, QgsAttributesDnDTree *tree ) -{ - Q_UNUSED( widgetDef ); - Q_UNUSED( parent ); - Q_UNUSED( tree ); - // auto setCommonProperties = [widgetDef]( DnDTreeItemData &itemData ) { - // itemData.setShowLabel( widgetDef->showLabel() ); - // itemData.setLabelStyle( widgetDef->labelStyle() ); - // itemData.setHorizontalStretch( widgetDef->horizontalStretch() ); - // itemData.setVerticalStretch( widgetDef->verticalStretch() ); - // }; - - // QTreeWidgetItem *newWidget = nullptr; - // switch ( widgetDef->type() ) - // { - // case Qgis::AttributeEditorType::Field: - // { - // DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Field, widgetDef->name(), widgetDef->name() ); - // setCommonProperties( itemData ); - // newWidget = tree->addItem( parent, itemData ); - // break; - // } - - // case Qgis::AttributeEditorType::Action: - // { - // const QgsAttributeEditorAction *actionEditor = static_cast( widgetDef ); - // const QgsAction action { actionEditor->action( mLayer ) }; - // if ( action.isValid() ) - // { - // DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Action, action.id().toString(), action.shortTitle().isEmpty() ? action.name() : action.shortTitle() ); - // setCommonProperties( itemData ); - // newWidget = tree->addItem( parent, itemData ); - // } - // else - // { - // QgsDebugError( QStringLiteral( "Invalid form action" ) ); - // } - // break; - // } - - // case Qgis::AttributeEditorType::Relation: - // { - // const QgsAttributeEditorRelation *relationEditor = static_cast( widgetDef ); - // DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Relation, relationEditor->relation().id(), relationEditor->relation().name() ); - // setCommonProperties( itemData ); - - // RelationEditorConfiguration relEdConfig; - // // relEdConfig.buttons = relationEditor->visibleButtons(); - // relEdConfig.mRelationWidgetType = relationEditor->relationWidgetTypeId(); - // relEdConfig.mRelationWidgetConfig = relationEditor->relationEditorConfiguration(); - // relEdConfig.nmRelationId = relationEditor->nmRelationId(); - // relEdConfig.forceSuppressFormPopup = relationEditor->forceSuppressFormPopup(); - // relEdConfig.label = relationEditor->label(); - // itemData.setRelationEditorConfiguration( relEdConfig ); - // newWidget = tree->addItem( parent, itemData ); - // break; - // } - - // case Qgis::AttributeEditorType::Container: - // { - // DnDTreeItemData itemData( DnDTreeItemData::Container, widgetDef->name(), widgetDef->name() ); - - // const QgsAttributeEditorContainer *container = static_cast( widgetDef ); - // if ( !container ) - // break; - - // itemData.setColumnCount( container->columnCount() ); - // itemData.setContainerType( container->type() ); - // itemData.setBackgroundColor( container->backgroundColor() ); - // itemData.setVisibilityExpression( container->visibilityExpression() ); - // itemData.setCollapsedExpression( container->collapsedExpression() ); - // itemData.setCollapsed( container->collapsed() ); - - // setCommonProperties( itemData ); - - // newWidget = tree->addItem( parent, itemData ); - - // const QList children = container->children(); - // for ( QgsAttributeEditorElement *wdg : children ) - // { - // loadAttributeEditorTreeItem( wdg, newWidget, tree ); - // } - // break; - // } - - // case Qgis::AttributeEditorType::QmlElement: - // { - // const QgsAttributeEditorQmlElement *qmlElementEditor = static_cast( widgetDef ); - // DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::QmlWidget, widgetDef->name(), widgetDef->name() ); - // QmlElementEditorConfiguration qmlEdConfig; - // qmlEdConfig.qmlCode = qmlElementEditor->qmlCode(); - // itemData.setQmlElementEditorConfiguration( qmlEdConfig ); - // setCommonProperties( itemData ); - // newWidget = tree->addItem( parent, itemData ); - // break; - // } - - // case Qgis::AttributeEditorType::HtmlElement: - // { - // const QgsAttributeEditorHtmlElement *htmlElementEditor = static_cast( widgetDef ); - // DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::HtmlWidget, widgetDef->name(), widgetDef->name() ); - // HtmlElementEditorConfiguration htmlEdConfig; - // htmlEdConfig.htmlCode = htmlElementEditor->htmlCode(); - // itemData.setHtmlElementEditorConfiguration( htmlEdConfig ); - // setCommonProperties( itemData ); - // newWidget = tree->addItem( parent, itemData ); - // break; - // } - - // case Qgis::AttributeEditorType::TextElement: - // { - // const QgsAttributeEditorTextElement *textElementEditor = static_cast( widgetDef ); - // DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::TextWidget, widgetDef->name(), widgetDef->name() ); - // TextElementEditorConfiguration textEdConfig; - // textEdConfig.text = textElementEditor->text(); - // itemData.setTextElementEditorConfiguration( textEdConfig ); - // setCommonProperties( itemData ); - // newWidget = tree->addItem( parent, itemData ); - // break; - // } - - // case Qgis::AttributeEditorType::SpacerElement: - // { - // const QgsAttributeEditorSpacerElement *spacerElementEditor = static_cast( widgetDef ); - // DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::SpacerWidget, widgetDef->name(), widgetDef->name() ); - // SpacerElementEditorConfiguration spacerEdConfig; - // spacerEdConfig.drawLine = spacerElementEditor->drawLine(); - // itemData.setSpacerElementEditorConfiguration( spacerEdConfig ); - // setCommonProperties( itemData ); - // itemData.setShowLabel( false ); - // newWidget = tree->addItem( parent, itemData ); - // break; - // } - - // case Qgis::AttributeEditorType::Invalid: - // { - // QgsDebugError( QStringLiteral( "Not loading invalid attribute editor type..." ) ); - // break; - // } - // } - - // if ( newWidget ) - // newWidget->setExpanded( true ); - - // return newWidget; -} - - void QgsAttributesFormProperties::onAttributeSelectionChanged() { disconnect( mFormLayoutTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged ); @@ -583,9 +434,10 @@ void QgsAttributesFormProperties::loadAttributeSpecificEditor( QTreeView *emitte } else { - auto indexType = static_cast< QgsAttributeFormTreeData::AttributeFormTreeItemType >( emitter->selectionModel()->selectedRows().at( 0 ).data( QgsAttributesAvailableWidgetsModel::TreeItemTypeRole ).toInt() ); + const QModelIndex index = emitter->selectionModel()->selectedRows().at( 0 ); + auto indexType = static_cast< QgsAttributeFormTreeData::AttributeFormTreeItemType >( index.data( QgsAttributesAvailableWidgetsModel::TreeItemTypeRole ).toInt() ); //emitter->selectionModel()->selectedIndexes().at( 0 ) - //const DnDTreeItemData itemData = emitter->selectedItems().at( 0 )->data( 0, DnDTreeRole ).value(); + //const QgsAttributeFormTreeData::DnDTreeItemData itemData = index.data( QgsAttributesAvailableWidgetsModel::DnDTreeRole ).value(); switch ( indexType ) { // case DnDTreeItemData::Relation: @@ -617,13 +469,13 @@ void QgsAttributesFormProperties::loadAttributeSpecificEditor( QTreeView *emitte // loadAttributeContainerEdit(); // break; // } - // case DnDTreeItemData::Action: - // { - // receiver->selectFirstMatchingItem( itemData ); - // const QgsAction action { mLayer->actions()->action( itemData.name() ) }; - // loadInfoWidget( action.html() ); - // break; - // } + case QgsAttributeFormTreeData::Action: + { + //receiver->selectFirstMatchingItem( itemData ); + const QgsAction action { mLayer->actions()->action( index.data( QgsAttributesAvailableWidgetsModel::TreeItemIdRole ).toString() ) }; + loadInfoWidget( action.html() ); + break; + } case QgsAttributeFormTreeData::AttributeFormTreeItemType::QmlWidget: case QgsAttributeFormTreeData::AttributeFormTreeItemType::HtmlWidget: case QgsAttributeFormTreeData::AttributeFormTreeItemType::TextWidget: @@ -927,7 +779,7 @@ void QgsAttributesFormProperties::apply() index = mAvailableWidgetsModel->index( i, 0, fieldContainer ); const QgsAttributeFormTreeData::FieldConfig cfg = mAvailableWidgetsModel->data( index, QgsAttributesAvailableWidgetsModel::FieldPropertiesRoles::FieldConfigRole ).value(); - const QString fieldName = mAvailableWidgetsModel->data( index, QgsAttributesAvailableWidgetsModel::FieldPropertiesRoles::FieldNameRole ).toString(); + const QString fieldName = mAvailableWidgetsModel->data( index, QgsAttributesAvailableWidgetsModel::FieldPropertiesRoles::TreeItemNameRole ).toString(); const int idx = mLayer->fields().indexOf( fieldName ); //continue in case field does not exist anymore @@ -1030,40 +882,6 @@ void QgsAttributesFormProperties::apply() } -/* - * FieldConfig implementation - */ -QgsAttributesFormProperties::FieldConfig::FieldConfig( QgsVectorLayer *layer, int idx ) -{ - mAlias = layer->fields().at( idx ).alias(); - mDataDefinedProperties = layer->editFormConfig().dataDefinedFieldProperties( layer->fields().at( idx ).name() ); - mComment = layer->fields().at( idx ).comment(); - mEditable = !layer->editFormConfig().readOnly( idx ); - mLabelOnTop = layer->editFormConfig().labelOnTop( idx ); - mReuseLastValues = layer->editFormConfig().reuseLastValue( idx ); - mFieldConstraints = layer->fields().at( idx ).constraints(); - const QgsEditorWidgetSetup setup = QgsGui::editorWidgetRegistry()->findBest( layer, layer->fields().field( idx ).name() ); - mEditorWidgetType = setup.type(); - mEditorWidgetConfig = setup.config(); - mSplitPolicy = layer->fields().at( idx ).splitPolicy(); - mDuplicatePolicy = layer->fields().at( idx ).duplicatePolicy(); - mMergePolicy = layer->fields().at( idx ).mergePolicy(); -} - -QgsAttributesFormProperties::FieldConfig::operator QVariant() -{ - return QVariant::fromValue( *this ); -} - -/* - * RelationEditorConfiguration implementation - */ - -QgsAttributesFormProperties::RelationEditorConfiguration::operator QVariant() -{ - return QVariant::fromValue( *this ); -} - /* * DnDTree implementation */ @@ -1093,53 +911,6 @@ QgsAttributesDnDTree::QgsAttributesDnDTree( QgsVectorLayer *layer, QWidget *pare connect( this, &QTreeWidget::itemDoubleClicked, this, &QgsAttributesDnDTree::onItemDoubleClicked ); } -// QTreeWidgetItem *QgsAttributesDnDTree::addItem( QTreeWidgetItem *parent, const QgsAttributesFormProperties::DnDTreeItemData &data, int index, const QIcon &icon ) -// { -// QTreeWidgetItem *newItem = new QTreeWidgetItem( QStringList() << data.name() ); - -// switch ( data.type() ) -// { -// case QgsAttributesFormProperties::DnDTreeItemData::Action: -// case QgsAttributesFormProperties::DnDTreeItemData::Field: -// case QgsAttributesFormProperties::DnDTreeItemData::Relation: -// case QgsAttributesFormProperties::DnDTreeItemData::QmlWidget: -// case QgsAttributesFormProperties::DnDTreeItemData::HtmlWidget: -// case QgsAttributesFormProperties::DnDTreeItemData::TextWidget: -// case QgsAttributesFormProperties::DnDTreeItemData::SpacerWidget: -// newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled ); -// break; - -// case QgsAttributesFormProperties::DnDTreeItemData::WidgetType: -// case QgsAttributesFormProperties::DnDTreeItemData::Container: -// { -// newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled ); -// newItem->setBackground( 0, QBrush( Qt::lightGray ) ); -// } -// break; -// } - -// newItem->setData( 0, QgsAttributesFormProperties::DnDTreeRole, QVariant::fromValue( data ) ); -// newItem->setText( 0, data.displayName() ); -// newItem->setIcon( 0, icon ); - -// if ( data.type() == QgsAttributesFormProperties::DnDTreeItemData::Relation ) -// { -// const QgsRelation relation = QgsProject::instance()->relationManager()->relation( data.name() ); -// if ( !relation.isValid() || relation.referencedLayer() != mLayer ) -// { -// newItem->setText( 0, tr( "Invalid relation" ) ); -// newItem->setForeground( 0, QColor( 255, 0, 0 ) ); -// } -// } - -// if ( index < 0 ) -// parent->addChild( newItem ); -// else -// parent->insertChild( index, newItem ); - -// return newItem; -// } - /** * Is called when mouse is moved over attributes tree before a * drop event. Used to inhibit dropping fields onto the root item. @@ -1828,7 +1599,6 @@ void QgsAttributesDnDTree::selectFirstMatchingItem( const QgsAttributeFormTreeDa // clearSelection(); } - /* * Serialization helpers for DesigerTreeItemData so we can stuff this easily into QMimeData */ @@ -1854,6 +1624,68 @@ void QgsAttributesDnDTree::selectFirstMatchingItem( const QgsAttributeFormTreeDa // return stream; // } + +// +// QgsAttributesFormTreeView implementation +// + +QgsAttributesFormTreeView::QgsAttributesFormTreeView( QgsVectorLayer *layer, QWidget *parent ) + : QTreeView( parent ) + , mLayer( layer ) +{ + connect( this, &QTreeView::doubleClicked, this, &QgsAttributesFormTreeView::onItemDoubleClicked ); +} + +void QgsAttributesFormTreeView::selectFirstMatchingItem( const QgsAttributeFormTreeData::DnDTreeItemData &data ) +{ + Q_UNUSED( data ) + // To be used with Relations, fields and actions + // QTreeWidgetItemIterator it( this ); + // while ( *it ) + // { + // const QgsAttributeFormTreeData::DnDTreeItemData rowData = ( *it )->data( 0, QgsAttributesFormProperties::DnDTreeRole ).value(); + // if ( data.type() == rowData.type() && data.name() == rowData.name() ) + // { + // if ( selectedItems().count() == 1 && ( *it )->isSelected() == true ) + // { + // // the selection is already good + // } + // else + // { + // clearSelection(); + // ( *it )->setSelected( true ); + // } + // return; + // } + // ++it; + // } + // clearSelection(); +} + +void QgsAttributesFormTreeView::onItemDoubleClicked( const QModelIndex &index ) +{ + Q_UNUSED( index ) + // TODO +} + +QgsExpressionContext QgsAttributesFormTreeView::createExpressionContext() const +{ + QgsExpressionContext expContext; + expContext << QgsExpressionContextUtils::globalScope() + << QgsExpressionContextUtils::projectScope( QgsProject::instance() ); + + if ( mLayer ) + expContext << QgsExpressionContextUtils::layerScope( mLayer ); + + expContext.appendScope( QgsExpressionContextUtils::formScope() ); + return expContext; +} + + +// +// QgsAttributesFormProperties implementation +// + void QgsAttributesFormProperties::updatedFields() { // Store configuration to insure changes made are kept after refreshing the list @@ -1862,7 +1694,7 @@ void QgsAttributesFormProperties::updatedFields() for ( int i = 0; i < fieldContainer->childCount(); i++ ) { QTreeWidgetItem *fieldItem = fieldContainer->child( i ); - const QString fieldName = fieldItem->data( 0, FieldNameRole ).toString(); + const QString fieldName = fieldItem->data( 0, QgsAttributesAvailableWidgetsModel::TreeItemNameRole ).toString(); const QgsAttributeFormTreeData::FieldConfig cfg = fieldItem->data( 0, FieldConfigRole ).value(); fieldConfigs[fieldName] = cfg; } @@ -1873,7 +1705,7 @@ void QgsAttributesFormProperties::updatedFields() for ( int i = 0; i < fieldContainer->childCount(); i++ ) { QTreeWidgetItem *fieldItem = fieldContainer->child( i ); - const QString fieldName = fieldItem->data( 0, FieldNameRole ).toString(); + const QString fieldName = fieldItem->data( 0, QgsAttributesAvailableWidgetsModel::TreeItemNameRole ).toString(); if ( fieldConfigs.contains( fieldName ) ) { fieldItem->setData( 0, FieldConfigRole, fieldConfigs[fieldName] ); @@ -1905,63 +1737,78 @@ void QgsAttributesFormProperties::copyWidgetConfiguration() // if ( mAvailableWidgetsTree->selectedItems().count() != 1 ) // return; - const QTreeWidgetItem *item = mAvailableWidgetsTree->selectedItems().at( 0 ); - const DnDTreeItemData itemData = item->data( 0, DnDTreeRole ).value(); - if ( itemData.type() != DnDTreeItemData::Field ) - return; + // const QTreeWidgetItem *item = mAvailableWidgetsTree->selectedItems().at( 0 ); + // const DnDTreeItemData itemData = item->data( 0, DnDTreeRole ).value(); + // if ( itemData.type() != DnDTreeItemData::Field ) + // return; - const QString fieldName = item->data( 0, FieldNameRole ).toString(); - const int index = mLayer->fields().indexOf( fieldName ); + // const QString fieldName = item->data( 0, TreeItemNameRole ).toString(); + // const int index = mLayer->fields().indexOf( fieldName ); - if ( index < 0 ) - return; + // if ( index < 0 ) + // return; - const QgsField field = mLayer->fields().field( index ); + // const QgsField field = mLayer->fields().field( index ); - // We won't copy field aliases nor comments - QDomDocument doc; - QDomElement documentElement = doc.createElement( QStringLiteral( "FormWidgetClipboard" ) ); - documentElement.setAttribute( QStringLiteral( "name" ), field.name() ); + // // We won't copy field aliases nor comments + // QDomDocument doc; + // QDomElement documentElement = doc.createElement( QStringLiteral( "FormWidgetClipboard" ) ); + // documentElement.setAttribute( QStringLiteral( "name" ), field.name() ); - // Editor widget setup - QgsEditorWidgetSetup widgetSetup = field.editorWidgetSetup(); + // // Editor widget setup + // QgsEditorWidgetSetup widgetSetup = field.editorWidgetSetup(); - QDomElement editWidgetElement = doc.createElement( QStringLiteral( "editWidget" ) ); - documentElement.appendChild( editWidgetElement ); - editWidgetElement.setAttribute( QStringLiteral( "type" ), widgetSetup.type() ); - QDomElement editWidgetConfigElement = doc.createElement( QStringLiteral( "config" ) ); + // QDomElement editWidgetElement = doc.createElement( QStringLiteral( "editWidget" ) ); + // documentElement.appendChild( editWidgetElement ); + // editWidgetElement.setAttribute( QStringLiteral( "type" ), widgetSetup.type() ); + // QDomElement editWidgetConfigElement = doc.createElement( QStringLiteral( "config" ) ); - editWidgetConfigElement.appendChild( QgsXmlUtils::writeVariant( widgetSetup.config(), doc ) ); - editWidgetElement.appendChild( editWidgetConfigElement ); + // editWidgetConfigElement.appendChild( QgsXmlUtils::writeVariant( widgetSetup.config(), doc ) ); + // editWidgetElement.appendChild( editWidgetConfigElement ); - // Split policy - QDomElement splitPolicyElement = doc.createElement( QStringLiteral( "splitPolicy" ) ); - splitPolicyElement.setAttribute( QStringLiteral( "policy" ), qgsEnumValueToKey( field.splitPolicy() ) ); - documentElement.appendChild( splitPolicyElement ); + // // Split policy + // QDomElement splitPolicyElement = doc.createElement( QStringLiteral( "splitPolicy" ) ); + // splitPolicyElement.setAttribute( QStringLiteral( "policy" ), qgsEnumValueToKey( field.splitPolicy() ) ); + // documentElement.appendChild( splitPolicyElement ); - // Duplicate policy - QDomElement duplicatePolicyElement = doc.createElement( QStringLiteral( "duplicatePolicy" ) ); - duplicatePolicyElement.setAttribute( QStringLiteral( "policy" ), qgsEnumValueToKey( field.duplicatePolicy() ) ); - documentElement.appendChild( duplicatePolicyElement ); + // // Duplicate policy + // QDomElement duplicatePolicyElement = doc.createElement( QStringLiteral( "duplicatePolicy" ) ); + // duplicatePolicyElement.setAttribute( QStringLiteral( "policy" ), qgsEnumValueToKey( field.duplicatePolicy() ) ); + // documentElement.appendChild( duplicatePolicyElement ); - // Merge policy - QDomElement mergePolicyElement = doc.createElement( QStringLiteral( "mergePolicy" ) ); - mergePolicyElement.setAttribute( QStringLiteral( "policy" ), qgsEnumValueToKey( field.mergePolicy() ) ); - documentElement.appendChild( mergePolicyElement ); + // // Default expressions + // QDomElement defaultElem = doc.createElement( QStringLiteral( "default" ) ); + // defaultElem.setAttribute( QStringLiteral( "expression" ), field.defaultValueDefinition().expression() ); + // defaultElem.setAttribute( QStringLiteral( "applyOnUpdate" ), field.defaultValueDefinition().applyOnUpdate() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ); + // documentElement.appendChild( defaultElem ); - // Default expressions - QDomElement defaultElem = doc.createElement( QStringLiteral( "default" ) ); - defaultElem.setAttribute( QStringLiteral( "expression" ), field.defaultValueDefinition().expression() ); - defaultElem.setAttribute( QStringLiteral( "applyOnUpdate" ), field.defaultValueDefinition().applyOnUpdate() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ); - documentElement.appendChild( defaultElem ); + // // Constraints + // QDomElement constraintElem = doc.createElement( QStringLiteral( "constraint" ) ); + // constraintElem.setAttribute( QStringLiteral( "constraints" ), field.constraints().constraints() ); + // constraintElem.setAttribute( QStringLiteral( "unique_strength" ), field.constraints().constraintStrength( QgsFieldConstraints::ConstraintUnique ) ); + // constraintElem.setAttribute( QStringLiteral( "notnull_strength" ), field.constraints().constraintStrength( QgsFieldConstraints::ConstraintNotNull ) ); + // constraintElem.setAttribute( QStringLiteral( "exp_strength" ), field.constraints().constraintStrength( QgsFieldConstraints::ConstraintExpression ) ); + // documentElement.appendChild( constraintElem ); - // Constraints - QDomElement constraintElem = doc.createElement( QStringLiteral( "constraint" ) ); - constraintElem.setAttribute( QStringLiteral( "constraints" ), field.constraints().constraints() ); - constraintElem.setAttribute( QStringLiteral( "unique_strength" ), field.constraints().constraintStrength( QgsFieldConstraints::ConstraintUnique ) ); - constraintElem.setAttribute( QStringLiteral( "notnull_strength" ), field.constraints().constraintStrength( QgsFieldConstraints::ConstraintNotNull ) ); - constraintElem.setAttribute( QStringLiteral( "exp_strength" ), field.constraints().constraintStrength( QgsFieldConstraints::ConstraintExpression ) ); - documentElement.appendChild( constraintElem ); + // // Constraint expressions + // QDomElement constraintExpressionElem = doc.createElement( QStringLiteral( "constraintExpression" ) ); + // constraintExpressionElem.setAttribute( QStringLiteral( "exp" ), field.constraints().constraintExpression() ); + // constraintExpressionElem.setAttribute( QStringLiteral( "desc" ), field.constraints().constraintDescription() ); + // documentElement.appendChild( constraintExpressionElem ); + + // // Widget general settings + // QDomElement widgetGeneralSettingsElem = doc.createElement( QStringLiteral( "widgetGeneralSettings" ) ); + // widgetGeneralSettingsElem.setAttribute( QStringLiteral( "editable" ), !mLayer->editFormConfig().readOnly( index ) ); + // widgetGeneralSettingsElem.setAttribute( QStringLiteral( "reuse_last_values" ), mLayer->editFormConfig().labelOnTop( index ) ); + // widgetGeneralSettingsElem.setAttribute( QStringLiteral( "label_on_top" ), mLayer->editFormConfig().reuseLastValue( index ) ); + // documentElement.appendChild( widgetGeneralSettingsElem ); + + // // Widget display section + // if ( mAttributeWidgetEdit ) + // { + // // Go for the corresponding form layout item and extract its display settings + // if ( mFormLayoutTree->selectedItems().count() != 1 ) + // return; // Constraint expressions QDomElement constraintExpressionElem = doc.createElement( QStringLiteral( "constraintExpression" ) ); diff --git a/src/gui/vector/qgsattributesformproperties.h b/src/gui/vector/qgsattributesformproperties.h index 8f673d594a8..0c8afdc84e7 100644 --- a/src/gui/vector/qgsattributesformproperties.h +++ b/src/gui/vector/qgsattributesformproperties.h @@ -422,6 +422,7 @@ class GUI_EXPORT QgsAttributesFormProperties : public QWidget, public QgsExpress QgsAttributesDnDTree *mFormLayoutTree = nullptr; QTreeView *mAvailableWidgetsTreeView = nullptr; + QTreeView *mFormLayoutTreeView = nullptr; QgsAttributeWidgetEdit *mAttributeWidgetEdit = nullptr; QgsAttributeTypeDialog *mAttributeTypeDialog = nullptr; @@ -467,6 +468,7 @@ class GUI_EXPORT QgsAttributesFormProperties : public QWidget, public QgsExpress QTreeWidgetItem *loadAttributeEditorTreeItem( QgsAttributeEditorElement *widgetDef, QTreeWidgetItem *parent, QgsAttributesDnDTree *tree ); QgsAttributesAvailableWidgetsModel *mAvailableWidgetsModel; + QgsAttributesFormLayoutModel *mFormLayoutModel; QgsMessageBar *mMessageBar = nullptr; @@ -561,4 +563,57 @@ class GUI_EXPORT QgsAttributesDnDTree : public QTreeWidget, private QgsExpressio }; +class GUI_EXPORT QgsAttributesFormTreeView : public QTreeView, private QgsExpressionContextGenerator +{ + Q_OBJECT + + public: + explicit QgsAttributesFormTreeView( QgsVectorLayer *layer, QWidget *parent = nullptr ); + + /** + * Adds a new item to a \a parent. If \a index is -1, the item is added to the end of the parent's existing children. + * Otherwise it is inserted at the specified \a index. + */ + //QTreeWidgetItem *addItem( QTreeWidgetItem *parent, const QgsAttributesFormProperties::DnDTreeItemData &data, int index = -1, const QIcon &icon = QIcon() ); + + enum Type + { + Drag, + Drop + }; + + + Type type() const; + void setType( QgsAttributesDnDTree::Type value ); + + public slots: + void selectFirstMatchingItem( const QgsAttributeFormTreeData::DnDTreeItemData &data ); + + // protected: + // void dragMoveEvent( QDragMoveEvent *event ) override; + // void dropEvent( QDropEvent *event ) override; + // bool dropMimeData( QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action ) override; + + // QTreeWidget interface + // protected: + // QStringList mimeTypes() const override; + + // #if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + // QMimeData *mimeData( const QList items ) const override; + // #else + // QMimeData *mimeData( const QList &items ) const override; + // #endif + + private slots: + void onItemDoubleClicked( const QModelIndex &index ); + + private: + QgsVectorLayer *mLayer = nullptr; + // Type mType = QgsAttributesDnDTree::Type::Drag; + + // QgsExpressionContextGenerator interface + public: + QgsExpressionContext createExpressionContext() const override; +}; + #endif // QGSATTRIBUTESFORMPROPERTIES_H