diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 4ec7c3d11e..5e6d8fb3f2 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -604,114 +604,6 @@ class CustomProperties : public VariantMapProperty }; -PropertiesWidget::PropertiesWidget(QWidget *parent) - : QWidget{parent} - , mCustomProperties(new CustomProperties) - , mScrollArea(new QScrollArea(this)) -{ - auto scrollWidget = new QWidget(mScrollArea); - scrollWidget->setBackgroundRole(QPalette::AlternateBase); - scrollWidget->setMinimumWidth(Utils::dpiScaled(120)); - - auto verticalLayout = new QVBoxLayout(scrollWidget); - mPropertyBrowser = new VariantEditor(scrollWidget); - verticalLayout->addWidget(mPropertyBrowser); - verticalLayout->addStretch(); - verticalLayout->setContentsMargins(0, 0, 0, Utils::dpiScaled(4)); - - mScrollArea->setWidget(scrollWidget); - mScrollArea->setWidgetResizable(true); - - mActionAddProperty = new QAction(this); - mActionAddProperty->setEnabled(false); - mActionAddProperty->setIcon(QIcon(QLatin1String(":/images/16/add.png"))); - connect(mActionAddProperty, &QAction::triggered, - this, &PropertiesWidget::openAddPropertyDialog); - - mActionRemoveProperty = new QAction(this); - mActionRemoveProperty->setEnabled(false); - mActionRemoveProperty->setIcon(QIcon(QLatin1String(":/images/16/remove.png"))); - mActionRemoveProperty->setShortcuts(QKeySequence::Delete); - connect(mActionRemoveProperty, &QAction::triggered, - this, &PropertiesWidget::removeProperties); - - mActionRenameProperty = new QAction(this); - mActionRenameProperty->setEnabled(false); - mActionRenameProperty->setIcon(QIcon(QLatin1String(":/images/16/rename.png"))); - // connect(mActionRenameProperty, &QAction::triggered, - // this, &PropertiesWidget::renameProperty); - - Utils::setThemeIcon(mActionAddProperty, "add"); - Utils::setThemeIcon(mActionRemoveProperty, "remove"); - Utils::setThemeIcon(mActionRenameProperty, "rename"); - - QToolBar *toolBar = new QToolBar; - toolBar->setFloatable(false); - toolBar->setMovable(false); - toolBar->setIconSize(Utils::smallIconSize()); - toolBar->addAction(mActionAddProperty); - toolBar->addAction(mActionRemoveProperty); - toolBar->addAction(mActionRenameProperty); - - QVBoxLayout *layout = new QVBoxLayout(this); - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(0); - layout->addWidget(mScrollArea); - layout->addWidget(toolBar); - setLayout(layout); - - mPropertyBrowser->setContextMenuPolicy(Qt::CustomContextMenu); - connect(mPropertyBrowser, &PropertyBrowser::customContextMenuRequested, - this, &PropertiesWidget::showContextMenu); - // connect(mPropertyBrowser, &PropertyBrowser::selectedItemsChanged, - // this, &PropertiesWidget::updateActions); - - connect(mCustomProperties, &VariantMapProperty::renameRequested, - this, &PropertiesWidget::renameProperty); - - retranslateUi(); -} - -PropertiesWidget::~PropertiesWidget() -{ - // Disconnect to avoid crashing due to signals emitted during destruction - mPropertyBrowser->disconnect(this); -} - -void PropertiesWidget::setDocument(Document *document) -{ - if (mDocument == document) - return; - - if (mDocument) - mDocument->disconnect(this); - - mDocument = document; - // mPropertyBrowser->setDocument(document); - mCustomProperties->setDocument(document); - - if (document) { - connect(document, &Document::currentObjectChanged, - this, &PropertiesWidget::currentObjectChanged); - connect(document, &Document::editCurrentObject, - this, &PropertiesWidget::bringToFront); - - connect(document, &Document::propertyAdded, - this, &PropertiesWidget::updateActions); - connect(document, &Document::propertyRemoved, - this, &PropertiesWidget::updateActions); - - currentObjectChanged(document->currentObject()); - } else { - currentObjectChanged(nullptr); - } -} - -void PropertiesWidget::selectCustomProperty(const QString &name) -{ - // mPropertyBrowser->selectCustomProperty(name); -} - static bool anyObjectHasProperty(const QList &objects, const QString &name) { for (Object *obj : objects) { @@ -852,24 +744,19 @@ class MapSizeProperty : public SizeProperty MapDocument *mMapDocument; }; -class ObjectProperties : public QObject +class ObjectProperties : public GroupProperty { Q_OBJECT public: ObjectProperties(Document *document, Object *object, QObject *parent = nullptr) - : QObject(parent) + : GroupProperty(parent) , mDocument(document) , mObject(object) { mClassProperty = new ClassNameProperty(document, object, this); } - virtual void populateEditor(VariantEditor *) - { - // nothing added here due to property grouping - } - protected: void push(QUndoCommand *command) { @@ -1026,16 +913,13 @@ class MapProperties : public ObjectProperties mMapProperties->addProperty(mRenderOrderProperty); mMapProperties->addProperty(mBackgroundColorProperty); + addProperty(mMapProperties); + updateEnabledState(); connect(document, &Document::changed, this, &MapProperties::onChanged); } - void populateEditor(VariantEditor *editor) override - { - editor->addProperty(mMapProperties); - } - private: void onChanged(const ChangeEvent &event) { @@ -1248,15 +1132,12 @@ class LayerProperties : public ObjectProperties mLayerProperties->addProperty(mOffsetProperty); mLayerProperties->addProperty(mParallaxFactorProperty); + addProperty(mLayerProperties); + connect(document, &Document::changed, this, &LayerProperties::onChanged); } - void populateEditor(VariantEditor *editor) override - { - editor->addProperty(mLayerProperties); - } - protected: virtual void onChanged(const ChangeEvent &event) { @@ -1360,12 +1241,8 @@ class ImageLayerProperties : public LayerProperties mImageLayerProperties->addProperty(mTransparentColorProperty); mImageLayerProperties->addSeparator(); mImageLayerProperties->addProperty(mRepeatProperty); - } - void populateEditor(VariantEditor *editor) override - { - LayerProperties::populateEditor(editor); - editor->addProperty(mImageLayerProperties); + addProperty(mImageLayerProperties); } private: @@ -1427,12 +1304,8 @@ class ObjectGroupProperties : public LayerProperties mObjectGroupProperties = new GroupProperty(tr("Object Layer")); mObjectGroupProperties->addProperty(mColorProperty); mObjectGroupProperties->addProperty(mDrawOrderProperty); - } - void populateEditor(VariantEditor *editor) override - { - LayerProperties::populateEditor(editor); - editor->addProperty(mObjectGroupProperties); + addProperty(mObjectGroupProperties); } private: @@ -1621,6 +1494,8 @@ class TilesetProperties : public ObjectProperties if (!tileset()->isCollection()) mTilesetProperties->addProperty(mTilesetImageProperty); + addProperty(mTilesetProperties); + updateEnabledState(); connect(tilesetDocument(), &Document::changed, this, &TilesetProperties::onChanged); @@ -1635,11 +1510,6 @@ class TilesetProperties : public ObjectProperties this, &TilesetProperties::onTilesetChanged); } - void populateEditor(VariantEditor *editor) - { - editor->addProperty(mTilesetProperties); - } - private: void onChanged(const ChangeEvent &event) { @@ -1887,17 +1757,14 @@ class MapObjectProperties : public ObjectProperties mObjectProperties->addProperty(mTextColorProperty); } + addProperty(mObjectProperties); + connect(document, &Document::changed, this, &MapObjectProperties::onChanged); updateEnabledState(); } - void populateEditor(VariantEditor *editor) override - { - editor->addProperty(mObjectProperties); - } - private: void onChanged(const ChangeEvent &event) { @@ -2059,6 +1926,8 @@ class TileProperties : public ObjectProperties mTileProperties->addProperty(mRectangleProperty); mTileProperties->addProperty(mProbabilityProperty); + addProperty(mTileProperties); + // annoying... maybe we should somehow always have the relevant TilesetDocument if (auto tilesetDocument = qobject_cast(document)) { connect(tilesetDocument, &TilesetDocument::tileImageSourceChanged, @@ -2077,11 +1946,6 @@ class TileProperties : public ObjectProperties updateEnabledState(); } - void populateEditor(VariantEditor *editor) override - { - editor->addProperty(mTileProperties); - } - private: void tileImageSourceChanged(Tile *tile) { @@ -2166,17 +2030,14 @@ class WangSetProperties : public ObjectProperties mWangSetProperties->addProperty(mTypeProperty); mWangSetProperties->addProperty(mColorCountProperty); + addProperty(mWangSetProperties); + connect(document, &Document::changed, this, &WangSetProperties::onChanged); updateEnabledState(); } - void populateEditor(VariantEditor *editor) override - { - editor->addProperty(mWangSetProperties); - } - private: void onChanged(const ChangeEvent &event) { @@ -2264,17 +2125,14 @@ class WangColorProperties : public ObjectProperties mWangColorProperties->addProperty(mColorProperty); mWangColorProperties->addProperty(mProbabilityProperty); + addProperty(mWangColorProperties); + connect(document, &Document::changed, this, &WangColorProperties::onChanged); updateEnabledState(); } - void populateEditor(VariantEditor *editor) override - { - editor->addProperty(mWangColorProperties); - } - private: void onChanged(const ChangeEvent &event) { @@ -2326,10 +2184,131 @@ class WangColorProperties : public ObjectProperties }; +PropertiesWidget::PropertiesWidget(QWidget *parent) + : QWidget{parent} + , mCustomProperties(new CustomProperties) + , mScrollArea(new QScrollArea(this)) +{ + auto scrollWidget = new QWidget(mScrollArea); + scrollWidget->setBackgroundRole(QPalette::AlternateBase); + scrollWidget->setMinimumWidth(Utils::dpiScaled(120)); + + auto verticalLayout = new QVBoxLayout(scrollWidget); + mPropertyBrowser = new VariantEditor(scrollWidget); + verticalLayout->addWidget(mPropertyBrowser); + verticalLayout->addStretch(); + verticalLayout->setContentsMargins(0, 0, 0, Utils::dpiScaled(4)); + + mScrollArea->setWidget(scrollWidget); + mScrollArea->setWidgetResizable(true); + + mActionAddProperty = new QAction(this); + mActionAddProperty->setEnabled(false); + mActionAddProperty->setIcon(QIcon(QLatin1String(":/images/16/add.png"))); + connect(mActionAddProperty, &QAction::triggered, + this, &PropertiesWidget::openAddPropertyDialog); + + mActionRemoveProperty = new QAction(this); + mActionRemoveProperty->setEnabled(false); + mActionRemoveProperty->setIcon(QIcon(QLatin1String(":/images/16/remove.png"))); + mActionRemoveProperty->setShortcuts(QKeySequence::Delete); + connect(mActionRemoveProperty, &QAction::triggered, + this, &PropertiesWidget::removeProperties); + + mActionRenameProperty = new QAction(this); + mActionRenameProperty->setEnabled(false); + mActionRenameProperty->setIcon(QIcon(QLatin1String(":/images/16/rename.png"))); + // connect(mActionRenameProperty, &QAction::triggered, + // this, &PropertiesWidget::renameProperty); + + Utils::setThemeIcon(mActionAddProperty, "add"); + Utils::setThemeIcon(mActionRemoveProperty, "remove"); + Utils::setThemeIcon(mActionRenameProperty, "rename"); + + QToolBar *toolBar = new QToolBar; + toolBar->setFloatable(false); + toolBar->setMovable(false); + toolBar->setIconSize(Utils::smallIconSize()); + toolBar->addAction(mActionAddProperty); + toolBar->addAction(mActionRemoveProperty); + toolBar->addAction(mActionRenameProperty); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + layout->addWidget(mScrollArea); + layout->addWidget(toolBar); + setLayout(layout); + + mPropertyBrowser->setContextMenuPolicy(Qt::CustomContextMenu); + connect(mPropertyBrowser, &PropertyBrowser::customContextMenuRequested, + this, &PropertiesWidget::showContextMenu); + // connect(mPropertyBrowser, &PropertyBrowser::selectedItemsChanged, + // this, &PropertiesWidget::updateActions); + + connect(mCustomProperties, &VariantMapProperty::renameRequested, + this, &PropertiesWidget::renameProperty); + + retranslateUi(); +} + +PropertiesWidget::~PropertiesWidget() +{ + // Disconnect to avoid crashing due to signals emitted during destruction + mPropertyBrowser->disconnect(this); +} + +void PropertiesWidget::setDocument(Document *document) +{ + if (mDocument == document) + return; + + if (mDocument) + mDocument->disconnect(this); + + mDocument = document; + // mPropertyBrowser->setDocument(document); + mCustomProperties->setDocument(document); + + if (document) { + connect(document, &Document::currentObjectChanged, + this, &PropertiesWidget::currentObjectChanged); + connect(document, &Document::editCurrentObject, + this, [this] { + if (mPropertiesObject) + mPropertiesObject->expandAll(); + emit bringToFront(); + }); + + connect(document, &Document::propertyAdded, + this, &PropertiesWidget::updateActions); + connect(document, &Document::propertyRemoved, + this, &PropertiesWidget::updateActions); + + currentObjectChanged(document->currentObject()); + } else { + currentObjectChanged(nullptr); + } +} + +void PropertiesWidget::selectCustomProperty(const QString &name) +{ + // mPropertyBrowser->selectCustomProperty(name); +} + void PropertiesWidget::currentObjectChanged(Object *object) { mPropertyBrowser->clear(); + // Remember the expanded states + if (mPropertiesObject) { + const auto &subProperties = mPropertiesObject->subProperties(); + for (int i = 0; i < subProperties.size(); ++i) { + if (auto subGroupProperty = qobject_cast(subProperties.at(i))) + mExpandedStates[i] = subGroupProperty->isExpanded(); + } + } + delete mPropertiesObject; mPropertiesObject = nullptr; @@ -2392,9 +2371,18 @@ void PropertiesWidget::currentObjectChanged(Object *object) } } + // Restore the expanded states + if (mPropertiesObject) { + const auto &subProperties = mPropertiesObject->subProperties(); + for (int i = 0; i < subProperties.size(); ++i) { + if (auto subGroupProperty = qobject_cast(subProperties.at(i))) + subGroupProperty->setExpanded(mExpandedStates.value(i, true)); + } + } + if (object) { if (mPropertiesObject) - mPropertiesObject->populateEditor(mPropertyBrowser); + mPropertyBrowser->addProperty(mPropertiesObject); mPropertyBrowser->addProperty(mCustomProperties); } diff --git a/src/tiled/propertieswidget.h b/src/tiled/propertieswidget.h index 0eea6bd412..1742f8c82f 100644 --- a/src/tiled/propertieswidget.h +++ b/src/tiled/propertieswidget.h @@ -20,6 +20,7 @@ #pragma once +#include #include class QScrollArea; @@ -79,6 +80,7 @@ public slots: Document *mDocument = nullptr; ObjectProperties *mPropertiesObject = nullptr; CustomProperties *mCustomProperties = nullptr; + QMap mExpandedStates; QScrollArea *mScrollArea; VariantEditor *mPropertyBrowser; QAction *mActionAddProperty; diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index 632c47496a..5c55782d79 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -72,6 +72,17 @@ void Property::setActions(Actions actions) } } +Property::DisplayMode GroupProperty::displayMode() const +{ + if (name().isEmpty()) + return DisplayMode::ChildrenOnly; + + if (m_header) + return DisplayMode::Header; + + return DisplayMode::Default; +} + void GroupProperty::setExpanded(bool expanded) { if (m_expanded != expanded) { @@ -626,6 +637,15 @@ QLayout *VariantEditor::createPropertyLayout(Property *property) const auto halfSpacing = Utils::dpiScaled(2); + if (displayMode == Property::DisplayMode::ChildrenOnly) { + if (auto groupProperty = qobject_cast(property)) { + widgets.childrenLayout = new QVBoxLayout; + widgets.layout = widgets.childrenLayout; + setPropertyChildrenExpanded(groupProperty, true); + return widgets.layout; + } + } + auto rowLayout = new QHBoxLayout; rowLayout->setSpacing(halfSpacing * 2); @@ -739,9 +759,10 @@ void VariantEditor::setPropertyChildrenExpanded(GroupProperty *groupProperty, bo const auto halfSpacing = Utils::dpiScaled(2); widgets.children = new VariantEditor(this); - if (widgets.label->isHeader()) + if (widgets.label && widgets.label->isHeader()) widgets.children->setContentsMargins(0, halfSpacing, 0, halfSpacing); - widgets.children->setLevel(m_level + 1); + if (groupProperty->displayMode() != Property::DisplayMode::ChildrenOnly) + widgets.children->setLevel(m_level + 1); widgets.children->setEnabled(groupProperty->isEnabled()); for (auto property : groupProperty->subProperties()) widgets.children->addProperty(property); diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index 596db0ab76..4693e36504 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -55,7 +55,8 @@ class Property : public QObject Default, NoLabel, Header, - Separator + Separator, + ChildrenOnly }; enum class Action { @@ -134,14 +135,20 @@ class GroupProperty : public Property Q_PROPERTY(bool expanded READ isExpanded WRITE setExpanded NOTIFY expandedChanged) public: - GroupProperty(const QString &name, QObject *parent = nullptr) + /** + * Creates an unnamed group, which will only display its children. + */ + explicit GroupProperty(QObject *parent = nullptr) + : Property(QString(), parent) + {} + + explicit GroupProperty(const QString &name, QObject *parent = nullptr) : Property(name, parent) {} ~GroupProperty() override { clear(); } - DisplayMode displayMode() const override - { return m_header ? DisplayMode::Header : DisplayMode::Default; } + DisplayMode displayMode() const override; QWidget *createEditor(QWidget */* parent */) override { return nullptr; }