Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add new attribute form container type "Row"
This container always lays out child widgets in a horizontal
row, where the number of columns is automatically determined
by the number of child widgets.

It's useful for creation of compact forms, where no space will
be wasted by assigning extraneous horizontal width to widgets
where the expected values will always be short.

Eg: creating 3 rows with 3, 2, 1 child widgets respectively
results in the layout:

    Attr 1: [...] Attr 2: [...] Attr 3: [...]
    Attr 4: [..........] Attr 5: [..........]
    Attr 6: [...............................]

Without the option of row containers then the all horizontal
rows will have the same number of columns, eg:

    Attr 1: [...] Attr 2: [...] Attr 3: [...]
    Attr 4: [...] Attr 5: [...] Attr 6: [...]

(leaving insufficient horizontal length for attributes 4-6), or

    Attr 1: [..........] Attr 2: [..........]
    Attr 2: [..........] Attr 3: [..........]
    Attr 4: [..........] Attr 5: [..........]
    Attr 6: [..........]

(resulting in wasted horizontal space next to attribute 6, and
an extra row taking up vertical space)

Sponsored by NIWA
  • Loading branch information
nyalldawson committed May 1, 2023
1 parent 1de12f3 commit ab19e8e
Show file tree
Hide file tree
Showing 12 changed files with 272 additions and 237 deletions.
3 changes: 2 additions & 1 deletion python/core/auto_additions/qgis.py
Expand Up @@ -3519,7 +3519,8 @@
# monkey patching scoped based enum
Qgis.AttributeEditorContainerType.GroupBox.__doc__ = "A group box"
Qgis.AttributeEditorContainerType.Tab.__doc__ = "A tab widget"
Qgis.AttributeEditorContainerType.__doc__ = 'Attribute editor container types.\n\n.. versionadded:: 3.32\n\n' + '* ``GroupBox``: ' + Qgis.AttributeEditorContainerType.GroupBox.__doc__ + '\n' + '* ``Tab``: ' + Qgis.AttributeEditorContainerType.Tab.__doc__
Qgis.AttributeEditorContainerType.Row.__doc__ = "A row of editors (horizontal layout)"
Qgis.AttributeEditorContainerType.__doc__ = 'Attribute editor container types.\n\n.. versionadded:: 3.32\n\n' + '* ``GroupBox``: ' + Qgis.AttributeEditorContainerType.GroupBox.__doc__ + '\n' + '* ``Tab``: ' + Qgis.AttributeEditorContainerType.Tab.__doc__ + '\n' + '* ``Row``: ' + Qgis.AttributeEditorContainerType.Row.__doc__
# --
Qgis.AttributeEditorContainerType.baseClass = Qgis
QgsEditFormConfig.EditorLayout = Qgis.AttributeFormLayout
Expand Down
1 change: 1 addition & 0 deletions python/core/auto_generated/qgis.sip.in
Expand Up @@ -1996,6 +1996,7 @@ The development version
{
GroupBox,
Tab,
Row,
};

enum class AttributeFormLayout
Expand Down
1 change: 1 addition & 0 deletions src/core/qgis.h
Expand Up @@ -3458,6 +3458,7 @@ class CORE_EXPORT Qgis
{
GroupBox, //!< A group box
Tab, //!< A tab widget
Row, //!< A row of editors (horizontal layout)
};
Q_ENUM( AttributeEditorContainerType )

Expand Down
66 changes: 38 additions & 28 deletions src/gui/attributeformconfig/qgsattributeformcontaineredit.cpp
Expand Up @@ -31,18 +31,12 @@ QgsAttributeFormContainerEdit::QgsAttributeFormContainerEdit( QTreeWidgetItem *i
{
// only top level items can be tabs
mTypeCombo->addItem( tr( "Tab" ), QVariant::fromValue( Qgis::AttributeEditorContainerType::Tab ) );
mTypeCombo->addItem( tr( "Group Box" ), QVariant::fromValue( Qgis::AttributeEditorContainerType::GroupBox ) );
}
if ( item->parent() )
{
// i.e. it's always a group box if it's a nested container
mTypeCombo->addItem( tr( "Group Box" ), QVariant::fromValue( Qgis::AttributeEditorContainerType::GroupBox ) );
mTypeCombo->setEnabled( false );
}
mTypeCombo->addItem( tr( "Group Box" ), QVariant::fromValue( Qgis::AttributeEditorContainerType::GroupBox ) );
mTypeCombo->addItem( tr( "Row" ), QVariant::fromValue( Qgis::AttributeEditorContainerType::Row ) );

mTitleLineEdit->setText( itemData.name() );
mShowLabelCheckBox->setChecked( itemData.showLabel() );
mShowLabelCheckBox->setEnabled( itemData.containerType() == Qgis::AttributeEditorContainerType::GroupBox ); // show label makes sense for group box, not other container types
mTypeCombo->setCurrentIndex( mTypeCombo->findData( QVariant::fromValue( itemData.containerType() ) ) );
if ( mTypeCombo->currentIndex() < 0 )
mTypeCombo->setCurrentIndex( 0 );
Expand All @@ -54,31 +48,13 @@ QgsAttributeFormContainerEdit::QgsAttributeFormContainerEdit( QTreeWidgetItem *i
mBackgroundColorButton->setShowNull( true );
mBackgroundColorButton->setColor( itemData.backgroundColor() );
mCollapsedCheckBox->setChecked( itemData.collapsed() );
mCollapsedCheckBox->setEnabled( itemData.containerType() == Qgis::AttributeEditorContainerType::GroupBox );
mControlCollapsedGroupBox->setChecked( itemData.collapsedExpression().enabled() );
mControlCollapsedGroupBox->setEnabled( itemData.containerType() == Qgis::AttributeEditorContainerType::GroupBox );
mCollapsedExpressionWidget->setExpression( itemData.collapsedExpression()->expression() );

mFormLabelFormatWidget->setLabelStyle( itemData.labelStyle() );

// show label makes sense for group box, not for tabs
connect( mTypeCombo, qOverload<int>( &QComboBox::currentIndexChanged ), this, [this]
{
const Qgis::AttributeEditorContainerType type = mTypeCombo->currentData().value< Qgis::AttributeEditorContainerType >();
switch ( type )
{
case Qgis::AttributeEditorContainerType::GroupBox:
mShowLabelCheckBox->setEnabled( true );
mCollapsedCheckBox->setEnabled( true );
mControlCollapsedGroupBox->setEnabled( true );
break;
case Qgis::AttributeEditorContainerType::Tab:
mShowLabelCheckBox->setEnabled( false );
mCollapsedCheckBox->setEnabled( false );
mControlCollapsedGroupBox->setEnabled( false );
break;
}
} );
connect( mTypeCombo, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsAttributeFormContainerEdit::containerTypeChanged );
containerTypeChanged();
}

void QgsAttributeFormContainerEdit::registerExpressionContextGenerator( QgsExpressionContextGenerator *generator )
Expand Down Expand Up @@ -112,3 +88,37 @@ void QgsAttributeFormContainerEdit::updateItemData()
mTreeItem->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
mTreeItem->setText( 0, itemData.name() );
}

void QgsAttributeFormContainerEdit::containerTypeChanged()
{
// show label makes sense for group box, not for tabs
const Qgis::AttributeEditorContainerType type = mTypeCombo->currentData().value< Qgis::AttributeEditorContainerType >();
switch ( type )
{
case Qgis::AttributeEditorContainerType::GroupBox:
mShowLabelCheckBox->setEnabled( true );
mCollapsedCheckBox->setEnabled( true );
mControlCollapsedGroupBox->setEnabled( true );
mColumnsLabel->show();
mColumnCountSpinBox->show();
break;
case Qgis::AttributeEditorContainerType::Tab:
mShowLabelCheckBox->setEnabled( false );
mShowLabelCheckBox->setChecked( true );
mCollapsedCheckBox->setEnabled( false );
mCollapsedCheckBox->setChecked( false );
mControlCollapsedGroupBox->setEnabled( false );
mColumnsLabel->show();
mColumnCountSpinBox->show();
break;
case Qgis::AttributeEditorContainerType::Row:
mShowLabelCheckBox->setEnabled( false );
mShowLabelCheckBox->setChecked( false );
mCollapsedCheckBox->setEnabled( false );
mCollapsedCheckBox->setChecked( false );
mControlCollapsedGroupBox->setEnabled( false );
mColumnsLabel->hide();
mColumnCountSpinBox->hide();
break;
}
}
4 changes: 4 additions & 0 deletions src/gui/attributeformconfig/qgsattributeformcontaineredit.h
Expand Up @@ -47,6 +47,10 @@ class GUI_EXPORT QgsAttributeFormContainerEdit: public QWidget, private Ui_QgsAt

void updateItemData();

private slots:

void containerTypeChanged();

private:
QTreeWidgetItem *mTreeItem = nullptr;
};
Expand Down
113 changes: 69 additions & 44 deletions src/gui/qgsaddtaborgroup.cpp
Expand Up @@ -25,100 +25,125 @@
#include <QComboBox>
#include <QRadioButton>

QgsAddTabOrGroup::QgsAddTabOrGroup( QgsVectorLayer *lyr, const QList < TabPair > &tabList, QTreeWidgetItem *currentTab, QWidget *parent )
QgsAddAttributeFormContainerDialog::QgsAddAttributeFormContainerDialog( QgsVectorLayer *lyr, const QList<ContainerPair> &existingContainerList, QTreeWidgetItem *currentTab, QWidget *parent )
: QDialog( parent )
, mLayer( lyr )
, mTabs( tabList )
, mExistingContainers( existingContainerList )
{
setupUi( this );
connect( mGroupButton, &QRadioButton::toggled, this, &QgsAddTabOrGroup::mGroupButton_toggled );
connect( mTabButton, &QRadioButton::toggled, this, &QgsAddTabOrGroup::mTabButton_toggled );

mTabButton->setChecked( true );
mTabList->setEnabled( false );
if ( !mTabs.isEmpty() )
mTypeCombo->addItem( tr( "Tab" ), QVariant::fromValue( Qgis::AttributeEditorContainerType::Tab ) );
mTypeCombo->addItem( tr( "Group Box" ), QVariant::fromValue( Qgis::AttributeEditorContainerType::GroupBox ) );
mTypeCombo->addItem( tr( "Row" ), QVariant::fromValue( Qgis::AttributeEditorContainerType::Row ) );

mTypeCombo->setCurrentIndex( mTypeCombo->findData( QVariant::fromValue( Qgis::AttributeEditorContainerType::Tab ) ) );

mParentCombo->setEnabled( false );
if ( !mExistingContainers.isEmpty() )
{
int i = 0;
const auto constMTabs = mTabs;
for ( const TabPair &tab : constMTabs )
for ( const ContainerPair &container : std::as_const( mExistingContainers ) )
{
mTabList->addItem( tab.first, i );
if ( tab.second == currentTab )
mParentCombo->addItem( container.first, i );
if ( container.second == currentTab )
{
mTabList->setCurrentIndex( i );
mGroupButton->setChecked( true );
mParentCombo->setCurrentIndex( i );
mTypeCombo->setCurrentIndex( mTypeCombo->findData( QVariant::fromValue( Qgis::AttributeEditorContainerType::GroupBox ) ) );
}
++i;
}
}
else
{
mGroupButton->setEnabled( false );
// top level items must be tabs
mTypeCombo->removeItem( mTypeCombo->findData( QVariant::fromValue( Qgis::AttributeEditorContainerType::GroupBox ) ) );
mTypeCombo->removeItem( mTypeCombo->findData( QVariant::fromValue( Qgis::AttributeEditorContainerType::Row ) ) );
}

connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsAddTabOrGroup::showHelp );
connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsAddAttributeFormContainerDialog::showHelp );

mColumnCountSpinBox->setValue( QgsSettings().value( QStringLiteral( "/qgis/attributeForm/defaultTabColumnCount" ), 1 ).toInt() );

setWindowTitle( tr( "Add Container for %1" ).arg( mLayer->name() ) );

connect( mTypeCombo, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsAddAttributeFormContainerDialog::containerTypeChanged );
containerTypeChanged();
}

QString QgsAddTabOrGroup::name()
QString QgsAddAttributeFormContainerDialog::name()
{
return mName->text();
}

QTreeWidgetItem *QgsAddTabOrGroup::tab()
QTreeWidgetItem *QgsAddAttributeFormContainerDialog::parentContainerItem()
{
const TabPair tab = mTabs.at( mTabList->currentData().toInt() );
if ( containerType() == Qgis::AttributeEditorContainerType::Tab )
return nullptr;

const ContainerPair tab = mExistingContainers.at( mParentCombo->currentData().toInt() );
return tab.second;
}

int QgsAddTabOrGroup::columnCount() const
int QgsAddAttributeFormContainerDialog::columnCount() const
{
return mColumnCountSpinBox->value();
}

Qgis::AttributeEditorContainerType QgsAddTabOrGroup::containerType() const
Qgis::AttributeEditorContainerType QgsAddAttributeFormContainerDialog::containerType() const
{
return mTabButton->isChecked() ? Qgis::AttributeEditorContainerType::Tab : Qgis::AttributeEditorContainerType::GroupBox;
return mTypeCombo->currentData().value< Qgis::AttributeEditorContainerType >();
}

void QgsAddTabOrGroup::accept()
void QgsAddAttributeFormContainerDialog::accept()
{
if ( mColumnCountSpinBox->value() > 0 )
{
if ( mGroupButton->isChecked() )
switch ( containerType() )
{
QgsSettings().setValue( QStringLiteral( "/qgis/attributeForm/defaultGroupColumnCount" ), mColumnCountSpinBox->value() );
}
else
{
QgsSettings().setValue( QStringLiteral( "/qgis/attributeForm/defaultTabColumnCount" ), mColumnCountSpinBox->value() );

case Qgis::AttributeEditorContainerType::GroupBox:
QgsSettings().setValue( QStringLiteral( "/qgis/attributeForm/defaultGroupColumnCount" ), mColumnCountSpinBox->value() );
break;
case Qgis::AttributeEditorContainerType::Tab:
QgsSettings().setValue( QStringLiteral( "/qgis/attributeForm/defaultTabColumnCount" ), mColumnCountSpinBox->value() );
break;
case Qgis::AttributeEditorContainerType::Row:
break;
}
}

QDialog::accept();
}

void QgsAddTabOrGroup::mGroupButton_toggled( bool checked )
void QgsAddAttributeFormContainerDialog::showHelp()
{
mTabList->setEnabled( checked );

if ( checked )
{
mColumnCountSpinBox->setValue( QgsSettings().value( QStringLiteral( "/qgis/attributeForm/defaultGroupColumnCount" ), 1 ).toInt() );
}
}

void QgsAddTabOrGroup::mTabButton_toggled( bool checked )
{
mTabList->setEnabled( !checked );
if ( checked )
mColumnCountSpinBox->setValue( QgsSettings().value( QStringLiteral( "/qgis/attributeForm/defaultTabColumnCount" ), 1 ).toInt() );
QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#the-drag-and-drop-designer" ) );
}

void QgsAddTabOrGroup::showHelp()
void QgsAddAttributeFormContainerDialog::containerTypeChanged()
{
QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#the-drag-and-drop-designer" ) );
const Qgis::AttributeEditorContainerType type = mTypeCombo->currentData().value< Qgis::AttributeEditorContainerType >();
switch ( type )
{
case Qgis::AttributeEditorContainerType::GroupBox:
mParentCombo->show();
mLabelParent->show();
mColumnsLabel->show();
mColumnCountSpinBox->show();
mColumnCountSpinBox->setValue( QgsSettings().value( QStringLiteral( "/qgis/attributeForm/defaultGroupColumnCount" ), 1 ).toInt() );
break;
case Qgis::AttributeEditorContainerType::Tab:
mParentCombo->hide();
mLabelParent->hide();
mColumnsLabel->show();
mColumnCountSpinBox->show();
mColumnCountSpinBox->setValue( QgsSettings().value( QStringLiteral( "/qgis/attributeForm/defaultTabColumnCount" ), 1 ).toInt() );
break;
case Qgis::AttributeEditorContainerType::Row:
mParentCombo->show();
mLabelParent->show();
mColumnsLabel->hide();
mColumnCountSpinBox->hide();
break;
}
}
19 changes: 11 additions & 8 deletions src/gui/qgsaddtaborgroup.h
Expand Up @@ -36,22 +36,26 @@ class QgsVectorLayer;
* \note This class is not a part of public API
* \since QGIS 3.14
*/
class GUI_EXPORT QgsAddTabOrGroup : public QDialog, private Ui::QgsAddTabOrGroupBase
class GUI_EXPORT QgsAddAttributeFormContainerDialog : public QDialog, private Ui::QgsAddTabOrGroupBase
{
Q_OBJECT

public:
typedef QPair<QString, QTreeWidgetItem *> TabPair;
typedef QPair<QString, QTreeWidgetItem *> ContainerPair;

public:
//! constructor
QgsAddTabOrGroup( QgsVectorLayer *lyr, const QList<TabPair> &tabList, QTreeWidgetItem *currentTab = nullptr, QWidget *parent = nullptr );
QgsAddAttributeFormContainerDialog( QgsVectorLayer *lyr, const QList<ContainerPair> &existingContainerList, QTreeWidgetItem *currentTab = nullptr, QWidget *parent = nullptr );

//! Returns the name of the tab or group
QString name();

//! Returns tree item corresponding to the added tab
QTreeWidgetItem *tab();
/**
* Returns tree item corresponding to the selected parent container.
*
* Will be NULLPTR when a new tab is created.
*/
QTreeWidgetItem *parentContainerItem();

//! Returns the column count
int columnCount() const;
Expand All @@ -67,13 +71,12 @@ class GUI_EXPORT QgsAddTabOrGroup : public QDialog, private Ui::QgsAddTabOrGroup
void accept() override;

private slots:
void mGroupButton_toggled( bool checked );
void mTabButton_toggled( bool checked );
void showHelp();
void containerTypeChanged();

protected:
QgsVectorLayer *mLayer = nullptr;
QList< TabPair > mTabs;
QList< ContainerPair > mExistingContainers;
};

#endif

0 comments on commit ab19e8e

Please sign in to comment.